Copy entity tracker optimisations from Folia
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user