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:
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user