Copy entity tracker optimisations from Folia

This commit is contained in:
Spottedleaf
2024-07-11 09:00:15 -07:00
parent 6ff9aca2db
commit 6d8e12ed6f
15 changed files with 715 additions and 1 deletions

View File

@@ -0,0 +1,209 @@
package ca.spottedleaf.moonrise.common.misc;
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 it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
public final class NearbyPlayers {
public static enum NearbyMapType {
GENERAL,
GENERAL_SMALL,
GENERAL_REALLY_SMALL,
TICK_VIEW_DISTANCE,
VIEW_DISTANCE,
SPAWN_RANGE,
}
private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values();
public static final int TOTAL_MAP_TYPES = MAP_TYPES.length;
private static final int GENERAL_AREA_VIEW_DISTANCE = MoonriseConstants.MAX_VIEW_DISTANCE + 1;
private static final int GENERAL_SMALL_VIEW_DISTANCE = 10;
private static final int GENERAL_REALLY_SMALL_VIEW_DISTANCE = 3;
public static final int GENERAL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_AREA_VIEW_DISTANCE << 4);
public static final int GENERAL_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_SMALL_VIEW_DISTANCE << 4);
public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4);
private final ServerLevel world;
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
public NearbyPlayers(final ServerLevel world) {
this.world = world;
}
public void addPlayer(final ServerPlayer player) {
final TrackedPlayer[] newTrackers = new TrackedPlayer[TOTAL_MAP_TYPES];
if (this.players.putIfAbsent(player, newTrackers) != null) {
throw new IllegalStateException("Already have player " + player);
}
final ChunkPos chunk = player.chunkPosition();
for (int i = 0; i < TOTAL_MAP_TYPES; ++i) {
// use 0 for default, will be updated by tickPlayer
(newTrackers[i] = new TrackedPlayer(player, MAP_TYPES[i])).add(chunk.x, chunk.z, 0);
}
// update view distances
this.tickPlayer(player);
}
public void removePlayer(final ServerPlayer player) {
final TrackedPlayer[] players = this.players.remove(player);
if (players == null) {
return; // May be called during teleportation before the player is actually placed
}
for (final TrackedPlayer tracker : players) {
tracker.remove();
}
}
public void tickPlayer(final ServerPlayer player) {
final TrackedPlayer[] players = this.players.get(player);
if (players == null) {
throw new IllegalStateException("Don't have player " + player);
}
final ChunkPos chunk = player.chunkPosition();
players[NearbyMapType.GENERAL.ordinal()].update(chunk.x, chunk.z, GENERAL_AREA_VIEW_DISTANCE);
players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE);
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));
}
public TrackedChunk getChunk(final ChunkPos pos) {
return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
}
public TrackedChunk getChunk(final BlockPos pos) {
return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
}
public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public ReferenceList<ServerPlayer> getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) {
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public ReferenceList<ServerPlayer> getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) {
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public static final class TrackedChunk {
private static final ServerPlayer[] EMPTY_PLAYERS_ARRAY = new ServerPlayer[0];
private final ReferenceList<ServerPlayer>[] players = new ReferenceList[TOTAL_MAP_TYPES];
private int nonEmptyLists;
private long updateCount;
public boolean isEmpty() {
return this.nonEmptyLists == 0;
}
public long getUpdateCount() {
return this.updateCount;
}
public ReferenceList<ServerPlayer> getPlayers(final NearbyMapType type) {
return this.players[type.ordinal()];
}
public void addPlayer(final ServerPlayer player, final NearbyMapType type) {
++this.updateCount;
final int idx = type.ordinal();
final ReferenceList<ServerPlayer> list = this.players[idx];
if (list == null) {
++this.nonEmptyLists;
(this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY, 0)).add(player);
return;
}
if (!list.add(player)) {
throw new IllegalStateException("Already contains player " + player);
}
}
public void removePlayer(final ServerPlayer player, final NearbyMapType type) {
++this.updateCount;
final int idx = type.ordinal();
final ReferenceList<ServerPlayer> list = this.players[idx];
if (list == null) {
throw new IllegalStateException("Does not contain player " + player);
}
if (!list.remove(player)) {
throw new IllegalStateException("Does not contain player " + player);
}
if (list.size() == 0) {
this.players[idx] = null;
--this.nonEmptyLists;
}
}
}
private final class TrackedPlayer extends SingleUserAreaMap<ServerPlayer> {
private final NearbyMapType type;
public TrackedPlayer(final ServerPlayer player, final NearbyMapType type) {
super(player);
this.type = type;
}
@Override
protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> {
return new TrackedChunk();
}).addPlayer(parameter, this.type);
}
@Override
protected void removeCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey);
if (chunk == null) {
throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey));
}
chunk.removePlayer(parameter, this.type);
if (chunk.isEmpty()) {
NearbyPlayers.this.byChunk.remove(chunkKey);
}
}
}
}

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.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
@@ -104,6 +105,9 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe
@Unique
private long tickedBlocksOrFluids;
@Unique
private final NearbyPlayers nearbyPlayers = new NearbyPlayers((ServerLevel)(Object)this);
/**
* @reason Initialise fields / destroy entity manager state
* @author Spottedleaf
@@ -306,6 +310,11 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe
this.lastMidTickFailure = time;
}
@Override
public final NearbyPlayers moonrise$getNearbyPlayers() {
return this.nearbyPlayers;
}
/**
* @reason Entities are guaranteed to be ticking in the new chunk system
* @author Spottedleaf

View File

@@ -0,0 +1,132 @@
package ca.spottedleaf.moonrise.mixin.entity_tracker;
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_system.level.entity.server.ServerEntityLookup;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.datafixers.DataFixer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.GeneratingChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.chunk.storage.ChunkStorage;
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Iterator;
@Mixin(ChunkMap.class)
public abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerProvider, GeneratingChunkMap {
@Shadow
@Final
public ServerLevel level;
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
super(regionStorageInfo, path, dataFixer, bl);
}
/**
* @reason The new tracker tick method will perform the necessary tracker updates.
* @author Spottedleaf
*/
@Redirect(
method = "move",
at = @At(
value = "INVOKE",
target = "Ljava/util/Iterator;hasNext()Z",
ordinal = 0
)
)
private boolean skipMoveTrackerUpdate(final Iterator<?> iterator) {
return false;
}
/**
* @reason New entity tracker tick method which scales better.
* @author Spottedleaf
*/
@Redirect(
method = "tick()V",
at = @At(
value = "INVOKE",
target = "Ljava/util/Iterator;hasNext()Z",
ordinal = 1
)
)
private boolean newTrackerTick(final Iterator<?> iterator) {
final NearbyPlayers nearbyPlayers = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup)((ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();;
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
for (int i = 0, len = trackerEntities.size(); i < len; ++i) {
final Entity entity = trackerEntitiesRaw[i];
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity)entity).moonrise$getTrackedEntity();
if (tracker == null) {
continue;
}
((EntityTrackerTrackedEntity)tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
tracker.serverEntity.sendChanges();
}
// process unloads
final ReferenceList<Entity> unloadedEntities = entityLookup.trackerUnloadedEntities;
final Entity[] unloadedEntitiesRaw = Arrays.copyOf(unloadedEntities.getRawDataUnchecked(), unloadedEntities.size());
unloadedEntities.clear();
for (final Entity entity : unloadedEntitiesRaw) {
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity)entity).moonrise$getTrackedEntity();
if (tracker == null) {
continue;
}
((EntityTrackerTrackedEntity)tracker).moonrise$clearPlayers();
}
return false;
}
/**
* @reason Update tracker field
* @author Spottedleaf
*/
@Inject(
method = "addEntity",
at = @At(
value = "INVOKE",
target = "Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;put(ILjava/lang/Object;)Ljava/lang/Object;",
shift = At.Shift.AFTER
)
)
private void addEntityTrackerField(final Entity entity, final CallbackInfo ci,
@Local(ordinal = 0) final ChunkMap.TrackedEntity trackedEntity) {
if (((EntityTrackerEntity)entity).moonrise$getTrackedEntity() != null) {
throw new IllegalStateException("Entity is already tracked");
}
((EntityTrackerEntity)entity).moonrise$setTrackedEntity(trackedEntity);
}
/**
* @reason Update tracker field
* @author Spottedleaf
*/
@Inject(
method = "removeEntity",
at = @At(
value = "RETURN"
)
)
private void removeEntityTrackerField(final Entity entity, final CallbackInfo ci) {
((EntityTrackerEntity)entity).moonrise$setTrackedEntity(null);
}
}

