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();
+ }
+ }
+
+}