From 38ea543b80111899f6f17b198dff6e5ccb9b7e1f Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Sun, 10 Oct 2021 22:33:30 +0200 Subject: [PATCH] Improves chunk unloading Adds all chunk unloading to a queue Adds a thread which unloads chunks Updates chunk unload requests such that a chunk won't be unloaded twice, and an old unloading request cannot unload a chunk too soon --- .../java/net/knarcraft/stargate/Stargate.java | 30 ++++++++++- .../container/ChunkUnloadRequest.java | 54 +++++++++++++++++++ .../net/knarcraft/stargate/portal/Portal.java | 19 ++----- .../stargate/thread/ChunkUnloadThread.java | 36 +++++++++++++ 4 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java create mode 100644 src/main/java/net/knarcraft/stargate/thread/ChunkUnloadThread.java diff --git a/src/main/java/net/knarcraft/stargate/Stargate.java b/src/main/java/net/knarcraft/stargate/Stargate.java index 1fea40c..dfb3ea7 100644 --- a/src/main/java/net/knarcraft/stargate/Stargate.java +++ b/src/main/java/net/knarcraft/stargate/Stargate.java @@ -3,6 +3,7 @@ package net.knarcraft.stargate; import net.knarcraft.stargate.command.CommandStarGate; import net.knarcraft.stargate.command.StarGateTabCompleter; import net.knarcraft.stargate.container.BlockChangeRequest; +import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.listener.BlockEventListener; import net.knarcraft.stargate.listener.BungeeCordListener; import net.knarcraft.stargate.listener.EntityEventListener; @@ -15,6 +16,7 @@ import net.knarcraft.stargate.portal.GateHandler; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.thread.BlockChangeThread; +import net.knarcraft.stargate.thread.ChunkUnloadThread; import net.knarcraft.stargate.thread.StarGateThread; import net.knarcraft.stargate.utility.EconomyHandler; import net.knarcraft.stargate.utility.FileHelper; @@ -31,6 +33,7 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.scheduler.BukkitScheduler; import java.io.File; import java.io.IOException; @@ -38,6 +41,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; +import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; @@ -50,6 +54,7 @@ public class Stargate extends JavaPlugin { public static final Queue blockChangeRequestQueue = new LinkedList<>(); public static final ConcurrentLinkedQueue openPortalsQueue = new ConcurrentLinkedQueue<>(); public static final ConcurrentLinkedQueue activePortalsQueue = new ConcurrentLinkedQueue<>(); + public static final Queue chunkUnloadQueue = new PriorityQueue<>(); //Amount of seconds before deactivating/closing portals private static final int activeTime = 10; @@ -334,8 +339,10 @@ public class Stargate extends JavaPlugin { setupVaultEconomy(); //Run necessary threads - getServer().getScheduler().runTaskTimer(this, new StarGateThread(), 0L, 100L); - getServer().getScheduler().runTaskTimer(this, new BlockChangeThread(), 0L, 1L); + BukkitScheduler scheduler = getServer().getScheduler(); + scheduler.runTaskTimer(this, new StarGateThread(), 0L, 100L); + scheduler.runTaskTimer(this, new BlockChangeThread(), 0L, 1L); + scheduler.runTaskTimer(this, new ChunkUnloadThread(), 0L, 100L); this.registerCommands(); } @@ -615,4 +622,23 @@ public class Stargate extends JavaPlugin { } } + /** + * Gets the chunk unload queue containing chunks to unload + * + * @return

The chunk unload queue