View File

@@ -0,0 +1,57 @@
package ca.spottedleaf.moonrise.mixin.entity_tracker;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
import com.google.common.collect.ImmutableList;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.entity.Entity;
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 java.util.ArrayList;
import java.util.List;
@Mixin(Entity.class)
public abstract class EntityMixin implements EntityTrackerEntity {
@Shadow
private ImmutableList<Entity> passengers;
@Unique
private ChunkMap.TrackedEntity trackedEntity;
@Override
public final ChunkMap.TrackedEntity moonrise$getTrackedEntity() {
return this.trackedEntity;
}
@Override
public final void moonrise$setTrackedEntity(final ChunkMap.TrackedEntity trackedEntity) {
this.trackedEntity = trackedEntity;
}
@Unique
private static void collectIndirectPassengers(final List<Entity> into, final List<Entity> from) {
for (final Entity passenger : from) {
into.add(passenger);
collectIndirectPassengers(into, ((EntityMixin)(Object)passenger).passengers);
}
}
/**
* @reason Replace with more optimised method
* @author Spottedleaf
*/
@Overwrite
public Iterable<Entity> getIndirectPassengers() {
final List<Entity> ret = new ArrayList<>();
if (this.passengers.isEmpty()) {
return ret;
}
collectIndirectPassengers(ret, this.passengers);
return ret;
}
}

