From 2ff0076e69ed35ea63b70420d6ba60d18527cb2e Mon Sep 17 00:00:00 2001 From: asofold Date: Sat, 11 Jun 2016 15:49:49 +0200 Subject: [PATCH] [BLEEDING] Switch implementation for moving.passable. Switch to AxisTracing instead of RayTracing. This means most workarounds are removed, including some (legacy) performance saving tweaks. --- .../checks/moving/player/Passable.java | 195 +++++++++++++----- .../utilities/BlockProperties.java | 34 +++ .../utilities/RichBoundsLocation.java | 42 +++- .../utilities/collision/AxisTracing.java | 19 +- 4 files changed, 232 insertions(+), 58 deletions(-) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java index a610b3ba..2545f59c 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java @@ -39,7 +39,7 @@ public class Passable extends Check { // TODO: Make this configurable once a working set of settings has been found. // TODO: Once made configurable... intense testing... and test cases. - private static boolean rt_legacy = true; + private static boolean rt_legacy = false; // TODO: rt_xzFactor = 1.0; // Problems: Doors, fences. private static double rt_xzFactor = 0.98; // TODO: Test bumping head into things. @@ -68,56 +68,49 @@ public class Passable extends Check { public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc) { - // TODO: if (!from.isSameCoords(loc)) {...check passable for loc -> from !?... + sf etc too?} - // TODO: Future: Account for the players bounding box? [test very-strict setting for at least the end points...] + // TODO: WAT: if (!from.isSameCoords(loc)) {...check passable for loc -> from !?... + sf etc too?} + + + // TODO: Distinguish feet vs. box. + String tags = ""; // Block distances (sum, max) for from-to (not for loc!). final int manhattan = from.manhattan(to); - // Skip moves inside of ignored blocks right away [works as long as we only check between foot-locations]. - if (manhattan <= 1 && BlockProperties.isPassable(from.getTypeId())) { - // TODO: Monitor: BlockProperties.isPassable checks slightly different than before. - if (manhattan == 0){ - return null; - } else { - // manhattan == 1 - if (BlockProperties.isPassable(to.getTypeId())) { + + if (rt_legacy) { + // Skip moves inside of ignored blocks right away [works as long as we only check between foot-locations]. + if (manhattan <= 1 && BlockProperties.isPassable(from.getTypeId())) { + // TODO: Monitor: BlockProperties.isPassable checks slightly different than before. + if (manhattan == 0){ return null; + } else { + // manhattan == 1 + if (BlockProperties.isPassable(to.getTypeId())) { + return null; + } } } - } + } + boolean toPassable = to.isPassable(); // General condition check for using ray-tracing. - if (toPassable && cc.passableRayTracingCheck && (!cc.passableRayTracingBlockChangeOnly || manhattan > 0)) { - setNormalMargins(rayTracing, from); - rayTracing.set(from, to); - rayTracing.loop(); - if (rayTracing.collides() || rayTracing.getStepsDone() >= rayTracing.getMaxSteps()) { - if (data.debug) { - debugExtraCollisionDetails(player, rayTracing, "initial"); - } - final int maxBlockDist = manhattan <= 1 ? manhattan : from.maxBlockDist(to); - if (maxBlockDist <= 1 && rayTracing.getStepsDone() == 1 && !from.isPassable()) { - // Redo ray-tracing for moving out of blocks. - if (collidesIgnoreFirst(from, to)) { - toPassable = false; - tags = "raytracing_2x_"; - if (data.debug) { - debugExtraCollisionDetails(player, rayTracing, "ingoreFirst"); - } - } - else if (data.debug) { - debug(player, "Allow moving out of a block."); - } - } - else{ - if (!allowsSplitMove(from, to, manhattan, data)) { - toPassable = false; - tags = "raytracing_"; - } + if ((!rt_legacy || toPassable) && cc.passableRayTracingCheck + && (!cc.passableRayTracingBlockChangeOnly || manhattan > 0)) { + final String newTag; + if (rt_legacy) { + newTag = checkRayTracingLegacy(player, from, to, manhattan, data, cc); + if (newTag != null) { + toPassable = false; + tags = newTag; + } + } + else { + newTag = checkRayTracing(player, from, to, manhattan, data, cc); + if (newTag != null) { + // Direct return. + return potentialViolation(player, from, to, manhattan, tags, data, cc); } } - // TODO: Future: If accuracy is demanded, also check the head position (or bounding box right away). - rayTracing.cleanup(); } // TODO: Checking order: If loc is not the same as from, a quick return here might not be wanted. @@ -127,11 +120,68 @@ public class Passable extends Check { data.passableVL *= 0.99; return null; } else { - return potentialViolation(player, from, to, manhattan, tags, data, cc); + return potentialViolationLegacy(player, from, to, manhattan, tags, data, cc); } } + private String checkRayTracing(final Player player, final PlayerLocation from, final PlayerLocation to, + final int manhattan, final MovingData data, final MovingConfig cc) { + String tags = null; + setNormalMargins(rayTracing, from); + rayTracing.set(from, to); + rayTracing.setIgnoreInitiallyColliding(true); + //rayTracing.setCutOppositeDirectionMargin(true); + rayTracing.loop(); + rayTracing.setIgnoreInitiallyColliding(false); + //rayTracing.setCutOppositeDirectionMargin(false); + if (rayTracing.collides()) { + tags = "raytracing_collide_"; + } + else if (rayTracing.getStepsDone() >= rayTracing.getMaxSteps()) { + tags = "raytracing_maxsteps_"; + } + if (data.debug) { + debugExtraCollisionDetails(player, rayTracing, "std"); + } + rayTracing.cleanup(); + return tags; + } + + private String checkRayTracingLegacy(final Player player, final PlayerLocation from, final PlayerLocation to, + final int manhattan, final MovingData data, final MovingConfig cc) { + setNormalMargins(rayTracing, from); + rayTracing.set(from, to); + rayTracing.loop(); + String tags = null; + if (rayTracing.collides() || rayTracing.getStepsDone() >= rayTracing.getMaxSteps()) { + if (data.debug) { + debugExtraCollisionDetails(player, rayTracing, "legacy"); + } + final int maxBlockDist = manhattan <= 1 ? manhattan : from.maxBlockDist(to); + if (maxBlockDist <= 1 && rayTracing.getStepsDone() == 1 && !from.isPassable()) { + // Redo ray-tracing for moving out of blocks. + if (collidesIgnoreFirst(from, to)) { + tags = "raytracing_2x_"; + if (data.debug) { + debugExtraCollisionDetails(player, rayTracing, "ingoreFirst"); + } + } + else if (data.debug) { + debug(player, "Allow moving out of a block."); + } + } + else{ + if (!allowsSplitMove(from, to, manhattan, data)) { + tags = "raytracing_"; + } + } + } + // TODO: Future: If accuracy is demanded, also check the head position (or bounding box right away). + rayTracing.cleanup(); + return tags; + } + /** * Default/normal margins. * @param rayTracing @@ -141,8 +191,44 @@ public class Passable extends Check { rayTracing.setMargins(from.getEyeHeight() * rt_heightFactor, from.getWidth() / 2.0 * rt_xzFactor); // max from/to + resolution ? } - private Location potentialViolation(final Player player, final PlayerLocation from, final PlayerLocation to, final int manhattan, String tags, final MovingData data, final MovingConfig cc) { + /** + * Axis-wise ray-tracing violation skipping conditions. + * + * @param player + * @param from + * @param to + * @param manhattan + * @param tags + * @param data + * @param cc + * @return + */ + private Location potentialViolation(final Player player, + final PlayerLocation from, final PlayerLocation to, final int manhattan, + String tags, final MovingData data, final MovingConfig cc) { + + // TODO: Might need the workaround for fences. + + return actualViolation(player, from, to, tags, data, cc); + } + + /** + * Legacy skipping conditions, before triggering an actual violation. + * + * @param player + * @param from + * @param to + * @param manhattan + * @param tags + * @param data + * @param cc + * @return + */ + private Location potentialViolationLegacy(final Player player, + final PlayerLocation from, final PlayerLocation to, final int manhattan, + String tags, final MovingData data, final MovingConfig cc) { // Moving into a block, possibly a violation. + // TODO: Do account for settings and ray-tracing here. // First check if the player is moving from a passable location. // If not, the move might still be allowed, if moving inside of the same block, or from and to have head position passable. @@ -163,21 +249,27 @@ public class Passable extends Check { // (Mind that this can be the case on the same block theoretically.) // Keep loc as set-back. // } - else if (manhattan == 1 && to.isBlockAbove(from) && BlockProperties.isPassable(from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) { + else if (manhattan == 1 && to.isBlockAbove(from) + && BlockProperties.isPassable(from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) { // else if (to.isBlockAbove(from) && BlockProperties.isPassableExact(from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) { // Allow the move up if the head is free. - // TODO: Better distinguish ray-tracing (through something thin) or check to-head-passable too? return null; } else if (manhattan > 0) { // Otherwise keep from as set-back. tags += "cross"; } - else{ - // All blocks are the same, allow the move. + else { + // manhattan == 0 + // TODO: Even legacy ray-tracing will now account for actual initial collision. return null; } + return actualViolation(player, from, to, tags, data, cc); + } + + private Location actualViolation(final Player player, final PlayerLocation from, final PlayerLocation to, + final String tags, final MovingData data, final MovingConfig cc) { Location setBackLoc = null; // Alternative to from.getLocation(). // Prefer the set-back location from the data. @@ -188,8 +280,6 @@ public class Passable extends Check { } } - // TODO: set data.set-back ? or something: still some aji here. - // Return the reset position. data.passableVL += 1d; final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions); @@ -290,6 +380,13 @@ public class Passable extends Check { return false; } + /** + * Debug only if colliding. + * + * @param player + * @param rayTracing + * @param tag + */ private void debugExtraCollisionDetails(Player player, ICollidePassable rayTracing, String tag) { if (rayTracing.collides()) { debug(player, "Raytracing collision (" + tag + "): " + rayTracing.getCollidingAxis()); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java index 1fde0b19..2018c7af 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java @@ -3154,6 +3154,40 @@ public class BlockProperties { return false; } + /** + * Check if the bounding box collides with a block (passable + accounting + * for workarounds). + * + * @param access + * @param minX + * @param minY + * @param minZ + * @param maxX + * @param maxY + * @param maxZ + * @return + */ + public static final boolean isPassableBox(final BlockCache access, + final double minX, final double minY, final double minZ, + final double maxX, final double maxY, final double maxZ) { + final int iMinX = Location.locToBlock(minX); + final int iMaxX = Location.locToBlock(maxX); + final int iMinY = Location.locToBlock(minY); + final int iMaxY = Location.locToBlock(maxY); + final int iMinZ = Location.locToBlock(minZ); + final int iMaxZ = Location.locToBlock(maxZ); + for (int x = iMinX; x <= iMaxX; x++) { + for (int z = iMinZ; z <= iMaxZ; z++) { + for (int y = iMinY; y <= iMaxY; y++) { + if (!isPassableBox(access, x, y, z, minX, minY, minZ, maxX, maxY, maxZ)) { + return false; + } + } + } + } + return true; + } + /** * Add the block coordinates that are colliding via a isPassableBox check * for the given bounds to the given container. diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RichBoundsLocation.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RichBoundsLocation.java index 61be3d9e..c06df0bf 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RichBoundsLocation.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RichBoundsLocation.java @@ -86,6 +86,9 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition /** Simple test if the exact position is passable. */ Boolean passable = null; + /** Bounding box collides with blocks. */ + Boolean passableBox = null; + /** Is the player above stairs? */ Boolean aboveStairs = null; @@ -756,12 +759,44 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition */ public boolean isPassable() { if (passable == null) { - passable = BlockProperties.isPassable(blockCache, x, y, z, getTypeId()); - // passable = BlockProperties.isPassableExact(blockCache, x, y, z, getTypeId()); + if (isBlockFlagsPassable()) { + passable = true; + } + else { + passable = BlockProperties.isPassable(blockCache, x, y, z, getTypeId()); + //passable = BlockProperties.isPassableExact(blockCache, x, y, z, getTypeId()); + } } return passable; } + /** + * Test if the bounding box is colliding (passable check with accounting for + * workarounds). + * + * @return + */ + public boolean isPassableBox() { + // TODO: Might need a variation with margins as parameters. + if (passableBox == null) { + if (isBlockFlagsPassable()) { + passableBox = true; + } + else if (passable != null && !passable) { + passableBox = false; + } + else { + // Fetch. + passableBox = BlockProperties.isPassableBox(blockCache, minX, minY, minZ, maxX, maxY, maxZ); + } + } + return passableBox; + } + + private boolean isBlockFlagsPassable() { + return blockFlags != null && (blockFlags & (BlockProperties.F_SOLID | BlockProperties.F_GROUND)) == 0; + } + /** * Set block flags using yOnGround, unless already set. Check the maximally * used bounds for the block checking, to have flags ready for faster @@ -907,6 +942,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition this.notOnGroundMaxY = other.notOnGroundMaxY; this.onGroundMinY = other.onGroundMinY; this.passable = other.passable; + this.passableBox = other.passableBox; // Access methods. this.typeId = other.getTypeId(); this.typeIdBelow = other.getTypeIdBelow(); @@ -957,7 +993,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition // Reset cached values. typeId = typeIdBelow = data = null; - aboveStairs = inLava = inWater = inWeb = onIce = onGround = onClimbable = passable = null; + aboveStairs = inLava = inWater = inWeb = onIce = onGround = onClimbable = passable = passableBox = null; onGroundMinY = Double.MAX_VALUE; notOnGroundMaxY = Double.MIN_VALUE; blockFlags = null; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/AxisTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/AxisTracing.java index 3885e52a..812c258d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/AxisTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/AxisTracing.java @@ -180,6 +180,13 @@ public abstract class AxisTracing implements ICollide, ISetMargins { } } + private boolean shouldCheckForIgnoredBlocks() { + return ignoreInitiallyColliding && !ignoredBlocks.isEmpty(); + } + + private boolean isBlockIgnored(final int x, final int y, final int z) { + return ignoredBlocks.containsBlockPosition(x, y, z); + } private void runAxisY(final double xIn, final double yIn, final double zIn) { // Skip if there is nothing to iterate. @@ -218,10 +225,10 @@ public abstract class AxisTracing implements ICollide, ISetMargins { if (step > maxSteps) { return; } - final boolean checkInitiallyColliding = ignoreInitiallyColliding && !ignoredBlocks.isEmpty(); + final boolean checkInitiallyColliding = shouldCheckForIgnoredBlocks(); for (int x = iMinX; x <= iMaxX; x++) { for (int z = iMinZ; z <= iMaxZ; z++) { - if (checkInitiallyColliding && ignoredBlocks.containsBlockPosition(x, y, z)) { + if (checkInitiallyColliding && isBlockIgnored(x, y, z)) { // Ignore. } else if (!step(x, y, z, xMin, increment == 1 ? yStart : yEnd, zMin, xMax, increment == 1 ? yEnd : yStart, zMax, Axis.Y_AXIS, increment)) { @@ -272,10 +279,10 @@ public abstract class AxisTracing implements ICollide, ISetMargins { if (step > maxSteps) { return; } - final boolean checkInitiallyColliding = ignoreInitiallyColliding && !ignoredBlocks.isEmpty(); + final boolean checkInitiallyColliding = shouldCheckForIgnoredBlocks(); for (int y = iMinY; y <= iMaxY; y++) { for (int z = iMinZ; z <= iMaxZ; z++) { - if (checkInitiallyColliding && ignoredBlocks.containsBlockPosition(x, y, z)) { + if (checkInitiallyColliding && isBlockIgnored(x, y, z)) { // Ignore. } else if (!step(x, y, z, increment == 1 ? xStart : xEnd, yMin, zMin, increment == 1 ? xEnd : xStart, yMax, zMax, Axis.X_AXIS, increment)) { @@ -320,7 +327,7 @@ public abstract class AxisTracing implements ICollide, ISetMargins { final int iMaxX = Location.locToBlock(xMax); final int iStartZ = Location.locToBlock(zStart); axisStep = 0; - final boolean checkInitiallyColliding = ignoreInitiallyColliding && !ignoredBlocks.isEmpty(); + final boolean checkInitiallyColliding = shouldCheckForIgnoredBlocks(); for (int z = iStartZ; z != iEndZ; z += increment) { ++step; ++axisStep; @@ -329,7 +336,7 @@ public abstract class AxisTracing implements ICollide, ISetMargins { } for (int y = iMinY; y <= iMaxY; y++) { for (int x = iMinX; x <= iMaxX; x++) { - if (checkInitiallyColliding && ignoredBlocks.containsBlockPosition(x, y, z)) { + if (checkInitiallyColliding && isBlockIgnored(x, y, z)) { // Ignore. } else if (!step(x, y, z, xMin, yMin, increment == 1 ? zStart : zEnd, xMax, yMax, increment == 1 ? zEnd : zStart, Axis.Z_AXIS, increment)) {