From 88a23222b63f7918d970137bf36bc0c8f6cf0de7 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 29 Nov 2021 14:50:41 +0100 Subject: [PATCH] Fix vanilla 1.18 bug causing entities to get stuck outside sim range MC uses the client-side position of an entity to determine whether it is inside the simulation range and therefore whether it will get ticked. For some (most) entities it interpolates the client-side position to match the server-side one inside of the tick method... no prices for guessing where this goes wrong. Somewhat similar to the issue we have with chunk unloads which we work around in FullReplaySender but worse cause they'll get stuck if they ever leave the sim range even for a single tick, rather then just when their chunk is unloaded. --- .../replay/mixin/ClientWorldAccessor.java | 13 +++++++ .../mixin/Mixin_FixEntityNotTracking.java | 1 + .../resources/mixins.replay.replaymod.json | 4 ++ .../mixin/Mixin_FixEntityNotTracking.java | 38 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 src/main/java/com/replaymod/replay/mixin/ClientWorldAccessor.java create mode 100644 src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java create mode 100644 versions/1.18/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java diff --git a/src/main/java/com/replaymod/replay/mixin/ClientWorldAccessor.java b/src/main/java/com/replaymod/replay/mixin/ClientWorldAccessor.java new file mode 100644 index 00000000..ba16ed59 --- /dev/null +++ b/src/main/java/com/replaymod/replay/mixin/ClientWorldAccessor.java @@ -0,0 +1,13 @@ +package com.replaymod.replay.mixin; + +import net.minecraft.client.world.ClientWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ClientWorld.class) +public interface ClientWorldAccessor { + //#if MC>=11800 + //$$ @Accessor + //$$ net.minecraft.world.EntityList getEntityList(); + //#endif +} diff --git a/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java b/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java new file mode 100644 index 00000000..464bb200 --- /dev/null +++ b/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java @@ -0,0 +1 @@ +// 1.18+ only diff --git a/src/main/resources/mixins.replay.replaymod.json b/src/main/resources/mixins.replay.replaymod.json index feb30fa7..6a481a03 100644 --- a/src/main/resources/mixins.replay.replaymod.json +++ b/src/main/resources/mixins.replay.replaymod.json @@ -6,6 +6,9 @@ "server": [], "client": [ "Mixin_FixNPCSkinCaching", + //#if MC>=11800 + //$$ "Mixin_FixEntityNotTracking", + //#endif //#if MC>=11600 "Mixin_MoveRealmsButton", //#endif @@ -13,6 +16,7 @@ "MixinCamera", "MixinInGameHud", //#endif + "ClientWorldAccessor", "EntityLivingBaseAccessor", //#if MC>=11400 "Mixin_ShowSpectatedHand_NoOF", diff --git a/versions/1.18/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java b/versions/1.18/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java new file mode 100644 index 00000000..1d95dd1b --- /dev/null +++ b/versions/1.18/src/main/java/com/replaymod/replay/mixin/Mixin_FixEntityNotTracking.java @@ -0,0 +1,38 @@ +package com.replaymod.replay.mixin; + +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.Vec3d; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(ClientPlayNetworkHandler.class) +public class Mixin_FixEntityNotTracking { + @ModifyVariable(method = { "onEntityPosition", "onEntity", "onEntityPassengersSet" }, at = @At("RETURN"), ordinal = 0) + private Entity updatePositionIfNotTracked$0(Entity entity) { + if (entity != null) { + entity.streamSelfAndPassengers().forEach(this::updatePositionIfNotTracked); + } + return entity; + } + + private void updatePositionIfNotTracked(Entity entity) { + if (entity != null && entity.world instanceof ClientWorldAccessor world) { + if (!world.getEntityList().has(entity)) { + // Skip interpolation of position updates coming from server + // (See: newX in EntityLivingBase or otherPlayerMPX in EntityOtherPlayerMP) + int ticks = 0; + Vec3d prevPos; + do { + prevPos = entity.getPos(); + if (entity.hasVehicle()) { + entity.tickRiding(); + } else { + entity.tick(); + } + } while (prevPos.squaredDistanceTo(entity.getPos()) > 0.0001 && ticks++ < 100); + } + } + } +}