View File

@@ -0,0 +1,107 @@
package ca.spottedleaf.moonrise.mixin.entity_tracker;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
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 java.util.Set;
@Mixin(ChunkMap.TrackedEntity.class)
public abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
@Shadow
@Final
private Set<ServerPlayerConnection> seenBy;
@Shadow
public abstract void updatePlayer(ServerPlayer serverPlayer);
@Shadow
public abstract void removePlayer(ServerPlayer serverPlayer);
@Unique
private long lastChunkUpdate = -1L;
@Unique
private NearbyPlayers.TrackedChunk lastTrackedChunk;
@Override
public final void moonrise$tick(final NearbyPlayers.TrackedChunk chunk) {
if (chunk == null) {
this.moonrise$clearPlayers();
return;
}
final ReferenceList<ServerPlayer> players = chunk.getPlayers(NearbyPlayers.NearbyMapType.VIEW_DISTANCE);
if (players == null) {
this.moonrise$clearPlayers();
return;
}
final long lastChunkUpdate = this.lastChunkUpdate;
final long currChunkUpdate = chunk.getUpdateCount();
final NearbyPlayers.TrackedChunk lastTrackedChunk = this.lastTrackedChunk;
this.lastChunkUpdate = currChunkUpdate;
this.lastTrackedChunk = chunk;
final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
for (int i = 0, len = players.size(); i < len; ++i) {
final ServerPlayer player = playersRaw[i];
this.updatePlayer(player);
}
if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
// need to purge any players possible not in the chunk list
for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
final ServerPlayer player = conn.getPlayer();
if (!players.contains(player)) {
this.removePlayer(player);
}
}
}
}
@Override
public final void moonrise$removeNonTickThreadPlayers() {
boolean foundToRemove = false;
for (final ServerPlayerConnection conn : this.seenBy) {
if (!TickThread.isTickThreadFor(conn.getPlayer())) {
foundToRemove = true;
break;
}
}
if (!foundToRemove) {
return;
}
for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
ServerPlayer player = conn.getPlayer();
if (!TickThread.isTickThreadFor(player)) {
this.removePlayer(player);
}
}
}
@Override
public final void moonrise$clearPlayers() {
this.lastChunkUpdate = -1;
this.lastTrackedChunk = null;
if (this.seenBy.isEmpty()) {
return;
}
for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
ServerPlayer player = conn.getPlayer();
this.removePlayer(player);
}
}
}

View File

@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.patches.chunk_system.level;
import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
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;
@@ -49,4 +50,6 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
public long moonrise$getLastMidTickFailure();
public void moonrise$setLastMidTickFailure(final long time);
public NearbyPlayers moonrise$getNearbyPlayers();
}

View File

