From 04c6e6c01a8a48adbcc2273f3b3dcd2d7ed5761c Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Thu, 12 Sep 2024 12:07:59 -0700 Subject: [PATCH] Track broadcast chunks separately from player ticking chunks Not all broadcast chunks are ticking in the new chunk system, so we need to track them separately to ensure updates take place in non-simulation range chunks. --- .../mixin/chunk_system/ChunkHolderMixin.java | 35 +++++++++++++- .../chunk_system/ServerChunkCacheMixin.java | 34 +++++++++++++ .../mixin/chunk_system/ServerLevelMixin.java | 48 +++++++++++++++++++ .../ServerChunkCacheMixin.java | 31 ++++++------ .../level/ChunkSystemServerLevel.java | 9 ++++ .../level/chunk/ChunkSystemChunkHolder.java | 4 ++ .../scheduling/NewChunkHolder.java | 1 + 7 files changed, 143 insertions(+), 19 deletions(-) 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 fc99003..3bbeb2a 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 @@ -66,6 +66,9 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy @Unique private final ReferenceList playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY); + @Unique + private boolean isMarkedDirtyForPlayers; + @Unique private ChunkMap getChunkMap() { return (ChunkMap)this.playerProvider; @@ -120,6 +123,16 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy return ret; } + @Override + public final boolean moonrise$isMarkedDirtyForPlayers() { + return this.isMarkedDirtyForPlayers; + } + + @Override + public final void moonrise$markDirtyForPlayers(final boolean value) { + this.isMarkedDirtyForPlayers = value; + } + @Unique private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = new ServerPlayer[0]; @@ -287,7 +300,14 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy // no players to sent to, so don't need to update anything return null; } - return this.getChunkToSend(); + final LevelChunk ret = this.getChunkToSend(); + + if (ret != null) { + ((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this); + return ret; + } + + return ret; } /** @@ -302,7 +322,18 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy ) ) private LevelChunk redirectLightUpdate(final ChunkHolder instance) { - return this.getChunkToSend(); + if (this.playersSentChunkTo.size() == 0) { + // no players to sent to, so don't need to update anything + return null; + } + final LevelChunk ret = this.getChunkToSend(); + + if (ret != null) { + ((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this); + return ret; + } + + return ret; } /** diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java index 6b9a76f..c95b09d 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java @@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system; import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; import ca.spottedleaf.concurrentutil.util.Priority; import ca.spottedleaf.moonrise.common.PlatformHooks; +import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.util.CoordinateUtils; import ca.spottedleaf.moonrise.common.util.TickThread; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; @@ -33,6 +34,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.io.IOException; +import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -357,4 +360,35 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) { return true; } + + /** + * @reason Since chunks in non-simulation range are only brought up to FULL status, not TICKING, + * those chunks may not be present in the ticking list and as a result we need to use our own list + * to ensure these chunks broadcast changes + * @author Spottedleaf + */ + @Redirect( + method = "tickChunks", + at = @At( + value = "INVOKE", + target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V" + ) + ) + private void fixBroadcastChanges(final List instance, + final Consumer consumer) { + final ReferenceList unsyncedChunks = ((ChunkSystemServerLevel)this.level).moonrise$getUnsyncedChunks(); + final ChunkHolder[] chunkHolders = unsyncedChunks.getRawDataUnchecked(); + final int totalUnsyncedChunks = unsyncedChunks.size(); + + Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length); + for (int i = 0; i < totalUnsyncedChunks; ++i) { + final ChunkHolder chunkHolder = chunkHolders[i]; + final LevelChunk chunk = chunkHolder.getChunkToSend(); + if (chunk != null) { + chunkHolder.broadcastChanges(chunk); + } + } + + ((ChunkSystemServerLevel)this.level).moonrise$clearUnsyncedChunks(); + } } 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 144c510..37d3ba9 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 @@ -10,6 +10,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityData import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; +import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup; import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; @@ -26,6 +27,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.ServerChunkCache; @@ -62,6 +64,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.io.Writer; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; @@ -120,6 +123,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel, @Unique private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0]; + @Unique + private static final ChunkHolder[] EMPTY_CHUNK_HOLDERS = new ChunkHolder[0]; + @Unique private final ReferenceList loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); @@ -129,6 +135,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel, @Unique private final ReferenceList entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); + @Unique + private final ReferenceList unsyncedChunks = new ReferenceList<>(EMPTY_CHUNK_HOLDERS); + /** * @reason Initialise fields / destroy entity manager state * @author Spottedleaf @@ -345,6 +354,45 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel, return this.entityTickingChunks; } + @Override + public final ReferenceList moonrise$getUnsyncedChunks() { + return this.unsyncedChunks; + } + + @Override + public final void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder) { + if (((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) { + return; + } + + ((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(true); + this.unsyncedChunks.add(chunkHolder); + } + + @Override + public final void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder) { + if (!((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) { + return; + } + + ((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false); + this.unsyncedChunks.remove(chunkHolder); + } + + @Override + public final void moonrise$clearUnsyncedChunks() { + final ChunkHolder[] chunkHolders = this.unsyncedChunks.getRawDataUnchecked(); + final int totalUnsyncedChunks = this.unsyncedChunks.size(); + + Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length); + for (int i = 0; i < totalUnsyncedChunks; ++i) { + final ChunkHolder chunkHolder = chunkHolders[i]; + + ((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false); + } + this.unsyncedChunks.clear(); + } + @Override public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) { final ServerChunkCache chunkSource = this.chunkSource; 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 index db34375..3ff75d1 100644 --- 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 @@ -24,9 +24,12 @@ 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -42,6 +45,9 @@ abstract class ServerChunkCacheMixin extends ChunkSource { @Unique private ServerChunkCache.ChunkAndHolder[] iterationCopy; + @Unique + private int iterationCopyLen; + @Unique private final SimpleRandom shuffleRandom = new SimpleRandom(0L); @@ -83,6 +89,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource { if (this.iterationCopy == null || this.iterationCopy.length < size) { this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length]; } + this.iterationCopyLen = size; System.arraycopy(raw, 0, this.iterationCopy, 0, size); return ObjectArrayList.wrap( @@ -135,7 +142,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource { ) ) private boolean useNearbyCache(final ChunkMap instance, final ChunkPos chunkPos, - @Local(ordinal = 0, argsOnly = false) final LevelChunk levelChunk) { + @Local(ordinal = 0, argsOnly = false) final LevelChunk levelChunk) { final ChunkData chunkData = ((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder()) .moonrise$getRealChunkHolder().holderData; @@ -164,29 +171,19 @@ abstract class ServerChunkCacheMixin extends ChunkSource { } /** - * @reason Clear the iteration array, and at the same time broadcast chunk changes. + * @reason Clear the iteration array after the list is done being used. * @author Spottedleaf */ - @Redirect( + @Inject( method = "tickChunks", at = @At( value = "INVOKE", target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V", - ordinal = 0 + ordinal = 0, + shift = At.Shift.AFTER ) ) - 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()); - } + private void broadcastChanges(final CallbackInfo ci) { + Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null); } } 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 39a2755..0fb7377 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 @@ -7,6 +7,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; 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.ChunkHolder; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.status.ChunkStatus; @@ -60,4 +61,12 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel { public ReferenceList moonrise$getTickingChunks(); public ReferenceList moonrise$getEntityTickingChunks(); + + public ReferenceList moonrise$getUnsyncedChunks(); + + public void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder); + + public void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder); + + public void moonrise$clearUnsyncedChunks(); } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemChunkHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemChunkHolder.java index 7d049d7..ba9111f 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemChunkHolder.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemChunkHolder.java @@ -23,4 +23,8 @@ public interface ChunkSystemChunkHolder { public LevelChunk moonrise$getFullChunk(); + public boolean moonrise$isMarkedDirtyForPlayers(); + + public void moonrise$markDirtyForPlayers(final boolean value); + } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java index b74ec68..1cacbf4 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java @@ -760,6 +760,7 @@ public final class NewChunkHolder { void onUnload() { this.unloaded = true; + ((ChunkSystemServerLevel)this.world).moonrise$removeUnsyncedChunk(this.vanillaChunkHolder); ((ChunkSystemLevel)this.world).moonrise$releaseChunkData(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ)); }