diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java new file mode 100644 index 00000000..fdde1f5f --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java @@ -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 players = new Reference2ReferenceOpenHashMap<>(); + private final Long2ReferenceOpenHashMap 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 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 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 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 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[] 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 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 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 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 { + + 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); + } + } + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java index 8f2a1f7a..fbcfee49 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java @@ -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 @@ protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey 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 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 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); + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/entity_tracker/EntityMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/entity_tracker/EntityMixin.java new file mode 100644 index 00000000..eb4b2621 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/entity_tracker/EntityMixin.java @@ -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 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 into, final List 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 getIndirectPassengers() { + final List ret = new ArrayList<>(); + + if (this.passengers.isEmpty()) { + return ret; + } + + collectIndirectPassengers(ret, this.passengers); + + return ret; + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/entity_tracker/TrackedEntityMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/entity_tracker/TrackedEntityMixin.java new file mode 100644 index 00000000..f9321d93 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/entity_tracker/TrackedEntityMixin.java @@ -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 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 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); + } + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java index becb0023..93bd370d 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java @@ -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(); } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java index f6a3eb3d..a346435a 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java @@ -73,6 +73,24 @@ public EntityLookup(final Level world, final LevelCallback worldCallback 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 void entityStatusChange(final Entity entity, final ChunkEntitySlices slic 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 void entityStatusChange(final Entity entity, final ChunkEntitySlices slic } if (!oldVisibility.isTicking() && newVisibility.isTicking()) { + EntityLookup.this.entityStartTicking(entity); if (EntityLookup.this.worldCallback != null) { EntityLookup.this.worldCallback.onTickingStart(entity); } @@ -267,12 +287,14 @@ public void entityStatusChange(final Entity entity, final ChunkEntitySlices slic } 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 @@ protected boolean addEntity(final Entity entity, final boolean fromDisk) { 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 @@ protected ChunkEntitySlices moveEntity(final Entity entity) { this.onEmptySlices(sectionX, sectionZ); } + this.entitySectionChangeCallback( + entity, + sectionX, sectionY, sectionZ, + newSectionX, newSectionY, newSectionZ + ); + return slices; } @@ -1012,6 +1042,7 @@ public void onMove() { // 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 void onRemove(final Entity.RemovalReason reason) { EntityLookup.this.entityStatusChange(entity, null, tickingState, Visibility.HIDDEN, false, false, reason.shouldDestroy()); + EntityLookup.this.removeEntityCallback(entity); + this.entity.setLevelCallback(NoOpCallback.INSTANCE); } } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java index fc4ea13a..77e81414 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java @@ -59,6 +59,43 @@ protected void onEmptySlices(final int chunkX, final int chunkZ) { 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); diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java index a9b0e8e9..4747499e 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java @@ -46,6 +46,43 @@ protected void onEmptySlices(final int chunkX, final int chunkZ) { 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 { @Override diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java index a6f4caf5..524d93bc 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java @@ -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 trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY, 0); // Moonrise - entity tracker + public final ReferenceList trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY, 0); // Moonrise - entity tracker public ServerEntityLookup(final ServerLevel world, final LevelCallback worldCallback) { super(world, worldCallback); @@ -48,4 +54,54 @@ protected ChunkEntitySlices createEntityChunk(final int chunkX, final int chunkZ 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) { + + } } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java index 4f7be7ee..7f94a05e 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java @@ -205,6 +205,8 @@ public void updatePlayer(final ServerPlayer player) { 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); } } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerEntity.java b/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerEntity.java new file mode 100644 index 00000000..5f5734c0 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerEntity.java @@ -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); + +} diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java b/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java new file mode 100644 index 00000000..1fa07bef --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java @@ -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(); + +} diff --git a/src/main/resources/moonrise.accesswidener b/src/main/resources/moonrise.accesswidener index c52b6ce7..3fe371ac 100644 --- a/src/main/resources/moonrise.accesswidener +++ b/src/main/resources/moonrise.accesswidener @@ -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; \ No newline at end of file +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; \ No newline at end of file diff --git a/src/main/resources/moonrise.mixins.json b/src/main/resources/moonrise.mixins.json index c546e2fd..3dd30b85 100644 --- a/src/main/resources/moonrise.mixins.json +++ b/src/main/resources/moonrise.mixins.json @@ -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",