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.
This commit is contained in:
Spottedleaf
2024-07-17 07:25:48 -07:00
parent cec8e6fd9d
commit bbf85f323f
19 changed files with 620 additions and 21 deletions

View File

@@ -7,23 +7,30 @@ import java.util.NoSuchElementException;
public final class ReferenceList<E> implements Iterable<E> {
private final Reference2IntOpenHashMap<E> referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f);
{
this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE);
}
private static final Object[] EMPTY_LIST = new Object[0];
private final Reference2IntOpenHashMap<E> 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<E> referenceToIndex) {
this.references = references;
this.count = count;
this.referenceToIndex = referenceToIndex;
}
public ReferenceList<E> copy() {
return new ReferenceList<>(this.references.clone(), this.count, this.referenceToIndex.clone());
}
public int size() {

View File

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

View File

@@ -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<T> {
private final Reference2ReferenceOpenHashMap<T, PositionCounter> counters = new Reference2ReferenceOpenHashMap<>();
private final Long2IntOpenHashMap positions = new Long2IntOpenHashMap();
public ReferenceSet<T> 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<T> {
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);
}
}
}
}

View File

@@ -4,7 +4,7 @@ import ca.spottedleaf.concurrentutil.util.IntegerUtil;
public abstract class SingleUserAreaMap<T> {
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<T> {
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));

View File

@@ -64,7 +64,7 @@ public abstract class ChunkHolderMixin extends GenerationChunkHolder implements
private NewChunkHolder newChunkHolder;
@Unique
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY, 0);
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY);
@Unique
private ChunkMap getChunkMap() {

View File

@@ -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.

View File

@@ -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<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> 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<ServerChunkCache.ChunkAndHolder> moonrise$getLoadedChunks() {
return this.loadedChunks;
}
@Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks() {
return this.tickingChunks;
}
@Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks() {
return this.entityTickingChunks;
}
/**
* @reason Declare method in this class so that any invocations are virtual, and not interface.
* @author Spottedleaf

View File

@@ -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<ServerPlayer> 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<ServerPlayer> getPlayersCloseForSpawning(final ChunkPos pos) {
final List<ServerPlayer> ret = new ArrayList<>();
final ReferenceList<ServerPlayer> 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;
}
}

View File

@@ -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<ServerPlayer> 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 = "<init>",
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));
}
}

View File

@@ -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 <T> ArrayList<T> 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<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
final ReferenceList<ServerChunkCache.ChunkAndHolder> 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 <E> boolean skipTickAdd(final Iterator<E> 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<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());
}
}
}

View File

@@ -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) {

View File

@@ -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<ServerChunkCache.ChunkAndHolder> moonrise$getLoadedChunks();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
}

View File

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

View File

@@ -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<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY, 0); // Moonrise - entity tracker
public final ReferenceList<Entity> trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY, 0); // Moonrise - entity tracker
public final ReferenceList<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
public final ReferenceList<Entity> trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
public ServerEntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
super(world, worldCallback);

View File

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

View File

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

View File

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

View File

@@ -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;
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 <init> (Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/server/level/ChunkHolder;)V

View File

@@ -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",