mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-26 20:46:59 +01:00
de04cbced5
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: f29cb801 Separate checkstyle-suppressions file is not required 86f99bbe SPIGOT-7540, PR-946: Add ServerTickManager API d4119585 SPIGOT-6903, PR-945: Add BlockData#getMapColor b7a2ed41 SPIGOT-7530, PR-947: Add Player#removeResourcePack 9dd56255 SPIGOT-7527, PR-944: Add WindCharge#explode() 994a6163 Attempt upgrade of resolver libraries CraftBukkit Changes: b3b43a6ad Add Checkstyle check for unused imports 13fb3358e SPIGOT-7544: Scoreboard#getEntries() doesn't get entries but class names 3dda99c06 SPIGOT-7540, PR-1312: Add ServerTickManager API 2ab4508c0 SPIGOT-6903, PR-1311: Add BlockData#getMapColor 1dbdbbed4 PR-1238: Remove unnecessary sign ticking 659728d2a MC-264285, SPIGOT-7439, PR-1237: Fix unbreakable flint and steel is completely consumed while igniting creeper e37e29ce0 Increase outdated build delay c00438b39 SPIGOT-7530, PR-1313: Add Player#removeResourcePack 492dd80ce SPIGOT-7527, PR-1310: Add WindCharge#explode() e11fbb9d7 Upgrade MySQL driver 9f3a0bd2a Attempt upgrade of resolver libraries 60d16d7ca PR-1306: Centralize Bukkit and Minecraft entity conversion Spigot Changes: 06d602e7 Rebuild patches
409 lines
25 KiB
Diff
409 lines
25 KiB
Diff
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..17ba07cbd4792f63d88ce29d00da280f30c4abff 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);
|
|
}
|
|
|
|
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 2b998bdbe49bf8211b755e0eb7c1bf13ac280eab..5afeb59ff25fed2d565407acacffec8383398006 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -79,11 +79,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
|
|
|
|
@@ -230,7 +238,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 +262,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 +277,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() {
|
|
+ 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 - 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 e3c1015a5538ad74b9f109837df5916e6ce7e711..baa8497a18474ed142535749edfca200ef31f93e 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -192,6 +192,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) {
|
|
@@ -241,6 +242,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(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
|
|
@@ -410,7 +415,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
// Paper end
|
|
|
|
- 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 68550d4497a5f10bf653482f79be77373df53f27..55f96545d6db95e3e657502a7910d96fded1113e 100644
|
|
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
|
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
|
@@ -50,7 +50,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
|
|
|
|
@@ -136,7 +136,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
|
|
}
|
|
@@ -150,7 +150,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
|
|
}
|
|
@@ -192,13 +192,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 5152979bf81345fc365e7b16028c7d970d2f5856..6e212f672579a3e08dc362c287be59ca5170d717 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -516,18 +516,43 @@ 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
|
|
|
|
- while (iterator.hasNext()) {
|
|
- ChunkHolder playerchunk = (ChunkHolder) iterator.next();
|
|
- LevelChunk chunk = playerchunk.getTickingChunk();
|
|
+ // 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);
|
|
|
|
- if (chunk != null) {
|
|
- list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
|
|
+ // 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 - optimise chunk tick iteration
|
|
|
|
if (this.level.getServer().tickRateManager().runsNormally()) {
|
|
gameprofilerfiller.popPush("naturalSpawnCount");
|
|
@@ -562,38 +587,107 @@ 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 - call player naturally spawn event
|
|
- 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
|
|
+ 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
|
|
+ // 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();
|
|
|
|
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 - 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");
|
|
@@ -605,11 +699,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 695a103f2517f3e888e86e76f3615bd69b00a175..5fe743567f2e9cf0f9442ddd87453c51af8060e6 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -325,6 +325,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);
|