From 38dc3b25d8d7ff5a14fbcc70ef16541ed396db4f Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 23 Sep 2023 21:43:10 -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. --- ...erCloseEnoughForSpawning-to-use-dist.patch | 346 ------------ .../0685-Optimise-chunk-tick-iteration.patch | 215 ------- patches/server/0009-MC-Utils.patch | 527 ++++++++++++++++-- patches/server/0014-Timings-v2.patch | 10 +- patches/server/0016-Starlight.patch | 2 +- .../server/0018-Rewrite-chunk-system.patch | 371 +++--------- ...event-tile-entity-and-entity-crashes.patch | 4 +- .../0060-Add-exception-reporting-event.patch | 6 +- ...ldBounds-and-getBlockState-for-inlin.patch | 4 +- ...80-Configurable-Chunk-Inhabited-Time.patch | 4 +- ...0-PlayerNaturallySpawnCreaturesEvent.patch | 6 +- ...ies-option-to-debug-dupe-uuid-issues.patch | 6 +- ...316-Fix-World-isChunkGenerated-calls.patch | 6 +- .../0328-Duplicate-UUID-Resolve-Option.patch | 8 +- .../0340-Tracking-Range-Improvements.patch | 4 +- ...ement-optional-per-player-mob-spawns.patch | 365 +----------- patches/server/0343-Anti-Xray.patch | 4 +- ...e-getChunkAt-calls-for-loaded-chunks.patch | 2 +- .../0347-Add-debug-for-sync-chunk-loads.patch | 2 +- .../server/0357-Optimise-Chunk-getFluid.patch | 6 +- ...spawn-settings-and-per-player-option.patch | 2 +- ...-PlayerChunkMap-adds-crashing-server.patch | 4 +- .../server/0366-Don-t-tick-dead-players.patch | 4 +- ...move-existing-players-to-world-spawn.patch | 6 +- .../0371-Improved-Watchdog-Support.patch | 4 +- ...vent-opening-inventories-when-frozen.patch | 6 +- ...-Implement-Player-Client-Options-API.patch | 4 +- ...g-Broken-behavior-of-PlayerJoinEvent.patch | 8 +- ...ktraces-in-log-messages-crash-report.patch | 4 +- ...ld-Difficulty-Remembering-Difficulty.patch | 4 +- ...-incremental-chunk-and-player-saving.patch | 4 +- .../server/0488-Add-API-for-quit-reason.patch | 4 +- ...-shield-blocking-on-dimension-change.patch | 4 +- ...ditions-to-PlayerGameModeChangeEvent.patch | 8 +- ...PlayerDropItemEvent-using-wrong-item.patch | 4 +- ...k-event-leave-message-not-being-sent.patch | 4 +- ...n-t-apply-cramming-damage-to-players.patch | 4 +- .../server/0647-Add-PlayerSetSpawnEvent.patch | 8 +- ...-logic-for-inventories-on-chunk-unlo.patch | 4 +- ...aper-mobcaps-and-paper-playermobcaps.patch | 4 +- .../0689-Execute-chunk-tasks-mid-tick.patch | 2 +- ...primise-map-impl-for-tracked-players.patch | 4 +- .../0704-Ensure-valid-vehicle-status.patch | 4 +- .../server/0728-Collision-optimisations.patch | 6 +- ...ables-running-when-mob-loot-gamerule.patch | 4 +- ...-strict-advancement-dimension-checks.patch | 4 +- .../0860-Fix-a-bunch-of-vanilla-bugs.patch | 4 +- ...5-Add-PlayerInventorySlotChangeEvent.patch | 4 +- .../0892-Sync-offhand-slot-in-menus.patch | 4 +- .../0893-Player-Entity-Tracking-Events.patch | 4 +- ...ng-PreCreatureSpawnEvent-with-per-pl.patch | 39 +- ...ntity-tracking-range-by-Y-coordinate.patch | 4 +- ...-titleOverride-to-InventoryOpenEvent.patch | 6 +- .../1029-Optimise-chunk-tick-iteration.patch | 374 +++++++++++++ 54 files changed, 1115 insertions(+), 1340 deletions(-) delete mode 100644 patches/removed/1.20.2/0684-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch delete mode 100644 patches/removed/1.20.2/0685-Optimise-chunk-tick-iteration.patch create mode 100644 patches/server/1029-Optimise-chunk-tick-iteration.patch diff --git a/patches/removed/1.20.2/0684-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch b/patches/removed/1.20.2/0684-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch deleted file mode 100644 index 5554cf8ae0..0000000000 --- a/patches/removed/1.20.2/0684-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch +++ /dev/null @@ -1,346 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Tue, 5 May 2020 20:40:53 -0700 -Subject: [PATCH] Optimize anyPlayerCloseEnoughForSpawning to use distance maps - -Use a distance map to find the players in range quickly - -diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index c5389e7f3665c06e487dfde3200b7e229694fbd2..4164204ba80f68a768de0ed1721c6447b972a631 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -79,16 +79,29 @@ public class ChunkHolder { - - // Paper start - public void onChunkAdd() { -- -+ // Paper start - optimise anyPlayerCloseEnoughForSpawning -+ long key = io.papermc.paper.util.MCUtil.getCoordinateKey(this.pos); -+ this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); -+ this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); -+ // Paper end - optimise anyPlayerCloseEnoughForSpawning - } - - public void onChunkRemove() { -- -+ // Paper start - optimise anyPlayerCloseEnoughForSpawning -+ this.playersInMobSpawnRange = null; -+ this.playersInChunkTickRange = null; -+ // Paper end - optimise anyPlayerCloseEnoughForSpawning - } - // Paper end - - public final io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder; // Paper - rewrite chunk system - -+ // Paper start - optimise anyPlayerCloseEnoughForSpawning -+ // cached here to avoid a map lookup -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInMobSpawnRange; -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInChunkTickRange; -+ // Paper end - optimise anyPlayerCloseEnoughForSpawning -+ - // Paper start - replace player chunk loader - private final com.destroystokyo.paper.util.maplist.ReferenceList playersSentChunkTo = new com.destroystokyo.paper.util.maplist.ReferenceList<>(); - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 52d9d0f2366f292c56eee9fe241cb43bc2900b23..80b2ff12e48e1aabebd9ebcf5958c1f5d073d55b 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -174,12 +174,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return net.minecraft.server.MinecraftServer.getServer().getScaledTrackingDistance(vanilla); - } - // Paper end - use distance map to optimise tracker -+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning -+ // A note about the naming used here: -+ // Previously, mojang used a "spawn range" of 8 for controlling both ticking and -+ // mob spawn range. However, spigot makes the spawn range configurable by -+ // checking if the chunk is in the tick range (8) and the spawn range -+ // obviously this means a spawn range > 8 cannot be implemented -+ -+ // these maps are named after spigot's uses -+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick -+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap; -+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning - - void addPlayerToDistanceMaps(ServerPlayer player) { - this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader - int chunkX = MCUtil.getChunkCoordinate(player.getX()); - int chunkZ = MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated -+ this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning - // Paper start - per player mob spawning - if (this.playerMobDistanceMap != null) { - this.playerMobDistanceMap.add(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player)); -@@ -198,6 +210,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - void removePlayerFromDistanceMaps(ServerPlayer player) { - this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader - -+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning -+ this.playerMobSpawnMap.remove(player); -+ this.playerChunkTickRangeMap.remove(player); -+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning - // Paper start - per player mob spawning - if (this.playerMobDistanceMap != null) { - this.playerMobDistanceMap.remove(player); -@@ -215,6 +231,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - int chunkZ = MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated - this.level.playerChunkLoader.updatePlayer(player); // Paper - replace chunk loader -+ this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning - // Paper start - per player mob spawning - if (this.playerMobDistanceMap != null) { - this.playerMobDistanceMap.update(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player)); -@@ -356,6 +373,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); - } - // Paper end - use distance map to optimise entity tracker -+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning -+ this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, -+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ)); -+ if (playerChunk != null) { -+ playerChunk.playersInChunkTickRange = newState; -+ } -+ }, -+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ)); -+ if (playerChunk != null) { -+ playerChunk.playersInChunkTickRange = newState; -+ } -+ }); -+ this.playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, -+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ)); -+ if (playerChunk != null) { -+ playerChunk.playersInMobSpawnRange = newState; -+ } -+ }, -+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ)); -+ if (playerChunk != null) { -+ playerChunk.playersInMobSpawnRange = newState; -+ } -+ }); -+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning - } - - protected ChunkGenerator generator() { -@@ -930,43 +979,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return this.anyPlayerCloseEnoughForSpawning(pos, false); - } - -- boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) { -- int chunkRange = level.spigotConfig.mobSpawnRange; -- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange; -- chunkRange = (chunkRange > 8) ? 8 : chunkRange; -- -- final int finalChunkRange = chunkRange; // Paper for lambda below -- //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event -- double blockRange = 16384.0D; // Paper -- // Spigot end -- long i = chunkcoordintpair.toLong(); -+ // Paper start - optimise anyPlayerCloseEnoughForSpawning -+ final boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) { -+ return this.anyPlayerCloseEnoughForSpawning(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange); -+ } - -- if (!this.distanceManager.hasPlayersNearby(i)) { -+ final boolean anyPlayerCloseEnoughForSpawning(ChunkHolder playerchunk, ChunkPos chunkcoordintpair, boolean reducedRange) { -+ // this function is so hot that removing the map lookup call can have an order of magnitude impact on its performance -+ // tested and confirmed via System.nanoTime() -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInRange = reducedRange ? playerchunk.playersInMobSpawnRange : playerchunk.playersInChunkTickRange; -+ if (playersInRange == null) { - return false; -- } else { -- Iterator iterator = this.playerMap.getPlayers(i).iterator(); -- -- ServerPlayer entityplayer; -+ } -+ Object[] backingSet = playersInRange.getBackingSet(); - -- do { -- if (!iterator.hasNext()) { -- return false; -+ if (reducedRange) { -+ for (int i = 0, len = backingSet.length; i < len; ++i) { -+ Object raw = backingSet[i]; -+ if (!(raw instanceof ServerPlayer player)) { -+ continue; - } -- -- entityplayer = (ServerPlayer) iterator.next(); -- // Paper start - add PlayerNaturallySpawnCreaturesEvent -- com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; -- blockRange = 16384.0D; -- if (reducedRange) { -- event = entityplayer.playerNaturallySpawnedEvent; -- if (event == null || event.isCancelled()) return false; -- blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4)); -+ // don't check spectator and whatnot, already handled by mob spawn map update -+ if (euclideanDistanceSquared(chunkcoordintpair, player) < player.lastEntitySpawnRadiusSquared) { -+ return true; // in range - } -- // Paper end -- } while (!this.playerIsCloseEnoughForSpawning(entityplayer, chunkcoordintpair, blockRange)); // Spigot -- -- return true; -+ } -+ } else { -+ final double range = (DistanceManager.MOB_SPAWN_RANGE * 16) * (DistanceManager.MOB_SPAWN_RANGE * 16); -+ // before spigot, mob spawn range was actually mob spawn range + tick range, but it was split -+ for (int i = 0, len = backingSet.length; i < len; ++i) { -+ Object raw = backingSet[i]; -+ if (!(raw instanceof ServerPlayer player)) { -+ continue; -+ } -+ // don't check spectator and whatnot, already handled by mob spawn map update -+ if (euclideanDistanceSquared(chunkcoordintpair, player) < range) { -+ return true; // in range -+ } -+ } - } -+ // no players in range -+ return false; -+ // Paper end - optimise anyPlayerCloseEnoughForSpawning - } - - public List getPlayersCloseForSpawning(ChunkPos pos) { -diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java -index 0a926afa06a5e37cf2650afa1b5099a2a9ffa659..ae4a4710ba07614be42cdcbf52cee04cfa08466b 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> 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 - no longer used - // 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 - no longer used - //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 - no longer used - //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,17 @@ public abstract class DistanceManager { - } - - public int getNaturalSpawnChunkCount() { -- this.naturalSpawnChunkCounter.runAllUpdates(); -- return this.naturalSpawnChunkCounter.chunks.size(); -+ // Paper start - use distance map to implement -+ // note: this is the spawn chunk count -+ return this.chunkMap.playerChunkTickRangeMap.size(); -+ // Paper end - use distance map to implement - } - - public boolean hasPlayersNearby(long chunkPos) { -- this.naturalSpawnChunkCounter.runAllUpdates(); -- return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos); -+ // Paper start - use distance map to implement -+ // note: this is the is spawn chunk method -+ return this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(chunkPos) != null; -+ // Paper end - use distance map to implement - } - - 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 3f5d572994bc8b3f1e106105dc0bb202ad005b8c..5c5fe2087a7617324ab8e18389e3ffa9ac413026 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -517,6 +517,37 @@ public class ServerChunkCache extends ChunkSource { - if (flag) { - this.chunkMap.tick(); - } else { -+ // Paper start - optimize isOutisdeRange -+ ChunkMap playerChunkMap = this.chunkMap; -+ for (ServerPlayer player : this.level.players) { -+ if (!player.affectsSpawning || player.isSpectator()) { -+ playerChunkMap.playerMobSpawnMap.remove(player); -+ continue; -+ } -+ -+ int viewDistance = this.chunkMap.getEffectiveViewDistance(); -+ -+ // copied and modified from isOutisdeRange -+ int chunkRange = level.spigotConfig.mobSpawnRange; -+ chunkRange = (chunkRange > viewDistance) ? (byte)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.playerChunkTickRangeMap.getLastViewDistance(player) == -1) { -+ playerChunkMap.playerMobSpawnMap.remove(player); -+ continue; -+ } -+ -+ int range = Math.min(event.getSpawnRadius(), 32); // limit to max view distance -+ int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); -+ int chunkZ = io.papermc.paper.util.MCUtil.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 - optimize isOutisdeRange - LevelData worlddata = this.level.getLevelData(); - ProfilerFiller gameprofilerfiller = this.level.getProfiler(); - -@@ -560,15 +591,7 @@ public class ServerChunkCache extends ChunkSource { - boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit - - Collections.shuffle(list); -- // 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 end -+ // Paper - moved natural spawn event up - Iterator iterator1 = list.iterator(); - - while (iterator1.hasNext()) { -@@ -576,9 +599,9 @@ public class ServerChunkCache extends ChunkSource { - LevelChunk chunk1 = chunkproviderserver_a.chunk; - ChunkPos chunkcoordintpair = chunk1.getPos(); - -- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) { -+ if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning - chunk1.incrementInhabitedTime(j); -- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot -+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning - NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); - } - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index ce2972c62f91724f6a9e56c8103fba219f7a60bb..6c00162847b59ffa3d1403b956545c57ba539139 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -273,6 +273,7 @@ public class ServerPlayer extends Player { - public Integer clientViewDistance; - // CraftBukkit end - public boolean isRealPlayer; // Paper -+ public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks - public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet cachedSingleHashSet; // Paper - public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event diff --git a/patches/removed/1.20.2/0685-Optimise-chunk-tick-iteration.patch b/patches/removed/1.20.2/0685-Optimise-chunk-tick-iteration.patch deleted file mode 100644 index 760883c7b9..0000000000 --- a/patches/removed/1.20.2/0685-Optimise-chunk-tick-iteration.patch +++ /dev/null @@ -1,215 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -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 4164204ba80f68a768de0ed1721c6447b972a631..4ae1ba645d9fdc1eb6d5a3e4f8ceed9b4841e003 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -84,6 +84,11 @@ public class ChunkHolder { - this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); - this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); - // Paper end - optimise anyPlayerCloseEnoughForSpawning -+ // Paper start - optimise chunk tick iteration -+ if (this.needsBroadcastChanges()) { -+ this.chunkMap.needsChangeBroadcasting.add(this); -+ } -+ // Paper end - optimise chunk tick iteration - } - - public void onChunkRemove() { -@@ -91,6 +96,11 @@ public class ChunkHolder { - this.playersInMobSpawnRange = null; - this.playersInChunkTickRange = null; - // Paper end - optimise anyPlayerCloseEnoughForSpawning -+ // Paper start - optimise chunk tick iteration -+ if (this.needsBroadcastChanges()) { -+ this.chunkMap.needsChangeBroadcasting.remove(this); -+ } -+ // Paper end - optimise chunk tick iteration - } - // Paper end - -@@ -230,7 +240,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 +264,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 +279,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(); - 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 80b2ff12e48e1aabebd9ebcf5958c1f5d073d55b..bcf0dfe50add8e260a280e45673727f964bac6fd 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -115,6 +115,8 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator; - import org.bukkit.entity.Player; - // CraftBukkit end - -+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -+ - public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { - - private static final byte CHUNK_TYPE_REPLACEABLE = -1; -@@ -152,6 +154,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - private final Queue unloadQueue; - int viewDistance; - public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper -+ public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); - - // Paper - rewrite chunk system - -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 5c5fe2087a7617324ab8e18389e3ffa9ac413026..828de28f2777e2477a9c6545c8af96c4ca4e352b 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -48,6 +48,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelData; - import net.minecraft.world.level.storage.LevelStorageSource; -+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper - - public class ServerChunkCache extends ChunkSource { - -@@ -574,42 +575,59 @@ public class ServerChunkCache extends ChunkSource { - - this.lastSpawnState = spawnercreature_d; - gameprofilerfiller.popPush("filteringLoadedChunks"); -- List 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 iteratio -+ Iterator iterator1; -+ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { -+ iterator1 = this.entityTickingChunks.iterator(); -+ } else { -+ iterator1 = this.entityTickingChunks.unsafeIterator(); -+ List shuffled = Lists.newArrayListWithCapacity(this.entityTickingChunks.size()); -+ while (iterator1.hasNext()) { -+ shuffled.add(iterator1.next()); -+ } -+ Collections.shuffle(shuffled); -+ iterator1 = shuffled.iterator(); -+ } -+ try { - while (iterator1.hasNext()) { -- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next(); -- LevelChunk chunk1 = chunkproviderserver_a.chunk; -+ LevelChunk chunk1 = iterator1.next(); -+ ChunkHolder holder = chunk1.playerChunk; -+ if (holder != null) { -+ // Paper - move down -+ // Paper end - optimise chunk tick iteration - ChunkPos chunkcoordintpair = chunk1.getPos(); - -- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning -+ if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking - chunk1.incrementInhabitedTime(j); -- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning -+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration - NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); - } - -- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { -+ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking - this.level.tickChunk(chunk1, k); - } - } -+ // Paper start - optimise chunk tick iteration -+ } -+ } -+ -+ } finally { -+ if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) { -+ safeIterator.finishedIterating(); -+ } - } -+ // Paper end - optimise chunk tick iteration - this.level.timings.chunkTicks.stopTiming(); // Paper - gameprofilerfiller.popPush("customSpawners"); - if (flag2) { -@@ -617,15 +635,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 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(); - } - } diff --git a/patches/server/0009-MC-Utils.patch b/patches/server/0009-MC-Utils.patch index f3af21405f..5c9faa8f6a 100644 --- a/patches/server/0009-MC-Utils.patch +++ b/patches/server/0009-MC-Utils.patch @@ -5467,6 +5467,453 @@ index 0000000000000000000000000000000000000000..808d1449ac44ae86a650932365081fba + } + } +} +diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java +new file mode 100644 +index 0000000000000000000000000000000000000000..60c7680d678f9dc6a6a3109b1f6af8150ccd9100 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java +@@ -0,0 +1,203 @@ ++package io.papermc.paper.util.player; ++ ++import com.destroystokyo.paper.util.maplist.ReferenceList; ++import io.papermc.paper.chunk.system.ChunkSystem; ++import io.papermc.paper.util.CoordinateUtils; ++import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; ++import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; ++import net.minecraft.core.BlockPos; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.ServerPlayer; ++import net.minecraft.world.level.ChunkPos; ++ ++public final class NearbyPlayers { ++ ++ public static enum NearbyMapType { ++ GENERAL, ++ GENERAL_SMALL, ++ GENERAL_REALLY_SMALL, ++ TICK_VIEW_DISTANCE, ++ VIEW_DISTANCE; ++ } ++ ++ private static final NearbyMapType[] MOB_TYPES = NearbyMapType.values(); ++ public static final int TOTAL_MAP_TYPES = MOB_TYPES.length; ++ ++ 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; ++ ++ 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); ++ ++ private final ServerLevel world; ++ private final Reference2ReferenceOpenHashMap players = new Reference2ReferenceOpenHashMap<>(); ++ private final Long2ReferenceOpenHashMap byChunk = new Long2ReferenceOpenHashMap<>(); ++ ++ public NearbyPlayers(final ServerLevel world) { ++ this.world = world; ++ } ++ ++ public void addPlayer(final ServerPlayer player) { ++ final TrackedPlayer[] newTrackers = new TrackedPlayer[TOTAL_MAP_TYPES]; ++ if (this.players.put(player, newTrackers) != null) { ++ throw new IllegalStateException("Already have player " + player); ++ } ++ ++ final ChunkPos chunk = player.chunkPosition(); ++ ++ for (int i = 0; i < TOTAL_MAP_TYPES; ++i) { ++ // use 0 for default, will be updated by tickPlayer ++ (newTrackers[i] = new TrackedPlayer(player, MOB_TYPES[i])).add(chunk.x, chunk.z, 0); ++ } ++ ++ // update view distances ++ this.tickPlayer(player); ++ } ++ ++ public void removePlayer(final ServerPlayer player) { ++ final TrackedPlayer[] players = this.players.remove(player); ++ if (players == null) { ++ throw new IllegalStateException("Don't have player " + player); ++ } ++ ++ for (final TrackedPlayer tracker : players) { ++ tracker.remove(); ++ } ++ } ++ ++ public void tickPlayer(final ServerPlayer player) { ++ final TrackedPlayer[] players = this.players.get(player); ++ if (players == null) { ++ throw new IllegalStateException("Don't have player " + player); ++ } ++ ++ final ChunkPos chunk = player.chunkPosition(); ++ ++ players[NearbyMapType.GENERAL.ordinal()].update(chunk.x, chunk.z, GENERAL_AREA_VIEW_DISTANCE); ++ players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE); ++ 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)); ++ } ++ ++ public TrackedChunk getChunk(final ChunkPos pos) { ++ return this.byChunk.get(CoordinateUtils.getChunkKey(pos)); ++ } ++ ++ public TrackedChunk getChunk(final BlockPos pos) { ++ return this.byChunk.get(CoordinateUtils.getChunkKey(pos)); ++ } ++ ++ public ReferenceList getPlayers(final BlockPos pos, final NearbyMapType type) { ++ final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos)); ++ ++ return chunk == null ? null : chunk.players[type.ordinal()]; ++ } ++ ++ public ReferenceList getPlayers(final ChunkPos pos, final NearbyMapType type) { ++ final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos)); ++ ++ return chunk == null ? null : chunk.players[type.ordinal()]; ++ } ++ ++ public ReferenceList getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) { ++ final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ)); ++ ++ return chunk == null ? null : chunk.players[type.ordinal()]; ++ } ++ ++ public ReferenceList getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) { ++ final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4)); ++ ++ return chunk == null ? null : chunk.players[type.ordinal()]; ++ } ++ ++ public static final class TrackedChunk { ++ ++ public final ReferenceList[] players = new ReferenceList[TOTAL_MAP_TYPES]; ++ private int nonEmptyLists; ++ private int updateCount; ++ ++ public boolean isEmpty() { ++ return this.nonEmptyLists == 0; ++ } ++ ++ public int getUpdateCount() { ++ return this.updateCount; ++ } ++ ++ public ReferenceList getPlayers(final NearbyMapType type) { ++ return this.players[type.ordinal()]; ++ } ++ ++ public void addPlayer(final ServerPlayer player, final NearbyMapType type) { ++ ++this.updateCount; ++ final int idx = type.ordinal(); ++ final ReferenceList list = this.players[idx]; ++ if (list == null) { ++ ++this.nonEmptyLists; ++ (this.players[idx] = new ReferenceList<>()).add(player); ++ return; ++ } ++ ++ if (!list.add(player)) { ++ throw new IllegalStateException("Already contains player " + player); ++ } ++ } ++ ++ public void removePlayer(final ServerPlayer player, final NearbyMapType type) { ++ ++this.updateCount; ++ final int idx = type.ordinal(); ++ final ReferenceList list = this.players[idx]; ++ if (list == null) { ++ throw new IllegalStateException("Does not contain player " + player); ++ } ++ ++ if (!list.remove(player)) { ++ throw new IllegalStateException("Does not contain player " + player); ++ } ++ ++ if (list.size() == 0) { ++ this.players[idx] = null; ++ --this.nonEmptyLists; ++ } ++ } ++ } ++ ++ private final class TrackedPlayer extends SingleUserAreaMap { ++ ++ final NearbyMapType type; ++ ++ public TrackedPlayer(final ServerPlayer player, final NearbyMapType type) { ++ super(player); ++ this.type = type; ++ } ++ ++ @Override ++ protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) { ++ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ); ++ ++ NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> { ++ return new TrackedChunk(); ++ }).addPlayer(parameter, this.type); ++ } ++ ++ @Override ++ protected void removeCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) { ++ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ); ++ ++ final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey); ++ if (chunk == null) { ++ throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey)); ++ } ++ ++ chunk.removePlayer(parameter, this.type); ++ ++ if (chunk.isEmpty()) { ++ NearbyPlayers.this.byChunk.remove(chunkKey); ++ } ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/util/player/SingleUserAreaMap.java b/src/main/java/io/papermc/paper/util/player/SingleUserAreaMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d603887f4d0464f4463172fd79bcd5298d54983e +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/player/SingleUserAreaMap.java +@@ -0,0 +1,232 @@ ++package io.papermc.paper.util.player; ++ ++import io.papermc.paper.util.IntegerUtil; ++ ++public abstract class SingleUserAreaMap { ++ ++ private static final int NOT_SET = Integer.MIN_VALUE; ++ ++ private final T parameter; ++ private int lastChunkX = NOT_SET; ++ private int lastChunkZ = NOT_SET; ++ private int distance = NOT_SET; ++ ++ public SingleUserAreaMap(final T parameter) { ++ this.parameter = parameter; ++ } ++ ++ /* math sign function except 0 returns 1 */ ++ protected static int sign(int val) { ++ return 1 | (val >> (Integer.SIZE - 1)); ++ } ++ ++ protected abstract void addCallback(final T parameter, final int chunkX, final int chunkZ); ++ ++ protected abstract void removeCallback(final T parameter, final int chunkX, final int chunkZ); ++ ++ private void addToNew(final T parameter, final int chunkX, final int chunkZ, final int distance) { ++ final int maxX = chunkX + distance; ++ final int maxZ = chunkZ + distance; ++ ++ for (int cx = chunkX - distance; cx <= maxX; ++cx) { ++ for (int cz = chunkZ - distance; cz <= maxZ; ++cz) { ++ this.addCallback(parameter, cx, cz); ++ } ++ } ++ } ++ ++ private void removeFromOld(final T parameter, final int chunkX, final int chunkZ, final int distance) { ++ final int maxX = chunkX + distance; ++ final int maxZ = chunkZ + distance; ++ ++ for (int cx = chunkX - distance; cx <= maxX; ++cx) { ++ for (int cz = chunkZ - distance; cz <= maxZ; ++cz) { ++ this.removeCallback(parameter, cx, cz); ++ } ++ } ++ } ++ ++ public final boolean add(final int chunkX, final int chunkZ, final int distance) { ++ if (distance < 0) { ++ throw new IllegalArgumentException(Integer.toString(distance)); ++ } ++ if (this.lastChunkX != NOT_SET) { ++ return false; ++ } ++ this.lastChunkX = chunkX; ++ this.lastChunkZ = chunkZ; ++ this.distance = distance; ++ ++ this.addToNew(this.parameter, chunkX, chunkZ, distance); ++ ++ return true; ++ } ++ ++ public final boolean update(final int toX, final int toZ, final int newViewDistance) { ++ if (newViewDistance < 0) { ++ throw new IllegalArgumentException(Integer.toString(newViewDistance)); ++ } ++ final int fromX = this.lastChunkX; ++ final int fromZ = this.lastChunkZ; ++ final int oldViewDistance = this.distance; ++ if (fromX == NOT_SET) { ++ return false; ++ } ++ ++ this.lastChunkX = toX; ++ this.lastChunkZ = toZ; ++ this.distance = newViewDistance; ++ ++ final T parameter = this.parameter; ++ ++ ++ final int dx = toX - fromX; ++ final int dz = toZ - fromZ; ++ ++ final int totalX = IntegerUtil.branchlessAbs(fromX - toX); ++ final int totalZ = IntegerUtil.branchlessAbs(fromZ - toZ); ++ ++ if (Math.max(totalX, totalZ) > (2 * Math.max(newViewDistance, oldViewDistance))) { ++ // teleported? ++ this.removeFromOld(parameter, fromX, fromZ, oldViewDistance); ++ this.addToNew(parameter, toX, toZ, newViewDistance); ++ return true; ++ } ++ ++ if (oldViewDistance != newViewDistance) { ++ // remove loop ++ ++ final int oldMinX = fromX - oldViewDistance; ++ final int oldMinZ = fromZ - oldViewDistance; ++ final int oldMaxX = fromX + oldViewDistance; ++ final int oldMaxZ = fromZ + oldViewDistance; ++ for (int currX = oldMinX; currX <= oldMaxX; ++currX) { ++ for (int currZ = oldMinZ; currZ <= oldMaxZ; ++currZ) { ++ ++ // only remove if we're outside the new view distance... ++ if (Math.max(IntegerUtil.branchlessAbs(currX - toX), IntegerUtil.branchlessAbs(currZ - toZ)) > newViewDistance) { ++ this.removeCallback(parameter, currX, currZ); ++ } ++ } ++ } ++ ++ // add loop ++ ++ final int newMinX = toX - newViewDistance; ++ final int newMinZ = toZ - newViewDistance; ++ final int newMaxX = toX + newViewDistance; ++ final int newMaxZ = toZ + newViewDistance; ++ for (int currX = newMinX; currX <= newMaxX; ++currX) { ++ for (int currZ = newMinZ; currZ <= newMaxZ; ++currZ) { ++ ++ // only add if we're outside the old view distance... ++ if (Math.max(IntegerUtil.branchlessAbs(currX - fromX), IntegerUtil.branchlessAbs(currZ - fromZ)) > oldViewDistance) { ++ this.addCallback(parameter, currX, currZ); ++ } ++ } ++ } ++ ++ return true; ++ } ++ ++ // x axis is width ++ // z axis is height ++ // right refers to the x axis of where we moved ++ // top refers to the z axis of where we moved ++ ++ // same view distance ++ ++ // used for relative positioning ++ final int up = sign(dz); // 1 if dz >= 0, -1 otherwise ++ final int right = sign(dx); // 1 if dx >= 0, -1 otherwise ++ ++ // The area excluded by overlapping the two view distance squares creates four rectangles: ++ // Two on the left, and two on the right. The ones on the left we consider the "removed" section ++ // and on the right the "added" section. ++ // https://i.imgur.com/MrnOBgI.png is a reference image. Note that the outside border is not actually ++ // exclusive to the regions they surround. ++ ++ // 4 points of the rectangle ++ int maxX; // exclusive ++ int minX; // inclusive ++ int maxZ; // exclusive ++ int minZ; // inclusive ++ ++ if (dx != 0) { ++ // handle right addition ++ ++ maxX = toX + (oldViewDistance * right) + right; // exclusive ++ minX = fromX + (oldViewDistance * right) + right; // inclusive ++ maxZ = fromZ + (oldViewDistance * up) + up; // exclusive ++ minZ = toZ - (oldViewDistance * up); // inclusive ++ ++ for (int currX = minX; currX != maxX; currX += right) { ++ for (int currZ = minZ; currZ != maxZ; currZ += up) { ++ this.addCallback(parameter, currX, currZ); ++ } ++ } ++ } ++ ++ if (dz != 0) { ++ // handle up addition ++ ++ maxX = toX + (oldViewDistance * right) + right; // exclusive ++ minX = toX - (oldViewDistance * right); // inclusive ++ maxZ = toZ + (oldViewDistance * up) + up; // exclusive ++ minZ = fromZ + (oldViewDistance * up) + up; // inclusive ++ ++ for (int currX = minX; currX != maxX; currX += right) { ++ for (int currZ = minZ; currZ != maxZ; currZ += up) { ++ this.addCallback(parameter, currX, currZ); ++ } ++ } ++ } ++ ++ if (dx != 0) { ++ // handle left removal ++ ++ maxX = toX - (oldViewDistance * right); // exclusive ++ minX = fromX - (oldViewDistance * right); // inclusive ++ maxZ = fromZ + (oldViewDistance * up) + up; // exclusive ++ minZ = toZ - (oldViewDistance * up); // inclusive ++ ++ for (int currX = minX; currX != maxX; currX += right) { ++ for (int currZ = minZ; currZ != maxZ; currZ += up) { ++ this.removeCallback(parameter, currX, currZ); ++ } ++ } ++ } ++ ++ if (dz != 0) { ++ // handle down removal ++ ++ maxX = fromX + (oldViewDistance * right) + right; // exclusive ++ minX = fromX - (oldViewDistance * right); // inclusive ++ maxZ = toZ - (oldViewDistance * up); // exclusive ++ minZ = fromZ - (oldViewDistance * up); // inclusive ++ ++ for (int currX = minX; currX != maxX; currX += right) { ++ for (int currZ = minZ; currZ != maxZ; currZ += up) { ++ this.removeCallback(parameter, currX, currZ); ++ } ++ } ++ } ++ ++ return true; ++ } ++ ++ public final boolean remove() { ++ final int chunkX = this.lastChunkX; ++ final int chunkZ = this.lastChunkZ; ++ final int distance = this.distance; ++ if (chunkX == NOT_SET) { ++ return false; ++ } ++ ++ this.lastChunkX = this.lastChunkZ = this.distance = NOT_SET; ++ ++ this.removeFromOld(this.parameter, chunkX, chunkZ, distance); ++ ++ return true; ++ } ++} diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java index 092bfedfb32c38550d1cab1b696feac6df09131f..9870eccc1dc5c2201f12f8e2affe647f6b0375f8 100644 --- a/src/main/java/net/minecraft/Util.java @@ -5580,30 +6027,29 @@ index 929d31aa624f035eb314dece08969b102f5781fc..e94f0361cbca873f05b5b768c68c0933 + // Paper end - MC Utils - default query payloads } diff --git a/src/main/java/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java b/src/main/java/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java -index 44cac39893eb968aa8ea21ee571c0dcb866ce06c..953e7573f6b2ca73a8c27c4d63edec89b173f333 100644 +index 44cac39893eb968aa8ea21ee571c0dcb866ce06c..5151d68ba6ec72a7124f298253c5f0af080b2ea6 100644 --- a/src/main/java/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java +++ b/src/main/java/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java -@@ -15,7 +15,18 @@ public record ServerboundCustomQueryAnswerPacket(int transactionId, @Nullable Cu +@@ -15,7 +15,17 @@ public record ServerboundCustomQueryAnswerPacket(int transactionId, @Nullable Cu } private static CustomQueryAnswerPayload readPayload(int queryId, FriendlyByteBuf buf) { - return readUnknownPayload(buf); + // Paper start - MC Utils - default query payloads -+ return new net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket.QueryAnswerPayload( -+ buf.readNullable((buf2) -> { -+ int i = buf2.readableBytes(); -+ if (i >= 0 && i <= MAX_PAYLOAD_SIZE) { -+ return new FriendlyByteBuf(buf2.readBytes(i)); -+ } else { -+ throw new IllegalArgumentException("Payload may not be larger than " + MAX_PAYLOAD_SIZE + " bytes"); -+ } -+ }) -+ ); ++ FriendlyByteBuf buffer = buf.readNullable((buf2) -> { ++ int i = buf2.readableBytes(); ++ if (i >= 0 && i <= MAX_PAYLOAD_SIZE) { ++ return new FriendlyByteBuf(buf2.readBytes(i)); ++ } else { ++ throw new IllegalArgumentException("Payload may not be larger than " + MAX_PAYLOAD_SIZE + " bytes"); ++ } ++ }); ++ return buffer == null ? null : new net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket.QueryAnswerPayload(buffer); + // Paper end - MC Utils - default query payloads } private static CustomQueryAnswerPayload readUnknownPayload(FriendlyByteBuf buf) { -@@ -40,4 +51,21 @@ public record ServerboundCustomQueryAnswerPacket(int transactionId, @Nullable Cu +@@ -40,4 +50,21 @@ public record ServerboundCustomQueryAnswerPacket(int transactionId, @Nullable Cu public void handle(ServerLoginPacketListener listener) { listener.handleCustomQueryPacket(this); } @@ -5920,10 +6366,10 @@ index 1641bdf8725df778ba91bf5cd22c1ebbb3745058..facfdbb87e89f4db33ce13233c2ba436 + // Paper end } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb54dd199ce 100644 +index e9cf8686b59c232816b2fde92fc6616f77979a64..10132390dd356461f696971b34df3eeed05c572e 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -167,6 +167,56 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -167,6 +167,62 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider }; // CraftBukkit end @@ -5934,16 +6380,21 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 + int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); + int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); + // Note: players need to be explicitly added to distance maps before they can be updated ++ this.nearbyPlayers.addPlayer(player); + } + + void removePlayerFromDistanceMaps(ServerPlayer player) { -+ ++ int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); ++ int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); ++ // Note: players need to be explicitly added to distance maps before they can be updated ++ this.nearbyPlayers.removePlayer(player); + } + + void updateMaps(ServerPlayer player) { + int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); + int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); + // Note: players need to be explicitly added to distance maps before they can be updated ++ this.nearbyPlayers.tickPlayer(player); + } + // Paper end + // Paper start @@ -5975,23 +6426,33 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 + public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) { + return this.pendingUnloads.get(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ)); + } ++ public final io.papermc.paper.util.player.NearbyPlayers nearbyPlayers; + // Paper end + public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); this.visibleChunkMap = this.updatingChunkMap.clone(); -@@ -220,6 +270,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -220,7 +276,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.overworldDataStorage = persistentStateManagerFactory; this.poiManager = new PoiManager(path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); this.setServerViewDistance(viewDistance); + // Paper start + this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new); + this.regionManagers.add(this.dataRegionManager); ++ this.nearbyPlayers = new io.papermc.paper.util.player.NearbyPlayers(this.level); + // Paper end ++ } ++ ++ // Paper start ++ // always use accessor, so folia can override ++ public final io.papermc.paper.util.player.NearbyPlayers getNearbyPlayers() { ++ return this.nearbyPlayers; } ++ // Paper end protected ChunkGenerator generator() { -@@ -321,6 +375,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + return this.generator; +@@ -321,6 +389,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } @@ -6007,7 +6468,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 private CompletableFuture, ChunkHolder.ChunkLoadingFailure>> getChunkRangeFuture(ChunkHolder centerChunk, int margin, IntFunction distanceToStatus) { if (margin == 0) { ChunkStatus chunkstatus = (ChunkStatus) distanceToStatus.apply(0); -@@ -417,9 +480,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -417,9 +494,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider }; stringbuilder.append("Updating:").append(System.lineSeparator()); @@ -6019,7 +6480,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 CrashReport crashreport = CrashReport.forThrowable(exception, "Chunk loading"); CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Chunk loading"); -@@ -461,8 +524,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -461,8 +538,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider holder.setTicketLevel(level); } else { holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this); @@ -6034,7 +6495,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 this.updatingChunkMap.put(pos, holder); this.modified = true; } -@@ -484,7 +553,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -484,7 +567,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider protected void saveAllChunks(boolean flush) { if (flush) { @@ -6043,7 +6504,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 MutableBoolean mutableboolean = new MutableBoolean(); do { -@@ -513,7 +582,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -513,7 +596,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider }); this.flushWorker(); } else { @@ -6052,7 +6513,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 } } -@@ -532,7 +601,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -532,7 +615,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public boolean hasWork() { @@ -6061,7 +6522,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 } private void processUnloads(BooleanSupplier shouldKeepTicking) { -@@ -543,6 +612,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -543,6 +626,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j); if (playerchunk != null) { @@ -6069,7 +6530,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 this.pendingUnloads.put(j, playerchunk); this.modified = true; ++i; -@@ -560,7 +630,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -560,7 +644,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } int l = 0; @@ -6078,7 +6539,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) { if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) { -@@ -578,7 +648,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -578,7 +662,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (completablefuture1 != completablefuture) { this.scheduleUnload(pos, holder); } else { @@ -6091,7 +6552,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 if (ichunkaccess instanceof LevelChunk) { ((LevelChunk) ichunkaccess).setLoaded(false); } -@@ -594,7 +668,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -594,7 +682,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lightEngine.tryScheduleUpdate(); this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null); this.chunkSaveCooldowns.remove(ichunkaccess.getPos().toLong()); @@ -6102,7 +6563,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 } }; -@@ -1037,7 +1113,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1037,7 +1127,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public int size() { @@ -6111,7 +6572,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 } public DistanceManager getDistanceManager() { -@@ -1045,19 +1121,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1045,19 +1135,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } protected Iterable getChunks() { @@ -6136,7 +6597,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 Optional optional = Optional.ofNullable(playerchunk.getLastAvailable()); Optional optional1 = optional.flatMap((ichunkaccess) -> { return ichunkaccess instanceof LevelChunk ? Optional.of((LevelChunk) ichunkaccess) : Optional.empty(); -@@ -1182,6 +1258,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1182,6 +1272,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider player.setChunkTrackingView(ChunkTrackingView.EMPTY); this.updateChunkTracking(player); @@ -6144,7 +6605,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 } else { SectionPos sectionposition = player.getLastSectionPos(); -@@ -1190,6 +1267,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1190,6 +1281,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.distanceManager.removePlayer(sectionposition, player); } @@ -6152,7 +6613,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 this.applyChunkTrackingView(player, ChunkTrackingView.EMPTY); } -@@ -1241,6 +1319,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1241,6 +1333,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.updateChunkTracking(player); } @@ -6160,7 +6621,7 @@ index e9cf8686b59c232816b2fde92fc6616f77979a64..90dce7ed0d3d3dee3bae6657ac3cbeb5 } private void updateChunkTracking(ServerPlayer player) { -@@ -1493,7 +1572,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1493,7 +1586,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider private class ChunkDistanceManager extends DistanceManager { protected ChunkDistanceManager(Executor workerExecutor, Executor mainThreadExecutor) { diff --git a/patches/server/0014-Timings-v2.patch b/patches/server/0014-Timings-v2.patch index 8fcfb50cdf..5ad1e1c85c 100644 --- a/patches/server/0014-Timings-v2.patch +++ b/patches/server/0014-Timings-v2.patch @@ -1010,7 +1010,7 @@ index ff3f00cf1d5180f83b16acac5676aa22cd967c8a..5aa21689e308842fe5b64689265ba45a // CraftBukkit end } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 90dce7ed0d3d3dee3bae6657ac3cbeb54dd199ce..449bd9f505fdfb9eab46ff9309d7665bced1e5f8 100644 +index 10132390dd356461f696971b34df3eeed05c572e..4dc04c16453174dde2c6bfad711ec9dec01c8f30 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -1,8 +1,10 @@ @@ -1024,7 +1024,7 @@ index 90dce7ed0d3d3dee3bae6657ac3cbeb54dd199ce..449bd9f505fdfb9eab46ff9309d7665b import com.google.common.collect.Lists; import com.google.common.collect.Queues; import com.google.common.collect.Sets; -@@ -882,6 +884,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -896,6 +898,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ChunkStatus chunkstatus = ChunkLevel.generationStatus(chunkHolder.getTicketLevel()); return !chunkstatus.isOrAfter(ChunkStatus.FULL) ? ChunkHolder.UNLOADED_CHUNK : either.mapLeft((ichunkaccess) -> { @@ -1032,7 +1032,7 @@ index 90dce7ed0d3d3dee3bae6657ac3cbeb54dd199ce..449bd9f505fdfb9eab46ff9309d7665b ChunkPos chunkcoordintpair = chunkHolder.getPos(); ProtoChunk protochunk = (ProtoChunk) ichunkaccess; LevelChunk chunk; -@@ -906,6 +909,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -920,6 +923,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } return chunk; @@ -1040,7 +1040,7 @@ index 90dce7ed0d3d3dee3bae6657ac3cbeb54dd199ce..449bd9f505fdfb9eab46ff9309d7665b }); }, (runnable) -> { ProcessorHandle mailbox = this.mainThreadMailbox; -@@ -1457,6 +1461,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1471,6 +1475,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider List list = Lists.newArrayList(); List list1 = this.level.players(); ObjectIterator objectiterator = this.entityMap.values().iterator(); @@ -1048,7 +1048,7 @@ index 90dce7ed0d3d3dee3bae6657ac3cbeb54dd199ce..449bd9f505fdfb9eab46ff9309d7665b ChunkMap.TrackedEntity playerchunkmap_entitytracker; -@@ -1481,14 +1486,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1495,14 +1500,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider playerchunkmap_entitytracker.serverEntity.sendChanges(); } } diff --git a/patches/server/0016-Starlight.patch b/patches/server/0016-Starlight.patch index a955f07d77..1dc99c348d 100644 --- a/patches/server/0016-Starlight.patch +++ b/patches/server/0016-Starlight.patch @@ -4493,7 +4493,7 @@ index facfdbb87e89f4db33ce13233c2ba4366d35c15b..807a6bb1026dac2c4cd0a50afe06fd62 private final DebugBuffer chunkToSaveHistory; public int oldTicketLevel; diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 449bd9f505fdfb9eab46ff9309d7665bced1e5f8..8c84461a5711cb408b0ead397417c31cb2f4d336 100644 +index 4dc04c16453174dde2c6bfad711ec9dec01c8f30..b47e30e9ebe0d6a930e01aaf67c138fc345fb11e 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -124,7 +124,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider diff --git a/patches/server/0018-Rewrite-chunk-system.patch b/patches/server/0018-Rewrite-chunk-system.patch index 9b478f4d59..12b7328dc6 100644 --- a/patches/server/0018-Rewrite-chunk-system.patch +++ b/patches/server/0018-Rewrite-chunk-system.patch @@ -2095,7 +2095,7 @@ index 0000000000000000000000000000000000000000..99f49b5625cf51d6c97640553cf5c420 + } +} diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java -index 87ae7d64e67ebae5ab53cc239cdf6580dca31652..56c35149fd142af6ffe31434fec0064cf6ea3be5 100644 +index 87ae7d64e67ebae5ab53cc239cdf6580dca31652..cbeaadaecf816070b3a37938c8e683180939afc4 100644 --- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java +++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java @@ -32,191 +32,41 @@ public final class ChunkSystem { @@ -2299,7 +2299,40 @@ index 87ae7d64e67ebae5ab53cc239cdf6580dca31652..56c35149fd142af6ffe31434fec0064c } public static boolean hasAnyChunkHolders(final ServerLevel level) { -@@ -270,23 +120,15 @@ public final class ChunkSystem { +@@ -243,26 +93,31 @@ public final class ChunkSystem { + + public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) { + chunk.playerChunk = holder; ++ chunk.chunkStatus = net.minecraft.server.level.FullChunkStatus.FULL; + } + + public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) { +- ++ chunk.chunkStatus = net.minecraft.server.level.FullChunkStatus.INACCESSIBLE; + } + + public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) { + chunk.level.getChunkSource().tickingChunks.add(chunk); ++ chunk.chunkStatus = net.minecraft.server.level.FullChunkStatus.BLOCK_TICKING; + } + + public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) { + chunk.level.getChunkSource().tickingChunks.remove(chunk); ++ chunk.chunkStatus = net.minecraft.server.level.FullChunkStatus.FULL; + } + + public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) { + chunk.level.getChunkSource().entityTickingChunks.add(chunk); ++ chunk.chunkStatus = net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING; + } + + public static void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) { + chunk.level.getChunkSource().entityTickingChunks.remove(chunk); ++ chunk.chunkStatus = net.minecraft.server.level.FullChunkStatus.BLOCK_TICKING; + } + + public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) { +@@ -270,23 +125,15 @@ public final class ChunkSystem { } public static int getSendViewDistance(final ServerPlayer player) { @@ -2328,21 +2361,20 @@ index 87ae7d64e67ebae5ab53cc239cdf6580dca31652..56c35149fd142af6ffe31434fec0064c private ChunkSystem() { diff --git a/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java new file mode 100644 -index 0000000000000000000000000000000000000000..842848872b86e5335863d309af956fee74110d52 +index 0000000000000000000000000000000000000000..1b090f1e79b996e52097afc49c1cec85936653e6 --- /dev/null +++ b/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java -@@ -0,0 +1,1440 @@ +@@ -0,0 +1,1208 @@ +package io.papermc.paper.chunk.system; + +import ca.spottedleaf.concurrentutil.collection.SRSWLinkedQueue; +import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; +import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; -+import io.papermc.paper.chunk.system.io.RegionFileIOThread; +import io.papermc.paper.chunk.system.scheduling.ChunkHolderManager; +import io.papermc.paper.configuration.GlobalConfiguration; +import io.papermc.paper.util.CoordinateUtils; -+import io.papermc.paper.util.IntegerUtil; +import io.papermc.paper.util.TickThread; ++import io.papermc.paper.util.player.SingleUserAreaMap; +import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; +import it.unimi.dsi.fastutil.longs.LongArrayList; @@ -2353,7 +2385,6 @@ index 0000000000000000000000000000000000000000..842848872b86e5335863d309af956fee +import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; +import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; +import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; -+import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ChunkTrackingView; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; @@ -2365,7 +2396,6 @@ index 0000000000000000000000000000000000000000..842848872b86e5335863d309af956fee +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.BelowZeroRetrogen; -+import org.apache.commons.lang3.mutable.MutableObject; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import java.lang.invoke.VarHandle; @@ -2845,7 +2875,7 @@ index 0000000000000000000000000000000000000000..842848872b86e5335863d309af956fee + parameter.sendUnloadChunk(chunkX, chunkZ); + } + }; -+ private final SingleUserAreaMap loadTicketCleanup = new SingleUserAreaMap<>(this) { ++ private final SingleUserAreaMap loadTicketCleanup = new SingleUserAreaMap<>(this) { + @Override + protected void addCallback(final PlayerChunkLoaderData parameter, final int chunkX, final int chunkZ) { + // do nothing, we only care about remove @@ -3488,235 +3518,6 @@ index 0000000000000000000000000000000000000000..842848872b86e5335863d309af956fee + } + } + -+ public static abstract class SingleUserAreaMap { -+ -+ private static final int NOT_SET = Integer.MIN_VALUE; -+ -+ private final T parameter; -+ private int lastChunkX = NOT_SET; -+ private int lastChunkZ = NOT_SET; -+ private int distance = NOT_SET; -+ -+ public SingleUserAreaMap(final T parameter) { -+ this.parameter = parameter; -+ } -+ -+ /* math sign function except 0 returns 1 */ -+ protected static int sign(int val) { -+ return 1 | (val >> (Integer.SIZE - 1)); -+ } -+ -+ protected abstract void addCallback(final T parameter, final int chunkX, final int chunkZ); -+ -+ protected abstract void removeCallback(final T parameter, final int chunkX, final int chunkZ); -+ -+ private void addToNew(final T parameter, final int chunkX, final int chunkZ, final int distance) { -+ final int maxX = chunkX + distance; -+ final int maxZ = chunkZ + distance; -+ -+ for (int cx = chunkX - distance; cx <= maxX; ++cx) { -+ for (int cz = chunkZ - distance; cz <= maxZ; ++cz) { -+ this.addCallback(parameter, cx, cz); -+ } -+ } -+ } -+ -+ private void removeFromOld(final T parameter, final int chunkX, final int chunkZ, final int distance) { -+ final int maxX = chunkX + distance; -+ final int maxZ = chunkZ + distance; -+ -+ for (int cx = chunkX - distance; cx <= maxX; ++cx) { -+ for (int cz = chunkZ - distance; cz <= maxZ; ++cz) { -+ this.removeCallback(parameter, cx, cz); -+ } -+ } -+ } -+ -+ public final boolean add(final int chunkX, final int chunkZ, final int distance) { -+ if (distance < 0) { -+ throw new IllegalArgumentException(Integer.toString(distance)); -+ } -+ if (this.lastChunkX != NOT_SET) { -+ return false; -+ } -+ this.lastChunkX = chunkX; -+ this.lastChunkZ = chunkZ; -+ this.distance = distance; -+ -+ this.addToNew(this.parameter, chunkX, chunkZ, distance); -+ -+ return true; -+ } -+ -+ public final boolean update(final int toX, final int toZ, final int newViewDistance) { -+ if (newViewDistance < 0) { -+ throw new IllegalArgumentException(Integer.toString(newViewDistance)); -+ } -+ final int fromX = this.lastChunkX; -+ final int fromZ = this.lastChunkZ; -+ final int oldViewDistance = this.distance; -+ if (fromX == NOT_SET) { -+ return false; -+ } -+ -+ this.lastChunkX = toX; -+ this.lastChunkZ = toZ; -+ this.distance = newViewDistance; -+ -+ final T parameter = this.parameter; -+ -+ -+ final int dx = toX - fromX; -+ final int dz = toZ - fromZ; -+ -+ final int totalX = IntegerUtil.branchlessAbs(fromX - toX); -+ final int totalZ = IntegerUtil.branchlessAbs(fromZ - toZ); -+ -+ if (Math.max(totalX, totalZ) > (2 * Math.max(newViewDistance, oldViewDistance))) { -+ // teleported? -+ this.removeFromOld(parameter, fromX, fromZ, oldViewDistance); -+ this.addToNew(parameter, toX, toZ, newViewDistance); -+ return true; -+ } -+ -+ if (oldViewDistance != newViewDistance) { -+ // remove loop -+ -+ final int oldMinX = fromX - oldViewDistance; -+ final int oldMinZ = fromZ - oldViewDistance; -+ final int oldMaxX = fromX + oldViewDistance; -+ final int oldMaxZ = fromZ + oldViewDistance; -+ for (int currX = oldMinX; currX <= oldMaxX; ++currX) { -+ for (int currZ = oldMinZ; currZ <= oldMaxZ; ++currZ) { -+ -+ // only remove if we're outside the new view distance... -+ if (Math.max(IntegerUtil.branchlessAbs(currX - toX), IntegerUtil.branchlessAbs(currZ - toZ)) > newViewDistance) { -+ this.removeCallback(parameter, currX, currZ); -+ } -+ } -+ } -+ -+ // add loop -+ -+ final int newMinX = toX - newViewDistance; -+ final int newMinZ = toZ - newViewDistance; -+ final int newMaxX = toX + newViewDistance; -+ final int newMaxZ = toZ + newViewDistance; -+ for (int currX = newMinX; currX <= newMaxX; ++currX) { -+ for (int currZ = newMinZ; currZ <= newMaxZ; ++currZ) { -+ -+ // only add if we're outside the old view distance... -+ if (Math.max(IntegerUtil.branchlessAbs(currX - fromX), IntegerUtil.branchlessAbs(currZ - fromZ)) > oldViewDistance) { -+ this.addCallback(parameter, currX, currZ); -+ } -+ } -+ } -+ -+ return true; -+ } -+ -+ // x axis is width -+ // z axis is height -+ // right refers to the x axis of where we moved -+ // top refers to the z axis of where we moved -+ -+ // same view distance -+ -+ // used for relative positioning -+ final int up = sign(dz); // 1 if dz >= 0, -1 otherwise -+ final int right = sign(dx); // 1 if dx >= 0, -1 otherwise -+ -+ // The area excluded by overlapping the two view distance squares creates four rectangles: -+ // Two on the left, and two on the right. The ones on the left we consider the "removed" section -+ // and on the right the "added" section. -+ // https://i.imgur.com/MrnOBgI.png is a reference image. Note that the outside border is not actually -+ // exclusive to the regions they surround. -+ -+ // 4 points of the rectangle -+ int maxX; // exclusive -+ int minX; // inclusive -+ int maxZ; // exclusive -+ int minZ; // inclusive -+ -+ if (dx != 0) { -+ // handle right addition -+ -+ maxX = toX + (oldViewDistance * right) + right; // exclusive -+ minX = fromX + (oldViewDistance * right) + right; // inclusive -+ maxZ = fromZ + (oldViewDistance * up) + up; // exclusive -+ minZ = toZ - (oldViewDistance * up); // inclusive -+ -+ for (int currX = minX; currX != maxX; currX += right) { -+ for (int currZ = minZ; currZ != maxZ; currZ += up) { -+ this.addCallback(parameter, currX, currZ); -+ } -+ } -+ } -+ -+ if (dz != 0) { -+ // handle up addition -+ -+ maxX = toX + (oldViewDistance * right) + right; // exclusive -+ minX = toX - (oldViewDistance * right); // inclusive -+ maxZ = toZ + (oldViewDistance * up) + up; // exclusive -+ minZ = fromZ + (oldViewDistance * up) + up; // inclusive -+ -+ for (int currX = minX; currX != maxX; currX += right) { -+ for (int currZ = minZ; currZ != maxZ; currZ += up) { -+ this.addCallback(parameter, currX, currZ); -+ } -+ } -+ } -+ -+ if (dx != 0) { -+ // handle left removal -+ -+ maxX = toX - (oldViewDistance * right); // exclusive -+ minX = fromX - (oldViewDistance * right); // inclusive -+ maxZ = fromZ + (oldViewDistance * up) + up; // exclusive -+ minZ = toZ - (oldViewDistance * up); // inclusive -+ -+ for (int currX = minX; currX != maxX; currX += right) { -+ for (int currZ = minZ; currZ != maxZ; currZ += up) { -+ this.removeCallback(parameter, currX, currZ); -+ } -+ } -+ } -+ -+ if (dz != 0) { -+ // handle down removal -+ -+ maxX = fromX + (oldViewDistance * right) + right; // exclusive -+ minX = fromX - (oldViewDistance * right); // inclusive -+ maxZ = toZ - (oldViewDistance * up); // exclusive -+ minZ = fromZ - (oldViewDistance * up); // inclusive -+ -+ for (int currX = minX; currX != maxX; currX += right) { -+ for (int currZ = minZ; currZ != maxZ; currZ += up) { -+ this.removeCallback(parameter, currX, currZ); -+ } -+ } -+ } -+ -+ return true; -+ } -+ -+ public final boolean remove() { -+ final int chunkX = this.lastChunkX; -+ final int chunkZ = this.lastChunkZ; -+ final int distance = this.distance; -+ if (chunkX == NOT_SET) { -+ return false; -+ } -+ -+ this.lastChunkX = this.lastChunkZ = this.distance = NOT_SET; -+ -+ this.removeFromOld(this.parameter, chunkX, chunkZ, distance); -+ -+ return true; -+ } -+ } -+ + static final class CountedSRSWLinkedQueue { + + private final SRSWLinkedQueue queue = new SRSWLinkedQueue<>(); @@ -17710,7 +17511,7 @@ index 807a6bb1026dac2c4cd0a50afe06fd62ce23558b..2b998bdbe49bf8211b755e0eb7c1bf13 // Paper end } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059cba30552 100644 +index b47e30e9ebe0d6a930e01aaf67c138fc345fb11e..e2510ee3f0cb93eae2452bec642855cd6c0c2974 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -118,10 +118,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -17745,7 +17546,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 private final String storageName; private final PlayerMap playerMap; public final Int2ObjectMap entityMap; -@@ -148,37 +143,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -148,26 +143,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider private final Queue unloadQueue; private int serverViewDistance; @@ -17773,34 +17574,38 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 // Paper start - distance maps private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>(); - - void addPlayerToDistanceMaps(ServerPlayer player) { -+ this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader - int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); +@@ -177,6 +153,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); // Note: players need to be explicitly added to distance maps before they can be updated + this.nearbyPlayers.addPlayer(player); ++ this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader } void removePlayerFromDistanceMaps(ServerPlayer player) { -+ this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader - - } - -@@ -186,6 +164,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); +@@ -184,6 +161,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); // Note: players need to be explicitly added to distance maps before they can be updated + this.nearbyPlayers.removePlayer(player); ++ this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader + } + + void updateMaps(ServerPlayer player) { +@@ -191,6 +169,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); + // Note: players need to be explicitly added to distance maps before they can be updated + this.nearbyPlayers.tickPlayer(player); + this.level.playerChunkLoader.updatePlayer(player); // Paper - replace chunk loader } // Paper end // Paper start -@@ -215,16 +194,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -220,17 +199,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) { - return this.pendingUnloads.get(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ)); + return null; // Paper - rewrite chunk system } + public final io.papermc.paper.util.player.NearbyPlayers nearbyPlayers; // Paper end public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { @@ -17813,7 +17618,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 this.tickingGenerated = new AtomicInteger(); this.playerMap = new PlayerMap(); this.entityMap = new Int2ObjectOpenHashMap(); -@@ -255,19 +231,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -261,19 +237,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.chunkGeneratorState = chunkGenerator.createState(iregistrycustom.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, j, world.spigotConfig); // Spigot this.mainThreadExecutor = mainThreadExecutor; @@ -17838,7 +17643,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor); this.overworldDataStorage = persistentStateManagerFactory; this.poiManager = new PoiManager(path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); -@@ -311,23 +285,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -325,23 +299,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } boolean isChunkTracked(ServerPlayer player, int chunkX, int chunkZ) { @@ -17868,7 +17673,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } protected ThreadedLevelLightEngine getLightEngine() { -@@ -336,20 +302,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -350,20 +316,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @Nullable protected ChunkHolder getUpdatingChunkIfPresent(long pos) { @@ -17898,7 +17703,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } public String getChunkDebugData(ChunkPos chunkPos) { -@@ -379,92 +347,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -393,92 +361,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper start public final int getEffectiveViewDistance() { @@ -17993,7 +17798,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } public ReportedException debugFuturesAndCreateReportedException(IllegalStateException exception, String details) { -@@ -494,263 +382,72 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -508,263 +396,72 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public CompletableFuture> prepareEntityTickingChunk(ChunkHolder chunk) { @@ -18277,7 +18082,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 return nbt.contains("Status", 8); } -@@ -786,54 +483,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -800,54 +497,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } private CompletableFuture> scheduleChunkGeneration(ChunkHolder holder, ChunkStatus requiredStatus) { @@ -18333,7 +18138,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } protected void releaseLightTicket(ChunkPos pos) { -@@ -844,7 +494,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -858,7 +508,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider })); } @@ -18342,7 +18147,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 ChunkStatus chunkstatus1; if (distance == 0) { -@@ -856,7 +506,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -870,7 +520,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return chunkstatus1; } @@ -18351,7 +18156,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 if (!nbt.isEmpty()) { // CraftBukkit start - these are spawned serialized (DefinedStructure) and we don't call an add event below at the moment due to ordering complexities world.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(nbt, world).filter((entity) -> { -@@ -871,111 +521,27 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -885,111 +535,27 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider needsRemoval = true; } return !needsRemoval; @@ -18468,7 +18273,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } public int getTickingGenerated() { -@@ -983,130 +549,52 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -997,130 +563,52 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } private boolean saveChunkIfNeeded(ChunkHolder chunkHolder) { @@ -18616,7 +18421,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } @Nullable -@@ -1129,30 +617,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1143,30 +631,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } void dumpChunks(Writer writer) throws IOException { @@ -18648,7 +18453,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } private static String printFuture(CompletableFuture> future) { -@@ -1171,6 +636,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1185,6 +650,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } @@ -18684,7 +18489,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 private CompletableFuture> readChunk(ChunkPos chunkPos) { return this.read(chunkPos).thenApplyAsync((optional) -> { return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit -@@ -1260,8 +754,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1274,8 +768,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.distanceManager.addPlayer(SectionPos.of((EntityAccess) player), player); } @@ -18694,7 +18499,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 this.addPlayerToDistanceMaps(player); // Paper - distance maps } else { SectionPos sectionposition = player.getLastSectionPos(); -@@ -1272,7 +765,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1286,7 +779,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } this.removePlayerFromDistanceMaps(player); // Paper - distance maps @@ -18703,7 +18508,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } } -@@ -1320,73 +813,30 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1334,73 +827,30 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.playerMap.unIgnorePlayer(player); } @@ -18787,7 +18592,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } public void addEntity(Entity entity) { -@@ -1450,13 +900,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1464,13 +914,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } protected void tick() { @@ -18802,7 +18607,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 List list = Lists.newArrayList(); List list1 = this.level.players(); -@@ -1565,16 +1009,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1579,16 +1023,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void waitForLightBeforeSending(ChunkPos centerPos, int radius) { @@ -18820,7 +18625,7 @@ index 8c84461a5711cb408b0ead397417c31cb2f4d336..ea0b82165e452b7b82d1d9a58eef6059 } private class ChunkDistanceManager extends DistanceManager { -@@ -1585,7 +1020,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1599,7 +1034,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @Override protected boolean isChunkToRemove(long pos) { @@ -21501,10 +21306,18 @@ index 846ae3fd184a1d63b743aa25e045604576697c96..a907b79fd8291a0e92db138f37239d17 public int getIndex() { diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b8955037d3d 100644 +index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..85b38592c87bbc071d3fc5de5db131c6626fe004 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -661,9 +661,26 @@ public class LevelChunk extends ChunkAccess { +@@ -83,6 +83,7 @@ public class LevelChunk extends ChunkAccess { + private final Int2ObjectMap gameEventListenerRegistrySections; + private final LevelChunkTicks blockTicks; + private final LevelChunkTicks fluidTicks; ++ public volatile FullChunkStatus chunkStatus = FullChunkStatus.INACCESSIBLE; // Paper - rewrite chunk system + + public LevelChunk(Level world, ChunkPos pos) { + this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); +@@ -661,9 +662,26 @@ public class LevelChunk extends ChunkAccess { } @@ -21534,7 +21347,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 int chunkX = this.chunkPos.x; int chunkZ = this.chunkPos.z; net.minecraft.server.level.ServerChunkCache chunkProvider = this.level.getChunkSource(); -@@ -678,10 +695,55 @@ public class LevelChunk extends ChunkAccess { +@@ -678,10 +696,55 @@ public class LevelChunk extends ChunkAccess { } } this.setNeighbourLoaded(0, 0, this); @@ -21592,7 +21405,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 if (server != null) { /* * If it's a new world, the first few chunks are generated inside -@@ -690,6 +752,7 @@ public class LevelChunk extends ChunkAccess { +@@ -690,6 +753,7 @@ public class LevelChunk extends ChunkAccess { */ org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration)); @@ -21600,7 +21413,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 if (this.needsDecoration) { try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper -@@ -718,9 +781,11 @@ public class LevelChunk extends ChunkAccess { +@@ -718,9 +782,11 @@ public class LevelChunk extends ChunkAccess { } public void unloadCallback() { @@ -21613,7 +21426,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 server.getPluginManager().callEvent(unloadEvent); // note: saving can be prevented, but not forced if no saving is actually required this.mustNotSave = !unloadEvent.isSaveChunk(); -@@ -742,9 +807,26 @@ public class LevelChunk extends ChunkAccess { +@@ -742,9 +808,26 @@ public class LevelChunk extends ChunkAccess { // Paper end } @@ -21641,7 +21454,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 } // CraftBukkit end -@@ -813,7 +895,9 @@ public class LevelChunk extends ChunkAccess { +@@ -813,7 +896,9 @@ public class LevelChunk extends ChunkAccess { return this.blockEntities; } @@ -21651,7 +21464,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 ChunkPos chunkcoordintpair = this.getPos(); for (int i = 0; i < this.postProcessing.length; ++i) { -@@ -834,6 +918,7 @@ public class LevelChunk extends ChunkAccess { +@@ -834,6 +919,7 @@ public class LevelChunk extends ChunkAccess { BlockState iblockdata1 = Block.updateFromNeighbourShapes(iblockdata, this.level, blockposition); this.level.setBlock(blockposition, iblockdata1, 20); @@ -21659,7 +21472,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 } } -@@ -851,6 +936,10 @@ public class LevelChunk extends ChunkAccess { +@@ -851,6 +937,10 @@ public class LevelChunk extends ChunkAccess { this.pendingBlockEntities.clear(); this.upgradeData.upgrade(this); @@ -21670,7 +21483,7 @@ index bcdaa86cfd31c2ce4aadad900c348aee0a9e3fc8..a8b2ce0debe54afeb937a4f1215d6b89 } @Nullable -@@ -900,7 +989,7 @@ public class LevelChunk extends ChunkAccess { +@@ -900,7 +990,7 @@ public class LevelChunk extends ChunkAccess { } public FullChunkStatus getFullStatus() { diff --git a/patches/server/0034-Prevent-tile-entity-and-entity-crashes.patch b/patches/server/0034-Prevent-tile-entity-and-entity-crashes.patch index 3e7cc84d14..e7c7192347 100644 --- a/patches/server/0034-Prevent-tile-entity-and-entity-crashes.patch +++ b/patches/server/0034-Prevent-tile-entity-and-entity-crashes.patch @@ -44,10 +44,10 @@ index b300d12e9e00519028b53aca9c3fb01f589eaa91..63acd109a79ed752a05df3d4f1b99309 } } diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index a8b2ce0debe54afeb937a4f1215d6b8955037d3d..515aedcf9969b818dfdc02a16a7f7178b5bb5593 100644 +index 85b38592c87bbc071d3fc5de5db131c6626fe004..fde60133d2c5557cd8d4a4d2e3a16aa96974fcbb 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -1146,11 +1146,11 @@ public class LevelChunk extends ChunkAccess { +@@ -1147,11 +1147,11 @@ public class LevelChunk extends ChunkAccess { gameprofilerfiller.pop(); } catch (Throwable throwable) { diff --git a/patches/server/0060-Add-exception-reporting-event.patch b/patches/server/0060-Add-exception-reporting-event.patch index 92b6b59dad..17d579396e 100644 --- a/patches/server/0060-Add-exception-reporting-event.patch +++ b/patches/server/0060-Add-exception-reporting-event.patch @@ -131,7 +131,7 @@ index 5d65baba605dd83e5f74d526aeda36d8ede8c014..92e76dd39dc3575e9466031dd799080a } diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 515aedcf9969b818dfdc02a16a7f7178b5bb5593..56900156d79781f15357a3d25906d32b615366ce 100644 +index fde60133d2c5557cd8d4a4d2e3a16aa96974fcbb..d13652d5001c117582b8c470d40207a6c10cb096 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -1,6 +1,7 @@ @@ -142,7 +142,7 @@ index 515aedcf9969b818dfdc02a16a7f7178b5bb5593..56900156d79781f15357a3d25906d32b import com.google.common.collect.Maps; import com.google.common.collect.UnmodifiableIterator; import com.mojang.logging.LogUtils; -@@ -568,10 +569,16 @@ public class LevelChunk extends ChunkAccess { +@@ -569,10 +570,16 @@ public class LevelChunk extends ChunkAccess { // CraftBukkit start } else { @@ -163,7 +163,7 @@ index 515aedcf9969b818dfdc02a16a7f7178b5bb5593..56900156d79781f15357a3d25906d32b // CraftBukkit end } } -@@ -1149,6 +1156,7 @@ public class LevelChunk extends ChunkAccess { +@@ -1150,6 +1157,7 @@ public class LevelChunk extends ChunkAccess { // Paper start - Prevent tile entity and entity crashes final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable); diff --git a/patches/server/0077-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch b/patches/server/0077-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch index a4c9a41195..34d756f06b 100644 --- a/patches/server/0077-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch +++ b/patches/server/0077-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch @@ -88,10 +88,10 @@ index 60e760b42dd6471a229dfd45490dcf8c51979d35..4a3ac7dedf5cb1e76f16ec4f18e82afc @Override public FluidState getFluidState(BlockPos pos) { diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 56900156d79781f15357a3d25906d32b615366ce..0eedaf7df86b7bdd22623231f9a49da8e798f204 100644 +index d13652d5001c117582b8c470d40207a6c10cb096..f6286284b95df0ab6109ff14fda33a48b163f8c2 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -295,12 +295,29 @@ public class LevelChunk extends ChunkAccess { +@@ -296,12 +296,29 @@ public class LevelChunk extends ChunkAccess { } } diff --git a/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch b/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch index c046cafc1f..5e374f3dc0 100644 --- a/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch +++ b/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch @@ -11,10 +11,10 @@ For people who want all chunks to be treated equally, you can chose a fixed valu This allows to fine-tune vanilla gameplay. diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 0eedaf7df86b7bdd22623231f9a49da8e798f204..cf910b466a4b75c0b9219abef5c9bff30793d7bf 100644 +index f6286284b95df0ab6109ff14fda33a48b163f8c2..391ef1eb8c842a1a2dbce45eec8a030f615926d6 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -280,6 +280,13 @@ public class LevelChunk extends ChunkAccess { +@@ -281,6 +281,13 @@ public class LevelChunk extends ChunkAccess { return new ChunkAccess.TicksToSave(this.blockTicks, this.fluidTicks); } diff --git a/patches/server/0170-PlayerNaturallySpawnCreaturesEvent.patch b/patches/server/0170-PlayerNaturallySpawnCreaturesEvent.patch index f76b6a1210..89cf565540 100644 --- a/patches/server/0170-PlayerNaturallySpawnCreaturesEvent.patch +++ b/patches/server/0170-PlayerNaturallySpawnCreaturesEvent.patch @@ -9,10 +9,10 @@ from triggering monster spawns on a server. Also a highly more effecient way to blanket block spawns in a world diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index ea0b82165e452b7b82d1d9a58eef6059cba30552..cdcad9e1470a0b465ee96d1f2b4b8b31e8c3d219 100644 +index e2510ee3f0cb93eae2452bec642855cd6c0c2974..c483acf248484220aaca2100c84e75f3b46fbd31 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -687,7 +687,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -701,7 +701,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange; chunkRange = (chunkRange > 8) ? 8 : chunkRange; @@ -23,7 +23,7 @@ index ea0b82165e452b7b82d1d9a58eef6059cba30552..cdcad9e1470a0b465ee96d1f2b4b8b31 // Spigot end if (!this.distanceManager.hasPlayersNearby(chunkcoordintpair.toLong())) { return false; -@@ -702,6 +704,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -716,6 +718,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } entityplayer = (ServerPlayer) iterator.next(); diff --git a/patches/server/0236-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/patches/server/0236-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch index 8d32ee0f06..2793d417bf 100644 --- a/patches/server/0236-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch +++ b/patches/server/0236-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch @@ -8,10 +8,10 @@ Add -Ddebug.entities=true to your JVM flags to gain more information 1.17: Needs to be reworked for new entity storage system diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index cdcad9e1470a0b465ee96d1f2b4b8b31e8c3d219..165daf5e4b44425974703e9f603955fa09984f1b 100644 +index c483acf248484220aaca2100c84e75f3b46fbd31..c69be9cb3f03ec50e4e57d7e1e93a83701e4cd6c 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -865,6 +865,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -879,6 +879,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } else { ChunkMap.TrackedEntity playerchunkmap_entitytracker = new ChunkMap.TrackedEntity(entity, i, j, entitytypes.trackDeltas()); @@ -19,7 +19,7 @@ index cdcad9e1470a0b465ee96d1f2b4b8b31e8c3d219..165daf5e4b44425974703e9f603955fa this.entityMap.put(entity.getId(), playerchunkmap_entitytracker); playerchunkmap_entitytracker.updatePlayers(this.level.players()); if (entity instanceof ServerPlayer) { -@@ -907,7 +908,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -921,7 +922,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (playerchunkmap_entitytracker1 != null) { playerchunkmap_entitytracker1.broadcastRemoved(); } diff --git a/patches/server/0316-Fix-World-isChunkGenerated-calls.patch b/patches/server/0316-Fix-World-isChunkGenerated-calls.patch index 08287c2bec..990affd052 100644 --- a/patches/server/0316-Fix-World-isChunkGenerated-calls.patch +++ b/patches/server/0316-Fix-World-isChunkGenerated-calls.patch @@ -8,10 +8,10 @@ This patch also adds a chunk status cache on region files (note that its only purpose is to cache the status on DISK) diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 165daf5e4b44425974703e9f603955fa09984f1b..a8533bbac77c6024cd6163149affe4f4733019b5 100644 +index c69be9cb3f03ec50e4e57d7e1e93a83701e4cd6c..5b7260b5a14cb9d2b90cf3c411d119e6bfa84046 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -666,9 +666,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -680,9 +680,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper end private CompletableFuture> readChunk(ChunkPos chunkPos) { @@ -28,7 +28,7 @@ index 165daf5e4b44425974703e9f603955fa09984f1b..a8533bbac77c6024cd6163149affe4f4 } // CraftBukkit start -@@ -677,6 +681,63 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -691,6 +695,63 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // CraftBukkit end } diff --git a/patches/server/0328-Duplicate-UUID-Resolve-Option.patch b/patches/server/0328-Duplicate-UUID-Resolve-Option.patch index 17a74454e7..3529175289 100644 --- a/patches/server/0328-Duplicate-UUID-Resolve-Option.patch +++ b/patches/server/0328-Duplicate-UUID-Resolve-Option.patch @@ -33,7 +33,7 @@ But for those who are ok with leaving this inconsistent behavior, you may use WA It is recommended you regenerate the entities, as these were legit entities, and deserve your love. diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java -index 56c35149fd142af6ffe31434fec0064cf6ea3be5..14e24e3bd8695c7038e8e1f4fb92ca3fd20c6258 100644 +index cbeaadaecf816070b3a37938c8e683180939afc4..95e5073a68e4dd38b70e8268daf2160922c3a12f 100644 --- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java +++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java @@ -74,7 +74,17 @@ public final class ChunkSystem { @@ -56,10 +56,10 @@ index 56c35149fd142af6ffe31434fec0064cf6ea3be5..14e24e3bd8695c7038e8e1f4fb92ca3f public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index a8533bbac77c6024cd6163149affe4f4733019b5..de89863bae07b6e1264b0c70287c085a716862e4 100644 +index 5b7260b5a14cb9d2b90cf3c411d119e6bfa84046..0b1aadd3648f46680e4edc137de3e5ce52428383 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -520,6 +520,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -534,6 +534,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider entity.discard(); needsRemoval = true; } @@ -67,7 +67,7 @@ index a8533bbac77c6024cd6163149affe4f4733019b5..de89863bae07b6e1264b0c70287c085a return !needsRemoval; }), position); // Paper - rewrite chunk system // CraftBukkit end -@@ -531,6 +532,49 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -545,6 +546,49 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider throw new UnsupportedOperationException(); // Paper - rewrite chunk system } diff --git a/patches/server/0340-Tracking-Range-Improvements.patch b/patches/server/0340-Tracking-Range-Improvements.patch index 621f176e28..6c4625adcf 100644 --- a/patches/server/0340-Tracking-Range-Improvements.patch +++ b/patches/server/0340-Tracking-Range-Improvements.patch @@ -8,10 +8,10 @@ Sets tracking range of watermobs to animals instead of misc and simplifies code Also ignores Enderdragon, defaulting it to Mojang's setting diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index de89863bae07b6e1264b0c70287c085a716862e4..ed43ade8c884f9fe7945ef20c1dd2cf5c70680d3 100644 +index 0b1aadd3648f46680e4edc137de3e5ce52428383..06e53890fab287846c8d846f1f829650a1b3c09b 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1251,6 +1251,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1265,6 +1265,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider while (iterator.hasNext()) { Entity entity = (Entity) iterator.next(); int j = entity.getType().clientTrackingRange() * 16; diff --git a/patches/server/0342-implement-optional-per-player-mob-spawns.patch b/patches/server/0342-implement-optional-per-player-mob-spawns.patch index 896a8d77e9..8842042bf8 100644 --- a/patches/server/0342-implement-optional-per-player-mob-spawns.patch +++ b/patches/server/0342-implement-optional-per-player-mob-spawns.patch @@ -4,309 +4,11 @@ Date: Mon, 19 Aug 2019 01:27:58 +0500 Subject: [PATCH] implement optional per player mob spawns -diff --git a/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java b/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java -new file mode 100644 -index 0000000000000000000000000000000000000000..11de56afaf059b00fa5bec293516bcdce7c4b2b9 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java -@@ -0,0 +1,241 @@ -+package com.destroystokyo.paper.util; -+ -+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; -+import java.lang.ref.WeakReference; -+import java.util.Iterator; -+ -+/** @author Spottedleaf */ -+public class PooledHashSets { -+ -+ // we really want to avoid that equals() check as much as possible... -+ protected final Object2ObjectOpenHashMap, PooledObjectLinkedOpenHashSet> mapPool = new Object2ObjectOpenHashMap<>(64, 0.25f); -+ -+ protected void decrementReferenceCount(final PooledObjectLinkedOpenHashSet current) { -+ if (current.referenceCount == 0) { -+ throw new IllegalStateException("Cannot decrement reference count for " + current); -+ } -+ if (current.referenceCount == -1 || --current.referenceCount > 0) { -+ return; -+ } -+ -+ this.mapPool.remove(current); -+ return; -+ } -+ -+ public PooledObjectLinkedOpenHashSet findMapWith(final PooledObjectLinkedOpenHashSet current, final E object) { -+ final PooledObjectLinkedOpenHashSet cached = current.getAddCache(object); -+ -+ if (cached != null) { -+ if (cached.referenceCount != -1) { -+ ++cached.referenceCount; -+ } -+ -+ decrementReferenceCount(current); -+ -+ return cached; -+ } -+ -+ if (!current.add(object)) { -+ return current; -+ } -+ -+ // we use get/put since we use a different key on put -+ PooledObjectLinkedOpenHashSet ret = this.mapPool.get(current); -+ -+ if (ret == null) { -+ ret = new PooledObjectLinkedOpenHashSet<>(current); -+ current.remove(object); -+ this.mapPool.put(ret, ret); -+ ret.referenceCount = 1; -+ } else { -+ if (ret.referenceCount != -1) { -+ ++ret.referenceCount; -+ } -+ current.remove(object); -+ } -+ -+ current.updateAddCache(object, ret); -+ -+ decrementReferenceCount(current); -+ return ret; -+ } -+ -+ // rets null if current.size() == 1 -+ public PooledObjectLinkedOpenHashSet findMapWithout(final PooledObjectLinkedOpenHashSet current, final E object) { -+ if (current.set.size() == 1) { -+ decrementReferenceCount(current); -+ return null; -+ } -+ -+ final PooledObjectLinkedOpenHashSet cached = current.getRemoveCache(object); -+ -+ if (cached != null) { -+ if (cached.referenceCount != -1) { -+ ++cached.referenceCount; -+ } -+ -+ decrementReferenceCount(current); -+ -+ return cached; -+ } -+ -+ if (!current.remove(object)) { -+ return current; -+ } -+ -+ // we use get/put since we use a different key on put -+ PooledObjectLinkedOpenHashSet ret = this.mapPool.get(current); -+ -+ if (ret == null) { -+ ret = new PooledObjectLinkedOpenHashSet<>(current); -+ current.add(object); -+ this.mapPool.put(ret, ret); -+ ret.referenceCount = 1; -+ } else { -+ if (ret.referenceCount != -1) { -+ ++ret.referenceCount; -+ } -+ current.add(object); -+ } -+ -+ current.updateRemoveCache(object, ret); -+ -+ decrementReferenceCount(current); -+ return ret; -+ } -+ -+ public static final class PooledObjectLinkedOpenHashSet implements Iterable { -+ -+ private static final WeakReference NULL_REFERENCE = new WeakReference(null); -+ -+ final ObjectLinkedOpenHashSet set; -+ int referenceCount; // -1 if special -+ int hash; // optimize hashcode -+ -+ // add cache -+ WeakReference lastAddObject = NULL_REFERENCE; -+ WeakReference> lastAddMap = NULL_REFERENCE; -+ -+ // remove cache -+ WeakReference lastRemoveObject = NULL_REFERENCE; -+ WeakReference> lastRemoveMap = NULL_REFERENCE; -+ -+ public PooledObjectLinkedOpenHashSet() { -+ this.set = new ObjectLinkedOpenHashSet<>(2, 0.6f); -+ } -+ -+ public PooledObjectLinkedOpenHashSet(final E single) { -+ this(); -+ this.referenceCount = -1; -+ this.add(single); -+ } -+ -+ public PooledObjectLinkedOpenHashSet(final PooledObjectLinkedOpenHashSet other) { -+ this.set = other.set.clone(); -+ this.hash = other.hash; -+ } -+ -+ // from https://github.com/Spottedleaf/ConcurrentUtil/blob/master/src/main/java/ca/spottedleaf/concurrentutil/util/IntegerUtil.java -+ // generated by https://github.com/skeeto/hash-prospector -+ static int hash0(int x) { -+ x *= 0x36935555; -+ x ^= x >>> 16; -+ return x; -+ } -+ -+ public PooledObjectLinkedOpenHashSet getAddCache(final E element) { -+ final E currentAdd = this.lastAddObject.get(); -+ -+ if (currentAdd == null || !(currentAdd == element || currentAdd.equals(element))) { -+ return null; -+ } -+ -+ final PooledObjectLinkedOpenHashSet map = this.lastAddMap.get(); -+ if (map == null || map.referenceCount == 0) { -+ // we need to ret null if ref count is zero as calling code will assume the map is in use -+ return null; -+ } -+ -+ return map; -+ } -+ -+ public PooledObjectLinkedOpenHashSet getRemoveCache(final E element) { -+ final E currentRemove = this.lastRemoveObject.get(); -+ -+ if (currentRemove == null || !(currentRemove == element || currentRemove.equals(element))) { -+ return null; -+ } -+ -+ final PooledObjectLinkedOpenHashSet map = this.lastRemoveMap.get(); -+ if (map == null || map.referenceCount == 0) { -+ // we need to ret null if ref count is zero as calling code will assume the map is in use -+ return null; -+ } -+ -+ return map; -+ } -+ -+ public void updateAddCache(final E element, final PooledObjectLinkedOpenHashSet map) { -+ this.lastAddObject = new WeakReference<>(element); -+ this.lastAddMap = new WeakReference<>(map); -+ } -+ -+ public void updateRemoveCache(final E element, final PooledObjectLinkedOpenHashSet map) { -+ this.lastRemoveObject = new WeakReference<>(element); -+ this.lastRemoveMap = new WeakReference<>(map); -+ } -+ -+ boolean add(final E element) { -+ boolean added = this.set.add(element); -+ -+ if (added) { -+ this.hash += hash0(element.hashCode()); -+ } -+ -+ return added; -+ } -+ -+ boolean remove(Object element) { -+ boolean removed = this.set.remove(element); -+ -+ if (removed) { -+ this.hash -= hash0(element.hashCode()); -+ } -+ -+ return removed; -+ } -+ -+ @Override -+ public Iterator iterator() { -+ return this.set.iterator(); -+ } -+ -+ @Override -+ public int hashCode() { -+ return this.hash; -+ } -+ -+ @Override -+ public boolean equals(final Object other) { -+ if (!(other instanceof PooledObjectLinkedOpenHashSet)) { -+ return false; -+ } -+ if (this.referenceCount == 0) { -+ return other == this; -+ } else { -+ if (other == this) { -+ // Unfortunately we are never equal to our own instance while in use! -+ return false; -+ } -+ return this.hash == ((PooledObjectLinkedOpenHashSet)other).hash && this.set.equals(((PooledObjectLinkedOpenHashSet)other).set); -+ } -+ } -+ -+ @Override -+ public String toString() { -+ return "PooledHashSet: size: " + this.set.size() + ", reference count: " + this.referenceCount + ", hash: " + -+ this.hashCode() + ", identity: " + System.identityHashCode(this) + " map: " + this.set.toString(); -+ } -+ } -+} diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index ed43ade8c884f9fe7945ef20c1dd2cf5c70680d3..1f30d06781cea0855be26784c239e71ed1a2f6c3 100644 +index 06e53890fab287846c8d846f1f829650a1b3c09b..847019bf4a81b82e6e11a19aebd69b7270d81b06 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -142,6 +142,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - private final Long2LongMap chunkSaveCooldowns; - private final Queue unloadQueue; - private int serverViewDistance; -+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper - - // Paper - rewrite chunk system - -@@ -153,11 +154,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX()); - int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated -+ // Paper start - per player mob spawning -+ if (this.playerMobDistanceMap != null) { -+ this.playerMobDistanceMap.add(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player)); -+ } -+ // Paper end - per player mob spawning - } - - void removePlayerFromDistanceMaps(ServerPlayer player) { - this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader - -+ // Paper start - per player mob spawning -+ if (this.playerMobDistanceMap != null) { -+ this.playerMobDistanceMap.remove(player); -+ } -+ // Paper end - per player mob spawning - } - - void updateMaps(ServerPlayer player) { -@@ -165,6 +176,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ()); - // Note: players need to be explicitly added to distance maps before they can be updated - this.level.playerChunkLoader.updatePlayer(player); // Paper - replace chunk loader -+ -+ // Paper start - per player mob spawning -+ if (this.playerMobDistanceMap != null) { -+ this.playerMobDistanceMap.update(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player)); -+ } -+ // Paper end - per player mob spawning - } - // Paper end - // Paper start -@@ -250,6 +267,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new); - this.regionManagers.add(this.dataRegionManager); - // Paper end -+ this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper - } - - protected ChunkGenerator generator() { -@@ -275,6 +293,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -289,6 +289,29 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider }); } @@ -317,16 +19,14 @@ index ed43ade8c884f9fe7945ef20c1dd2cf5c70680d3..1f30d06781cea0855be26784c239e71e + } + int index = entity.getType().getCategory().ordinal(); + -+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = this.playerMobDistanceMap.getObjectsInRange(entity.chunkPosition()); ++ final com.destroystokyo.paper.util.maplist.ReferenceList inRange = ++ this.getNearbyPlayers().getPlayers(entity.chunkPosition(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE); + if (inRange == null) { + return; + } -+ final Object[] backingSet = inRange.getBackingSet(); -+ for (int i = 0; i < backingSet.length; i++) { -+ if (!(backingSet[i] instanceof final ServerPlayer player)) { -+ continue; -+ } -+ ++player.mobCounts[index]; ++ final Object[] backingSet = inRange.getRawData(); ++ for (int i = 0, len = inRange.size(); i < len; i++) { ++ ++((ServerPlayer)backingSet[i]).mobCounts[index]; + } + } + @@ -339,7 +39,7 @@ index ed43ade8c884f9fe7945ef20c1dd2cf5c70680d3..1f30d06781cea0855be26784c239e71e double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8); double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8); diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 4649e597b86335b33d9e9227d966dd7ad8208096..e655cf837f7bd7d4271dca80b9c6764372336910 100644 +index 4649e597b86335b33d9e9227d966dd7ad8208096..b73d45c205db39ed3e97a127e53371db81e0d63f 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -543,7 +543,18 @@ public class ServerChunkCache extends ChunkSource { @@ -349,45 +49,36 @@ index 4649e597b86335b33d9e9227d966dd7ad8208096..e655cf837f7bd7d4271dca80b9c67643 - NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)); + // Paper start - per player mob spawning + NaturalSpawner.SpawnState spawnercreature_d; // moved down -+ if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled ++ 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) { + Arrays.fill(player.mobCounts, 0); + } + spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); + } else { -+ spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); + } + // Paper end this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings this.lastSpawnState = spawnercreature_d; diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index dcb6a1bd3774e63dd14fbd8ddd6ff09c68741379..ad34d1562131a5140c4cc83aa3ea94fab08d12da 100644 +index dcb6a1bd3774e63dd14fbd8ddd6ff09c68741379..55c3416f209178a1a100228d9b1db620c79bc288 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -246,6 +246,11 @@ public class ServerPlayer extends Player { +@@ -246,6 +246,10 @@ public class ServerPlayer extends Player { public boolean queueHealthUpdatePacket = false; public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket; // Paper end + // Paper start - mob spawning rework + public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length; + public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper -+ public final com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet cachedSingleMobDistanceMap; -+ // Paper end ++ // Paper end - mob spawning rework // CraftBukkit start public String displayName; -@@ -383,6 +388,7 @@ public class ServerPlayer extends Player { - this.adventure$displayName = net.kyori.adventure.text.Component.text(this.getScoreboardName()); // Paper - this.bukkitPickUpLoot = true; - this.maxHealthCache = this.getMaxHealth(); -+ this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper - } - - // Yes, this doesn't match Vanilla, but it's the best we can do for now. diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a62668ea5037 100644 +index fe38079d69f3e9987ad5ab077ae09d05017a681a..9df761f5cf043e8d2dffa711c20ab32fe2992331 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java @@ -70,6 +70,12 @@ public final class NaturalSpawner { @@ -421,7 +112,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 }); } } -@@ -143,13 +154,37 @@ public final class NaturalSpawner { +@@ -143,13 +154,35 @@ public final class NaturalSpawner { continue; } @@ -433,14 +124,12 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 + + if (world.paperConfig().entities.spawning.perPlayerMobSpawns) { + int minDiff = Integer.MAX_VALUE; -+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = world.getChunkSource().chunkMap.playerMobDistanceMap.getObjectsInRange(chunk.getPos()); ++ final com.destroystokyo.paper.util.maplist.ReferenceList inRange = ++ world.chunkSource.chunkMap.getNearbyPlayers().getPlayers(chunk.getPos(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE); + if (inRange != null) { -+ final Object[] backingSet = inRange.getBackingSet(); -+ for (int k = 0; k < backingSet.length; k++) { -+ if (!(backingSet[k] instanceof final net.minecraft.server.level.ServerPlayer player)) { -+ continue; -+ } -+ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear(player, enumcreaturetype), minDiff); ++ final Object[] backingSet = inRange.getRawData(); ++ for (int k = 0, len = inRange.size(); k < len; k++) { ++ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear((net.minecraft.server.level.ServerPlayer)backingSet[k], enumcreaturetype), minDiff); + } + } + difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; @@ -461,7 +150,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 } } -@@ -158,11 +193,17 @@ public final class NaturalSpawner { +@@ -158,11 +191,17 @@ public final class NaturalSpawner { } public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { @@ -480,7 +169,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 } @VisibleForDebug -@@ -173,15 +214,21 @@ public final class NaturalSpawner { +@@ -173,15 +212,21 @@ public final class NaturalSpawner { }); } @@ -503,7 +192,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 int k = 0; while (k < 3) { -@@ -223,14 +270,14 @@ public final class NaturalSpawner { +@@ -223,14 +268,14 @@ public final class NaturalSpawner { // Paper start PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2); if (doSpawning == PreSpawnStatus.ABORT) { @@ -520,7 +209,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 } entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F); -@@ -243,10 +290,15 @@ public final class NaturalSpawner { +@@ -243,10 +288,15 @@ public final class NaturalSpawner { ++j; ++k1; runner.run(entityinsentient, chunk); @@ -538,7 +227,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 } if (entityinsentient.isMaxGroupSizeReached(k1)) { -@@ -268,6 +320,7 @@ public final class NaturalSpawner { +@@ -268,6 +318,7 @@ public final class NaturalSpawner { } } @@ -546,7 +235,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 } private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) { -@@ -567,7 +620,7 @@ public final class NaturalSpawner { +@@ -567,7 +618,7 @@ public final class NaturalSpawner { MobCategory enumcreaturetype = entitytypes.getCategory(); this.mobCategoryCounts.addTo(enumcreaturetype, 1); @@ -555,7 +244,7 @@ index fe38079d69f3e9987ad5ab077ae09d05017a681a..7983f9a21251cec4857529ad1180a626 } public int getSpawnableChunkCount() { -@@ -583,6 +636,7 @@ public final class NaturalSpawner { +@@ -583,6 +634,7 @@ public final class NaturalSpawner { int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; // CraftBukkit end diff --git a/patches/server/0343-Anti-Xray.patch b/patches/server/0343-Anti-Xray.patch index 286de288fc..818476c21f 100644 --- a/patches/server/0343-Anti-Xray.patch +++ b/patches/server/0343-Anti-Xray.patch @@ -1213,10 +1213,10 @@ index 525c89bc926f13af6f94fc46c897525e37477eca..8b96d1b7548d354fbcabe6d1b5e9d6c3 } diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index cf910b466a4b75c0b9219abef5c9bff30793d7bf..1bb82df6602bddd3f5785c800124d2a3096c0f8d 100644 +index 391ef1eb8c842a1a2dbce45eec8a030f615926d6..abb75d8bea8f975c5b04c9a9fff7187a3520ac26 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -90,7 +90,7 @@ public class LevelChunk extends ChunkAccess { +@@ -91,7 +91,7 @@ public class LevelChunk extends ChunkAccess { } public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks blockTickScheduler, LevelChunkTicks fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) { diff --git a/patches/server/0346-Optimise-getChunkAt-calls-for-loaded-chunks.patch b/patches/server/0346-Optimise-getChunkAt-calls-for-loaded-chunks.patch index 09cc020c8f..3b47f02e45 100644 --- a/patches/server/0346-Optimise-getChunkAt-calls-for-loaded-chunks.patch +++ b/patches/server/0346-Optimise-getChunkAt-calls-for-loaded-chunks.patch @@ -7,7 +7,7 @@ bypass the need to get a player chunk, then get the either, then unwrap it... diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index e655cf837f7bd7d4271dca80b9c6764372336910..2d44b8a98216f879f2bc89fba1dad53cd5558ff9 100644 +index b73d45c205db39ed3e97a127e53371db81e0d63f..ab1a45f30930408a3e3c7172169ff648643ba54d 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -270,6 +270,12 @@ public class ServerChunkCache extends ChunkSource { diff --git a/patches/server/0347-Add-debug-for-sync-chunk-loads.patch b/patches/server/0347-Add-debug-for-sync-chunk-loads.patch index cecbe09306..70c3c7be99 100644 --- a/patches/server/0347-Add-debug-for-sync-chunk-loads.patch +++ b/patches/server/0347-Add-debug-for-sync-chunk-loads.patch @@ -300,7 +300,7 @@ index 0000000000000000000000000000000000000000..95d6022c9cfb2e36ec5a71be6e343540 + } +} diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 2d44b8a98216f879f2bc89fba1dad53cd5558ff9..7881a34e01dd6c8e31b55577bb9323a0171d24bf 100644 +index ab1a45f30930408a3e3c7172169ff648643ba54d..a8c1620ec48adcd2bb116d48f8bb2d4af50f5f43 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -294,6 +294,7 @@ public class ServerChunkCache extends ChunkSource { diff --git a/patches/server/0357-Optimise-Chunk-getFluid.patch b/patches/server/0357-Optimise-Chunk-getFluid.patch index 62bf1a4c2a..530e61be7c 100644 --- a/patches/server/0357-Optimise-Chunk-getFluid.patch +++ b/patches/server/0357-Optimise-Chunk-getFluid.patch @@ -8,10 +8,10 @@ faster on its own, however removing the try catch makes it easier to inline due to code size diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 1bb82df6602bddd3f5785c800124d2a3096c0f8d..95531e115f69f52cb6ec55c6e4bc34fb109758c4 100644 +index abb75d8bea8f975c5b04c9a9fff7187a3520ac26..d2cdb8100fbbdc3d92eb7a61748340c40de343f7 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -380,18 +380,20 @@ public class LevelChunk extends ChunkAccess { +@@ -381,18 +381,20 @@ public class LevelChunk extends ChunkAccess { } public FluidState getFluidState(int x, int y, int z) { @@ -38,7 +38,7 @@ index 1bb82df6602bddd3f5785c800124d2a3096c0f8d..95531e115f69f52cb6ec55c6e4bc34fb } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting fluid state"); CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block being got"); -@@ -401,6 +403,7 @@ public class LevelChunk extends ChunkAccess { +@@ -402,6 +404,7 @@ public class LevelChunk extends ChunkAccess { }); throw new ReportedException(crashreport); } diff --git a/patches/server/0362-Pillager-patrol-spawn-settings-and-per-player-option.patch b/patches/server/0362-Pillager-patrol-spawn-settings-and-per-player-option.patch index ed89befa49..a30c8f377b 100644 --- a/patches/server/0362-Pillager-patrol-spawn-settings-and-per-player-option.patch +++ b/patches/server/0362-Pillager-patrol-spawn-settings-and-per-player-option.patch @@ -10,7 +10,7 @@ When not per player it will use the Vanilla mechanic of one delay per world and the world age for the start day. diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index ad34d1562131a5140c4cc83aa3ea94fab08d12da..a6b9f246ed2d59122c31ff212d99adc0602ef35b 100644 +index 55c3416f209178a1a100228d9b1db620c79bc288..5c0b406214b7da3b33acf1f545105d4ecae1f783 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -242,6 +242,7 @@ public class ServerPlayer extends Player { diff --git a/patches/server/0365-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch b/patches/server/0365-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch index a9d149d2a6..bc5a912401 100644 --- a/patches/server/0365-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch +++ b/patches/server/0365-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch @@ -7,10 +7,10 @@ Suspected case would be around the technique used in .stopRiding Stack will identify any causer of this and warn instead of crashing. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 1f30d06781cea0855be26784c239e71ed1a2f6c3..91373583904a22bc28c82cf3dfec9f60aebee9a3 100644 +index 847019bf4a81b82e6e11a19aebd69b7270d81b06..41a6712d4c55907df1274f5a825e86d7fedb42bb 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1000,6 +1000,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -994,6 +994,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public void addEntity(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot diff --git a/patches/server/0366-Don-t-tick-dead-players.patch b/patches/server/0366-Don-t-tick-dead-players.patch index 051c7f34bd..609adabf08 100644 --- a/patches/server/0366-Don-t-tick-dead-players.patch +++ b/patches/server/0366-Don-t-tick-dead-players.patch @@ -7,10 +7,10 @@ Causes sync chunk loads and who knows what all else. This is safe because Spectators are skipped in unloaded chunks too in vanilla. diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index a6b9f246ed2d59122c31ff212d99adc0602ef35b..88152bb21f42f705f6d321f0f088e7f83ac66e62 100644 +index 5c0b406214b7da3b33acf1f545105d4ecae1f783..40709c98687334b5383ad9d412d5b6ff7748b02b 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -727,7 +727,7 @@ public class ServerPlayer extends Player { +@@ -725,7 +725,7 @@ public class ServerPlayer extends Player { public void doTick() { try { diff --git a/patches/server/0369-Don-t-move-existing-players-to-world-spawn.patch b/patches/server/0369-Don-t-move-existing-players-to-world-spawn.patch index 7671a8d6dd..24403cc3e9 100644 --- a/patches/server/0369-Don-t-move-existing-players-to-world-spawn.patch +++ b/patches/server/0369-Don-t-move-existing-players-to-world-spawn.patch @@ -13,10 +13,10 @@ By skipping this, we avoid potential for a large spike on server start. public net.minecraft.server.level.ServerPlayer fudgeSpawnLocation(Lnet/minecraft/server/level/ServerLevel;)V diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 88152bb21f42f705f6d321f0f088e7f83ac66e62..99cabb83c54106ff39e89f9f138548aa734ccac3 100644 +index 40709c98687334b5383ad9d412d5b6ff7748b02b..de8121b1e4a30e091ce6eb7d3e5beb6029fd95d8 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -379,7 +379,7 @@ public class ServerPlayer extends Player { +@@ -378,7 +378,7 @@ public class ServerPlayer extends Player { this.stats = server.getPlayerList().getPlayerStats(this); this.advancements = server.getPlayerList().getPlayerAdvancements(this); this.setMaxUpStep(1.0F); @@ -25,7 +25,7 @@ index 88152bb21f42f705f6d321f0f088e7f83ac66e62..99cabb83c54106ff39e89f9f138548aa this.updateOptions(clientOptions); this.cachedSingleHashSet = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper -@@ -614,7 +614,7 @@ public class ServerPlayer extends Player { +@@ -612,7 +612,7 @@ public class ServerPlayer extends Player { position = Vec3.atCenterOf(world.getSharedSpawnPos()); } this.setLevel(world); diff --git a/patches/server/0371-Improved-Watchdog-Support.patch b/patches/server/0371-Improved-Watchdog-Support.patch index 1e1f1ae1f2..3731fb00ff 100644 --- a/patches/server/0371-Improved-Watchdog-Support.patch +++ b/patches/server/0371-Improved-Watchdog-Support.patch @@ -308,10 +308,10 @@ index 7a1886585bd00dc8213ce22130b8b6fea52c5cf6..52a44510d0499df56d2ebef0963fe816 final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); MinecraftServer.LOGGER.error(msg, throwable); diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 95531e115f69f52cb6ec55c6e4bc34fb109758c4..cd725cdfe37eb53eea78f110d5d3ca00342781c5 100644 +index d2cdb8100fbbdc3d92eb7a61748340c40de343f7..535e71bdb2531432974dc91ec9d5edb2b3ebc744 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -1180,6 +1180,7 @@ public class LevelChunk extends ChunkAccess { +@@ -1181,6 +1181,7 @@ public class LevelChunk extends ChunkAccess { gameprofilerfiller.pop(); } catch (Throwable throwable) { diff --git a/patches/server/0375-Prevent-opening-inventories-when-frozen.patch b/patches/server/0375-Prevent-opening-inventories-when-frozen.patch index 5d74aed43c..fa840acd71 100644 --- a/patches/server/0375-Prevent-opening-inventories-when-frozen.patch +++ b/patches/server/0375-Prevent-opening-inventories-when-frozen.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Prevent opening inventories when frozen diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 99cabb83c54106ff39e89f9f138548aa734ccac3..1ff911085da7d1a2a378c60c66f8e5364b9774b4 100644 +index de8121b1e4a30e091ce6eb7d3e5beb6029fd95d8..0bab5208a38e8e7c848e9162396df535eb7e5711 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -696,7 +696,7 @@ public class ServerPlayer extends Player { +@@ -694,7 +694,7 @@ public class ServerPlayer extends Player { containerUpdateDelay = this.level().paperConfig().tickRates.containerUpdate; } // Paper end @@ -17,7 +17,7 @@ index 99cabb83c54106ff39e89f9f138548aa734ccac3..1ff911085da7d1a2a378c60c66f8e536 this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper this.containerMenu = this.inventoryMenu; } -@@ -1545,7 +1545,7 @@ public class ServerPlayer extends Player { +@@ -1543,7 +1543,7 @@ public class ServerPlayer extends Player { } else { // CraftBukkit start this.containerMenu = container; diff --git a/patches/server/0377-Implement-Player-Client-Options-API.patch b/patches/server/0377-Implement-Player-Client-Options-API.patch index 2a6f5b32ff..adf3d115ae 100644 --- a/patches/server/0377-Implement-Player-Client-Options-API.patch +++ b/patches/server/0377-Implement-Player-Client-Options-API.patch @@ -87,10 +87,10 @@ index 0000000000000000000000000000000000000000..b6f4400df3d8ec7e06a996de54f8cabb + } +} diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 1ff911085da7d1a2a378c60c66f8e5364b9774b4..c13f088a74448f80e111bdeeb712087a238ed2b8 100644 +index 0bab5208a38e8e7c848e9162396df535eb7e5711..baec871645598c36cefda7478e91b560bdfdf6fe 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1952,7 +1952,23 @@ public class ServerPlayer extends Player { +@@ -1950,7 +1950,23 @@ public class ServerPlayer extends Player { } } diff --git a/patches/server/0379-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch b/patches/server/0379-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch index 3fd0832cbf..62f1c4ed1c 100644 --- a/patches/server/0379-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch +++ b/patches/server/0379-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch @@ -31,10 +31,10 @@ delays anymore. public net.minecraft.server.level.ChunkMap addEntity(Lnet/minecraft/world/entity/Entity;)V diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 91373583904a22bc28c82cf3dfec9f60aebee9a3..463a82f08b7848a4b8a4eb89b201a6e8424c6831 100644 +index 41a6712d4c55907df1274f5a825e86d7fedb42bb..c10ef2e6af3e6c52eeb8c96cdbf5352d9dd25dd2 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1006,6 +1006,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1000,6 +1000,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable()); return; } @@ -43,10 +43,10 @@ index 91373583904a22bc28c82cf3dfec9f60aebee9a3..463a82f08b7848a4b8a4eb89b201a6e8 if (!(entity instanceof EnderDragonPart)) { EntityType entitytypes = entity.getType(); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index c13f088a74448f80e111bdeeb712087a238ed2b8..db722b6c6d6d7c5b973f56a2da624514a76b53cd 100644 +index baec871645598c36cefda7478e91b560bdfdf6fe..d31670aecc2ebe67bf6071c67126ce4142b93053 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -265,6 +265,7 @@ public class ServerPlayer extends Player { +@@ -264,6 +264,7 @@ public class ServerPlayer extends Player { public double maxHealthCache; public boolean joining = true; public boolean sentListPacket = false; diff --git a/patches/server/0392-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0392-Deobfuscate-stacktraces-in-log-messages-crash-report.patch index a0f9fd4934..756c113410 100644 --- a/patches/server/0392-Deobfuscate-stacktraces-in-log-messages-crash-report.patch +++ b/patches/server/0392-Deobfuscate-stacktraces-in-log-messages-crash-report.patch @@ -612,10 +612,10 @@ index 5db27d7bcaaa2eeaeeb08401513d8d23f6cb63c7..ce43cb0152ba07c6c21e08142d65813d } } diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index cd725cdfe37eb53eea78f110d5d3ca00342781c5..c4d2036d51fe16b640605ae26a2b71323758cc26 100644 +index 535e71bdb2531432974dc91ec9d5edb2b3ebc744..4abec88caab4116cfa318f7b66c6b1a8346a7401 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -603,7 +603,7 @@ public class LevelChunk extends ChunkAccess { +@@ -604,7 +604,7 @@ public class LevelChunk extends ChunkAccess { + " (" + getBlockState(blockposition) + ") where there was no entity tile!\n" + "Chunk coordinates: " + (this.chunkPos.x * 16) + "," + (this.chunkPos.z * 16) + "\nWorld: " + level.getLevel().dimension().location()); diff --git a/patches/server/0419-Fix-Per-World-Difficulty-Remembering-Difficulty.patch b/patches/server/0419-Fix-Per-World-Difficulty-Remembering-Difficulty.patch index d065dcce24..9f6a79d59b 100644 --- a/patches/server/0419-Fix-Per-World-Difficulty-Remembering-Difficulty.patch +++ b/patches/server/0419-Fix-Per-World-Difficulty-Remembering-Difficulty.patch @@ -76,10 +76,10 @@ index 1f8dcc331505890ba72777b5d0cda2427e0ccfd1..a661f8a83d4e57ca48bbf28dc668f8ff @Override diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index db722b6c6d6d7c5b973f56a2da624514a76b53cd..7b5496d3a21316a7410dfbfbf9335a432b466560 100644 +index d31670aecc2ebe67bf6071c67126ce4142b93053..6de4e9f379fe0cb44f14fe68ae1dee55a73d2d99 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1206,7 +1206,7 @@ public class ServerPlayer extends Player { +@@ -1204,7 +1204,7 @@ public class ServerPlayer extends Player { this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(worldserver), (byte) 3)); diff --git a/patches/server/0424-incremental-chunk-and-player-saving.patch b/patches/server/0424-incremental-chunk-and-player-saving.patch index b68fc8af57..e2b9a35c1f 100644 --- a/patches/server/0424-incremental-chunk-and-player-saving.patch +++ b/patches/server/0424-incremental-chunk-and-player-saving.patch @@ -53,7 +53,7 @@ index a106f5f573c9eec1f56fa88f7398ac28d231b5e5..04258e07f806acd9a0d659b682e0119a // Paper start - move executeAll() into full server tick timing try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) { diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 7881a34e01dd6c8e31b55577bb9323a0171d24bf..7b5851cd4f5a91bf7609464d7d6c4bb5a3da14bf 100644 +index a8c1620ec48adcd2bb116d48f8bb2d4af50f5f43..7d7dc8246f44d6ffd46b05f917ffbdb9cc24372b 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -452,6 +452,15 @@ public class ServerChunkCache extends ChunkSource { @@ -115,7 +115,7 @@ index 8467ea174dd48010b94b3f3c84ce097ecbb2ef14..5826536978724b54080d7d4ee61aa798 // Paper start - rewrite chunk system - add close param this.save(progressListener, flush, savingDisabled, false); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 7b5496d3a21316a7410dfbfbf9335a432b466560..4d96b44533d39962b60ee43c232d5aa80b88e185 100644 +index 6de4e9f379fe0cb44f14fe68ae1dee55a73d2d99..4a4002b666c70c7065cad0743377cd13e443aa37 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -184,6 +184,7 @@ import org.bukkit.inventory.MainHand; diff --git a/patches/server/0488-Add-API-for-quit-reason.patch b/patches/server/0488-Add-API-for-quit-reason.patch index aaf4aab245..3898c56f56 100644 --- a/patches/server/0488-Add-API-for-quit-reason.patch +++ b/patches/server/0488-Add-API-for-quit-reason.patch @@ -25,10 +25,10 @@ index b9d1eefb2afbf24bdc902c6dea06e911e835d698..4f5216cb3ac323de1a79c2b9c4d2ddd7 Connection.LOGGER.debug("Failed to sent packet", throwable); if (this.getSending() == PacketFlow.CLIENTBOUND) { diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 4d96b44533d39962b60ee43c232d5aa80b88e185..c70a7feab56fd63354ac2dd887a5d6157eaf8407 100644 +index 4a4002b666c70c7065cad0743377cd13e443aa37..576fe4a114c7ad0edde3ce7b2cf1b397a287adcd 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -272,6 +272,7 @@ public class ServerPlayer extends Player { +@@ -271,6 +271,7 @@ public class ServerPlayer extends Player { public boolean isRealPlayer; // Paper public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet cachedSingleHashSet; // Paper public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper diff --git a/patches/server/0545-Reset-shield-blocking-on-dimension-change.patch b/patches/server/0545-Reset-shield-blocking-on-dimension-change.patch index 001c6c482d..ab5d569d14 100644 --- a/patches/server/0545-Reset-shield-blocking-on-dimension-change.patch +++ b/patches/server/0545-Reset-shield-blocking-on-dimension-change.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Reset shield blocking on dimension change diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index c70a7feab56fd63354ac2dd887a5d6157eaf8407..cb2f86e0ad0edc54711826215df4406c5725f29c 100644 +index 576fe4a114c7ad0edde3ce7b2cf1b397a287adcd..717e7d0cd5fd62058d5308fc15070bdf72a03bc4 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1243,6 +1243,11 @@ public class ServerPlayer extends Player { +@@ -1241,6 +1241,11 @@ public class ServerPlayer extends Player { this.level().getCraftServer().getPluginManager().callEvent(changeEvent); // CraftBukkit end } diff --git a/patches/server/0600-additions-to-PlayerGameModeChangeEvent.patch b/patches/server/0600-additions-to-PlayerGameModeChangeEvent.patch index ae25cb7336..11c0119bdd 100644 --- a/patches/server/0600-additions-to-PlayerGameModeChangeEvent.patch +++ b/patches/server/0600-additions-to-PlayerGameModeChangeEvent.patch @@ -45,10 +45,10 @@ index aee8618e27b893b72931e925724dd683d2e6d2aa..5cb15e2209d7b315904a1fc6d650ce1e } diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index cb2f86e0ad0edc54711826215df4406c5725f29c..0abc9dfd73b3cb16b29bdbcc40f0d0ea1c022f34 100644 +index 717e7d0cd5fd62058d5308fc15070bdf72a03bc4..327307404d336d0e66a265e79448f2342cc36220 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1888,8 +1888,16 @@ public class ServerPlayer extends Player { +@@ -1886,8 +1886,16 @@ public class ServerPlayer extends Player { } public boolean setGameMode(GameType gameMode) { @@ -67,7 +67,7 @@ index cb2f86e0ad0edc54711826215df4406c5725f29c..0abc9dfd73b3cb16b29bdbcc40f0d0ea } else { this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float) gameMode.getId())); if (gameMode == GameType.SPECTATOR) { -@@ -1901,7 +1909,7 @@ public class ServerPlayer extends Player { +@@ -1899,7 +1907,7 @@ public class ServerPlayer extends Player { this.onUpdateAbilities(); this.updateEffectVisibility(); @@ -76,7 +76,7 @@ index cb2f86e0ad0edc54711826215df4406c5725f29c..0abc9dfd73b3cb16b29bdbcc40f0d0ea } } -@@ -2305,6 +2313,16 @@ public class ServerPlayer extends Player { +@@ -2303,6 +2311,16 @@ public class ServerPlayer extends Player { } public void loadGameTypes(@Nullable CompoundTag nbt) { diff --git a/patches/server/0627-Fix-PlayerDropItemEvent-using-wrong-item.patch b/patches/server/0627-Fix-PlayerDropItemEvent-using-wrong-item.patch index ea75e13bd6..7bc72819d3 100644 --- a/patches/server/0627-Fix-PlayerDropItemEvent-using-wrong-item.patch +++ b/patches/server/0627-Fix-PlayerDropItemEvent-using-wrong-item.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fix PlayerDropItemEvent using wrong item diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0abc9dfd73b3cb16b29bdbcc40f0d0ea1c022f34..2265259b24ae27a85beea1d47ec2c1bae6baa2a7 100644 +index 327307404d336d0e66a265e79448f2342cc36220..1d8328bc7acde64f3a0cff4d20bdca44baddd409 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -2282,7 +2282,7 @@ public class ServerPlayer extends Player { +@@ -2280,7 +2280,7 @@ public class ServerPlayer extends Player { if (retainOwnership) { if (!itemstack1.isEmpty()) { diff --git a/patches/server/0634-Fixes-kick-event-leave-message-not-being-sent.patch b/patches/server/0634-Fixes-kick-event-leave-message-not-being-sent.patch index c0df3e2eed..761176f4d2 100644 --- a/patches/server/0634-Fixes-kick-event-leave-message-not-being-sent.patch +++ b/patches/server/0634-Fixes-kick-event-leave-message-not-being-sent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fixes kick event leave message not being sent diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 2265259b24ae27a85beea1d47ec2c1bae6baa2a7..552d64e47f17d916a4e374b877d12ff7a583a9e7 100644 +index 1d8328bc7acde64f3a0cff4d20bdca44baddd409..a75377cf94c70011a967e7ede93f903aae4f9f05 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -267,7 +267,6 @@ public class ServerPlayer extends Player { +@@ -266,7 +266,6 @@ public class ServerPlayer extends Player { public boolean joining = true; public boolean sentListPacket = false; public boolean supressTrackerForLogin = false; // Paper diff --git a/patches/server/0637-Don-t-apply-cramming-damage-to-players.patch b/patches/server/0637-Don-t-apply-cramming-damage-to-players.patch index f294bdc5cb..0482995caf 100644 --- a/patches/server/0637-Don-t-apply-cramming-damage-to-players.patch +++ b/patches/server/0637-Don-t-apply-cramming-damage-to-players.patch @@ -11,7 +11,7 @@ It does not make a lot of sense to damage players if they get crammed, For those who really want it a config option is provided. diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 552d64e47f17d916a4e374b877d12ff7a583a9e7..a59ac2011b85ff9b1185a14eca4d2796b2666df1 100644 +index a75377cf94c70011a967e7ede93f903aae4f9f05..b8f60c2deb7e46f654d0169751c793b3b3408748 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -95,6 +95,7 @@ import net.minecraft.util.Mth; @@ -22,7 +22,7 @@ index 552d64e47f17d916a4e374b877d12ff7a583a9e7..a59ac2011b85ff9b1185a14eca4d2796 import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; -@@ -1475,7 +1476,7 @@ public class ServerPlayer extends Player { +@@ -1473,7 +1474,7 @@ public class ServerPlayer extends Player { @Override public boolean isInvulnerableTo(DamageSource damageSource) { diff --git a/patches/server/0647-Add-PlayerSetSpawnEvent.patch b/patches/server/0647-Add-PlayerSetSpawnEvent.patch index dc78ab51dc..61e9279b6b 100644 --- a/patches/server/0647-Add-PlayerSetSpawnEvent.patch +++ b/patches/server/0647-Add-PlayerSetSpawnEvent.patch @@ -49,10 +49,10 @@ index a2d0699e8427b2262a2396495111125eccafbb66..d797637f61bdf8a424f56fbb48e28b7c } } diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index a59ac2011b85ff9b1185a14eca4d2796b2666df1..c2b535c5b1d05d70a16e15283ff5c9e45c59d8e3 100644 +index b8f60c2deb7e46f654d0169751c793b3b3408748..39515b81fd753c8c6d50d1efa4c5792f9f0abda9 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1352,7 +1352,7 @@ public class ServerPlayer extends Player { +@@ -1350,7 +1350,7 @@ public class ServerPlayer extends Player { } else if (this.bedBlocked(blockposition, enumdirection)) { return Either.left(Player.BedSleepingProblem.OBSTRUCTED); } else { @@ -61,7 +61,7 @@ index a59ac2011b85ff9b1185a14eca4d2796b2666df1..c2b535c5b1d05d70a16e15283ff5c9e4 if (this.level().isDay()) { return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_NOW); } else { -@@ -2202,44 +2202,50 @@ public class ServerPlayer extends Player { +@@ -2200,44 +2200,50 @@ public class ServerPlayer extends Player { return this.respawnForced; } @@ -145,7 +145,7 @@ index a59ac2011b85ff9b1185a14eca4d2796b2666df1..c2b535c5b1d05d70a16e15283ff5c9e4 } else { this.respawnPosition = null; this.respawnDimension = Level.OVERWORLD; -@@ -2247,6 +2253,7 @@ public class ServerPlayer extends Player { +@@ -2245,6 +2251,7 @@ public class ServerPlayer extends Player { this.respawnForced = false; } diff --git a/patches/server/0674-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch b/patches/server/0674-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch index d728967204..5a6a16df7b 100644 --- a/patches/server/0674-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch +++ b/patches/server/0674-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch @@ -28,10 +28,10 @@ index a11bd2d9f5b2fb248cc322ea61886050fa2e73a9..80f2a1204bee79daf0cae9310ce8d552 } // Spigot End diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index c2b535c5b1d05d70a16e15283ff5c9e45c59d8e3..de9248f622834dc6df4af7016f0ddab864ed0264 100644 +index 39515b81fd753c8c6d50d1efa4c5792f9f0abda9..5f0ff1b2466469eff0bf78042b9cdd2fcf747dc7 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1618,6 +1618,18 @@ public class ServerPlayer extends Player { +@@ -1616,6 +1616,18 @@ public class ServerPlayer extends Player { this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); this.doCloseContainer(); } diff --git a/patches/server/0677-Add-paper-mobcaps-and-paper-playermobcaps.patch b/patches/server/0677-Add-paper-mobcaps-and-paper-playermobcaps.patch index 23f5ec3c91..c00a648173 100644 --- a/patches/server/0677-Add-paper-mobcaps-and-paper-playermobcaps.patch +++ b/patches/server/0677-Add-paper-mobcaps-and-paper-playermobcaps.patch @@ -257,10 +257,10 @@ index 0000000000000000000000000000000000000000..d3b39d88a72ca25057fd8574d32f28db + } +} diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 7983f9a21251cec4857529ad1180a62668ea5037..e1c4cf4b60b810837526c888ec82acbfc8fe540c 100644 +index 9df761f5cf043e8d2dffa711c20ab32fe2992331..d08c7b0b52065980f1f13c5533ff6355028322db 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -192,6 +192,16 @@ public final class NaturalSpawner { +@@ -190,6 +190,16 @@ public final class NaturalSpawner { world.getProfiler().pop(); } diff --git a/patches/server/0689-Execute-chunk-tasks-mid-tick.patch b/patches/server/0689-Execute-chunk-tasks-mid-tick.patch index f7213f0549..f4272a0512 100644 --- a/patches/server/0689-Execute-chunk-tasks-mid-tick.patch +++ b/patches/server/0689-Execute-chunk-tasks-mid-tick.patch @@ -106,7 +106,7 @@ index 7e4f3b7a5790292b3b8439fb4696d9d6cfec0167..6c02d5233c782b0e5049afdf84250687 + // Paper end - execute chunk tasks mid tick } diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 7b5851cd4f5a91bf7609464d7d6c4bb5a3da14bf..50da1f746edeff63f723776b8e30b041c8de0138 100644 +index 7d7dc8246f44d6ffd46b05f917ffbdb9cc24372b..dbedc73039f53ddf042999524eec62f032a20d5d 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -571,6 +571,7 @@ public class ServerChunkCache extends ChunkSource { diff --git a/patches/server/0690-Oprimise-map-impl-for-tracked-players.patch b/patches/server/0690-Oprimise-map-impl-for-tracked-players.patch index 242ae5cd96..63b0b6da7a 100644 --- a/patches/server/0690-Oprimise-map-impl-for-tracked-players.patch +++ b/patches/server/0690-Oprimise-map-impl-for-tracked-players.patch @@ -7,10 +7,10 @@ Reference2BooleanOpenHashMap is going to have better lookups than HashMap. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 463a82f08b7848a4b8a4eb89b201a6e8424c6831..93a2dfca4484d1bdfa00dfb2cf8d94314123aeb8 100644 +index c10ef2e6af3e6c52eeb8c96cdbf5352d9dd25dd2..c16e84ee54bf2a65d8bd41f8156b7a4edee5e941 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1210,7 +1210,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1204,7 +1204,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; diff --git a/patches/server/0704-Ensure-valid-vehicle-status.patch b/patches/server/0704-Ensure-valid-vehicle-status.patch index 568197c14b..025bf9caac 100644 --- a/patches/server/0704-Ensure-valid-vehicle-status.patch +++ b/patches/server/0704-Ensure-valid-vehicle-status.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Ensure valid vehicle status diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index de9248f622834dc6df4af7016f0ddab864ed0264..e498d339830d8fa6dcf0eeee4a27b6295c84ee2e 100644 +index 5f0ff1b2466469eff0bf78042b9cdd2fcf747dc7..981a2b55a58f85f551991945adc7a202b4a4ec72 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -570,7 +570,7 @@ public class ServerPlayer extends Player { +@@ -568,7 +568,7 @@ public class ServerPlayer extends Player { } } diff --git a/patches/server/0728-Collision-optimisations.patch b/patches/server/0728-Collision-optimisations.patch index c35df096fd..1420bc276b 100644 --- a/patches/server/0728-Collision-optimisations.patch +++ b/patches/server/0728-Collision-optimisations.patch @@ -2204,10 +2204,10 @@ index d0a8092bf57a29ab7c00ec0ddf52a9fdb2a33267..392406722b0a040c1d41fdc1154d75d3 private Direction(int id, int idOpposite, int idHorizontal, String name, Direction.AxisDirection direction, Direction.Axis axis, Vec3i vector) { this.data3d = id; diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index e498d339830d8fa6dcf0eeee4a27b6295c84ee2e..6a33fbfb4f7dbe4a54044ccc408e196e537d96fa 100644 +index 981a2b55a58f85f551991945adc7a202b4a4ec72..24deceb51bc2892231dcd1745082d7f766eed4b5 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -463,7 +463,7 @@ public class ServerPlayer extends Player { +@@ -461,7 +461,7 @@ public class ServerPlayer extends Player { if (blockposition1 != null) { this.moveTo(blockposition1, 0.0F, 0.0F); @@ -2216,7 +2216,7 @@ index e498d339830d8fa6dcf0eeee4a27b6295c84ee2e..6a33fbfb4f7dbe4a54044ccc408e196e break; } } -@@ -471,7 +471,7 @@ public class ServerPlayer extends Player { +@@ -469,7 +469,7 @@ public class ServerPlayer extends Player { } else { this.moveTo(blockposition, 0.0F, 0.0F); diff --git a/patches/server/0790-fix-player-loottables-running-when-mob-loot-gamerule.patch b/patches/server/0790-fix-player-loottables-running-when-mob-loot-gamerule.patch index 77b8a5780b..ec558775cf 100644 --- a/patches/server/0790-fix-player-loottables-running-when-mob-loot-gamerule.patch +++ b/patches/server/0790-fix-player-loottables-running-when-mob-loot-gamerule.patch @@ -5,10 +5,10 @@ Subject: [PATCH] fix player loottables running when mob loot gamerule is false diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 6a33fbfb4f7dbe4a54044ccc408e196e537d96fa..6a9e1b3fd1d7d1964835c78808f5c71ba8a82af7 100644 +index 24deceb51bc2892231dcd1745082d7f766eed4b5..d865907d4c5d1428d85e4bb63eb45529802d0c0d 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -918,12 +918,14 @@ public class ServerPlayer extends Player { +@@ -916,12 +916,14 @@ public class ServerPlayer extends Player { } } } diff --git a/patches/server/0822-Add-option-for-strict-advancement-dimension-checks.patch b/patches/server/0822-Add-option-for-strict-advancement-dimension-checks.patch index cbf944bc69..0f81bdba33 100644 --- a/patches/server/0822-Add-option-for-strict-advancement-dimension-checks.patch +++ b/patches/server/0822-Add-option-for-strict-advancement-dimension-checks.patch @@ -24,10 +24,10 @@ index d4a673a9fb604876c554f955ed13ad31a2adb217..e75b3df4db9cb618aef4837acb8cde92 } else { BlockPos blockPos = BlockPos.containing(x, y, z); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 6a9e1b3fd1d7d1964835c78808f5c71ba8a82af7..b3fa0a7e75a7f367ab491ffad22facb985d8085a 100644 +index d865907d4c5d1428d85e4bb63eb45529802d0c0d..fc70531c97ce292a9964512008278bcf9377feb6 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1317,6 +1317,12 @@ public class ServerPlayer extends Player { +@@ -1315,6 +1315,12 @@ public class ServerPlayer extends Player { ResourceKey maindimensionkey = CraftDimensionUtil.getMainDimensionKey(origin); ResourceKey maindimensionkey1 = CraftDimensionUtil.getMainDimensionKey(this.level()); diff --git a/patches/server/0860-Fix-a-bunch-of-vanilla-bugs.patch b/patches/server/0860-Fix-a-bunch-of-vanilla-bugs.patch index 8794f7518f..49a728f77e 100644 --- a/patches/server/0860-Fix-a-bunch-of-vanilla-bugs.patch +++ b/patches/server/0860-Fix-a-bunch-of-vanilla-bugs.patch @@ -111,10 +111,10 @@ index 6cd6d69a20e95e344fc18ab67dc300824537a59b..2e2a7c2cf3081187da817479a9da3eb1 } } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 93a2dfca4484d1bdfa00dfb2cf8d94314123aeb8..8cda789284a30dab012bfda8f97409bdde861c7e 100644 +index c16e84ee54bf2a65d8bd41f8156b7a4edee5e941..d7dcffde09e099ee2554ec201f553ee079f12274 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -825,7 +825,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -819,7 +819,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } // Paper end diff --git a/patches/server/0865-Add-PlayerInventorySlotChangeEvent.patch b/patches/server/0865-Add-PlayerInventorySlotChangeEvent.patch index 3599cf788f..7d9ac76188 100644 --- a/patches/server/0865-Add-PlayerInventorySlotChangeEvent.patch +++ b/patches/server/0865-Add-PlayerInventorySlotChangeEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add PlayerInventorySlotChangeEvent diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index b3fa0a7e75a7f367ab491ffad22facb985d8085a..3e37684e3fbbb01ad006f2ae7f1e38ed63acc2b8 100644 +index fc70531c97ce292a9964512008278bcf9377feb6..d882295abf739967fa5f8621945a5dcd0b730ee4 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -372,6 +372,25 @@ public class ServerPlayer extends Player { +@@ -371,6 +371,25 @@ public class ServerPlayer extends Player { } } diff --git a/patches/server/0892-Sync-offhand-slot-in-menus.patch b/patches/server/0892-Sync-offhand-slot-in-menus.patch index 055281b83d..e571b4eb7d 100644 --- a/patches/server/0892-Sync-offhand-slot-in-menus.patch +++ b/patches/server/0892-Sync-offhand-slot-in-menus.patch @@ -8,10 +8,10 @@ offhand slot isn't sent. This is not correct because you *can* put stuff into th by pressing the offhand swap item diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 3e37684e3fbbb01ad006f2ae7f1e38ed63acc2b8..215786e3991942ad5773306a68519946372e94d4 100644 +index d882295abf739967fa5f8621945a5dcd0b730ee4..1021872e3ee489bc38e64bdea8bbe53284c87183 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -341,6 +341,13 @@ public class ServerPlayer extends Player { +@@ -340,6 +340,13 @@ public class ServerPlayer extends Player { } diff --git a/patches/server/0893-Player-Entity-Tracking-Events.patch b/patches/server/0893-Player-Entity-Tracking-Events.patch index 4427e68f26..521a6d3a6e 100644 --- a/patches/server/0893-Player-Entity-Tracking-Events.patch +++ b/patches/server/0893-Player-Entity-Tracking-Events.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Player Entity Tracking Events diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 8cda789284a30dab012bfda8f97409bdde861c7e..3d5f992c2f4a8df1cf0db1c209e2cce19e214b8a 100644 +index d7dcffde09e099ee2554ec201f553ee079f12274..160e8175fcae33c6643c2f731cc20b397ee86be9 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1282,9 +1282,18 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1276,9 +1276,18 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // CraftBukkit end if (flag) { if (this.seenBy.add(player.connection)) { diff --git a/patches/server/0993-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch b/patches/server/0993-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch index 3acfa0c78f..57566912fd 100644 --- a/patches/server/0993-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch +++ b/patches/server/0993-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 3d5f992c2f4a8df1cf0db1c209e2cce19e214b8a..91549ce95c4829f2262ccddf51372b1c858da15c 100644 +index 160e8175fcae33c6643c2f731cc20b397ee86be9..1e5ea1db14d7ae4ce25ba91e0bcd5245204af78b 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -313,8 +313,27 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -307,8 +307,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } @@ -19,15 +19,14 @@ index 3d5f992c2f4a8df1cf0db1c209e2cce19e214b8a..91549ce95c4829f2262ccddf51372b1c + return; + } + int idx = mobCategory.ordinal(); -+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet inRange = this.playerMobDistanceMap.getObjectsInRange(chunkX, chunkZ); -+ if (inRange != null) { -+ final Object[] set = inRange.getBackingSet(); -+ for (int i = 0; i < set.length; i++) { -+ if (!(set[i] instanceof ServerPlayer serverPlayer)) { -+ continue; -+ } -+ ++serverPlayer.mobBackoffCounts[idx]; -+ } ++ final com.destroystokyo.paper.util.maplist.ReferenceList inRange = ++ this.getNearbyPlayers().getPlayersByChunk(chunkX, chunkZ, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE); ++ if (inRange == null) { ++ return; ++ } ++ final Object[] backingSet = inRange.getRawData(); ++ for (int i = 0, len = inRange.size(); i < len; i++) { ++ ++((ServerPlayer)backingSet[i]).mobBackoffCounts[idx]; + } + } + // Paper end - per player mob count backoff @@ -39,11 +38,11 @@ index 3d5f992c2f4a8df1cf0db1c209e2cce19e214b8a..91549ce95c4829f2262ccddf51372b1c // 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 50da1f746edeff63f723776b8e30b041c8de0138..2ab16e10323982e193e647246bd116e31f17bad2 100644 +index dbedc73039f53ddf042999524eec62f032a20d5d..aaaf3a60696389b4e27a45de69743311c58e5829 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -532,7 +532,17 @@ public class ServerChunkCache extends ChunkSource { - if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled + 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) { - Arrays.fill(player.mobCounts, 0); @@ -62,22 +61,22 @@ index 50da1f746edeff63f723776b8e30b041c8de0138..2ab16e10323982e193e647246bd116e3 spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); } else { diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 215786e3991942ad5773306a68519946372e94d4..50dcbace6fea4669af904000df08ee6d13df77f8 100644 +index 1021872e3ee489bc38e64bdea8bbe53284c87183..d2729c381632bc32ca915bf60614847db07e488b 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -252,6 +252,7 @@ public class ServerPlayer extends Player { - // Paper start - mob spawning rework +@@ -253,6 +253,7 @@ public class ServerPlayer extends Player { public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length; public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper + // Paper end - mob spawning rework + public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff - public final com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet cachedSingleMobDistanceMap; - // Paper end + // CraftBukkit start + public String displayName; diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index e1c4cf4b60b810837526c888ec82acbfc8fe540c..64656c384863a6430e933e506d965ee628f08669 100644 +index d08c7b0b52065980f1f13c5533ff6355028322db..3cdddda9c0618e95288b81b975d499c8dd30c05f 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -279,6 +279,11 @@ public final class NaturalSpawner { +@@ -277,6 +277,11 @@ public final class NaturalSpawner { // Paper start PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2); diff --git a/patches/server/0998-Configurable-entity-tracking-range-by-Y-coordinate.patch b/patches/server/0998-Configurable-entity-tracking-range-by-Y-coordinate.patch index a69e34ff7d..4b8d87f7b0 100644 --- a/patches/server/0998-Configurable-entity-tracking-range-by-Y-coordinate.patch +++ b/patches/server/0998-Configurable-entity-tracking-range-by-Y-coordinate.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Configurable entity tracking range by Y coordinate Options to configure entity tracking by Y coordinate, also for each entity category. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 91549ce95c4829f2262ccddf51372b1c858da15c..dcf12bea1759d851d663896938ea101303ab63a5 100644 +index 1e5ea1db14d7ae4ce25ba91e0bcd5245204af78b..edbd5d4e2dcf5112148dc3d7a9173bb4d0a5d445 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1293,6 +1293,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1286,6 +1286,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z; double d2 = d0 * d0; boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); diff --git a/patches/server/1013-Add-titleOverride-to-InventoryOpenEvent.patch b/patches/server/1013-Add-titleOverride-to-InventoryOpenEvent.patch index 1faf111c41..ba1a92cd25 100644 --- a/patches/server/1013-Add-titleOverride-to-InventoryOpenEvent.patch +++ b/patches/server/1013-Add-titleOverride-to-InventoryOpenEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add titleOverride to InventoryOpenEvent diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 50dcbace6fea4669af904000df08ee6d13df77f8..6a11b262a6f1cd5eba96471666098c82978027ff 100644 +index d2729c381632bc32ca915bf60614847db07e488b..261d0cc1812831fa99358ec1c6438a4e89629bde 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1561,12 +1561,17 @@ public class ServerPlayer extends Player { +@@ -1559,12 +1559,17 @@ public class ServerPlayer extends Player { this.nextContainerCounter(); AbstractContainerMenu container = factory.createMenu(this.containerCounter, this.getInventory(), this); @@ -27,7 +27,7 @@ index 50dcbace6fea4669af904000df08ee6d13df77f8..6a11b262a6f1cd5eba96471666098c82 if (container == null && !cancelled) { // Let pre-cancelled events fall through // SPIGOT-5263 - close chest if cancelled if (factory instanceof Container) { -@@ -1588,7 +1593,7 @@ public class ServerPlayer extends Player { +@@ -1586,7 +1591,7 @@ public class ServerPlayer extends Player { } else { // CraftBukkit start this.containerMenu = container; diff --git a/patches/server/1029-Optimise-chunk-tick-iteration.patch b/patches/server/1029-Optimise-chunk-tick-iteration.patch new file mode 100644 index 0000000000..c172557be5 --- /dev/null +++ b/patches/server/1029-Optimise-chunk-tick-iteration.patch @@ -0,0 +1,374 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +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 60c7680d678f9dc6a6a3109b1f6af8150ccd9100..d05195358a00e498e0184ab105ebb3dc7a39e15a 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 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 edbd5d4e2dcf5112148dc3d7a9173bb4d0a5d445..0c2617574e21037d94ac56ad08b490f9bca5c5af 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -162,6 +162,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Note: players need to be explicitly added to distance maps before they can be updated + this.nearbyPlayers.removePlayer(player); + this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader ++ this.playerMobSpawnMap.remove(player); // Paper - optimise chunk tick iteration + } + + void updateMaps(ServerPlayer player) { +@@ -203,6 +204,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 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 mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { + super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); +@@ -330,7 +335,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> 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 aaaf3a60696389b4e27a45de69743311c58e5829..17b6925b46f8386dcfc561483693de516465ec12 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -553,52 +553,114 @@ public class ServerChunkCache extends ChunkSource { + + this.lastSpawnState = spawnercreature_d; + gameprofilerfiller.popPush("filteringLoadedChunks"); +- List list = Lists.newArrayListWithCapacity(l); +- Iterator iterator = this.chunkMap.getChunks().iterator(); ++ // Paper - optimise chunk tick iteration ++ // Paper - optimise chunk tick iteration + 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 + + gameprofilerfiller.popPush("spawnAndTick"); + boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit + +- Collections.shuffle(list); +- // 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 end +- Iterator iterator1 = list.iterator(); ++ // 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 - optimise chunk tick iteration + + int chunksTicked = 0; // Paper ++ // Paper start - optimise chunk tick iteration ++ io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = this.chunkMap.getNearbyPlayers(); // Paper - optimise chunk tick iteration ++ Iterator iterator1; ++ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ iterator1 = this.tickingChunks.iterator(); ++ } else { ++ iterator1 = this.tickingChunks.unsafeIterator(); ++ List shuffled = Lists.newArrayListWithCapacity(this.tickingChunks.size()); ++ while (iterator1.hasNext()) { ++ shuffled.add(iterator1.next()); ++ } ++ Collections.shuffle(shuffled); ++ iterator1 = shuffled.iterator(); ++ } ++ try { ++ // Paper end - optimise chunk tick iteration + while (iterator1.hasNext()) { +- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next(); +- LevelChunk chunk1 = chunkproviderserver_a.chunk; ++ LevelChunk chunk1 = iterator1.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 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; ++ } ++ } ++ // Paper end - optimise chunk tick iteration ++ if (tick && chunk1.chunkStatus.isOrAfter(net.minecraft.server.level.FullChunkStatus.ENTITY_TICKING)) { // Paper - optimise chunk tick iteration + chunk1.incrementInhabitedTime(j); +- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot ++ if (spawn && flag2 && (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, k); + if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper + } + } + } ++ // 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) { +@@ -608,11 +670,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 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(); + this.chunkMap.tick(); +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 261d0cc1812831fa99358ec1c6438a4e89629bde..d4aec99cac3f83d764e21946cc904c00e084704e 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -317,6 +317,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);