+ */ + public static Queue getChunkUnloadQueue() { + return chunkUnloadQueue; + } + + /** + * Adds a new chunk unload request to the chunk unload queue + * + * @param request

The new chunk unload request to add

+ */ + public static void addChunkUnloadRequest(ChunkUnloadRequest request) { + chunkUnloadQueue.removeIf((item) -> item.getChunkToUnload().equals(request.getChunkToUnload())); + chunkUnloadQueue.add(request); + } + } diff --git a/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java b/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java new file mode 100644 index 0000000..a2657ca --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java @@ -0,0 +1,54 @@ +package net.knarcraft.stargate.container; + +import org.bukkit.Chunk; +import org.jetbrains.annotations.NotNull; + +/** + * Requests the unloading of a chunk which has been previously loaded by the Stargate plugin + */ +public class ChunkUnloadRequest implements Comparable { + + private final Long unloadNanoTime; + private final Chunk chunkToUnload; + + /** + * Instantiates a new chunk unloading request + * + * @param chunkToUnload

The chunk to request the unloading of

+ * @param timeUntilUnload

The time in milliseconds to wait before unloading the chunk

+ */ + public ChunkUnloadRequest(Chunk chunkToUnload, Long timeUntilUnload) { + this.chunkToUnload = chunkToUnload; + long systemNanoTime = System.nanoTime(); + this.unloadNanoTime = systemNanoTime + (timeUntilUnload * 1000000); + } + + /** + * Gets the chunk to unload + * + * @return

The chunk to unload

+ */ + public Chunk getChunkToUnload() { + return this.chunkToUnload; + } + + /** + * Gets the time system nano time denoting at which time the unload request should be executed + * + * @return

The system nano time denoting when the chunk is to be unloaded

+ */ + public Long getUnloadNanoTime() { + return this.unloadNanoTime; + } + + @Override + public String toString() { + return "{" + chunkToUnload + ", " + unloadNanoTime + "}"; + } + + @Override + public int compareTo(@NotNull ChunkUnloadRequest otherRequest) { + return unloadNanoTime.compareTo(otherRequest.unloadNanoTime); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/portal/Portal.java b/src/main/java/net/knarcraft/stargate/portal/Portal.java index e656c9e..2f476c8 100644 --- a/src/main/java/net/knarcraft/stargate/portal/Portal.java +++ b/src/main/java/net/knarcraft/stargate/portal/Portal.java @@ -3,6 +3,7 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockLocation; +import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.event.StargateActivateEvent; import net.knarcraft.stargate.event.StargateCloseEvent; @@ -501,8 +502,6 @@ public class Portal { //Load chunks to make sure not to teleport to the void loadChunks(); - Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, - this::unloadChunks, 4000); //If no event is passed in, assume it's a teleport, and act as such if (event == null) { @@ -551,8 +550,6 @@ public class Portal { //Load chunks to make sure not to teleport to the void loadChunks(); - Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, - this::unloadChunks, 4000); if (!passengers.isEmpty()) { if (vehicle instanceof RideableMinecart || vehicle instanceof Boat) { @@ -628,7 +625,7 @@ public class Portal { * but there needs to be a delay between teleporting the vehicle and teleporting and adding the passenger.

* * @param targetVehicle

The vehicle to add the passenger to

- * @param passenger

The passenger to teleport and add

+ * @param passenger

The passenger to teleport and add

*/ private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger) { if (!passenger.teleport(targetVehicle.getLocation())) { @@ -770,21 +767,15 @@ public class Portal { return traveller; } - /** - * Unloads the chunks outside the portal's entrance - */ - private void unloadChunks() { - for (Chunk chunk : getChunksToLoad()) { - chunk.removePluginChunkTicket(Stargate.stargate); - } - } - /** * Loads the chunks outside the portal's entrance */ private void loadChunks() { for (Chunk chunk : getChunksToLoad()) { chunk.addPluginChunkTicket(Stargate.stargate); + //Allow the chunk to unload after 3 seconds + Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 3000L)); + Stargate.debug("loadChunks", "Added chunk unloading request for chunk " + chunk); } } diff --git a/src/main/java/net/knarcraft/stargate/thread/ChunkUnloadThread.java b/src/main/java/net/knarcraft/stargate/thread/ChunkUnloadThread.java new file mode 100644 index 0000000..49f0757 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/thread/ChunkUnloadThread.java @@ -0,0 +1,36 @@ +package net.knarcraft.stargate.thread; + +import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.container.ChunkUnloadRequest; +import org.bukkit.Chunk; + +import java.util.Queue; + +/** + * Unloads chunks which should no longer be forced to stay loaded + */ +public class ChunkUnloadThread implements Runnable { + + @Override + public void run() { + long systemNanoTime = System.nanoTime(); + Queue unloadQueue = Stargate.getChunkUnloadQueue(); + + //Peek at the first element to check if the chunk should be unloaded + ChunkUnloadRequest firstElement = unloadQueue.peek(); + if (firstElement != null) { + Stargate.debug("ChunkUnloadThread", "Found chunk unload request: " + firstElement); + Stargate.debug("ChunkUnloadThread", "Current time: " + systemNanoTime); + } + //Repeat until all un-loadable chunks have been processed + while (firstElement != null && firstElement.getUnloadNanoTime() < systemNanoTime) { + unloadQueue.remove(); + Chunk chunkToUnload = firstElement.getChunkToUnload(); + //Allow the chunk to be unloaded + chunkToUnload.removePluginChunkTicket(Stargate.stargate); + Stargate.debug("ChunkUnloadThread", "Unloaded chunk " + chunkToUnload); + firstElement = unloadQueue.peek(); + } + } + +}