From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 7 May 2020 05:48:54 -0700
Subject: [PATCH] Optimise chunk tick iteration

Use a dedicated list of entity ticking chunks to reduce the cost

diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index e1a17abda657c7eb7aee7cd0763bcb48cc8b154a..8c0aed61400a89799592d3d457c1d78f0217d066 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -22,6 +22,12 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Paper
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+// Tuinity start
+import it.unimi.dsi.fastutil.objects.Object2BooleanLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+// Tuinity end
+
 public class ChunkProviderServer extends IChunkProvider {
 
     private static final List<ChunkStatus> b = ChunkStatus.a(); static final List<ChunkStatus> getPossibleChunkStatuses() { return ChunkProviderServer.b; } // Paper - OBFHELPER
@@ -982,19 +988,23 @@ public class ChunkProviderServer extends IChunkProvider {
             //List<PlayerChunk> list = Lists.newArrayList(this.playerChunkMap.f()); // Paper
             //Collections.shuffle(list); // Paper
             // Paper - moved up
-            final int[] chunksTicked = {0}; this.playerChunkMap.forEachVisibleChunk((playerchunk) -> { // Paper - safe iterator incase chunk loads, also no wrapping
-                Optional<Chunk> optional = ((Either) playerchunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
-
-                if (optional.isPresent()) {
+            // Tuinity start - optimise chunk tick iteration
+            com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Chunk> iterator = this.entityTickingChunks.iterator();
+            try {
+            while (iterator.hasNext()) {
+                Chunk chunk = iterator.next();
+                PlayerChunk playerchunk = chunk.playerChunk;
+                if (playerchunk != null) { // make sure load event has been called along with the load logic we put there
+                    // Tuinity end - optimise chunk tick iteration
                     this.world.getMethodProfiler().enter("broadcast");
                     this.world.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
-                    playerchunk.a((Chunk) optional.get());
+                    playerchunk.a(chunk); // Tuinity
                     this.world.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
                     this.world.getMethodProfiler().exit();
-                    Optional<Chunk> optional1 = ((Either) playerchunk.b().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
+                    // Tuinity
 
-                    if (optional1.isPresent()) {
-                        Chunk chunk = (Chunk) optional1.get();
+                    if (true) { // Tuinity
+                        // Tuinity
                         ChunkCoordIntPair chunkcoordintpair = playerchunk.i();
 
                         if (!this.playerChunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange
@@ -1010,7 +1020,11 @@ public class ChunkProviderServer extends IChunkProvider {
                         }
                     }
                 }
-            });
+            } // Tuinity start - optimise chunk tick iteration
+            } finally {
+                iterator.finishedIterating();
+            }
+            // Tuinity end - optimise chunk tick iteration
             this.world.getMethodProfiler().enter("customSpawners");
             if (flag1) {
                 try (co.aikar.timings.Timing ignored = this.world.timings.miscMobSpawning.startTiming()) { // Paper - timings
