13
0
geforkt von Mirrors/Paper
Paper/Spigot-Server-Patches/Delay-Chunk-Unloads-based-on-Player-Movement.patch
Shane Freeder 8ef9dc94ca Updated Upstream (Bukkit/CraftBukkit)
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:
0969eedc Clarify furnace burn time behaviour as per SPIGOT-844
16453bfd SPIGOT-4503: Add API to insert complete ItemStack into Jukebox

CraftBukkit Changes:
dff66dfc Reduce copying of positions from block states
91cae6ef SPIGOT-4387: Durability looping from cancelled BlockPlaceEvent
24c5e68c SPIGOT-4493: Allow burnt out furnaces to remain lit like Vanilla whilst retaining SPIGOT-844 API
bc943daf Fix Jukebox API not synchronizing playing data with state
fe89a8c1 SPIGOT-4503: Add API to insert complete ItemStack into Jukebox
fc102494 Make CraftBlockState use BlockPosition
89ab4887 SPIGOT-4543: Jukebox playing calls should not use legacy data
6ff5a64c SPIGOT-4541: Cancelled bucket events require inventory update
2018-12-23 17:04:13 +00:00

222 Zeilen
10 KiB
Diff

From 0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 f6bfd6ece..7380c4386 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 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
@@ -0,0 +0,0 @@ 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 1d08ec37f..516a583a8 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 {
}
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 1d672eaa1..5497a458d 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 {
ChunkProviderServer chunkproviderserver = playerchunkmap.getWorld().getChunkProvider();
chunkproviderserver.a(ix, j);
this.chunk = chunkproviderserver.getChunkAt(ix, 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;
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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 3d17ad646..c3ac66d35 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().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 c484493b0..9a0e5342d 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -0,0 +0,0 @@ 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 ff1ccbd64..3e71cdcdc 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.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
@@ -0,0 +0,0 @@ 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;
}
}
--