aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-Server-Patches/0146-Delay-Chunk-Unloads-based-on-Player-Movement.patch
blob: 8ef94d9ada5d37a6d038fb2c1fa64cdd0dfd8d4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
From a2a8ba613f501d788f0a391e52906209e2b259d1 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 894d40662..04861f146 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -345,4 +345,13 @@ public class PaperWorldConfig {
     private void isHopperPushBased() {
         isHopperPushBased = getBoolean("hopper.push-based", false);
     }
+
+    public long delayChunkUnloadsBy;
+    private void delayChunkUnloadsBy() {
+        delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s"));
+        if (delayChunkUnloadsBy > 0) {
+            log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds");
+            delayChunkUnloadsBy *= 1000;
+        }
+    }
 }
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index 98f2cff15..88437d77a 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -30,6 +30,7 @@ public class Chunk {
     private boolean j;
     public final World world;
     public final int[] heightMap;
+    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 57ae50d2e..0a8e09e5e 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -315,6 +315,19 @@ public class ChunkProviderServer implements IChunkProvider {
 
                 activityAccountant.endActivity(); // Spigot
             }
+            // 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 dd40e98c8..f109e986d 100644
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
@@ -32,8 +32,16 @@ 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) {
@@ -42,6 +50,7 @@ public class PlayerChunk {
         // CraftBukkit start
         loadInProgress = true;
         this.chunk = playerchunkmap.getWorld().getChunkProviderServer().getChunkAt(i, j, loadedRunnable, false);
+        markChunkUsed(); // Paper - delay chunk unloads
         // CraftBukkit end
     }
 
@@ -110,6 +119,7 @@ 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 5b27bd1c5..b163346be 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -487,7 +487,13 @@ 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 9965235ab..3f8859a1f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1558,7 +1558,7 @@ 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;
             }
 
-- 
2.12.0.windows.1