From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 21 Mar 2021 11:22:10 -0700
Subject: [PATCH] Do not copy visible chunks

For servers with a lot of chunk holders, copying for each
tickDistanceManager call can take up quite a bit in
the function. I saw approximately 1/3rd of the function
on the copy.

diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
index 848eb25ed0640db61a0f28bc26ddabd0444e9ed4..ec2b9995f1bf0f6cf029df7bfac526c2672acf3a 100644
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
@@ -407,7 +407,7 @@ public class PaperCommand extends Command {
             int ticking = 0;
             int entityTicking = 0;
 
-            for (PlayerChunk chunk : world.getChunkProvider().playerChunkMap.updatingChunks.values()) {
+            for (PlayerChunk chunk : world.getChunkProvider().playerChunkMap.updatingChunks.getUpdatingMap().values()) { // Tuinity - change updating chunks map
                 if (chunk.getFullChunkIfCached() == null) {
                     continue;
                 }
diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
index d7eede51f1c4ebbe8e00b16efd6331c87db53bb4..b28995ecfd7f45e6b6197be96c418aa0d05d3383 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
@@ -116,8 +116,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();
     // Paper start - faster copying
-    public final Long2ObjectLinkedOpenHashMap<PlayerChunk> updatingChunks = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<>(); // Paper - faster copying
-    public final Long2ObjectLinkedOpenHashMap<PlayerChunk> visibleChunks = new ProtectedVisibleChunksMap(); // Paper - faster copying
+    // Tuinity start - Don't copy
+    public final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<PlayerChunk> updatingChunks = new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>();
+    // Tuinity end - Don't copy
 
     private class ProtectedVisibleChunksMap extends com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<PlayerChunk> {
         @Override
@@ -140,8 +141,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
         }
     }
     // Paper end
-    public final com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<PlayerChunk> pendingVisibleChunks = new com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<PlayerChunk>(); // Paper - this is used if the visible chunks is updated while iterating only
-    public transient com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<PlayerChunk> visibleChunksClone; // Paper - used for async access of visible chunks, clone and cache only when needed
+    // Tuinity - Don't copy
     private final Long2ObjectLinkedOpenHashMap<PlayerChunk> pendingUnload;
     public final LongSet loadedChunks; // Paper - private -> public
     public final WorldServer world;
@@ -735,7 +735,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
 
     @Nullable
     public PlayerChunk getUpdatingChunk(long i) { // Paper
-        return (PlayerChunk) this.updatingChunks.get(i);
+        return this.updatingChunks.getUpdating(i); // Tuinity - Don't copy
     }
 
     // Paper start - remove cloning of visible chunks unless accessed as a collection async
@@ -743,47 +743,25 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
     private boolean isIterating = false;
     private boolean hasPendingVisibleUpdate = false;
     public void forEachVisibleChunk(java.util.function.Consumer<PlayerChunk> consumer) {
-        org.spigotmc.AsyncCatcher.catchOp("forEachVisibleChunk");
-        boolean prev = isIterating;
-        isIterating = true;
-        try {
-            for (PlayerChunk value : this.visibleChunks.values()) {
-                consumer.accept(value);
-            }
-        } finally {
-            this.isIterating = prev;
-            if (!this.isIterating && this.hasPendingVisibleUpdate) {
-                ((ProtectedVisibleChunksMap)this.visibleChunks).copyFrom(this.pendingVisibleChunks);
-                this.pendingVisibleChunks.clear();
-                this.hasPendingVisibleUpdate = false;
-            }
-        }
+        throw new UnsupportedOperationException(); // Tuinity - Don't copy
     }
     public Long2ObjectLinkedOpenHashMap<PlayerChunk> getVisibleChunks() {
-        if (Thread.currentThread() == this.world.serverThread) {
-            return this.visibleChunks;
-        } else {
-            synchronized (this.visibleChunks) {
-                if (DEBUG_ASYNC_VISIBLE_CHUNKS) new Throwable("Async getVisibleChunks").printStackTrace();
-                if (this.visibleChunksClone == null) {
-                    this.visibleChunksClone = this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.clone() : ((ProtectedVisibleChunksMap)this.visibleChunks).clone();
-                }
-                return this.visibleChunksClone;
-            }
+        // Tuinity start - Don't copy (except in rare cases)
+        synchronized (this.updatingChunks) {
+            return this.updatingChunks.getVisibleMap().clone();
         }
+        // Tuinity end - Don't copy (except in rare cases)
     }
     // Paper end
 
     @Nullable
     public PlayerChunk getVisibleChunk(long i) { // Paper - protected -> public
-        // Paper start - mt safe get
-        if (Thread.currentThread() != this.world.serverThread) {
-            synchronized (this.visibleChunks) {
-                return (PlayerChunk) (this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(i) : ((ProtectedVisibleChunksMap)this.visibleChunks).safeGet(i));
-            }
+        // Tuinity start - Don't copy
+        if (Thread.currentThread() == this.world.serverThread) {
+            return this.updatingChunks.getVisible(i);
         }
-        return (PlayerChunk) (this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(i) : ((ProtectedVisibleChunksMap)this.visibleChunks).safeGet(i));
-        // Paper end
+        return this.updatingChunks.getVisibleAsync(i);
+        // Tuinity end - Don't copy
     }
 
     protected final IntSupplier getPrioritySupplier(long i) { return c(i); } // Paper - OBFHELPER
@@ -911,7 +889,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
                 }
                 this.getVillagePlace().dequeueUnload(playerchunk.location.pair()); // Tuinity - unload POI data
 
-                this.updatingChunks.put(i, playerchunk);
+                this.updatingChunks.queueUpdate(i, playerchunk); // Tuinity - Don't copy
                 this.updatingChunksModified = true;
             }
 
@@ -1064,7 +1042,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
         while (longiterator.hasNext()) { // Spigot
             long j = longiterator.nextLong();
             longiterator.remove(); // Spigot
-            PlayerChunk playerchunk = (PlayerChunk) this.updatingChunks.remove(j);
+            PlayerChunk playerchunk = this.updatingChunks.queueRemove(j); // Tuinity - Don't copy
 
             if (playerchunk != null) {
                 this.pendingUnload.put(j, playerchunk);
@@ -1218,19 +1196,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
         if (!this.updatingChunksModified) {
             return false;
         } else {
-            // Paper start - stop cloning visibleChunks
-            synchronized (this.visibleChunks) {
-                if (isIterating) {
-                    hasPendingVisibleUpdate = true;
-                    this.pendingVisibleChunks.copyFrom((com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<PlayerChunk>)this.updatingChunks);
-                } else {
-                    hasPendingVisibleUpdate = false;
-                    this.pendingVisibleChunks.clear();
-                    ((ProtectedVisibleChunksMap)this.visibleChunks).copyFrom((com.destroystokyo.paper.util.map.Long2ObjectLinkedOpenHashMapFastCopy<PlayerChunk>)this.updatingChunks);
-                    this.visibleChunksClone = null;
-                }
+            // Tuinity start - Don't copy
+            synchronized (this.updatingChunks) {
+                this.updatingChunks.performUpdates();
             }
-            // Paper end
+            // Tuinity end - Don't copy
 
             this.updatingChunksModified = false;
             return true;
@@ -1701,7 +1671,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
     }
 
     public int d() {
-        return this.visibleChunks.size();
+        return this.updatingChunks.getVisibleMap().size(); // Tuinity - Don't copy
     }
 
     protected PlayerChunkMap.a e() {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 09cef73a92679e47c8670e4e4c1ba988878aee24..f7b17db87e351e8218d7865c3f9a0162892ca269 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -298,7 +298,7 @@ public class CraftWorld implements World {
     public int getTileEntityCount() {
         return net.minecraft.server.MCUtil.ensureMain(() -> {
         // We don't use the full world tile entity list, so we must iterate chunks
-        Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks = world.getChunkProvider().playerChunkMap.visibleChunks;
+        Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks = world.getChunkProvider().playerChunkMap.updatingChunks.getVisibleMap(); // Tuinity - change updating chunks map
         int size = 0;
         for (PlayerChunk playerchunk : chunks.values()) {
             net.minecraft.world.level.chunk.Chunk chunk = playerchunk.getChunk();
@@ -317,7 +317,7 @@ public class CraftWorld implements World {
         return net.minecraft.server.MCUtil.ensureMain(() -> {
         int ret = 0;
 
-        for (PlayerChunk chunkHolder : world.getChunkProvider().playerChunkMap.visibleChunks.values()) {
+        for (PlayerChunk chunkHolder : world.getChunkProvider().playerChunkMap.updatingChunks.getVisibleMap().values()) { // Tuinity - change updating chunks map
             if (chunkHolder.getChunk() != null) {
                 ++ret;
             }
@@ -471,13 +471,16 @@ public class CraftWorld implements World {
     public Chunk[] getLoadedChunks() {
         // Paper start
         if (Thread.currentThread() != world.getMinecraftWorld().serverThread) {
-            synchronized (world.getChunkProvider().playerChunkMap.visibleChunks) {
-                Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks = world.getChunkProvider().playerChunkMap.visibleChunks;
-                return chunks.values().stream().map(PlayerChunk::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.Chunk::getBukkitChunk).toArray(Chunk[]::new);
+            // Tuinity start - change updating chunks map
+            Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks;
+            synchronized (world.getChunkProvider().playerChunkMap.updatingChunks) {
+                chunks = world.getChunkProvider().playerChunkMap.updatingChunks.getVisibleMap().clone();
             }
+            return chunks.values().stream().map(PlayerChunk::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.Chunk::getBukkitChunk).toArray(Chunk[]::new);
+            // Tuinity end - change updating chunks map
         }
         // Paper end
-        Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks = world.getChunkProvider().playerChunkMap.visibleChunks;
+        Long2ObjectLinkedOpenHashMap<PlayerChunk> chunks = world.getChunkProvider().playerChunkMap.updatingChunks.getVisibleMap(); // Tuinity - change updating chunks map
         return chunks.values().stream().map(PlayerChunk::getFullChunk).filter(Objects::nonNull).map(net.minecraft.world.level.chunk.Chunk::getBukkitChunk).toArray(Chunk[]::new);
     }
 
