Skip to content

Commit

Permalink
Fix inaccuracy in entity position tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
SamB440 committed May 30, 2024
1 parent aa6d7c2 commit 1676c35
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/main/java/ac/grim/grimac/checks/impl/combat/Reach.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private boolean isKnownInvalid(PacketEntity reachEntity) {
} else {
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();
if (reachEntity.getType() == EntityTypes.END_CRYSTAL) {
targetBox = new SimpleCollisionBox(reachEntity.desyncClientPos.subtract(1, 0, 1), reachEntity.desyncClientPos.add(1, 2, 1));
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
}
return ReachUtils.getMinReachToBox(player, targetBox) > player.compensatedEntities.getSelf().getEntityInteractRange();
}
Expand Down Expand Up @@ -159,7 +159,7 @@ private String checkReach(PacketEntity reachEntity, Vector3d from, boolean isPre
SimpleCollisionBox targetBox = reachEntity.getPossibleCollisionBoxes();

if (reachEntity.getType() == EntityTypes.END_CRYSTAL) { // Hardcode end crystal box
targetBox = new SimpleCollisionBox(reachEntity.desyncClientPos.subtract(1, 0, 1), reachEntity.desyncClientPos.add(1, 2, 1));
targetBox = new SimpleCollisionBox(reachEntity.trackedServerPosition.getPos().subtract(1, 0, 1), reachEntity.trackedServerPosition.getPos().add(1, 2, 1));
}

// 1.7 and 1.8 players get a bit of extra hitbox (this is why you should use 1.8 on cross version servers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ public void set(Vector3i position, WrappedBlockState state) {
// This happens due to the lack of an idle packet on 1.9+ clients
// On 1.8 clients this should practically never happen
if (interpWidth - width > 0.05 || interpHeight - height > 0.05) {
Vector3d entityPos = entity.desyncClientPos;
Vector3d entityPos = entity.trackedServerPosition.getPos();
interpBox = GetBoundingBox.getPacketEntityBoundingBox(player, entityPos.getX(), entityPos.getY(), entityPos.getZ(), entity);
}

Expand Down
26 changes: 15 additions & 11 deletions src/main/java/ac/grim/grimac/utils/data/ReachInterpolationData.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.packetentity.PacketEntity;
import ac.grim.grimac.utils.nmsutil.BoundingBoxSize;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.Vector3d;

// You may not copy the check unless you are licensed under GPL
public class ReachInterpolationData {
Expand All @@ -32,9 +33,12 @@ public class ReachInterpolationData {
private int interpolationStepsHighBound = 0;
private int interpolationSteps = 1;

public ReachInterpolationData(GrimPlayer player, SimpleCollisionBox startingLocation, double x, double y, double z, boolean isPointNine, PacketEntity entity) {
public ReachInterpolationData(GrimPlayer player, SimpleCollisionBox startingLocation, TrackedPosition position, PacketEntity entity) {
final boolean isPointNine = !player.compensatedEntities.getSelf().inVehicle() && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9);

this.startingLocation = startingLocation;
this.targetLocation = GetBoundingBox.getPacketEntityBoundingBox(player, x, y, z, entity);
final Vector3d pos = position.getPos();
this.targetLocation = GetBoundingBox.getPacketEntityBoundingBox(player, pos.x, pos.y, pos.z, entity);

// 1.9 -> 1.8 precision loss in packets
// (ViaVersion is doing some stuff that makes this code difficult)
Expand All @@ -44,11 +48,11 @@ public ReachInterpolationData(GrimPlayer player, SimpleCollisionBox startingLoca

if (EntityTypes.isTypeInstanceOf(entity.getType(), EntityTypes.BOAT)) {
interpolationSteps = 10;
} else if (EntityTypes.isTypeInstanceOf(entity.getType(), EntityTypes.MINECART_ABSTRACT)) {
} else if (entity.isMinecart()) {
interpolationSteps = 5;
} else if (entity.getType() == EntityTypes.SHULKER) {
interpolationSteps = 1;
} else if (EntityTypes.isTypeInstanceOf(entity.getType(), EntityTypes.LIVINGENTITY)) {
} else if (entity.isLivingEntity()) {
interpolationSteps = 3;
} else {
interpolationSteps = 1;
Expand Down Expand Up @@ -85,12 +89,12 @@ public static SimpleCollisionBox combineCollisionBox(SimpleCollisionBox one, Sim
public SimpleCollisionBox getPossibleLocationCombined() {
int interpSteps = getInterpolationSteps();

double stepMinX = (targetLocation.minX - startingLocation.minX) / interpSteps;
double stepMaxX = (targetLocation.maxX - startingLocation.maxX) / interpSteps;
double stepMinY = (targetLocation.minY - startingLocation.minY) / interpSteps;
double stepMaxY = (targetLocation.maxY - startingLocation.maxY) / interpSteps;
double stepMinZ = (targetLocation.minZ - startingLocation.minZ) / interpSteps;
double stepMaxZ = (targetLocation.maxZ - startingLocation.maxZ) / interpSteps;
double stepMinX = (targetLocation.minX - startingLocation.minX) / (double) interpSteps;
double stepMaxX = (targetLocation.maxX - startingLocation.maxX) / (double) interpSteps;
double stepMinY = (targetLocation.minY - startingLocation.minY) / (double) interpSteps;
double stepMaxY = (targetLocation.maxY - startingLocation.maxY) / (double) interpSteps;
double stepMinZ = (targetLocation.minZ - startingLocation.minZ) / (double) interpSteps;
double stepMaxZ = (targetLocation.maxZ - startingLocation.maxZ) / (double) interpSteps;

SimpleCollisionBox minimumInterpLocation = new SimpleCollisionBox(
startingLocation.minX + (interpolationStepsLowBound * stepMinX),
Expand Down
67 changes: 67 additions & 0 deletions src/main/java/ac/grim/grimac/utils/data/TrackedPosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package ac.grim.grimac.utils.data;

import ac.grim.grimac.player.GrimPlayer;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.Vector3d;

public final class TrackedPosition {

private static final double MODERN_COORDINATE_SCALE = 4096.0;
private static final double LEGACY_COORDINATE_SCALE = 32.0;

private final double scale;
private Vector3d pos = new Vector3d();

public TrackedPosition() {
// this.scale = player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9) ? MODERN_COORDINATE_SCALE : LEGACY_COORDINATE_SCALE;
this.scale = MODERN_COORDINATE_SCALE;
}

public double getScale() {
return scale;
}

public static long pack(double value, double scale) {
return Math.round(value * scale);
}

public static double packLegacy(double value, double scale) {
return Math.floor(value * scale);
}

private double unpack(long value) {
return (double) value / scale;
}

private double unpackLegacy(double value) {
return value / scale;
}

public Vector3d getPos() {
return pos;
}

// Method since 1.16.
public Vector3d withDelta(long x, long y, long z) {
if (x == 0L && y == 0L && z == 0L) {
return this.pos;
}

double d = x == 0L ? this.pos.x : unpack(pack(this.pos.x, scale) + x);
double e = y == 0L ? this.pos.y : unpack(pack(this.pos.y, scale) + y);
double f = z == 0L ? this.pos.z : unpack(pack(this.pos.z, scale) + z);
return new Vector3d(d, e, f);
}

// In 1.16-, this was different.
public Vector3d withDeltaLegacy(double x, double y, double z) {
double d = unpackLegacy(packLegacy(this.pos.x, scale) + x);
double e = unpackLegacy(packLegacy(this.pos.y, scale) + y);
double f = unpackLegacy(packLegacy(this.pos.z, scale) + z);
return new Vector3d(d, e, f);
}

public void setPos(Vector3d pos) {
this.pos = pos;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import ac.grim.grimac.player.GrimPlayer;
import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox;
import ac.grim.grimac.utils.data.ReachInterpolationData;
import ac.grim.grimac.utils.data.TrackedPosition;
import ac.grim.grimac.utils.data.TrackedPosition;
import ac.grim.grimac.utils.nmsutil.GetBoundingBox;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
Expand All @@ -30,7 +32,8 @@

// You may not copy this check unless your anticheat is licensed under GPL
public class PacketEntity extends TypedPacketEntity {
public Vector3d desyncClientPos;

public final TrackedPosition trackedServerPosition;

public PacketEntity riding;
public List<PacketEntity> passengers = new ArrayList<>(0);
Expand All @@ -47,16 +50,17 @@ public class PacketEntity extends TypedPacketEntity {

public PacketEntity(EntityType type) {
super(type);
this.trackedServerPosition = new TrackedPosition();
}

public PacketEntity(GrimPlayer player, EntityType type, double x, double y, double z) {
super(type);
this.desyncClientPos = new Vector3d(x, y, z);
this.trackedServerPosition = new TrackedPosition();
this.trackedServerPosition.setPos(new Vector3d(x, y, z));
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) { // Thanks ViaVersion
desyncClientPos = new Vector3d(((int) (desyncClientPos.getX() * 32)) / 32d, ((int) (desyncClientPos.getY() * 32)) / 32d, ((int) (desyncClientPos.getZ() * 32)) / 32d);
trackedServerPosition.setPos(new Vector3d(((int) (x * 32)) / 32d, ((int) (y * 32)) / 32d, ((int) (z * 32)) / 32d));
}
this.newPacketLocation = new ReachInterpolationData(player, GetBoundingBox.getPacketEntityBoundingBox(player, x, y, z, this),
desyncClientPos.getX(), desyncClientPos.getY(), desyncClientPos.getZ(), !player.compensatedEntities.getSelf().inVehicle() && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9), this);
this.newPacketLocation = new ReachInterpolationData(player, GetBoundingBox.getPacketEntityBoundingBox(player, x, y, z, this), trackedServerPosition, this);
}

// Set the old packet location to the new one
Expand All @@ -65,21 +69,27 @@ public void onFirstTransaction(boolean relative, boolean hasPos, double relX, do
if (hasPos) {
if (relative) {
// This only matters for 1.9+ clients, but it won't hurt 1.8 clients either... align for imprecision
desyncClientPos = new Vector3d(Math.floor(desyncClientPos.getX() * 4096) / 4096, Math.floor(desyncClientPos.getY() * 4096) / 4096, Math.floor(desyncClientPos.getZ() * 4096) / 4096);
desyncClientPos = desyncClientPos.add(new Vector3d(relX, relY, relZ));
final double scale = trackedServerPosition.getScale();
Vector3d vec3d;
if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_16)) {
vec3d = trackedServerPosition.withDelta(TrackedPosition.pack(relX, scale), TrackedPosition.pack(relY, scale), TrackedPosition.pack(relZ, scale));
} else {
vec3d = trackedServerPosition.withDeltaLegacy(TrackedPosition.packLegacy(relX, scale), TrackedPosition.packLegacy(relY, scale), TrackedPosition.packLegacy(relZ, scale));
}
trackedServerPosition.setPos(vec3d);
} else {
desyncClientPos = new Vector3d(relX, relY, relZ);
trackedServerPosition.setPos(new Vector3d(relX, relY, relZ));
// ViaVersion desync's here for teleports
// It simply teleports the entity with its position divided by 32... ignoring the offset this causes.
// Thanks a lot ViaVersion! Please don't fix this, or it will be a pain to support.
if (player.getClientVersion().isOlderThan(ClientVersion.V_1_9)) {
desyncClientPos = new Vector3d(((int) (desyncClientPos.getX() * 32)) / 32d, ((int) (desyncClientPos.getY() * 32)) / 32d, ((int) (desyncClientPos.getZ() * 32)) / 32d);
trackedServerPosition.setPos(new Vector3d(((int) (relX * 32)) / 32d, ((int) (relY * 32)) / 32d, ((int) (relZ * 32)) / 32d));
}
}
}

this.oldPacketLocation = newPacketLocation;
this.newPacketLocation = new ReachInterpolationData(player, oldPacketLocation.getPossibleLocationCombined(), desyncClientPos.getX(), desyncClientPos.getY(), desyncClientPos.getZ(), !player.compensatedEntities.getSelf().inVehicle() && player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_9), this);
this.newPacketLocation = new ReachInterpolationData(player, oldPacketLocation.getPossibleLocationCombined(), trackedServerPosition, this);
}

// Remove the possibility of the old packet location
Expand Down Expand Up @@ -119,7 +129,7 @@ public void eject() {
public void setPositionRaw(SimpleCollisionBox box) {
// I'm disappointed in you mojang. Please don't set the packet position as it desyncs it...
// But let's follow this flawed client-sided logic!
this.desyncClientPos = new Vector3d((box.maxX - box.minX) / 2 + box.minX, box.minY, (box.maxZ - box.minZ) / 2 + box.minZ);
this.trackedServerPosition.setPos(new Vector3d((box.maxX - box.minX) / 2 + box.minX, box.minY, (box.maxZ - box.minZ) / 2 + box.minZ));
// This disables interpolation
this.newPacketLocation = new ReachInterpolationData(box);
}
Expand Down

0 comments on commit 1676c35

Please sign in to comment.