diff --git a/api/src/main/java/net/jitse/npclib/listeners/ChunkListener.java b/api/src/main/java/net/jitse/npclib/listeners/ChunkListener.java index 10d7902..29ce768 100755 --- a/api/src/main/java/net/jitse/npclib/listeners/ChunkListener.java +++ b/api/src/main/java/net/jitse/npclib/listeners/ChunkListener.java @@ -9,12 +9,14 @@ import net.jitse.npclib.internal.NPCManager; import net.jitse.npclib.internal.SimpleNPC; import org.bukkit.Bukkit; import org.bukkit.Chunk; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkUnloadEvent; +import java.util.Objects; import java.util.UUID; /** @@ -33,20 +35,18 @@ public class ChunkListener implements Listener { Chunk chunk = event.getChunk(); for (SimpleNPC npc : NPCManager.getAllNPCs()) { - Chunk npcChunk = npc.getLocation().getChunk(); + if (!isSameChunk(npc.getLocation(), chunk)) + continue; // We aren't unloading the chunk with the NPC in it. - if (chunk.equals(npcChunk)) { - // Unloaded chunk with NPC in it. Hiding it from all players currently shown to. - - for (UUID uuid : npc.getShown()) { - // Safety check so it doesn't send packets if the NPC has already - // been automatically despawned by the system. - if (npc.getAutoHidden().contains(uuid)) { - continue; - } - - npc.hide(Bukkit.getPlayer(uuid), true, true); + // We found an NPC in the chunk being unloaded. Time to hide this NPC from all players. + for (UUID uuid : npc.getShown()) { + // Safety check so it doesn't send packets if the NPC has already + // been automatically despawned by the system. + if (npc.getAutoHidden().contains(uuid)) { + continue; } + + npc.hide(Bukkit.getPlayer(uuid), true, true); } } } @@ -56,33 +56,45 @@ public class ChunkListener implements Listener { Chunk chunk = event.getChunk(); for (SimpleNPC npc : NPCManager.getAllNPCs()) { - Chunk npcChunk = npc.getLocation().getChunk(); + if (!isSameChunk(npc.getLocation(), chunk)) + continue; // The NPC is not in the loaded chunk. - if (chunk.equals(npcChunk)) { - // Loaded chunk with NPC in it. Showing it to the players again. + // The chunk being loaded has this NPC in it. Showing it to all the players again. + for (UUID uuid : npc.getShown()) { + // Make sure not to respawn a not-hidden NPC. + if (!npc.getAutoHidden().contains(uuid)) { + continue; + } - for (UUID uuid : npc.getShown()) { - // Make sure not to respawn a not-hidden NPC. - if (!npc.getAutoHidden().contains(uuid)) { - continue; - } + Player player = Bukkit.getPlayer(uuid); + if (player == null) + continue; // Couldn't find the player, so skip. - Player player = Bukkit.getPlayer(uuid); + if (!Objects.equals(npc.getWorld(), player.getWorld())) { + continue; // Player and NPC are not in the same world. + } - if (!npcChunk.getWorld().equals(player.getWorld())) { - continue; // Player and NPC are not in the same world. - } + double hideDistance = instance.getAutoHideDistance(); + double distanceSquared = player.getLocation().distanceSquared(npc.getLocation()); + boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4); - double hideDistance = instance.getAutoHideDistance(); - double distanceSquared = player.getLocation().distanceSquared(npc.getLocation()); - boolean inRange = distanceSquared <= (hideDistance * hideDistance) || distanceSquared <= (Bukkit.getViewDistance() << 4); - - // Show the NPC (if in range). - if (inRange) { - npc.show(player, true); - } + // Show the NPC (if in range). + if (inRange) { + npc.show(player, true); } } } } + + + private static int getChunkCoordinate(int coordinate) { + return coordinate >> 4; + } + + // Using Location#getChunk will load the chunk, which is a pretty hefty task, so we'll want to avoid it. + // Using Location#getChunk would effectively mean we'd load every chunk an NPC is located in when any chunk is unloaded. + private static boolean isSameChunk(Location loc, Chunk chunk) { + return getChunkCoordinate(loc.getBlockX()) == chunk.getX() + && getChunkCoordinate(loc.getBlockZ()) == chunk.getZ(); + } }