diff options
Diffstat (limited to 'patches/server/0690-Optimise-chunk-tick-iteration.patch')
-rw-r--r-- | patches/server/0690-Optimise-chunk-tick-iteration.patch | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/patches/server/0690-Optimise-chunk-tick-iteration.patch b/patches/server/0690-Optimise-chunk-tick-iteration.patch new file mode 100644 index 0000000000..701231d24f --- /dev/null +++ b/patches/server/0690-Optimise-chunk-tick-iteration.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +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/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index 4164204ba80f68a768de0ed1721c6447b972a631..4ae1ba645d9fdc1eb6d5a3e4f8ceed9b4841e003 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -84,6 +84,11 @@ public class ChunkHolder { + this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); + this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); + // Paper end - optimise anyPlayerCloseEnoughForSpawning ++ // Paper start - optimise chunk tick iteration ++ if (this.needsBroadcastChanges()) { ++ this.chunkMap.needsChangeBroadcasting.add(this); ++ } ++ // Paper end - optimise chunk tick iteration + } + + public void onChunkRemove() { +@@ -91,6 +96,11 @@ public class ChunkHolder { + this.playersInMobSpawnRange = null; + this.playersInChunkTickRange = null; + // Paper end - optimise anyPlayerCloseEnoughForSpawning ++ // Paper start - optimise chunk tick iteration ++ if (this.needsBroadcastChanges()) { ++ this.chunkMap.needsChangeBroadcasting.remove(this); ++ } ++ // Paper end - optimise chunk tick iteration + } + // Paper end + +@@ -230,7 +240,7 @@ 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(); + } + +@@ -254,6 +264,7 @@ 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) { +@@ -268,8 +279,19 @@ public class ChunkHolder { + } + } + ++ // Paper start - optimise chunk tick iteration ++ public final boolean needsBroadcastChanges() { ++ return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty(); ++ } ++ ++ private void addToBroadcastMap() { ++ org.spigotmc.AsyncCatcher.catchOp("ChunkHolder update"); ++ 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 - 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 4e65e1d2d1538366f554d94bff31ab74c2e8a225..ee298a136d95a2759ee77ffc11cf6e43cc54c1bb 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -115,6 +115,8 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator; + import org.bukkit.entity.Player; + // CraftBukkit end + ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper ++ + public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { + + private static final byte CHUNK_TYPE_REPLACEABLE = -1; +@@ -152,6 +154,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + private final Queue<Runnable> unloadQueue; + int viewDistance; + public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper ++ public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>(); + + // Paper - rewrite chunk system + +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 5c5fe2087a7617324ab8e18389e3ffa9ac413026..828de28f2777e2477a9c6545c8af96c4ca4e352b 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -48,6 +48,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp + import net.minecraft.world.level.storage.DimensionDataStorage; + import net.minecraft.world.level.storage.LevelData; + import net.minecraft.world.level.storage.LevelStorageSource; ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper + + public class ServerChunkCache extends ChunkSource { + +@@ -574,42 +575,59 @@ public class ServerChunkCache extends ChunkSource { + + this.lastSpawnState = spawnercreature_d; + gameprofilerfiller.popPush("filteringLoadedChunks"); +- List<ServerChunkCache.ChunkAndHolder> 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 iteratio ++ Iterator<LevelChunk> iterator1; ++ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ iterator1 = this.entityTickingChunks.iterator(); ++ } else { ++ iterator1 = this.entityTickingChunks.unsafeIterator(); ++ List<LevelChunk> 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) { ++ // Paper - move down ++ // Paper end - optimise chunk tick iteration + ChunkPos chunkcoordintpair = chunk1.getPos(); + +- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning ++ if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking + 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); + } + +- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { ++ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking + 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) { +@@ -617,15 +635,24 @@ public class ServerChunkCache extends ChunkSource { + this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); + } // 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 start - use set of chunks requiring updates, rather than iterating every single one loaded ++ gameprofilerfiller.popPush("broadcast"); ++ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing ++ if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { ++ 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); ++ } ++ } ++ } ++ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing + gameprofilerfiller.pop(); ++ // Paper end - use set of chunks requiring updates, rather than iterating every single one loaded + this.chunkMap.tick(); + } + } |