@@ -73,6 +73,24 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
protected abstract void onEmptySlices(final int chunkX, final int chunkZ);
protected abstract void entitySectionChangeCallback(
final Entity entity,
final int oldSectionX, final int oldSectionY, final int oldSectionZ,
final int newSectionX, final int newSectionY, final int newSectionZ
);
protected abstract void addEntityCallback(final Entity entity);
protected abstract void removeEntityCallback(final Entity entity);
protected abstract void entityStartLoaded(final Entity entity);
protected abstract void entityEndLoaded(final Entity entity);
protected abstract void entityStartTicking(final Entity entity);
protected abstract void entityEndTicking(final Entity entity);
private static Entity maskNonAccessible(final Entity entity) {
if (entity == null) {
return null;
@@ -251,6 +269,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
if (newVisibility.ordinal() > oldVisibility.ordinal()) {
// status upgrade
if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) {
EntityLookup.this.entityStartLoaded(entity);
synchronized (this.accessibleEntities) {
this.accessibleEntities.add(entity);
}
@@ -260,6 +279,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
}
if (!oldVisibility.isTicking() && newVisibility.isTicking()) {
EntityLookup.this.entityStartTicking(entity);
if (EntityLookup.this.worldCallback != null) {
EntityLookup.this.worldCallback.onTickingStart(entity);
}
@@ -267,12 +287,14 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
} else {
// status downgrade
if (oldVisibility.isTicking() && !newVisibility.isTicking()) {
EntityLookup.this.entityEndTicking(entity);
if (EntityLookup.this.worldCallback != null) {
EntityLookup.this.worldCallback.onTickingEnd(entity);
}
}
if (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
EntityLookup.this.entityEndLoaded(entity);
synchronized (this.accessibleEntities) {
this.accessibleEntities.remove(entity);
}
@@ -414,6 +436,8 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
entity.setLevelCallback(new EntityCallback(entity));
this.addEntityCallback(entity);
this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false);
return true;
@@ -521,6 +545,12 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
this.onEmptySlices(sectionX, sectionZ);
}
this.entitySectionChangeCallback(
entity,
sectionX, sectionY, sectionZ,
newSectionX, newSectionY, newSectionZ
);
return slices;
}
@@ -1012,6 +1042,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
// no new section, so didn't change sections
return;
}
final Visibility newVisibility = getEntityStatus(entity);
EntityLookup.this.entityStatusChange(entity, newSlices, oldVisibility, newVisibility, true, false, false);
@@ -1027,6 +1058,8 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
EntityLookup.this.entityStatusChange(entity, null, tickingState, Visibility.HIDDEN, false, false, reason.shouldDestroy());
EntityLookup.this.removeEntityCallback(entity);
this.entity.setLevelCallback(NoOpCallback.INSTANCE);
}
}

View File

