Finish chunk tick iteration optimisation port from Moonrise
This commit is contained in:
		
					parent
					
						
							
								077f411288
							
						
					
				
			
			
				commit
				
					
						e43b9191b2
					
				
			
		
					 3 changed files with 78 additions and 383 deletions
				
			
		| 
						 | 
				
			
			@ -25176,6 +25176,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 | 
			
		|||
+        return load ? this.syncLoad(chunkX, chunkZ, toStatus) : null;
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end - rewrite chunk system
 | 
			
		||||
+    private ServerChunkCache.ChunkAndHolder[] iterationCopy; // Paper - chunk tick iteration optimisations
 | 
			
		||||
 
 | 
			
		||||
     public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) {
 | 
			
		||||
         this.level = world;
 | 
			
		||||
| 
						 | 
				
			
			@ -25210,13 +25211,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 | 
			
		|||
-            }
 | 
			
		||||
-            // Paper end - Perf: Optimise getChunkAt calls for loaded chunks
 | 
			
		||||
-            ProfilerFiller gameprofilerfiller = this.level.getProfiler();
 | 
			
		||||
-
 | 
			
		||||
-            gameprofilerfiller.incrementCounter("getChunk");
 | 
			
		||||
-            long k = ChunkPos.asLong(x, z);
 | 
			
		||||
+        // Paper start - rewrite chunk system
 | 
			
		||||
+        if (leastStatus == ChunkStatus.FULL) {
 | 
			
		||||
+            final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(x, z));
 | 
			
		||||
 
 | 
			
		||||
-            gameprofilerfiller.incrementCounter("getChunk");
 | 
			
		||||
-            long k = ChunkPos.asLong(x, z);
 | 
			
		||||
-
 | 
			
		||||
-            for (int l = 0; l < 4; ++l) {
 | 
			
		||||
-                if (k == this.lastChunkPos[l] && leastStatus == this.lastChunkStatus[l]) {
 | 
			
		||||
-                    ChunkAccess ichunkaccess = this.lastChunk[l];
 | 
			
		||||
| 
						 | 
				
			
			@ -25431,6 +25432,44 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 | 
			
		|||
         long i = this.level.getGameTime();
 | 
			
		||||
         long j = i - this.lastInhabitedUpdate;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
 
 | 
			
		||||
             gameprofilerfiller.push("pollingChunks");
 | 
			
		||||
             gameprofilerfiller.push("filteringLoadedChunks");
 | 
			
		||||
-            List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size());
 | 
			
		||||
-            Iterator iterator = this.chunkMap.getChunks().iterator();
 | 
			
		||||
-            if (this.level.getServer().tickRateManager().runsNormally()) this.level.timings.chunkTicks.startTiming(); // Paper
 | 
			
		||||
+            // Paper start - chunk tick iteration optimisations
 | 
			
		||||
+            List<ServerChunkCache.ChunkAndHolder> list;
 | 
			
		||||
+            {
 | 
			
		||||
+                final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerChunkCache.ChunkAndHolder> tickingChunks =
 | 
			
		||||
+                    ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) this.level).moonrise$getTickingChunks();
 | 
			
		||||
 
 | 
			
		||||
-            while (iterator.hasNext()) {
 | 
			
		||||
-                ChunkHolder playerchunk = (ChunkHolder) iterator.next();
 | 
			
		||||
-                LevelChunk chunk = playerchunk.getTickingChunk();
 | 
			
		||||
+                final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
 | 
			
		||||
+                final int size = tickingChunks.size();
 | 
			
		||||
 
 | 
			
		||||
-                if (chunk != null) {
 | 
			
		||||
-                    list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
 | 
			
		||||
+                if (this.iterationCopy == null || this.iterationCopy.length < size) {
 | 
			
		||||
+                    this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length];
 | 
			
		||||
                 }
 | 
			
		||||
+                System.arraycopy(raw, 0, this.iterationCopy, 0, size);
 | 
			
		||||
+
 | 
			
		||||
+                list = it.unimi.dsi.fastutil.objects.ObjectArrayList.wrap(
 | 
			
		||||
+                    this.iterationCopy, size
 | 
			
		||||
+                );
 | 
			
		||||
             }
 | 
			
		||||
