2018-10-01 07:00:09 +02:00
|
|
|
From 35769e706c68ba327d1264aacd7cd27c1df767fe Mon Sep 17 00:00:00 2001
|
2016-06-19 05:33:57 +02:00
|
|
|
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.
|
|
|
|
|
2018-10-01 02:34:05 +02:00
|
|
|
This also makes the Chunk GC System useless, by auto scheduling unload as soon as
|
|
|
|
a spare chunk is added to the server thats outside of view distance.
|
|
|
|
|
2016-06-19 05:33:57 +02:00
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
2018-09-26 06:57:59 +02:00
|
|
|
index ff1a2046f6..0cd15c17e8 100644
|
2016-06-19 05:33:57 +02:00
|
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
2018-09-26 06:57:59 +02:00
|
|
|
@@ -296,4 +296,18 @@ public class PaperWorldConfig {
|
2018-07-16 22:08:09 +02:00
|
|
|
preventTntFromMovingInWater = getBoolean("prevent-tnt-from-moving-in-water", false);
|
|
|
|
log("Prevent TNT from moving in water: " + preventTntFromMovingInWater);
|
2016-06-19 05:33:57 +02:00
|
|
|
}
|
2016-07-29 03:54:48 +02:00
|
|
|
+
|
2016-06-19 05:33:57 +02:00
|
|
|
+ public long delayChunkUnloadsBy;
|
|
|
|
+ private void delayChunkUnloadsBy() {
|
2016-07-29 03:57:36 +02:00
|
|
|
+ delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s"));
|
2016-06-19 05:33:57 +02:00
|
|
|
+ if (delayChunkUnloadsBy > 0) {
|
|
|
|
+ log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds");
|
|
|
|
+ delayChunkUnloadsBy *= 1000;
|
|
|
|
+ }
|
2018-05-30 22:31:55 +02:00
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public boolean skipEntityTickingInChunksScheduledForUnload = true;
|
|
|
|
+ private void skipEntityTickingInChunksScheduledForUnload() {
|
2018-05-30 22:38:27 +02:00
|
|
|
+ skipEntityTickingInChunksScheduledForUnload = getBoolean("skip-entity-ticking-in-chunks-scheduled-for-unload", skipEntityTickingInChunksScheduledForUnload);
|
2016-06-19 05:33:57 +02:00
|
|
|
+ }
|
2016-07-29 03:54:48 +02:00
|
|
|
}
|
2016-06-19 05:33:57 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
2018-09-29 01:31:59 +02:00
|
|
|
index 41d3aaa80b..824727ec66 100644
|
2016-06-19 05:33:57 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
2018-08-26 20:11:49 +02:00
|
|
|
@@ -37,6 +37,7 @@ public class Chunk implements IChunkAccess {
|
|
|
|
private boolean i;public boolean isLoaded() { return i; } // Paper - OBFHELPER
|
2016-06-19 05:33:57 +02:00
|
|
|
public final World world;
|
2018-07-16 22:08:09 +02:00
|
|
|
public final Map<HeightMap.Type, HeightMap> heightMap;
|
2016-06-19 05:33:57 +02:00
|
|
|
+ public Long scheduledForUnload; // Paper - delay chunk unloads
|
|
|
|
public final int locX;
|
|
|
|
public final int locZ;
|
Improve Light Queue and force enable it for all
There is no reason for the light queue to even be an option. This
enables the light queue for everyone.
This also improves the "can we still tick" time logic to always
check before running a light operation.
previously, we always executed at least 10 on the first world
(but not other worlds...), but we are seeing light take up some
heavy time, so improving that for now.
I've now also improved recheck gaps logic to happen at the end of all single block updates
This also prevents multiple gap checks, as previously if a tick skipped
the gaps check, the next tick would end up re-adding the entry again,
resulting in multiple gap checks.
This now just sets a marker "We need to recheck gaps" and will only occur
once.
This also should reduce chunk loads, as previously, we checked if
the neighbor chunks were loaded for the gap check, however those
neighbor chunks might of unloaded before the light queue operation
actually ran. Now, the neighbor chunk is done when the gap check
is being done, so it should avoid loading chunks.
Fixes #1466
Fixes #1431
2018-09-22 17:46:31 +02:00
|
|
|
private boolean l; public boolean needsGapCheck() { return l; } // Paper - OBFHELPER
|
2018-10-01 02:34:05 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
|
|
index df967ff07d..a3dc90fd2b 100644
|
|
|
|
--- a/src/main/java/net/minecraft/server/ChunkMap.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
|
|
@@ -48,6 +48,15 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
+ // Paper start - if this is a spare chunk (not part of any players view distance), go ahead and queue it for unload.
|
|
|
|
+ if (!((WorldServer)chunk.world).getPlayerChunkMap().isChunkInUse(chunk.locX, chunk.locZ)) {
|
|
|
|
+ if (chunk.world.paperConfig.delayChunkUnloadsBy > 0) {
|
|
|
|
+ chunk.scheduledForUnload = System.currentTimeMillis();
|
|
|
|
+ } else {
|
|
|
|
+ ((WorldServer) chunk.world).getChunkProviderServer().unload(chunk);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // Paper end
|
|
|
|
chunk.world.timings.syncChunkLoadPostTimer.stopTiming(); // Paper
|
|
|
|
// CraftBukkit end
|
|
|
|
|
2016-06-19 05:33:57 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
2018-09-27 04:35:42 +02:00
|
|
|
index 2d10f4aa37..719d5deb2c 100644
|
2016-06-19 05:33:57 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
2018-08-27 06:15:12 +02:00
|
|
|
@@ -306,6 +306,19 @@ public class ChunkProviderServer implements IChunkProvider {
|
2018-08-26 20:11:49 +02:00
|
|
|
}
|
2017-01-31 05:33:54 +01:00
|
|
|
activityAccountant.endActivity(); // Spigot
|
2016-06-19 05:33:57 +02:00
|
|
|
}
|
|
|
|
+ // 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
|
|
|
|
|
2018-08-26 20:11:49 +02:00
|
|
|
this.chunkScheduler.a(booleansupplier);
|
|
|
|
}
|
2016-06-19 05:33:57 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
2018-10-01 07:00:09 +02:00
|
|
|
index ac0e90eeca..2fd8fa30ee 100644
|
2016-06-19 05:33:57 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
2018-10-01 02:34:05 +02:00
|
|
|
@@ -33,8 +33,22 @@ public class PlayerChunk {
|
2016-06-19 05:33:57 +02:00
|
|
|
public void run() {
|
|
|
|
loadInProgress = false;
|
2018-08-26 20:11:49 +02:00
|
|
|
PlayerChunk.this.chunk = PlayerChunk.this.playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(location.x, location.z, true, true);
|
2016-06-19 05:33:57 +02:00
|
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
|
|
}
|
|
|
|
};
|
|
|
|
+ // Paper start - delay chunk unloads
|
2018-10-01 02:34:05 +02:00
|
|
|
+ private void markChunkUsed() {
|
|
|
|
+ if (chunk == null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
2018-10-01 07:00:09 +02:00
|
|
|
+ if (chunkHasPlayers) {
|
2016-06-19 05:33:57 +02:00
|
|
|
+ chunk.scheduledForUnload = null;
|
2018-10-01 02:34:05 +02:00
|
|
|
+ } else if (chunk.scheduledForUnload == null) {
|
|
|
|
+ chunk.scheduledForUnload = System.currentTimeMillis();
|
2016-06-19 05:33:57 +02:00
|
|
|
+ }
|
|
|
|
+ }
|
2018-10-01 07:00:09 +02:00
|
|
|
+ private boolean chunkHasPlayers = false;
|
2016-06-19 05:33:57 +02:00
|
|
|
+ // Paper end
|
|
|
|
// CraftBukkit end
|
|
|
|
|
|
|
|
public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) {
|
2018-10-01 02:34:05 +02:00
|
|
|
@@ -44,6 +58,7 @@ public class PlayerChunk {
|
2018-08-26 20:11:49 +02:00
|
|
|
|
|
|
|
chunkproviderserver.a(i, j);
|
|
|
|
this.chunk = chunkproviderserver.getChunkAt(i, j, true, false);
|
2016-06-19 05:33:57 +02:00
|
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
|
|
}
|
|
|
|
|
2018-07-16 22:08:09 +02:00
|
|
|
public ChunkCoordIntPair a() {
|
2018-10-01 02:34:05 +02:00
|
|
|
@@ -56,6 +71,8 @@ public class PlayerChunk {
|
|
|
|
} else {
|
|
|
|
if (this.c.isEmpty()) {
|
|
|
|
this.i = this.playerChunkMap.getWorld().getTime();
|
2018-10-01 07:00:09 +02:00
|
|
|
+ chunkHasPlayers = true; // Paper - delay chunk unloads
|
2018-10-01 02:34:05 +02:00
|
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
|
|
}
|
|
|
|
|
|
|
|
this.c.add(entityplayer);
|
|
|
|
@@ -74,6 +91,8 @@ public class PlayerChunk {
|
|
|
|
|
|
|
|
this.c.remove(entityplayer);
|
|
|
|
if (this.c.isEmpty()) {
|
2018-10-01 07:00:09 +02:00
|
|
|
+ chunkHasPlayers = false; // Paper - delay chunk unloads
|
2018-10-01 02:34:05 +02:00
|
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
|
|
this.playerChunkMap.b(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -85,6 +104,7 @@ public class PlayerChunk {
|
2018-08-26 20:11:49 +02:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z, true, flag);
|
|
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
2018-07-16 22:08:09 +02:00
|
|
|
return this.chunk != null;
|
2018-08-26 20:11:49 +02:00
|
|
|
}
|
|
|
|
}
|
2016-06-19 05:33:57 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
Improve Light Queue and force enable it for all
There is no reason for the light queue to even be an option. This
enables the light queue for everyone.
This also improves the "can we still tick" time logic to always
check before running a light operation.
previously, we always executed at least 10 on the first world
(but not other worlds...), but we are seeing light take up some
heavy time, so improving that for now.
I've now also improved recheck gaps logic to happen at the end of all single block updates
This also prevents multiple gap checks, as previously if a tick skipped
the gaps check, the next tick would end up re-adding the entry again,
resulting in multiple gap checks.
This now just sets a marker "We need to recheck gaps" and will only occur
once.
This also should reduce chunk loads, as previously, we checked if
the neighbor chunks were loaded for the gap check, however those
neighbor chunks might of unloaded before the light queue operation
actually ran. Now, the neighbor chunk is done when the gap check
is being done, so it should avoid loading chunks.
Fixes #1466
Fixes #1431
2018-09-22 17:46:31 +02:00
|
|
|
index a69d510dd1..7b67fa3208 100644
|
2016-06-19 05:33:57 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
2018-09-09 20:38:27 +02:00
|
|
|
@@ -460,7 +460,13 @@ public class PlayerChunkMap {
|
2016-06-19 05:33:57 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2018-05-30 22:31:55 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
Improve Light Queue and force enable it for all
There is no reason for the light queue to even be an option. This
enables the light queue for everyone.
This also improves the "can we still tick" time logic to always
check before running a light operation.
previously, we always executed at least 10 on the first world
(but not other worlds...), but we are seeing light take up some
heavy time, so improving that for now.
I've now also improved recheck gaps logic to happen at the end of all single block updates
This also prevents multiple gap checks, as previously if a tick skipped
the gaps check, the next tick would end up re-adding the entry again,
resulting in multiple gap checks.
This now just sets a marker "We need to recheck gaps" and will only occur
once.
This also should reduce chunk loads, as previously, we checked if
the neighbor chunks were loaded for the gap check, however those
neighbor chunks might of unloaded before the light queue operation
actually ran. Now, the neighbor chunk is done when the gap check
is being done, so it should avoid loading chunks.
Fixes #1466
Fixes #1431
2018-09-22 17:46:31 +02:00
|
|
|
index 785f020652..d31101861c 100644
|
2018-05-30 22:31:55 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
2018-09-18 03:50:02 +02:00
|
|
|
@@ -1342,7 +1342,13 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
|
2018-08-26 20:11:49 +02:00
|
|
|
if (!tileentity.x() && tileentity.hasWorld()) {
|
2018-05-30 22:31:55 +02:00
|
|
|
BlockPosition blockposition = tileentity.getPosition();
|
|
|
|
|
2018-07-16 22:08:09 +02:00
|
|
|
- if (this.isLoaded(blockposition) && this.K.a(blockposition)) {
|
2018-05-30 22:31:55 +02:00
|
|
|
+ // Paper start - Skip ticking in chunks scheduled for unload
|
|
|
|
+ net.minecraft.server.Chunk chunk = this.getChunkIfLoaded(blockposition);
|
|
|
|
+ boolean shouldTick = chunk != null;
|
|
|
|
+ if(this.paperConfig.skipEntityTickingInChunksScheduledForUnload)
|
2018-08-26 20:11:49 +02:00
|
|
|
+ shouldTick = shouldTick && chunk.scheduledForUnload == null;
|
2018-07-16 22:08:09 +02:00
|
|
|
+ if (shouldTick && this.K.a(blockposition)) {
|
2018-05-30 22:31:55 +02:00
|
|
|
+ // Paper end
|
|
|
|
try {
|
|
|
|
this.methodProfiler.a(() -> {
|
2018-07-16 22:08:09 +02:00
|
|
|
return String.valueOf(TileEntityTypes.a(tileentity.C()));
|
2016-06-19 05:33:57 +02:00
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
2018-09-27 04:35:42 +02:00
|
|
|
index 72d1a0f51d..3536433d14 100644
|
2016-06-19 05:33:57 +02:00
|
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
2018-09-27 04:35:42 +02:00
|
|
|
@@ -1633,7 +1633,7 @@ public class CraftWorld implements World {
|
2016-06-19 05:33:57 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-05-30 22:31:55 +02:00
|
|
|
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
Improve Light Queue and force enable it for all
There is no reason for the light queue to even be an option. This
enables the light queue for everyone.
This also improves the "can we still tick" time logic to always
check before running a light operation.
previously, we always executed at least 10 on the first world
(but not other worlds...), but we are seeing light take up some
heavy time, so improving that for now.
I've now also improved recheck gaps logic to happen at the end of all single block updates
This also prevents multiple gap checks, as previously if a tick skipped
the gaps check, the next tick would end up re-adding the entry again,
resulting in multiple gap checks.
This now just sets a marker "We need to recheck gaps" and will only occur
once.
This also should reduce chunk loads, as previously, we checked if
the neighbor chunks were loaded for the gap check, however those
neighbor chunks might of unloaded before the light queue operation
actually ran. Now, the neighbor chunk is done when the gap check
is being done, so it should avoid loading chunks.
Fixes #1466
Fixes #1431
2018-09-22 17:46:31 +02:00
|
|
|
index 12040596df..f9bb19fed6 100644
|
2018-05-30 22:31:55 +02:00
|
|
|
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
|
|
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
2018-08-26 20:11:49 +02:00
|
|
|
@@ -284,6 +284,11 @@ public class ActivationRange
|
2018-05-30 22:31:55 +02:00
|
|
|
{
|
|
|
|
isActive = false;
|
|
|
|
}
|
|
|
|
+ // Paper start - Skip ticking in chunks scheduled for unload
|
2018-08-26 20:11:49 +02:00
|
|
|
+ else if (entity.world.paperConfig.skipEntityTickingInChunksScheduledForUnload && (chunk == null || chunk.scheduledForUnload != null)) {
|
2018-05-30 22:31:55 +02:00
|
|
|
+ isActive = false;
|
2018-08-26 20:11:49 +02:00
|
|
|
+ }
|
2018-05-30 22:31:55 +02:00
|
|
|
+ // Paper end
|
|
|
|
return isActive;
|
|
|
|
}
|
|
|
|
}
|
2016-06-19 05:33:57 +02:00
|
|
|
--
|
2018-09-15 18:10:26 +02:00
|
|
|
2.19.0
|
2016-06-19 05:33:57 +02:00
|
|
|
|