mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-11 09:23:42 +01:00
7fa8870043
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 7902647a PR-737: Update WorldCreator#generatorSettings docs 67556a50 PR-736: Update README CraftBukkit Changes: 10922194 Java 18 support d53c4fb6 PR-1039: Use correct ops in GeneratorSettings a567e4ae PR-1038: Removed the no longer needed getChunkUnchecked method from ChunkProviderServer. 4ac8fcce SPIGOT-6980: Since 1.18.2, World#isChunkLoaded returned false for chunks that have just been loaded (e.g. inside ChunkLoadEvent). e6cc7c70 PR-1035: Update README 3ec79a27 SPIGOT-5140: Call EntityChangeBlockEvent when a ChorusFlower is destroyed by a projectile
203 lines
11 KiB
Diff
203 lines
11 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
|
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 b9d3535dd4e0af771bb74c85f18265e38f383e0e..469b921cceabc3f42961e8aa5314bdc849ad41b9 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -86,11 +86,21 @@ public class ChunkHolder {
|
|
long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
|
|
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
|
|
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
|
|
+ // Paper start - optimise chunk tick iteration
|
|
+ if (this.needsBroadcastChanges()) {
|
|
+ this.chunkMap.needsChangeBroadcasting.add(this);
|
|
+ }
|
|
+ // Paper end - optimise chunk tick iteration
|
|
}
|
|
|
|
void onChunkRemove() {
|
|
this.playersInMobSpawnRange = null;
|
|
this.playersInChunkTickRange = null;
|
|
+ // Paper start - optimise chunk tick iteration
|
|
+ if (this.needsBroadcastChanges()) {
|
|
+ this.chunkMap.needsChangeBroadcasting.remove(this);
|
|
+ }
|
|
+ // Paper end - optimise chunk tick iteration
|
|
}
|
|
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
long lastAutoSaveTime; // Paper - incremental autosave
|
|
@@ -253,7 +263,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();
|
|
}
|
|
|
|
@@ -273,6 +283,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) {
|
|
@@ -286,8 +297,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 5d60fa26e9f5d3c9c57a61c509d5b924bbed3281..ef28e0f57ba593265a3eca4d3f21d0b1b51e8740 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -159,6 +159,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<>();
|
|
|
|
// CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
|
|
public final CallbackExecutor callbackExecutor = new CallbackExecutor();
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index eca75916c5e476db280e58522e5540def4f4de68..d06ad4f2c76eebd3b8191f3de6c0199fa3bb5f22 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -46,6 +46,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana
|
|
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 {
|
|
|
|
@@ -986,34 +987,42 @@ 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.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 (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
|
|
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);
|
|
}
|
|
|
|
@@ -1021,7 +1030,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) {
|
|
@@ -1029,15 +1047,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();
|
|
}
|
|
}
|