mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-28 23:38:25 +01:00
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.
This commit is contained in:
parent
0b16cce6e6
commit
a20286c5a8
5 changed files with 927 additions and 591 deletions
|
@ -19,15 +19,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ return;
|
||||
+ }
|
||||
+ int idx = mobCategory.ordinal();
|
||||
+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> 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<ServerPlayer> 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
|
||||
|
@ -43,7 +42,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ 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);
|
||||
|
@ -66,13 +65,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
|
||||
// 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
|
||||
// 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<ServerPlayer> 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
|
|
|
@ -5467,6 +5467,453 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+}
|
||||
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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
|
||||
+ private final Long2ReferenceOpenHashMap<TrackedChunk> 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<ServerPlayer> 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<ServerPlayer> 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<ServerPlayer> 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<ServerPlayer> 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<ServerPlayer>[] 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<ServerPlayer> 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<ServerPlayer> 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<ServerPlayer> 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<ServerPlayer> {
|
||||
+
|
||||
+ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/player/SingleUserAreaMap.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper.util.player;
|
||||
+
|
||||
+import io.papermc.paper.util.IntegerUtil;
|
||||
+
|
||||
+public abstract class SingleUserAreaMap<T> {
|
||||
+
|
||||
+ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/Util.java
|
||||
|
@ -5589,16 +6036,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -5934,16 +6380,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ 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,6 +6426,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ 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<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
||||
|
@ -5987,10 +6439,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // 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() {
|
||||
return this.generator;
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
}
|
||||
}
|
||||
|
|
374
patches/server/Optimise-chunk-tick-iteration.patch
Normal file
374
patches/server/Optimise-chunk-tick-iteration.patch
Normal file
|
@ -0,0 +1,374 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 23 Sep 2023 21:36:36 -0700
|
||||
Subject: [PATCH] Optimise chunk tick iteration
|
||||
|
||||
When per-player mob spawning is enabled we do not need to randomly
|
||||
shuffle the chunk list. Additionally, we can use the NearbyPlayers
|
||||
class to quickly retrieve nearby players instead of possible
|
||||
searching all players on the server.
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
|
||||
@@ -0,0 +0,0 @@ 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();
|
||||
@@ -0,0 +0,0 @@ public final class NearbyPlayers {
|
||||
private static final int GENERAL_AREA_VIEW_DISTANCE = 33;
|
||||
private static final int GENERAL_SMALL_VIEW_DISTANCE = 10;
|
||||
private static final int GENERAL_REALLY_SMALL_VIEW_DISTANCE = 3;
|
||||
+ private static final int SPAWN_RANGE_VIEW_DISTANCE = net.minecraft.server.level.DistanceManager.MOB_SPAWN_RANGE; // Paper - optimise chunk iteration
|
||||
|
||||
public static final int GENERAL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_AREA_VIEW_DISTANCE << 4);
|
||||
public static final int GENERAL_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_SMALL_VIEW_DISTANCE << 4);
|
||||
public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4);
|
||||
+ public static final int SPAWN_RANGE_VIEW_DISTANCE_BLOCKS = (SPAWN_RANGE_VIEW_DISTANCE << 4); // Paper - optimise chunk iteration
|
||||
|
||||
private final ServerLevel world;
|
||||
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
|
||||
@@ -0,0 +0,0 @@ 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();
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ 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) {
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -0,0 +0,0 @@ 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) {
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
}
|
||||
public final io.papermc.paper.util.player.NearbyPlayers nearbyPlayers;
|
||||
// Paper end
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ public final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>();
|
||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
|
||||
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
||||
super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
||||
private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
|
||||
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
|
||||
// Paper - rewrite chunk system
|
||||
- private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
|
||||
+ public static final int MOB_SPAWN_RANGE = 8; //private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - optimise chunk tick iteration
|
||||
// Paper - rewrite chunk system
|
||||
private final ChunkMap chunkMap; // Paper
|
||||
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
}
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
}
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
this.lastSpawnState = spawnercreature_d;
|
||||
gameprofilerfiller.popPush("filteringLoadedChunks");
|
||||
- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(l);
|
||||
- Iterator iterator = this.chunkMap.getChunks().iterator();
|
||||
+ // Paper - 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<LevelChunk> iterator1;
|
||||
+ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ iterator1 = this.tickingChunks.iterator();
|
||||
+ } else {
|
||||
+ iterator1 = this.tickingChunks.unsafeIterator();
|
||||
+ List<LevelChunk> 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<ServerPlayer> playersNearby
|
||||
+ = nearbyPlayers.getPlayers(chunkcoordintpair, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.SPAWN_RANGE);
|
||||
+ if (playersNearby == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ Object[] rawData = playersNearby.getRawData();
|
||||
+ boolean spawn = false;
|
||||
+ boolean tick = false;
|
||||
+ for (int itr = 0, len = playersNearby.size(); itr < len; ++itr) {
|
||||
+ ServerPlayer player = (ServerPlayer)rawData[itr];
|
||||
+ if (player.isSpectator()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ double distance = ChunkMap.euclideanDistanceSquared(chunkcoordintpair, player);
|
||||
+ spawn |= player.lastEntitySpawnRadiusSquared >= distance;
|
||||
+ tick |= ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) * ((double)io.papermc.paper.util.player.NearbyPlayers.SPAWN_RANGE_VIEW_DISTANCE_BLOCKS) >= distance;
|
||||
+ if (spawn & tick) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ // 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) {
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
}
|
||||
|
||||
gameprofilerfiller.popPush("broadcast");
|
||||
- list.forEach((chunkproviderserver_a1) -> {
|
||||
+ // Paper - optimise chunk tick iteration
|
||||
this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
|
||||
- chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
|
||||
+ // Paper start - optimise chunk tick iteration
|
||||
+ if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
|
||||
+ it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
|
||||
+ this.chunkMap.needsChangeBroadcasting.clear();
|
||||
+ for (ChunkHolder holder : copy) {
|
||||
+ holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
|
||||
+ if (holder.needsBroadcastChanges()) {
|
||||
+ // I DON'T want to KNOW what DUMB plugins might be doing.
|
||||
+ this.chunkMap.needsChangeBroadcasting.add(holder);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimise chunk tick iteration
|
||||
this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
||||
- });
|
||||
+ // Paper - optimise chunk tick iteration
|
||||
gameprofilerfiller.pop();
|
||||
gameprofilerfiller.pop();
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -0,0 +0,0 @@ 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);
|
|
@ -2300,6 +2300,39 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
public static boolean hasAnyChunkHolders(final ServerLevel level) {
|
||||
@@ -0,0 +0,0 @@ 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) {
|
||||
@@ -0,0 +0,0 @@ public final class ChunkSystem {
|
||||
}
|
||||
|
||||
public static int getSendViewDistance(final ServerPlayer player) {
|
||||
|
@ -2337,12 +2370,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+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..00000000000000000000000000000000
|
|||
+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..00000000000000000000000000000000
|
|||
+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..00000000000000000000000000000000
|
|||
+ parameter.sendUnloadChunk(chunkX, chunkZ);
|
||||
+ }
|
||||
+ };
|
||||
+ private final SingleUserAreaMap<PlayerChunkLoaderData> loadTicketCleanup = new SingleUserAreaMap<>(this) {
|
||||
+ private final SingleUserAreaMap<io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.PlayerChunkLoaderData> 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..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static abstract class SingleUserAreaMap<T> {
|
||||
+
|
||||
+ 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<E> {
|
||||
+
|
||||
+ private final SRSWLinkedQueue<E> queue = new SRSWLinkedQueue<>();
|
||||
|
@ -17773,23 +17574,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
|
||||
// Paper start - distance maps
|
||||
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> 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());
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
this.nearbyPlayers.removePlayer(player);
|
||||
+ this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader
|
||||
}
|
||||
|
||||
void updateMaps(ServerPlayer player) {
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
|
@ -17801,6 +17605,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
- 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<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
||||
|
@ -21504,6 +21309,14 @@ diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src
|
|||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -0,0 +0,0 @@ public class LevelChunk extends ChunkAccess {
|
||||
private final Int2ObjectMap<GameEventListenerRegistry> gameEventListenerRegistrySections;
|
||||
private final LevelChunkTicks<Block> blockTicks;
|
||||
private final LevelChunkTicks<Fluid> 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);
|
||||
@@ -0,0 +0,0 @@ public class LevelChunk extends ChunkAccess {
|
||||
|
||||
}
|
||||
|
|
|
@ -4,308 +4,10 @@ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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<E> {
|
||||
+
|
||||
+ // we really want to avoid that equals() check as much as possible...
|
||||
+ protected final Object2ObjectOpenHashMap<PooledObjectLinkedOpenHashSet<E>, PooledObjectLinkedOpenHashSet<E>> mapPool = new Object2ObjectOpenHashMap<>(64, 0.25f);
|
||||
+
|
||||
+ protected void decrementReferenceCount(final PooledObjectLinkedOpenHashSet<E> 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<E> findMapWith(final PooledObjectLinkedOpenHashSet<E> current, final E object) {
|
||||
+ final PooledObjectLinkedOpenHashSet<E> 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<E> 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<E> findMapWithout(final PooledObjectLinkedOpenHashSet<E> current, final E object) {
|
||||
+ if (current.set.size() == 1) {
|
||||
+ decrementReferenceCount(current);
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final PooledObjectLinkedOpenHashSet<E> 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<E> 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<E> implements Iterable<E> {
|
||||
+
|
||||
+ private static final WeakReference NULL_REFERENCE = new WeakReference(null);
|
||||
+
|
||||
+ final ObjectLinkedOpenHashSet<E> set;
|
||||
+ int referenceCount; // -1 if special
|
||||
+ int hash; // optimize hashcode
|
||||
+
|
||||
+ // add cache
|
||||
+ WeakReference<E> lastAddObject = NULL_REFERENCE;
|
||||
+ WeakReference<PooledObjectLinkedOpenHashSet<E>> lastAddMap = NULL_REFERENCE;
|
||||
+
|
||||
+ // remove cache
|
||||
+ WeakReference<E> lastRemoveObject = NULL_REFERENCE;
|
||||
+ WeakReference<PooledObjectLinkedOpenHashSet<E>> 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<E> 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<E> getAddCache(final E element) {
|
||||
+ final E currentAdd = this.lastAddObject.get();
|
||||
+
|
||||
+ if (currentAdd == null || !(currentAdd == element || currentAdd.equals(element))) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final PooledObjectLinkedOpenHashSet<E> 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<E> getRemoveCache(final E element) {
|
||||
+ final E currentRemove = this.lastRemoveObject.get();
|
||||
+
|
||||
+ if (currentRemove == null || !(currentRemove == element || currentRemove.equals(element))) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final PooledObjectLinkedOpenHashSet<E> 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<E> map) {
|
||||
+ this.lastAddObject = new WeakReference<>(element);
|
||||
+ this.lastAddMap = new WeakReference<>(map);
|
||||
+ }
|
||||
+
|
||||
+ public void updateRemoveCache(final E element, final PooledObjectLinkedOpenHashSet<E> 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<E> 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
private final Long2LongMap chunkSaveCooldowns;
|
||||
private final Queue<Runnable> unloadQueue;
|
||||
private int serverViewDistance;
|
||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
|
||||
|
||||
// Paper - rewrite chunk system
|
||||
|
||||
@@ -0,0 +0,0 @@ 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) {
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
@@ -0,0 +0,0 @@ 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() {
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
});
|
||||
}
|
||||
|
@ -317,16 +19,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ int index = entity.getType().getCategory().ordinal();
|
||||
+
|
||||
+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> inRange = this.playerMobDistanceMap.getObjectsInRange(entity.chunkPosition());
|
||||
+ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> 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];
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
@ -349,14 +49,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
- 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
|
||||
|
@ -373,19 +73,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ // 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<ServerPlayer> cachedSingleMobDistanceMap;
|
||||
+ // Paper end
|
||||
+ // Paper end - mob spawning rework
|
||||
|
||||
// CraftBukkit start
|
||||
public String displayName;
|
||||
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
|
@ -433,14 +124,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ int minDiff = Integer.MAX_VALUE;
|
||||
+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> inRange = world.getChunkSource().chunkMap.playerMobDistanceMap.getObjectsInRange(chunk.getPos());
|
||||
+ final com.destroystokyo.paper.util.maplist.ReferenceList<net.minecraft.server.level.ServerPlayer> 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;
|
||||
|
|
Loading…
Reference in a new issue