From e7cba79e1a609cce5f8e1c05108f42bc6ddffff0 Mon Sep 17 00:00:00 2001 From: fullwall Date: Wed, 29 May 2019 16:45:18 +0800 Subject: [PATCH] Add a proper 1.14 solution for KEEP_CHUNKS_LOADED --- .../java/net/citizensnpcs/EventListen.java | 73 +++---------------- .../net/citizensnpcs/npc/CitizensNPC.java | 36 ++++++++- .../net/citizensnpcs/util/ChunkCoord.java | 69 ++++++++++++++++++ 3 files changed, 114 insertions(+), 64 deletions(-) create mode 100644 main/src/main/java/net/citizensnpcs/util/ChunkCoord.java diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index a4d0c6278..7df695b52 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -7,7 +7,6 @@ import java.util.Map.Entry; import java.util.UUID; import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.EntityType; @@ -93,6 +92,7 @@ import net.citizensnpcs.editor.Editor; import net.citizensnpcs.npc.skin.SkinUpdateTracker; import net.citizensnpcs.trait.Controllable; import net.citizensnpcs.trait.CurrentLocation; +import net.citizensnpcs.util.ChunkCoord; import net.citizensnpcs.util.Messages; import net.citizensnpcs.util.NMS; import net.citizensnpcs.util.Util; @@ -142,7 +142,7 @@ public class EventListen implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onChunkLoad(ChunkLoadEvent event) { - respawnAllFromCoord(toCoord(event.getChunk())); + respawnAllFromCoord(new ChunkCoord(event.getChunk())); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -150,7 +150,7 @@ public class EventListen implements Listener { Runnable runnable = new Runnable() { @Override public void run() { - ChunkCoord coord = toCoord(event.getChunk()); + ChunkCoord coord = new ChunkCoord(event.getChunk()); Location loc = new Location(null, 0, 0, 0); boolean loadChunk = false; for (NPC npc : getAllNPCs()) { @@ -161,14 +161,12 @@ public class EventListen implements Listener { if (!sameChunkCoordinates || !event.getWorld().equals(loc.getWorld())) continue; if (!npc.despawn(DespawnReason.CHUNK_UNLOAD)) { - try { - ((Cancellable) event).setCancelled(true); - } catch (Throwable e) { - // TODO: event.getChunk().setForceLoaded(true); ? + if (!(event instanceof Cancellable)) { loadChunk = true; toRespawn.put(coord, npc); continue; } + ((Cancellable) event).setCancelled(true); if (Messaging.isDebugging()) { Messaging.debug("Cancelled chunk unload at [" + coord.x + "," + coord.z + "]"); } @@ -193,10 +191,10 @@ public class EventListen implements Listener { } } }; - if (Util.getMinecraftRevision().contains("1_14")) { - Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), runnable); - } else { + if (event instanceof Cancellable) { runnable.run(); + } else { + Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), runnable); } } @@ -395,7 +393,7 @@ public class EventListen implements Listener { @EventHandler public void onNeedsRespawn(NPCNeedsRespawnEvent event) { - ChunkCoord coord = toCoord(event.getSpawnLocation()); + ChunkCoord coord = new ChunkCoord(event.getSpawnLocation()); if (toRespawn.containsEntry(coord, event.getNPC())) return; Messaging.debug("Stored", event.getNPC().getId(), "for respawn from NPCNeedsRespawnEvent"); @@ -409,7 +407,7 @@ public class EventListen implements Listener { Messaging.debug("Preventing further respawns of " + event.getNPC().getId() + " due to DespawnReason." + event.getReason().name()); if (event.getNPC().getStoredLocation() != null) { - toRespawn.remove(toCoord(event.getNPC().getStoredLocation()), event.getNPC()); + toRespawn.remove(new ChunkCoord(event.getNPC().getStoredLocation()), event.getNPC()); } } else { Messaging.debug("Removing " + event.getNPC().getId() + " from skin tracker due to DespawnReason." @@ -640,55 +638,6 @@ public class EventListen implements Listener { } private void storeForRespawn(NPC npc) { - toRespawn.put(toCoord(npc.getEntity().getLocation()), npc); - } - - private ChunkCoord toCoord(Chunk chunk) { - return new ChunkCoord(chunk); - } - - private ChunkCoord toCoord(Location loc) { - return new ChunkCoord(loc.getWorld().getUID(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4); - } - - private static class ChunkCoord { - private final UUID worldUUID; - private final int x; - private final int z; - - private ChunkCoord(Chunk chunk) { - this(chunk.getWorld().getUID(), chunk.getX(), chunk.getZ()); - } - - private ChunkCoord(UUID worldUUID, int x, int z) { - this.x = x; - this.z = z; - this.worldUUID = worldUUID; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ChunkCoord other = (ChunkCoord) obj; - if (worldUUID == null) { - if (other.worldUUID != null) { - return false; - } - } else if (!worldUUID.equals(other.worldUUID)) { - return false; - } - return x == other.x && z == other.z; - } - - @Override - public int hashCode() { - final int prime = 31; - return prime * (prime * (prime + ((worldUUID == null) ? 0 : worldUUID.hashCode())) + x) + z; - } + toRespawn.put(new ChunkCoord(npc.getEntity().getLocation()), npc); } } diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index ddf3e90ef..962c81bac 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -20,6 +20,8 @@ import org.bukkit.scoreboard.Team.OptionStatus; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; import net.citizensnpcs.NPCNeedsRespawnEvent; import net.citizensnpcs.Settings.Setting; @@ -42,11 +44,13 @@ import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.npc.ai.CitizensNavigator; import net.citizensnpcs.npc.skin.SkinnableEntity; import net.citizensnpcs.trait.CurrentLocation; +import net.citizensnpcs.util.ChunkCoord; import net.citizensnpcs.util.Messages; import net.citizensnpcs.util.NMS; import net.citizensnpcs.util.Util; public class CitizensNPC extends AbstractNPC { + private ChunkCoord cachedCoord; private EntityController entityController; private final CitizensNavigator navigator = new CitizensNavigator(this); private int updateCounter = 0; @@ -75,8 +79,7 @@ public class CitizensNPC extends AbstractNPC { } Bukkit.getPluginManager().callEvent(event); if (event.isCancelled() && reason != DespawnReason.DEATH) { - getEntity().getLocation().getChunk(); - Messaging.debug("Couldn't despawn", getId(), "due to despawn event cancellation. Force loaded chunk.", + Messaging.debug("Couldn't despawn", getId(), "due to despawn event cancellation. Will load chunk.", getEntity().isValid()); return false; } @@ -100,6 +103,12 @@ public class CitizensNPC extends AbstractNPC { return true; } + @Override + public void destroy() { + super.destroy(); + resetCachedCoord(); + } + @Override public void faceLocation(Location location) { if (!isSpawned()) @@ -149,6 +158,17 @@ public class CitizensNPC extends AbstractNPC { navigator.load(root.getRelative("navigator")); } + private void resetCachedCoord() { + if (cachedCoord != null) { + CHUNK_LOADERS.remove(NPC_METADATA_MARKER, CHUNK_LOADERS); + CHUNK_LOADERS.remove(cachedCoord, this); + if (CHUNK_LOADERS.get(cachedCoord).size() == 0) { + cachedCoord.setForceLoaded(false); + } + cachedCoord = null; + } + } + @Override public void save(DataKey root) { super.save(root); @@ -205,6 +225,7 @@ public class CitizensNPC extends AbstractNPC { data().get(NPC.DEFAULT_PROTECTED_METADATA, true); at = at.clone(); + getTrait(CurrentLocation.class).setLocation(at); entityController.spawn(at, this); @@ -282,6 +303,7 @@ public class CitizensNPC extends AbstractNPC { try { super.update(); if (!isSpawned()) { + resetCachedCoord(); return; } if (data().get(NPC.SWIMMING_METADATA, true)) { @@ -297,6 +319,15 @@ public class CitizensNPC extends AbstractNPC { } if (!getNavigator().isNavigating() && updateCounter++ > Setting.PACKET_UPDATE_DELAY.asInt()) { + if (Setting.KEEP_CHUNKS_LOADED.asBoolean()) { + ChunkCoord currentCoord = new ChunkCoord(getStoredLocation()); + if (!currentCoord.equals(cachedCoord)) { + resetCachedCoord(); + currentCoord.setForceLoaded(true); + CHUNK_LOADERS.put(currentCoord, this); + cachedCoord = currentCoord; + } + } updateCounter = 0; Player player = getEntity().getType() == EntityType.PLAYER ? (Player) getEntity() : null; @@ -385,6 +416,7 @@ public class CitizensNPC extends AbstractNPC { } } + private static final SetMultimap CHUNK_LOADERS = HashMultimap.create(); private static final String NPC_METADATA_MARKER = "NPC"; private static boolean SUPPORT_GLOWING = true; private static boolean SUPPORT_SILENT = true; diff --git a/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java new file mode 100644 index 000000000..ea81d15c3 --- /dev/null +++ b/main/src/main/java/net/citizensnpcs/util/ChunkCoord.java @@ -0,0 +1,69 @@ +package net.citizensnpcs.util; + +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; + +public class ChunkCoord { + public final UUID worldUUID; + public final int x; + public final int z; + + public ChunkCoord(Chunk chunk) { + this(chunk.getWorld().getUID(), chunk.getX(), chunk.getZ()); + } + + public ChunkCoord(Location loc) { + this(loc.getWorld().getUID(), loc.getBlockX() >> 4, loc.getBlockZ() >> 4); + } + + public ChunkCoord(UUID worldUUID, int x, int z) { + this.x = x; + this.z = z; + this.worldUUID = worldUUID; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ChunkCoord other = (ChunkCoord) obj; + if (worldUUID == null) { + if (other.worldUUID != null) { + return false; + } + } else if (!worldUUID.equals(other.worldUUID)) { + return false; + } + return x == other.x && z == other.z; + } + + public Chunk getChunk() { + return Bukkit.getWorld(worldUUID).getChunkAt(x, z); + } + + @Override + public int hashCode() { + final int prime = 31; + return prime * (prime * (prime + ((worldUUID == null) ? 0 : worldUUID.hashCode())) + x) + z; + } + + public void setForceLoaded(boolean b) { + Chunk chunk = getChunk(); + if (chunk != null && SUPPORTS_FORCE_LOADED) { + try { + chunk.setForceLoaded(b); + } catch (NoSuchMethodError e) { + SUPPORTS_FORCE_LOADED = false; + } + } + } + + private static boolean SUPPORTS_FORCE_LOADED = true; +} \ No newline at end of file