From 3b3e38fd01309eb9a9144731194119936b8f24ed Mon Sep 17 00:00:00 2001 From: Aikar Date: Thu, 9 Apr 2020 20:45:46 -0400 Subject: [PATCH] Fix issue with loading chunk during ticking chunks issue Also speed up performance of copying pending/updating to visible by directly copying the internal array data instead of putAll --- ...essing-of-chunk-loads-and-generation.patch | 132 +++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/Spigot-Server-Patches/0462-Speed-up-processing-of-chunk-loads-and-generation.patch b/Spigot-Server-Patches/0462-Speed-up-processing-of-chunk-loads-and-generation.patch index f8c08ca721..bb5af760ea 100644 --- a/Spigot-Server-Patches/0462-Speed-up-processing-of-chunk-loads-and-generation.patch +++ b/Spigot-Server-Patches/0462-Speed-up-processing-of-chunk-loads-and-generation.patch @@ -1,4 +1,4 @@ -From 78cfc2701151f762cf58e4fb0390ecc680ae71df Mon Sep 17 00:00:00 2001 +From ae2e2e396fefd177b43f3c44db54bceafaf07eb1 Mon Sep 17 00:00:00 2001 From: Aikar Date: Thu, 9 Apr 2020 00:09:26 -0400 Subject: [PATCH] Speed up processing of chunk loads and generation @@ -44,6 +44,46 @@ index 69e26a8267..434833d50e 100644 public static final Timing playerListTimer = Timings.ofSafe("Player List"); public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions"); public static final Timing connectionTimer = Timings.ofSafe("Connection Handler"); +diff --git a/src/main/java/com/destroystokyo/paper/util/map/Long2ObjectLinkedOpenHashMapFastCopy.java b/src/main/java/com/destroystokyo/paper/util/map/Long2ObjectLinkedOpenHashMapFastCopy.java +new file mode 100644 +index 0000000000..99e2041bd9 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/util/map/Long2ObjectLinkedOpenHashMapFastCopy.java +@@ -0,0 +1,34 @@ ++package com.destroystokyo.paper.util.map; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; ++ ++public class Long2ObjectLinkedOpenHashMapFastCopy extends Long2ObjectLinkedOpenHashMap { ++ ++ public void copyFrom(Long2ObjectLinkedOpenHashMapFastCopy map) { ++ if (key.length < map.key.length) { ++ key = null; ++ key = new long[map.key.length]; ++ } ++ if (value.length < map.value.length) { ++ value = null; ++ //noinspection unchecked ++ value = (V[]) new Object[map.value.length]; ++ } ++ if (link.length < map.link.length) { ++ link = null; ++ link = new long[map.link.length]; ++ } ++ System.arraycopy(map.key, 0, this.key, 0, map.key.length); ++ System.arraycopy(map.value, 0, this.value, 0, map.value.length); ++ System.arraycopy(map.link, 0, this.link, 0, map.link.length); ++ this.size = map.size; ++ this.mask = map.mask; ++ this.first = map.first; ++ this.last = map.last; ++ this.n = map.n; ++ this.maxFill = map.maxFill; ++ if (map.containsKey(null)) { ++ this.put(null, map.get(null)); ++ } ++ } ++} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index e627440c41..15450f36a4 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -164,10 +204,96 @@ index 06c395000f..936434110c 100644 protected TickTask postToMainThread(Runnable runnable) { return new TickTask(this.ticks, runnable); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index ce645a69bd..c21a3ea506 100644 +index ce645a69bd..32fed8a39a 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -785,7 +785,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -55,8 +55,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + + private static final Logger LOGGER = LogManager.getLogger(); + public static final int GOLDEN_TICKET = 33 + ChunkStatus.b(); +- public final Long2ObjectLinkedOpenHashMap updatingChunks = new Long2ObjectLinkedOpenHashMap(); +- public final Long2ObjectLinkedOpenHashMap visibleChunks = new Long2ObjectLinkedOpenHashMap(); // Paper - remove copying, make mt safe ++ public final com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy updatingChunks = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<>(); ++ public final com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy visibleChunks = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<>(); // Paper - remove copying, make mt safe ++ public final com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy pendingVisibleChunks = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<>(); // Paper - remove copying, this is used if the visible chunks is updated while iterating only + public transient Long2ObjectLinkedOpenHashMap visibleChunksClone; // Paper - remove copying, make mt safe + private final Long2ObjectLinkedOpenHashMap pendingUnload; + final LongSet loadedChunks; // Paper - private -> package +@@ -266,10 +267,10 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + // Paper start - remove cloning of visible chunks unless accessed as a collection async + private static final boolean DEBUG_ASYNC_VISIBLE_CHUNKS = Boolean.getBoolean("paper.debug-async-visible-chunks"); + private boolean isIterating = false; ++ private boolean hasPendingVisibleUpdate = false; + public void forEachVisibleChunk(java.util.function.Consumer consumer) { + org.spigotmc.AsyncCatcher.catchOp("forEachVisibleChunk"); + boolean prev = isIterating; +- boolean wasUpdating = updatingChunksModified; + isIterating = true; + try { + for (PlayerChunk value : this.visibleChunks.values()) { +@@ -277,8 +278,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + } + } finally { + this.isIterating = prev; +- if (!this.isIterating && updatingChunksModified && !wasUpdating) { +- this.updateVisibleChunks(); ++ if (!this.isIterating && hasPendingVisibleUpdate) { ++ this.visibleChunks.copyFrom(this.pendingVisibleChunks); ++ this.pendingVisibleChunks.clear(); + } + } + } +@@ -289,7 +291,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + synchronized (this.visibleChunks) { + if (DEBUG_ASYNC_VISIBLE_CHUNKS) new Throwable("Async getVisibleChunks").printStackTrace(); + if (this.visibleChunksClone == null) { +- this.visibleChunksClone = this.visibleChunks.clone(); ++ this.visibleChunksClone = this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.clone() : this.visibleChunks.clone(); + } + return this.visibleChunksClone; + } +@@ -302,11 +304,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + // Paper start - mt safe get + if (Thread.currentThread() != this.world.serverThread) { + synchronized (this.visibleChunks) { +- return (PlayerChunk) this.visibleChunks.get(i); ++ return (PlayerChunk) (this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(i) : this.visibleChunks.get(i)); + } + } ++ return (PlayerChunk) (this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(i) : this.visibleChunks.get(i)); + // Paper end +- return (PlayerChunk) this.visibleChunks.get(i); + } + + protected IntSupplier c(long i) { +@@ -682,16 +684,21 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + }); + } + +- protected boolean updateVisibleChunks() { return b(); } // Paper - OBFHELPER + protected boolean b() { +- if (!this.updatingChunksModified || this.isIterating) { // Paper ++ if (!this.updatingChunksModified) { + return false; + } else { + // Paper start - stop cloning visibleChunks + synchronized (this.visibleChunks) { +- this.visibleChunks.clear(); +- this.visibleChunks.putAll(this.updatingChunks); +- this.visibleChunksClone = null; ++ if (isIterating) { ++ hasPendingVisibleUpdate = true; ++ this.pendingVisibleChunks.copyFrom(this.updatingChunks); ++ } else { ++ hasPendingVisibleUpdate = false; ++ this.pendingVisibleChunks.clear(); ++ this.visibleChunks.copyFrom(this.updatingChunks); ++ this.visibleChunksClone = null; ++ } + } + // Paper end + +@@ -785,7 +792,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { CompletableFuture> ret = new CompletableFuture<>(); Consumer chunkHolderConsumer = (ChunkRegionLoader.InProgressChunkHolder holder) -> {