From bbf85f323fff7516064c06f565c6348cf284dba0 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 17 Jul 2024 07:25:48 -0700 Subject: [PATCH] Optimise chunk tick iteration The basic problem with the chunk tick iteration is that Vanilla will iterate over all chunk holders to find ticking chunks. However, there are usually many more chunk holders than ticking chunks. We can eliminate the cost of finding the ticking chunks by maintaining our own list of ticking chunks. --- .../moonrise/common/list/ReferenceList.java | 23 ++- .../moonrise/common/misc/NearbyPlayers.java | 6 +- .../common/misc/PositionCountingAreaMap.java | 94 ++++++++++++ .../common/misc/SingleUserAreaMap.java | 18 ++- .../mixin/chunk_system/ChunkHolderMixin.java | 2 +- .../mixin/chunk_system/LevelChunkMixin.java | 14 ++ .../mixin/chunk_system/ServerLevelMixin.java | 28 ++++ .../chunk_tick_iteration/ChunkMapMixin.java | 141 ++++++++++++++++++ .../DistanceManagerMixin.java | 113 ++++++++++++++ .../ServerChunkCacheMixin.java | 122 +++++++++++++++ .../patches/chunk_system/ChunkSystem.java | 23 ++- .../level/ChunkSystemServerLevel.java | 8 + .../level/chunk/ChunkSystemLevelChunk.java | 6 + .../entity/server/ServerEntityLookup.java | 4 +- .../scheduling/task/ChunkFullTask.java | 5 +- .../ChunkTickConstants.java | 7 + .../ChunkTickDistanceManager.java | 16 ++ src/main/resources/moonrise.accesswidener | 8 +- src/main/resources/moonrise.mixins.json | 3 + 19 files changed, 620 insertions(+), 21 deletions(-) create mode 100644 src/main/java/ca/spottedleaf/moonrise/common/misc/PositionCountingAreaMap.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ChunkMapMixin.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/DistanceManagerMixin.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ServerChunkCacheMixin.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickDistanceManager.java diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java index 93e8c81..2e876b9 100644 --- a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java +++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java @@ -7,23 +7,30 @@ import java.util.NoSuchElementException; public final class ReferenceList implements Iterable { - private final Reference2IntOpenHashMap referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f); - { - this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE); - } - private static final Object[] EMPTY_LIST = new Object[0]; + private final Reference2IntOpenHashMap referenceToIndex; private E[] references; private int count; public ReferenceList() { - this((E[])EMPTY_LIST, 0); + this((E[])EMPTY_LIST); } - public ReferenceList(final E[] array, final int count) { - this.references = array; + public ReferenceList(final E[] referenceArray) { + this.references = referenceArray; + this.referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f); + this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE); + } + + private ReferenceList(final E[] references, final int count, final Reference2IntOpenHashMap referenceToIndex) { + this.references = references; this.count = count; + this.referenceToIndex = referenceToIndex; + } + + public ReferenceList copy() { + return new ReferenceList<>(this.references.clone(), this.count, this.referenceToIndex.clone()); } public int size() { diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java index fdde1f5..3d5be35 100644 --- a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java @@ -4,6 +4,7 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.util.CoordinateUtils; import ca.spottedleaf.moonrise.common.util.MoonriseConstants; import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystem; +import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants; import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.minecraft.core.BlockPos; @@ -19,7 +20,7 @@ public final class NearbyPlayers { GENERAL_REALLY_SMALL, TICK_VIEW_DISTANCE, VIEW_DISTANCE, - SPAWN_RANGE, + SPAWN_RANGE, // Moonrise - chunk tick iteration } private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values(); @@ -82,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, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration } public TrackedChunk getChunk(final ChunkPos pos) { @@ -143,7 +145,7 @@ public final class NearbyPlayers { final ReferenceList list = this.players[idx]; if (list == null) { ++this.nonEmptyLists; - (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY, 0)).add(player); + (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)).add(player); return; } diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/PositionCountingAreaMap.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/PositionCountingAreaMap.java new file mode 100644 index 0000000..efefd94 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/PositionCountingAreaMap.java @@ -0,0 +1,94 @@ +package ca.spottedleaf.moonrise.common.misc; + +import ca.spottedleaf.concurrentutil.util.IntPairUtil; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceSet; + +public final class PositionCountingAreaMap { + + private final Reference2ReferenceOpenHashMap counters = new Reference2ReferenceOpenHashMap<>(); + private final Long2IntOpenHashMap positions = new Long2IntOpenHashMap(); + + public ReferenceSet getObjects() { + return this.counters.keySet(); + } + + public int getTotalPositions() { + return this.positions.size(); + } + + public boolean hasObjectsNear(final int toX, final int toZ) { + return this.positions.containsKey(IntPairUtil.key(toX, toZ)); + } + + public int getObjectsNear(final int toX, final int toZ) { + return this.positions.get(IntPairUtil.key(toX, toZ)); + } + + public boolean add(final T parameter, final int toX, final int toZ, final int distance) { + final PositionCounter existing = this.counters.get(parameter); + if (existing != null) { + return false; + } + + final PositionCounter counter = new PositionCounter(parameter); + + this.counters.put(parameter, counter); + + return counter.add(toX, toZ, distance); + } + + public boolean addOrUpdate(final T parameter, final int toX, final int toZ, final int distance) { + final PositionCounter existing = this.counters.get(parameter); + if (existing != null) { + return existing.update(toX, toZ, distance); + } + + final PositionCounter counter = new PositionCounter(parameter); + + this.counters.put(parameter, counter); + + return counter.add(toX, toZ, distance); + } + + public boolean remove(final T parameter) { + final PositionCounter counter = this.counters.remove(parameter); + if (counter == null) { + return false; + } + + counter.remove(); + + return true; + } + + public boolean update(final T parameter, final int toX, final int toZ, final int distance) { + final PositionCounter counter = this.counters.get(parameter); + if (counter == null) { + return false; + } + + return counter.update(toX, toZ, distance); + } + + private final class PositionCounter extends SingleUserAreaMap { + + public PositionCounter(final T parameter) { + super(parameter); + } + + @Override + protected void addCallback(final T parameter, final int toX, final int toZ) { + PositionCountingAreaMap.this.positions.addTo(IntPairUtil.key(toX, toZ), 1); + } + + @Override + protected void removeCallback(final T parameter, final int toX, final int toZ) { + final long key = IntPairUtil.key(toX, toZ); + if (PositionCountingAreaMap.this.positions.addTo(key, -1) == 1) { + PositionCountingAreaMap.this.positions.remove(key); + } + } + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java index 61f7024..94689e0 100644 --- a/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java +++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java @@ -4,7 +4,7 @@ import ca.spottedleaf.concurrentutil.util.IntegerUtil; public abstract class SingleUserAreaMap { - private static final int NOT_SET = Integer.MIN_VALUE; + public static final int NOT_SET = Integer.MIN_VALUE; private final T parameter; private int lastChunkX = NOT_SET; @@ -15,6 +15,22 @@ public abstract class SingleUserAreaMap { this.parameter = parameter; } + public final T getParameter() { + return this.parameter; + } + + public final int getLastChunkX() { + return this.lastChunkX; + } + + public final int getLastChunkZ() { + return this.lastChunkZ; + } + + public final int getLastDistance() { + return this.distance; + } + /* math sign function except 0 returns 1 */ protected static int sign(int val) { return 1 | (val >> (Integer.SIZE - 1)); diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ChunkHolderMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ChunkHolderMixin.java index 36455dd..0c65c8c 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ChunkHolderMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ChunkHolderMixin.java @@ -64,7 +64,7 @@ public abstract class ChunkHolderMixin extends GenerationChunkHolder implements private NewChunkHolder newChunkHolder; @Unique - private final ReferenceList playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY, 0); + private final ReferenceList playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY); @Unique private ChunkMap getChunkMap() { diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelChunkMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelChunkMixin.java index 136103d..eb0f560 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelChunkMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelChunkMixin.java @@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system; import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk; import ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks; import net.minecraft.core.Registry; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelHeightAccessor; @@ -46,11 +47,24 @@ public abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystem @Unique private boolean postProcessingDone; + @Unique + private ServerChunkCache.ChunkAndHolder chunkAndHolder; + @Override public final boolean moonrise$isPostProcessingDone() { return this.postProcessingDone; } + @Override + public final ServerChunkCache.ChunkAndHolder moonrise$getChunkAndHolder() { + return this.chunkAndHolder; + } + + @Override + public final void moonrise$setChunkAndHolder(final ServerChunkCache.ChunkAndHolder holder) { + this.chunkAndHolder = holder; + } + /** * @reason Hook to set {@link #postProcessingDone} to {@code true} when post-processing completes to avoid invoking * this function many times by the player chunk loader. diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java index 5302431..4de7bfa 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java @@ -1,6 +1,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system; import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; +import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; import ca.spottedleaf.moonrise.common.util.CoordinateUtils; import ca.spottedleaf.moonrise.common.util.MoonriseCommon; @@ -114,6 +115,18 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe @Unique private final NearbyPlayers nearbyPlayers = new NearbyPlayers((ServerLevel)(Object)this); + @Unique + private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0]; + + @Unique + private final ReferenceList loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); + + @Unique + private final ReferenceList tickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); + + @Unique + private final ReferenceList entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); + /** * @reason Initialise fields / destroy entity manager state * @author Spottedleaf @@ -312,6 +325,21 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe return this.nearbyPlayers; } + @Override + public final ReferenceList moonrise$getLoadedChunks() { + return this.loadedChunks; + } + + @Override + public final ReferenceList moonrise$getTickingChunks() { + return this.tickingChunks; + } + + @Override + public final ReferenceList moonrise$getEntityTickingChunks() { + return this.entityTickingChunks; + } + /** * @reason Declare method in this class so that any invocations are virtual, and not interface. * @author Spottedleaf diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ChunkMapMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ChunkMapMixin.java new file mode 100644 index 0000000..e596720 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ChunkMapMixin.java @@ -0,0 +1,141 @@ +package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration; + +import ca.spottedleaf.moonrise.common.list.ReferenceList; +import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; +import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; +import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.ChunkPos; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Mixin(ChunkMap.class) +public abstract class ChunkMapMixin { + + @Shadow + @Final + private ChunkMap.DistanceManager distanceManager; + + @Shadow + @Final + public ServerLevel level; + + @Shadow + protected abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos); + + /** + * @reason Hook for updating the spawn tracker in distance manager. We add our own hook instead of using the + * addPlayer/removePlayer calls as it is more efficient to update the spawn tracker than to add and remove, + * as the update method will only update chunks that are different. + * @author Spottedleaf + */ + @Inject( + method = "move", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/ChunkMap;updatePlayerPos(Lnet/minecraft/server/level/ServerPlayer;)V" + ) + ) + private void updateSpawnTracker(final ServerPlayer player, final CallbackInfo ci, + @Local(ordinal = 0) final SectionPos oldPos, @Local(ordinal = 1) final SectionPos newPos, + @Local(ordinal = 0) final boolean oldIgnore, @Local(ordinal = 1) final boolean newIgnore) { + ((ChunkTickDistanceManager)this.distanceManager).moonrise$updatePlayer(player, oldPos, newPos, oldIgnore, newIgnore); + } + + /** + * @reason Add hook for spawn tracker + * @author Spottedleaf + */ + @Inject( + method = "updatePlayerStatus", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/ChunkMap$DistanceManager;addPlayer(Lnet/minecraft/core/SectionPos;Lnet/minecraft/server/level/ServerPlayer;)V" + ) + ) + private void addPlayerToSpawnTracker(final ServerPlayer player, final boolean add, final CallbackInfo ci) { + ((ChunkTickDistanceManager)this.distanceManager).moonrise$addPlayer(player, SectionPos.of(player)); + } + + /** + * @reason Remove hook for spawn tracker + * @author Spottedleaf + */ + @Inject( + method = "updatePlayerStatus", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/ChunkMap$DistanceManager;removePlayer(Lnet/minecraft/core/SectionPos;Lnet/minecraft/server/level/ServerPlayer;)V" + ) + ) + private void removePlayerFromSpawnTracker(final ServerPlayer player, final boolean add, final CallbackInfo ci) { + ((ChunkTickDistanceManager)this.distanceManager).moonrise$removePlayer(player, SectionPos.of(player)); + } + + /** + * @reason Use nearby players to avoid iterating over all online players + * @author Spottedleaf + */ + @Overwrite + public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) { + final ReferenceList players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers( + pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE + ); + if (players == null) { + return false; + } + + final ServerPlayer[] raw = players.getRawDataUnchecked(); + final int len = players.size(); + + Objects.checkFromIndexSize(0, len, raw.length); + for (int i = 0; i < len; ++i) { + if (this.playerIsCloseEnoughForSpawning(raw[i], pos)) { + return true; + } + } + + return false; + } + + /** + * @reason Use nearby players to avoid iterating over all online players + * @author Spottedleaf + */ + @Overwrite + public List getPlayersCloseForSpawning(final ChunkPos pos) { + final List ret = new ArrayList<>(); + + final ReferenceList players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers( + pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE + ); + if (players == null) { + return ret; + } + + final ServerPlayer[] raw = players.getRawDataUnchecked(); + final int len = players.size(); + + Objects.checkFromIndexSize(0, len, raw.length); + for (int i = 0; i < len; ++i) { + final ServerPlayer player = raw[i]; + if (this.playerIsCloseEnoughForSpawning(player, pos)) { + ret.add(player); + } + } + + return ret; + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/DistanceManagerMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/DistanceManagerMixin.java new file mode 100644 index 0000000..26a96fd --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/DistanceManagerMixin.java @@ -0,0 +1,113 @@ +package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration; + +import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants; +import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.DistanceManager; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; +import net.minecraft.server.level.TickingTracker; +import net.minecraft.world.level.ChunkPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(DistanceManager.class) +public abstract class DistanceManagerMixin implements ChunkTickDistanceManager { + + @Shadow + private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter; + + + @Unique + private final PositionCountingAreaMap spawnChunkTracker = new PositionCountingAreaMap<>(); + + @Override + public final void moonrise$addPlayer(final ServerPlayer player, final SectionPos pos) { + this.spawnChunkTracker.add(player, pos.x(), pos.z(), ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); + } + + @Override + public final void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos) { + this.spawnChunkTracker.remove(player); + } + + @Override + public final void moonrise$updatePlayer(final ServerPlayer player, + final SectionPos oldPos, final SectionPos newPos, + final boolean oldIgnore, final boolean newIgnore) { + if (newIgnore) { + this.spawnChunkTracker.remove(player); + } else { + this.spawnChunkTracker.addOrUpdate(player, newPos.x(), newPos.z(), ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); + } + } + + /** + * @reason Destroy natural spawning tracker field to prevent it from being used + * @author Spottedleaf + */ + @Inject( + method = "", + at = @At( + value = "RETURN" + ) + ) + private void destroyFields(final CallbackInfo ci) { + this.naturalSpawnChunkCounter = null; + } + + /** + * @reason Destroy hook to old spawn tracker + * @author Spottedleaf + */ + @Redirect( + method = "addPlayer", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker;update(JIZ)V" + ) + ) + private void skipSpawnTrackerAdd(final DistanceManager.FixedPlayerDistanceChunkTracker instance, + final long pos, final int i0, final boolean b0) {} + + /** + * @reason Destroy hook to old spawn tracker + * @author Spottedleaf + */ + @Redirect( + method = "removePlayer", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker;update(JIZ)V" + ) + ) + private void skipSpawnTrackerRemove(final DistanceManager.FixedPlayerDistanceChunkTracker instance, + final long pos, final int i0, final boolean b0) {} + + /** + * @reason Use spawnChunkTracker instead + * @author Spottedleaf + */ + @Overwrite + public int getNaturalSpawnChunkCount() { + return this.spawnChunkTracker.getTotalPositions(); + } + + /** + * @reason Use spawnChunkTracker instead + * @author Spottedleaf + */ + @Overwrite + public boolean hasPlayersNearby(final long pos) { + return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos)); + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ServerChunkCacheMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ServerChunkCacheMixin.java new file mode 100644 index 0000000..2123493 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_tick_iteration/ServerChunkCacheMixin.java @@ -0,0 +1,122 @@ +package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration; + +import ca.spottedleaf.moonrise.common.list.ReferenceList; +import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.chunk.ChunkSource; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +@Mixin(ServerChunkCache.class) +public abstract class ServerChunkCacheMixin extends ChunkSource { + + @Shadow + @Final + public ServerLevel level; + + @Unique + private ServerChunkCache.ChunkAndHolder[] iterationCopy; + + /** + * @reason Avoid creating the list, which is sized at the chunkholder count. The actual number of ticking + * chunks is always lower. The mixin below will initialise the list to non-null. + * @author Spottedleaf + */ + @Redirect( + method = "tickChunks", + at = @At( + value = "INVOKE", + target = "Lcom/google/common/collect/Lists;newArrayListWithCapacity(I)Ljava/util/ArrayList;" + ) + ) + private ArrayList avoidListCreation(final int initialArraySize) { + return null; + } + + /** + * @reason Initialise the list to contain only the ticking chunks. + * @author Spottedleaf + */ + @ModifyVariable( + method = "tickChunks", + at = @At( + value = "STORE", + opcode = Opcodes.ASTORE, + ordinal = 0 + ) + ) + private List initTickChunks(final List shouldBeNull) { + final ReferenceList tickingChunks = + ((ChunkSystemServerLevel)this.level).moonrise$getTickingChunks(); + + final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked(); + final int size = tickingChunks.size(); + + if (this.iterationCopy == null || this.iterationCopy.length < size) { + this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length]; + } + System.arraycopy(raw, 0, this.iterationCopy, 0, size); + + return ObjectArrayList.wrap( + this.iterationCopy, size + ); + } + + /** + * @reason Do not initialise ticking chunk list, as we did that above. + * @author Spottedleaf + */ + @Redirect( + method = "tickChunks", + at = @At( + value = "INVOKE", + target = "Ljava/util/Iterator;hasNext()Z", + ordinal = 0 + ) + ) + private boolean skipTickAdd(final Iterator instance) { + return false; + } + + /** + * @reason Clear the iteration array, and at the same time broadcast chunk changes. + * @author Spottedleaf + */ + @Redirect( + method = "tickChunks", + at = @At( + value = "INVOKE", + target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V", + ordinal = 0 + ) + ) + private void broadcastChanges(final List instance, + final Consumer consumer) { + final ObjectArrayList chunks = (ObjectArrayList)instance; + final ServerChunkCache.ChunkAndHolder[] raw = chunks.elements(); + final int size = chunks.size(); + + Objects.checkFromToIndex(0, size, raw.length); + for (int i = 0; i < size; ++i) { + final ServerChunkCache.ChunkAndHolder holder = raw[i]; + raw[i] = null; + + holder.holder().broadcastChanges(holder.chunk()); + } + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java index 7d158f6..1ef7e0c 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java @@ -86,11 +86,15 @@ public final class ChunkSystem { } public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().add( + ((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder() + ); } public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().remove( + ((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder() + ); } public static void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) { @@ -99,6 +103,9 @@ public final class ChunkSystem { } public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) { + ((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().add( + ((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder() + ); if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) { chunk.postProcessGeneration(); } @@ -107,15 +114,21 @@ public final class ChunkSystem { } public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove( + ((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder() + ); } public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().add( + ((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder() + ); } public static void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().remove( + ((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder() + ); } public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) { diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java index 93bd370..93583ea 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java @@ -1,11 +1,13 @@ package ca.spottedleaf.moonrise.patches.chunk_system.level; import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; +import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread; import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.status.ChunkStatus; import java.util.List; @@ -52,4 +54,10 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel { public void moonrise$setLastMidTickFailure(final long time); public NearbyPlayers moonrise$getNearbyPlayers(); + + public ReferenceList moonrise$getLoadedChunks(); + + public ReferenceList moonrise$getTickingChunks(); + + public ReferenceList moonrise$getEntityTickingChunks(); } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemLevelChunk.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemLevelChunk.java index 755b08d..5b092bc 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemLevelChunk.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemLevelChunk.java @@ -1,7 +1,13 @@ package ca.spottedleaf.moonrise.patches.chunk_system.level.chunk; +import net.minecraft.server.level.ServerChunkCache; + public interface ChunkSystemLevelChunk { public boolean moonrise$isPostProcessingDone(); + public ServerChunkCache.ChunkAndHolder moonrise$getChunkAndHolder(); + + public void moonrise$setChunkAndHolder(final ServerChunkCache.ChunkAndHolder holder); + } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java index 524d93b..0053ecb 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java @@ -15,8 +15,8 @@ public final class ServerEntityLookup extends EntityLookup { private static final Entity[] EMPTY_ENTITY_ARRAY = new Entity[0]; private final ServerLevel serverWorld; - public final ReferenceList trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY, 0); // Moonrise - entity tracker - public final ReferenceList trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY, 0); // Moonrise - entity tracker + public final ReferenceList trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker + public final ReferenceList trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker public ServerEntityLookup(final ServerLevel world, final LevelCallback worldCallback) { super(world, worldCallback); diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java index 789537a..00729ce 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java @@ -3,11 +3,12 @@ package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task; import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; +import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk; import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiManager; import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ImposterProtoChunk; @@ -65,6 +66,8 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false)); } + ((ChunkSystemLevelChunk)chunk).moonrise$setChunkAndHolder(new ServerChunkCache.ChunkAndHolder(chunk, this.chunkHolder.vanillaChunkHolder)); + final NewChunkHolder chunkHolder = this.chunkHolder; chunk.setFullStatus(chunkHolder::getChunkStatus); diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java new file mode 100644 index 0000000..e97e7d2 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java @@ -0,0 +1,7 @@ +package ca.spottedleaf.moonrise.patches.chunk_tick_iteration; + +public final class ChunkTickConstants { + + public static final int PLAYER_SPAWN_TRACK_RANGE = 8; + +} diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickDistanceManager.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickDistanceManager.java new file mode 100644 index 0000000..f28fd0e --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickDistanceManager.java @@ -0,0 +1,16 @@ +package ca.spottedleaf.moonrise.patches.chunk_tick_iteration; + +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ServerPlayer; + +public interface ChunkTickDistanceManager { + + public void moonrise$addPlayer(final ServerPlayer player, final SectionPos pos); + + public void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos); + + public void moonrise$updatePlayer(final ServerPlayer player, + final SectionPos oldPos, final SectionPos newPos, + final boolean oldIgnore, final boolean newIgnore); + +} diff --git a/src/main/resources/moonrise.accesswidener b/src/main/resources/moonrise.accesswidener index 3fe371a..1e40fc9 100644 --- a/src/main/resources/moonrise.accesswidener +++ b/src/main/resources/moonrise.accesswidener @@ -190,6 +190,7 @@ mutable field net/minecraft/server/level/DistanceManager ticketThrottlerInput Ln mutable field net/minecraft/server/level/DistanceManager ticketThrottlerReleaser Lnet/minecraft/util/thread/ProcessorHandle; mutable field net/minecraft/server/level/DistanceManager ticketsToRelease Lit/unimi/dsi/fastutil/longs/LongSet; mutable field net/minecraft/server/level/DistanceManager mainThreadExecutor Ljava/util/concurrent/Executor; +mutable field net/minecraft/server/level/DistanceManager naturalSpawnChunkCounter Lnet/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker; # DistanceManager$ChunkTicketTracker @@ -268,4 +269,9 @@ mutable field net/minecraft/server/level/GenerationChunkHolder generationRefCoun # ChunkMap.TrackedEntity accessible class net/minecraft/server/level/ChunkMap$TrackedEntity -accessible field net/minecraft/server/level/ChunkMap$TrackedEntity serverEntity Lnet/minecraft/server/level/ServerEntity; \ No newline at end of file +accessible field net/minecraft/server/level/ChunkMap$TrackedEntity serverEntity Lnet/minecraft/server/level/ServerEntity; + + +# ServerChunkCache$ChunkAndHolder +accessible class net/minecraft/server/level/ServerChunkCache$ChunkAndHolder +accessible method net/minecraft/server/level/ServerChunkCache$ChunkAndHolder (Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/server/level/ChunkHolder;)V \ No newline at end of file diff --git a/src/main/resources/moonrise.mixins.json b/src/main/resources/moonrise.mixins.json index a72a262..c8b73d3 100644 --- a/src/main/resources/moonrise.mixins.json +++ b/src/main/resources/moonrise.mixins.json @@ -52,6 +52,9 @@ "chunk_system.StructureCheckMixin", "chunk_system.StructureTemplate$PaletteMixin", "chunk_system.TicketMixin", + "chunk_tick_iteration.ChunkMapMixin", + "chunk_tick_iteration.DistanceManagerMixin", + "chunk_tick_iteration.ServerChunkCacheMixin", "collisions.ArmorStandMixin", "collisions.ArrayVoxelShapeMixin", "collisions.BitSetDiscreteVoxelShapeMixin",