+            // Paper end - chunk tick iteration optimisations
 | 
			
		||||
+            Iterator iterator = null; // Paper - chunk tick iteration optimisations
 | 
			
		||||
+            if (this.level.getServer().tickRateManager().runsNormally()) this.level.timings.chunkTicks.startTiming(); // Paper
 | 
			
		||||
+
 | 
			
		||||
+            // Paper - chunk tick iteration optimisations
 | 
			
		||||
 
 | 
			
		||||
             if (this.level.tickRateManager().runsNormally()) {
 | 
			
		||||
                 gameprofilerfiller.popPush("naturalSpawnCount");
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
                     LevelChunk chunk1 = chunkproviderserver_a.chunk;
 | 
			
		||||
                     ChunkPos chunkcoordintpair = chunk1.getPos();
 | 
			
		||||
| 
						 | 
				
			
			@ -25454,6 +25493,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 | 
			
		|||
                     }
 | 
			
		||||
                 }
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
             }
 | 
			
		||||
 
 | 
			
		||||
             gameprofilerfiller.popPush("broadcast");
 | 
			
		||||
-            list.forEach((chunkproviderserver_a1) -> {
 | 
			
		||||
-                this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
 | 
			
		||||
-                chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
 | 
			
		||||
-                this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
 | 
			
		||||
-            });
 | 
			
		||||
+            // Paper start - chunk tick iteration optimisations
 | 
			
		||||
+            this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
 | 
			
		||||
+            {
 | 
			
		||||
+                final it.unimi.dsi.fastutil.objects.ObjectArrayList<net.minecraft.server.level.ServerChunkCache.ChunkAndHolder> chunks = (it.unimi.dsi.fastutil.objects.ObjectArrayList<net.minecraft.server.level.ServerChunkCache.ChunkAndHolder>)list;
 | 
			
		||||
+                final ServerChunkCache.ChunkAndHolder[] raw = chunks.elements();
 | 
			
		||||
+                final int size = chunks.size();
 | 
			
		||||
+
 | 
			
		||||
+                Objects.checkFromToIndex(0, size, raw.length);
 | 
			
		||||
+                for (int idx = 0; idx < size; ++idx) {
 | 
			
		||||
+                    final ServerChunkCache.ChunkAndHolder holder = raw[idx];
 | 
			
		||||
+                    raw[idx] = null;
 | 
			
		||||
+
 | 
			
		||||
+                    holder.holder().broadcastChanges(holder.chunk());
 | 
			
		||||
+                }
 | 
			
		||||
+            }
 | 
			
		||||
+            this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
 | 
			
		||||
+            // Paper end - chunk tick iteration optimisations
 | 
			
		||||
             gameprofilerfiller.pop();
 | 
			
		||||
             gameprofilerfiller.pop();
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     private void getFullChunk(long pos, Consumer<LevelChunk> chunkConsumer) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 | 
			
		|||
                 this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
 | 
			
		||||
 
 | 
			
		||||
                 this.lastSpawnState = spawnercreature_d;
 | 
			
		||||
                 gameprofilerfiller.popPush("spawnAndTick");
 | 
			
		||||
                 boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
 | 
			
		||||
 
 | 
			
		||||
-                Util.shuffle(list, this.level.random);
 | 
			
		||||
+                if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.level.random); // Paper - per player mob spawns - do not need this when per-player is enabled
 | 
			
		||||
                 // Paper start - PlayerNaturallySpawnCreaturesEvent
 | 
			
		||||
                 int chunkRange = level.spigotConfig.mobSpawnRange;
 | 
			
		||||
                 chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,380 +0,0 @@
 | 
			
		|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
 | 
			
		||||
Date: Sat, 23 Sep 2023 21:36:36 -0700
 | 
			
		||||
Subject: [PATCH] Optimise chunk tick iteration
 | 
			
		||||
 | 
			
		||||
When per-player mob spawning is enabled we do not need to randomly
 | 
			
		||||
shuffle the chunk list. Additionally, we can use the NearbyPlayers
 | 
			
		||||
class to quickly retrieve nearby players instead of possible
 | 
			
		||||
searching all players on the server.
 | 
			
		||||
 | 
			
		||||
diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
 | 
			
		||||
+++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
 | 
			
		||||
