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 61f16516..fb15d291 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockProperties.java @@ -2242,7 +2242,7 @@ public class BlockProperties { * @param z * @param id * @param bounds Not null: bounds of the block at x, y, z. - * @param flags Block flags for the block at x, y, z. + * @param flags Block flags for the block at x, y, z. Mix in F_COLLIDE_EDGES to disallow the "high edges" of blocks. * @return */ public static final boolean collidesBlock(final BlockCache access, final double minX, double minY, final double minZ, final double maxX, final double maxY, final double maxZ, final int x, final int y, final int z, final int id, final double[] bounds, final long flags) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InteractRayTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InteractRayTracing.java index c614a775..0fa13cf5 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InteractRayTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InteractRayTracing.java @@ -1,6 +1,5 @@ package fr.neatmonster.nocheatplus.utilities; -import org.bukkit.Location; /** * Rough ray-tracing for interaction with something. This does not do any smart end-point guessing. @@ -8,151 +7,148 @@ import org.bukkit.Location; * */ public class InteractRayTracing extends RayTracing { - - private static final int[][] incr = new int[][]{ - {1, 0, 0}, - {0, 1, 0}, - {0, 0, 1}, - {-1, 0, 0}, - {0, -1, 0}, - {0, 0, -1}, - }; - - protected BlockCache blockCache = null; - - protected boolean collides = false; - - protected boolean strict = false; - - protected int lastBx, lastBy, lastBz; - - protected int targetBx, targetBy, targetBz; - - public InteractRayTracing(){ - super(); - } - - public InteractRayTracing(boolean strict){ - super(); - this.strict = strict; - } - - public BlockCache getBlockCache() { - return blockCache; - } - public void setBlockCache(BlockCache blockCache) { - this.blockCache = blockCache; - } - - /* (non-Javadoc) - * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) - */ - @Override - public void set(double x0, double y0, double z0, double x1, double y1, double z1) { - super.set(x0, y0, z0, x1, y1, z1); - collides = false; - lastBx = blockX; - lastBy = blockY; - lastBz = blockZ; - targetBx = Location.locToBlock(x1); - targetBy = Location.locToBlock(y1); - targetBz = Location.locToBlock(z1); - } - - public boolean collides(){ - return collides; - } + private static final int[][] incr = new int[][]{ + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {-1, 0, 0}, + {0, -1, 0}, + {0, 0, -1}, + }; - /** - * Remove reference to BlockCache. - */ - public void cleanup(){ - if (blockCache != null){ - blockCache = null; - } - } - - /** - * Simplistic collision check (can interact through this block). - * @param blockX - * @param blockY - * @param blockZ - * @return - */ - private final boolean doesCollide(int blockX, int blockY, int blockZ){ - final int id = blockCache.getTypeId(blockX, blockY, blockZ); - final long flags = BlockProperties.getBlockFlags(id); - if ((flags & BlockProperties.F_SOLID) == 0){ - // Ignore non solid blocks anyway. - return false; - } - if ((flags & (BlockProperties.F_LIQUID | BlockProperties.F_IGN_PASSABLE | BlockProperties.F_STAIRS | BlockProperties.F_VARIABLE)) != 0){ - // Special cases. - // TODO: F_VARIABLE: Bounding boxes are roughly right ? - return false; - } - if (!blockCache.isFullBounds(blockX, blockY, blockZ)) return false; - return true; - } - - /** - * Check if the block may be interacted through by use of some workaround. - * @param blockX - * @param blockY - * @param blockZ - * @return - */ - private final boolean allowsWorkaround(final int blockX, final int blockY, final int blockZ) { - // TODO: This allows some bypasses for "strange" setups. - // TODO: Consider using distance to target as heuristic ? [should not get smaller !?] - final int dX = blockX - lastBx; - final int dY = blockY - lastBy; - final int dZ = blockZ - lastBz; - final double dSq = dX * dX + dY * dY + dZ * dZ; - for (int i = 0; i < 6; i++){ - final int[] dir = incr[i]; - final int rX = blockX + dir[0]; - if (Math.abs(lastBx - rX) > 1) continue; - final int rY = blockY + dir[1]; - if (Math.abs(lastBy - rY) > 1) continue; - final int rZ = blockZ + dir[2]; - if (Math.abs(lastBz - rZ) > 1) continue; - final int dRx = rX - lastBx; - final int dRy = rY - lastBy; - final int dRz = rZ - lastBz; - if (dRx * dRx + dRy * dRy + dRz * dRz <= dSq) continue; - if (!doesCollide(rX, rY, rZ)){ - // NOTE: Don't check "rX == targetBx && rZ == targetBz && rY == targetBy". - return true; - } - } - return false; - } - - @Override - protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) { - // TODO: Make an optional, more precise check (like passable) ? - if (blockX == targetBx && blockZ == targetBz && blockY == targetBy || !doesCollide(blockX, blockY, blockZ)){ - lastBx = blockX; - lastBy = blockY; - lastBz = blockZ; - return true; - } - if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy){ - collides = true; - return false; - } - // Check workarounds... - if (allowsWorkaround(blockX, blockY, blockZ)){ - lastBx = blockX; - lastBy = blockY; - lastBz = blockZ; - return true; - } - // No workaround found. - collides = true; - return false; - } + protected BlockCache blockCache = null; + + protected boolean collides = false; + + protected boolean strict = false; + + protected int lastBx, lastBy, lastBz; + + public InteractRayTracing(){ + super(); + } + + public InteractRayTracing(boolean strict){ + super(); + this.strict = strict; + } + + public BlockCache getBlockCache() { + return blockCache; + } + + public void setBlockCache(BlockCache blockCache) { + this.blockCache = blockCache; + } + + /* (non-Javadoc) + * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) + */ + @Override + public void set(double x0, double y0, double z0, double x1, double y1, double z1) { + super.set(x0, y0, z0, x1, y1, z1); + collides = false; + lastBx = blockX; + lastBy = blockY; + lastBz = blockZ; + } + + public boolean collides(){ + return collides; + } + + /** + * Remove reference to BlockCache. + */ + public void cleanup(){ + if (blockCache != null){ + blockCache = null; + } + } + + /** + * Simplistic collision check (can interact through this block). + * @param blockX + * @param blockY + * @param blockZ + * @return + */ + private final boolean doesCollide(int blockX, int blockY, int blockZ){ + final int id = blockCache.getTypeId(blockX, blockY, blockZ); + final long flags = BlockProperties.getBlockFlags(id); + if ((flags & BlockProperties.F_SOLID) == 0){ + // Ignore non solid blocks anyway. + return false; + } + if ((flags & (BlockProperties.F_LIQUID | BlockProperties.F_IGN_PASSABLE | BlockProperties.F_STAIRS | BlockProperties.F_VARIABLE)) != 0){ + // Special cases. + // TODO: F_VARIABLE: Bounding boxes are roughly right ? + return false; + } + if (!blockCache.isFullBounds(blockX, blockY, blockZ)) return false; + return true; + } + + /** + * Check if the block may be interacted through by use of some workaround. + * @param blockX + * @param blockY + * @param blockZ + * @return + */ + private final boolean allowsWorkaround(final int blockX, final int blockY, final int blockZ) { + // TODO: This allows some bypasses for "strange" setups. + // TODO: Consider using distance to target as heuristic ? [should not get smaller !?] + final int dX = blockX - lastBx; + final int dY = blockY - lastBy; + final int dZ = blockZ - lastBz; + final double dSq = dX * dX + dY * dY + dZ * dZ; + for (int i = 0; i < 6; i++){ + final int[] dir = incr[i]; + final int rX = blockX + dir[0]; + if (Math.abs(lastBx - rX) > 1) continue; + final int rY = blockY + dir[1]; + if (Math.abs(lastBy - rY) > 1) continue; + final int rZ = blockZ + dir[2]; + if (Math.abs(lastBz - rZ) > 1) continue; + final int dRx = rX - lastBx; + final int dRy = rY - lastBy; + final int dRz = rZ - lastBz; + if (dRx * dRx + dRy * dRy + dRz * dRz <= dSq) continue; + if (!doesCollide(rX, rY, rZ)){ + // NOTE: Don't check "rX == targetBx && rZ == targetBz && rY == targetBy". + return true; + } + } + return false; + } + + @Override + protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, final boolean isPrimary) { + // TODO: Make an optional, more precise check (like passable) ? + // TODO: Account for primary line vs. secondary. + // TODO: isEndBlock -> blockInteractedWith, because the offset edge might be on the next block. + if (isEndBlock() || !doesCollide(blockX, blockY, blockZ)){ + lastBx = blockX; + lastBy = blockY; + lastBz = blockZ; + return true; + } + if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy){ + collides = true; + return false; + } + // Check workarounds... + if (allowsWorkaround(blockX, blockY, blockZ)){ + lastBx = blockX; + lastBy = blockY; + lastBz = blockZ; + return true; + } + // No workaround found. + collides = true; + return false; + } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PassableRayTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PassableRayTracing.java index 266f638d..ea199a74 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PassableRayTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/PassableRayTracing.java @@ -2,81 +2,81 @@ package fr.neatmonster.nocheatplus.utilities; public class PassableRayTracing extends RayTracing{ - protected BlockCache blockCache = null; - - protected boolean collides = false; - - protected boolean ignorefirst = false; + protected BlockCache blockCache = null; - public BlockCache getBlockCache() { - return blockCache; - } + protected boolean collides = false; - public void setBlockCache(BlockCache blockCache) { - this.blockCache = blockCache; - } - - /** - * Set from PlayerLocation instances. Currently takes BlockCache from the from-location. - * @param from - * @param to - */ - public void set(final PlayerLocation from, final PlayerLocation to){ - set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); - setBlockCache(from.getBlockCache()); // TODO: This might better be done extra. - } - - /* (non-Javadoc) - * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) - */ - @Override - public void set(double x0, double y0, double z0, double x1, double y1, double z1) { - super.set(x0, y0, z0, x1, y1, z1); - collides = false; - ignorefirst = false; - } - - public boolean collides(){ - return collides; - } - - /** - * Ignore the first block. Must be called after set, because set will override this with false. - */ - public void setIgnorefirst(){ - this.ignorefirst = true; - } + protected boolean ignorefirst = false; - /** - * Test if the first block is set to be ignored (resets to false with set). - * @return - */ - public boolean getIgnoreFirst(){ - return ignorefirst; - } + public BlockCache getBlockCache() { + return blockCache; + } - /** - * Remove reference to BlockCache. - */ - public void cleanup(){ - if (blockCache != null){ - blockCache = null; - } - } - - @Override - protected boolean step(final int blockX, final int blockY, final int blockZ, final double oX, final double oY, final double oZ, final double dT) { - // Just delegate. - if (step == 1 && ignorefirst){ - return true; - } - if (BlockProperties.isPassableRay(blockCache, blockX, blockY, blockZ, oX, oY, oZ, dX, dY, dZ, dT)){ - return true; - } - else{ - collides = true; - return false; - } - } + public void setBlockCache(BlockCache blockCache) { + this.blockCache = blockCache; + } + + /** + * Set from PlayerLocation instances. Currently takes BlockCache from the from-location. + * @param from + * @param to + */ + public void set(final PlayerLocation from, final PlayerLocation to){ + set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); + setBlockCache(from.getBlockCache()); // TODO: This might better be done extra. + } + + /* (non-Javadoc) + * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) + */ + @Override + public void set(double x0, double y0, double z0, double x1, double y1, double z1) { + super.set(x0, y0, z0, x1, y1, z1); + collides = false; + ignorefirst = false; + } + + public boolean collides(){ + return collides; + } + + /** + * Ignore the first block. Must be called after set, because set will override this with false. + */ + public void setIgnorefirst(){ + this.ignorefirst = true; + } + + /** + * Test if the first block is set to be ignored (resets to false with set). + * @return + */ + public boolean getIgnoreFirst(){ + return ignorefirst; + } + + /** + * Remove reference to BlockCache. + */ + public void cleanup(){ + if (blockCache != null){ + blockCache = null; + } + } + + @Override + protected boolean step(final int blockX, final int blockY, final int blockZ, final double oX, final double oY, final double oZ, final double dT, final boolean isPrimary) { + // Just delegate. + if (step == 1 && ignorefirst){ + return true; + } + if (BlockProperties.isPassableRay(blockCache, blockX, blockY, blockZ, oX, oY, oZ, dX, dY, dZ, dT)){ + return true; + } + else{ + collides = true; + return false; + } + } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java index fd7e41e5..90b6828e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/RayTracing.java @@ -33,9 +33,20 @@ public abstract class RayTracing { /** Tolerance for time, for checking the abort condition: 1.0 - t <= tol . */ protected double tol = 0.0; - /** Counting the number of steps. Step is incremented before calling step(), and is 0 after set(...). Checking this from within step means to get the current step number, checking after loop gets the number of steps done. */ + /** + * Counting the number of steps along the primary line. Step is incremented + * before calling step(), and is 0 after set(...). Checking this from within + * step means to get the current step number, checking after loop gets the + * number of steps done. + */ protected int step = 0; + /** If to call stepSecondary at all (secondary transitions).*/ + protected boolean secondaryStep = true; + + /** If to pass secondary transitions if ANY sub-call succeeds. */ + protected boolean secondaryPassSingular = false; + /** Maximum steps that will be done. */ private int maxSteps = Integer.MAX_VALUE; @@ -80,11 +91,19 @@ public abstract class RayTracing { step = 0; } - private static final double tDiff(final double dTotal, final double offset, final int block, final int endBlock) { + /** + * + * @param dTotal + * @param offset + * @param isEndBlock If the end block coordinate is reached for this axis. + * @return + */ + private static final double tDiff(final double dTotal, final double offset, final boolean isEndBlock) { + // TODO: endBlock check only for == not ? if (dTotal > 0.0) { if (offset >= 1.0) { // Static block change (e.g. diagonal move). - return 0.0; // block == endBlock ? Double.MAX_VALUE : 0.0; + return isEndBlock ? Double.MAX_VALUE : 0.0; } else { return (1.0 - offset) / dTotal; } @@ -92,7 +111,7 @@ public abstract class RayTracing { else if (dTotal < 0.0) { if (offset <= 0.0) { // Static block change (e.g. diagonal move). - return 0.0; //block == endBlock ? Double.MAX_VALUE : 0.0; + return isEndBlock ? Double.MAX_VALUE : 0.0; } else { return offset / -dTotal; } @@ -106,23 +125,31 @@ public abstract class RayTracing { * Loop through blocks. */ public void loop() { - // TODO: Might intercept 0 dist ? // Time to block edge. double tX, tY, tZ, tMin; - boolean changed; + // Number of axes to make a transition for. + int transitions; + // Transition direction per axis. + boolean transX, transY, transZ; + + // Actual loop. + // TODO: Fix last transition not taken sometimes (with "off by x-th digit" or "t=0 transition"). while (1.0 - t > tol) { - // Determine smallest time to block edge. - tX = tDiff(dX, oX, blockX, endBlockX); - tY = tDiff(dY, oY, blockY, endBlockY); - tZ = tDiff(dZ, oZ, blockZ, endBlockZ); - tMin = Math.min(tX, Math.min(tY, tZ)); + // Determine smallest time to block edge, per axis. + // TODO: if all coords are in the end block: ensure the full distance is taken towards the end coordinate. + tX = tDiff(dX, oX, blockX == endBlockX); + tY = tDiff(dY, oY, blockY == endBlockY); + tZ = tDiff(dZ, oZ, blockZ == endBlockZ); + // Adjust time. + tMin = Math.max(0.0, Math.min(tX, Math.min(tY, tZ))); if (tMin == Double.MAX_VALUE) { // All differences are 0 (no progress). if (step < 1) { - // Allow one step. + // Allow one step always. tMin = 0.0; - } else { + } + else { break; } } @@ -130,69 +157,202 @@ public abstract class RayTracing { // Set to the remaining distance (does trigger). tMin = 1.0 - t; } - // Call step with appropriate arguments. + + + // Step for the primary line. step ++; - if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin)) { + if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin, true)) { break; } - if (t + tMin >= 1.0 - tol) { // && isEndBlock()) { + + // Abort if arrived. + if (t + tMin >= 1.0 - tol && isEndBlock()) { break; } - // Advance (add to t etc.). - changed = false; + + // Determine transitions, per axis. + transitions = 0; + transX = transY = transZ = false; + if (tX == tMin && blockX != endBlockX && dX != 0.0) { + transX = true; + transitions ++; + } + if (tY == tMin && blockY != endBlockY && dY != 0.0) { + transY = true; + transitions ++; + } + if (tZ == tMin && blockZ != endBlockZ && dZ != 0.0) { + transZ = true; + transitions ++; + } + + // Advance on-block origin based on this move. + // TODO: Calculate "directly" based on this/next block or and/t? oX = Math.min(1.0, Math.max(0.0, oX + tMin * dX)); oY = Math.min(1.0, Math.max(0.0, oY + tMin * dY)); oZ = Math.min(1.0, Math.max(0.0, oZ + tMin * dZ)); - // TODO: Consider Heuristic change of the checking order for dy > 0 vs. dy < 0. - // x - if (tX == tMin && blockX != endBlockX) { - if (dX < 0) { - oX = 1.0; - blockX --; - changed = true; - } - else if (dX > 0) { - oX = 0.0; - blockX ++; - changed = true; + + // Advance time. + t = Math.min(1.0, t + tMin); + + // Handle block transitions. + if (transitions > 0) { + if (!handleTransitions(transitions, transX, transY, transZ, tMin)) { + break; } } - if (!changed) { - // y - if (tY == tMin && blockY != endBlockY) { - if (dY < 0) { - oY = 1.0; - blockY --; - changed = true; - } - else if (dY > 0) { - oY = 0.0; - blockY ++; - changed = true; - } - } - if (!changed) { - // z - if (tZ == tMin && blockZ != endBlockZ) { - if (dZ < 0) { - oZ = 1.0; - blockZ --; - changed = true; - } - else if (dZ > 0) { - oZ = 0.0; - blockZ ++; - changed = true; - } - } - } - } - t += tMin; - if (!changed || step >= maxSteps) { + + // Abort if done or exceeded maxSteps. + if (transitions == 0 || step >= maxSteps) { break; } } - // TODO: Catch special case with going beyond coordinates. + } + + /** + * + * @param transitions + * @param transX + * @param transY + * @param transZ + * @param tMin + * @return If to continue at all. + */ + protected boolean handleTransitions(final int transitions, final boolean transX, final boolean transY, final boolean transZ, final double tMin) { + // Secondary transitions. + if (transitions > 1 && secondaryStep) { + if (!handleSecondaryTransitions(transitions, transX, transY, transZ, tMin)) { + return false; + } + } + + // Apply all transitions to the primary line. + if (transX) { + if (dX > 0.0) { + blockX ++; + oX = 0.0; + } + else { + blockX --; + oX = 1.0; + } + } + if (transY) { + if (dY > 0.0) { + blockY ++; + oY = 0.0; + } + else { + blockY --; + oY = 1.0; + } + } + if (transZ) { + if (dZ > 0.0) { + blockZ ++; + oZ = 0.0; + } + else { + blockZ --; + oZ = 1.0; + } + } + return true; // Continue loop. + } + + /** + * + * @param transitions + * @param transX + * @param transY + * @param transZ + * @param tMin + * @return If to continue at all. + */ + protected boolean handleSecondaryTransitions(final int transitions, final boolean transX, final boolean transY, final boolean transZ, final double tMin) { + // Handle one transition. + if (transX) { + if (step(blockX + (dX > 0 ? 1 : -1), blockY, blockZ, dX > 0 ? 0.0 : 1.0, oY, oZ, 0.0, false)) { + if (secondaryPassSingular) { + return true; + } + } + else if (!secondaryPassSingular) { + return false; + } + } + if (transY) { + if (step(blockX, blockY + (dY > 0 ? 1 : -1), blockZ, oX, dY > 0 ? 0.0 : 1.0, oZ, 0.0, false)) { + if (secondaryPassSingular) { + return true; + } + } + else if (!secondaryPassSingular) { + return false; + } + } + if (transZ) { + if (step(blockX, blockY, blockZ + (dZ > 0 ? 1 : -1), oX, oY, dZ > 0 ? 0.0 : 1.0, 0.0, false)) { + if (secondaryPassSingular) { + return true; + } + } + else if (!secondaryPassSingular) { + return false; + } + } + + // Handle two transitions. + if (transitions == 3) { + if (!handleSecondaryDoubleTransitions(transitions, transX, transY, transZ, tMin)) { + return false; + } + } + + // If secondaryPassSingular is true, all returned false, otherwise none returned false. + return !secondaryPassSingular; + } + + /** + * + * @param transitions + * @param transX + * @param transY + * @param transZ + * @param tMin + * @return + */ + protected boolean handleSecondaryDoubleTransitions(final int transitions, final boolean transX, final boolean transY, final boolean transZ, final double tMin) { + // Two transitions at once, thus step directly. + // X and Y. + if (step(blockX + (dX > 0 ? 1 : -1), blockY + (dY > 0 ? 1 : -1), blockZ, dX > 0 ? 0.0 : 1.0, dY > 0 ? 0.0 : 1.0, oZ, 0.0, false)) { + if (secondaryPassSingular) { + return true; + } + } + else if (!secondaryPassSingular) { + return false; + } + // X and Z. + if (step(blockX + (dX > 0 ? 1 : -1), blockY, blockZ + (dZ > 0 ? 1 : -1), dX > 0 ? 0.0 : 1.0, oY, dZ > 0 ? 0.0 : 1.0, 0.0, false)) { + if (secondaryPassSingular) { + return true; + } + } + else if (!secondaryPassSingular) { + return false; + } + // Y and Z. + if (step(blockX, blockY + (dY > 0 ? 1 : -1), blockZ + (dZ > 0 ? 1 : -1), oX, dY > 0 ? 0.0 : 1.0, dZ > 0 ? 0.0 : 1.0, 0.0, false)) { + if (secondaryPassSingular) { + return true; + } + } + else if (!secondaryPassSingular) { + return false; + } + // If secondaryPassSingular is true, all returned false, otherwise none returned false. + return !secondaryPassSingular; } /** @@ -203,6 +363,10 @@ public abstract class RayTracing { return false; } + /** + * (Might later get changed to protected visibility.) + * @return + */ public boolean isEndBlock() { return blockX == endBlockX && blockY == endBlockY && blockZ == endBlockZ; } @@ -236,8 +400,24 @@ public abstract class RayTracing { /** * One step in the loop. - * @return If to continue loop. + * + * @param blockX + * The block coordinates regarded in this step. + * @param blockY + * @param blockZ + * @param oX + * Origin relative to the block coordinates. + * @param oY + * @param oZ + * @param dT + * Amount of time regarded in this step (note that 0.0 is + * possible for transitions). + * @param isPrimary + * If this is along the primary line, for which all transitions + * are done at once. The secondary line would cover all + * combinations of transitions off the primary line. + * @return If to continue processing at all. */ - protected abstract boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT); + protected abstract boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, boolean isPrimary); } diff --git a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestRayTracing.java b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestRayTracing.java index 384b8e92..67397430 100644 --- a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestRayTracing.java +++ b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestRayTracing.java @@ -21,6 +21,7 @@ public class TestRayTracing { protected static double maxFactor = 9.0; protected static int maxSteps(double dX, double dY, double dZ) { + // TODO: Better calculation. return (int) (maxFactor * (1 + Math.abs(dX) + Math.abs(dY) + Math.abs(dZ))); } @@ -32,7 +33,7 @@ public class TestRayTracing { @Override protected boolean step(int blockX, int blockY, int blockZ, double oX, - double oY, double oZ, double dT) { + double oY, double oZ, double dT, boolean isPrimary) { if (step > maxSteps(dX, dY, dZ)) { System.out.println("[WARNING] Max steps exceeded: " + maxSteps(dX, dY, dZ)); return false; @@ -80,7 +81,7 @@ public class TestRayTracing { protected double ldt = 0; - protected int step = 0; + protected double lox, loy, loz; /* (non-Javadoc) * @see fr.neatmonster.nocheatplus.utilities.RayTracing#set(double, double, double, double, double, double) @@ -91,8 +92,10 @@ public class TestRayTracing { lbx = blockX - 1; lby = blockY - 1; lbz = blockZ - 1; + lox = oX; + loy = oY; + loz = oZ; ldt = 0; - step = 0; } // private boolean ignEdge(double offset, double dTotal) { @@ -100,9 +103,8 @@ public class TestRayTracing { // } @Override - protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) { + protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, boolean isPrimary) { // TODO: This does not check last step for some occasions where it should. - step ++; if (dT < 0.0) { doFail("dT < 0 at t = " + StringUtil.fdec3.format(t), coords); @@ -130,21 +132,28 @@ public class TestRayTracing { lbx = blockX; lby = blockY; lbz = blockZ; + lox = oX; + loy = oY; + loz = oZ; ldt = dT; - if (step > maxSteps(dX, dY, dZ)) doFail("max steps exceeded: " + maxSteps(dX, dY, dZ), coords); + if (step > maxSteps(dX, dY, dZ)) { + doFail("max steps exceeded: " + maxSteps(dX, dY, dZ), coords); + } return true; } private void checkOffset(double offset, String name) { - if (offset < 0.0 || offset > 1.0) doFail("Bad " + name + "-offset: " + offset, coords); + if (offset < 0.0 || offset > 1.0) { + doFail("Bad " + name + "-offset: " + offset, coords); + } } @Override public void loop() { super.loop(); -// checkBlockTarget(coords[3], blockX, oX, dX, ldt, "x"); -// checkBlockTarget(coords[4], blockY, oY, dY, ldt, "y"); -// checkBlockTarget(coords[5], blockZ, oZ, dZ, ldt, "z"); + checkBlockTarget(coords[3], lbx, lox, dX, ldt, "x"); + checkBlockTarget(coords[4], lby, loy, dY, ldt, "y"); + checkBlockTarget(coords[5], lbz, loz, dZ, ldt, "z"); } private void checkBlockTarget(double target, int current, double offset, double dTotal, double dT, String name) { @@ -153,19 +162,22 @@ public class TestRayTracing { // TODO: Might do with or without these ? // if (current == b + 1 && dTotal > 0 && offset == 0) return; // if (current == b - 1 && dTotal < 0 && offset == 1) return; - if (Math.abs(dT * dTotal + offset + (double) current - target) <= 0.001) { + double diff = Math.abs(dT * dTotal + offset + (double) current - target); + if (diff <= 0.001) { + // TODO: Test how far off this usually is... // TODO: Narrow down by edge coordinates or so. return; } System.out.println(target + "|" + current + "|" + offset + "|" + dT * dTotal); // Failure. - doFail("Bad target " + name + "-coordinate: " + current + " instead of " + b, coords); + doFail("Bad target " + name + "-coordinate: " + current + " instead of " + b + " (" + diff + " off)", coords); } } }; rt.loop(); if (!rt.isEndBlock()) { - //doFail("Incorrect end block.", coords); + // TODO: Fix last transition not taken sometimes (with "off by x-th digit" or "t=0 transition"). +// doFail("Incorrect end block.", coords); } return rt; } @@ -177,9 +189,6 @@ public class TestRayTracing { if (done != steps) { doFail("Wrong number of steps: " + done + " instead of " + steps, coords); } - if (!crt.isEndBlock()) { - //doFail("Incorrect end block.", coords); - } return crt; } @@ -192,7 +201,7 @@ public class TestRayTracing { public static RayTracing dumpRawRayTracing(final double[] coords) { RayTracing rt = new RayTracing(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]) { @Override - protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) { + protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, boolean isPrimary) { dump(blockX, blockY, blockZ, oX, oY, oZ, t, dT); if (step > maxSteps(dX, dY, dZ)) { System.out.println("[WARNING] Max steps exceeded: " + maxSteps(dX, dY, dZ)); @@ -211,7 +220,7 @@ public class TestRayTracing { @Test public void testNumberOfSteps() { // Hand picked stuff. - checkNumberOfSteps(new double[]{0.5, 0.5, 0.5, 1.5, -0.5, 1.5}, 4); + checkNumberOfSteps(new double[]{0.5, 0.5, 0.5, 1.5, -0.5, 1.5}, 2); } @Test @@ -219,13 +228,16 @@ public class TestRayTracing { // Past failures / making a difference. for (double[] coords : new double[][] { // Sort by x0. - new double[]{-9.873, -4.773, -3.387, -0.161, -1.879, -7.079}, - new double[]{-3.0066423238842366, 0.8056808285866079, 5.359238045631369 , 2.0000000356757375, -2.3002237817433757, -5.889349195033338}, - new double[]{2.5619753859456917, -5.010424935746547, -7.39326637860553 , -4.678643570182639, -2.0000000105642313, -4.634727842675916}, - new double[]{7.388348424961977, -8.000000029346532, -2.5365675909347507 , 2.17126848312847, 3.236994108042559, -8.423292642985071}, - new double[]{7.525633617461991, 2.654408573114717, 3.5119744782127893 , 9.99999995904821, 9.599753890871172, 6.721727939686946}, - new double[] {-6.0, -4.0, -3.0 , -4.0, -3.0, -2.0}, - new double[]{-3.0, 3.0, -6.0 , 2.0, -3.0, 4.0}, + {-9.873, -4.773, -3.387, -0.161, -1.879, -7.079}, + {-3.0066423238842366, 0.8056808285866079, 5.359238045631369 , 2.0000000356757375, -2.3002237817433757, -5.889349195033338}, + {2.5619753859456917, -5.010424935746547, -7.39326637860553 , -4.678643570182639, -2.0000000105642313, -4.634727842675916}, + {7.388348424961977, -8.000000029346532, -2.5365675909347507 , 2.17126848312847, 3.236994108042559, -8.423292642985071}, + {7.525633617461991, 2.654408573114717, 3.5119744782127893 , 9.99999995904821, 9.599753890871172, 6.721727939686946}, + {1.1, 1.1, 1.1 , 1.3, 1.3, 1.3}, + {1.3, 1.3, 1.3 , 1.1, 1.1, 1.1}, + {1.1, 1.1, 1.1 , 1.1, 1.1, 1.1}, + {-6.0, -4.0, -3.0 , -4.0, -3.0, -2.0}, + {-3.0, 3.0, -6.0 , 2.0, -3.0, 4.0}, }) { checkConsistency(coords); } @@ -237,7 +249,6 @@ public class TestRayTracing { checkConsistency(randomCoords(10.0)); } - // TODO: make these work. for (int i = 0; i < (e ? 10000000 : 1000); i++) { checkConsistency(randomBlockCoords(6)); }