diff --git a/patches/removed/1.18/0765-Optimise-chunk-tick-iteration.patch b/patches/removed/1.18/0765-Optimise-chunk-tick-iteration.patch deleted file mode 100644 index 7ca2497335..0000000000 --- a/patches/removed/1.18/0765-Optimise-chunk-tick-iteration.patch +++ /dev/null @@ -1,94 +0,0 @@ -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 b5c8f3f57d09de4caffeb9f3e20e9bf4daba1cdd..d6981bbcf480c5856b51960013d144beba2361b3 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -1007,19 +1007,35 @@ public class ServerChunkCache extends ChunkSource { - - this.lastSpawnState = spawnercreature_d; - this.level.getProfiler().pop(); -- List list = Lists.newArrayList(this.chunkMap.getChunks()); -- -- Collections.shuffle(list); -+ // Paper - moved down, enabled if per-player = false - // Paper - moved natural spawn event up - this.level.timings.chunkTicks.startTiming(); // Paper -- list.forEach((playerchunk) -> { -- Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); -- -- if (optional.isPresent()) { -- LevelChunk chunk = (LevelChunk) optional.get(); -+ // Paper start -+ java.util.Iterator iterator; -+ if (this.level.paperConfig.perPlayerMobSpawns) { -+ iterator = this.entityTickingChunks.iterator(); -+ } else { -+ iterator = this.entityTickingChunks.unsafeIterator(); -+ List shuffled = new java.util.ArrayList<>(this.entityTickingChunks.size()); -+ while (iterator.hasNext()) { -+ shuffled.add(iterator.next()); -+ } -+ Collections.shuffle(shuffled); -+ iterator = shuffled.iterator(); -+ } -+ try { while (iterator.hasNext()) { -+ LevelChunk chunk = iterator.next(); -+ ChunkHolder playerchunk = chunk.playerChunk; -+ if (playerchunk != null) { -+ this.level.getProfiler().push("broadcast"); -+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings -+ playerchunk.broadcastChanges(chunk); -+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings -+ this.level.getProfiler().pop(); -+ // Paper end - ChunkPos chunkcoordintpair = chunk.getPos(); - -- if (this.level.isPositionEntityTicking(chunkcoordintpair) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange -+ if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange // Paper - we only iterate entity ticking chunks - chunk.setInhabitedTime(chunk.getInhabitedTime() + j); - if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, true)) { // Spigot // Paper - optimise isOutsideOfRange - NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2); -@@ -1030,7 +1046,13 @@ public class ServerChunkCache extends ChunkSource { - // this.level.timings.doTickTiles.stopTiming(); // Spigot // Paper - } - } -- }); -+ } // Paper start - optimise chunk tick iteration -+ } finally { -+ if (iterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator) { -+ ((io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator)iterator).finishedIterating(); -+ } -+ } -+ // Paper end - optimise chunk tick iteration - this.level.timings.chunkTicks.stopTiming(); // Paper - this.level.getProfiler().push("customSpawners"); - if (flag1) { -@@ -1039,21 +1061,7 @@ public class ServerChunkCache extends ChunkSource { - } // Paper - timings - } - -- this.level.getProfiler().popPush("broadcast"); -- this.chunkMap.getChunks().forEach((playerchunk) -> { // Paper - no... just no... -- Optional optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error -- -- Objects.requireNonNull(playerchunk); -- -- // Paper start - timings -- optional.ifPresent(chunk -> { -- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings -- playerchunk.broadcastChanges(chunk); -- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings -- }); -- // Paper end -- }); -- this.level.getProfiler().pop(); -+ // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up - this.level.getProfiler().pop(); - } - diff --git a/patches/server/0006-MC-Utils.patch b/patches/server/0006-MC-Utils.patch index f5a4027421..b1fe48c977 100644 --- a/patches/server/0006-MC-Utils.patch +++ b/patches/server/0006-MC-Utils.patch @@ -5477,7 +5477,7 @@ index ef87c37633cee4ab438f1991144188ade1c4e65f..19d3802becd353e130b785f8286e595e protected void purgeStaleTickets() { diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index d672c467267ef4d96184e104c35d0379116880db..566c32a71054479257b7fba981507fa2559aa9e6 100644 +index d672c467267ef4d96184e104c35d0379116880db..be11caf7c0dcdb11e24cfb053cda45ac1867da63 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -48,6 +48,8 @@ import net.minecraft.world.level.storage.LevelData; @@ -5874,15 +5874,6 @@ index d672c467267ef4d96184e104c35d0379116880db..566c32a71054479257b7fba981507fa2 @Nullable @Override public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) { -@@ -444,7 +817,7 @@ public class ServerChunkCache extends ChunkSource { - gameprofilerfiller.popPush("spawnAndTick"); - boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit - -- Collections.shuffle(list); -+ //Collections.shuffle(list); // Paper - no... just no... - Iterator iterator1 = list.iterator(); - - while (iterator1.hasNext()) { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 9812116e2f28d01423373990ecce0e5ab6229ce2..e66b4837498a8bfefabc4dd95e91ccfba67843e4 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java diff --git a/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch b/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch index 8ea98d2d14..d05077e4a2 100644 --- a/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch +++ b/patches/server/0172-PlayerNaturallySpawnCreaturesEvent.patch @@ -40,13 +40,13 @@ index e954365c9fe7a5f442c4ab31ab8c8db9684409e8..fb9a82b4fabdb73e9a44bcbc20b848d8 return true; diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 109d6638d8fe035498bd717c180c9143ff00e0ad..9090bc174c0ccb542616e756be66967f55f0a626 100644 +index 8f5e9f1bdfd2e02512e1341f91b147b16e959e5b..87c9a7ffc69206554cf37c7d2c9939eb3cbea3a9 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -827,6 +827,15 @@ public class ServerChunkCache extends ChunkSource { boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit - //Collections.shuffle(list); // Paper - no... just no... + Collections.shuffle(list); + // Paper start - call player naturally spawn event + int chunkRange = level.spigotConfig.mobSpawnRange; + chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange; diff --git a/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch b/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch index dda84a76de..2d9c5091d3 100644 --- a/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch +++ b/patches/server/0435-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch @@ -272,7 +272,7 @@ index 8868ffcda194e8c2300181a2cdda9337dbde6284..95f195980e28bb59f43e5ca1d5e79ebe 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 3c36dfa4f4c6025b02ec455d997f13226de75a64..76b56ea346d843aba482c52c4bfe877fdf0e9225 100644 +index 41d2027cd4cf8f5de7bd59283361f7f1075356cb..24d0b02264e4cced08a60f36b5c41bb350a1dc60 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -881,6 +881,37 @@ public class ServerChunkCache extends ChunkSource { @@ -316,7 +316,7 @@ index 3c36dfa4f4c6025b02ec455d997f13226de75a64..76b56ea346d843aba482c52c4bfe877f @@ -928,15 +959,7 @@ public class ServerChunkCache extends ChunkSource { boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit - //Collections.shuffle(list); // Paper - no... just no... + Collections.shuffle(list); - // Paper start - call player naturally spawn event - int chunkRange = level.spigotConfig.mobSpawnRange; - chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange; diff --git a/patches/server/0827-Optimise-chunk-tick-iteration.patch b/patches/server/0827-Optimise-chunk-tick-iteration.patch new file mode 100644 index 0000000000..129bc70c02 --- /dev/null +++ b/patches/server/0827-Optimise-chunk-tick-iteration.patch @@ -0,0 +1,106 @@ +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..ca16bde30da84e79fef856304ada9e3b8bfb9806 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());