From 402431270631f32f89ca519c1ec29957f9c67b73 Mon Sep 17 00:00:00 2001 From: Jitse Boonstra Date: Sat, 18 Apr 2020 10:16:42 +0200 Subject: [PATCH] Some work to solve #70 --- .../net/jitse/npclib/internal/NPCBase.java | 75 ++++++++++++------- .../npclib/listeners/PlayerListener.java | 63 ++++------------ .../net/jitse/npclib/utilities/MathUtil.java | 9 +++ 3 files changed, 71 insertions(+), 76 deletions(-) create mode 100644 api/src/main/java/net/jitse/npclib/utilities/MathUtil.java diff --git a/api/src/main/java/net/jitse/npclib/internal/NPCBase.java b/api/src/main/java/net/jitse/npclib/internal/NPCBase.java index 304b0b5..3defe8b 100644 --- a/api/src/main/java/net/jitse/npclib/internal/NPCBase.java +++ b/api/src/main/java/net/jitse/npclib/internal/NPCBase.java @@ -16,6 +16,7 @@ import net.jitse.npclib.api.skin.Skin; import net.jitse.npclib.api.state.NPCSlot; import net.jitse.npclib.api.state.NPCState; import net.jitse.npclib.hologram.Hologram; +import net.jitse.npclib.utilities.MathUtil; import net.labymod.utilities.LMCUtils; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -102,7 +103,7 @@ public abstract class NPCBase implements NPC, NPCPacketHandler { } public void disableFOV() { - this.cosFOV = 0; // Or equals Math.cos(1/2 * Math.PI). + this.cosFOV = 0; } public void setFOV(double fov) { @@ -154,6 +155,26 @@ public abstract class NPCBase implements NPC, NPCPacketHandler { hasTeamRegistered.remove(player.getUniqueId()); } + public boolean inRangeOf(Player player) { + if (!player.getWorld().equals(location.getWorld())) { + // No need to continue our checks, they are in different worlds. + return false; + } + + // If Bukkit doesn't track the NPC entity anymore, bypass the hiding distance variable. + // This will cause issues otherwise (e.g. custom skin disappearing). + double hideDistance = instance.getAutoHideDistance(); + double distanceSquared = player.getLocation().distanceSquared(location); + double bukkitRange = Bukkit.getViewDistance() << 4; + + return distanceSquared <= MathUtil.square(hideDistance) && distanceSquared <= MathUtil.square(bukkitRange); + } + + public boolean inViewOf(Player player) { + Vector dir = location.toVector().subtract(player.getEyeLocation().toVector()).normalize(); + return dir.dot(player.getLocation().getDirection()) >= cosFOV; + } + @Override public void show(Player player) { show(player, false); @@ -166,46 +187,33 @@ public abstract class NPCBase implements NPC, NPCPacketHandler { return; } - if (!canSeeNPC(player)) { - if (!auto) { - shown.add(player.getUniqueId()); - } - - autoHidden.add(player.getUniqueId()); - return; + if (isShown(player)) { + throw new IllegalArgumentException("NPC is already shown to player"); } if (auto) { sendShowPackets(player); sendMetadataPacket(player); sendEquipmentPackets(player); + + // NPC is auto-shown now, we can remove the UUID from the set. + autoHidden.remove(player.getUniqueId()); } else { - if (isShown(player)) { - throw new IllegalStateException("Cannot call show method twice."); - } - - if (shown.contains(player.getUniqueId())) { - return; - } - + // Adding the UUID to the set. shown.add(player.getUniqueId()); - if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) - <= instance.getAutoHideDistance()) { + if (inRangeOf(player) && inViewOf(player)) { + // The player can see the NPC and is in range, send the packets. sendShowPackets(player); sendMetadataPacket(player); sendEquipmentPackets(player); } else { + // We'll wait until we can show the NPC to the player via auto-show. autoHidden.add(player.getUniqueId()); } } } - private boolean canSeeNPC(Player player) { - Vector dir = location.toVector().subtract(player.getEyeLocation().toVector()).normalize(); - return dir.dot(player.getLocation().getDirection()) >= cosFOV; - } - @Override public void hide(Player player) { hide(player, false); @@ -218,19 +226,28 @@ public abstract class NPCBase implements NPC, NPCPacketHandler { return; } + if (!shown.contains(player.getUniqueId())) { + throw new IllegalArgumentException("NPC cannot be hidden from player before calling NPC#show first"); + } + if (auto) { - sendHidePackets(player); - } else { - if (!shown.contains(player.getUniqueId())) { - throw new IllegalStateException("Cannot call hide method without calling NPC#show."); + if (autoHidden.contains(player.getUniqueId())) { + throw new IllegalStateException("NPC cannot be auto-hidden twice"); } + sendHidePackets(player); + + // NPC is auto-hidden now, we will add the UUID to the set. + autoHidden.add(player.getUniqueId()); + } else { + // Removing the UUID from the set. shown.remove(player.getUniqueId()); - if (player.getWorld().equals(location.getWorld()) && player.getLocation().distance(location) - <= instance.getAutoHideDistance()) { + if (inRangeOf(player)) { + // The player is in range of the NPC, send the packets. sendHidePackets(player); } else { + // We don't have to send any packets, just don't let it auto-show again by removing the UUID from the set. autoHidden.remove(player.getUniqueId()); } } 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 bf787c2..83a7fc9 100755 --- a/api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java +++ b/api/src/main/java/net/jitse/npclib/listeners/PlayerListener.java @@ -7,7 +7,6 @@ package net.jitse.npclib.listeners; import net.jitse.npclib.NPCLib; import net.jitse.npclib.internal.NPCBase; import net.jitse.npclib.internal.NPCManager; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; @@ -49,11 +48,8 @@ public class PlayerListener implements Listener { // Need to auto hide the NPCs from the player, or else the system will think they can see the NPC on respawn. Player player = event.getEntity(); for (NPCBase npc : NPCManager.getAllNPCs()) { - if (npc.getWorld().equals(player.getWorld())) { - if (!npc.getAutoHidden().contains(player.getUniqueId())) { - npc.getAutoHidden().add(player.getUniqueId()); - npc.hide(player, true); - } + if (npc.isShown(player) && npc.getWorld().equals(player.getWorld())) { + npc.hide(player, true); } } } @@ -83,13 +79,10 @@ public class PlayerListener implements Listener { Player player = event.getPlayer(); World from = event.getFrom(); - // The PlayerTeleportEvent is call, and will handle visibility in the new world. + // The PlayerTeleportEvent is called, and will handle visibility in the new world. for (NPCBase npc : NPCManager.getAllNPCs()) { - if (npc.getWorld().equals(from)) { - if (!npc.getAutoHidden().contains(player.getUniqueId())) { - npc.getAutoHidden().add(player.getUniqueId()); - npc.hide(player, true); - } + if (npc.isShown(player) && npc.getWorld().equals(from)) { + npc.hide(player, true); } } } @@ -98,13 +91,12 @@ public class PlayerListener implements Listener { public void onPlayerMove(PlayerMoveEvent event) { Location from = event.getFrom(); Location to = event.getTo(); - // 11/4/20: Added pitch and yaw to the if statement. If the change in this is 10 or more degrees, check the movement. - if (to == null || (Math.abs(from.getPitch() - to.getPitch()) <= 10 - || Math.abs(from.getYaw() - to.getYaw()) <= 10 - || from.getBlockX() != to.getBlockX() + // 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()); // Verify the player changed which block they are on. Since PlayerMoveEvent is one of the most called events, this is worth it. + || from.getBlockZ() != to.getBlockZ()) + handleMove(event.getPlayer()); } @EventHandler @@ -113,41 +105,18 @@ public class PlayerListener implements Listener { } private void handleMove(Player player) { - Location location = player.getLocation(); for (NPCBase npc : NPCManager.getAllNPCs()) { if (!npc.getShown().contains(player.getUniqueId())) { continue; // NPC was never supposed to be shown to the player. } - if (!npc.getWorld().equals(location.getWorld())) { - continue; // NPC is not in the same world. - } - - // If Bukkit doesn't track the NPC entity anymore, bypass the hiding distance variable. - // This will cause issues otherwise (e.g. custom skin disappearing). - double hideDistance = instance.getAutoHideDistance(); - double distanceSquared = location.distanceSquared(npc.getLocation()); - - int tempRange = Bukkit.getViewDistance() << 4; - boolean inRange = distanceSquared <= square(hideDistance) && distanceSquared <= square(tempRange); - if (npc.getAutoHidden().contains(player.getUniqueId())) { - // Check if the player and NPC are within the range to sendShowPackets it again. - if (inRange) { - npc.getAutoHidden().remove(player.getUniqueId()); - npc.show(player, true); - } - } else { - // Check if the player and NPC are out of range to sendHidePackets it. - if (!inRange) { - npc.getAutoHidden().add(player.getUniqueId()); - npc.hide(player, true); - } + 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); } } } - - // Avoiding Math.pow due to how resource intensive it is. - private double square(double val) { - return val * val; - } } diff --git a/api/src/main/java/net/jitse/npclib/utilities/MathUtil.java b/api/src/main/java/net/jitse/npclib/utilities/MathUtil.java new file mode 100644 index 0000000..1bee426 --- /dev/null +++ b/api/src/main/java/net/jitse/npclib/utilities/MathUtil.java @@ -0,0 +1,9 @@ +package net.jitse.npclib.utilities; + +public class MathUtil { + + // Avoiding Math.pow due to how resource intensive it is. + public static double square(double val) { + return val * val; + } +}