aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0690-Optimise-chunk-tick-iteration.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0690-Optimise-chunk-tick-iteration.patch')
-rw-r--r--patches/server/0690-Optimise-chunk-tick-iteration.patch215
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();
+ }
+ }