geforkt von Mirrors/Paper
b9badbf2bc
Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 79f6ba25 Skip tests on default builds b85cc32f Further discourage World.regenerateChunk CraftBukkit Changes:7560d3b5
Skip tests on default buildsd0a9130d
SPIGOT-4563: Fix regenerating chunks saved to disk already Spigot Changes: 8173d06f Remove need for redundant second clone of repositories 8ede0393 Rebuild patches
225 Zeilen
10 KiB
Diff
225 Zeilen
10 KiB
Diff
From e9a7436242095e39bf7c216ead3291c98cb26689 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.
|
|
|
|
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.
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
index 42d951554..d8f258105 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -301,4 +301,18 @@ public class PaperWorldConfig {
|
|
preventTntFromMovingInWater = getBoolean("prevent-tnt-from-moving-in-water", false);
|
|
log("Prevent TNT from moving in water: " + preventTntFromMovingInWater);
|
|
}
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean skipEntityTickingInChunksScheduledForUnload = true;
|
|
+ private void skipEntityTickingInChunksScheduledForUnload() {
|
|
+ skipEntityTickingInChunksScheduledForUnload = getBoolean("skip-entity-ticking-in-chunks-scheduled-for-unload", skipEntityTickingInChunksScheduledForUnload);
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
|
index 0421f8f95..ddccdc939 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -40,6 +40,7 @@ public class Chunk implements IChunkAccess {
|
|
private boolean i;public boolean isLoaded() { return i; } // Paper - OBFHELPER
|
|
public final World world;
|
|
public final Map<HeightMap.Type, HeightMap> heightMap;
|
|
+ public Long scheduledForUnload; // Paper - delay chunk unloads
|
|
public final int locX;
|
|
public final int locZ;
|
|
private boolean l;
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
index 8b3738c8f..2021c0d02 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).getChunkProvider().unload(chunk);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
chunk.world.timings.syncChunkLoadPostTimer.stopTiming(); // Paper
|
|
// CraftBukkit end
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index c54df4583..d0bf0f72d 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -307,6 +307,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.chunkScheduler.a(booleansupplier);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
index e47aae2f8..b9d90c4fb 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -29,8 +29,23 @@ public class PlayerChunk {
|
|
|
|
chunkproviderserver.a(i, j);
|
|
this.chunk = chunkproviderserver.getChunkAt(i, j, true, false);
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
}
|
|
|
|
+ // Paper start
|
|
+ private void markChunkUsed() {
|
|
+ if (chunk == null) {
|
|
+ return;
|
|
+ }
|
|
+ if (chunkHasPlayers) {
|
|
+ chunk.scheduledForUnload = null;
|
|
+ } else if (chunk.scheduledForUnload == null) {
|
|
+ chunk.scheduledForUnload = System.currentTimeMillis();
|
|
+ }
|
|
+ }
|
|
+ private boolean chunkHasPlayers = false;
|
|
+ // Paper end
|
|
+
|
|
public ChunkCoordIntPair a() {
|
|
return this.location;
|
|
}
|
|
@@ -41,6 +56,8 @@ public class PlayerChunk {
|
|
} else {
|
|
if (this.players.isEmpty()) {
|
|
this.i = this.playerChunkMap.getWorld().getTime();
|
|
+ chunkHasPlayers = true; // Paper - delay chunk unloads
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
}
|
|
|
|
this.players.add(entityplayer);
|
|
@@ -59,6 +76,8 @@ public class PlayerChunk {
|
|
|
|
this.players.remove(entityplayer);
|
|
if (this.players.isEmpty()) {
|
|
+ chunkHasPlayers = false; // Paper - delay chunk unloads
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
this.playerChunkMap.b(this);
|
|
}
|
|
|
|
@@ -70,6 +89,7 @@ public class PlayerChunk {
|
|
return true;
|
|
} else {
|
|
this.chunk = this.playerChunkMap.getWorld().getChunkProvider().getChunkAt(this.location.x, this.location.z, true, flag);
|
|
+ markChunkUsed(); // Paper - delay chunk unloads
|
|
return this.chunk != null;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index 206457650..ab4f3b722 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -452,7 +452,13 @@ public class PlayerChunkMap {
|
|
Chunk chunk = playerchunk.f();
|
|
|
|
if (chunk != null) {
|
|
- this.getWorld().getChunkProvider().unload(chunk);
|
|
+ // Paper start - delay chunk unloads
|
|
+ if (world.paperConfig.delayChunkUnloadsBy <= 0) {
|
|
+ this.getWorld().getChunkProvider().unload(chunk);
|
|
+ } else {
|
|
+ chunk.scheduledForUnload = System.currentTimeMillis();
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index b51415e9b..1ffa3f0aa 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -1287,7 +1287,13 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
|
|
if (!tileentity.x() && tileentity.hasWorld()) {
|
|
BlockPosition blockposition = tileentity.getPosition();
|
|
|
|
- if (this.isLoaded(blockposition) && this.K.a(blockposition)) {
|
|
+ // 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)
|
|
+ shouldTick = shouldTick && chunk.scheduledForUnload == null;
|
|
+ if (shouldTick && this.K.a(blockposition)) {
|
|
+ // Paper end
|
|
try {
|
|
this.methodProfiler.a(() -> {
|
|
return String.valueOf(TileEntityTypes.a(tileentity.C()));
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 274e8626d..cecdc5388 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -1772,7 +1772,7 @@ public class CraftWorld implements World {
|
|
ChunkProviderServer cps = world.getChunkProvider();
|
|
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;
|
|
}
|
|
|
|
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
|
index d08ef3fe1..081789a8f 100644
|
|
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
|
@@ -323,6 +323,11 @@ public class ActivationRange
|
|
{
|
|
isActive = false;
|
|
}
|
|
+ // Paper start - Skip ticking in chunks scheduled for unload
|
|
+ else if (entity.world.paperConfig.skipEntityTickingInChunksScheduledForUnload && (chunk == null || chunk.scheduledForUnload != null)) {
|
|
+ isActive = false;
|
|
+ }
|
|
+ // Paper end
|
|
return isActive;
|
|
}
|
|
}
|
|
--
|
|
2.20.1
|
|
|