Files
KauriV3-mirror/src/main/java/dev/brighten/ac/handler/MovementHandler.java
T
2022-08-31 22:00:39 -04:00

568 lines
22 KiB
Java

package dev.brighten.ac.handler;
import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.data.obj.CMove;
import dev.brighten.ac.handler.compat.CompatHandler;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutPosition;
import dev.brighten.ac.utils.*;
import dev.brighten.ac.utils.objects.evicting.EvictingList;
import dev.brighten.ac.utils.timer.Timer;
import dev.brighten.ac.utils.timer.impl.TickTimer;
import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.val;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.potion.PotionEffectType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@RequiredArgsConstructor
public class MovementHandler {
private final APlayer player;
@Getter
private final CMove to = new CMove(), from = new CMove();
@Getter
private double deltaX, deltaY, deltaZ, deltaXZ,
lDeltaX, lDeltaY, lDeltaZ, lDeltaXZ;
@Getter
private float lookX, lookY, lastLookX, lastLookY;
@Getter
private float deltaYaw, deltaPitch, lDeltaYaw, lDeltaPitch, pitchGCD, lastPitchGCD, yawGCD, lastYawGCD;
@Getter
private int moveTicks;
private final List<KLocation> posLocs = new ArrayList<>();
@Getter
private boolean checkMovement, accurateYawData, cinematic, jumped, inAir;
@Getter
@Setter
private boolean excuseNextFlying;
@Getter
private final Timer lastTeleport = new TickTimer(), lastHighRate = new TickTimer();
@Getter
private int teleportsToConfirm;
@Getter
private final LinkedList<Float> yawGcdList = new EvictingList<>(45),
pitchGcdList = new EvictingList<>(45);
@Getter
private float sensitivityX, sensitivityY, currentSensX, currentSensY, sensitivityMcp, yawMode, pitchMode;
@Getter
private int sensXPercent, sensYPercent, airTicks, groundTicks;
private int ticks;
private double lastX, lastY, lastLastY, lastYawAcelleration, lastPitchAcelleration;
private boolean inTick;
@Getter
private TickTimer lastCinematic = new TickTimer(2);
private Timer lastReset = new TickTimer(2), generalProcess = new TickTimer(3);
private final EvictingList<Integer> sensitivitySamples = new EvictingList<>(50);
public void process(WPacketPlayInFlying packet, long currentTime) {
player.getPotionHandler().onFlying(packet);
checkMovement = MovementUtils.checkMovement(player.getPlayerConnection());
if (checkMovement) {
moveTicks++;
if(!packet.isMoved()) moveTicks = 1;
} else moveTicks = 0;
updateLocations(packet);
if (moveTicks > 0) {
// Updating block locations
player.getInfo().setBlockOnTo(BlockUtils
.getBlockAsync(to.getLoc().toLocation(player.getBukkitPlayer().getWorld())));
player.getInfo().setBlockBelow(BlockUtils
.getBlockAsync(to.getLoc().toLocation(player.getBukkitPlayer().getWorld())
.subtract(0, 1, 0)));
if (packet.isMoved()) {
// Updating player bounding box
player.getInfo().getLastMove().reset();
player.getInfo().setOnLadder(MovementUtils.isOnLadder(player));
}
if(packet.isMoved() && !lastTeleport.isNotPassed(2) && !player.getInfo().isCreative()
&& !player.getInfo().isCanFly()) {
synchronized (player.pastLocations) { //To prevent ConcurrentModificationExceptions
player.pastLocations.add(new Tuple<>(getTo().getLoc().clone(),
deltaXZ + Math.abs(deltaY)));
}
}
player.getBlockInfo().runCollisionCheck();
if(player.getBlockInfo().blocksAbove) {
player.getInfo().getBlockAbove().reset();
}
}
processVelocity();
if (player.getBlockInfo().onSlime) player.getInfo().slimeTimer.reset();
if(player.getBlockInfo().onClimbable) player.getInfo().climbTimer.reset();
checkForTeleports(packet);
if (packet.isLooked()) {
process();
float deltaYaw = Math.abs(this.deltaYaw), lastDeltaYaw = Math.abs(this.lDeltaYaw);
final double differenceYaw = Math.abs(this.deltaYaw - lastDeltaYaw);
final double differencePitch = Math.abs(this.deltaPitch - this.lDeltaPitch);
final double joltYaw = Math.abs(differenceYaw - deltaYaw);
final double joltPitch = Math.abs(differencePitch - this.deltaPitch);
final float yawThreshold = Math.max(1.0f, deltaYaw / 2f),
pitchThreshold = Math.max(1.f, Math.abs(this.deltaPitch) / 2f);
if (joltYaw > yawThreshold && joltPitch > pitchThreshold) this.lastHighRate.reset();
this.lastPitchGCD = this.pitchGCD;
this.lastYawGCD = this.yawGCD;
this.yawGCD = MathUtils
.gcdSmall(this.deltaYaw, this.lDeltaYaw);
this.pitchGCD = MathUtils
.gcdSmall(this.deltaPitch, this.lDeltaPitch);
val origin = this.to.getLoc().clone();
origin.y += player.getInfo().isSneaking() ? 1.54 : 1.62;
if (lastTeleport.isPassed(1)) {
predictionHandling:
{
float yawGcd = this.yawGCD,
pitchGcd = this.pitchGCD;
//Adding gcd of yaw and pitch.
if (this.yawGCD > 0.01 && this.yawGCD < 1.2) {
yawGcdList.add(yawGcd);
}
if (this.pitchGCD > 0.01 && this.pitchGCD < 1.2)
pitchGcdList.add(pitchGcd);
if (yawGcdList.size() < 20 || pitchGcdList.size() < 20) {
accurateYawData = false;
break predictionHandling;
}
accurateYawData = true;
//Making sure to get shit within the std for a more accurate result.
currentSensX = getSensitivityFromYawGCD(yawGcd);
currentSensY = getSensitivityFromPitchGCD(pitchGcd);
if (lastReset.isPassed()) {
yawMode = MathUtils.getMode(yawGcdList);
pitchMode = MathUtils.getMode(pitchGcdList);
lastReset.reset();
sensXPercent = sensToPercent(sensitivityX = getSensitivityFromYawGCD(yawMode));
sensYPercent = sensToPercent(sensitivityY = getSensitivityFromPitchGCD(pitchMode));
table:
{
sensitivitySamples.add(Math.max(sensXPercent, sensYPercent));
if (sensitivitySamples.size() > 30) {
final long mode = MathUtils.getMode(sensitivitySamples);
sensitivityMcp = AimbotUtil.SENSITIVITY_MAP.getOrDefault((int) mode, -1.0F);
}
}
}
lastLookX = lookX;
lastLookY = lookY;
lookX = getExperimentalDeltaX(player);
lookY = getExperimentalDeltaY(player);
lastLookX = lookX;
lastLookY = lookY;
lookX = getExperimentalDeltaX(player);
lookY = getExperimentalDeltaY(player);
}
} else {
yawGcdList.clear();
pitchGcdList.clear();
}
}
if (packet.isOnGround()) {
player.getInfo().setWasOnSlime(player.getBlockInfo().onSlime);
groundTicks++;
airTicks = 0;
player.getInfo().groundJumpBoost = player.getPotionHandler().getEffectByType(PotionEffectType.JUMP);
} else {
airTicks++;
groundTicks = 0;
}
player.getInfo().setCreative(player.getBukkitPlayer().getGameMode() == GameMode.CREATIVE
|| player.getBukkitPlayer().getGameMode() == GameMode.SPECTATOR
|| player.getInfo().getPossibleCapabilities().stream()
.anyMatch(capability -> capability.canInstantlyBuild));
player.getInfo().setCanFly(player.getBukkitPlayer().getAllowFlight()
|| player.getInfo().getPossibleCapabilities().stream()
.anyMatch(capability -> capability.canFly));
boolean hasLevitation = ProtocolVersion.getGameVersion().isOrAbove(ProtocolVersion.V1_9)
&& player.getPotionHandler().hasPotionEffect(XPotion.LEVITATION.getPotionEffectType());
player.getInfo().setRiptiding(CompatHandler.getInstance().isRiptiding(player.getBukkitPlayer()));
player.getInfo().setGliding(CompatHandler.getInstance().isGliding(player.getBukkitPlayer()));
// Resetting glide/sneak timers
if (player.getInfo().isGliding()) player.getInfo().getLastElytra().reset();
if (player.getInfo().isSneaking()) player.getInfo().getLastSneak().reset();
if (player.getBlockInfo().inLiquid) player.getInfo().getLastLiquid().reset();
if (player.getBlockInfo().inWeb) player.getInfo().lastWeb.reset();
if (player.getBlockInfo().onHalfBlock) player.getInfo().getLastHalfBlock().reset();
if (player.getBlockInfo().fenceBelow) player.getInfo().getLastFence().reset();
if (!to.isOnGround() && moveTicks > 0) {
if (!jumped && from.isOnGround()
&& deltaY >= 0) {
jumped = true;
} else {
inAir = true;
jumped = false;
}
} else jumped = inAir = false;
player.getInfo().setGeneralCancel(player.getBukkitPlayer().getAllowFlight()
|| moveTicks == 0
|| excuseNextFlying
|| player.getBukkitPlayer().isFlying()
|| player.getInfo().isCanFly()
|| player.getInfo().isCreative()
|| player.getInfo().isInVehicle()
|| player.getInfo().getVehicleSwitch().isNotPassed(1)
|| player.getBukkitPlayer().isSleeping()
|| player.getInfo().isGliding()
|| player.getInfo().isRiptiding()
|| hasLevitation);
/*
ata.playerInfo.generalCancel = data.getPlayer().getAllowFlight()
|| this.creativelastLastY
|| hasLeviit
it
|| data.excuseNextFlying
|| data.getPlayer().isSleeping()
|| (this.lastGhostCollision.isNotPassed() && this.lastBlockPlace.isPassed(2))
|| this.doingTeleport
|| this.lastTeleportTimer.isNotPassed(1)
|| this.riptiding
|| this.gliding
|| this.vehicleTimer.isNotPassed(3)
|| this.lastPlaceLiquid.isNotPassed(5)
|| this.inVehicle
|| ((this.lastChunkUnloaded.isNotPassed(35) || this.doingBlockUpdate)
&& MathUtils.getDelta(-0.098, this.deltaY) < 0.0001)
|| timeStamp - this.lastRespawn < 2500L
|| this.lastToggleFlight.isNotPassed(40)
|| timeStamp - data.creation < 4000
|| Kauri.INSTANCE.lastTickLag.isNotPassed(5);
*/
}
// generate a method that processes velocityHistory and compares to current deltaY.
private void processVelocity() {
//Iterate through player.getInfo().getVelocityHistory() and compare to current deltaY.
synchronized (player.getInfo().getVelocityHistory()) {
val iterator = player.getInfo().getVelocityHistory().iterator();
while (iterator.hasNext()) {
val velocity = iterator.next();
if(Math.abs(velocity.getY() - getDeltaY()) < 0.01) {
player.getInfo().getVelocity().reset();
player.getInfo().setDoingVelocity(false);
player.getOnVelocityTasks().forEach(vectorConsumer -> vectorConsumer.accept(velocity));
iterator.remove();
break;
}
}
}
}
private static float getDeltaX(float yawDelta, float gcd) {
return MathHelper.ceiling_float_int(yawDelta / gcd);
}
private static float getDeltaY(float pitchDelta, float gcd) {
return MathHelper.ceiling_float_int(pitchDelta / gcd);
}
public void process() {
float yawAcelleration = Math.abs(getDeltaYaw());
float pitchAcelleration = Math.abs(getDeltaPitch());
// They are not rotating
if (yawAcelleration < 0.002 || pitchAcelleration < 0.002) return;
// Deltas between the current acelleration and last
double x = Math.abs(yawAcelleration - this.lastYawAcelleration);
double y = Math.abs(pitchAcelleration - this.lastPitchAcelleration);
// Deltas between last X & Y
double deltaX = Math.abs(x - this.lastX);
double deltaY = Math.abs(y - this.lastY);
// Pitch delta change
double pitchChangeAcelleration = Math.abs(this.lastLastY - deltaY);
this.inTick = false;
// we have to check something different for pitch due to it being a little harder to check for being smooth
if (x < .04 || y < .04
|| (pitchAcelleration > .08 && pitchChangeAcelleration > 0
&& !MathUtils.isScientificNotation(pitchChangeAcelleration)
&& pitchChangeAcelleration < .0855)) {
if (this.isInvalidGCD()) {
this.ticks += (this.ticks < 20 ? 1 : 0);
}
} else {
this.ticks -= this.ticks > 0 ? 1 : 0;
}
this.lastLastY = deltaY;
this.lastX = x;
this.lastY = y;
this.lastYawAcelleration = yawAcelleration;
this.lastPitchAcelleration = pitchAcelleration;
this.cinematic = this.ticks > 5;
if(cinematic) lastCinematic.reset();
}
boolean isInvalidGCD() {
return pitchGCD < 0.0078125;
}
public static float getExperimentalDeltaX(APlayer data) {
float deltaPitch = data.getMovement().getDeltaYaw();
float sens = data.getMovement().sensitivityX;
float f = sens * 0.6f + .2f;
float calc = f * f * f * 8;
return deltaPitch / (calc * .15f);
}
public float getExperimentalDelta(float deltaAngle) {
float sens = player.getMovement().sensitivityMcp;
float f = sens * 0.6f + .2f;
float calc = f * f * f * 8;
return deltaAngle / (calc * .15f);
}
public double[] getEyeHeights() {
if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_14)) {
return new double[]{0.4f, 1.27f, 1.62f};
} else if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
return new double[]{0.4f, 1.54f, 1.62f};
} else return new double[]{1.54f, 1.62f};
}
public static float getExperimentalDeltaY(APlayer data) {
float deltaPitch = data.getMovement().getDeltaPitch();
float sens = data.getMovement().sensitivityY;
float f = sens * 0.6f + .2f;
float calc = f * f * f * 8;
return deltaPitch / (calc * .15f);
}
public static int sensToPercent(float sensitivity) {
return MathHelper.floor_float(sensitivity / .5f * 100);
}
public static float percentToSens(int percent) {
return percent * .0070422534f;
}
public static float getSensitivityFromYawGCD(float gcd) {
return ((float) Math.cbrt(yawToF2(gcd) / 8f) - .2f) / .6f;
}
private static float getSensitivityFromPitchGCD(float gcd) {
return ((float) Math.cbrt(pitchToF3(gcd) / 8f) - .2f) / .6f;
}
private static float getF1FromYaw(float gcd) {
float f = getFFromYaw(gcd);
return f * f * f * 8;
}
private static float getFFromYaw(float gcd) {
float sens = getSensitivityFromYawGCD(gcd);
return sens * .6f + .2f;
}
private static float getFFromPitch(float gcd) {
float sens = getSensitivityFromPitchGCD(gcd);
return sens * .6f + .2f;
}
private static float getF1FromPitch(float gcd) {
float f = getFFromPitch(gcd);
return (float) Math.pow(f, 3) * 8;
}
private static float yawToF2(float yawDelta) {
return yawDelta / .15f;
}
private static float pitchToF3(float pitchDelta) {
int b0 = pitchDelta >= 0 ? 1 : -1; //Checking for inverted mouse.
return (pitchDelta / b0) / .15f;
}
public void addPosition(WPacketPlayOutPosition packet) {
int i = 0;
KLocation loc = new KLocation(packet.getX(), packet.getY(), packet.getZ(),
packet.getYaw(), packet.getPitch());
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X)) {
loc.x += player.getMovement().getTo().getLoc().x;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y)) {
loc.y += player.getMovement().getTo().getLoc().y;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Z)) {
loc.z += player.getMovement().getTo().getLoc().z;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT)) {
loc.pitch += player.getMovement().getTo().getLoc().pitch;
}
if (packet.getFlags().contains(WPacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT)) {
loc.yaw += player.getMovement().getTo().getLoc().yaw;
}
teleportsToConfirm++;
player.runKeepaliveAction(ka -> teleportsToConfirm--, 2);
synchronized (posLocs) {
posLocs.add(loc);
}
}
/**
* Updating the "to" and "from" location to current location.
* Resetting position tracking; meant primarily for instant teleports.
*
* @param location Location
*/
public void moveTo(Location location) {
to.getLoc().x = from.getLoc().x = location.getX();
to.getLoc().y = from.getLoc().y = location.getY();
to.getLoc().z = from.getLoc().z = location.getZ();
to.getLoc().yaw = from.getLoc().yaw = location.getYaw();
to.getLoc().pitch = from.getLoc().pitch = location.getPitch();
deltaX = deltaY = deltaZ = deltaXZ
= lDeltaX = lDeltaY = lDeltaZ
= lDeltaXZ = 0;
deltaYaw = lDeltaYaw =
deltaPitch = lDeltaPitch = 0;
moveTicks = 0;
//doingTeleport = inventoryOpen = false;
}
private void checkForTeleports(WPacketPlayInFlying packet) {
if (packet.isMoved() && packet.isLooked() && !packet.isOnGround()) {
synchronized (posLocs) {
Iterator<KLocation> iterator = posLocs.iterator();
//Iterating through the ArrayList to find a potential teleport. We can't remove from the list
//without causing a CME unless we use Iterator#remove().
while (iterator.hasNext()) {
KLocation posLoc = iterator.next();
KLocation to = new KLocation(packet.getX(), packet.getY(), packet.getZ());
double distance = MathUtils.getDistanceWithoutRoot(to, posLoc);
if (distance < 1E-9) {
lastTeleport.reset();
iterator.remove();
break;
}
}
//Ensuring the list doesn't overflow with old locations, a potential crash exploit.
if (teleportsToConfirm == 0 && posLocs.size() > 0) {
posLocs.clear();
}
}
}
}
/**
* Setting the to and from to current location only if the player either moved or looked.
*
* @param packet WPacketPlayInFlyingh
*/
private void setTo(WPacketPlayInFlying packet) {
to.setWorld(player.getBukkitPlayer().getWorld());
if (packet.isMoved()) {
to.getLoc().x = packet.getX();
to.getLoc().y = packet.getY();
to.getLoc().z = packet.getZ();
}
if (packet.isLooked()) {
to.getLoc().yaw = packet.getYaw();
to.getLoc().pitch = packet.getPitch();
}
to.setBox(new SimpleCollisionBox(to.getLoc(), 0.6, 1.8));
to.setOnGround(packet.isOnGround());
}
/**
* If from location is null, update to loc after to is set, otherwise, update to before from.
* Updates the location of player and its general delta movement.
*
* @param packet WPacketPlayInFlying
*/
private void updateLocations(WPacketPlayInFlying packet) {
from.setLoc(to);
setTo(packet);
lDeltaX = deltaX;
lDeltaY = deltaY;
lDeltaZ = deltaZ;
lDeltaXZ = deltaXZ;
lDeltaYaw = deltaYaw;
lDeltaPitch = deltaPitch;
deltaX = to.getLoc().x - from.getLoc().x;
deltaY = to.getLoc().y - from.getLoc().y;
deltaZ = to.getLoc().z - from.getLoc().z;
deltaXZ = Math.hypot(deltaX, deltaZ); // Calculating here to cache since hypot() can be heavy.
deltaYaw = to.getLoc().yaw - from.getLoc().yaw;
deltaPitch = to.getLoc().pitch - from.getLoc().pitch;
player.getInfo().setClientGroundTicks(packet.isOnGround() ? player.getInfo().getClientGroundTicks() + 1 : 0);
player.getInfo().setClientAirTicks(!packet.isOnGround() ? player.getInfo().getClientAirTicks() + 1 : 0);
}
}