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 b31f0575..f09fde7a 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
@@ -15,195 +15,207 @@ import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.InteractRayTracing;
public class Visible extends Check {
-
- /** Offset from bounds to estimate some reference end position for ray-tracing. */
- private static final double offset = 0.0001;
-
- private BlockCache blockCache;
-
- private final InteractRayTracing rayTracing = new InteractRayTracing(false);
- public Visible() {
- super(CheckType.BLOCKINTERACT_VISIBLE);
- blockCache = mcAccess.getBlockCache(null);
- rayTracing.setMaxSteps(60); // TODO: Configurable ?
- }
-
- /* (non-Javadoc)
- * @see fr.neatmonster.nocheatplus.checks.Check#setMCAccess(fr.neatmonster.nocheatplus.compat.MCAccess)
- */
- @Override
- public void setMCAccess(MCAccess mcAccess) {
- super.setMCAccess(mcAccess);
- // Renew the BlockCache instance.
- blockCache = mcAccess.getBlockCache(null);
- }
-
- private static final double getEnd(final double[] bounds, final int index, final int mod){
- if (bounds == null){
- return 0.5 + (0.5 + offset) * mod;
- }
- if (mod == 0){
- // Middle.
- return (bounds[index] + bounds[index + 3]) / 2.0;
- }
- else if (mod == 1){
- // TODO: Slightly outside or dependent on exact position (inside, exact edge, outside)?
- return Math.min(1.0, bounds[index + 3]) + offset;
- }
- else if (mod == -1){
- // TODO: Slightly outside or dependent on exact position (inside, exact edge, outside)?
- return Math.max(0.0, bounds[index]) - offset;
- }
- else{
- throw new IllegalArgumentException("BlockFace.getModX|Y|Z must be 0, 1 or -1.");
- }
- }
+ /** Offset from bounds to estimate some reference end position for ray-tracing. */
+ private static final double offset = 0.0001;
- public boolean check(final Player player, final Location loc, final Block block, final BlockFace face, final Action action, final BlockInteractData data, final BlockInteractConfig cc) {
- // TODO: This check might make parts of interact/blockbreak/... + direction (+?) obsolete.
- // TODO: Might confine what to check for (left/right-click, target blocks depending on item in hand, container blocks).
- final boolean collides;
- final int blockX = block.getX();
- final int blockY = block.getY();
- final int blockZ = block.getZ();
- final double eyeX = loc.getX();
- final double eyeY = loc.getY() + player.getEyeHeight();
- final double eyeZ = loc.getZ();
-
- // TODO: Add tags for fail_passable, fail_raytracing, (fail_face).
- // TODO: Reachable face check ?
-
- if (blockX == Location.locToBlock(eyeX) && blockZ == Location.locToBlock(eyeZ) && block.getY() == Location.locToBlock(eyeY)){
- // Player is interacting with the block their head is in.
- // TODO: Should the reachable-face-check be done here too (if it is added at all)?
- collides = false;
- }
- else{
- // Initialize.
- blockCache.setAccess(loc.getWorld());
- rayTracing.setBlockCache(blockCache);
-
- collides = checkRayTracing(eyeX, eyeY, eyeZ, blockX, blockY, blockZ, face);
-
- // Cleanup.
- rayTracing.cleanup();
- blockCache.cleanup();
- }
-
- if (data.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
- // TODO: Tags
- player.sendMessage("Interact visible: " + (action == Action.RIGHT_CLICK_BLOCK ? "right" : "left") + " collide=" + rayTracing.collides());
+ private BlockCache blockCache;
+
+ /**
+ * Strict set to false, due to false positives.
+ */
+ private final InteractRayTracing rayTracing = new InteractRayTracing(false);
+
+ public Visible() {
+ super(CheckType.BLOCKINTERACT_VISIBLE);
+ blockCache = mcAccess.getBlockCache(null);
+ rayTracing.setMaxSteps(60); // TODO: Configurable ?
+ }
+
+ /* (non-Javadoc)
+ * @see fr.neatmonster.nocheatplus.checks.Check#setMCAccess(fr.neatmonster.nocheatplus.compat.MCAccess)
+ */
+ @Override
+ public void setMCAccess(MCAccess mcAccess) {
+ super.setMCAccess(mcAccess);
+ // Renew the BlockCache instance.
+ blockCache = mcAccess.getBlockCache(null);
+ }
+
+ private static final double getEnd(final double[] bounds, final int index, final int mod){
+ if (bounds == null){
+ return 0.5 + (0.5 + offset) * mod;
}
-
- // Actions ?
- boolean cancel = false;
- if (collides){
- data.visibleVL += 1;
- if (executeActions(player, data.visibleVL, 1, cc.visibleActions)){
- cancel = true;
- }
- }
- else{
- data.visibleVL *= 0.99;
- }
-
- return cancel;
- }
-
- private boolean checkRayTracing(final double eyeX, final double eyeY, final double eyeZ, final int blockX, final int blockY, final int blockZ, final BlockFace face){
-
- // Estimated target-middle-position (does it for most cases).
- @SuppressWarnings("deprecation")
- final double[] bounds = BlockProperties.getCorrectedBounds(blockCache, blockX, blockY, blockZ);
- final int modX = face.getModX();
- final int modY = face.getModY();
- final int modZ = face.getModZ();
- final double estX = (double) blockX + getEnd(bounds, 0, modX);
- final double estY = (double) blockY + getEnd(bounds, 1, modY);
- final double estZ = (double) blockZ + getEnd(bounds, 2, modZ);
- final int bEstX = Location.locToBlock(estX);
- final int bEstY = Location.locToBlock(estY);
- final int bEstZ = Location.locToBlock(estZ);
- final int estId = blockCache.getTypeId(bEstX, bEstY, bEstZ);
-
- // Ignore passable if the estimate is on the clicked block.
- final boolean skipPassable = blockX == bEstX && blockY == bEstY && blockZ == bEstZ;
-
- // TODO: Might also use looking direction (test how accurate).
- return checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ, estId, bounds, modX, modY, modZ, skipPassable);
-
- }
+ if (mod == 0){
+ // Middle.
+ return (bounds[index] + bounds[index + 3]) / 2.0;
+ }
+ else if (mod == 1){
+ // TODO: Slightly outside or dependent on exact position (inside, exact edge, outside)?
+ return Math.min(1.0, bounds[index + 3]) + offset;
+ }
+ else if (mod == -1){
+ // TODO: Slightly outside or dependent on exact position (inside, exact edge, outside)?
+ return Math.max(0.0, bounds[index]) - offset;
+ }
+ else{
+ throw new IllegalArgumentException("BlockFace.getModX|Y|Z must be 0, 1 or -1.");
+ }
+ }
- /**
- * Recursively check alternate positions.
- * @param eyeX
- * @param eyeY
- * @param eyeZ
- * @param estX
- * @param estY
- * @param estZ
- * @param estId
- * @param modX
- * @param modY
- * @param modZ
- * @param skipPassable
- * @return
- */
- private boolean checkCollision(final double eyeX, final double eyeY, final double eyeZ, final double estX, final double estY, final double estZ, final int estId, final double[] bounds, final int modX, final int modY, final int modZ, final boolean skipPassable) {
- // Check current position.
- if (skipPassable || BlockProperties.isPassable(blockCache, estX, estY, estZ, estId)){
- // Perform ray-tracing.
- rayTracing.set(eyeX, eyeY, eyeZ, estX, estY, estZ);
- rayTracing.loop();
- if (!rayTracing.collides() && rayTracing.getStepsDone() < rayTracing.getMaxSteps()){
- return false;
- }
- }
- // Note: Center of bounds is used for mod == 0.
- // TODO: Could "sort" positions by setting signum of d by which is closer to the player.
- // TODO: Could consider slightly in-set positions.
- if (modX == 0){
- // TODO: Might ensure to check if it is the same block?
- final double d = bounds == null ? 0.5 : (bounds[3] - bounds[0]) / 2.0;
- if (d >= 0.05){
- // Recursion with adapted x position (if differs enough from bounds.
- if (!checkCollision(eyeX, eyeY, eyeZ, estX - d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable)){
- return false;
- }
- if (!checkCollision(eyeX, eyeY, eyeZ, estX + d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable)){
- return false;
- }
- }
- }
- if (modZ == 0){
- // TODO: Might ensure to check if it is the same block?
- final double d = bounds == null ? 0.5 : (bounds[5] - bounds[2]) / 2.0;
- if (d >= 0.05){
- // Recursion with adapted x position (if differs enough from bounds.
- if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ - d, estId, bounds, 1, modY, 1, skipPassable)){
- return false;
- }
- if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ + d, estId, bounds, 1, modY, 1, skipPassable)){
- return false;
- }
- }
- }
- if (modY == 0){
- // TODO: Might ensure to check if it is the same block?
- final double d = bounds == null ? 0.5 : (bounds[4] - bounds[1]) / 2.0;
- if (d >= 0.05){
- // Recursion with adapted x position (if differs enough from bounds.
- if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY - d, estZ, estId, bounds, 1, 1, 1, skipPassable)){
- return false;
- }
- if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY + d, estZ, estId, bounds, 1, 1, 1, skipPassable)){
- return false;
- }
- }
- }
-
- return true;
- }
+ public boolean check(final Player player, final Location loc, final Block block, final BlockFace face, final Action action, final BlockInteractData data, final BlockInteractConfig cc) {
+ // TODO: This check might make parts of interact/blockbreak/... + direction (+?) obsolete.
+ // TODO: Might confine what to check for (left/right-click, target blocks depending on item in hand, container blocks).
+ final boolean collides;
+ final int blockX = block.getX();
+ final int blockY = block.getY();
+ final int blockZ = block.getZ();
+ final double eyeX = loc.getX();
+ final double eyeY = loc.getY() + player.getEyeHeight();
+ final double eyeZ = loc.getZ();
+
+ // TODO: Add tags for fail_passable, fail_raytracing, (fail_face).
+ // TODO: Reachable face check ?
+
+ if (blockX == Location.locToBlock(eyeX) && blockZ == Location.locToBlock(eyeZ) && block.getY() == Location.locToBlock(eyeY)){
+ // Player is interacting with the block their head is in.
+ // TODO: Should the reachable-face-check be done here too (if it is added at all)?
+ collides = false;
+ }
+ else{
+ // Initialize.
+ blockCache.setAccess(loc.getWorld());
+ rayTracing.setBlockCache(blockCache);
+
+ collides = checkRayTracing(eyeX, eyeY, eyeZ, blockX, blockY, blockZ, face);
+
+ // Cleanup.
+ rayTracing.cleanup();
+ blockCache.cleanup();
+ }
+
+ if (data.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
+ // TODO: Tags
+ player.sendMessage("Interact visible: " + (action == Action.RIGHT_CLICK_BLOCK ? "right" : "left") + " collide=" + rayTracing.collides());
+ }
+
+ // Actions ?
+ boolean cancel = false;
+ if (collides){
+ data.visibleVL += 1;
+ if (executeActions(player, data.visibleVL, 1, cc.visibleActions)){
+ cancel = true;
+ }
+ }
+ else{
+ data.visibleVL *= 0.99;
+ }
+
+ return cancel;
+ }
+
+ private boolean checkRayTracing(final double eyeX, final double eyeY, final double eyeZ, final int blockX, final int blockY, final int blockZ, final BlockFace face){
+
+ /*
+ * TODO: Always use the exact looking direction first (calculate where
+ * it hits the target block, and which faces are exposed then, estimate
+ * alternative positions based on that, get rid of the workaround).
+ */
+ /*
+ * TODO: Consider using (slightly less than) full block bounds always ?
+ * (alt: 2,3 classes of size)
+ */
+ // Estimated target-middle-position (does it for most cases).
+ @SuppressWarnings("deprecation")
+ final double[] bounds = BlockProperties.getCorrectedBounds(blockCache, blockX, blockY, blockZ);
+ final int modX = face.getModX();
+ final int modY = face.getModY();
+ final int modZ = face.getModZ();
+ final double estX = (double) blockX + getEnd(bounds, 0, modX);
+ final double estY = (double) blockY + getEnd(bounds, 1, modY);
+ final double estZ = (double) blockZ + getEnd(bounds, 2, modZ);
+ final int bEstX = Location.locToBlock(estX);
+ final int bEstY = Location.locToBlock(estY);
+ final int bEstZ = Location.locToBlock(estZ);
+ final int estId = blockCache.getTypeId(bEstX, bEstY, bEstZ);
+
+ // Ignore passable if the estimate is on the clicked block.
+ final boolean skipPassable = blockX == bEstX && blockY == bEstY && blockZ == bEstZ;
+
+ // TODO: Might also use looking direction (test how accurate).
+ return checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ, estId, bounds, modX, modY, modZ, skipPassable, blockX, blockY, blockZ);
+
+ }
+
+ /**
+ * Recursively check alternate positions.
+ * @param eyeX
+ * @param eyeY
+ * @param eyeZ
+ * @param estX
+ * @param estY
+ * @param estZ
+ * @param estId
+ * @param modX
+ * @param modY
+ * @param modZ
+ * @param skipPassable
+ * @return
+ */
+ private boolean checkCollision(final double eyeX, final double eyeY, final double eyeZ, final double estX, final double estY, final double estZ, final int estId, final double[] bounds, final int modX, final int modY, final int modZ, final boolean skipPassable, final int clickedX, final int clickedY, final int clickedZ) {
+ // Check current position.
+ if (skipPassable || BlockProperties.isPassable(blockCache, estX, estY, estZ, estId)){
+ // Perform ray-tracing.
+ rayTracing.set(eyeX, eyeY, eyeZ, estX, estY, estZ, clickedX, clickedY, clickedZ);
+ rayTracing.loop();
+ if (!rayTracing.collides() && rayTracing.getStepsDone() < rayTracing.getMaxSteps()){
+ return false;
+ }
+ }
+ // Note: Center of bounds is used for mod == 0.
+ // TODO: Could "sort" positions by setting signum of d by which is closer to the player.
+ // TODO: Could consider slightly in-set positions.
+ if (modX == 0){
+ // TODO: Might ensure to check if it is the same block?
+ final double d = bounds == null ? 0.5 : (bounds[3] - bounds[0]) / 2.0;
+ if (d >= 0.05){
+ // Recursion with adapted x position (if differs enough from bounds.
+ if (!checkCollision(eyeX, eyeY, eyeZ, estX - d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable, clickedX, clickedY, clickedZ)){
+ return false;
+ }
+ if (!checkCollision(eyeX, eyeY, eyeZ, estX + d, estY, estZ, estId, bounds, 1, modY, modZ, skipPassable, clickedX, clickedY, clickedZ)){
+ return false;
+ }
+ }
+ }
+ if (modZ == 0){
+ // TODO: Might ensure to check if it is the same block?
+ final double d = bounds == null ? 0.5 : (bounds[5] - bounds[2]) / 2.0;
+ if (d >= 0.05){
+ // Recursion with adapted x position (if differs enough from bounds.
+ if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ - d, estId, bounds, 1, modY, 1, skipPassable, clickedX, clickedY, clickedZ)){
+ return false;
+ }
+ if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY, estZ + d, estId, bounds, 1, modY, 1, skipPassable, clickedX, clickedY, clickedZ)){
+ return false;
+ }
+ }
+ }
+ if (modY == 0){
+ // TODO: Might ensure to check if it is the same block?
+ final double d = bounds == null ? 0.5 : (bounds[4] - bounds[1]) / 2.0;
+ if (d >= 0.05){
+ // Recursion with adapted x position (if differs enough from bounds.
+ if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY - d, estZ, estId, bounds, 1, 1, 1, skipPassable, clickedX, clickedY, clickedZ)){
+ return false;
+ }
+ if (!checkCollision(eyeX, eyeY, eyeZ, estX, estY + d, estZ, estId, bounds, 1, 1, 1, skipPassable, clickedX, clickedY, clickedZ)){
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java
index 0079d0f2..d437c410 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java
@@ -88,6 +88,52 @@ public class FakeBlockCache extends BlockCache {
}
}
+ public void fill(int x1, int y1, int z1, int x2, int y2, int z2, Material type) {
+ fill(x1, y1, z1, x2, y2, z2, BlockProperties.getId(type), 0, new double[]{0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
+ }
+
+ public void fill(int x1, int y1, int z1, int x2, int y2, int z2, int typeId, int data, double[] bounds) {
+ for (int x = x1; x <= x2; x++) {
+ for (int y = y1; y <= y2; y ++) {
+ for (int z = z1; z <= z2; z++) {
+ set(x, y, z, typeId, data, bounds);
+ }
+ }
+ }
+ }
+
+ public void walls(int x1, int y1, int z1, int x2, int y2, int z2, Material type) {
+ walls(x1, y1, z1, x2, y2, z2, BlockProperties.getId(type), 0, new double[]{0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
+ }
+
+ public void walls(int x1, int y1, int z1, int x2, int y2, int z2, int typeId, int data, double[] bounds) {
+ for (int x = x1; x <= x2; x++) {
+ for (int y = y1; y <= y2; y ++) {
+ for (int z = z1; z <= z2; z++) {
+ if (x == x1 || x == x2 || z == z1 || z == z2) {
+ set(x, y, z, typeId, data, bounds);
+ }
+ }
+ }
+ }
+ }
+
+ public void room(int x1, int y1, int z1, int x2, int y2, int z2, Material type) {
+ room(x1, y1, z1, x2, y2, z2, BlockProperties.getId(type), 0, new double[]{0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
+ }
+
+ public void room(int x1, int y1, int z1, int x2, int y2, int z2, int typeId, int data, double[] bounds) {
+ for (int x = x1; x <= x2; x++) {
+ for (int y = y1; y <= y2; y ++) {
+ for (int z = z1; z <= z2; z++) {
+ if (x == x1 || x == x2 || z == z1 || z == z2 || y == y1 || y == y2) {
+ set(x, y, z, typeId, data, bounds);
+ }
+ }
+ }
+ }
+ }
+
@Override
public void setAccess(World world) {
// Ignore.
@@ -123,7 +169,7 @@ public class FakeBlockCache extends BlockCache {
@Override
public boolean standsOnEntity(Entity entity, double minX, double minY,
double minZ, double maxX, double maxY, double maxZ) {
- // TODO: Consider adding blocks where this might be the case.
+ // TODO: Consider adding cuboids which mean "ground" if the foot location is inside.
return false;
}
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 0fa13cf5..26dec161 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InteractRayTracing.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/InteractRayTracing.java
@@ -1,5 +1,8 @@
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.
@@ -25,11 +28,13 @@ public class InteractRayTracing extends RayTracing {
protected int lastBx, lastBy, lastBz;
- public InteractRayTracing(){
+ protected int targetX, targetY, targetZ;
+
+ public InteractRayTracing() {
super();
}
- public InteractRayTracing(boolean strict){
+ public InteractRayTracing(boolean strict) {
super();
this.strict = strict;
}
@@ -42,27 +47,43 @@ public class InteractRayTracing extends RayTracing {
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) {
+ set(x0, y0, z0, x1, y1, z1, Location.locToBlock(x1), Location.locToBlock(y1), Location.locToBlock(z1));
+ // Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ *
+ * @param x0
+ * @param y0
+ * @param z0
+ * @param x1
+ * @param y1
+ * @param z1
+ * @param targetX The block clicked/interacted with (can be different to the end point of ray-tracing, or ignored with Integer.MAX_VALUE).
+ * @param targetY
+ * @param targetZ
+ */
+ public void set(double x0, double y0, double z0, double x1, double y1, double z1, int targetX, int targetY, int targetZ) {
super.set(x0, y0, z0, x1, y1, z1);
collides = false;
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
+ this.targetX = targetX;
+ this.targetY = targetY;
+ this.targetZ = targetZ;
}
- public boolean collides(){
+ public boolean collides() {
return collides;
}
/**
* Remove reference to BlockCache.
*/
- public void cleanup(){
- if (blockCache != null){
+ public void cleanup() {
+ if (blockCache != null) {
blockCache = null;
}
}
@@ -74,49 +95,75 @@ public class InteractRayTracing extends RayTracing {
* @param blockZ
* @return
*/
- private final boolean doesCollide(int blockX, int blockY, int blockZ){
+ private final boolean doesCollide(final int blockX, final int blockY, final int blockZ) {
final int id = blockCache.getTypeId(blockX, blockY, blockZ);
final long flags = BlockProperties.getBlockFlags(id);
- if ((flags & BlockProperties.F_SOLID) == 0){
+ 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){
+ 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;
+ if (!blockCache.isFullBounds(blockX, blockY, blockZ)) {
+ return false;
+ }
return true;
}
+ /**
+ * Test if the primary line is on the block interacted with (may be a
+ * different one that the end point of ray-tracing).
+ *
+ * @return
+ */
+ public boolean isTargetBlock() {
+ return targetX != Integer.MAX_VALUE && blockX == targetX && blockY == targetY && blockZ == targetZ;
+ }
+
/**
* 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: Recode this/other.
+
+ // TODO: This could allow some bypasses for "strange" setups.
// TODO: Consider using distance to target as heuristic ? [should not get smaller !?]
+ // TODO: Consider (min/max) offset for distance.
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++){
+ // TODO: Limit distance more here !?
+ 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;
+ if (Math.abs(lastBx - rX) > 1) {
+ continue;
+ }
final int rY = blockY + dir[1];
- if (Math.abs(lastBy - rY) > 1) continue;
+ if (Math.abs(lastBy - rY) > 1) {
+ continue;
+ }
final int rZ = blockZ + dir[2];
- if (Math.abs(lastBz - rZ) > 1) continue;
+ 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)){
+ 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;
}
@@ -125,22 +172,24 @@ public class InteractRayTracing extends RayTracing {
}
@Override
- protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT, final boolean isPrimary) {
+ 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) {
// 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;
+ // TODO: isTargetBlock checks the primary line (!, might be ok.).
+ if (isTargetBlock() || !doesCollide(blockX, blockY, blockZ)) {
+ if (isPrimary) {
+ lastBx = blockX;
+ lastBy = blockY;
+ lastBz = blockZ;
+ }
return true;
}
- if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy){
+ if (strict || blockX == lastBx && blockZ == lastBz && blockY == lastBy) {
collides = true;
return false;
}
// Check workarounds...
- if (allowsWorkaround(blockX, blockY, blockZ)){
+ if (isPrimary && allowsWorkaround(blockX, blockY, blockZ)) {
lastBx = blockX;
lastBy = blockY;
lastBz = blockZ;
diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java
index faa1a9d3..110855ec 100644
--- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java
+++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccessFactory.java
@@ -111,7 +111,7 @@ public class MCAccessFactory {
};
// TEMP END //
- // 1.7.10
+ // 1.7.10
try{
return new MCAccessCB3100();
}
diff --git a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestInteractRayTracing.java b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestInteractRayTracing.java
new file mode 100644
index 00000000..e83c4eb5
--- /dev/null
+++ b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestInteractRayTracing.java
@@ -0,0 +1,145 @@
+package fr.neatmonster.nocheatplus.test;
+
+import org.bukkit.Material;
+import org.junit.Test;
+
+import fr.neatmonster.nocheatplus.logging.StaticLog;
+import fr.neatmonster.nocheatplus.utilities.FakeBlockCache;
+import fr.neatmonster.nocheatplus.utilities.InteractRayTracing;
+import fr.neatmonster.nocheatplus.utilities.build.BuildParameters;
+
+public class TestInteractRayTracing {
+
+ public final class CenteredInteractRayTracing extends InteractRayTracing {
+ private int centerX, centerY, centerZ;
+ public CenteredInteractRayTracing(boolean strict, int centerX, int centerY, int centerZ) {
+ super(strict);
+ this.centerX = centerX;
+ this.centerY = centerY;
+ this.centerZ = centerZ;
+ }
+ @Override
+ public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
+ super.set(x0, y0, z0, x1, y1, z1, centerX, centerY, centerZ);
+ }
+ }
+
+ // TODO: Blunt copy and paste from TestPassableRayTracing, add something that makes sense.
+
+ public TestInteractRayTracing() {
+ StaticLog.setUseLogManager(false);
+ BlockTests.initBlockProperties();
+ StaticLog.setUseLogManager(true);
+ }
+
+ @Test
+ public void testAir() {
+ FakeBlockCache bc = new FakeBlockCache();
+ InteractRayTracing rt = new InteractRayTracing();
+ rt.setBlockCache(bc);
+ double[] coords = new double[]{0.5, 0.5, -0.5, 0.5, 0.5, 1.5};
+ rt.set(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+ rt.loop();
+ if (rt.collides()) {
+ TestRayTracing.doFail("Expect not to collide with air.", coords);
+ }
+ if (rt.getStepsDone() > 4) {
+ TestRayTracing.doFail("Expect less than 4 steps for moving straight through a block of air.", coords);
+ }
+ rt.cleanup();
+ bc.cleanup();
+ }
+
+ /**
+ * Moving diagonally through an "empty corner", seen from above:
+ * ox
+ * xo
+ */
+ @Test
+ public void testEmptyCorner() {
+ FakeBlockCache bc = new FakeBlockCache();
+ // The "empty corner" setup.
+ for (int y = 70 ; y < 73; y ++) {
+ bc.set(10, y, 10, Material.STONE);
+ bc.set(11, y, 11, Material.STONE);
+ }
+ // Ground.
+ for (int x = 9; x < 13; x++) {
+ for (int z = 9; z < 13; z++) {
+ bc.set(x, 69, z, Material.STONE);
+ }
+ }
+ // TODO: Make work with strict set to false.
+ InteractRayTracing rt = new InteractRayTracing(true);
+ //InteractRayTracing rt = new InteractRayTracing(false);
+ rt.setBlockCache(bc);
+ // TODO: More Directions, also just behind the corner.
+ double[][] setups = new double[][] {
+ // Slightly off the middle (11, y, 11)
+ {11.4, 70.0, 10.4, 10.6, 70.0, 11.4},
+ // Going exactly through the middle (11, y, 11)
+ {11.4, 70.0, 10.6, 10.6, 70.0, 11.4},
+ {11.5, 70.0, 10.5, 10.5, 70.0, 11.5},
+ };
+ TestRayTracing.runCoordinates(rt, setups, true, false, 3.0, true);
+ rt.cleanup();
+ bc.cleanup();
+ }
+
+ @Test
+ public void testWall() {
+ FakeBlockCache bc = new FakeBlockCache();
+ // Wall using full blocks.
+ bc.walls(0, 65, 0, 16, 67, 0, Material.STONE);
+ // Ground using full blocks (roughly 16 margin to each side).
+ bc.fill(-16, 64, -16, 32, 64, 16, Material.STONE);
+ // TODO: Test chest like bounds for target blocks.
+ InteractRayTracing rt = new InteractRayTracing(false);
+ rt.setBlockCache(bc);
+ // TODO: More cases, head inside block itself, angles, ...
+ double[][] noCollision = new double[][] {
+ {8.5, 66.75, 1.2 , 8.5, 65.8, 1.0},
+ {8.5, 66.75, 1.2 , 8.5, 69.0, 0.0}, // "Above enough".
+ };
+ TestRayTracing.runCoordinates(rt, noCollision, false, true, 3.0, true);
+ double[][] shouldCollide = new double[][] {
+ {8.5, 66.75, 1.2 , 8.5, 65.8, 0.0},
+ {8.5, 66.75, 1.2 , 8.5, 65.8, -0.2},
+ };
+ TestRayTracing.runCoordinates(rt, shouldCollide, true, false, 3.0, true);
+ rt.cleanup();
+ bc.cleanup();
+ }
+
+ @Test
+ public void testRoom() {
+ // TODO: Test for differing middle points (negative to positive range, random, selected rays).
+ FakeBlockCache bc = new FakeBlockCache();
+ bc.room(-1, 64, -1, 1, 66, 1, Material.STONE);
+ // Note that reversed checks are slightly different with the centered version, but start + end blocks are air anyway.
+ double[] middle = new double[] {0.5, 65.5, 0.5}; // Free spot.
+ // TODO: Must work with strict set to false.
+ //CenteredInteractRayTracing rt = new CenteredInteractRayTracing(false, 0, 65, 0);
+ CenteredInteractRayTracing rt = new CenteredInteractRayTracing(true, 0, 65, 0);
+ rt.setBlockCache(bc);
+ double[][] pastFailures = new double[][] {
+ {2.1393379885667643, 67.18197661625649, 1.7065201483677281 , 0.0, 65.0, 0.0},
+ {2.7915547712543676, 66.65545738305906, 1.310222428430474 , 0.0, 65.0, 0.0},
+ {0.0, 65.0, 4.5 , 0.0, 65.0, 1.0}, // strict is false.
+ {-3.5, 61.5, -3.5 , 0.0, 65.0, 0.0} // strict is false.
+ };
+ TestRayTracing.runCoordinates(rt, pastFailures, true, false, 3, true);
+ boolean intense = BuildParameters.testLevel > 1;
+ for (double x = -0.5; x < 1.0; x += 0.5) {
+ for (double y = -0.5; y < 1.0; y += 0.5) {
+ for (double z = -0.5; z < 1.0; z += 0.5) {
+ double add = Math.abs(x) + Math.abs(y) + Math.abs(z);
+ TestRayTracing.runCenterRays(rt, middle[0] + x, middle[1] + y, middle[2] + z, 2.0 + add, intense ? 10000 : 1000, true, false, true);
+ }
+ }
+ }
+ rt.cleanup();
+ bc.cleanup();
+ }
+
+}
diff --git a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestPassableRayTracing.java b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestPassableRayTracing.java
index 80fa4235..20d4ece3 100644
--- a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestPassableRayTracing.java
+++ b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestPassableRayTracing.java
@@ -6,22 +6,23 @@ import org.junit.Test;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.utilities.FakeBlockCache;
import fr.neatmonster.nocheatplus.utilities.PassableRayTracing;
+import fr.neatmonster.nocheatplus.utilities.build.BuildParameters;
public class TestPassableRayTracing {
-
+
// TODO: Moving into a block,
// TODO: Moving out of a block
// TODO: Moving horizontally on various kinds of ground (normal, half blocks)
// TODO: Moving up stairs etc ?
// TODO: From ground and onto ground moves, onto-edge moves (block before edge, into block, etc).
// TODO: Randomized tests (Collide with inner sphere, not collide with outer sphere).
-
+
public TestPassableRayTracing() {
StaticLog.setUseLogManager(false);
BlockTests.initBlockProperties();
StaticLog.setUseLogManager(true);
}
-
+
@Test
public void testAir() {
FakeBlockCache bc = new FakeBlockCache();
@@ -39,7 +40,7 @@ public class TestPassableRayTracing {
rt.cleanup();
bc.cleanup();
}
-
+
@Test
public void testThroughOneBlock() {
FakeBlockCache bc = new FakeBlockCache();
@@ -47,26 +48,26 @@ public class TestPassableRayTracing {
PassableRayTracing rt = new PassableRayTracing();
rt.setBlockCache(bc);
double[][] setups = new double[][] {
- // Through the middle of the block.
- {0.5, 0.5, -0.5, 0.5, 0.5, 1.5},
- {-0.5, 0.5, 0.5, 1.5, 0.5, 0.5},
- {0.5, -0.5, 0.5, 0.5, 1.5, 0.5},
- // Along the edges.
- {0.5, 0.0, -0.5, 0.5, 0.0, 1.5},
- {-0.5, 0.0, 0.5, 1.5, 0.0, 0.5},
- // Exactly diagonal.
- {-0.5, -0.5, -0.5, 1.5, 1.5, 1.5}, // 3d
- {-0.5, 0.0, -0.5, 1.5, 0.0, 1.5}, // 2d
- // Through a corner.
- {1.2, 0.5, 0.5, 0.5, 0.5, 1.2},
-
- // TODO: More of each and other... + generic set-ups?
+ // Through the middle of the block.
+ {0.5, 0.5, -0.5, 0.5, 0.5, 1.5},
+ {-0.5, 0.5, 0.5, 1.5, 0.5, 0.5},
+ {0.5, -0.5, 0.5, 0.5, 1.5, 0.5},
+ // Along the edges.
+ {0.5, 0.0, -0.5, 0.5, 0.0, 1.5},
+ {-0.5, 0.0, 0.5, 1.5, 0.0, 0.5},
+ // Exactly diagonal.
+ {-0.5, -0.5, -0.5, 1.5, 1.5, 1.5}, // 3d
+ {-0.5, 0.0, -0.5, 1.5, 0.0, 1.5}, // 2d
+ // Through a corner.
+ {1.2, 0.5, 0.5, 0.5, 0.5, 1.2},
+
+ // TODO: More of each and other... + generic set-ups?
};
TestRayTracing.runCoordinates(rt, setups, true, false, 3.0, true);
rt.cleanup();
bc.cleanup();
}
-
+
/**
* Moving diagonally through an "empty corner", seen from above:
* ox
@@ -88,17 +89,18 @@ public class TestPassableRayTracing {
rt.setBlockCache(bc);
// TODO: More Directions, over a corner, sides, etc.
double[][] setups = new double[][] {
- // Slightly off the middle (11, y, 11)
- {11.4, 70.0, 10.4, 10.6, 70.0, 11.4},
- // Going exactly through the middle (11, y, 11)
- {11.4, 70.0, 10.6, 10.6, 70.0, 11.4},
- {11.5, 70.0, 10.5, 10.5, 70.0, 11.5},
+ // Slightly off the middle (11, y, 11)
+ {11.4, 70.0, 10.4, 10.6, 70.0, 11.4},
+ // Going exactly through the middle (11, y, 11)
+ {11.4, 70.0, 10.6, 10.6, 70.0, 11.4},
+ {11.5, 70.0, 10.5, 10.5, 70.0, 11.5},
+ //{11.5, 70.0, 10.5, 10.99999999999, 70.0, 11.00000000001}, // TODO: Craft something here
};
TestRayTracing.runCoordinates(rt, setups, true, false, 3.0, true);
rt.cleanup();
bc.cleanup();
}
-
+
@Test
public void testGround() {
FakeBlockCache bc = new FakeBlockCache();
@@ -112,21 +114,21 @@ public class TestPassableRayTracing {
rt.setBlockCache(bc);
// TODO: More Directions, also from air underneath to ground).
double[][] noCollision = new double[][] {
- {1.3, 66.0, 2.43, 5.25, 66.0, 7.12},
+ {1.3, 66.0, 2.43, 5.25, 66.0, 7.12},
};
TestRayTracing.runCoordinates(rt, noCollision, false, true, 3.0, true);
double[][] shouldCollide = new double[][] {
- {1.3, 65.1, 2.43, 2.3, 65.1, 4.43},
- {1.3, 65.0, 2.43, 2.3, 65.0, 4.43},
- {1.3, 66.0, 2.43, 1.3, 65.9, 2.43},
- {1.3, 66.0, 2.43, 5.25, 65.9, 7.12},
- {1.3, 65.4, 2.43, 1.3, 65.4, 2.43}, // No distance.
+ {1.3, 65.1, 2.43, 2.3, 65.1, 4.43},
+ {1.3, 65.0, 2.43, 2.3, 65.0, 4.43},
+ {1.3, 66.0, 2.43, 1.3, 65.9, 2.43},
+ {1.3, 66.0, 2.43, 5.25, 65.9, 7.12},
+ {1.3, 65.4, 2.43, 1.3, 65.4, 2.43}, // No distance.
};
TestRayTracing.runCoordinates(rt, shouldCollide, true, false, 3.0, true);
rt.cleanup();
bc.cleanup();
}
-
+
@Test
public void testGroundSteps() {
FakeBlockCache bc = new FakeBlockCache();
@@ -142,19 +144,39 @@ public class TestPassableRayTracing {
rt.setBlockCache(bc);
// TODO: More Directions, also from air underneath to ground).
double[][] noCollision = new double[][] {
- {1.3, 65.5, 2.43, 5.25, 65.5, 7.12},
+ {1.3, 65.5, 2.43, 5.25, 65.5, 7.12},
};
TestRayTracing.runCoordinates(rt, noCollision, false, true, 3.0, true);
double[][] shouldCollide = new double[][] {
- {1.3, 65.1, 2.43, 2.3, 65.1, 7.43},
- {1.3, 65.0, 2.43, 2.3, 65.0, 7.43},
- {1.3, 65.5, 2.43, 1.3, 65.4, 2.43},
- {1.3, 65.5, 2.43, 5.25, 65.4, 7.12},
- {1.3, 65.4, 2.43, 1.3, 65.4, 2.43}, // No distance.
+ {1.3, 65.1, 2.43, 2.3, 65.1, 7.43},
+ {1.3, 65.0, 2.43, 2.3, 65.0, 7.43},
+ {1.3, 65.5, 2.43, 1.3, 65.4, 2.43},
+ {1.3, 65.5, 2.43, 5.25, 65.4, 7.12},
+ {1.3, 65.4, 2.43, 1.3, 65.4, 2.43}, // No distance.
};
TestRayTracing.runCoordinates(rt, shouldCollide, true, false, 3.0, true);
rt.cleanup();
bc.cleanup();
}
-
+
+ @Test
+ public void testRoom() {
+ FakeBlockCache bc = new FakeBlockCache();
+ bc.room(-1, 64, -1, 1, 66, 1, Material.STONE);
+ double[] middle = new double[] {0.5, 65.5, 0.5}; // Free spot.
+ PassableRayTracing rt = new PassableRayTracing();
+ rt.setBlockCache(bc);
+ boolean intense = BuildParameters.testLevel > 1;
+ for (double x = -0.5; x < 1.0; x += 0.5) {
+ for (double y = -0.5; y < 1.0; y += 0.5) {
+ for (double z = -0.5; z < 1.0; z += 0.5) {
+ double add = Math.abs(x) + Math.abs(y) + Math.abs(z);
+ TestRayTracing.runCenterRays(rt, middle[0] + x, middle[1] + y, middle[2] + z, 2.0 + add, intense ? 10000 : 1000, true, false, true);
+ }
+ }
+ }
+ rt.cleanup();
+ bc.cleanup();
+ }
+
}
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 112a4982..93661c0d 100644
--- a/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestRayTracing.java
+++ b/NCPPlugin/src/test/java/fr/neatmonster/nocheatplus/test/TestRayTracing.java
@@ -5,6 +5,7 @@ import static org.junit.Assert.fail;
import java.util.Random;
import org.bukkit.Location;
+import org.bukkit.util.Vector;
import org.junit.Test;
import fr.neatmonster.nocheatplus.utilities.RayTracing;
@@ -259,6 +260,16 @@ public class TestRayTracing {
// TODO: Add tests for typical coordinates a with interact, passable.
}
+ /**
+ *
+ * @param rt
+ * @param setup
+ * @param expectCollide
+ * @param expectNotCollide
+ * @param stepsManhattan
+ * @param reverse If set to true, end points will be exchanged for this run (not in addition).
+ * @param tag
+ */
public static void runCoordinates(RayTracing rt, double[] setup, boolean expectCollide, boolean expectNotCollide, double stepsManhattan, boolean reverse, String tag) {
if (reverse) {
rt.set(setup[3], setup [4], setup[5], setup[0], setup[1], setup[2]);
@@ -291,7 +302,7 @@ public class TestRayTracing {
* @param expectCollide
* @param expectNotCollide
* @param stepsManhattan
- * @param testReversed
+ * @param testReversed If to test the each ray with reversed end points in addition.
*/
public static void runCoordinates(RayTracing rt, double[][] setups, boolean expectCollide, boolean expectNotCollide, double stepsManhattan, boolean testReversed) {
for (int i = 0; i < setups.length; i++) {
@@ -304,4 +315,61 @@ public class TestRayTracing {
}
}
+ /**
+ * Run (some) standard directions towards the center.
+ * @param rt
+ * @param cX
+ * @param cY
+ * @param cZ
+ * @param length Rough length of the rays (might be applied per-axis including an additum).
+ * @param nRandom Test a number of random rays as well.
+ * @param expectCollide
+ * @param expectNotCollide
+ * @param testReversed If to test the each ray with reversed end points in addition.
+ */
+ public static void runCenterRays(RayTracing rt, double cX, double cY, double cZ, double length, int nRandom, boolean expectCollide, boolean expectNotCollide, boolean testReversed) {
+ double[] mult = new double[] {-1.0, 0.0, 1.0};
+ for (int ix = 0; ix < 3; ix ++) {
+ for (int iy = 0; iy < 3; iy++) {
+ for (int iz = 0; iz < 3; iz++) {
+ if (ix == 1 && iy == 1 && iz == 1) {
+ // Skip the center itself.
+ continue;
+ }
+ double[] coords = new double[] {
+ cX + length * mult[ix],
+ cY + length * mult[iy],
+ cZ + length * mult[iz],
+ cX,
+ cY,
+ cZ
+ };
+ // TODO: Generate differing target points on/near middle as well.
+ TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, false, "");
+ if (testReversed) {
+ TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, true, "");
+ }
+ }
+ }
+ }
+ // TODO: Consider running block coordinates with larger radius (potentially all within some radius?).
+ for (int n = 0; n < nRandom; n ++) {
+ // TODO: Check if normalize is necessary.
+ // One totally random vector.
+ Vector vec = Vector.getRandom().normalize().multiply(length);
+ double[] coords = new double[] {
+ cX + vec.getX(),
+ cY + vec.getY(),
+ cZ + vec.getZ(),
+ cX,
+ cY,
+ cZ
+ };
+ TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, false, "random");
+ if (testReversed) {
+ TestRayTracing.runCoordinates(rt, coords, true, false, 3.0, true, "random");
+ }
+ }
+ }
+
}