@@ -0,0 +0,0 @@ public final class NearbyPlayers {
 | 
			
		||||
         GENERAL_SMALL,
 | 
			
		||||
         GENERAL_REALLY_SMALL,
 | 
			
		||||
         TICK_VIEW_DISTANCE,
 | 
			
		||||
-        VIEW_DISTANCE;
 | 
			
		||||
+        VIEW_DISTANCE, // Paper - optimise chunk iteration
 | 
			
		||||
+        SPAWN_RANGE, // Paper - optimise chunk iteration
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     private static final NearbyMapType[] MOB_TYPES = NearbyMapType.values();
 | 
			
		||||
@@ -0,0 +0,0 @@ public final class NearbyPlayers {
 | 
			
		||||
     private static final int GENERAL_AREA_VIEW_DISTANCE = 33;
 | 
			
		||||
     private static final int GENERAL_SMALL_VIEW_DISTANCE = 10;
 | 
			
		||||
     private static final int GENERAL_REALLY_SMALL_VIEW_DISTANCE = 3;
 | 
			
		||||
+    private static final int SPAWN_RANGE_VIEW_DISTANCE = net.minecraft.server.level.DistanceManager.MOB_SPAWN_RANGE; // Paper - optimise chunk iteration
 | 
			
		||||
 
 | 
			
		||||
     public static final int GENERAL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_AREA_VIEW_DISTANCE << 4);
 | 
			
		||||
     public static final int GENERAL_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_SMALL_VIEW_DISTANCE << 4);
 | 
			
		||||
     public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4);
 | 
			
		||||
+    public static final int SPAWN_RANGE_VIEW_DISTANCE_BLOCKS = (SPAWN_RANGE_VIEW_DISTANCE << 4); // Paper - optimise chunk iteration
 | 
			
		||||
 
 | 
			
		||||
     private final ServerLevel world;
 | 
			
		||||
     private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
 | 
			
		||||
@@ -0,0 +0,0 @@ public final class NearbyPlayers {
 | 
			
		||||
         players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE);
 | 
			
		||||
         players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getTickViewDistance(player));
 | 
			
		||||
         players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getLoadViewDistance(player));
 | 
			
		||||
