Optimize redundant entity tracker update

This commit is contained in:
IzzelAliz 2022-05-11 21:57:15 +08:00
parent f0436fb466
commit 62bf757f00
No known key found for this signature in database
GPG Key ID: EE50E123A11D8338
8 changed files with 175 additions and 2 deletions

View File

@ -37,4 +37,8 @@ public interface ServerPlayerEntityBridge extends PlayerEntityBridge {
Entity bridge$changeDimension(ServerLevel world, PlayerTeleportEvent.TeleportCause cause);
boolean bridge$initialized();
boolean bridge$isTrackerDirty();
void bridge$setTrackerDirty(boolean flag);
}

View File

@ -1,8 +1,16 @@
package io.izzel.arclight.common.bridge.core.world.server;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.world.entity.Entity;
public interface ChunkMap_TrackedEntityBridge {
ServerEntity bridge$getServerEntity();
Entity bridge$getEntity();
SectionPos bridge$getLastSectionPos();
void bridge$setLastSectionPos(SectionPos pos);
}

View File

@ -2,6 +2,7 @@ package io.izzel.arclight.common.mixin.core.server.level;
import io.izzel.arclight.common.bridge.core.world.ServerEntityBridge;
import io.izzel.arclight.common.bridge.core.world.server.ChunkMap_TrackedEntityBridge;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.network.ServerPlayerConnection;
@ -21,6 +22,8 @@ public abstract class ChunkMap_TrackedEntityMixin implements ChunkMap_TrackedEnt
// @formatter:off
@Shadow @Final ServerEntity serverEntity;
@Shadow @Final public Set<ServerPlayerConnection> seenBy;
@Shadow @Final Entity entity;
@Shadow SectionPos lastSectionPos;
// @formatter:on
@Inject(method = "<init>", at = @At("RETURN"))
@ -32,4 +35,19 @@ public abstract class ChunkMap_TrackedEntityMixin implements ChunkMap_TrackedEnt
public ServerEntity bridge$getServerEntity() {
return this.serverEntity;
}
@Override
public Entity bridge$getEntity() {
return this.entity;
}
@Override
public SectionPos bridge$getLastSectionPos() {
return this.lastSectionPos;
}
@Override
public void bridge$setLastSectionPos(SectionPos pos) {
this.lastSectionPos = pos;
}
}

View File

@ -0,0 +1,88 @@
package io.izzel.arclight.common.mixin.optimization.general.network;
import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge;
import io.izzel.arclight.common.bridge.core.world.server.ChunkMap_TrackedEntityBridge;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import it.unimi.dsi.fastutil.objects.ObjectCollection;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.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.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.util.ArrayList;
import java.util.Objects;
import java.util.Set;
@Mixin(ChunkMap.class)
public class ChunkMapMixin_Optimize {
// @formatter:off
@Shadow @Final public Int2ObjectMap<ChunkMap.TrackedEntity> entityMap;
@Shadow @Final public ServerLevel level;
// @formatter:on
@Redirect(method = "move", at = @At(value = "INVOKE", remap = false, target = "Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;values()Lit/unimi/dsi/fastutil/objects/ObjectCollection;"))
private ObjectCollection<ChunkMap.TrackedEntity> arclight$markDirty(Int2ObjectMap<ChunkMap.TrackedEntity> instance, ServerPlayer player) {
((ServerPlayerEntityBridge) player).bridge$setTrackerDirty(true);
return new ObjectArraySet<>();
}
@Inject(method = "tick()V", cancellable = true, at = @At("HEAD"))
private void arclight$optimizedTick(CallbackInfo ci) {
var list = new ArrayList<ChunkMap.TrackedEntity>(this.level.players().size());
for (var trackedEntity : this.entityMap.values()) {
var entity = ((ChunkMap_TrackedEntityBridge) trackedEntity).bridge$getEntity();
if (entity instanceof ServerPlayer player && ((ServerPlayerEntityBridge) player).bridge$isTrackerDirty()) {
list.add(trackedEntity);
((ServerPlayerEntityBridge) player).bridge$setTrackerDirty(false);
}
((ChunkMap_TrackedEntityBridge) trackedEntity).bridge$getServerEntity().sendChanges();
}
for (var trackedEntity : this.entityMap.values()) {
var entity = ((ChunkMap_TrackedEntityBridge) trackedEntity).bridge$getEntity();
SectionPos lastSectionPos = ((ChunkMap_TrackedEntityBridge) trackedEntity).bridge$getLastSectionPos();
SectionPos newSectionPos = SectionPos.of(entity);
((ChunkMap_TrackedEntityBridge) trackedEntity).bridge$setLastSectionPos(newSectionPos);
if (entity instanceof ServerPlayer player) {
for (var otherTracker : list) {
var other = (ServerPlayer) ((ChunkMap_TrackedEntityBridge) otherTracker).bridge$getEntity();
if (other.getId() > entity.getId()) {
trackedEntity.updatePlayer(other);
otherTracker.updatePlayer(player);
}
}
} else {
boolean chunkChanged = !Objects.equals(lastSectionPos, newSectionPos);
if (chunkChanged) {
trackedEntity.updatePlayers(this.level.players());
} else {
for (var other : list) {
trackedEntity.updatePlayer((ServerPlayer) ((ChunkMap_TrackedEntityBridge) other).bridge$getEntity());
}
}
}
}
ci.cancel();
}
@Mixin(ChunkMap.TrackedEntity.class)
public static class TrackedEntityMixin {
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"))
private Set<ServerPlayerConnection> arclight$useFastUtilSet() {
return new ReferenceOpenHashSet<>();
}
}
}

