Finish chunk tick iteration optimisation port from Moonrise

This commit is contained in:
Spottedleaf 2024-07-17 11:33:13 -07:00
parent 4efd24b336
commit b653276565
5 changed files with 98 additions and 403 deletions

View file

@ -25105,7 +25105,7 @@ index 3dc1daa3c6a04d3ff1a2353773b465fc380994a2..3575782f13a7f3c52e64dc5046803305
}
}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf285f13222e 100644
index 60f678c26fb5144386d8697ddfd5b6d841563b6f..17fafa62f21e505f9f77cf486f7f766a404f7c31 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -46,7 +46,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
@ -25117,7 +25117,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
public static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList();
@@ -71,6 +71,61 @@ public class ServerChunkCache extends ChunkSource {
@@ -71,6 +71,62 @@ public class ServerChunkCache extends ChunkSource {
private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
long chunkFutureAwaitCounter;
// Paper end
@ -25176,10 +25176,11 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
+ return load ? this.syncLoad(chunkX, chunkZ, toStatus) : null;
+ }
+ // Paper end - rewrite chunk system
+ private ServerChunkCache.ChunkAndHolder[] iterationCopy; // Paper - chunk tick iteration optimisations
public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) {
this.level = world;
@@ -97,13 +152,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -97,13 +153,7 @@ public class ServerChunkCache extends ChunkSource {
}
// CraftBukkit end
// Paper start
@ -25194,7 +25195,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
@Nullable
public ChunkAccess getChunkAtImmediately(int x, int z) {
@@ -174,63 +223,25 @@ public class ServerChunkCache extends ChunkSource {
@@ -174,63 +224,25 @@ public class ServerChunkCache extends ChunkSource {
@Nullable
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
@ -25210,13 +25211,13 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
- }
- // Paper end - Perf: Optimise getChunkAt calls for loaded chunks
- ProfilerFiller gameprofilerfiller = this.level.getProfiler();
-
- gameprofilerfiller.incrementCounter("getChunk");
- long k = ChunkPos.asLong(x, z);
+ // Paper start - rewrite chunk system
+ if (leastStatus == ChunkStatus.FULL) {
+ final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(x, z));
- gameprofilerfiller.incrementCounter("getChunk");
- long k = ChunkPos.asLong(x, z);
-
- for (int l = 0; l < 4; ++l) {
- if (k == this.lastChunkPos[l] && leastStatus == this.lastChunkStatus[l]) {
- ChunkAccess ichunkaccess = this.lastChunk[l];
@ -25268,7 +25269,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
}
private void clearCache() {
@@ -261,56 +272,59 @@ public class ServerChunkCache extends ChunkSource {
@@ -261,56 +273,59 @@ public class ServerChunkCache extends ChunkSource {
}
private CompletableFuture<ChunkResult<ChunkAccess>> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
@ -25364,7 +25365,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
}
@Override
@@ -323,16 +337,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -323,16 +338,7 @@ public class ServerChunkCache extends ChunkSource {
}
public boolean runDistanceManagerUpdates() { // Paper - public
@ -25382,7 +25383,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
}
// Paper start
@@ -342,13 +347,14 @@ public class ServerChunkCache extends ChunkSource {
@@ -342,13 +348,14 @@ public class ServerChunkCache extends ChunkSource {
// Paper end
public boolean isPositionTicking(long pos) {
@ -25401,7 +25402,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
this.chunkMap.saveAllChunks(flush);
} // Paper - Timings
@@ -361,12 +367,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -361,12 +368,7 @@ public class ServerChunkCache extends ChunkSource {
}
public void close(boolean save) throws IOException {
@ -25415,7 +25416,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
}
// CraftBukkit start - modelled on below
@@ -394,6 +395,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -394,6 +396,7 @@ public class ServerChunkCache extends ChunkSource {
this.level.getProfiler().popPush("chunks");
if (tickChunks) {
this.level.timings.chunks.startTiming(); // Paper - timings
@ -25423,7 +25424,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
this.tickChunks();
this.level.timings.chunks.stopTiming(); // Paper - timings
this.chunkMap.tick();
@@ -408,6 +410,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -408,6 +411,7 @@ public class ServerChunkCache extends ChunkSource {
}
private void tickChunks() {
@ -25431,7 +25432,45 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
long i = this.level.getGameTime();
long j = i - this.lastInhabitedUpdate;
@@ -460,14 +463,19 @@ public class ServerChunkCache extends ChunkSource {
@@ -417,18 +421,29 @@ public class ServerChunkCache extends ChunkSource {
gameprofilerfiller.push("pollingChunks");
gameprofilerfiller.push("filteringLoadedChunks");
- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size());
- Iterator iterator = this.chunkMap.getChunks().iterator();
- if (this.level.getServer().tickRateManager().runsNormally()) this.level.timings.chunkTicks.startTiming(); // Paper
+ // Paper start - chunk tick iteration optimisations
+ List<ServerChunkCache.ChunkAndHolder> list;
+ {
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerChunkCache.ChunkAndHolder> tickingChunks =
+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) this.level).moonrise$getTickingChunks();
- while (iterator.hasNext()) {
- ChunkHolder playerchunk = (ChunkHolder) iterator.next();
- LevelChunk chunk = playerchunk.getTickingChunk();
+ final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
+ final int size = tickingChunks.size();
- if (chunk != null) {
- list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
+ if (this.iterationCopy == null || this.iterationCopy.length < size) {
+ this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length];
}
+ System.arraycopy(raw, 0, this.iterationCopy, 0, size);
+
+ list = it.unimi.dsi.fastutil.objects.ObjectArrayList.wrap(
+ this.iterationCopy, size
+ );
}
+ // Paper end - chunk tick iteration optimisations
+ Iterator iterator = null; // Paper - chunk tick iteration optimisations
+ if (this.level.getServer().tickRateManager().runsNormally()) this.level.timings.chunkTicks.startTiming(); // Paper
+
+ // Paper - chunk tick iteration optimisations
if (this.level.tickRateManager().runsNormally()) {
gameprofilerfiller.popPush("naturalSpawnCount");
@@ -460,14 +475,19 @@ public class ServerChunkCache extends ChunkSource {
LevelChunk chunk1 = chunkproviderserver_a.chunk;
ChunkPos chunkcoordintpair = chunk1.getPos();
@ -25453,7 +25492,35 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
}
}
}
@@ -493,11 +501,12 @@ public class ServerChunkCache extends ChunkSource {
@@ -482,22 +502,35 @@ public class ServerChunkCache extends ChunkSource {
}
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
- });
+ // Paper start - chunk tick iteration optimisations
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
+ {
+ final it.unimi.dsi.fastutil.objects.ObjectArrayList<net.minecraft.server.level.ServerChunkCache.ChunkAndHolder> chunks = (it.unimi.dsi.fastutil.objects.ObjectArrayList<net.minecraft.server.level.ServerChunkCache.ChunkAndHolder>)list;
+ final ServerChunkCache.ChunkAndHolder[] raw = chunks.elements();
+ final int size = chunks.size();
+
+ Objects.checkFromToIndex(0, size, raw.length);
+ for (int idx = 0; idx < size; ++idx) {
+ final ServerChunkCache.ChunkAndHolder holder = raw[idx];
+ raw[idx] = null;
+
+ holder.holder().broadcastChanges(holder.chunk());
+ }
+ }
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
+ // Paper end - chunk tick iteration optimisations
gameprofilerfiller.pop();
gameprofilerfiller.pop();
}
}
private void getFullChunk(long pos, Consumer<LevelChunk> chunkConsumer) {
@ -25470,7 +25537,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
}
@@ -591,6 +600,12 @@ public class ServerChunkCache extends ChunkSource {
@@ -591,6 +624,12 @@ public class ServerChunkCache extends ChunkSource {
this.chunkMap.setServerViewDistance(watchDistance);
}
@ -25483,7 +25550,7 @@ index 60f678c26fb5144386d8697ddfd5b6d841563b6f..9aec513f05b10e78579f79ccda6acf28
public void setSimulationDistance(int simulationDistance) {
this.distanceManager.updateSimulationDistance(simulationDistance);
}
@@ -669,21 +684,19 @@ public class ServerChunkCache extends ChunkSource {
@@ -669,21 +708,19 @@ public class ServerChunkCache extends ChunkSource {
@Override
// CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
public boolean pollTask() {

View file

@ -37,10 +37,10 @@ index edb36dee707433d4f9419aef6ac6cc0bec5f285e..c282566ee45d79b4fb3297989e054c80
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 9aec513f05b10e78579f79ccda6acf285f13222e..96a03770d47c47e07615815bcc3d474d6cd7711b 100644
index 17fafa62f21e505f9f77cf486f7f766a404f7c31..2ed9a088d0fd56043d161909ce8e0df683a6413a 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -437,7 +437,19 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -449,14 +449,26 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
gameprofilerfiller.popPush("naturalSpawnCount");
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
int k = this.distanceManager.getNaturalSpawnChunkCount();
@ -61,6 +61,14 @@ index 9aec513f05b10e78579f79ccda6acf285f13222e..96a03770d47c47e07615815bcc3d474d
this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
this.lastSpawnState = spawnercreature_d;
gameprofilerfiller.popPush("spawnAndTick");
boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
- Util.shuffle(list, this.level.random);
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.level.random); // Paper - per player mob spawns - do not need this when per-player is enabled
// Paper start - PlayerNaturallySpawnCreaturesEvent
int chunkRange = level.spigotConfig.mobSpawnRange;
chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index fd61e10fd3151bf8cc206a93d4ede22a7c681dbf..23ddb7735623e502eda2b7b3029c6597f4b0f9a0 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java

View file

@ -37,10 +37,10 @@ index c282566ee45d79b4fb3297989e054c803873a294..dc64227e3b0d7855ef8870935aa437b7
}
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 96a03770d47c47e07615815bcc3d474d6cd7711b..5f195875564822f422127462fbbeea5df4a4e1cb 100644
index 2ed9a088d0fd56043d161909ce8e0df683a6413a..cdb9160244cc69acd36ac9afcfe109a15ab2ab58 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -443,7 +443,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -455,7 +455,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
// re-set mob counts
for (ServerPlayer player : this.level.players) {

View file

@ -6,10 +6,10 @@ Subject: [PATCH] Write SavedData IO async
Co-Authored-By: Shane Freeder <theboyetronic@gmail.com>
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 5f195875564822f422127462fbbeea5df4a4e1cb..5149f7a5f0ee8620df582dcf26151e5842463cae 100644
index cdb9160244cc69acd36ac9afcfe109a15ab2ab58..8a0b00d645e4cf2ca96ec7e8ebc6ef726a0ab8b0 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -368,6 +368,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -369,6 +369,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
public void close(boolean save) throws IOException {
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(save, true); // Paper - rewrite chunk system

View file

@ -1,380 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 23 Sep 2023 21:36:36 -0700
Subject: [PATCH] Optimise chunk tick iteration
When per-player mob spawning is enabled we do not need to randomly
shuffle the chunk list. Additionally, we can use the NearbyPlayers
class to quickly retrieve nearby players instead of possible
searching all players on the server.
diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
index c3ce8a42dddd76b7189ad5685b23f9d9f8ccadb3..f164256d59b761264876ca0c85f812d101bfd5de 100644
--- a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
+++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
@@ -17,7 +17,8 @@ public final class NearbyPlayers {
GENERAL_SMALL,
GENERAL_REALLY_SMALL,
TICK_VIEW_DISTANCE,
- VIEW_DISTANCE;
+ VIEW_DISTANCE, // Paper - optimise chunk iteration
+ SPAWN_RANGE, // Paper - optimise chunk iteration
}
private static final NearbyMapType[] MOB_TYPES = NearbyMapType.values();
@@ -26,10 +27,12 @@ public final class NearbyPlayers {
private static final int GENERAL_AREA_VIEW_DISTANCE = 33;
private static final int GENERAL_SMALL_VIEW_DISTANCE = 10;
private static final int GENERAL_REALLY_SMALL_VIEW_DISTANCE = 3;
+ private static final int SPAWN_RANGE_VIEW_DISTANCE = net.minecraft.server.level.DistanceManager.MOB_SPAWN_RANGE; // Paper - optimise chunk iteration
public static final int GENERAL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_AREA_VIEW_DISTANCE << 4);
public static final int GENERAL_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_SMALL_VIEW_DISTANCE << 4);
public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4);
+ public static final int SPAWN_RANGE_VIEW_DISTANCE_BLOCKS = (SPAWN_RANGE_VIEW_DISTANCE << 4); // Paper - optimise chunk iteration
private final ServerLevel world;
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
@@ -80,6 +83,7 @@ public final class NearbyPlayers {
players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE);
players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getTickViewDistance(player));
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getLoadViewDistance(player));
+ players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, SPAWN_RANGE_VIEW_DISTANCE); // Paper - optimise chunk iteration
}
public TrackedChunk getChunk(final ChunkPos pos) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 13d15a135dd0373bef4a5ac9ffb56dbbf53353a0..472b9494f8a34a8ba90d6a2936b0db7530a229ad 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -77,11 +77,19 @@ public class ChunkHolder {
// Paper start
public void onChunkAdd() {
-
+ // Paper start - optimise chunk tick iteration
+ if (this.needsBroadcastChanges()) {
+ this.chunkMap.needsChangeBroadcasting.add(this);
+ }
+ // Paper end - optimise chunk tick iteration
}
public void onChunkRemove() {
-
+ // Paper start - optimise chunk tick iteration
+ if (this.needsBroadcastChanges()) {
+ this.chunkMap.needsChangeBroadcasting.remove(this);
+ }
+ // Paper end - optimise chunk tick iteration
}
// Paper end
@@ -216,7 +224,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();
}
@@ -236,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) {
@@ -254,9 +263,19 @@ public class ChunkHolder {
this.broadcast(this.getPlayers(onChunkViewEdge), packet); // Paper - rewrite chunk system
}
// Paper end - starlight
+ // Paper start - optimise chunk tick iteration
+ public final boolean needsBroadcastChanges() {
+ return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty();
+ }
+
+ private void addToBroadcastMap() {
+ io.papermc.paper.util.TickThread.ensureTickThread(this.chunkMap.level, this.pos, "Asynchronous ChunkHolder update is not allowed");
+ 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 - optimise chunk tick iteration; 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 48f7997e8a20f5a5a77516cbde990d0aacc2078a..dbe9df1e1973db133f7c8516256697ef7c968137 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -194,6 +194,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerEntityTrackerTrackMaps[i].remove(player);
}
// Paper end - use distance map to optimise tracker
+ this.playerMobSpawnMap.remove(player); // Paper - optimise chunk tick iteration
}
void updateMaps(ServerPlayer player) {
@@ -243,6 +244,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public final io.papermc.paper.util.player.NearbyPlayers nearbyPlayers;
// Paper end
+ // Paper start - optimise chunk tick iteration
+ public final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>();
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
+ // Paper end - optimise chunk tick iteration
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
super(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
@@ -411,7 +416,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// Paper end - Optional per player mob spawns
- private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
+ public static double euclideanDistanceSquared(ChunkPos pos, Entity entity) { // Paper - optimise chunk iteration; public
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
double d2 = d0 - entity.getX();
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index ed5154e41ca858f4d6b4d1c276c66831c038d2a6..cdb3c2cde5d9133ef60cf96d91762e6a7c8aeb4a 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -49,7 +49,7 @@ public abstract class DistanceManager {
private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
// Paper - rewrite chunk system
- private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
+ public static final int MOB_SPAWN_RANGE = 8; //private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - optimise chunk tick iteration
// Paper - rewrite chunk system
private final ChunkMap chunkMap; // Paper
@@ -135,7 +135,7 @@ public abstract class DistanceManager {
long i = chunkcoordintpair.toLong();
// Paper - no longer used
- this.naturalSpawnChunkCounter.update(i, 0, true);
+ //this.naturalSpawnChunkCounter.update(i, 0, true); // Paper - optimise chunk tick iteration
//this.playerTicketManager.update(i, 0, true); // Paper - no longer used
//this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
}
@@ -149,7 +149,7 @@ public abstract class DistanceManager {
if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully
if (objectset == null || objectset.isEmpty()) { // Paper
this.playersPerChunk.remove(i);
- this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
+ //this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false); // Paper - optimise chunk tick iteration
//this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
//this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
}
@@ -191,13 +191,11 @@ public abstract class DistanceManager {
}
public int getNaturalSpawnChunkCount() {
- this.naturalSpawnChunkCounter.runAllUpdates();
- return this.naturalSpawnChunkCounter.chunks.size();
+ return this.chunkMap.playerMobSpawnMap.size(); // Paper - optimise chunk tick iteration
}
public boolean hasPlayersNearby(long chunkPos) {
- this.naturalSpawnChunkCounter.runAllUpdates();
- return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos);
+ return this.chunkMap.playerMobSpawnMap.getObjectsInRange(chunkPos) != null; // Paper - optimise chunk tick iteration
}
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 01577dbb16ff6abc9edcf897f7fadaad14350184..e2c67c011503c9c37b9d637c0268717baac13e32 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -499,18 +499,10 @@ public class ServerChunkCache extends ChunkSource {
gameprofilerfiller.push("pollingChunks");
gameprofilerfiller.push("filteringLoadedChunks");
- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size());
- Iterator iterator = this.chunkMap.getChunks().iterator();
+ // Paper - optimise chunk tick iteration
if (this.level.getServer().tickRateManager().runsNormally()) 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 - optimise chunk tick iteration
if (this.level.tickRateManager().runsNormally()) {
gameprofilerfiller.popPush("naturalSpawnCount");
@@ -545,38 +537,109 @@ public class ServerChunkCache extends ChunkSource {
gameprofilerfiller.popPush("spawnAndTick");
boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
- Util.shuffle(list, this.level.random);
- // Paper start - PlayerNaturallySpawnCreaturesEvent
- int chunkRange = level.spigotConfig.mobSpawnRange;
- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
- chunkRange = Math.min(chunkRange, 8);
- for (ServerPlayer entityPlayer : this.level.players()) {
- entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
- entityPlayer.playerNaturallySpawnedEvent.callEvent();
+ // Paper start - optimise chunk tick iteration
+ ChunkMap playerChunkMap = this.chunkMap;
+ for (ServerPlayer player : this.level.players) {
+ if (!player.affectsSpawning || player.isSpectator()) {
+ playerChunkMap.playerMobSpawnMap.remove(player);
+ player.playerNaturallySpawnedEvent = null;
+ player.lastEntitySpawnRadiusSquared = -1.0;
+ continue;
+ }
+
+ int viewDistance = io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player);
+
+ // copied and modified from isOutisdeRange
+ int chunkRange = (int)level.spigotConfig.mobSpawnRange;
+ chunkRange = (chunkRange > viewDistance) ? viewDistance : chunkRange;
+ chunkRange = (chunkRange > DistanceManager.MOB_SPAWN_RANGE) ? DistanceManager.MOB_SPAWN_RANGE : chunkRange;
+
+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), (byte)chunkRange);
+ event.callEvent();
+ if (event.isCancelled() || event.getSpawnRadius() < 0) {
+ playerChunkMap.playerMobSpawnMap.remove(player);
+ player.playerNaturallySpawnedEvent = null;
+ player.lastEntitySpawnRadiusSquared = -1.0;
+ continue;
+ }
+
+ int range = Math.min(event.getSpawnRadius(), DistanceManager.MOB_SPAWN_RANGE); // limit to max spawn range
+ int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkCoordinate(player.getX());
+ int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkCoordinate(player.getZ());
+
+ playerChunkMap.playerMobSpawnMap.addOrUpdate(player, chunkX, chunkZ, range);
+ player.lastEntitySpawnRadiusSquared = (double)((range << 4) * (range << 4)); // used in anyPlayerCloseEnoughForSpawning
+ player.playerNaturallySpawnedEvent = event;
}
- // Paper end - PlayerNaturallySpawnCreaturesEvent
+ // Paper end - optimise chunk tick iteration
int l = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
- Iterator iterator1 = list.iterator();
+ // Paper - optimise chunk tick iteration
int chunksTicked = 0; // Paper
- while (iterator1.hasNext()) {
- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
- LevelChunk chunk1 = chunkproviderserver_a.chunk;
+ // Paper start - optimise chunk tick iteration
+ io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = this.chunkMap.getNearbyPlayers(); // Paper - optimise chunk tick iteration
+ Iterator<LevelChunk> chunkIterator;
+ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ chunkIterator = this.tickingChunks.iterator();
+ } else {
+ chunkIterator = this.tickingChunks.unsafeIterator();
+ List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.tickingChunks.size());
+ while (chunkIterator.hasNext()) {
+ shuffled.add(chunkIterator.next());
+ }
+ Util.shuffle(shuffled, this.level.random);
+ chunkIterator = shuffled.iterator();
+ }
+ try {
+ // Paper end - optimise chunk tick iteration
+ while (chunkIterator.hasNext()) {
+ LevelChunk chunk1 = chunkIterator.next();
+ // Paper end - optimise chunk tick iteration
ChunkPos chunkcoordintpair = chunk1.getPos();
- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) {
+ // Paper start - optimise chunk tick iteration
+ com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> playersNearby
+ = nearbyPlayers.getPlayers(chunkcoordintpair, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.SPAWN_RANGE);
+ if (playersNearby == null) {
+ continue;
+ }
+ Object[] rawData = playersNearby.getRawData();
+ boolean spawn = false;
+ boolean tick = false;
+ for (int itr = 0, len = playersNearby.size(); itr < len; ++itr) {
+ ServerPlayer player = (ServerPlayer)rawData[itr];
+ if (player.isSpectator()) {
+ continue;
+ }
+
+ double distance = ChunkMap.euclideanDistanceSquared(chunkcoordintpair, player);
+ spawn |= player.lastEntitySpawnRadiusSquared >= distance;
+ tick |= ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) * ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) >= distance;
+ if (spawn & tick) {
+ break;
+ }
+ }
+ if (tick && chunk1.chunkStatus.isOrAfter(net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING)) {
+ // Paper end - optimise chunk tick iteration
chunk1.incrementInhabitedTime(j);
- if (flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot
+ if (spawn && flag && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair)) { // Spigot // Paper - 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 - optimise chunk tick iteration
this.level.tickChunk(chunk1, l);
if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
}
}
}
+ // Paper start - optimise chunk tick iteration
+ } finally {
+ if (chunkIterator 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");
@@ -588,11 +651,23 @@ public class ServerChunkCache extends ChunkSource {
}
gameprofilerfiller.popPush("broadcast");
- list.forEach((chunkproviderserver_a1) -> {
+ // Paper - optimise chunk tick iteration
this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
- chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
+ // Paper start - optimise chunk tick iteration
+ if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
+ it.unimi.dsi.fastutil.objects.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);
+ }
+ }
+ }
+ // Paper end - optimise chunk tick iteration
this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
- });
+ // Paper - optimise chunk tick iteration
gameprofilerfiller.pop();
gameprofilerfiller.pop();
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 09a80b6816e50ea0d733725c59164520136308d6..6a4637eef14cbd84bbe26ef16f004b8f93367a3d 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -342,6 +342,9 @@ public class ServerPlayer extends Player {
});
}
// Paper end - replace player chunk loader
+ // Paper start - optimise chunk tick iteration
+ public double lastEntitySpawnRadiusSquared = -1.0;
+ // Paper end - optimise chunk tick iteration
public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) {
super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile);