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<BlockChangeRequest> blockChangeRequestQueue = new LinkedList<>(); public static final ConcurrentLinkedQueue<Portal> openPortalsQueue = new ConcurrentLinkedQueue<>(); public static final ConcurrentLinkedQueue<Portal> activePortalsQueue = new ConcurrentLinkedQueue<>(); + public static final Queue<ChunkUnloadRequest> 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 <p>The chunk unload queue</p> + */ + public static Queue<ChunkUnloadRequest> getChunkUnloadQueue() { + return chunkUnloadQueue; + } + + /** + * Adds a new chunk unload request to the chunk unload queue + * + * @param request <p>The new chunk unload request to add</p> + */ + 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<ChunkUnloadRequest> { + + private final Long unloadNanoTime; + private final Chunk chunkToUnload; + + /** + * Instantiates a new chunk unloading request + * + * @param chunkToUnload <p>The chunk to request the unloading of</p> + * @param timeUntilUnload <p>The time in milliseconds to wait before unloading the chunk</p> + */ + public ChunkUnloadRequest(Chunk chunkToUnload, Long timeUntilUnload) { + this.chunkToUnload = chunkToUnload; + long systemNanoTime = System.nanoTime(); + this.unloadNanoTime = systemNanoTime + (timeUntilUnload * 1000000); + } + + /** + * Gets the chunk to unload + * + * @return <p>The chunk to unload</p> + */ + public Chunk getChunkToUnload() { + return this.chunkToUnload; + } + + /** + * Gets the time system nano time denoting at which time the unload request should be executed + * + * @return <p>The system nano time denoting when the chunk is to be unloaded</p> + */ + 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.</p> * * @param targetVehicle <p>The vehicle to add the passenger to</p> - * @param passenger <p>The passenger to teleport and add</p> + * @param passenger <p>The passenger to teleport and add</p> */ 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<ChunkUnloadRequest> 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(); + } + } + +}