View File

@ -1,4 +1,4 @@
package io.izzel.arclight.common.mixin.optimization.general;
package io.izzel.arclight.common.mixin.optimization.general.network;
import io.netty.util.concurrent.AbstractEventExecutor;
import net.minecraft.network.Connection;

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mixin.optimization.general.network;
import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
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.Redirect;
@Mixin(ServerGamePacketListenerImpl.class)
public class ServerGamePacketListenerImplMixin_Optimize {
@Shadow public ServerPlayer player;
@Redirect(method = "handleMovePlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;move(Lnet/minecraft/server/level/ServerPlayer;)V"))
private void arclight$markTrackerDirty(ServerChunkCache instance, ServerPlayer player, ServerboundMovePlayerPacket packet) {
if (!packet.hasPosition()) {
// do not update tracker when no position is updated
var old = ((ServerPlayerEntityBridge) this.player).bridge$isTrackerDirty();
instance.move(player);
((ServerPlayerEntityBridge) this.player).bridge$setTrackerDirty(old);
} else {
instance.move(player);
}
}
}

View File

@ -0,0 +1,22 @@
package io.izzel.arclight.common.mixin.optimization.general.network;
import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge;
import net.minecraft.server.level.ServerPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin_Optimize implements ServerPlayerEntityBridge {
@Unique private boolean trackerDirty;
@Override
public boolean bridge$isTrackerDirty() {
return this.trackerDirty;
}
@Override
public void bridge$setTrackerDirty(boolean flag) {
this.trackerDirty = flag;
}
}

View File

@ -6,7 +6,6 @@
"compatibilityLevel": "JAVA_11",
"mixins": [
"ClassInheritanceMultiMapMixin",
"ConnectionMixin_Optimize",
"EntityDataManagerMixin_Optimize",
"EntityMixin_Optimize",
"GoalMixin",
@ -23,6 +22,11 @@
"activationrange.entity.ItemEntityMixin_ActivationRange",
"activationrange.entity.LivingEntityMixin_ActivationRange",
"activationrange.entity.VillagerEntityMixin_ActivationRange",
"network.ChunkMapMixin_Optimize",
"network.ChunkMapMixin_Optimize$TrackedEntityMixin",
"network.ConnectionMixin_Optimize",
"network.ServerGamePacketListenerImplMixin_Optimize",
"network.ServerPlayerMixin_Optimize",
"realtime.ItemEntityMixin_Realtime",
"realtime.PlayerInteractionManagerMixin_Realtime",
"trackingrange.ChunkManagerMixin_TrackingRange"