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 93e8c813..2e876b91 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 @@ 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[] referenceArray) { + this.references = referenceArray; + this.referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f); + this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE); } - public ReferenceList(final E[] array, final int count) { - this.references = array; + 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 fdde1f5f..3d5be35b 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.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 static enum NearbyMapType { 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 void tickPlayer(final ServerPlayer player) { 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 void addPlayer(final ServerPlayer player, final NearbyMapType type) { 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 00000000..efefd94b --- /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 61f70247..94689e03 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 @@ 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 SingleUserAreaMap(final T parameter) { 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 36455dd4..0c65c8cd 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 ChunkHolderMixin(ChunkPos chunkPos) { 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 136103d3..eb0f560f 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 @@ 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 LevelChunkMixin(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAc @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 53024318..4de7bfad 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 @@ protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey 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 @@ private void init(MinecraftServer minecraftServer, Executor executor, 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 00000000..e5967206 --- /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 00000000..26a96fd7 --- /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 00000000..2123493f --- /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 7d158f62..1ef7e0ce 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 static void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder ho } 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 static void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolde } 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 static void onChunkTicking(final LevelChunk chunk, final ChunkHolder hold } 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 93bd370d..93583ea9 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 755b08dd..5b092bca 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 524d93bc..0053ecb8 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 789537a1..00729ce5 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 @@ 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 void run() { 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 00000000..e97e7d27 --- /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 00000000..f28fd0e0 --- /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 3fe371ac..1e40fc95 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 a72a2621..c8b73d33 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",