mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-22 07:08:47 +01:00
ac554ad46d
Updated Upstream (Bukkit/CraftBukkit) 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: fa99e752 PR-1007: Add ItemMeta#getAsComponentString() 94a91782 Fix copy-pasted BlockType.Typed documentation 9b34ac8c Largely restore deprecated PotionData API 51a6449b PR-1008: Deprecate ITEMS_TOOLS, removed in 1.20.5 702d15fe Fix Javadoc reference 42f6cdf4 PR-919: Add internal ItemType and BlockType, delegate Material methods to them 237bb37b SPIGOT-1166, SPIGOT-7647: Expose Damager BlockState in EntityDamageByBlockEvent 035ea146 SPIGOT-6993: Allow #setVelocity to change the speed of a fireball and add a note to #setDirection about it 8c7880fb PR-1004: Improve field rename handling and centralize conversion between bukkit and string more 87c90e93 SPIGOT-7650: Add DamageSource for EntityDeathEvent and PlayerDeathEvent CraftBukkit Changes: 4af0f22e8 SPIGOT-7664: Item meta should prevail over block states c2ccc46ec SPIGOT-7666: Fix access to llama and horse special slot 124ac66d7 SPIGOT-7665: Fix ThrownPotion#getEffects() implementation only bringing custom effects 66f1f439a Restore null page behaviour of signed books even though not strictly allowed by API 6118e5398 Fix regression listening to minecraft:brand custom payloads c1a26b366 Fix unnecessary and potential not thread-safe chat visibility check 12360a7ec Remove unused imports 147b098b4 PR-1397: Add ItemMeta#getAsComponentString() 428aefe0e Largely restore deprecated PotionData API afe5b5ee9 PR-1275: Add internal ItemType and BlockType, delegate Material methods to them 8afeafa7d SPIGOT-1166, SPIGOT-7647: Expose Damager BlockState in EntityDamageByBlockEvent 4e7d749d4 SPIGOT-6993: Allow #setVelocity to change the speed of a fireball and add a note to #setDirection about it 441880757 Support both entity_data and bucket_entity_data on axolotl/fish buckets 0e22fdd1e Fix custom direct BlockState being not correctly set in DamageSource f2182ed47 SPIGOT-7659: TropicalFishBucketMeta should use BUCKET_ENTITY_DATA 2a6207fe1 PR-1393: Improve field rename handling and centralize conversion between bukkit and string more c024a5039 SPIGOT-7650: Add DamageSource for EntityDeathEvent and PlayerDeathEvent 741b84480 PR-1390: Improve internal handling of damage sources 0364df4e1 SPIGOT-7657: Error when loading angry entities
380 lines
24 KiB
Diff
380 lines
24 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..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 7fbeebe63f755624b967374072aa2e0565ce8c35..36caf354634d6675a3f1ec6829f4778e1d0623bc 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 d18aa39b93c1d7d2b6f9e24ba99008d277839d92..c10401e587a710d49c4af481c1e531b4bf51f755 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);
|