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.
This commit is contained in:
Spottedleaf
2024-09-12 12:07:59 -07:00
parent 7351872730
commit 04c6e6c01a
7 changed files with 143 additions and 19 deletions

View File

@@ -66,6 +66,9 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
@Unique
private final ReferenceList<ServerPlayer> 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;
}
/**

View File

@@ -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<ServerChunkCache.ChunkAndHolder> instance,
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
final ReferenceList<ChunkHolder> 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();
}
}

View File

@@ -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<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
@@ -129,6 +135,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
@Unique
private final ReferenceList<ChunkHolder> 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<ChunkHolder> 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;

View File

@@ -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(
@@ -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<ServerChunkCache.ChunkAndHolder> instance,
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
final ObjectArrayList<ServerChunkCache.ChunkAndHolder> chunks = (ObjectArrayList<ServerChunkCache.ChunkAndHolder>)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);
}
}

View File

@@ -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<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
public ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks();
public void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder);
public void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder);
public void moonrise$clearUnsyncedChunks();
}

View File

@@ -23,4 +23,8 @@ public interface ChunkSystemChunkHolder {
public LevelChunk moonrise$getFullChunk();
public boolean moonrise$isMarkedDirtyForPlayers();
public void moonrise$markDirtyForPlayers(final boolean value);
}

View File

@@ -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));
}