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/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
index 83dc09f6526206690c474b50a7a6e71cefc93ab4..7f76c304f5eb3c2f27b348918588ab67b795b1ba 100644
--- a/src/main/java/net/minecraft/server/ChunkSystem.java
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
@@ -202,19 +202,24 @@ public final class ChunkSystem {
     }
 
     public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
-        return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
+        if (Bukkit.isPrimaryThread()) {
+            return level.chunkSource.chunkMap.updatingChunks.getVisibleValuesCopy();
+        }
+        synchronized (level.chunkSource.chunkMap.updatingChunks) {
+            return level.chunkSource.chunkMap.updatingChunks.getVisibleValuesCopy();
+        }
     }
 
     public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
-        return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
+        return level.chunkSource.chunkMap.updatingChunks.getUpdatingValuesCopy();
     }
 
     public static int getVisibleChunkHolderCount(final ServerLevel level) {
-        return level.chunkSource.chunkMap.visibleChunkMap.size();
+        return level.chunkSource.chunkMap.updatingChunks.getVisibleMap().size();
     }
 
     public static int getUpdatingChunkHolderCount(final ServerLevel level) {
-        return level.chunkSource.chunkMap.updatingChunkMap.size();
+        return level.chunkSource.chunkMap.updatingChunks.getUpdatingMap().size();
     }
 
     public static boolean hasAnyChunkHolders(final ServerLevel level) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 2a9e5fb8164f79b0f9c1cb5497216e51f9df3454..ea27e6b1340a42c675bc68ed75f100569114be7a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -121,9 +121,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
     private static final int MIN_VIEW_DISTANCE = 3;
     public static final int MAX_VIEW_DISTANCE = 33;
     public static final int MAX_CHUNK_DISTANCE = 33 + ChunkStatus.maxDistance();
+    // Paper start - Don't copy
+    public final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<ChunkHolder> updatingChunks = new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>();
+    // Paper end - Don't copy
     public static final int FORCED_TICKET_LEVEL = 31;
-    public final Long2ObjectLinkedOpenHashMap<ChunkHolder> updatingChunkMap = new Long2ObjectLinkedOpenHashMap();
-    public volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
+    // Paper - Don't copy
     private final Long2ObjectLinkedOpenHashMap<ChunkHolder> pendingUnloads;
     public final LongSet entitiesInLevel;
     public final ServerLevel level;
@@ -224,7 +226,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 
     public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
         super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
-        this.visibleChunkMap = this.updatingChunkMap.clone();
+        // Paper - don't copy
         this.pendingUnloads = new Long2ObjectLinkedOpenHashMap();
         this.entitiesInLevel = new LongOpenHashSet();
         this.toDrop = new LongOpenHashSet();
@@ -327,12 +329,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 
     @Nullable
     public ChunkHolder getUpdatingChunkIfPresent(long pos) {
-        return (ChunkHolder) this.updatingChunkMap.get(pos);
+        return this.updatingChunks.getUpdating(pos); // Paper - Don't copy
     }
 
     @Nullable
     public ChunkHolder getVisibleChunkIfPresent(long pos) {
-        return (ChunkHolder) this.visibleChunkMap.get(pos);
+        // Paper start - Don't copy
+        if (Thread.currentThread() == this.level.thread) {
+            return this.updatingChunks.getVisible(pos);
+        }
+        return this.updatingChunks.getVisibleAsync(pos);
+        // Paper end - Don't copy
     }
 
     protected IntSupplier getChunkQueueLevel(long pos) {
@@ -515,7 +522,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
                 // Paper start
                 holder.onChunkAdd();
                 // Paper end
-                this.updatingChunkMap.put(pos, holder);
+                this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy
                 this.modified = true;
             }
 
@@ -592,7 +599,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 
         for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) {
             long j = longiterator.nextLong();
-            ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
+            ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy
 
             if (playerchunk != null) {
                 playerchunk.onChunkRemove(); // Paper
@@ -672,7 +679,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
         if (!this.modified) {
             return false;
         } else {
-            this.visibleChunkMap = this.updatingChunkMap.clone();
+            // Paper start - Don't copy
+            synchronized (this.updatingChunks) {
+                this.updatingChunks.performUpdates();
+            }
+            // Paper end - Don't copy
+
             this.modified = false;
             return true;
         }
