mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2025-02-02 13:31:43 +01:00
Add a proper 1.14 solution for KEEP_CHUNKS_LOADED
This commit is contained in:
parent
489438b2f1
commit
e7cba79e1a
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<ChunkCoord, NPC> CHUNK_LOADERS = HashMultimap.create();
|
||||
private static final String NPC_METADATA_MARKER = "NPC";
|
||||
private static boolean SUPPORT_GLOWING = true;
|
||||
private static boolean SUPPORT_SILENT = true;
|
||||
|
69
main/src/main/java/net/citizensnpcs/util/ChunkCoord.java
Normal file
69
main/src/main/java/net/citizensnpcs/util/ChunkCoord.java
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user