diff --git a/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch b/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch new file mode 100644 index 0000000000..b82e92ed2f --- /dev/null +++ b/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sat, 18 Jun 2016 23:22:12 -0400 +Subject: [PATCH] Delay Chunk Unloads based on Player Movement + +When players are moving in the world, doing things such as building or exploring, +they will commonly go back and forth in a small area. This causes a ton of chunk load +and unload activity on the edge chunks of their view distance. + +A simple back and forth movement in 6 blocks could spam a chunk to thrash a +loading and unload cycle over and over again. + +This is very wasteful. This system introduces a delay of inactivity on a chunk +before it actually unloads, which is maintained separately from ChunkGC. + +This allows servers with smaller worlds who do less long distance exploring to stop +wasting cpu cycles on saving/unloading/reloading chunks repeatedly. + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -0,0 +0,0 @@ public class PaperWorldConfig { + } + } + ++ public long delayChunkUnloadsBy; ++ private void delayChunkUnloadsBy() { ++ delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "30s")); ++ if (delayChunkUnloadsBy > 0) { ++ log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds"); ++ delayChunkUnloadsBy *= 1000; ++ } ++ } ++ + public boolean isHopperPushBased; + private void isHopperPushBased() { + isHopperPushBased = getBoolean("hopper.push-based", true); +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -0,0 +0,0 @@ public class Chunk { + public final World world; + public final int[] heightMap; + public final long chunkKey; // Paper ++ public Long scheduledForUnload; // Paper - delay chunk unloads + public final int locX; + public final int locZ; + private boolean m; +diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java ++++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java +@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { + } + } + } ++ // Paper start - delayed chunk unloads ++ long now = System.currentTimeMillis(); ++ long unloadAfter = world.paperConfig.delayChunkUnloadsBy; ++ if (unloadAfter > 0) { ++ //noinspection Convert2streamapi ++ for (Chunk chunk : chunks.values()) { ++ if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) { ++ chunk.scheduledForUnload = null; ++ unload(chunk); ++ } ++ } ++ } ++ // Paper end + + this.chunkLoader.a(); + } +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -0,0 +0,0 @@ public class PlayerChunk { + public void run() { + loadInProgress = false; + PlayerChunk.this.chunk = PlayerChunk.this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(location.x, location.z); ++ markChunkUsed(); // Paper - delay chunk unloads + } + }; ++ // Paper start - delay chunk unloads ++ public final void markChunkUsed() { ++ if (chunk != null && chunk.scheduledForUnload != null) { ++ chunk.scheduledForUnload = null; ++ } ++ } ++ // Paper end + // CraftBukkit end + + public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) { +@@ -0,0 +0,0 @@ public class PlayerChunk { + // CraftBukkit start + loadInProgress = true; + this.chunk = playerchunkmap.getWorld().getChunkProviderServer().getChunkAt(i, j, loadedRunnable, false); ++ markChunkUsed(); // Paper - delay chunk unloads + // CraftBukkit end + } + +@@ -0,0 +0,0 @@ public class PlayerChunk { + if (!loadInProgress) { + loadInProgress = true; + this.chunk = playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z, loadedRunnable, flag); ++ markChunkUsed(); // Paper - delay chunk unloads + } + // CraftBukkit end + +diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -0,0 +0,0 @@ public class PlayerChunkMap { + Chunk chunk = playerchunk.f(); + + if (chunk != null) { +- this.getWorld().getChunkProviderServer().unload(chunk); ++ // Paper start - delay chunk unloads ++ if (world.paperConfig.delayChunkUnloadsBy <= 0) { ++ this.getWorld().getChunkProviderServer().unload(chunk); ++ } else { ++ chunk.scheduledForUnload = System.currentTimeMillis(); ++ } ++ // Paper end + } + + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -0,0 +0,0 @@ public class CraftWorld implements World { + ChunkProviderServer cps = world.getChunkProviderServer(); + for (net.minecraft.server.Chunk chunk : cps.chunks.values()) { + // If in use, skip it +- if (isChunkInUse(chunk.locX, chunk.locZ)) { ++ if (isChunkInUse(chunk.locX, chunk.locZ) || chunk.scheduledForUnload != null) { // Paper - delayed chunk unloads + continue; + } + +-- \ No newline at end of file