216 lines
12 KiB
Diff
216 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
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 a61f55ed1fbe5aac5289014cb95cb6950b4c77fa..e11ec87e8007979a1c6932b414bcd70c10db746c 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -88,6 +88,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() {
|
|
@@ -95,6 +100,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
|
|
|
|
@@ -210,7 +220,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();
|
|
}
|
|
|
|
@@ -234,6 +244,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) {
|
|
@@ -248,8 +259,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();
|
|
int i = 0;
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index a488c33ce546cd29b30fcff9748869c20fc835aa..d91dbffab2c541bd3ca5542026f1a000f9b114d3 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -110,6 +110,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;
|
|
@@ -147,6 +149,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 a05e8d136dfeb41fb6008cba4d3b4abcddbd9557..4e73960a77165a959e989249fd25a7c5376e50bb 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -47,6 +47,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 {
|
|
|
|
@@ -723,42 +724,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 iteration
|
|
+ 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) {
|
|
@@ -766,15 +784,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();
|
|
}
|
|
}
|