+        players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, SPAWN_RANGE_VIEW_DISTANCE); // Paper - optimise chunk iteration
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public TrackedChunk getChunk(final ChunkPos pos) {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
 | 
			
		||||
 
 | 
			
		||||
     // Paper start
 | 
			
		||||
     public void onChunkAdd() {
 | 
			
		||||
-
 | 
			
		||||
+        // Paper start - optimise chunk tick iteration
 | 
			
		||||
+        if (this.needsBroadcastChanges()) {
 | 
			
		||||
+            this.chunkMap.needsChangeBroadcasting.add(this);
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end - optimise chunk tick iteration
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public void onChunkRemove() {
 | 
			
		||||
-
 | 
			
		||||
+        // Paper start - optimise chunk tick iteration
 | 
			
		||||
+        if (this.needsBroadcastChanges()) {
 | 
			
		||||
+            this.chunkMap.needsChangeBroadcasting.remove(this);
 | 
			
		||||
+        }
 | 
			
		||||
+        // Paper end - optimise chunk tick iteration
 | 
			
		||||
     }
 | 
			
		||||
     // Paper end
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
 | 
			
		||||
 
 | 
			
		||||
             if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
 | 
			
		||||
             if (this.changedBlocksPerSection[i] == null) {
 | 
			
		||||
-                this.hasChangedSections = true;
 | 
			
		||||
+                this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
 | 
			
		||||
                 this.changedBlocksPerSection[i] = new ShortOpenHashSet();
 | 
			
		||||
             }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
 | 
			
		||||
                 int k = this.lightEngine.getMaxLightSection();
 | 
			
		||||
 
 | 
			
		||||
                 if (y >= j && y <= k) {
 | 
			
		||||
+                    this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
 | 
			
		||||
                     int l = y - j;
 | 
			
		||||
 
 | 
			
		||||
                     if (lightType == LightLayer.SKY) {
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
 | 
			
		||||
         this.broadcast(this.getPlayers(onChunkViewEdge), packet); // Paper - rewrite chunk system
 | 
			
		||||
     }
 | 
			
		||||
     // Paper end - starlight
 | 
			
		||||
+    // Paper start - optimise chunk tick iteration
 | 
			
		||||
+    public final boolean needsBroadcastChanges() {
 | 
			
		||||
+        return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty();
 | 
			
		||||
+    }
 | 
			
		||||
+
 | 
			
		||||
+    private void addToBroadcastMap() {
 | 
			
		||||
+        io.papermc.paper.util.TickThread.ensureTickThread(this.chunkMap.level, this.pos, "Asynchronous ChunkHolder update is not allowed");
 | 
			
		||||
+        this.chunkMap.needsChangeBroadcasting.add(this);
 | 
			
		||||
+    }
 | 
			
		||||
+    // Paper end - optimise chunk tick iteration
 | 
			
		||||
 
 | 
			
		||||
     public void broadcastChanges(LevelChunk chunk) {
 | 
			
		||||
-        if (this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) {
 | 
			
		||||
+        if (this.needsBroadcastChanges()) { // Paper - optimise chunk tick iteration; moved into above, other logic needs to call
 | 
			
		||||
             Level world = chunk.getLevel();
 | 
			
		||||
             List list;
 | 
			
		||||
 
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 | 
			
		||||
             this.playerEntityTrackerTrackMaps[i].remove(player);
 | 
			
		||||
         }
 | 
			
		||||
         // Paper end - use distance map to optimise tracker
 | 
			
		||||
+        this.playerMobSpawnMap.remove(player); // Paper - optimise chunk tick iteration
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     void updateMaps(ServerPlayer player) {
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 | 
			
		||||
     }
 | 
			
		||||
     public final io.papermc.paper.util.player.NearbyPlayers nearbyPlayers;
 | 
			
		||||
     // Paper end
 | 
			
		||||
+    // Paper start - optimise chunk tick iteration
 | 
			
		||||
+    public final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>();
 | 
			
		||||
+    public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
 | 
			
		||||
+    // Paper end - optimise chunk tick iteration
 | 
			
		||||
 
 | 
			
		||||
     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(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
 | 
			
		||||
     }
 | 
			
		||||
     // Paper end - Optional per player mob spawns
 | 
			
		||||
 
 | 
			
		||||
-    private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
 | 
			
		||||
+    public static double euclideanDistanceSquared(ChunkPos pos, Entity entity) { // Paper - optimise chunk iteration; public
 | 
			
		||||
         double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
 | 
			
		||||
         double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
 | 
			
		||||
         double d2 = d0 - entity.getX();
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
 | 
			
		||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
 | 
			
		||||
     private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
 | 
			
		||||
     final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
 | 
			
		||||
     // Paper - rewrite chunk system
 | 
			
		||||
-    private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
 | 
			
		||||
+    public static final int MOB_SPAWN_RANGE = 8; //private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - optimise chunk tick iteration
 | 
			
		||||
     // Paper - rewrite chunk system
 | 
			
		||||
     private final ChunkMap chunkMap; // Paper
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
 | 
			
		||||
         long i = chunkcoordintpair.toLong();
 | 
			
		||||
 
 | 
			
		||||
         // Paper - no longer used
 | 
			
		||||
-        this.naturalSpawnChunkCounter.update(i, 0, true);
 | 
			
		||||
+        //this.naturalSpawnChunkCounter.update(i, 0, true); // Paper - optimise chunk tick iteration
 | 
			
		||||
         //this.playerTicketManager.update(i, 0, true); // Paper - no longer used
 | 
			
		||||
         //this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
 | 
			
		||||
     }
 | 
			
		||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
 | 
			
		||||
         if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
 | 
			
		||||
         if (objectset == null || objectset.isEmpty()) { // Paper
 | 
			
		||||
             this.playersPerChunk.remove(i);
 | 
			
		||||
-            this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
 | 
			
		||||
+            //this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false); // Paper - optimise chunk tick iteration
 | 
			
		||||
             //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
 | 
			
		||||
             //this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
 | 
			
		||||
         }
 | 
			
		||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public int getNaturalSpawnChunkCount() {
 | 
			
		||||
-        this.naturalSpawnChunkCounter.runAllUpdates();
 | 
			
		||||
-        return this.naturalSpawnChunkCounter.chunks.size();
 | 
			
		||||
+        return this.chunkMap.playerMobSpawnMap.size(); // Paper - optimise chunk tick iteration
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public boolean hasPlayersNearby(long chunkPos) {
 | 
			
		||||
-        this.naturalSpawnChunkCounter.runAllUpdates();
 | 
			
		||||
-        return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos);
 | 
			
		||||
+        return this.chunkMap.playerMobSpawnMap.getObjectsInRange(chunkPos) != null; // Paper - optimise chunk tick iteration
 | 
			
		||||
     }
 | 
			
		||||
 
 | 
			
		||||
     public String getDebugStatus() {
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
 
 | 
			
		||||
             gameprofilerfiller.push("pollingChunks");
 | 
			
		||||
             gameprofilerfiller.push("filteringLoadedChunks");
 | 
			
		||||
-            List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size());
 | 
			
		||||
-            Iterator iterator = this.chunkMap.getChunks().iterator();
 | 
			
		||||
+            // Paper - optimise chunk tick iteration
 | 
			
		||||
             if (this.level.getServer().tickRateManager().runsNormally()) this.level.timings.chunkTicks.startTiming(); // Paper
 | 
			
		||||
 
 | 
			
		||||
-            while (iterator.hasNext()) {
 | 
			
		||||
-                ChunkHolder playerchunk = (ChunkHolder) iterator.next();
 | 
			
		||||
-                LevelChunk chunk = playerchunk.getTickingChunk();
 | 
			
		||||
-
 | 
			
		||||
-                if (chunk != null) {
 | 
			
		||||
-                    list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
 | 
			
		||||
-                }
 | 
			
		||||
-            }
 | 
			
		||||
+            // Paper - optimise chunk tick iteration
 | 
			
		||||
 
 | 
			
		||||
             if (this.level.tickRateManager().runsNormally()) {
 | 
			
		||||
                 gameprofilerfiller.popPush("naturalSpawnCount");
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
                 gameprofilerfiller.popPush("spawnAndTick");
 | 
			
		||||
                 boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
 | 
			
		||||
 
 | 
			
		||||
-                Util.shuffle(list, this.level.random);
 | 
			
		||||
-                // Paper start - PlayerNaturallySpawnCreaturesEvent
 | 
			
		||||
-                int chunkRange = level.spigotConfig.mobSpawnRange;
 | 
			
		||||
-                chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
 | 
			
		||||
-                chunkRange = Math.min(chunkRange, 8);
 | 
			
		||||
-                for (ServerPlayer entityPlayer : this.level.players()) {
 | 
			
		||||
-                    entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
 | 
			
		||||
-                    entityPlayer.playerNaturallySpawnedEvent.callEvent();
 | 
			
		||||
+                // Paper start - optimise chunk tick iteration
 | 
			
		||||
+                ChunkMap playerChunkMap = this.chunkMap;
 | 
			
		||||
+                for (ServerPlayer player : this.level.players) {
 | 
			
		||||
+                    if (!player.affectsSpawning || player.isSpectator()) {
 | 
			
		||||
+                        playerChunkMap.playerMobSpawnMap.remove(player);
 | 
			
		||||
+                        player.playerNaturallySpawnedEvent = null;
 | 
			
		||||
+                        player.lastEntitySpawnRadiusSquared = -1.0;
 | 
			
		||||
+                        continue;
 | 
			
		||||
+                    }
 | 
			
		||||
+
 | 
			
		||||
+                    int viewDistance = io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player);
 | 
			
		||||
+
 | 
			
		||||
+                    // copied and modified from isOutisdeRange
 | 
			
		||||
+                    int chunkRange = (int)level.spigotConfig.mobSpawnRange;
 | 
			
		||||
+                    chunkRange = (chunkRange > viewDistance) ? viewDistance : chunkRange;
 | 
			
		||||
+                    chunkRange = (chunkRange > DistanceManager.MOB_SPAWN_RANGE) ? DistanceManager.MOB_SPAWN_RANGE : chunkRange;
 | 
			
		||||
+
 | 
			
		||||
+                    com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), (byte)chunkRange);
 | 
			
		||||
+                    event.callEvent();
 | 
			
		||||
+                    if (event.isCancelled() || event.getSpawnRadius() < 0) {
 | 
			
		||||
+                        playerChunkMap.playerMobSpawnMap.remove(player);
 | 
			
		||||
+                        player.playerNaturallySpawnedEvent = null;
 | 
			
		||||
+                        player.lastEntitySpawnRadiusSquared = -1.0;
 | 
			
		||||
+                        continue;
 | 
			
		||||
+                    }
 | 
			
		||||
+
 | 
			
		||||
+                    int range = Math.min(event.getSpawnRadius(), DistanceManager.MOB_SPAWN_RANGE); // limit to max spawn range
 | 
			
		||||
+                    int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkCoordinate(player.getX());
 | 
			
		||||
+                    int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkCoordinate(player.getZ());
 | 
			
		||||
+
 | 
			
		||||
+                    playerChunkMap.playerMobSpawnMap.addOrUpdate(player, chunkX, chunkZ, range);
 | 
			
		||||
+                    player.lastEntitySpawnRadiusSquared = (double)((range << 4) * (range << 4)); // used in anyPlayerCloseEnoughForSpawning
 | 
			
		||||
+                    player.playerNaturallySpawnedEvent = event;
 | 
			
		||||
                 }
 | 
			
		||||
-                // Paper end - PlayerNaturallySpawnCreaturesEvent
 | 
			
		||||
+                // Paper end - optimise chunk tick iteration
 | 
			
		||||
                 int l = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
 | 
			
		||||
                 boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
 | 
			
		||||
-                Iterator iterator1 = list.iterator();
 | 
			
		||||
+                // Paper - optimise chunk tick iteration
 | 
			
		||||
 
 | 
			
		||||
                 int chunksTicked = 0; // Paper
 | 
			
		||||
-                while (iterator1.hasNext()) {
 | 
			
		||||
-                    ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
 | 
			
		||||
-                    LevelChunk chunk1 = chunkproviderserver_a.chunk;
 | 
			
		||||
+                // Paper start - optimise chunk tick iteration
 | 
			
		||||
+                io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = this.chunkMap.getNearbyPlayers(); // Paper - optimise chunk tick iteration
 | 
			
		||||
+                Iterator<LevelChunk> chunkIterator;
 | 
			
		||||
+                if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
 | 
			
		||||
+                    chunkIterator = this.tickingChunks.iterator();
 | 
			
		||||
+                } else {
 | 
			
		||||
+                    chunkIterator = this.tickingChunks.unsafeIterator();
 | 
			
		||||
+                    List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.tickingChunks.size());
 | 
			
		||||
+                    while (chunkIterator.hasNext()) {
 | 
			
		||||
+                        shuffled.add(chunkIterator.next());
 | 
			
		||||
+                    }
 | 
			
		||||
+                    Util.shuffle(shuffled, this.level.random);
 | 
			
		||||
+                    chunkIterator = shuffled.iterator();
 | 
			
		||||
+                }
 | 
			
		||||
+                try {
 | 
			
		||||
+                // Paper end - optimise chunk tick iteration
 | 
			
		||||
+                while (chunkIterator.hasNext()) {
 | 
			
		||||
+                    LevelChunk chunk1 = chunkIterator.next();
 | 
			
		||||
+                    // Paper end - optimise chunk tick iteration
 | 
			
		||||
                     ChunkPos chunkcoordintpair = chunk1.getPos();
 | 
			
		||||
 
 | 
			
		||||
-                    if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) {
 | 
			
		||||
+                    // Paper start - optimise chunk tick iteration
 | 
			
		||||
+                    com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> playersNearby
 | 
			
		||||
+                        = nearbyPlayers.getPlayers(chunkcoordintpair, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.SPAWN_RANGE);
 | 
			
		||||
+                    if (playersNearby == null) {
 | 
			
		||||
+                        continue;
 | 
			
		||||
+                    }
 | 
			
		||||
+                    Object[] rawData = playersNearby.getRawData();
 | 
			
		||||
+                    boolean spawn = false;
 | 
			
		||||
+                    boolean tick = false;
 | 
			
		||||
+                    for (int itr = 0, len = playersNearby.size(); itr < len; ++itr) {
 | 
			
		||||
+                        ServerPlayer player = (ServerPlayer)rawData[itr];
 | 
			
		||||
+                        if (player.isSpectator()) {
 | 
			
		||||
+                            continue;
 | 
			
		||||
+                        }
 | 
			
		||||
+
 | 
			
		||||
+                        double distance = ChunkMap.euclideanDistanceSquared(chunkcoordintpair, player);
 | 
			
		||||
+                        spawn |= player.lastEntitySpawnRadiusSquared >= distance;
 | 
			
		||||
+                        tick |= ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) * ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) >= distance;
 | 
			
		||||
+                        if (spawn & tick) {
 | 
			
		||||
+                            break;
 | 
			
		||||
+                        }
 | 
			
		||||
+                    }
 | 
			
		||||
+                    if (tick && chunk1.chunkStatus.isOrAfter(net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING)) {
 | 
			
		||||
+                        // Paper end - optimise chunk tick iteration
 | 
			
		||||
                         chunk1.incrementInhabitedTime(j);
 | 
			
		||||
-                        if (flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot
 | 
			
		||||
+                        if (spawn && flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) { // Spigot // Paper - optimise chunk tick iteration
 | 
			
		||||
                             NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
 | 
			
		||||
                         }
 | 
			
		||||
 
 | 
			
		||||
-                        if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
 | 
			
		||||
+                        if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - optimise chunk tick iteration
 | 
			
		||||
                             this.level.tickChunk(chunk1, l);
 | 
			
		||||
                             if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
 | 
			
		||||
                         }
 | 
			
		||||
                     }
 | 
			
		||||
                 }
 | 
			
		||||
+                // Paper start - optimise chunk tick iteration
 | 
			
		||||
+                } finally {
 | 
			
		||||
+                    if (chunkIterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
 | 
			
		||||
+                        safeIterator.finishedIterating();
 | 
			
		||||
+                    }
 | 
			
		||||
+                }
 | 
			
		||||
+                // Paper end - optimise chunk tick iteration
 | 
			
		||||
                 this.level.timings.chunkTicks.stopTiming(); // Paper
 | 
			
		||||
 
 | 
			
		||||
                 gameprofilerfiller.popPush("customSpawners");
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
 | 
			
		||||
             }
 | 
			
		||||
 
 | 
			
		||||
             gameprofilerfiller.popPush("broadcast");
 | 
			
		||||
-            list.forEach((chunkproviderserver_a1) -> {
 | 
			
		||||
+            // Paper - optimise chunk tick iteration
 | 
			
		||||
                 this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
 | 
			
		||||
-                chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
 | 
			
		||||
+            // Paper start - optimise chunk tick iteration
 | 
			
		||||
+            if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
 | 
			
		||||
+                it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
 | 
			
		||||
+                this.chunkMap.needsChangeBroadcasting.clear();
 | 
			
		||||
+                for (ChunkHolder holder : copy) {
 | 
			
		||||
+                    holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
 | 
			
		||||
+                    if (holder.needsBroadcastChanges()) {
 | 
			
		||||
+                        // I DON'T want to KNOW what DUMB plugins might be doing.
 | 
			
		||||
+                        this.chunkMap.needsChangeBroadcasting.add(holder);
 | 
			
		||||
+                    }
 | 
			
		||||
+                }
 | 
			
		||||
+            }
 | 
			
		||||
+            // Paper end - optimise chunk tick iteration
 | 
			
		||||
                 this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
 | 
			
		||||
-            });
 | 
			
		||||
+            // Paper - optimise chunk tick iteration
 | 
			
		||||
             gameprofilerfiller.pop();
 | 
			
		||||
             gameprofilerfiller.pop();
 | 
			
		||||
         }
 | 
			
		||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
			
		||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
 | 
			
		||||
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
 | 
			
		||||
         });
 | 
			
		||||
     }
 | 
			
		||||
     // Paper end - replace player chunk loader
 | 
			
		||||
+    // Paper start - optimise chunk tick iteration
 | 
			
		||||
+    public double lastEntitySpawnRadiusSquared = -1.0;
 | 
			
		||||
+    // Paper end - optimise chunk tick iteration
 | 
			
		||||
 
 | 
			
		||||
     public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) {
 | 
			
		||||
         super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile);
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue