diff --git a/api/src/main/java/net/jitse/npclib/NPCLib.java b/api/src/main/java/net/jitse/npclib/NPCLib.java index 32de577d..12515c9d 100644 --- a/api/src/main/java/net/jitse/npclib/NPCLib.java +++ b/api/src/main/java/net/jitse/npclib/NPCLib.java @@ -4,11 +4,14 @@ package net.jitse.npclib; +import net.jitse.npclib.NPCLibOptions.MovementHandling; import net.jitse.npclib.api.NPC; import net.jitse.npclib.api.utilities.Logger; import net.jitse.npclib.listeners.ChunkListener; import net.jitse.npclib.listeners.PacketListener; +import net.jitse.npclib.listeners.PeriodicMoveListener; import net.jitse.npclib.listeners.PlayerListener; +import net.jitse.npclib.listeners.PlayerMoveEventListener; import net.jitse.npclib.metrics.NPCLibMetrics; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -23,7 +26,7 @@ public final class NPCLib { private double autoHideDistance = 50.0; - public NPCLib(JavaPlugin plugin) { + private NPCLib(JavaPlugin plugin, MovementHandling moveHandling) { this.plugin = plugin; this.logger = new Logger("NPCLib"); @@ -49,6 +52,12 @@ public NPCLib(JavaPlugin plugin) { pluginManager.registerEvents(new PlayerListener(this), plugin); pluginManager.registerEvents(new ChunkListener(this), plugin); + if (moveHandling.usePme) { + pluginManager.registerEvents(new PlayerMoveEventListener(), plugin); + } else { + pluginManager.registerEvents(new PeriodicMoveListener(this, moveHandling.updateInterval), plugin); + } + // Boot the according packet listener. new PacketListener().start(this); @@ -59,6 +68,14 @@ public NPCLib(JavaPlugin plugin) { logger.info("Enabled for Minecraft " + versionName); } + public NPCLib(JavaPlugin plugin) { + this(plugin, MovementHandling.playerMoveEvent()); + } + + public NPCLib(JavaPlugin plugin, NPCLibOptions options) { + this(plugin, options.moveHandling); + } + /** * @return The JavaPlugin instance. */ diff --git a/api/src/main/java/net/jitse/npclib/NPCLibOptions.java b/api/src/main/java/net/jitse/npclib/NPCLibOptions.java new file mode 100644 index 00000000..3ca8445f --- /dev/null +++ b/api/src/main/java/net/jitse/npclib/NPCLibOptions.java @@ -0,0 +1,77 @@ +package net.jitse.npclib; + +/** + * Mutable preferences for library usage + * + * @author A248 + * + */ +public class NPCLibOptions { + + MovementHandling moveHandling; + + /** + * Creates the default options + * + */ + public NPCLibOptions() { + moveHandling = MovementHandling.playerMoveEvent(); + } + + /** + * Specifies the motion handling which will be used for the library.
+ * Programmers may choose between using the PlayerMoveEvent or + * a periodic task.
+ *
+ * Note that NPCLib will always use events such as the PlayerTeleportEvent + * and PlayerChangedWorldEvent in addition to the specified option. + * + * @param moveHandling the movement handling + * @return the same NPCLibOptions + */ + public NPCLibOptions setMovementHandling(MovementHandling moveHandling) { + this.moveHandling = moveHandling; + return this; + } + + /** + * Options relating to movement handling + * + * @author A248 + * + */ + public static class MovementHandling { + + final boolean usePme; + final long updateInterval; + + private MovementHandling(boolean usePme, long updateInterval) { + this.usePme = usePme; + this.updateInterval = updateInterval; + } + + /** + * Gets movement handling using the PlayerMoveEvent + * + * @return movement handling + */ + public static MovementHandling playerMoveEvent() { + return new MovementHandling(false, 0); + } + + /** + * Gets movement handling using a periodic update interval in ticks. + * + * @param updateInterval the update interval in ticks + * @return movement handling + */ + public static MovementHandling repeatingTask(long updateInterval) { + if (updateInterval <= 0) { + throw new IllegalArgumentException("Negative update interval"); + } + return new MovementHandling(true, updateInterval); + } + + } + +} diff --git a/api/src/main/java/net/jitse/npclib/listeners/HandleMoveBase.java b/api/src/main/java/net/jitse/npclib/listeners/HandleMoveBase.java new file mode 100644 index 00000000..11d375ed --- /dev/null +++ b/api/src/main/java/net/jitse/npclib/listeners/HandleMoveBase.java @@ -0,0 +1,26 @@ +package net.jitse.npclib.listeners; + +import org.bukkit.entity.Player; + +import net.jitse.npclib.internal.NPCBase; +import net.jitse.npclib.internal.NPCManager; + +public class HandleMoveBase { + + void handleMove(Player player) { + for (NPCBase npc : NPCManager.getAllNPCs()) { + if (!npc.getShown().contains(player.getUniqueId())) { + continue; // NPC was never supposed to be shown to the player. + } + + if (!npc.isShown(player) && npc.inRangeOf(player) && npc.inViewOf(player)) { + // The player is in range and can see the NPC, auto-show it. + npc.show(player, true); + } else if (npc.isShown(player) && !npc.inRangeOf(player)) { + // The player is not in range of the NPC anymore, auto-hide it. + npc.hide(player, true); + } + } + } + +} diff --git a/api/src/main/java/net/jitse/npclib/listeners/PeriodicMoveListener.java b/api/src/main/java/net/jitse/npclib/listeners/PeriodicMoveListener.java new file mode 100644 index 00000000..66623e88 --- /dev/null +++ b/api/src/main/java/net/jitse/npclib/listeners/PeriodicMoveListener.java @@ -0,0 +1,48 @@ +package net.jitse.npclib.listeners; + +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; + +import net.jitse.npclib.NPCLib; + +public class PeriodicMoveListener extends HandleMoveBase implements Listener { + + private final NPCLib instance; + private final long updateInterval; + + private final HashMap tasks = new HashMap<>(); + + public PeriodicMoveListener(NPCLib instance, long updateInterval) { + this.instance = instance; + this.updateInterval = updateInterval; + } + + private void startTask(UUID uuid) { + // purposefully using UUIDs and not holding player references + tasks.put(uuid, Bukkit.getScheduler().runTaskTimer(instance.getPlugin(), () -> { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { // safety check + handleMove(player); + } + }, 1L, updateInterval)); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent evt) { + startTask(evt.getPlayer().getUniqueId()); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent evt) { + tasks.remove(evt.getPlayer().getUniqueId()).cancel(); + } + +} diff --git a/api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java b/api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java index 5f87379b..6a72aa28 100755 --- a/api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java +++ b/api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java @@ -11,7 +11,6 @@ import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.*; @@ -20,7 +19,7 @@ /** * @author Jitse Boonstra */ -public class PlayerListener implements Listener { +public class PlayerListener extends HandleMoveBase implements Listener { private final NPCLib instance; @@ -30,17 +29,8 @@ public PlayerListener(NPCLib instance) { @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { - onPlayerLeave(event.getPlayer()); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onPlayerKick(PlayerKickEvent event) { - onPlayerLeave(event.getPlayer()); - } - - private void onPlayerLeave(Player player) { - for (NPCBase npc : NPCManager.getAllNPCs()) - npc.onLogout(player); + for (NPCBase npc : NPCManager.getAllNPCs()) + npc.onLogout(event.getPlayer()); } @EventHandler @@ -74,7 +64,7 @@ public void run() { this.cancel(); } } - }.runTaskTimerAsynchronously(instance.getPlugin(), 0, 1); + }.runTaskTimer(instance.getPlugin(), 0L, 1L); } } @@ -91,36 +81,8 @@ public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { } } - @EventHandler - public void onPlayerMove(PlayerMoveEvent event) { - Location from = event.getFrom(); - Location to = event.getTo(); - // Only check movement when the player moves from one block to another. The event is called often - // as it is also called when the pitch or yaw change. This is worth it from a performance view. - if (to == null || from.getBlockX() != to.getBlockX() - || from.getBlockY() != to.getBlockY() - || from.getBlockZ() != to.getBlockZ()) - handleMove(event.getPlayer()); - } - @EventHandler public void onPlayerTeleport(PlayerTeleportEvent event) { handleMove(event.getPlayer()); } - - private void handleMove(Player player) { - for (NPCBase npc : NPCManager.getAllNPCs()) { - if (!npc.getShown().contains(player.getUniqueId())) { - continue; // NPC was never supposed to be shown to the player. - } - - if (!npc.isShown(player) && npc.inRangeOf(player) && npc.inViewOf(player)) { - // The player is in range and can see the NPC, auto-show it. - npc.show(player, true); - } else if (npc.isShown(player) && !npc.inRangeOf(player)) { - // The player is not in range of the NPC anymore, auto-hide it. - npc.hide(player, true); - } - } - } } diff --git a/api/src/main/java/net/jitse/npclib/listeners/PlayerMoveEventListener.java b/api/src/main/java/net/jitse/npclib/listeners/PlayerMoveEventListener.java new file mode 100644 index 00000000..a07aa10d --- /dev/null +++ b/api/src/main/java/net/jitse/npclib/listeners/PlayerMoveEventListener.java @@ -0,0 +1,22 @@ +package net.jitse.npclib.listeners; + +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +public class PlayerMoveEventListener extends HandleMoveBase implements Listener { + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Location from = event.getFrom(); + Location to = event.getTo(); + // Only check movement when the player moves from one block to another. The event is called often + // as it is also called when the pitch or yaw change. This is worth it from a performance view. + if (to == null || from.getBlockX() != to.getBlockX() + || from.getBlockY() != to.getBlockY() + || from.getBlockZ() != to.getBlockZ()) + handleMove(event.getPlayer()); + } + +}