diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java index 6550ddde..6515afec 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/BlockInteractListener.java @@ -33,6 +33,7 @@ import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig; import fr.neatmonster.nocheatplus.checks.moving.MovingData; import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil; import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle; +import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying; import fr.neatmonster.nocheatplus.compat.Bridge1_9; import fr.neatmonster.nocheatplus.compat.BridgeHealth; import fr.neatmonster.nocheatplus.compat.BridgeMisc; @@ -185,10 +186,27 @@ public class BlockInteractListener extends CheckListener { } else { data.subsequentCancel = 0; + if (data.debug) { + if (flyingHandle.isFlyingQueueFetched()) { + // Log which entry was used. + logUsedFlyingPacket(player, flyingHandle); + } + } } useLoc.setWorld(null); } + private void logUsedFlyingPacket(final Player player, final FlyingQueueHandle flyingHandle) { + final DataPacketFlying[] queue = flyingHandle.getHandle(); + for (int i = 0; i < queue.length; i++) { + final DataPacketFlying packet = queue[i]; + if (packet != null) { + debug(player, "Flying packet queue used at index " + i + ": pitch=" + packet.getPitch() + ",yaw=" + packet.getYaw()); + return; + } + } + } + private void onCancelInteract(final Player player, final Block block, final BlockFace face, final PlayerInteractEvent event, final int previousLastTick, final boolean preventUseItem, final BlockInteractData data, final BlockInteractConfig cc) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Direction.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Direction.java index 4f33992b..85395465 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Direction.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Direction.java @@ -22,6 +22,7 @@ import org.bukkit.util.Vector; import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle; +import fr.neatmonster.nocheatplus.checks.net.FlyingQueueLookBlockChecker; import fr.neatmonster.nocheatplus.utilities.collision.CollideRayVsAABB; import fr.neatmonster.nocheatplus.utilities.collision.ICollideRayVsAABB; import fr.neatmonster.nocheatplus.utilities.location.LocUtil; @@ -31,7 +32,43 @@ import fr.neatmonster.nocheatplus.utilities.location.LocUtil; */ public class Direction extends Check { + private final class BoulderChecker extends FlyingQueueLookBlockChecker { + // (Not static for convenience.) + + private double minDistance; + + @Override + protected boolean check(final double x, final double y, final double z, + final float yaw, final float pitch, + final int blockX, final int blockY, final int blockZ) { + final double distance = checkBoulder(x, y, z, yaw, pitch, blockX, blockY, blockZ); + if (distance == Double.MAX_VALUE) { + // minDistance is not updated, in case the information is interesting ever. + return true; + } + else { + minDistance = Math.min(minDistance, distance); + return false; + } + } + + @Override + public boolean checkFlyingQueue(double x, double y, double z, float oldYaw, float oldPitch, int blockX, + int blockY, int blockZ, FlyingQueueHandle flyingHandle) { + minDistance = Double.MAX_VALUE; + return super.checkFlyingQueue(x, y, z, oldYaw, oldPitch, blockX, blockY, blockZ, flyingHandle); + } + + public double getMinDistance() { + return minDistance; + } + + } + private final ICollideRayVsAABB boulder = new CollideRayVsAABB(); + private final Location useLoc = new Location(null, 0, 0, 0); + + private final BoulderChecker checker = new BoulderChecker(); /** * Instantiates a new direction check. @@ -53,20 +90,27 @@ public class Direction extends Check { final FlyingQueueHandle flyingHandle, final BlockInteractData data, final BlockInteractConfig cc) { boolean cancel = false; - // How far "off" is the player with their aim. - final Vector direction = loc.getDirection(); - // Initialize fully each time. - boulder.setFindNearestPointIfNotCollide(true) - .setRay(loc.getX(), loc.getY() + player.getEyeHeight(), loc.getZ(), - direction.getX(), direction.getY(), direction.getZ()) - .setAABB(block.getX(), block.getY(), block.getZ(), 0.1) - .loop(); - // TODO: if (boulder.collides()) { // Check flying queue. - - if (!boulder.collides()) { - final double distance = Math.sqrt(boulder.getClosestDistanceSquared()); + final double x = loc.getX(); + final double y = loc.getY() + player.getEyeHeight(); + final double z = loc.getZ(); + final int blockX = block.getX(); + final int blockY = block.getY(); + final int blockZ = block.getZ(); + // The distance is squared initially. + double distance = checkBoulder(x, y, z, loc.getYaw(), loc.getPitch(), blockX, blockY, blockZ); + if (distance != Double.MAX_VALUE) { + if (checker.checkFlyingQueue(x, y, z, loc.getYaw(), loc.getPitch(), + blockX, blockY, blockZ, flyingHandle)) { + distance = Double.MAX_VALUE; + } + else { + distance = Math.min(distance, checker.getMinDistance()); + } + } + if (distance != Double.MAX_VALUE) { + distance = Math.sqrt(distance); if (data.debug) { outputDebugFail(player, boulder, distance); } @@ -83,10 +127,46 @@ public class Direction extends Check { // Player did likely nothing wrong, reduce violation counter to reward them. data.directionVL *= 0.9D; } - return cancel; } + /** + * Check one configuration. + * + * @param x + * @param y + * @param z + * @param yaw + * @param pitch + * @param blockX + * @param blockY + * @param blockZ + * @return Double.MAX_VALUE if this passes the check, otherwise the squared + * violation distance (some measure). + */ + private double checkBoulder(final double x, final double y, final double z, + final float yaw, final float pitch, + final int blockX, final int blockY, final int blockZ) { + useLoc.setYaw(yaw); + useLoc.setPitch(pitch); + final Vector dir = useLoc.getDirection(); // TODO: More efficient. + final double dirX = dir.getX(); + final double dirY = dir.getY(); + final double dirZ = dir.getZ(); + // Initialize fully each time. + boulder.setFindNearestPointIfNotCollide(true) + .setRay(x, y, z, dirX, dirY, dirZ) + .setAABB(blockX, blockY, blockZ, 0.1) + .loop(); + // Interpret result. + if (boulder.collides()) { + return Double.MAX_VALUE; + } + else { + return boulder.getClosestDistanceSquared(); + } + } + private void outputDebugFail(Player player, ICollideRayVsAABB boulder, double distance) { debug(player, "Failed: collides: " + boulder.collides() + " , dist: " + distance + " , pos: " + LocUtil.simpleFormat(boulder)); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Reach.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Reach.java index 65e35d6f..03492ddd 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Reach.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Reach.java @@ -62,6 +62,7 @@ public class Reach extends Check { // Distance is calculated from eye location to center of targeted block. If the player is further away from their // target than allowed, the difference will be assigned to "distance". + // TODO: On failure loop through flying queue, and do set not working entries to null (!). final double distance = TrigUtil.distance(loc.getX(), loc.getY() + player.getEyeHeight(), loc.getZ(), 0.5 + block.getX(), 0.5 + block.getY(), 0.5 + block.getZ()) - distanceLimit; if (distance > 0) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Visible.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Visible.java index d69eea88..47bdd3f5 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Visible.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockinteract/Visible.java @@ -28,16 +28,69 @@ import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.ViolationData; import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle; -import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying; +import fr.neatmonster.nocheatplus.checks.net.FlyingQueueLookBlockChecker; import fr.neatmonster.nocheatplus.utilities.StringUtil; import fr.neatmonster.nocheatplus.utilities.collision.InteractRayTracing; -import fr.neatmonster.nocheatplus.utilities.location.LocUtil; import fr.neatmonster.nocheatplus.utilities.location.TrigUtil; import fr.neatmonster.nocheatplus.utilities.map.BlockCache; import fr.neatmonster.nocheatplus.utilities.map.WrapBlockCache; public class Visible extends Check { + private final class RayChecker extends FlyingQueueLookBlockChecker { + + private BlockFace face; + private List tags; + private boolean debug; + private Player player; + + @Override + protected boolean check(final double x, final double y, final double z, + final float yaw, final float pitch, + final int blockX, final int blockY, final int blockZ) { + // Run ray-tracing again with updated pitch and yaw. + useLoc.setPitch(pitch); + useLoc.setYaw(yaw); + final Vector direction = useLoc.getDirection(); // TODO: Better. + tags.clear(); + if (checkRayTracing(x, y, z, direction.getX(), direction.getY(), direction.getZ(), blockX, blockY, blockZ, face, tags, debug)) { + // Collision still. + if (debug) { + debug(player, "pitch=" + pitch + ",yaw=" + yaw + " tags=" + StringUtil.join(tags, "+")); + } + return false; + } + else { + return true; + } + + } + + public boolean checkFlyingQueue(double x, double y, double z, float oldYaw, float oldPitch, int blockX, + int blockY, int blockZ, FlyingQueueHandle flyingHandle, + BlockFace face, List tags, boolean debug, Player player) { + this.face = face; + this.tags = tags; + this.debug = debug; + this.player = player; + return super.checkFlyingQueue(x, y, z, oldYaw, oldPitch, blockX, blockY, blockZ, flyingHandle); + } + + @Override + public boolean checkFlyingQueue(double x, double y, double z, float oldYaw, float oldPitch, int blockX, + int blockY, int blockZ, FlyingQueueHandle flyingHandle) { + throw new UnsupportedOperationException("Use the other method."); + } + + public void cleanup () { + this.player = null; + this.face = null; + this.debug = false; + this.tags = null; + } + + } + private final WrapBlockCache wrapBlockCache; /** @@ -45,6 +98,8 @@ public class Visible extends Check { */ private final InteractRayTracing rayTracing = new InteractRayTracing(false); + private final RayChecker checker = new RayChecker(); + private final List tags = new ArrayList(); /** For temporary use, no nested use, setWorld(null) after use, etc. */ @@ -86,52 +141,15 @@ public class Visible extends Check { if (collides) { // Debug output. if (data.debug) { - debug(player, "pitch=" + loc.getPitch() + " yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+")); + debug(player, "pitch=" + loc.getPitch() + ",yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+")); } // Re-check with flying packets. - final DataPacketFlying[] flyingQueue = flyingHandle.getHandle(); - // TODO: Maybe just the latest one does (!).+ - LocUtil.set(useLoc, loc); - final float oldPitch = useLoc.getPitch(); - final float oldYaw = useLoc.getYaw(); - // TODO: Specific max-recheck-count (likely doesn't equal packet count). - for (int i = 0; i < flyingQueue.length; i++) { - final DataPacketFlying packetData = flyingQueue[i]; - if (packetData == null) { - // Other checks have pre-selected. - continue; - } - // TODO: Allow if within threshold(s) of last move. - // TODO: Confine by distance. - // Abort/skipping conditions. - // if (packetData.hasPos) { - // break; - // } - if (!packetData.hasLook) { - flyingQueue[i] = null; - continue; - } - // TODO: Might skip last pitch+yaw as well. - if (packetData.getPitch() == oldPitch && packetData.getYaw() == oldYaw) { - flyingQueue[i] = null; - continue; - } - // Run ray-tracing again with updated pitch and yaw. - useLoc.setPitch(packetData.getPitch()); - useLoc.setYaw(packetData.getYaw()); - direction = useLoc.getDirection(); // TODO: Better. - tags.clear(); - tags.add("flying(" + i + ")"); // Interesting if this gets through. - collides = checkRayTracing(eyeX, eyeY, eyeZ, direction.getX(), direction.getY(), direction.getZ(), blockX, blockY, blockZ, face, tags, data.debug); - if (!collides) { - break; - } - flyingQueue[i] = null; - // Debug output. - if (data.debug) { - debug(player, "pitch=" + loc.getPitch() + " yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+")); - } + if (checker.checkFlyingQueue(eyeX, eyeY, eyeZ, useLoc.getYaw(), useLoc.getPitch(), + blockX, blockY, blockZ, flyingHandle, face, tags, data.debug, player)) { + // Check passed. + collides = false; } + checker.cleanup(); useLoc.setWorld(null); } // Cleanup. @@ -155,7 +173,7 @@ public class Visible extends Check { else { data.visibleVL *= 0.99; if (data.debug) { - debug(player, "pitch=" + loc.getPitch() + " yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+")); + debug(player, "pitch=" + loc.getPitch() + ",yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+")); } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueHandle.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueHandle.java index 848e4412..ac1497d4 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueHandle.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueHandle.java @@ -31,4 +31,8 @@ public class FlyingQueueHandle implements IHandle { return queue; } + public boolean isFlyingQueueFetched() { + return queue != null; + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueLookBlockChecker.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueLookBlockChecker.java new file mode 100644 index 00000000..e8007fbb --- /dev/null +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/net/FlyingQueueLookBlockChecker.java @@ -0,0 +1,83 @@ +package fr.neatmonster.nocheatplus.checks.net; + +import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying; + +/** + * A simple loop through checker for the flying queue feature. + * @author asofold + * + */ +public abstract class FlyingQueueLookBlockChecker { + + /** + * Override to prevent setting elements that can't be used or for which + * check returned false to null. + */ + protected boolean setUnusableToNull = true; + + protected abstract boolean check(final double x, final double y, final double z, + final float yaw, final float pitch, + final int blockX, final int blockY, final int blockZ); + + /** + * Run check with the given start position (e.g. eye coordinates), but use + * yaw and pitch from the flying queue. Non matching entries are nulled, + * unless setUnusableToNull is set to false. + * + * @param x + * @param y + * @param z + * @param oldYaw + * @param oldPitch + * @param blockX + * @param blockY + * @param blockZ + * @param flyingHandle + * @return True, if check returned true (the first time is returned). False + * if the queue is empty or check has not returned true for any + * contained element. Special return values have to be set + * elsewhere. An empty queue also yields false as return value. + */ + public boolean checkFlyingQueue(final double x, final double y, final double z, + final float oldYaw, final float oldPitch, + final int blockX, final int blockY, final int blockZ, final FlyingQueueHandle flyingHandle) { + final DataPacketFlying[] queue = flyingHandle.getHandle(); + if (queue.length == 0) { + return false; + } + for (int i = 0; i < queue.length; i++) { + final DataPacketFlying packetData = queue[i]; + if (packetData == null) { + continue; + } + if (!packetData.hasLook) { + if (setUnusableToNull) { + queue[i] = null; + } + continue; + } + final float yaw = packetData.getYaw(); + final float pitch = packetData.getPitch(); + // Simple heuristic: reduce impact of checking by skipping redundant entries. + // TODO: Other heuristic / what's typical? + if (yaw == oldYaw && pitch == oldPitch) { + if (setUnusableToNull) { + queue[i] = null; + } + continue; + } + // TODO: Consider support some other type of metric (possibly checking positions too?); + if (check(x, y, z, yaw, pitch, blockX, blockY, blockZ)) { + // TODO: Consider to remember index and entry as well? + return true; + } + else { + if (setUnusableToNull) { + queue[i] = null; + } + } + } + return false; + } + +}