From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf 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/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java index 51c4ca36221e9af074fa92f6ab94fa7ba5403080..8d38a5a4c74d3f9298ba4f215e978fbc25ba91f0 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -1001,34 +1001,46 @@ public class ServerChunkCache extends ChunkSource { this.lastSpawnState = spawnercreature_d; gameprofilerfiller.popPush("filteringLoadedChunks"); - List list = Lists.newArrayListWithCapacity(l); - Iterator iterator = this.chunkMap.getChunks().iterator(); + // Paper - moved down 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 - moved down gameprofilerfiller.popPush("spawnAndTick"); boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit - Collections.shuffle(list); + // Paper - only shuffle if per-player mob spawning is disabled // Paper - moved natural spawn event up - Iterator iterator1 = list.iterator(); + // Paper start - optimise chunk tick iteration + Iterator iterator1; + if (this.level.paperConfig.perPlayerMobSpawns) { + iterator1 = this.entityTickingChunks.iterator(); + } else { + iterator1 = this.entityTickingChunks.unsafeIterator(); + List shuffled = Lists.newArrayListWithCapacity(this.entityTickingChunks.size()); + while (iterator1.hasNext()) { + shuffled.add(iterator1.next()); + } + Collections.shuffle(shuffled); + iterator1 = shuffled.iterator(); + } + try { while (iterator1.hasNext()) { - ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next(); - LevelChunk chunk1 = chunkproviderserver_a.chunk; + LevelChunk chunk1 = iterator1.next(); + ChunkHolder holder = chunk1.playerChunk; + if (holder != null) { + gameprofilerfiller.popPush("broadcast"); + this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing + holder.broadcastChanges(chunk1); + this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing + gameprofilerfiller.pop(); + // Paper end - optimise chunk tick iteration ChunkPos chunkcoordintpair = chunk1.getPos(); - if (this.level.isPositionEntityTicking(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning + if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration chunk1.incrementInhabitedTime(j); - if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning + if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); } @@ -1036,7 +1048,16 @@ public class ServerChunkCache extends ChunkSource { this.level.tickChunk(chunk1, k); } } + // Paper start - optimise chunk tick iteration + } } + + } finally { + if (iterator1 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"); if (flag2) { @@ -1045,13 +1066,7 @@ public class ServerChunkCache extends ChunkSource { } // Paper - timings } - 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 - }); - gameprofilerfiller.pop(); + // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up gameprofilerfiller.pop(); // Paper start - controlled flush for entity tracker packets List disabledFlushes = new java.util.ArrayList<>(this.level.players.size());