diff --git a/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java b/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java index 27b70d1759..9a8063d176 100644 --- a/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java +++ b/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java @@ -322,6 +322,9 @@ private static void handleBlockPlaceOrUseItem(PacketWrapper packet, GrimPlayer p } private boolean isMojangStupid(GrimPlayer player, WrapperPlayClientPlayerFlying flying) { + // Mojang has become less stupid! + if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21)) return false; + final Location location = flying.getLocation(); final double threshold = player.getMovementThreshold(); diff --git a/src/main/java/ac/grim/grimac/predictionengine/PlayerBaseTick.java b/src/main/java/ac/grim/grimac/predictionengine/PlayerBaseTick.java index 44cda1146e..3264ebc88b 100644 --- a/src/main/java/ac/grim/grimac/predictionengine/PlayerBaseTick.java +++ b/src/main/java/ac/grim/grimac/predictionengine/PlayerBaseTick.java @@ -8,6 +8,8 @@ import ac.grim.grimac.utils.latency.CompensatedEntities; import ac.grim.grimac.utils.math.GrimMath; import ac.grim.grimac.utils.nmsutil.*; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.manager.server.ServerVersion; import com.github.retrooper.packetevents.protocol.player.ClientVersion; import com.github.retrooper.packetevents.protocol.world.BlockFace; import com.github.retrooper.packetevents.protocol.world.states.type.StateType; @@ -142,7 +144,7 @@ public void updatePowderSnow() { if (player.getClientVersion().isOlderThanOrEquals(ClientVersion.V_1_16_4)) return; // The client first desync's this attribute - player.compensatedEntities.getSelf().playerSpeed.getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID)); + player.compensatedEntities.getSelf().playerSpeed.getModifiers().removeIf(modifier -> modifier.getUUID().equals(CompensatedEntities.SNOW_MODIFIER_UUID) || modifier.getName().getKey().equals("powder_snow")); // And then re-adds it using purely what the server has sent it StateType type = BlockProperties.getOnPos(player, player.mainSupportingBlockData, new Vector3d(player.x, player.y, player.z)); diff --git a/src/main/java/ac/grim/grimac/utils/collisions/datatypes/ComplexCollisionBox.java b/src/main/java/ac/grim/grimac/utils/collisions/datatypes/ComplexCollisionBox.java index 08c2f82f4f..23a0fe0f77 100644 --- a/src/main/java/ac/grim/grimac/utils/collisions/datatypes/ComplexCollisionBox.java +++ b/src/main/java/ac/grim/grimac/utils/collisions/datatypes/ComplexCollisionBox.java @@ -5,6 +5,7 @@ import java.util.List; public class ComplexCollisionBox implements CollisionBox { + private final List boxes = new ArrayList<>(); public ComplexCollisionBox(CollisionBox... boxes) { diff --git a/src/main/java/ac/grim/grimac/utils/collisions/datatypes/HexCollisionBox.java b/src/main/java/ac/grim/grimac/utils/collisions/datatypes/HexCollisionBox.java index 415c1e99e3..5b08696393 100644 --- a/src/main/java/ac/grim/grimac/utils/collisions/datatypes/HexCollisionBox.java +++ b/src/main/java/ac/grim/grimac/utils/collisions/datatypes/HexCollisionBox.java @@ -1,6 +1,7 @@ package ac.grim.grimac.utils.collisions.datatypes; public class HexCollisionBox extends SimpleCollisionBox { + public HexCollisionBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { this.minX = minX / 16d; this.minY = minY / 16d; diff --git a/src/main/java/ac/grim/grimac/utils/collisions/datatypes/SimpleCollisionBox.java b/src/main/java/ac/grim/grimac/utils/collisions/datatypes/SimpleCollisionBox.java index cdbed4ffaa..1d372ddee3 100644 --- a/src/main/java/ac/grim/grimac/utils/collisions/datatypes/SimpleCollisionBox.java +++ b/src/main/java/ac/grim/grimac/utils/collisions/datatypes/SimpleCollisionBox.java @@ -4,6 +4,9 @@ import com.github.retrooper.packetevents.protocol.world.BlockFace; import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.util.Vector3i; +import it.unimi.dsi.fastutil.doubles.AbstractDoubleList; +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; import org.bukkit.Location; import org.bukkit.util.Vector; @@ -11,9 +14,11 @@ import java.util.List; public class SimpleCollisionBox implements CollisionBox { + public static final double COLLISION_EPSILON = 1.0E-7; + public double minX, minY, minZ, maxX, maxY, maxZ; - boolean isFullBlock = false; + private boolean isFullBlock = false; public SimpleCollisionBox() { this(0, 0, 0, 0, 0, 0, false); @@ -425,6 +430,55 @@ public Vector min() { return new Vector(minX, minY, minZ); } + public DoubleList getYPointPositions() { + return create(minX, minY, minZ, maxX, maxY, maxZ); + } + + private DoubleList create(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + if (!(maxX - minX < 1.0E-7) && !(maxY - minY < 1.0E-7) && !(maxZ - minZ < 1.0E-7)) { + int i = findBits(minX, maxX); + int j = findBits(minY, maxY); + int k = findBits(minZ, maxZ); + if (i < 0 || j < 0 || k < 0) { + return DoubleArrayList.wrap(new double[]{minY, maxY}); + } else if (i == 0 && j == 0 && k == 0) { + return DoubleArrayList.wrap(new double[]{0, 1}); + } else { + int m = 1 << j; + + return new AbstractDoubleList() { + @Override + public double getDouble(int index) { + return (double) index / (double) m; + } + + @Override + public int size() { + return m + 1; + } + }; + } + } else { + return DoubleArrayList.of(); + } + } + + private int findBits(double min, double max) { + if (!(min < -COLLISION_EPSILON) && !(max > 1.0000001)) { + for (int i = 0; i <= 3; i++) { + int j = 1 << i; + double d = min * (double)j; + double e = max * (double)j; + boolean bl = Math.abs(d - (double)Math.round(d)) < COLLISION_EPSILON * (double)j; + boolean bl2 = Math.abs(e - (double)Math.round(e)) < COLLISION_EPSILON * (double)j; + if (bl && bl2) { + return i; + } + } + } + return -1; + } + @Override public String toString() { return "SimpleCollisionBox{" + diff --git a/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java b/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java index ce2402de63..330c09dd67 100644 --- a/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java +++ b/src/main/java/ac/grim/grimac/utils/latency/CompensatedEntities.java @@ -10,6 +10,7 @@ import ac.grim.grimac.utils.nmsutil.WatchableIndexUtil; import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.manager.server.ServerVersion; +import com.github.retrooper.packetevents.protocol.attribute.Attributes; import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; @@ -17,15 +18,19 @@ import com.github.retrooper.packetevents.protocol.potion.PotionType; import com.github.retrooper.packetevents.protocol.potion.PotionTypes; import com.github.retrooper.packetevents.protocol.world.BlockFace; +import com.github.retrooper.packetevents.resources.ResourceLocation; import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.bukkit.Bukkit; import java.util.*; public class CompensatedEntities { + private static final UUID SPRINTING_MODIFIER_UUID = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); public static final UUID SNOW_MODIFIER_UUID = UUID.fromString("1eaf83ff-7207-4596-b37a-d7a07b3ec4ce"); + public final Int2ObjectOpenHashMap entityMap = new Int2ObjectOpenHashMap<>(40, 0.7f); public final Int2ObjectOpenHashMap serverPositionsMap = new Int2ObjectOpenHashMap<>(40, 0.7f); public Integer serverPlayerVehicle = null; @@ -108,12 +113,12 @@ public double getPlayerMovementSpeed() { public void updateAttributes(int entityID, List objects) { if (entityID == player.entityID) { for (WrapperPlayServerUpdateAttributes.Property snapshotWrapper : objects) { - final String key = snapshotWrapper.getKey(); - if (key.toUpperCase().contains("MOVEMENT")) { + if (snapshotWrapper.getAttribute() == Attributes.GENERIC_MOVEMENT_SPEED) { boolean found = false; List modifiers = snapshotWrapper.getModifiers(); for (WrapperPlayServerUpdateAttributes.PropertyModifier modifier : modifiers) { - if (modifier.getUUID().equals(SPRINTING_MODIFIER_UUID)) { + final ResourceLocation name = modifier.getName(); + if (name.getKey().equals(SPRINTING_MODIFIER_UUID.toString()) || name.getKey().equals("sprinting")) { found = true; break; } @@ -125,6 +130,8 @@ public void updateAttributes(int entityID, List modifiers = snapshotWrapper.getModifiers(); - modifiers.removeIf(modifier -> modifier.getUUID().equals(SPRINTING_MODIFIER_UUID)); + modifiers.removeIf(modifier -> modifier.getUUID().equals(SPRINTING_MODIFIER_UUID) || modifier.getName().getKey().equals("sprinting")); for (WrapperPlayServerUpdateAttributes.PropertyModifier attributemodifier : modifiers) { if (attributemodifier.getOperation() == WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.ADDITION) diff --git a/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java b/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java index 4d0fa2717b..382ba21fc0 100644 --- a/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java +++ b/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java @@ -20,6 +20,9 @@ import com.github.retrooper.packetevents.protocol.world.states.type.StateType; import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes; import com.github.retrooper.packetevents.util.Vector3d; +import it.unimi.dsi.fastutil.floats.FloatArraySet; +import it.unimi.dsi.fastutil.floats.FloatArrays; +import it.unimi.dsi.fastutil.floats.FloatSet; import org.bukkit.Location; import org.bukkit.util.Vector; @@ -109,27 +112,50 @@ public static Vector collide(GrimPlayer player, double desiredX, double desiredY // While running up stairs and holding space, the player activates the "lastOnGround" part without otherwise being able to step // 0.03 movement must compensate for stepping elsewhere. Too much of a hack to include in this met5hod. - boolean movingIntoGround = (player.lastOnGround || (collisionResult.getY() != desiredY && (desiredY < 0 || clientVelY < 0))) || player.pointThreeEstimator.closeEnoughToGroundToStepWithPointThree(data, clientVelY); + boolean movingIntoGroundReal = player.pointThreeEstimator.closeEnoughToGroundToStepWithPointThree(data, clientVelY) || collisionResult.getY() != desiredY && (desiredY < 0 || clientVelY < 0); + boolean movingIntoGround = player.lastOnGround || movingIntoGroundReal; // If the player has x or z collision, is going in the downwards direction in the last or this tick, and can step up // If not, just return the collisions without stepping up that we calculated earlier if (stepUpHeight > 0.0F && movingIntoGround && (collisionResult.getX() != desiredX || collisionResult.getZ() != desiredZ)) { player.uncertaintyHandler.isStepMovement = true; - Vector regularStepUp = collideBoundingBoxLegacy(new Vector(desiredX, stepUpHeight, desiredZ), player.boundingBox, desiredMovementCollisionBoxes, order); - - // 1.7 clients do not have this stepping bug fix - if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) { - Vector stepUpBugFix = collideBoundingBoxLegacy(new Vector(0, stepUpHeight, 0), player.boundingBox.copy().expandToCoordinate(desiredX, 0, desiredZ), desiredMovementCollisionBoxes, order); - if (stepUpBugFix.getY() < stepUpHeight) { - Vector stepUpBugFixResult = collideBoundingBoxLegacy(new Vector(desiredX, 0, desiredZ), player.boundingBox.copy().offset(0, stepUpBugFix.getY(), 0), desiredMovementCollisionBoxes, order).add(stepUpBugFix); - if (getHorizontalDistanceSqr(stepUpBugFixResult) > getHorizontalDistanceSqr(regularStepUp)) { - regularStepUp = stepUpBugFixResult; + // 1.21 significantly refactored this + if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_21)) { + SimpleCollisionBox box2 = movingIntoGroundReal ? player.boundingBox.copy().offset(0.0, collisionResult.getY(), 0.0) : player.boundingBox.copy(); + SimpleCollisionBox box3 = box2.copy().expandToCoordinate(desiredX, stepUpHeight, desiredZ); + if (!movingIntoGroundReal) { + box3 = box3.copy().expandToCoordinate(0.0, -1.0E-5F, 0.0); + } + + final List list2 = new ArrayList<>(); + getCollisionBoxes(player, box3, list2, false); + final float[] stepHeights = collectStepHeights(box2, list2, (float) stepUpHeight, (float) collisionResult.getY()); + + for (float stepHeight : stepHeights) { + Vector vec3d2 = collideBoundingBoxLegacy(new Vector(desiredX, stepHeight, desiredZ), box2, list2, order); + if (getHorizontalDistanceSqr(vec3d2) > getHorizontalDistanceSqr(collisionResult)) { + final double d = player.boundingBox.minY - box2.minY; + collisionResult = vec3d2.add(new Vector(0.0, -d, 0.0)); + break; + } + } + } else { + Vector regularStepUp = collideBoundingBoxLegacy(new Vector(desiredX, stepUpHeight, desiredZ), player.boundingBox, desiredMovementCollisionBoxes, order); + + // 1.7 clients do not have this stepping bug fix + if (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_8)) { + Vector stepUpBugFix = collideBoundingBoxLegacy(new Vector(0, stepUpHeight, 0), player.boundingBox.copy().expandToCoordinate(desiredX, 0, desiredZ), desiredMovementCollisionBoxes, order); + if (stepUpBugFix.getY() < stepUpHeight) { + Vector stepUpBugFixResult = collideBoundingBoxLegacy(new Vector(desiredX, 0, desiredZ), player.boundingBox.copy().offset(0, stepUpBugFix.getY(), 0), desiredMovementCollisionBoxes, order).add(stepUpBugFix); + if (getHorizontalDistanceSqr(stepUpBugFixResult) > getHorizontalDistanceSqr(regularStepUp)) { + regularStepUp = stepUpBugFixResult; + } } } - } - if (getHorizontalDistanceSqr(regularStepUp) > getHorizontalDistanceSqr(collisionResult)) { - collisionResult = regularStepUp.add(collideBoundingBoxLegacy(new Vector(0, -regularStepUp.getY() + (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14) ? desiredY : 0), 0), player.boundingBox.copy().offset(regularStepUp.getX(), regularStepUp.getY(), regularStepUp.getZ()), desiredMovementCollisionBoxes, order)); + if (getHorizontalDistanceSqr(regularStepUp) > getHorizontalDistanceSqr(collisionResult)) { + collisionResult = regularStepUp.add(collideBoundingBoxLegacy(new Vector(0, -regularStepUp.getY() + (player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_14) ? desiredY : 0), 0), player.boundingBox.copy().offset(regularStepUp.getX(), regularStepUp.getY(), regularStepUp.getZ()), desiredMovementCollisionBoxes, order)); + } } } @@ -151,6 +177,27 @@ public static Vector collide(GrimPlayer player, double desiredX, double desiredY return bestOrderResult; } + private static float[] collectStepHeights(SimpleCollisionBox collisionBox, List collisions, float stepHeight, float collideY) { + final FloatSet floatSet = new FloatArraySet(4); + + for (SimpleCollisionBox blockBox : collisions) { + for (double possibleStepY : blockBox.getYPointPositions()) { + float yDiff = (float) (possibleStepY - collisionBox.minY); + if (!(yDiff < 0.0F) && yDiff != collideY) { + if (yDiff > stepHeight) { + break; + } + + floatSet.add(yDiff); + } + } + } + + float[] fs = floatSet.toFloatArray(); + FloatArrays.unstableSort(fs); + return fs; + } + public static boolean addWorldBorder(GrimPlayer player, SimpleCollisionBox wantedBB, List listOfBlocks, boolean onlyCheckCollide) { // Worldborders were added in 1.8 // Don't add to border unless the player is colliding with it and is near it