mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-11-07 11:10:05 +01:00
Add basic ray-tracing support.
This commit is contained in:
parent
7d5e5deb24
commit
9f7d3c6cfc
@ -0,0 +1,160 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.utilities;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ray tracing for block coordinates with entry point offsets.
|
||||||
|
* @author mc_dev
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class RayTracing {
|
||||||
|
|
||||||
|
// /** End point coordinates (from, to) */
|
||||||
|
// protected double x0, y0, z0, x1, y1, z1;
|
||||||
|
|
||||||
|
// /** Total distance between end points. */
|
||||||
|
// protected double d;
|
||||||
|
|
||||||
|
/** Distance per axis. */
|
||||||
|
protected double dX, dY, dZ;
|
||||||
|
|
||||||
|
/** Current block. */
|
||||||
|
protected int blockX, blockY, blockZ;
|
||||||
|
|
||||||
|
/** Offset within current block. */
|
||||||
|
protected double oX, oY, oZ;
|
||||||
|
|
||||||
|
/** Current "time" in [0..1]. */
|
||||||
|
protected double t = Double.MIN_VALUE;
|
||||||
|
|
||||||
|
/** Tolerance for time, for checking the abort condition: 1.0 - t <= tol . */
|
||||||
|
protected double tol = 0.0;
|
||||||
|
|
||||||
|
public RayTracing(double x0, double y0, double z0, double x1, double y1, double z1){
|
||||||
|
set(x0, y0, z0, x1, y1, z1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(double x0, double y0, double z0, double x1, double y1, double z1){
|
||||||
|
// // TODO: Consider not using end-points at all.
|
||||||
|
// this.x0 = x0;
|
||||||
|
// this.y0 = y0;
|
||||||
|
// this.z0 = z0;
|
||||||
|
// this.x1 = x1;
|
||||||
|
// this.y1 = y1;
|
||||||
|
// this.z1 = z1;
|
||||||
|
// // Set the "runtime" info.
|
||||||
|
// d = CheckUtils.distance(x0, y0, z0, x1, y1, z1);
|
||||||
|
dX = x1 - x0;
|
||||||
|
dY = y1 - y0;
|
||||||
|
dZ = z1 - z0;
|
||||||
|
blockX = Location.locToBlock(x0);
|
||||||
|
blockY = Location.locToBlock(y0);
|
||||||
|
blockZ = Location.locToBlock(z0);
|
||||||
|
oX = (double) (x0 - blockX);
|
||||||
|
oY = (double) (y0 - blockY);
|
||||||
|
oZ = (double) (z0 - blockZ);
|
||||||
|
t = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final double tDiff(final double dTotal, final double offset){
|
||||||
|
if (dTotal > 0.0){
|
||||||
|
return (1 - offset) / dTotal;
|
||||||
|
}
|
||||||
|
else if (dTotal < 0.0){
|
||||||
|
return offset / -dTotal;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return Double.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop through blocks.
|
||||||
|
*/
|
||||||
|
public void loop(){
|
||||||
|
// TODO: Might intercept 0 dist ?
|
||||||
|
|
||||||
|
// Time to block edge.
|
||||||
|
double tX, tY, tZ, tMin;
|
||||||
|
while (1.0 - t > tol){
|
||||||
|
// Determine smallest time to block edge.
|
||||||
|
tX = tDiff(dX, oX);
|
||||||
|
tY = tDiff(dY, oY);
|
||||||
|
tZ = tDiff(dZ, oZ);
|
||||||
|
tMin = Math.min(tX, Math.min(tY, tZ));
|
||||||
|
if (tMin == Double.MAX_VALUE || t + tMin > 1.0) tMin = 1.0 - t;
|
||||||
|
// Call step with appropriate arguments.
|
||||||
|
if (!step(blockX, blockY, blockZ, oX, oY, oZ, tMin)) break; // || tMin == 0) break;
|
||||||
|
if (t + tMin >= 1.0 - tol) break;
|
||||||
|
// Advance (add to t etc.).
|
||||||
|
// x
|
||||||
|
oX += tMin * dX;
|
||||||
|
if (tX == tMin){
|
||||||
|
if (dX < 0){
|
||||||
|
oX = 1;
|
||||||
|
blockX --;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
oX = 0;
|
||||||
|
blockX ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (oX >= 1){
|
||||||
|
oX -= 1;
|
||||||
|
blockX ++;
|
||||||
|
}
|
||||||
|
else if (oX < 0){
|
||||||
|
oX += 1;
|
||||||
|
blockX --;
|
||||||
|
}
|
||||||
|
// y
|
||||||
|
oY += tMin * dY;
|
||||||
|
if (tY == tMin){
|
||||||
|
if (dY < 0){
|
||||||
|
oY = 1;
|
||||||
|
blockY --;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
oY = 0;
|
||||||
|
blockY ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (oY >= 1){
|
||||||
|
oY -= 1;
|
||||||
|
blockY ++;
|
||||||
|
}
|
||||||
|
else if (oY < 0){
|
||||||
|
oY += 1;
|
||||||
|
blockY --;
|
||||||
|
}
|
||||||
|
// z
|
||||||
|
oZ += tMin * dZ;
|
||||||
|
if (tZ == tMin){
|
||||||
|
if (dZ < 0){
|
||||||
|
oZ = 1;
|
||||||
|
blockZ --;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
oZ = 0;
|
||||||
|
blockZ ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (oZ >= 1){
|
||||||
|
oZ -= 1;
|
||||||
|
blockZ ++;
|
||||||
|
}
|
||||||
|
else if (oZ < 0){
|
||||||
|
oZ += 1;
|
||||||
|
blockZ --;
|
||||||
|
}
|
||||||
|
t += tMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One step in the loop.
|
||||||
|
* @return If to continue loop.
|
||||||
|
*/
|
||||||
|
protected abstract boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
package fr.neatmonster.nocheatplus.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.RayTracing;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.StringUtil;
|
||||||
|
|
||||||
|
public class TestRayTracing {
|
||||||
|
|
||||||
|
protected static final Random random = new Random(System.nanoTime() + 13391);
|
||||||
|
|
||||||
|
public static class CountRayTracing extends RayTracing{
|
||||||
|
public CountRayTracing(double x0, double y0, double z0, double x1, double y1, double z1) {
|
||||||
|
super(x0, y0, z0, x1, y1, z1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int done = 0;
|
||||||
|
@Override
|
||||||
|
protected boolean step(int blockX, int blockY, int blockZ, double oX,
|
||||||
|
double oY, double oZ, double dT) {
|
||||||
|
done ++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int loopCount() {
|
||||||
|
super.loop();
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double[] randomCoords(double max){
|
||||||
|
double[] res = new double[6];
|
||||||
|
for (int i = 0; i < 6 ; i++){
|
||||||
|
res[i] = (random.nextDouble() * 2.0 - 1.0 ) * max;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void doFail(String message, double[] coords) {
|
||||||
|
System.out.println("---- Failure trace ----");
|
||||||
|
System.out.println(message);
|
||||||
|
if (coords != null){
|
||||||
|
System.out.println("{" + coords[0] + ", " + coords[1]+ ", " + coords[2] + " , " + coords[3] + ", " + coords[4]+ ", " + coords[5] + "}");
|
||||||
|
dumpRawRayTracing(coords);
|
||||||
|
}
|
||||||
|
fail(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mostly block-coordinate consistency checking.
|
||||||
|
* @param coords
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static RayTracing checkConsistency(final double[] coords){
|
||||||
|
RayTracing rt = new RayTracing(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]) {
|
||||||
|
|
||||||
|
protected int lbx, lby, lbz;
|
||||||
|
|
||||||
|
protected int step = 0;
|
||||||
|
|
||||||
|
/* (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);
|
||||||
|
lbx = blockX - 1;
|
||||||
|
lby = blockY - 1;
|
||||||
|
lbz = blockZ - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ignEdge(double offset, double dTotal){
|
||||||
|
return offset == 1 && dTotal > 0 || offset == 0 && dTotal < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean step(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double dT) {
|
||||||
|
// TODO: This does not check last step for some occasions where it should.
|
||||||
|
step ++;
|
||||||
|
if (dT == 0 && 1.0 - (t + dT) > tol){
|
||||||
|
if (!ignEdge(oX, dX) && !ignEdge(oY, dY) && !ignEdge(oZ, dZ)){
|
||||||
|
doFail("Premature dT = 0 at t = " + StringUtil.fdec3.format(t), coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: check with last block coordinates
|
||||||
|
if (lbx == blockX && lby == blockY && lbz == blockZ){
|
||||||
|
if (1.0 - (t + dT) > tol){
|
||||||
|
doFail("Expect block coordinates to change with each step (step=" + step + ", t=" + StringUtil.fdec3.format(t) +").", coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: check offsets
|
||||||
|
// Set to current.
|
||||||
|
lbx = blockX;
|
||||||
|
lby = blockY;
|
||||||
|
lbz = blockZ;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loop() {
|
||||||
|
super.loop();
|
||||||
|
checkBlockTarget(coords[3], blockX, oX, dX, "x");
|
||||||
|
checkBlockTarget(coords[4], blockY, oY, dY, "y");
|
||||||
|
checkBlockTarget(coords[5], blockZ, oZ, dZ, "z");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBlockTarget(double target, int current, double offset, double dTotal, String name){
|
||||||
|
int b = Location.locToBlock(target);
|
||||||
|
if (current != b){
|
||||||
|
// 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;
|
||||||
|
// Failure.
|
||||||
|
doFail("Bad target " + name + "-coordinate: " + current + " instead of " + b, coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
rt.loop();
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RayTracing checkNumberOfSteps(double[] coords, int steps) {
|
||||||
|
CountRayTracing crt = new CountRayTracing(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
|
||||||
|
int done = crt.loopCount();
|
||||||
|
if (done != steps) doFail("Wrong number of steps: " + done + " instead of " + steps, coords);
|
||||||
|
return crt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dump(int blockX, int blockY, int blockZ, double oX, double oY, double oZ, double t, double dT) {
|
||||||
|
System.out.println(StringUtil.fdec3.format(t) + " (+" + StringUtil.fdec3.format(dT) + "): " + blockX + ", "+blockY + ", " + blockZ + " / " + StringUtil.fdec3.format(oX) + ", " + StringUtil.fdec3.format(oY)+ ", " + StringUtil.fdec3.format(oZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RayTracing dumpRawRayTracing(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) {
|
||||||
|
dump(blockX, blockY, blockZ, oX, oY, oZ, t, dT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rt.loop();
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNumberOfSteps(){
|
||||||
|
// Hand picked stuff.
|
||||||
|
checkNumberOfSteps(new double[]{0.5, 0.5, 0.5, 1.5, -0.5, 1.5}, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConsistency(){
|
||||||
|
// 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},
|
||||||
|
}){
|
||||||
|
checkConsistency(coords);
|
||||||
|
}
|
||||||
|
// Random tests.
|
||||||
|
for (int i = 0; i < 100000; i++){
|
||||||
|
checkConsistency(randomCoords(10.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user