diff --git a/API/src/main/java/dev/brighten/ac/api/check/CheckType.java b/API/src/main/java/dev/brighten/ac/api/check/CheckType.java index 16e2e77..a8cb470 100644 --- a/API/src/main/java/dev/brighten/ac/api/check/CheckType.java +++ b/API/src/main/java/dev/brighten/ac/api/check/CheckType.java @@ -9,6 +9,7 @@ public enum CheckType { CHAT, CRASH, EXPLOIT, + INVENTORY, INTERACT, ORDER, BADPACKETS diff --git a/pom.xml b/pom.xml index 23d31ef..1a420d6 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,12 @@ + + dev.brighten.ac + API + 1.0 + compile + org.projectlombok lombok diff --git a/src/main/java/dev/brighten/ac/Anticheat.java b/src/main/java/dev/brighten/ac/Anticheat.java index 857c589..68db541 100644 --- a/src/main/java/dev/brighten/ac/Anticheat.java +++ b/src/main/java/dev/brighten/ac/Anticheat.java @@ -114,16 +114,18 @@ public class Anticheat extends LoaderPlugin { getAnticheatConfig().set("database.password", UUID.randomUUID().toString()); } + this.keepaliveProcessor = new KeepaliveProcessor(); + this.fakeTracker = new FakeEntityTracker(); this.checkManager = new CheckManager(); this.playerRegistry = new PlayerRegistry(); + HandlerAbstract.init(); + Bukkit.getOnlinePlayers().forEach(playerRegistry::generate); this.packetHandler = new PacketHandler(); logManager = new LoggerManager(); keepaliveProcessor.start(); - HandlerAbstract.init(); - logManager.init(); alog(Color.Green + "Loading WorldInfo system..."); @@ -136,19 +138,12 @@ public class Anticheat extends LoaderPlugin { e.printStackTrace(); } - fakeTracker = new FakeEntityTracker(); - Bukkit.getOnlinePlayers().forEach(HandlerAbstract.getHandler()::add); } public void onDisable() { scheduler.shutdown(); commandManager.unregisterCommands(); - // Unregistering packet listeners for players - HandlerAbstract.getHandler().shutdown(); - HandlerList.unregisterAll(getPluginInstance()); - packetProcessor.shutdown(); - packetProcessor = null; checkManager.getCheckClasses().clear(); Check.alertsEnabled.clear(); Check.debugInstances.clear(); @@ -158,9 +153,6 @@ public class Anticheat extends LoaderPlugin { ProtocolAPI.INSTANCE = null; tps = null; - fakeTracker.despawnAll(); - fakeTracker = null; - logManager.shutDown(); Bukkit.getScheduler().cancelTasks(getPluginInstance()); @@ -176,6 +168,15 @@ public class Anticheat extends LoaderPlugin { throw new RuntimeException(e); } + fakeTracker.despawnAll(); + fakeTracker = null; + + // Unregistering packet listeners for players + HandlerAbstract.getHandler().shutdown(); + HandlerList.unregisterAll(getPluginInstance()); + packetProcessor.shutdown(); + packetProcessor = null; + AnticheatAPI.INSTANCE = null; onTickEnd.clear(); diff --git a/src/main/java/dev/brighten/ac/check/impl/combat/aim/Aim.java b/src/main/java/dev/brighten/ac/check/impl/combat/aim/Aim.java index 96bbb75..cc00360 100644 --- a/src/main/java/dev/brighten/ac/check/impl/combat/aim/Aim.java +++ b/src/main/java/dev/brighten/ac/check/impl/combat/aim/Aim.java @@ -49,8 +49,6 @@ public class Aim extends Check { return; } - boolean increasing = deltaYaw > deltaX || deltaPitch > deltaY; - boolean flagged = false; if(player.getMovement().getPitchGCD() < 0.007 && lastGrid.isPassed() && !player.getMovement().isCinematic() @@ -69,7 +67,7 @@ public class Aim extends Check { /* * This is an attempt to reverse the logistics of cinematic camera without having to run a full on prediction using * mouse filters. Otherwise, we would need to run more heavy calculations which is not really production friendly. - * It may be more accurate but it is not really worth it if in the end of the day we're eating server performance. + * It may be more accurate, but it is not really worth it if in the end of the day we're eating server performance. */ protected static double getGrid(final List entry) { /* diff --git a/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KABot.java b/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KABot.java index bc25c79..84fe950 100644 --- a/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KABot.java +++ b/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KABot.java @@ -5,6 +5,7 @@ import dev.brighten.ac.check.Check; import dev.brighten.ac.check.CheckData; import dev.brighten.ac.check.WAction; import dev.brighten.ac.data.APlayer; +import dev.brighten.ac.packet.wrapper.in.WPacketPlayInArmAnimation; import dev.brighten.ac.packet.wrapper.in.WPacketPlayInUseEntity; import lombok.val; @@ -16,6 +17,12 @@ public class KABot extends Check { } private int buffer = 0; + private float buffer2; + + WAction arm = packet -> { + // We want to go ahead and lower the buffer every time they miss the bot to help prevent false positives + if(buffer2 > 0) buffer2-= 0.25f; + }; WAction packet = packet -> { val optional = player.getEntityLocationHandler().getFakeMob(packet.getEntityId()); @@ -25,5 +32,12 @@ public class KABot extends Check { flag("Attacked player without attacking bot!"); } } else buffer = 0; + + if(player.getMob().getEntityId() == packet.getEntityId()) { + if(++buffer2 > 3) { + buffer = 2; + flag("Player attacked bot"); + } + } else buffer2 = 0; }; } diff --git a/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KATrace.java b/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KATrace.java index 0eb0c08..75c7833 100644 --- a/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KATrace.java +++ b/src/main/java/dev/brighten/ac/check/impl/combat/killaura/KATrace.java @@ -8,13 +8,18 @@ import dev.brighten.ac.data.APlayer; import dev.brighten.ac.packet.ProtocolVersion; import dev.brighten.ac.packet.wrapper.in.WPacketPlayInUseEntity; import dev.brighten.ac.utils.KLocation; -import dev.brighten.ac.utils.MathUtils; import dev.brighten.ac.utils.world.CollisionBox; import dev.brighten.ac.utils.world.EntityData; import dev.brighten.ac.utils.world.types.RayCollision; import dev.brighten.ac.utils.world.types.SimpleCollisionBox; import org.bukkit.util.Vector; +/** + * @author funkemunky + * @since 12/31/2020 + * This check is designed to detect an attack through solid blocks, which would be impossible under normal + * circumstances. + */ @CheckData(name = "KillAura (Trace)", checkId = "katrace", type = CheckType.KILLAURA) public class KATrace extends Check { @@ -22,6 +27,7 @@ public class KATrace extends Check { public KATrace(APlayer player) { super(player); + // We're caching the player's sneak height here, so we don't have to do it every time. sneakY = player.getPlayerVersion().isBelow(ProtocolVersion.V1_14) ? 1.27f : 1.54f; } @@ -32,33 +38,42 @@ public class KATrace extends Check { || packet.getAction() != WPacketPlayInUseEntity.EnumEntityUseAction.ATTACK) return; + // If the player isn't looking at the target, then a raytrace check wouldn't work. if(player.getMovement().getLookingAtBoxes().size() == 0) { debug("No boxes to look at!"); buffer = 0; return; } - SimpleCollisionBox targetBox = (SimpleCollisionBox) EntityData.getEntityBox(player.getInfo().getTarget().getLocation(), + // Getting the target's bounding box + SimpleCollisionBox targetBox = (SimpleCollisionBox) EntityData.getEntityBox(player.getInfo().getTarget() + .getLocation(), player.getInfo().target); if(targetBox == null) return; final KLocation origin = player.getMovement().getTo().getLoc().clone(); + // Setting the player's eye height based on their sneak status origin.y+= player.getInfo().isSneaking() ? sneakY : 1.62f; final Vector originVec = origin.toVector(); - RayCollision collision = new RayCollision(originVec, MathUtils.getDirection(origin)); + // Setting a trace based on their view direction + RayCollision collision = new RayCollision(originVec, origin.getDirection()); Vector targetPoint = collision.collisionPoint(targetBox); //If the ray isn't collided, we might as well not run this check. Just a simple boxes on array check if(targetPoint == null) return; + // The distance bteween the player's eye and the intersect point of the ray and the target's bounding box. + // We don't do the square root to save on performance. double dist = originVec.distanceSquared(targetPoint); boolean rayCollidedOnBlock = false; + // Grabbing the boxes found on the ray trace. This is already grabbed in MovementProcessor so we can use + // the result from there in this check to save on compute time. synchronized (player.getMovement().getLookingAtBoxes()) { for (CollisionBox lookingAtBox : player.getMovement().getLookingAtBoxes()) { if((lookingAtBox instanceof SimpleCollisionBox)) { @@ -67,6 +82,7 @@ public class KATrace extends Check { || box.maxX % 1 != 0 || box.maxY % 1 != 0 || box.maxZ % 1 != 0) continue; + // We want to shrink the box slightly since there is a bit of a margin of error in the ray trace. Vector point = collision.collisionPoint(box.copy().shrink(0.005f, 0.005f, 0.005f)); if (point != null && originVec.distanceSquared(point) < dist - 0.2) { @@ -78,6 +94,7 @@ public class KATrace extends Check { } if(rayCollidedOnBlock) { + // The buffer is here to smooth out false positives. if(++buffer > 2) { flag("Attacker hit through block! [b=%s s=%s]", buffer, player.getMovement().getLookingAtBoxes().size()); diff --git a/src/main/java/dev/brighten/ac/check/impl/misc/inventory/InventoryA.java b/src/main/java/dev/brighten/ac/check/impl/misc/inventory/InventoryA.java new file mode 100644 index 0000000..a4803c0 --- /dev/null +++ b/src/main/java/dev/brighten/ac/check/impl/misc/inventory/InventoryA.java @@ -0,0 +1,19 @@ +package dev.brighten.ac.check.impl.misc.inventory; + +import dev.brighten.ac.api.check.CheckType; +import dev.brighten.ac.check.Check; +import dev.brighten.ac.check.CheckData; +import dev.brighten.ac.data.APlayer; + +/** + * Check runs inside {@link dev.brighten.ac.check.impl.movement.speed.Horizontal} + */ +@CheckData(name = "Inventory (A)", checkId = "inventoryA", type = CheckType.INVENTORY) +public class InventoryA extends Check { + + public InventoryA(APlayer player) { + super(player); + } + + public int buffer; +} diff --git a/src/main/java/dev/brighten/ac/check/impl/misc/inventory/InventoryB.java b/src/main/java/dev/brighten/ac/check/impl/misc/inventory/InventoryB.java new file mode 100644 index 0000000..fbb34b7 --- /dev/null +++ b/src/main/java/dev/brighten/ac/check/impl/misc/inventory/InventoryB.java @@ -0,0 +1,24 @@ +package dev.brighten.ac.check.impl.misc.inventory; + +import dev.brighten.ac.api.check.CheckType; +import dev.brighten.ac.check.Check; +import dev.brighten.ac.check.CheckData; +import dev.brighten.ac.check.WAction; +import dev.brighten.ac.data.APlayer; +import dev.brighten.ac.packet.wrapper.in.WPacketPlayInWindowClick; + +@CheckData(name = "Inventory (B)", checkId = "inventoryB", type = CheckType.INVENTORY) +public class InventoryB extends Check { + + public InventoryB(APlayer player) { + super(player); + } + + WAction windowClick = packet -> { + if(!player.getInfo().isInventoryOpen()) { + flag("Inventory not open"); + } + debug("inv=%s", player.getInfo().isInventoryOpen()); + }; + +} diff --git a/src/main/java/dev/brighten/ac/check/impl/movement/fly/FlyA.java b/src/main/java/dev/brighten/ac/check/impl/movement/fly/FlyA.java index 551d8a4..9bfe632 100644 --- a/src/main/java/dev/brighten/ac/check/impl/movement/fly/FlyA.java +++ b/src/main/java/dev/brighten/ac/check/impl/movement/fly/FlyA.java @@ -36,10 +36,30 @@ public class FlyA extends Check { return; } + // This stuff will false flag the detection and cause a buffer decrease, so we're just going to prevent + // the check from processing to save resources. + if(player.getInfo().isGeneralCancel() + || player.getMovement().getTeleportsToConfirm() > 0 + || player.getInfo().isOnLadder() + || player.getInfo().climbTimer.isNotPassed(2) + || player.getBlockInfo().inWeb + || player.getBlockInfo().inScaffolding + || player.getInfo().getLastLiquid().isNotPassed(2) + || player.getBlockInfo().fenceBelow + || !player.getInfo().worldLoaded + || player.getBlockInfo().onHalfBlock + || player.getInfo().getVelocity().isNotPassed(1) + || player.getBlockInfo().onSlime) { + if(buffer > 0) buffer-= 0.25f; + return; + } + boolean onGround = player.getMovement().getTo().isOnGround() && player.getBlockInfo().blocksBelow, fromGround = player.getMovement().getFrom().isOnGround(); - double lDeltaY = fromGround ? 0 : player.getMovement().getLDeltaY(); - double predicted = onGround ? lDeltaY : (lDeltaY - 0.08) * mult; + double lDeltaY = player.getMovement().getLDeltaY(); + + // Initial acceleration prediction the vanilla client does + double predicted = (lDeltaY - 0.08) * mult; if(fromGround && !onGround && player.getMovement().getDeltaY() > 0) { predicted = MovementUtils.getJumpHeight(player); @@ -50,6 +70,9 @@ public class FlyA extends Check { boolean willBeWeirdNext = didNextPrediction; didNextPrediction = false; + // Since the player skipped a flying packet, the client likely didn't send a small position update + // This is to go ahead and account for that just in case the >60ms delta is caused by a < 9.0E-4 small movement + // on all axis. See net.minecraft.client.entity.EntityPlayerSP#onUpdateWalkingPlayer method if(lastPos.isPassed(60L)) { double toCheck = (predicted - 0.08) * mult; @@ -60,6 +83,7 @@ public class FlyA extends Check { } } + // Vanilla wrapping the deltaY to 0 if it's less than a certain amount. if(player.getPlayerVersion().isBelow(ProtocolVersion.V1_9)) { if(Math.abs(predicted) < 0.005) predicted = 0; @@ -68,6 +92,8 @@ public class FlyA extends Check { debug("Setting y to 0"); } + // Vanilla collision algorithm to correct any false positives related to modified deltaY related to ground + // collision. if(player.getBlockInfo().blocksBelow || player.getInfo().isNearGround()) { List list = Helper.getCollisions(player, player.getMovement().getFrom().getBox().copy().addCoord(player.getMovement().getDeltaX(), predicted, @@ -90,20 +116,13 @@ public class FlyA extends Check { double deltaPredict = MathUtils.getDelta(player.getMovement().getDeltaY(), predicted); - if(!player.getInfo().isGeneralCancel() - && player.getMovement().getTeleportsToConfirm() == 0 - && !player.getInfo().isOnLadder() - && player.getInfo().climbTimer.isPassed(2) - && !player.getBlockInfo().inWeb - && !willBeWeirdNext - && !player.getBlockInfo().inScaffolding - && player.getInfo().getLastLiquid().isPassed(2) - && !player.getBlockInfo().fenceBelow - && player.getInfo().worldLoaded - && !player.getBlockInfo().onHalfBlock - && player.getInfo().getVelocity().isPassed(1) - && !player.getBlockInfo().onSlime - && deltaPredict > (player.getInfo().getClientAirTicks() < 3 ? 0.017 : 0.001)) { + // We want it to be at 0.005 since that is the maximum variance from loss of precision + // We also don't really want to flag if there was a skip in flying related to < 9.0E-4 movement. + if(!willBeWeirdNext + && deltaPredict > 0.005) { + // This slight buffer is to account for anything this check may have not accounted for + // It is not a permanent fix. However, Java Edition is quite a fickle game to work with; it's quite + // difficult to reliably check that vanilla behavior is vanilla behavior without weird outliers. if(++buffer > 1) { buffer = 1; flag("dY=%.3f p=%.3f dx=%.3f", player.getMovement().getDeltaY(), predicted, @@ -112,8 +131,10 @@ public class FlyA extends Check { } } else buffer-= buffer > 0 ? 0.25f : 0; - debug("dY=%.3f p=%.3f dx=%.3f b=%s velocity=%s", player.getMovement().getDeltaY(), predicted, - player.getMovement().getDeltaXZ(), buffer, player.getInfo().getVelocity().getPassed()); + debug("dY=%.3f p=%.3f dx=%.3f b=%s velocity=%s g=%s bbelow=%s ng=%s", + player.getMovement().getDeltaY(), predicted, player.getMovement().getDeltaXZ(), buffer, + player.getInfo().getVelocity().getPassed(), packet.isOnGround(), player.getBlockInfo().blocksBelow, + player.getInfo().isNearGround()); lastPos.reset(); }; diff --git a/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java b/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java index d19e164..9b607c0 100644 --- a/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java +++ b/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java @@ -4,6 +4,7 @@ import dev.brighten.ac.api.check.CheckType; import dev.brighten.ac.check.Check; import dev.brighten.ac.check.CheckData; import dev.brighten.ac.check.WAction; +import dev.brighten.ac.check.impl.misc.inventory.InventoryA; import dev.brighten.ac.data.APlayer; import dev.brighten.ac.packet.ProtocolVersion; import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying; @@ -21,6 +22,7 @@ import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.List; +import java.util.Optional; @CheckData(name = "Horizontal", checkId = "horizontala", type = CheckType.MOVEMENT, experimental = true) public class Horizontal extends Check { @@ -31,7 +33,7 @@ public class Horizontal extends Check { private int lastFlying; private KLocation previousFrom; - private Vector motionX = new Vector(0, 0, 0); + private Vector motion = new Vector(0, 0, 0); private static final boolean[] TRUE_FALSE = new boolean[]{true, false}; public double strafe, forward; @@ -44,6 +46,12 @@ public class Horizontal extends Check { check: { + if(!packet.isMoved() || (player.getMovement().getDeltaXZ() == 0)) { + forward = strafe = 0; + } + + Optional inventoryA = find(InventoryA.class); + if (!packet.isMoved() || player.getMovement().getMoveTicks() == 0 || player.getMovement().getLastTeleport().isNotPassed(1) @@ -54,8 +62,12 @@ public class Horizontal extends Check { || player.getMovement().getTo().getLoc() .distanceSquared(player.getMovement().getFrom().getLoc()) > 2500 || player.getInfo().lastLiquid.isNotPassed(2)) { - motionX = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), + motion = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), player.getMovement().getDeltaZ()); + inventoryA.ifPresent(check -> { + if(check.buffer > 0) + check.buffer--; + }); break check; } @@ -70,6 +82,9 @@ public class Horizontal extends Check { double precision = getPrecision(); TagsBuilder tags = null; + val speed = player.getPotionHandler().getEffectByType(PotionEffectType.SPEED); + val slow = player.getPotionHandler().getEffectByType(PotionEffectType.SLOW); + for (Iteration it : iterations) { TagsBuilder tagsBuilder = new TagsBuilder(); @@ -100,9 +115,17 @@ public class Horizontal extends Check { double aiMoveSpeed = player.getBukkitPlayer().getWalkSpeed() / 2; float drag = 0.91f; - double lmotionX = motionX.getX(), - lmotionY = motionX.getY(), - lmotionZ = motionX.getZ(); + double lmotionX = motion.getX(), + lmotionY = motion.getY(), + lmotionZ = motion.getZ(); + + double totalMotion = Math.abs(lmotionX) + Math.abs(lmotionY) + Math.abs(lmotionZ); + + if(totalMotion > 30) { + motion = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), + player.getMovement().getDeltaZ()); + return; + } lmotionY -= 0.08; lmotionY *= 0.98f; @@ -152,17 +175,12 @@ public class Horizontal extends Check { tagsBuilder.addTag("sprinting"); } - if (player.getPotionHandler().hasPotionEffect(PotionEffectType.SPEED)) { - aiMoveSpeed += (player.getPotionHandler().getEffectByType(PotionEffectType.SPEED) - .map(pe -> pe.getAmplifier() + 1)).orElse(0) - * 0.20000000298023224D * aiMoveSpeed; - + if (speed.isPresent()) { + aiMoveSpeed += (speed.get().getAmplifier() + 1) * (double) 0.20000000298023224D * aiMoveSpeed; tagsBuilder.addTag("speedPotion"); } - if (player.getPotionHandler().hasPotionEffect(PotionEffectType.SLOW)) { - aiMoveSpeed += (player.getPotionHandler().getEffectByType(PotionEffectType.SLOW) - .map(pe -> pe.getAmplifier() + 1)).orElse(0) - * -0.15000000596046448D * aiMoveSpeed; + if (slow.isPresent()) { + aiMoveSpeed += (slow.get().getAmplifier() + 1) * (double) -0.15000000596046448D * aiMoveSpeed; tagsBuilder.addTag("slowPotion"); } @@ -439,17 +457,11 @@ public class Horizontal extends Check { double delta = (diffX * diffX) + (diffZ * diffZ); double deltaAll = delta + (diffY * diffY); - if(forward > 0 && strafe == 0 && !it.attack && !it.sneaking && !it.using && it.sprinting - && !it.edge && !it.jumped) { - debug("Shit: tags=[%s] diffX=%.6f diffZ=%.6f oy=%.3f ly=%.3f", tagsBuilder.build(), diffX, diffZ, - originalY, lmotionY); - } - if (delta < smallestDeltaXZ) { - smallDelta = deltaAll; + //smallDelta = deltaAll; smallestDeltaXZ = delta; predictedMotionX = lmotionX; - predictedMotionY = lmotionY; + //predictedMotionY = lmotionY; predictedMotionZ = lmotionZ; tags = tagsBuilder; @@ -459,9 +471,9 @@ public class Horizontal extends Check { this.forward = it.f * 0.98f; if (precision < 1E-11) - motionX = new Vector(lmotionX, lmotionY, lmotionZ); + motion = new Vector(lmotionX, lmotionY, lmotionZ); else - motionX = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), player.getMovement().getDeltaZ()); + motion = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), player.getMovement().getDeltaZ()); if (player.getInfo().getLastCancel().isPassed(2)) player.getInfo() @@ -476,10 +488,24 @@ public class Horizontal extends Check { } } } + + // Inventory (A) check + inventoryA.ifPresent(check -> { + + if((strafe != 0 || forward != 0) && player.getInfo().isInventoryOpen()) { + if(check.buffer++ > 6) { + check.buffer = Math.min(8, check.buffer); + check.flag("s=%.2f f=%.2f", strafe, forward); + } + } else if(check.buffer > 0) check.buffer--; + + check.debug("buffer=%d inv=%s s=%.2f f=%.2f", check.buffer, + player.getInfo().isInventoryOpen(), strafe, forward); + }); iterations.clear(); if (!found) { - motionX = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), player.getMovement().getDeltaZ()); + motion = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), player.getMovement().getDeltaZ()); } final String builtTags = tags == null ? "null" : tags.build(); @@ -489,17 +515,15 @@ public class Horizontal extends Check { && player.getMovement().getDeltaXZ() > 0.1) { if ((buffer += smallestDeltaXZ > 58E-5 ? 1 : 0.5) > 1) { buffer = Math.min(3.5f, buffer); //Ensuring we don't have a run-away buffer - flag("smallest=%s b=%.1f to=%s dxz=%.2f tags=[%s]", smallestDeltaXZ, buffer, - player.getMovement().getTo().getLoc(), player.getMovement().getDeltaXZ(), builtTags); + flag("smallest=%.7f b=%.1f dxz=%.2f tags=[%s]", smallestDeltaXZ, buffer, + player.getMovement().getDeltaXZ(), builtTags); cancel(); } else debug("bad movement"); } else if (buffer > 0) buffer -= 0.05f; - /*debug("(%s): py=%.5f dy=%.5f pm=%.5f dxz=%.5f skip=%s b=%.1f tags=[%s]", - precision, predictedMotionY, player.getMovement().getDeltaY(), pmotion, - player.getMovement().getDeltaXZ(), maybeSkippedPos, buffer, builtTags);*/ - debug("x=%s z=%s tags=[%s]", predictedMotionX, predictedMotionZ, builtTags); + debug("[%.1f] smallest=%.7f dxz=%.2f tags=[%s]", buffer, smallestDeltaXZ, + player.getMovement().getDeltaXZ(), builtTags); } if (ProtocolVersion.getGameVersion().isBelow(ProtocolVersion.V1_9)) { @@ -586,7 +610,7 @@ public class Horizontal extends Check { } private static boolean[] getJumpingIteration(boolean onGround) { - return onGround ? new boolean[]{true, false} : new boolean[]{false}; + return new boolean[]{true, false}; } private static final float[] SIN_TABLE_FAST = new float[4096], SIN_TABLE_FAST_NEW = new float[4096]; diff --git a/src/main/java/dev/brighten/ac/check/impl/movement/velocity/VelocityB.java b/src/main/java/dev/brighten/ac/check/impl/movement/velocity/VelocityB.java index 16145b6..0efbe9f 100644 --- a/src/main/java/dev/brighten/ac/check/impl/movement/velocity/VelocityB.java +++ b/src/main/java/dev/brighten/ac/check/impl/movement/velocity/VelocityB.java @@ -12,11 +12,15 @@ import dev.brighten.ac.utils.MathHelper; import dev.brighten.ac.utils.math.IntVector; import dev.brighten.ac.utils.timer.Timer; import dev.brighten.ac.utils.timer.impl.TickTimer; +import lombok.AllArgsConstructor; import lombok.val; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers; import org.bukkit.potion.PotionEffectType; +import java.util.ArrayList; +import java.util.List; + @CheckData(name = "Velocity (Horizontal)", checkId = "velocityb", type = CheckType.MOVEMENT) public class VelocityB extends Check { private Timer lastVelocity = new TickTimer(); @@ -37,7 +41,6 @@ public class VelocityB extends Check { } private double pvX, pvZ; - private boolean useEntity, sprint; private int ticks; private double buffer; private float friction; @@ -74,155 +77,128 @@ public class VelocityB extends Check { double pmotionx = 0, pmotionz = 0; boolean onGround = player.getMovement().getFrom().isOnGround(); - loop: - { - for (int f = -1; f < 2; f++) { - for (int s = -1; s < 2; s++) { - for (boolean sprinting : TRUE_FALSE) { - for (int fastMath = 0; fastMath <= 2; fastMath++) { - for (boolean attack : TRUE_FALSE) { - for (boolean using : TRUE_FALSE) { - for (boolean sneaking : TRUE_FALSE) { - for (boolean jumped : TRUE_FALSE) { + val speed = player.getPotionHandler().getEffectByType(PotionEffectType.SPEED); + val slow = player.getPotionHandler().getEffectByType(PotionEffectType.SLOW); - float forward = f, strafe = s; + for (Iteration iteration : iterations) { + float forward = iteration.f, strafe = iteration.s; - if (sprinting && forward <= 0) { - continue; - } + if (iteration.sneaking) { + forward *= 0.3; + strafe *= 0.3; + } - if (sneaking) { - forward *= 0.3; - strafe *= 0.3; - } + float friction = CraftMagicNumbers.getBlock(underMaterial).frictionFactor; - float friction = CraftMagicNumbers.getBlock(underMaterial).frictionFactor; + if (iteration.using) { + forward *= 0.2; + strafe *= 0.2; + } - if (using) { - forward *= 0.2; - strafe *= 0.2; - } + //Multiplying by 0.98 like in client + forward *= 0.9800000190734863F; + strafe *= 0.9800000190734863F; - //Multiplying by 0.98 like in client - forward *= 0.9800000190734863F; - strafe *= 0.9800000190734863F; + double aiMoveSpeed = player.getBukkitPlayer().getWalkSpeed() / 2; - double aiMoveSpeed = player.getBukkitPlayer().getWalkSpeed() / 2; + float drag = 0.91f; + double lmotionX = pvX, + lmotionZ = pvZ; - float drag = 0.91f; - double lmotionX = pvX, - lmotionZ = pvZ; + //Running multiplication done after previous prediction + if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) { + if (Math.abs(lmotionX) < 0.003) + lmotionX = 0; + if (Math.abs(lmotionZ) < 0.003) + lmotionZ = 0; + } else { + if (Math.abs(lmotionX) < 0.005) + lmotionX = 0; + if (Math.abs(lmotionZ) < 0.005) + lmotionZ = 0; + } - //lmotionX *= (lastLastClientGround ? lfriction : 1) * 0.9100000262260437D; - //lmotionZ *= (lastLastClientGround ? lfriction : 1) * 0.9100000262260437D; + //Less than 0.05 + if (((lmotionX * lmotionX) + (lmotionZ * lmotionZ)) < 0.0025 && player.getMovement().getDeltaXZ() < 0.2) { + break check; + } + // Attack slowdown + if (iteration.attack) { + lmotionX *= 0.6; + lmotionZ *= 0.6; + } - //Running multiplication done after previous prediction - if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) { - if (Math.abs(lmotionX) < 0.003) - lmotionX = 0; - if (Math.abs(lmotionZ) < 0.003) - lmotionZ = 0; - } else { - if (Math.abs(lmotionX) < 0.005) - lmotionX = 0; - if (Math.abs(lmotionZ) < 0.005) - lmotionZ = 0; - } + if (iteration.sprinting) + aiMoveSpeed += aiMoveSpeed * 0.30000001192092896D; - //Less than 0.05 - if (((lmotionX * lmotionX) + (lmotionZ * lmotionZ)) < 0.0025 && player.getMovement().getDeltaXZ() < 0.2) { - break check; - } - // Attack slowdown - if (attack) { - lmotionX *= 0.6; - lmotionZ *= 0.6; - } + if (speed.isPresent()) + aiMoveSpeed += (speed.get().getAmplifier() + 1) * (double) 0.20000000298023224D * aiMoveSpeed; + if (slow.isPresent()) + aiMoveSpeed += (slow.get().getAmplifier() + 1) * (double) -0.15000000596046448D * aiMoveSpeed; - if (sprinting) - aiMoveSpeed += aiMoveSpeed * 0.30000001192092896D; + float f5; + if (onGround) { + drag *= friction; - if (player.getPotionHandler().hasPotionEffect(PotionEffectType.SPEED)) - aiMoveSpeed += (player.getPotionHandler().getEffectByType(PotionEffectType.SPEED) - .get() - .getAmplifier() + 1) * (double) 0.20000000298023224D * aiMoveSpeed; - if (player.getPotionHandler().hasPotionEffect(PotionEffectType.SLOW)) - aiMoveSpeed += (player.getPotionHandler().getEffectByType(PotionEffectType.SLOW) - .get() - .getAmplifier() + 1) * (double) -0.15000000596046448D * aiMoveSpeed; + f5 = (float) (aiMoveSpeed * (0.16277136F / (drag * drag * drag))); - float f5; - if (onGround) { - drag *= friction; - - f5 = (float) (aiMoveSpeed * (0.16277136F / (drag * drag * drag))); - - if (sprinting && jumped) { - float rot = player.getMovement().getTo().getLoc().yaw * 0.017453292F; - lmotionX -= sin(fastMath, rot) * 0.2F; - lmotionZ += cos(fastMath, rot) * 0.2F; - } - - } else f5 = sprinting ? 0.025999999F : 0.02f; - - if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) { - double keyedMotion = forward * forward + strafe * strafe; - - if (keyedMotion >= 1.0E-4F) { - keyedMotion = f5 / Math.max(1.0, Math.sqrt(keyedMotion)); - forward *= keyedMotion; - strafe *= keyedMotion; - - final float yawSin = sin(fastMath, - player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F), - yawCos = cos(fastMath, - player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F); - - lmotionX += (strafe * yawCos - forward * yawSin); - lmotionZ += (forward * yawCos + strafe * yawSin); - } - } else { - float keyedMotion = forward * forward + strafe * strafe; - - if (keyedMotion >= 1.0E-4F) { - keyedMotion = f5 / Math.max(1.0f, MathHelper.sqrt_float(keyedMotion)); - forward *= keyedMotion; - strafe *= keyedMotion; - - final float yawSin = sin(fastMath, - player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F), - yawCos = cos(fastMath, - player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F); - - lmotionX += (strafe * yawCos - forward * yawSin); - lmotionZ += (forward * yawCos + strafe * yawSin); - } - } - double diffX = player.getMovement().getDeltaX() - lmotionX, - diffZ = player.getMovement().getDeltaZ() - lmotionZ; - double delta = (diffX * diffX) + (diffZ * diffZ); - - if (delta < smallestDelta) { - smallestDelta = delta; - pmotionx = lmotionX; - pmotionz = lmotionZ; - this.friction = friction; - - if (delta < 4E-17) { - this.strafe = s * 0.98f; - this.forward = f * 0.98f; - - break loop; - } - } - } - } - } - } - } - } + if (iteration.sprinting && iteration.jumped) { + float rot = player.getMovement().getTo().getLoc().yaw * 0.017453292F; + lmotionX -= sin(iteration.fastMath, rot) * 0.2F; + lmotionZ += cos(iteration.fastMath, rot) * 0.2F; } + } else f5 = iteration.sprinting ? 0.025999999F : 0.02f; + + if (player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) { + double keyedMotion = forward * forward + strafe * strafe; + + if (keyedMotion >= 1.0E-4F) { + keyedMotion = f5 / Math.max(1.0, Math.sqrt(keyedMotion)); + forward *= keyedMotion; + strafe *= keyedMotion; + + final float yawSin = sin(iteration.fastMath, + player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F), + yawCos = cos(iteration.fastMath, + player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F); + + lmotionX += (strafe * yawCos - forward * yawSin); + lmotionZ += (forward * yawCos + strafe * yawSin); + } + } else { + float keyedMotion = forward * forward + strafe * strafe; + + if (keyedMotion >= 1.0E-4F) { + keyedMotion = f5 / Math.max(1.0f, MathHelper.sqrt_float(keyedMotion)); + forward *= keyedMotion; + strafe *= keyedMotion; + + final float yawSin = sin(iteration.fastMath, + player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F), + yawCos = cos(iteration.fastMath, + player.getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F); + + lmotionX += (strafe * yawCos - forward * yawSin); + lmotionZ += (forward * yawCos + strafe * yawSin); + } + } + double diffX = player.getMovement().getDeltaX() - lmotionX, + diffZ = player.getMovement().getDeltaZ() - lmotionZ; + double delta = (diffX * diffX) + (diffZ * diffZ); + + if (delta < smallestDelta) { + smallestDelta = delta; + pmotionx = lmotionX; + pmotionz = lmotionZ; + this.friction = friction; + + if (delta < 4E-17) { + this.strafe = iteration.s * 0.98f; + this.forward = iteration.f * 0.98f; + + break; + } } } @@ -250,6 +226,48 @@ public class VelocityB extends Check { previousFrom = player.getMovement().getFrom().getLoc().clone(); }; + /* + for (int f = -1; f < 2; f++) { + for (int s = -1; s < 2; s++) { + for (boolean sprinting : TRUE_FALSE) { + for (int fastMath = 0; fastMath <= 2; fastMath++) { + for (boolean attack : TRUE_FALSE) { + for (boolean using : TRUE_FALSE) { + for (boolean sneaking : TRUE_FALSE) { + for (boolean jumped : TRUE_FALSE) { + */ + + @AllArgsConstructor + private static class Iteration { + final int f, s, fastMath; + final boolean sprinting, attack, using, sneaking, jumped; + } + + private static final List iterations = new ArrayList<>(); + + static { + for (int f = -1; f < 2; f++) { + for (int s = -1; s < 2; s++) { + for (boolean sprinting : TRUE_FALSE) { + for (int fastMath = 0; fastMath <= 2; fastMath++) { + for (boolean attack : TRUE_FALSE) { + for (boolean using : TRUE_FALSE) { + for (boolean sneaking : TRUE_FALSE) { + for (boolean jumped : TRUE_FALSE) { + if(sprinting && f <= 0) continue; + if(jumped && !sprinting) continue; + iterations.add(new Iteration(f, s, fastMath, sprinting, + attack, using, sneaking, jumped)); + } + } + } + } + } + } + } + } + } + private static final float[] SIN_TABLE_FAST = new float[4096], SIN_TABLE_FAST_NEW = new float[4096]; private static final float[] SIN_TABLE = new float[65536]; private static final float radToIndex = roundToFloat(651.8986469044033D); diff --git a/src/main/java/dev/brighten/ac/command/AnticheatCommand.java b/src/main/java/dev/brighten/ac/command/AnticheatCommand.java index fe05389..25b864f 100644 --- a/src/main/java/dev/brighten/ac/command/AnticheatCommand.java +++ b/src/main/java/dev/brighten/ac/command/AnticheatCommand.java @@ -2,20 +2,15 @@ package dev.brighten.ac.command; import co.aikar.commands.*; import co.aikar.commands.annotation.*; -import co.aikar.commands.annotation.Optional; import co.aikar.commands.bukkit.contexts.OnlinePlayer; import dev.brighten.ac.Anticheat; import dev.brighten.ac.check.Check; import dev.brighten.ac.check.CheckData; import dev.brighten.ac.data.APlayer; -import dev.brighten.ac.gui.Logs; import dev.brighten.ac.handler.BBRevealHandler; import dev.brighten.ac.messages.Messages; import dev.brighten.ac.packet.handler.HandlerAbstract; -import dev.brighten.ac.utils.Color; -import dev.brighten.ac.utils.MiscUtils; -import dev.brighten.ac.utils.Pastebin; -import dev.brighten.ac.utils.Tuple; +import dev.brighten.ac.utils.*; import dev.brighten.ac.utils.annotation.Init; import dev.brighten.ac.utils.msg.ChatBuilder; import dev.brighten.ac.utils.reflections.Reflections; @@ -39,13 +34,13 @@ import org.bukkit.plugin.PluginDescriptionFile; import java.io.*; import java.nio.ByteBuffer; -import java.sql.Timestamp; -import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.stream.Collectors; import java.util.zip.CRC32; -@Init +@Init(priority = Priority.LOW) @CommandAlias("kauri|anticheat|ac") @CommandPermission("anticheat.command") public class AnticheatCommand extends BaseCommand { @@ -216,78 +211,6 @@ public class AnticheatCommand extends BaseCommand { "You've been given a very special wand. Handle it responsibly.").color(ChatColor.GREEN).create()); } - @Subcommand("logs") - @Syntax("[player] [check]") - @CommandCompletion("@players @checkIds") - @CommandPermission("anticheat.command.logs") - @Description("Get player logs") - public void onLogs(CommandSender sender, @Single String playername, - @Single @Optional @Default("none") String check) { - UUID uuid = Bukkit.getOfflinePlayer(playername).getUniqueId(); - - sender.sendMessage(Color.Red + "Getting logs for " + playername + "..."); - - if(sender instanceof Player) { - if(check.equals("none")) { - Logs logs = new Logs(uuid); - - logs.showMenu((Player) sender); - } else { - Logs logs = new Logs(uuid, check); - - logs.showMenu((Player) sender); - } - } else { - List logs = new ArrayList<>(); - - if(check.equals("none")) { - Anticheat.INSTANCE.getLogManager().getLogs(uuid, logsList -> { - logsList.forEach(log -> { - logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime() - .format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed " - + Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) + "(VL: " - + log.getVl() + ") {" + log.getData() + "}"); - }); - if(logs.size() == 0) { - sender.sendMessage(Color.Gray + "There are no logs for player \"" + playername + "\""); - } else { - String url = null; - try { - url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs", - Pastebin.Privacy.UNLISTED); - - sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - }); - } else { - Anticheat.INSTANCE.getLogManager().getLogs(uuid, check, logsList -> { - logsList.forEach(log -> { - logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime() - .format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed " - + Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) - + "(VL: " + log.getVl() + ") {" + log.getData() + "}"); - }); - if(logs.size() == 0) { - sender.sendMessage(Color.Gray + " does not have any violations for check \"" + check + "\""); - } else { - String url = null; - try { - url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs", - Pastebin.Privacy.UNLISTED); - - sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - }); - } - } - } - @Subcommand("title") @Private public void onTitle(CommandSender sender, OnlinePlayer target, String title) { diff --git a/src/main/java/dev/brighten/ac/command/LogsCommand.java b/src/main/java/dev/brighten/ac/command/LogsCommand.java new file mode 100644 index 0000000..6663bd7 --- /dev/null +++ b/src/main/java/dev/brighten/ac/command/LogsCommand.java @@ -0,0 +1,240 @@ +package dev.brighten.ac.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.*; +import co.aikar.commands.annotation.Optional; +import dev.brighten.ac.Anticheat; +import dev.brighten.ac.check.CheckSettings; +import dev.brighten.ac.gui.Logs; +import dev.brighten.ac.logging.Log; +import dev.brighten.ac.utils.Color; +import dev.brighten.ac.utils.Pastebin; +import dev.brighten.ac.utils.Priority; +import dev.brighten.ac.utils.annotation.Init; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.*; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.time.format.DateTimeFormatter; +import java.util.*; + +@Init(priority = Priority.LOW) +@CommandAlias("kauri|anticheat|ac") +@CommandPermission("anticheat.command") +public class LogsCommand extends BaseCommand { + + @Subcommand("logs") + @Syntax("[player] [check]") + @CommandCompletion("@players @checkIds") + @CommandPermission("anticheat.command.logs") + @Description("Get player logs") + public void onLogs(CommandSender sender, @Single String playername, + @Single @Optional @Default("none") String check) { + UUID uuid = Bukkit.getOfflinePlayer(playername).getUniqueId(); + + sender.sendMessage(Color.Red + "Getting logs for " + playername + "..."); + + if(sender instanceof Player) { + if(check.equals("none")) { + Logs logs = new Logs(uuid); + + logs.showMenu((Player) sender); + } else { + Logs logs = new Logs(uuid, check); + + logs.showMenu((Player) sender); + } + } else { + List logs = new ArrayList<>(); + + if(check.equals("none")) { + Anticheat.INSTANCE.getLogManager().getLogs(uuid, logsList -> { + logsList.forEach(log -> { + logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime() + .format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed " + + Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) + "(VL: " + + log.getVl() + ") {" + log.getData() + "}"); + }); + if(logs.size() == 0) { + sender.sendMessage(Color.Gray + "There are no logs for player \"" + playername + "\""); + } else { + String url = null; + try { + url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs", + Pastebin.Privacy.UNLISTED); + + sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + }); + } else { + Anticheat.INSTANCE.getLogManager().getLogs(uuid, check, logsList -> { + logsList.forEach(log -> { + logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime() + .format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed " + + Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) + + "(VL: " + log.getVl() + ") {" + log.getData() + "}"); + }); + if(logs.size() == 0) { + sender.sendMessage(Color.Gray + " does not have any violations for check \"" + check + "\""); + } else { + String url = null; + try { + url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs", + Pastebin.Privacy.UNLISTED); + + sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + }); + } + } + } + + @Subcommand("logs paste") + @CommandPermission("kauri.command.logs") + @Syntax("[player]") + @CommandCompletion("@players") + @Description("View logs via Pastebin") + public void onLogsPasteBin(CommandSender sender, String[] args) { + if(args.length == 0) { + sender.sendMessage(Color.Red + "Usage: /kauri logs web "); + return; + } + + String playername = args[0]; + + UUID uuid = Bukkit.getOfflinePlayer(playername).getUniqueId(); + + sender.sendMessage(Color.Red + "Getting logs for " + playername + "..."); + + if(sender instanceof Player) { + Logs logs = new Logs(uuid); + + logs.showMenu((Player) sender); + } else { + List logs = new ArrayList<>(); + + Anticheat.INSTANCE.getLogManager().getLogs(uuid, logsList -> { + logsList.forEach(log -> { + logs.add("[" + new Timestamp(log.getTime()).toLocalDateTime() + .format(DateTimeFormatter.ISO_DATE_TIME) + "] funkemunky failed " + + Anticheat.INSTANCE.getCheckManager().getIdToName().get(log.getCheckId()) + "(VL: " + + log.getVl() + ") {" + log.getData() + "}"); + }); + if(logs.size() == 0) { + sender.sendMessage(Color.Gray + "There are no logs for player \"" + playername + "\""); + } else { + String url = null; + try { + url = Pastebin.makePaste(String.join("\n", logs), playername + "'s Logs", + Pastebin.Privacy.UNLISTED); + + sender.sendMessage(Color.Green + "Logs for " + playername + ": " + Color.White + url); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + }); + } + } + + @Subcommand("logs web") + @CommandPermission("kauri.command.logs") + @Syntax("[player]") + @CommandCompletion("@players") + @Description("View the logs of a user") + public void onLogsWeb(CommandSender sender, String[] args) { + if(args.length == 0) { + if(sender instanceof Player) { + Player player = (Player) sender; + runWebLog(sender, player); + } else sender.sendMessage(Color.translate("You cannot view your own logs from console.")); + } else { + OfflinePlayer player = Bukkit.getOfflinePlayer(args[0]); + + if(player == null) { + sender.sendMessage(Color.translate("&cSomehow, out of hundreds of millions of" + + "Minecraft accounts, you found one that doesn't exist.")); + return; + } + + runWebLog(sender, player); + } + } + + private void runWebLog(CommandSender sender, OfflinePlayer target) { + //val logs = Kauri.INSTANCE.loggerManager.getLogs(target.getUniqueId()); + Anticheat.INSTANCE.getLogManager().getLogs(target.getUniqueId(), 100000, 0, logs -> { + Map violations = new HashMap<>(); + for (Log log : logs) { + violations.compute(log.getCheckId(), (name, count) -> { + if(count == null) { + return 1; + } + + return count + 1; + }); + } + + + StringBuilder url = new StringBuilder("https://funkemunky.cc/api/kauri?uuid=" + target.getUniqueId().toString().replaceAll("-", "") + (violations.keySet().size() > 0 ? "&violations=" : "")); + + if (violations.keySet().size() > 0) { + for (String key : violations.keySet()) { + if (Anticheat.INSTANCE.getCheckManager().isCheck(key)) { + CheckSettings checkData = Anticheat.INSTANCE.getCheckManager().getCheckSettings(Anticheat.INSTANCE.getCheckManager().getCheckClasses() + .get(Anticheat.INSTANCE.getCheckManager().getIdToName().get(key)).getCheckClass().getParent()); + int vl = violations.get(key), maxVL = checkData.getPunishVl(); + boolean developer = false; + + String toAppend = key + ":" + vl + ":" + maxVL + ":" + developer + ";"; + toAppend = toAppend.replaceAll(" ", "%20"); + + url.append(toAppend); + + } + } + + if (violations.keySet().size() > 0) { + url.deleteCharAt(url.length() - 1); + } + + String finalURL = "https://funkemunky.cc/api/kauri/cache/%id%"; + + try { + URL url2Run = new URL(url.toString()); + //%3F + BufferedReader reader = new BufferedReader(new InputStreamReader(url2Run + .openConnection().getInputStream(), StandardCharsets.UTF_8)); + + finalURL = finalURL.replace("%id%", readAll(reader)); + } catch (IOException e) { + e.printStackTrace(); + } + + sender.sendMessage(Color.translate("&aView the log here&7: &f" + finalURL)); + } else { + sender.sendMessage(Color.translate("&cPlayer has no logs.")); + } + }); + } + + private static String readAll(Reader rd) throws IOException { + StringBuilder sb = new StringBuilder(); + int cp; + while ((cp = rd.read()) != -1) { + sb.append((char) cp); + } + return sb.toString(); + } +} diff --git a/src/main/java/dev/brighten/ac/data/APlayer.java b/src/main/java/dev/brighten/ac/data/APlayer.java index 333ca42..4b752cc 100644 --- a/src/main/java/dev/brighten/ac/data/APlayer.java +++ b/src/main/java/dev/brighten/ac/data/APlayer.java @@ -13,12 +13,14 @@ import dev.brighten.ac.handler.MovementHandler; import dev.brighten.ac.handler.PotionHandler; import dev.brighten.ac.handler.VelocityHandler; import dev.brighten.ac.handler.block.BlockUpdateHandler; +import dev.brighten.ac.handler.entity.FakeMob; import dev.brighten.ac.handler.keepalive.KeepAlive; import dev.brighten.ac.handler.protocolsupport.ProtocolAPI; import dev.brighten.ac.messages.Messages; import dev.brighten.ac.packet.ProtocolVersion; import dev.brighten.ac.packet.handler.HandlerAbstract; import dev.brighten.ac.packet.wrapper.WPacket; +import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject; import dev.brighten.ac.utils.KLocation; import dev.brighten.ac.utils.RunUtils; import dev.brighten.ac.utils.Tuple; @@ -26,10 +28,14 @@ import dev.brighten.ac.utils.objects.evicting.EvictingList; import dev.brighten.ac.utils.reflections.impl.MinecraftReflection; import dev.brighten.ac.utils.timer.Timer; import dev.brighten.ac.utils.timer.impl.MillisTimer; +import dev.brighten.ac.utils.world.types.RayCollision; import lombok.Getter; import lombok.Setter; import lombok.val; import net.minecraft.server.v1_8_R3.PacketPlayOutTransaction; +import org.bukkit.Achievement; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.util.Vector; @@ -89,6 +95,8 @@ public class APlayer { @Getter private final List> onVelocityTasks = new ArrayList<>(); public final EvictingList> pastLocations = new EvictingList<>(20); + @Getter + private FakeMob mob; @Setter @Getter @@ -124,18 +132,38 @@ public class APlayer { }); }); + // Removing inventory achievement + getBukkitPlayer().removeAchievement(Achievement.OPEN_INVENTORY); + // Enabling alerts for players on join if they have the permissions to if(getBukkitPlayer().hasPermission("anticheat.command.alerts") || getBukkitPlayer().hasPermission("anticheat.alerts")) { Check.alertsEnabled.add(getUuid()); getBukkitPlayer().spigot().sendMessage(Messages.ALERTS_ON); } + + generateEntities(); + } + + private void generateEntities() { + mob = new FakeMob(EntityType.MAGMA_CUBE); + + KLocation origin = getMovement().getTo().getLoc().clone().add(0, 1.7, 0); + + RayCollision coll = new RayCollision(origin.toVector(), origin.getDirection().multiply(-1)); + + Location loc1 = coll.collisionPoint(2).toLocation(getBukkitPlayer().getWorld()); + + mob.spawn(true, loc1, + new ArrayList<>(Collections.singletonList( + new WrappedWatchableObject(0, 16, (byte) 1))), this); } protected void unload() { this.info = null; this.lagInfo = null; this.movement = null; + mob.despawn(); } diff --git a/src/main/java/dev/brighten/ac/data/PlayerRegistry.java b/src/main/java/dev/brighten/ac/data/PlayerRegistry.java index 8ebbd62..07f089b 100644 --- a/src/main/java/dev/brighten/ac/data/PlayerRegistry.java +++ b/src/main/java/dev/brighten/ac/data/PlayerRegistry.java @@ -10,7 +10,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongList; import lombok.SneakyThrows; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.Plugin; @@ -26,7 +25,6 @@ import java.util.zip.CRC32; public class PlayerRegistry { public PlayerRegistry() { - Bukkit.getOnlinePlayers().forEach(this::generate); checkIntegrity(); } public final Int2ObjectMap aplayerMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); diff --git a/src/main/java/dev/brighten/ac/data/info/GeneralInformation.java b/src/main/java/dev/brighten/ac/data/info/GeneralInformation.java index 218fcd9..8eac56b 100644 --- a/src/main/java/dev/brighten/ac/data/info/GeneralInformation.java +++ b/src/main/java/dev/brighten/ac/data/info/GeneralInformation.java @@ -31,11 +31,13 @@ public class GeneralInformation { lastRespawn = new TickTimer(), lastEntityCollision = new TickTimer(), lastWeb = new TickTimer(), lastLiquid = new TickTimer(), lastBlockDig = new TickTimer(), lastBlockPlace = new TickTimer(), lastBlockUpdate = new TickTimer(), lastMiscNear = new TickTimer(), lastHalfBlock = new TickTimer(), - lastFence = new TickTimer(), lastFakeBotHit = new TickTimer(); + lastFence = new TickTimer(), lastFakeBotHit = new TickTimer(), lastInventoryOpen = new TickTimer(), + botAttack = new TickTimer(); public LivingEntity target; public Optional groundJumpBoost; public boolean serverGround, lastServerGround, canFly, nearGround, worldLoaded, generalCancel, inVehicle, creative, - sneaking, lsneaking, sprinting, gliding, riptiding, wasOnSlime, onLadder, doingVelocity, breakingBlock; + sneaking, lsneaking, sprinting, gliding, riptiding, wasOnSlime, onLadder, doingVelocity, breakingBlock, + inventoryOpen; public List nearbyEntities = Collections.emptyList(); public PastLocation targetPastLocation = new PastLocation(); public KLocation lastKnownGoodPosition; diff --git a/src/main/java/dev/brighten/ac/handler/EntityLocationHandler.java b/src/main/java/dev/brighten/ac/handler/EntityLocationHandler.java index 5fe05e6..4751f2c 100644 --- a/src/main/java/dev/brighten/ac/handler/EntityLocationHandler.java +++ b/src/main/java/dev/brighten/ac/handler/EntityLocationHandler.java @@ -265,8 +265,10 @@ public class EntityLocationHandler { for (double offset : offsets) { FakeMob mob = new FakeMob(EntityType.MAGMA_CUBE); + // Setting Magma cube size to size 10 mob.spawn(true, location.clone().add(offset, offset, offset), - new ArrayList<>(Arrays.asList(new WrappedWatchableObject(0, 16, (byte)10))), data); + new ArrayList<>(Collections.singletonList( + new WrappedWatchableObject(0, 16, (byte) 10))), data); fakeMobToEntityId.put(mob.getEntityId(), entityId); diff --git a/src/main/java/dev/brighten/ac/handler/MovementHandler.java b/src/main/java/dev/brighten/ac/handler/MovementHandler.java index 02af16b..5a5532a 100644 --- a/src/main/java/dev/brighten/ac/handler/MovementHandler.java +++ b/src/main/java/dev/brighten/ac/handler/MovementHandler.java @@ -300,6 +300,8 @@ public class MovementHandler { lastFlying.reset(); + processBotMove(packet); + /* ata.playerInfo.generalCancel = data.getPlayer().getAllowFlight() || this.creativelastLastY @@ -342,6 +344,22 @@ it } } + private void processBotMove(WPacketPlayInFlying packet) { + if(packet.isMoved() || packet.isLooked()) { + KLocation origin = to.getLoc().clone().add(0, 1.7, 0); + + RayCollision coll = new RayCollision(origin.toVector(), origin.getDirection().multiply(-1)); + + Location loc1 = coll.collisionPoint(2.2).toLocation(player.getBukkitPlayer().getWorld()); + + if(player.getInfo().botAttack.isNotPassed(7)) { + loc1.setY(Math.max(origin.y + 1.2, loc1.getY())); + } else loc1.setY(Math.max(origin.y + 0.3, loc1.getY())); + + player.getMob().teleport(loc1.getX(), loc1.getY(), loc1.getZ(), loc1.getYaw(), loc1.getPitch()); + } + } + private static float getDeltaX(float yawDelta, float gcd) { return MathHelper.ceiling_float_int(yawDelta / gcd); } diff --git a/src/main/java/dev/brighten/ac/handler/PacketHandler.java b/src/main/java/dev/brighten/ac/handler/PacketHandler.java index 090d4a5..de714b5 100644 --- a/src/main/java/dev/brighten/ac/handler/PacketHandler.java +++ b/src/main/java/dev/brighten/ac/handler/PacketHandler.java @@ -294,6 +294,9 @@ public class PacketHandler { player.getEntityLocationHandler().removeFakeMob(targetId); player.getInfo().lastFakeBotHit.reset(); }); + if(player.getMob().getEntityId() == packet.getEntityId()) { + player.getInfo().botAttack.reset(); + } } else { Entity target = packet.getEntity(player.getBukkitPlayer().getWorld()); @@ -338,6 +341,32 @@ public class PacketHandler { player.getBlockUpdateHandler().onDig(packet); break; } + case CLIENT_COMMAND: { + WPacketPlayInClientCommand packet = (WPacketPlayInClientCommand) packetObject; + + if(packet.getCommand() == WPacketPlayInClientCommand.WrappedEnumClientCommand + .OPEN_INVENTORY_ACHIEVEMENT) { + player.getInfo().setInventoryOpen(true); + player.getInfo().lastInventoryOpen.reset(); + return true; + } + break; + } + case CLIENT_CLOSE_WINDOW: { + player.getInfo().setInventoryOpen(false); + break; + } + case SERVER_CLOSE_WINDOW: { + player.runKeepaliveAction(ka -> player.getInfo().setInventoryOpen(false)); + break; + } + case SERVER_OPEN_WINDOW: { + player.runKeepaliveAction(ka -> { + player.getInfo().setInventoryOpen(true); + player.getInfo().lastInventoryOpen.reset(); + }); + break; + } } if(player.sniffing) { diff --git a/src/main/java/dev/brighten/ac/listener/GeneralListener.java b/src/main/java/dev/brighten/ac/listener/GeneralListener.java index b0db79f..daccc1a 100644 --- a/src/main/java/dev/brighten/ac/listener/GeneralListener.java +++ b/src/main/java/dev/brighten/ac/listener/GeneralListener.java @@ -2,7 +2,11 @@ package dev.brighten.ac.listener; import dev.brighten.ac.Anticheat; import dev.brighten.ac.data.APlayer; +import dev.brighten.ac.packet.wrapper.objects.WrappedWatchableObject; +import dev.brighten.ac.utils.RunUtils; import dev.brighten.ac.utils.annotation.Init; +import dev.brighten.ac.utils.world.types.RayCollision; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -14,6 +18,9 @@ import org.bukkit.event.player.PlayerEditBookEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import java.util.ArrayList; +import java.util.Collections; + @Init public class GeneralListener implements Listener { @@ -50,7 +57,23 @@ public class GeneralListener implements Listener { if(event.getFrom().getWorld().equals(event.getTo().getWorld())) return; Anticheat.INSTANCE.getPlayerRegistry().getPlayer(event.getPlayer().getUniqueId()) - .ifPresent(player -> player.getBlockUpdateHandler().onWorldChange()); + .ifPresent(player -> { + player.getBlockUpdateHandler().onWorldChange(); + + // Updating bot loc when changing worlds + Location origin = event.getTo().clone().add(0, 1.7, 0); + + RayCollision coll = new RayCollision(origin.toVector(), origin.getDirection().multiply(-1)); + + Location loc1 = coll.collisionPoint(1.2).toLocation(event.getTo().getWorld()); + + RunUtils.taskLater(() -> { + player.getMob().despawn(); + player.getMob().spawn(true, loc1, + new ArrayList<>(Collections.singletonList( + new WrappedWatchableObject(0, 16, (byte) 1))), player); + }, 5); + }); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) diff --git a/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java b/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java index 02b4abe..887d390 100644 --- a/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java +++ b/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java @@ -133,6 +133,7 @@ public class PacketProcessor { public Object call(Player player, Object packet, PacketType type) { if(packet == null) return false; + PacketInfo info = new PacketInfo(player, packet, type, System.currentTimeMillis()), asyncInfo; boolean cancelled = false; diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java b/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java index 9e7c033..04bc8fc 100644 --- a/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java +++ b/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java @@ -90,4 +90,8 @@ public interface PacketConverter { WPacketPlayOutMapChunk processMapChunk(Object object); WPacketPlayOutMapChunkBulk processMapChunkBulk(Object packet); + + WPacketPlayInClientCommand processInClientCommand(Object packet); + + WPacketPlayInWindowClick processInWindowClick(Object packet); } diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java b/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java index 88769a9..62384f0 100644 --- a/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java +++ b/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java @@ -25,7 +25,7 @@ public enum PacketType { BLOCK_DIG("PacketPlayInBlockDig"), BLOCK_PLACE("PacketPlayInBlockPlace"), CHAT("PacketPlayInChat"), - CLIENT_COMMAND("PacketPlayInCommand"), + CLIENT_COMMAND("PacketPlayInClientCommand"), CLIENT_CLOSE_WINDOW("PacketPlayInCloseWindow"), ENTITY_ACTION("PacketPlayInEntityAction"), ENTITY_EFFECT("PacketPlayOutEntityEffect"), @@ -153,6 +153,10 @@ public enum PacketType { return convert.processMapChunk(object); case MAP_CHUNK_BULK: return convert.processMapChunkBulk(object); + case CLIENT_COMMAND: + return convert.processInClientCommand(object); + case WINDOW_CLICK: + return convert.processInWindowClick(object); default: return object; } diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java b/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java index c08cfb1..bfb22e0 100644 --- a/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java +++ b/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java @@ -793,6 +793,26 @@ public class Processor_18 implements PacketConverter { return new WPacketPlayOutMapChunkBulk(chunks); } + @Override + public WPacketPlayInClientCommand processInClientCommand(Object packet) { + PacketPlayInClientCommand clientCommand = (PacketPlayInClientCommand) packet; + + return new WPacketPlayInClientCommand(WPacketPlayInClientCommand + .WrappedEnumClientCommand.valueOf(clientCommand.a().name())); + } + + @Override + public WPacketPlayInWindowClick processInWindowClick(Object packet) { + PacketPlayInWindowClick windowClick = (PacketPlayInWindowClick) packet; + + + return WPacketPlayInWindowClick.builder().windowId(windowClick.a()) + .slot(windowClick.b()).button(windowClick.c()) + .action(windowClick.d()).mode(windowClick.f()) + .clickedItem(windowClick.e() == null ? null : CraftItemStack.asBukkitCopy(windowClick.e())) + .build(); + } + private static void processChunk(byte[] locs, int size, int chunkX, int chunkZ, boolean groundUp, Map blocks) { ChunkSection[] sections = new ChunkSection[16]; diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInWindowClick.java b/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInWindowClick.java new file mode 100644 index 0000000..78173bd --- /dev/null +++ b/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInWindowClick.java @@ -0,0 +1,26 @@ +package dev.brighten.ac.packet.wrapper.in; + +import dev.brighten.ac.packet.wrapper.PacketType; +import dev.brighten.ac.packet.wrapper.WPacket; +import lombok.Builder; +import lombok.Getter; +import org.bukkit.inventory.ItemStack; + +@Getter +@Builder +public class WPacketPlayInWindowClick extends WPacket { + + private int windowId, slot, button, mode; + private short action; + private ItemStack clickedItem; + + @Override + public PacketType getPacketType() { + return PacketType.WINDOW_CLICK; + } + + @Override + public Object getPacket() { + return null; + } +} diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/out/WPacketPlayInClientCommand.java b/src/main/java/dev/brighten/ac/packet/wrapper/out/WPacketPlayInClientCommand.java new file mode 100644 index 0000000..d638792 --- /dev/null +++ b/src/main/java/dev/brighten/ac/packet/wrapper/out/WPacketPlayInClientCommand.java @@ -0,0 +1,29 @@ +package dev.brighten.ac.packet.wrapper.out; + +import dev.brighten.ac.packet.wrapper.PacketType; +import dev.brighten.ac.packet.wrapper.WPacket; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class WPacketPlayInClientCommand extends WPacket { + + private WrappedEnumClientCommand command; + + @Override + public PacketType getPacketType() { + return PacketType.CLIENT_COMMAND; + } + + @Override + public Object getPacket() { + return null; + } + + public enum WrappedEnumClientCommand { + PERFORM_RESPAWN, + REQUEST_STATS, + OPEN_INVENTORY_ACHIEVEMENT; + } +} diff --git a/src/main/java/dev/brighten/ac/utils/Helper.java b/src/main/java/dev/brighten/ac/utils/Helper.java index 69b9607..2dfbfbe 100644 --- a/src/main/java/dev/brighten/ac/utils/Helper.java +++ b/src/main/java/dev/brighten/ac/utils/Helper.java @@ -22,347 +22,379 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; +/** + * @author DeprecatedLuke + * Taken from https://github.com/DeprecatedLuke/fireflyx/blob/master/src/main/java/com/ngxdev/anticheat/utils/Helper.java + */ public class Helper { - public static int angularDistance(double alpha, double beta) { - while (alpha < 0) alpha += 360; - while (beta < 0) beta += 360; - double phi = Math.abs(beta - alpha) % 360; - return (int) (phi > 180 ? 360 - phi : phi); - } + public static Vector vector(double yaw, double pitch) { + Vector vector = new Vector(); + vector.setY(-Math.sin(Math.toRadians(pitch))); + double xz = Math.cos(Math.toRadians(pitch)); + vector.setX(-xz * Math.sin(Math.toRadians(yaw))); + vector.setZ(xz * Math.cos(Math.toRadians(yaw))); + return vector; + } - public static Vector vector(double yaw, double pitch) { - Vector vector = new Vector(); - vector.setY(-Math.sin(Math.toRadians(pitch))); - double xz = Math.cos(Math.toRadians(pitch)); - vector.setX(-xz * Math.sin(Math.toRadians(yaw))); - vector.setZ(xz * Math.cos(Math.toRadians(yaw))); - return vector; - } + public static void drawRay(RayCollision collision, double distance, EnumParticle particle, Collection players) { + for (double i = 0; i < distance; i += 0.2) { + float fx = (float) (collision.originX + (collision.directionX * i)); + float fy = (float) (collision.originY + (collision.directionY * i)); + float fz = (float) (collision.originZ + (collision.directionZ * i)); - public static void drawRay(RayCollision collision, double distance, EnumParticle particle, Collection players) { - for (double i = 0; i < 3; i += 0.2) { - float fx = (float) (collision.originX + (collision.directionX * i)); - float fy = (float) (collision.originY + (collision.directionY * i)); - float fz = (float) (collision.originZ + (collision.directionZ * i)); - - WPacketPlayOutWorldParticles packet = WPacketPlayOutWorldParticles.builder() - .particle(particle) - .x(fx) - .y(fy) - .z(fz) - .offsetX(0) - .offsetY(0) - .offsetZ(0) - .speed(0) - .amount(1) - .longD(true) - .data(new int[0]) - .build(); - players.forEach(p -> HandlerAbstract.getHandler().sendPacketSilently(p, packet)); - } - } + WPacketPlayOutWorldParticles packet = WPacketPlayOutWorldParticles.builder() + .particle(particle) + .x(fx) + .y(fy) + .z(fz) + .offsetX(0) + .offsetY(0) + .offsetZ(0) + .speed(0) + .amount(1) + .longD(true) + .data(new int[0]) + .build(); + players.forEach(p -> HandlerAbstract.getHandler().sendPacketSilently(p, packet)); + } + } - public static void drawCuboid(SimpleCollisionBox box, EnumParticle particle, Collection players) { - Step.GenericStepper x = Step.step((float)box.minX, 0.241F, (float)box.maxX); - Step.GenericStepper y = Step.step((float)box.minY, 0.241F, (float)box.maxY); - Step.GenericStepper z = Step.step((float)box.minZ, 0.241F, (float)box.maxZ); - Iterator var6 = x.iterator(); + public static void drawCuboid(SimpleCollisionBox box, EnumParticle particle, Collection players) { + Step.GenericStepper x = Step.step((float) box.minX, 0.241F, (float) box.maxX); + Step.GenericStepper y = Step.step((float) box.minY, 0.241F, (float) box.maxY); + Step.GenericStepper z = Step.step((float) box.minZ, 0.241F, (float) box.maxZ); + Iterator var6 = x.iterator(); - while(var6.hasNext()) { - float fx = (Float)var6.next(); - Iterator var8 = y.iterator(); + while (var6.hasNext()) { + float fx = (Float) var6.next(); + Iterator var8 = y.iterator(); - label61: - while(var8.hasNext()) { - float fy = (Float)var8.next(); - Iterator var10 = z.iterator(); + label61: + while (var8.hasNext()) { + float fy = (Float) var8.next(); + Iterator var10 = z.iterator(); - while(true) { - float fz; - int check; - do { - if (!var10.hasNext()) { - continue label61; - } + while (true) { + float fz; + int check; + do { + if (!var10.hasNext()) { + continue label61; + } - fz = (Float)var10.next(); - check = 0; - if (x.first() || x.last()) { - ++check; - } + fz = (Float) var10.next(); + check = 0; + if (x.first() || x.last()) { + ++check; + } - if (y.first() || y.last()) { - ++check; - } + if (y.first() || y.last()) { + ++check; + } - if (z.first() || z.last()) { - ++check; - } - } while(check < 2); + if (z.first() || z.last()) { + ++check; + } + } while (check < 2); - WPacketPlayOutWorldParticles packet = WPacketPlayOutWorldParticles.builder() - .particle(particle) - .x(fx) - .y(fy) - .z(fz) - .offsetX(0) - .offsetY(0) - .offsetZ(0) - .speed(0) - .amount(1) - .data(new int[0]) - .build(); + WPacketPlayOutWorldParticles packet = WPacketPlayOutWorldParticles.builder() + .particle(particle) + .x(fx) + .y(fy) + .z(fz) + .offsetX(0) + .offsetY(0) + .offsetZ(0) + .speed(0) + .amount(1) + .data(new int[0]) + .build(); - Iterator var14 = players.iterator(); + Iterator var14 = players.iterator(); - while(var14.hasNext()) { - Player p = var14.next(); - HandlerAbstract.getHandler().sendPacketSilently(p, packet); - } - } - } - } + while (var14.hasNext()) { + Player p = var14.next(); + HandlerAbstract.getHandler().sendPacketSilently(p, packet); + } + } + } + } - } + } - public static void drawPoint(Vector point, EnumParticle particle, Collection players) { - WPacketPlayOutWorldParticles packet = WPacketPlayOutWorldParticles.builder() - .particle(particle) - .x((float)point.getX()) - .y((float)point.getY()) - .z((float)point.getZ()) - .offsetX(0) - .offsetY(0) - .offsetZ(0) - .speed(0) - .amount(1) - .data(new int[0]) - .build(); - Iterator var4 = players.iterator(); + public static void drawPoint(Vector point, EnumParticle particle, Collection players) { + WPacketPlayOutWorldParticles packet = WPacketPlayOutWorldParticles.builder() + .particle(particle) + .x((float) point.getX()) + .y((float) point.getY()) + .z((float) point.getZ()) + .offsetX(0) + .offsetY(0) + .offsetZ(0) + .speed(0) + .amount(1) + .data(new int[0]) + .build(); + Iterator var4 = players.iterator(); - while(var4.hasNext()) { - Player p = var4.next(); - HandlerAbstract.getHandler().sendPacketSilently(p, packet); - } + while (var4.hasNext()) { + Player p = var4.next(); + HandlerAbstract.getHandler().sendPacketSilently(p, packet); + } - } + } - public static Block getBlockAt(World world, int x, int y, int z) { - return world.isChunkLoaded(x >> 4, z >> 4) - ? world.getChunkAt(x >> 4, z >> 4).getBlock(x & 15, y, z & 15) - : null; - } + public static Block getBlockAt(World world, int x, int y, int z) { + return world.isChunkLoaded(x >> 4, z >> 4) + ? world.getChunkAt(x >> 4, z >> 4).getBlock(x & 15, y, z & 15) + : null; + } - public static SimpleCollisionBox wrap(SimpleCollisionBox a, SimpleCollisionBox b) { - double minX = Math.min(a.minX, b.minX); - double minY = Math.min(a.minY, b.minY); - double minZ = Math.min(a.minZ, b.minZ); - double maxX = Math.max(a.maxX, b.maxX); - double maxY = Math.max(a.maxY, b.maxY); - double maxZ = Math.max(a.maxZ, b.maxZ); - return new SimpleCollisionBox(minX, minY, minZ, maxX, maxY, maxZ); - } + public static SimpleCollisionBox wrap(SimpleCollisionBox a, SimpleCollisionBox b) { + double minX = Math.min(a.minX, b.minX); + double minY = Math.min(a.minY, b.minY); + double minZ = Math.min(a.minZ, b.minZ); + double maxX = Math.max(a.maxX, b.maxX); + double maxY = Math.max(a.maxY, b.maxY); + double maxZ = Math.max(a.maxZ, b.maxZ); + return new SimpleCollisionBox(minX, minY, minZ, maxX, maxY, maxZ); + } - public static SimpleCollisionBox wrap(List box) { - if (!box.isEmpty()) { - SimpleCollisionBox wrap = box.get(0).copy(); - for (int i = 1; i < box.size(); i++) { - SimpleCollisionBox a = box.get(i); - if (wrap.minX > a.minX) wrap.minX = a.minX; - if (wrap.minY > a.minY) wrap.minY = a.minY; - if (wrap.minZ > a.minZ) wrap.minZ = a.minZ; - if (wrap.maxX < a.maxX) wrap.maxX = a.maxX; - if (wrap.maxY < a.maxY) wrap.maxY = a.maxY; - if (wrap.maxZ < a.maxZ) wrap.maxZ = a.maxZ; - } - return wrap; - } - return null; - } + public static SimpleCollisionBox wrap(List box) { + if (!box.isEmpty()) { + SimpleCollisionBox wrap = box.get(0).copy(); + for (int i = 1; i < box.size(); i++) { + SimpleCollisionBox a = box.get(i); + if (wrap.minX > a.minX) wrap.minX = a.minX; + if (wrap.minY > a.minY) wrap.minY = a.minY; + if (wrap.minZ > a.minZ) wrap.minZ = a.minZ; + if (wrap.maxX < a.maxX) wrap.maxX = a.maxX; + if (wrap.maxY < a.maxY) wrap.maxY = a.maxY; + if (wrap.maxZ < a.maxZ) wrap.maxZ = a.maxZ; + } + return wrap; + } + return null; + } - public static List blockCollisions(List blocks, CollisionBox box) { - return blocks.stream() - .filter(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()).isCollided(box)) - .collect(Collectors.toCollection(LinkedList::new)); - } + public static List blockCollisions(List blocks, CollisionBox box) { + return blocks.stream() + .filter(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()).isCollided(box)) + .collect(Collectors.toCollection(LinkedList::new)); + } - public static boolean isCollided(SimpleCollisionBox toCheck, CollisionBox other) { - List downcasted = new ArrayList<>(); + public static boolean isCollided(SimpleCollisionBox toCheck, CollisionBox other) { + List downcasted = new ArrayList<>(); - other.downCast(downcasted); + other.downCast(downcasted); - return downcasted.stream().anyMatch(box -> box.maxX >= toCheck.minX && box.minX <= toCheck.maxX - && box.maxY >= toCheck.minY && box.minY <= toCheck.maxY && box.maxZ >= toCheck.minZ - && box.minZ <= toCheck.maxZ); - } + return downcasted.stream().anyMatch(box -> box.maxX >= toCheck.minX && box.minX <= toCheck.maxX + && box.maxY >= toCheck.minY && box.minY <= toCheck.maxY && box.maxZ >= toCheck.minZ + && box.minZ <= toCheck.maxZ); + } - public static List blockCollisions(List blocks, CollisionBox box, int material) { - return blocks.stream().filter(b -> Materials.checkFlag(b.getType(), material)) - .filter(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()).isCollided(box)) - .collect(Collectors.toCollection(LinkedList::new)); - } + public static List blockCollisions(List blocks, CollisionBox box, int material) { + return blocks.stream().filter(b -> Materials.checkFlag(b.getType(), material)) + .filter(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()).isCollided(box)) + .collect(Collectors.toCollection(LinkedList::new)); + } - public static List collisions(List boxes, CollisionBox box) { - return boxes.stream().filter(b -> b.isCollided(box)) - .collect(Collectors.toCollection(LinkedList::new)); - } + public static List collisions(List boxes, CollisionBox box) { + return boxes.stream().filter(b -> b.isCollided(box)) + .collect(Collectors.toCollection(LinkedList::new)); + } - public static List getCollisions(World world, SimpleCollisionBox collisionBox, int mask) { - int x1 = (int) Math.floor(collisionBox.minX); - int y1 = (int) Math.floor(collisionBox.minY); - int z1 = (int) Math.floor(collisionBox.minZ); - int x2 = (int) Math.floor(collisionBox.maxX + 1); - int y2 = (int) Math.floor(collisionBox.maxY + 1); - int z2 = (int) Math.floor(collisionBox.maxZ + 1); - List collisionBoxes = new ArrayList<>(); - Block block; - for (int x = x1; x < x2; ++x) - for (int y = y1 - 1; y < y2; ++y) - for (int z = z1; z < z2; ++z) - if ((block = getBlockAt(world, x, y, z)) != null - && BlockUtils.getXMaterial(block.getType()) != XMaterial.AIR) - if (Materials.checkFlag(block.getType(),mask)) { - CollisionBox box = BlockData.getData(block.getType()) - .getBox(block, ProtocolVersion.getGameVersion()); + public static List getCollisions(World world, SimpleCollisionBox collisionBox, int mask) { + int x1 = (int) Math.floor(collisionBox.minX); + int y1 = (int) Math.floor(collisionBox.minY); + int z1 = (int) Math.floor(collisionBox.minZ); + int x2 = (int) Math.floor(collisionBox.maxX + 1); + int y2 = (int) Math.floor(collisionBox.maxY + 1); + int z2 = (int) Math.floor(collisionBox.maxZ + 1); + List collisionBoxes = new ArrayList<>(); + Block block; + for (int x = x1; x < x2; ++x) + for (int y = y1 - 1; y < y2; ++y) + for (int z = z1; z < z2; ++z) + if ((block = getBlockAt(world, x, y, z)) != null + && BlockUtils.getXMaterial(block.getType()) != XMaterial.AIR) + if (Materials.checkFlag(block.getType(), mask)) { + CollisionBox box = BlockData.getData(block.getType()) + .getBox(block, ProtocolVersion.getGameVersion()); - if(box.isIntersected(collisionBox)) { - box.downCast(collisionBoxes); - } - } - return collisionBoxes; - } + if (box.isIntersected(collisionBox)) { + box.downCast(collisionBoxes); + } + } + return collisionBoxes; + } - public static List getCollisions(APlayer player, SimpleCollisionBox collisionBox) { - return getCollisions(player, collisionBox, Materials.COLLIDABLE); - } + public static List getCollisions(APlayer player, SimpleCollisionBox collisionBox) { + return getCollisions(player, collisionBox, Materials.COLLIDABLE); + } - public static SimpleCollisionBox getEntityCollision(Entity entity) { - return new SimpleCollisionBox((Object)MinecraftReflection.getEntityBoundingBox(entity)); - } + public static SimpleCollisionBox getEntityCollision(Entity entity) { + return new SimpleCollisionBox((Object) MinecraftReflection.getEntityBoundingBox(entity)); + } - public static List getCollisions(APlayer player, SimpleCollisionBox collisionBox, int mask) { - int x1 = (int) Math.floor(collisionBox.minX); - int y1 = (int) Math.floor(collisionBox.minY); - int z1 = (int) Math.floor(collisionBox.minZ); - int x2 = (int) Math.floor(collisionBox.maxX + 1); - int y2 = (int) Math.floor(collisionBox.maxY + 1); - int z2 = (int) Math.floor(collisionBox.maxZ + 1); - List collisionBoxes = new ArrayList<>(); - for (int x = x1; x < x2; ++x) - for (int y = y1 - 1; y < y2; ++y) - for (int z = z1; z < z2; ++z) { - IntVector vec = new IntVector(x, y, z); - Material type = player.getBlockUpdateHandler().getBlock(vec).getType(); + public static List getCollisions(APlayer player, SimpleCollisionBox collisionBox, int mask) { + int x1 = (int) Math.floor(collisionBox.minX); + int y1 = (int) Math.floor(collisionBox.minY); + int z1 = (int) Math.floor(collisionBox.minZ); + int x2 = (int) Math.floor(collisionBox.maxX + 1); + int y2 = (int) Math.floor(collisionBox.maxY + 1); + int z2 = (int) Math.floor(collisionBox.maxZ + 1); + List collisionBoxes = new ArrayList<>(); + for (int x = x1; x < x2; ++x) + for (int y = y1 - 1; y < y2; ++y) + for (int z = z1; z < z2; ++z) { + IntVector vec = new IntVector(x, y, z); + Material type = player.getBlockUpdateHandler().getBlock(vec).getType(); - if(type != Material.AIR && Materials.checkFlag(type, mask)) { - CollisionBox box = BlockData.getData(type) - .getBox(player, vec, ProtocolVersion.getGameVersion()); + if (type != Material.AIR && Materials.checkFlag(type, mask)) { + CollisionBox box = BlockData.getData(type) + .getBox(player, vec, ProtocolVersion.getGameVersion()); - if(box.isIntersected(collisionBox)) { - box.downCast(collisionBoxes); - } - } - } + if (box.isIntersected(collisionBox)) { + box.downCast(collisionBoxes); + } + } + } - for (Entity entity : player.getInfo().getNearbyEntities()) { - if(!BlockUtils.isEntityCollidable(entity)) continue; + for (Entity entity : player.getInfo().getNearbyEntities()) { + if (!BlockUtils.isEntityCollidable(entity)) continue; - SimpleCollisionBox entityCollisionBox = new SimpleCollisionBox(ReflectionsUtil.getBoundingBox(entity)); + SimpleCollisionBox entityCollisionBox = new SimpleCollisionBox(ReflectionsUtil.getBoundingBox(entity)); - if(entityCollisionBox.isIntersected(collisionBox)) - entityCollisionBox.downCast(collisionBoxes); - } + if (entityCollisionBox.isIntersected(collisionBox)) + entityCollisionBox.downCast(collisionBoxes); + } - return collisionBoxes; - } + return collisionBoxes; + } - public static List getCollisionsNoEntities(APlayer player, - SimpleCollisionBox collisionBox, int mask) { - int x1 = (int) Math.floor(collisionBox.minX); - int y1 = (int) Math.floor(collisionBox.minY); - int z1 = (int) Math.floor(collisionBox.minZ); - int x2 = (int) Math.floor(collisionBox.maxX + 1); - int y2 = (int) Math.floor(collisionBox.maxY + 1); - int z2 = (int) Math.floor(collisionBox.maxZ + 1); - List collisionBoxes = new ArrayList<>(); - for (int x = x1; x < x2; ++x) - for (int y = y1 - 1; y < y2; ++y) - for (int z = z1; z < z2; ++z) { - IntVector vec = new IntVector(x, y, z); - Material type = player.getBlockUpdateHandler().getBlock(vec).getType(); + public static List> + getCollisionsWithTypeNoEntities(APlayer player, SimpleCollisionBox collisionBox) { + return getCollisionsWithTypeNoEntities(player, collisionBox, Materials.COLLIDABLE); + } - if(type != Material.AIR && Materials.checkFlag(type, mask)) { - CollisionBox box = BlockData.getData(type) - .getBox(player, vec, ProtocolVersion.getGameVersion()); + public static List> + getCollisionsWithTypeNoEntities(APlayer player, SimpleCollisionBox collisionBox, int mask) { + int x1 = (int) Math.floor(collisionBox.minX); + int y1 = (int) Math.floor(collisionBox.minY); + int z1 = (int) Math.floor(collisionBox.minZ); + int x2 = (int) Math.floor(collisionBox.maxX + 1); + int y2 = (int) Math.floor(collisionBox.maxY + 1); + int z2 = (int) Math.floor(collisionBox.maxZ + 1); + List> collisionBoxes = new ArrayList<>(); + for (int x = x1; x < x2; ++x) + for (int y = y1 - 1; y < y2; ++y) + for (int z = z1; z < z2; ++z) { + IntVector vec = new IntVector(x, y, z); + Material type = player.getBlockUpdateHandler().getBlock(vec).getType(); - if(box.isIntersected(collisionBox)) { - box.downCast(collisionBoxes); - } - } - } + if (type != Material.AIR && Materials.checkFlag(type, mask)) { + CollisionBox box = BlockData.getData(type) + .getBox(player, vec, ProtocolVersion.getGameVersion()); - return collisionBoxes; - } + if (box.isIntersected(collisionBox)) { + for (SimpleCollisionBox simpleCollisionBox : box.downCast()) { + collisionBoxes.add(new Tuple<>(simpleCollisionBox, type)); + } + } + } + } - public static List getBlocksNearby2(World world, SimpleCollisionBox collisionBox, int mask) { - int x1 = (int) Math.floor(collisionBox.minX); - int y1 = (int) Math.floor(collisionBox.minY); - int z1 = (int) Math.floor(collisionBox.minZ); - int x2 = (int) Math.ceil(collisionBox.maxX); - int y2 = (int) Math.ceil(collisionBox.maxY); - int z2 = (int) Math.ceil(collisionBox.maxZ); - List blocks = new LinkedList<>(); - Block block; - for (int x = x1; x <= x2; x++) - for (int y = y1; y <= y2; y++) - for (int z = z1; z <= z2; z++) - if ((block = getBlockAt(world, x, y, z)) != null - && BlockUtils.getXMaterial(block.getType()) != XMaterial.AIR) - if (Materials.checkFlag(block.getType(),mask)) - blocks.add(block); - return blocks; - } + return collisionBoxes; + } - private static final int[] decimalPlaces = {0, 10, 100, 1000, 10000, 100000, 1000000, - 10000000, 100000000, 1000000000}; + public static List getCollisionsNoEntities(APlayer player, + SimpleCollisionBox collisionBox, int mask) { + int x1 = (int) Math.floor(collisionBox.minX); + int y1 = (int) Math.floor(collisionBox.minY); + int z1 = (int) Math.floor(collisionBox.minZ); + int x2 = (int) Math.floor(collisionBox.maxX + 1); + int y2 = (int) Math.floor(collisionBox.maxY + 1); + int z2 = (int) Math.floor(collisionBox.maxZ + 1); + List collisionBoxes = new ArrayList<>(); + for (int x = x1; x < x2; ++x) + for (int y = y1 - 1; y < y2; ++y) + for (int z = z1; z < z2; ++z) { + IntVector vec = new IntVector(x, y, z); + Material type = player.getBlockUpdateHandler().getBlock(vec).getType(); - public static double format(double d, int dec) { - return (long) (d * decimalPlaces[dec] + 0.5) / (double) decimalPlaces[dec]; - } + if (type != Material.AIR && Materials.checkFlag(type, mask)) { + CollisionBox box = BlockData.getData(type) + .getBox(player, vec, ProtocolVersion.getGameVersion()); - public static String drawUsage(long max, long time) { - double chunk = max / 50.; - String line = IntStream.range(0, 50).mapToObj(i -> (chunk * i < time ? "§c" : "§7") + "❘") - .collect(Collectors.joining("", "[", "")); - String zeros = "00"; - String nums = Integer.toString((int) ((time / (double) max) * 100)); - return line + "§f] §c" + zeros.substring(0, 3 - nums.length()) + nums + "% §f❘"; - } + if (box.isIntersected(collisionBox)) { + box.downCast(collisionBoxes); + } + } + } - public static String drawUsage(long max, double time) { - double chunk = max / 50.; - String line = IntStream.range(0, 50).mapToObj(i -> (chunk * i < time ? "§c" : "§7") + "❘") - .collect(Collectors.joining("", "[", "")); - String nums = String.valueOf(format((time / (double) max) * 100, 3)); - return line + "§f] §c" + nums + "%"; - } + return collisionBoxes; + } - public static List toCollisions(List blocks) { - return blocks.stream().map(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion())) - .collect(Collectors.toCollection(LinkedList::new)); - } + public static List getBlocksNearby2(World world, SimpleCollisionBox collisionBox, int mask) { + int x1 = (int) Math.floor(collisionBox.minX); + int y1 = (int) Math.floor(collisionBox.minY); + int z1 = (int) Math.floor(collisionBox.minZ); + int x2 = (int) Math.ceil(collisionBox.maxX); + int y2 = (int) Math.ceil(collisionBox.maxY); + int z2 = (int) Math.ceil(collisionBox.maxZ); + List blocks = new LinkedList<>(); + Block block; + for (int x = x1; x <= x2; x++) + for (int y = y1; y <= y2; y++) + for (int z = z1; z <= z2; z++) + if ((block = getBlockAt(world, x, y, z)) != null + && BlockUtils.getXMaterial(block.getType()) != XMaterial.AIR) + if (Materials.checkFlag(block.getType(), mask)) + blocks.add(block); + return blocks; + } - public static List toCollisionsDowncasted(List blocks) { - List collisions = new LinkedList<>(); - blocks.forEach(b -> BlockData.getData(b.getType()) - .getBox(b, ProtocolVersion.getGameVersion()).downCast(collisions)); - return collisions; - } + private static final int[] decimalPlaces = {0, 10, 100, 1000, 10000, 100000, 1000000, + 10000000, 100000000, 1000000000}; - public static CollisionBox toCollisions(Block b) { - return BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()); - } + public static double format(double d, int dec) { + return (long) (d * decimalPlaces[dec] + 0.5) / (double) decimalPlaces[dec]; + } + + public static String drawUsage(long max, long time) { + double chunk = max / 50.; + String line = IntStream.range(0, 50).mapToObj(i -> (chunk * i < time ? "§c" : "§7") + "❘") + .collect(Collectors.joining("", "[", "")); + String zeros = "00"; + String nums = Integer.toString((int) ((time / (double) max) * 100)); + return line + "§f] §c" + zeros.substring(0, 3 - nums.length()) + nums + "% §f❘"; + } + + public static String drawUsage(long max, double time) { + double chunk = max / 50.; + String line = IntStream.range(0, 50).mapToObj(i -> (chunk * i < time ? "§c" : "§7") + "❘") + .collect(Collectors.joining("", "[", "")); + String nums = String.valueOf(format((time / (double) max) * 100, 3)); + return line + "§f] §c" + nums + "%"; + } + + public static List toCollisions(List blocks) { + return blocks.stream().map(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion())) + .collect(Collectors.toCollection(LinkedList::new)); + } + + public static List toCollisionsDowncasted(List blocks) { + List collisions = new LinkedList<>(); + blocks.forEach(b -> BlockData.getData(b.getType()) + .getBox(b, ProtocolVersion.getGameVersion()).downCast(collisions)); + return collisions; + } + + public static CollisionBox toCollisions(Block b) { + return BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()); + } } diff --git a/src/main/java/dev/brighten/ac/utils/KLocation.java b/src/main/java/dev/brighten/ac/utils/KLocation.java index f372efe..8437a17 100644 --- a/src/main/java/dev/brighten/ac/utils/KLocation.java +++ b/src/main/java/dev/brighten/ac/utils/KLocation.java @@ -87,6 +87,10 @@ public class KLocation implements Cloneable { return this; } + public Vector getDirection() { + return MathUtils.getDirection(this); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/dev/brighten/ac/utils/MathUtils.java b/src/main/java/dev/brighten/ac/utils/MathUtils.java index c8cfbd4..7aba2a1 100644 --- a/src/main/java/dev/brighten/ac/utils/MathUtils.java +++ b/src/main/java/dev/brighten/ac/utils/MathUtils.java @@ -24,6 +24,12 @@ public class MathUtils { return to.subtract(from).length(); } + public static int angularDistance(double alpha, double beta) { + while (alpha < 0) alpha += 360; + while (beta < 0) beta += 360; + double phi = Math.abs(beta - alpha) % 360; + return (int) (phi > 180 ? 360 - phi : phi); + } public static boolean playerMoved(Location from, Location to) { return playerMoved(from.toVector(), to.toVector()); } diff --git a/src/main/java/dev/brighten/ac/utils/world/CollisionBox.java b/src/main/java/dev/brighten/ac/utils/world/CollisionBox.java index 8826a82..09ed3c2 100644 --- a/src/main/java/dev/brighten/ac/utils/world/CollisionBox.java +++ b/src/main/java/dev/brighten/ac/utils/world/CollisionBox.java @@ -15,5 +15,7 @@ public interface CollisionBox { CollisionBox expand(double x, double y, double z); void draw(EnumParticle particle, Player... players); void downCast(List list); + List downCast(); + boolean isNull(); } \ No newline at end of file diff --git a/src/main/java/dev/brighten/ac/utils/world/types/ComplexCollisionBox.java b/src/main/java/dev/brighten/ac/utils/world/types/ComplexCollisionBox.java index f2b7325..c90f37c 100644 --- a/src/main/java/dev/brighten/ac/utils/world/types/ComplexCollisionBox.java +++ b/src/main/java/dev/brighten/ac/utils/world/types/ComplexCollisionBox.java @@ -70,6 +70,16 @@ public class ComplexCollisionBox implements CollisionBox { box.downCast(list); } + @Override + public List downCast() { + List list = new ArrayList<>(); + + for (CollisionBox box : boxes) { + box.downCast(list); + } + return list; + } + @Override public boolean isNull() { for(CollisionBox box : boxes) diff --git a/src/main/java/dev/brighten/ac/utils/world/types/DynamicCollisionBox.java b/src/main/java/dev/brighten/ac/utils/world/types/DynamicCollisionBox.java index c3601e1..c214da4 100644 --- a/src/main/java/dev/brighten/ac/utils/world/types/DynamicCollisionBox.java +++ b/src/main/java/dev/brighten/ac/utils/world/types/DynamicCollisionBox.java @@ -8,6 +8,7 @@ import dev.brighten.ac.utils.world.CollisionBox; import lombok.Setter; import org.bukkit.entity.Player; +import java.util.ArrayList; import java.util.List; public class DynamicCollisionBox implements CollisionBox { @@ -70,6 +71,15 @@ public class DynamicCollisionBox implements CollisionBox { box.fetch(version, player, block).offset(x,y,z).downCast(list); } + @Override + public List downCast() { + List boxes = new ArrayList<>(); + + box.fetch(version, player, block).offset(x,y,z).downCast(boxes); + + return boxes; + } + @Override public boolean isNull() { return box.fetch(version, player, block).isNull(); diff --git a/src/main/java/dev/brighten/ac/utils/world/types/NoCollisionBox.java b/src/main/java/dev/brighten/ac/utils/world/types/NoCollisionBox.java index be65d58..9aedc6a 100644 --- a/src/main/java/dev/brighten/ac/utils/world/types/NoCollisionBox.java +++ b/src/main/java/dev/brighten/ac/utils/world/types/NoCollisionBox.java @@ -4,6 +4,7 @@ import dev.brighten.ac.packet.wrapper.objects.EnumParticle; import dev.brighten.ac.utils.world.CollisionBox; import org.bukkit.entity.Player; +import java.util.Collections; import java.util.List; public class NoCollisionBox implements CollisionBox { @@ -45,6 +46,11 @@ public class NoCollisionBox implements CollisionBox { @Override public void downCast(List list) { /**/ } + @Override + public List downCast() { + return Collections.emptyList(); + } + @Override public boolean isNull() { return true; diff --git a/src/main/java/dev/brighten/ac/utils/world/types/RayCollision.java b/src/main/java/dev/brighten/ac/utils/world/types/RayCollision.java index 5f0825a..88c727c 100644 --- a/src/main/java/dev/brighten/ac/utils/world/types/RayCollision.java +++ b/src/main/java/dev/brighten/ac/utils/world/types/RayCollision.java @@ -20,6 +20,7 @@ import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class RayCollision implements CollisionBox { @@ -211,6 +212,11 @@ public class RayCollision implements CollisionBox { @Override public void downCast(List list) {/*Do Nothing, Ray cannot be down-casted*/} + @Override + public List downCast() { + return Collections.emptyList(); + } + @Override public boolean isNull() { return true; diff --git a/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java b/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java index 3aeeec5..cb58c02 100644 --- a/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java +++ b/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java @@ -11,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.util.Vector; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -134,6 +135,11 @@ public class SimpleCollisionBox implements CollisionBox { list.add(this); } + @Override + public List downCast() { + return Collections.singletonList(this); + } + @Override public boolean isNull() { return false;