From 2c7c1d79af91e5b82e52f6bacb0a70b637c5d642 Mon Sep 17 00:00:00 2001 From: Term4 Date: Mon, 27 Jul 2026 18:43:13 -0500 Subject: [PATCH] Pass view-relative teleport flags through in 1.9->1.8 1.8 handles the rotation-relative position flags (0x08/0x10) natively, and resolving them against the tracked rotation snaps the camera to a stale look on every teleport. Keep the rotation bits and their deltas on the wire and only resolve the position bits. Since the client applies kept deltas to its own live look, its teleport confirm echoes a rotation the tracker cannot predict - match those confirms on position only and adopt the echoed rotation. --- .../rewriter/PlayerPacketRewriter1_9.java | 21 +++++++++++++------ .../storage/PlayerPositionTracker.java | 6 +++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java index fa9d68132..877070ed0 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/rewriter/PlayerPacketRewriter1_9.java @@ -167,7 +167,11 @@ public void register() { float yaw = wrapper.get(Types.FLOAT, 0); float pitch = wrapper.get(Types.FLOAT, 1); - wrapper.set(Types.BYTE, 0, (byte) 0); + // 1.8 supports the rotation-relative flags (0x08/0x10) natively, and resolving them here + // would snap the camera to the tracked rotation, which lags the client's live look. Keep + // them on the wire and only resolve the position bits (native 1.8 servers use zero-delta + // rotation-relative teleports to leave the camera untouched). + wrapper.set(Types.BYTE, 0, (byte) (flags & 0x18)); if (flags != 0) { if ((flags & 0x01) != 0) { @@ -183,19 +187,17 @@ public void register() { wrapper.set(Types.DOUBLE, 2, z); } if ((flags & 0x08) != 0) { - yaw += pos.getYaw(); - wrapper.set(Types.FLOAT, 0, yaw); + yaw += pos.getYaw(); // tracked estimate only; the wire keeps the delta } if ((flags & 0x10) != 0) { pitch += pos.getPitch(); - wrapper.set(Types.FLOAT, 1, pitch); } } pos.setPos(x, y, z); pos.setYaw(yaw); pos.setPitch(pitch); - pos.queueTeleport(teleportId, x, y, z, pos.getYaw(), pos.getPitch()); + pos.queueTeleport(teleportId, x, y, z, pos.getYaw(), pos.getPitch(), (flags & 0x18) != 0); }); } }); @@ -364,12 +366,19 @@ public void register() { // This Y error gets propagated from 1.7 to 1.8 to 1.9 which causes teleport confirmation to not properly be detected // This fixes it similarly to how anticheats detect teleports: https://github.com/GrimAnticheat/Grim/blob/67aa3a9483a9b2a6987d594092697b4104c781f0/common/src/main/java/ac/grim/grimac/manager/SetbackTeleportUtil.java#L315 boolean closeEnoughY = Math.abs(teleport.y() - y) <= 1e-7; + // A view-relative teleport keeps its rotation deltas on the wire, so the client applies them + // to its own live look and echoes that - match on position only and adopt the echoed rotation + final boolean rotationMatches = teleport.viewRelative() || (teleport.yaw() == yaw && teleport.pitch() == pitch); - if (teleport.x() == x && closeEnoughY && teleport.z() == z && teleport.yaw() == yaw && teleport.pitch() == pitch) { + if (teleport.x() == x && closeEnoughY && teleport.z() == z && rotationMatches) { final PacketWrapper confirmTeleport = PacketWrapper.create(ServerboundPackets1_9.ACCEPT_TELEPORTATION, wrapper.user()); confirmTeleport.write(Types.VAR_INT, teleport.id()); confirmTeleport.sendToServer(Protocol1_9To1_8.class); + if (teleport.viewRelative()) { + storage.setYaw(yaw); + storage.setPitch(pitch); + } storage.confirmTeleport(); } } else { diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/PlayerPositionTracker.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/PlayerPositionTracker.java index 97af46ca6..2bb3a7cbf 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/PlayerPositionTracker.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_9to1_8/storage/PlayerPositionTracker.java @@ -88,8 +88,8 @@ public boolean hasPendingTeleports() { return !this.pendingTeleports.isEmpty(); } - public void queueTeleport(final int id, final double x, final double y, final double z, final float yaw, final float pitch) { - this.pendingTeleports.add(new PendingTeleport(id, x, y, z, yaw, pitch)); + public void queueTeleport(final int id, final double x, final double y, final double z, final float yaw, final float pitch, final boolean viewRelative) { + this.pendingTeleports.add(new PendingTeleport(id, x, y, z, yaw, pitch, viewRelative)); } public PendingTeleport peekTeleport() { @@ -100,7 +100,7 @@ public void confirmTeleport() { this.pendingTeleports.poll(); } - public record PendingTeleport(int id, double x, double y, double z, float yaw, float pitch) { + public record PendingTeleport(int id, double x, double y, double z, float yaw, float pitch, boolean viewRelative) { } }