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
This commit is contained in:
Kristian Knarvik 2021-10-10 22:33:30 +02:00
parent 69a62c921c
commit 38ea543b80
4 changed files with 123 additions and 16 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

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