@@ -59,6 +59,43 @@ public final class ClientEntityLookup extends EntityLookup {
this.removeChunk(chunkX, chunkZ);
}
@Override
protected void entitySectionChangeCallback(final Entity entity,
final int oldSectionX, final int oldSectionY, final int oldSectionZ,
final int newSectionX, final int newSectionY, final int newSectionZ) {
}
@Override
protected void addEntityCallback(final Entity entity) {
}
@Override
protected void removeEntityCallback(final Entity entity) {
}
@Override
protected void entityStartLoaded(final Entity entity) {
}
@Override
protected void entityEndLoaded(final Entity entity) {
}
@Override
protected void entityStartTicking(final Entity entity) {
}
@Override
protected void entityEndTicking(final Entity entity) {
}
public void markTicking(final long pos) {
if (this.tickingChunks.add(pos)) {
final int chunkX = CoordinateUtils.getChunkX(pos);

View File

@@ -46,6 +46,43 @@ public final class DefaultEntityLookup extends EntityLookup {
this.removeChunk(chunkX, chunkZ);
}
@Override
protected void entitySectionChangeCallback(final Entity entity,
final int oldSectionX, final int oldSectionY, final int oldSectionZ,
final int newSectionX, final int newSectionY, final int newSectionZ) {
}
@Override
protected void addEntityCallback(final Entity entity) {
}
@Override
protected void removeEntityCallback(final Entity entity) {
}
@Override
protected void entityStartLoaded(final Entity entity) {
}
@Override
protected void entityEndLoaded(final Entity entity) {
}
@Override
protected void entityStartTicking(final Entity entity) {
}
@Override
protected void entityEndTicking(final Entity entity) {
}
protected static final class DefaultLevelCallback implements LevelCallback<Entity> {
@Override

View File

@@ -1,16 +1,22 @@
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.entity.LevelCallback;
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 ServerEntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
super(world, worldCallback);
@@ -48,4 +54,54 @@ public final class ServerEntityLookup extends EntityLookup {
protected void onEmptySlices(final int chunkX, final int chunkZ) {
// entity slices unloading is managed by ticket levels in chunk system
}
@Override
protected void entitySectionChangeCallback(final Entity entity,
final int oldSectionX, final int oldSectionY, final int oldSectionZ,
final int newSectionX, final int newSectionY, final int newSectionZ) {
if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().tickPlayer(player);
}
}
@Override
protected void addEntityCallback(final Entity entity) {
if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().addPlayer(player);
}
}
@Override
protected void removeEntityCallback(final Entity entity) {
if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().removePlayer(player);
}
this.trackerUnloadedEntities.remove(entity); // Moonrise - entity tracker
}
@Override
protected void entityStartLoaded(final Entity entity) {
// Moonrise start - entity tracker
this.trackerEntities.add(entity);
this.trackerUnloadedEntities.remove(entity);
// Moonrise end - entity tracker
}
@Override
protected void entityEndLoaded(final Entity entity) {
// Moonrise start - entity tracker
this.trackerEntities.remove(entity);
this.trackerUnloadedEntities.add(entity);
// Moonrise end - entity tracker
}
@Override
protected void entityStartTicking(final Entity entity) {
}
@Override
protected void entityEndTicking(final Entity entity) {
}
}

View File

@@ -205,6 +205,8 @@ public final class RegionizedPlayerChunkLoader {
final PlayerChunkLoaderData loader = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (loader != null) {
loader.update();
// update view distances for nearby players
((ChunkSystemServerLevel)loader.world).moonrise$getNearbyPlayers().tickPlayer(player);
}
}

View File

@@ -0,0 +1,11 @@
package ca.spottedleaf.moonrise.patches.entity_tracker;
import net.minecraft.server.level.ChunkMap;
public interface EntityTrackerEntity {
public ChunkMap.TrackedEntity moonrise$getTrackedEntity();
public void moonrise$setTrackedEntity(final ChunkMap.TrackedEntity trackedEntity);
}

View File

@@ -0,0 +1,13 @@
package ca.spottedleaf.moonrise.patches.entity_tracker;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
public interface EntityTrackerTrackedEntity {
public void moonrise$tick(final NearbyPlayers.TrackedChunk chunk);
public void moonrise$removeNonTickThreadPlayers();
public void moonrise$clearPlayers();
}

View File

@@ -263,4 +263,9 @@ mutable field net/minecraft/world/level/levelgen/structure/templatesystem/Struct
mutable field net/minecraft/server/level/GenerationChunkHolder startedWork Ljava/util/concurrent/atomic/AtomicReference;
mutable field net/minecraft/server/level/GenerationChunkHolder futures Ljava/util/concurrent/atomic/AtomicReferenceArray;
mutable field net/minecraft/server/level/GenerationChunkHolder task Ljava/util/concurrent/atomic/AtomicReference;
mutable field net/minecraft/server/level/GenerationChunkHolder generationRefCount Ljava/util/concurrent/atomic/AtomicInteger;
mutable field net/minecraft/server/level/GenerationChunkHolder generationRefCount Ljava/util/concurrent/atomic/AtomicInteger;
# ChunkMap.TrackedEntity
accessible class net/minecraft/server/level/ChunkMap$TrackedEntity
accessible field net/minecraft/server/level/ChunkMap$TrackedEntity serverEntity Lnet/minecraft/server/level/ServerEntity;

View File

@@ -68,6 +68,9 @@
"collisions.SliceShapeMixin",
"collisions.VoxelShapeMixin",
"command.CommandsMixin",
"entity_tracker.ChunkMapMixin",
"entity_tracker.EntityMixin",
"entity_tracker.TrackedEntityMixin",
"farm_block.FarmBlockMixin",
"fast_palette.CrudeIncrementalIntIdentityHashBiMapMixin",
"fast_palette.HashMapPaletteMixin",