Let blockinteract.direction check queued packets. Unify loop (visible).

* Add a class to loop the flying queue with a block as target (look
only).
* Pass the flyingHandle to sub checks (doesn't necessarily make sense
with reach - should probably re-check reach with the used flying queue
state, but that's more complicated due to the possibility of split pos
vs. look).
* Use the loop class both for visible and direction (not reach).

Likely similar has to be done with BlockPlace and BlockBreak - would be
good to find a skipping heuristic for blockbreak.direction etc., so we
know we have successfully checked that block from that exact position
for this player and nothing has happened between (and so on, or a more
relaxed heuristic).
This commit is contained in:
asofold 2017-04-28 19:03:10 +02:00
parent 970ed6b126
commit 180cf8a3c3
6 changed files with 263 additions and 59 deletions

View File

@ -33,6 +33,7 @@ import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig;
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
@ -185,10 +186,27 @@ public class BlockInteractListener extends CheckListener {
}
else {
data.subsequentCancel = 0;
if (data.debug) {
if (flyingHandle.isFlyingQueueFetched()) {
// Log which entry was used.
logUsedFlyingPacket(player, flyingHandle);
}
}
}
useLoc.setWorld(null);
}
private void logUsedFlyingPacket(final Player player, final FlyingQueueHandle flyingHandle) {
final DataPacketFlying[] queue = flyingHandle.getHandle();
for (int i = 0; i < queue.length; i++) {
final DataPacketFlying packet = queue[i];
if (packet != null) {
debug(player, "Flying packet queue used at index " + i + ": pitch=" + packet.getPitch() + ",yaw=" + packet.getYaw());
return;
}
}
}
private void onCancelInteract(final Player player, final Block block, final BlockFace face,
final PlayerInteractEvent event, final int previousLastTick, final boolean preventUseItem,
final BlockInteractData data, final BlockInteractConfig cc) {

View File

@ -22,6 +22,7 @@ import org.bukkit.util.Vector;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueLookBlockChecker;
import fr.neatmonster.nocheatplus.utilities.collision.CollideRayVsAABB;
import fr.neatmonster.nocheatplus.utilities.collision.ICollideRayVsAABB;
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
@ -31,7 +32,43 @@ import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
*/
public class Direction extends Check {
private final class BoulderChecker extends FlyingQueueLookBlockChecker {
// (Not static for convenience.)
private double minDistance;
@Override
protected boolean check(final double x, final double y, final double z,
final float yaw, final float pitch,
final int blockX, final int blockY, final int blockZ) {
final double distance = checkBoulder(x, y, z, yaw, pitch, blockX, blockY, blockZ);
if (distance == Double.MAX_VALUE) {
// minDistance is not updated, in case the information is interesting ever.
return true;
}
else {
minDistance = Math.min(minDistance, distance);
return false;
}
}
@Override
public boolean checkFlyingQueue(double x, double y, double z, float oldYaw, float oldPitch, int blockX,
int blockY, int blockZ, FlyingQueueHandle flyingHandle) {
minDistance = Double.MAX_VALUE;
return super.checkFlyingQueue(x, y, z, oldYaw, oldPitch, blockX, blockY, blockZ, flyingHandle);
}
public double getMinDistance() {
return minDistance;
}
}
private final ICollideRayVsAABB boulder = new CollideRayVsAABB();
private final Location useLoc = new Location(null, 0, 0, 0);
private final BoulderChecker checker = new BoulderChecker();
/**
* Instantiates a new direction check.
@ -53,20 +90,27 @@ public class Direction extends Check {
final FlyingQueueHandle flyingHandle, final BlockInteractData data, final BlockInteractConfig cc) {
boolean cancel = false;
// How far "off" is the player with their aim.
final Vector direction = loc.getDirection();
// Initialize fully each time.
boulder.setFindNearestPointIfNotCollide(true)
.setRay(loc.getX(), loc.getY() + player.getEyeHeight(), loc.getZ(),
direction.getX(), direction.getY(), direction.getZ())
.setAABB(block.getX(), block.getY(), block.getZ(), 0.1)
.loop();
// TODO: if (boulder.collides()) { // Check flying queue.
if (!boulder.collides()) {
final double distance = Math.sqrt(boulder.getClosestDistanceSquared());
final double x = loc.getX();
final double y = loc.getY() + player.getEyeHeight();
final double z = loc.getZ();
final int blockX = block.getX();
final int blockY = block.getY();
final int blockZ = block.getZ();
// The distance is squared initially.
double distance = checkBoulder(x, y, z, loc.getYaw(), loc.getPitch(), blockX, blockY, blockZ);
if (distance != Double.MAX_VALUE) {
if (checker.checkFlyingQueue(x, y, z, loc.getYaw(), loc.getPitch(),
blockX, blockY, blockZ, flyingHandle)) {
distance = Double.MAX_VALUE;
}
else {
distance = Math.min(distance, checker.getMinDistance());
}
}
if (distance != Double.MAX_VALUE) {
distance = Math.sqrt(distance);
if (data.debug) {
outputDebugFail(player, boulder, distance);
}
@ -83,10 +127,46 @@ public class Direction extends Check {
// Player did likely nothing wrong, reduce violation counter to reward them.
data.directionVL *= 0.9D;
}
return cancel;
}
/**
* Check one configuration.
*
* @param x
* @param y
* @param z
* @param yaw
* @param pitch
* @param blockX
* @param blockY
* @param blockZ
* @return Double.MAX_VALUE if this passes the check, otherwise the squared
* violation distance (some measure).
*/
private double checkBoulder(final double x, final double y, final double z,
final float yaw, final float pitch,
final int blockX, final int blockY, final int blockZ) {
useLoc.setYaw(yaw);
useLoc.setPitch(pitch);
final Vector dir = useLoc.getDirection(); // TODO: More efficient.
final double dirX = dir.getX();
final double dirY = dir.getY();
final double dirZ = dir.getZ();
// Initialize fully each time.
boulder.setFindNearestPointIfNotCollide(true)
.setRay(x, y, z, dirX, dirY, dirZ)
.setAABB(blockX, blockY, blockZ, 0.1)
.loop();
// Interpret result.
if (boulder.collides()) {
return Double.MAX_VALUE;
}
else {
return boulder.getClosestDistanceSquared();
}
}
private void outputDebugFail(Player player, ICollideRayVsAABB boulder, double distance) {
debug(player, "Failed: collides: " + boulder.collides() + " , dist: " + distance + " , pos: " + LocUtil.simpleFormat(boulder));
}

View File

@ -62,6 +62,7 @@ public class Reach extends Check {
// Distance is calculated from eye location to center of targeted block. If the player is further away from their
// target than allowed, the difference will be assigned to "distance".
// TODO: On failure loop through flying queue, and do set not working entries to null (!).
final double distance = TrigUtil.distance(loc.getX(), loc.getY() + player.getEyeHeight(), loc.getZ(), 0.5 + block.getX(), 0.5 + block.getY(), 0.5 + block.getZ()) - distanceLimit;
if (distance > 0) {

View File

@ -28,16 +28,69 @@ import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueLookBlockChecker;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
import fr.neatmonster.nocheatplus.utilities.collision.InteractRayTracing;
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
import fr.neatmonster.nocheatplus.utilities.location.TrigUtil;
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
import fr.neatmonster.nocheatplus.utilities.map.WrapBlockCache;
public class Visible extends Check {
private final class RayChecker extends FlyingQueueLookBlockChecker {
private BlockFace face;
private List<String> tags;
private boolean debug;
private Player player;
@Override
protected boolean check(final double x, final double y, final double z,
final float yaw, final float pitch,
final int blockX, final int blockY, final int blockZ) {
// Run ray-tracing again with updated pitch and yaw.
useLoc.setPitch(pitch);
useLoc.setYaw(yaw);
final Vector direction = useLoc.getDirection(); // TODO: Better.
tags.clear();
if (checkRayTracing(x, y, z, direction.getX(), direction.getY(), direction.getZ(), blockX, blockY, blockZ, face, tags, debug)) {
// Collision still.
if (debug) {
debug(player, "pitch=" + pitch + ",yaw=" + yaw + " tags=" + StringUtil.join(tags, "+"));
}
return false;
}
else {
return true;
}
}
public boolean checkFlyingQueue(double x, double y, double z, float oldYaw, float oldPitch, int blockX,
int blockY, int blockZ, FlyingQueueHandle flyingHandle,
BlockFace face, List<String> tags, boolean debug, Player player) {
this.face = face;
this.tags = tags;
this.debug = debug;
this.player = player;
return super.checkFlyingQueue(x, y, z, oldYaw, oldPitch, blockX, blockY, blockZ, flyingHandle);
}
@Override
public boolean checkFlyingQueue(double x, double y, double z, float oldYaw, float oldPitch, int blockX,
int blockY, int blockZ, FlyingQueueHandle flyingHandle) {
throw new UnsupportedOperationException("Use the other method.");
}
public void cleanup () {
this.player = null;
this.face = null;
this.debug = false;
this.tags = null;
}
}
private final WrapBlockCache wrapBlockCache;
/**
@ -45,6 +98,8 @@ public class Visible extends Check {
*/
private final InteractRayTracing rayTracing = new InteractRayTracing(false);
private final RayChecker checker = new RayChecker();
private final List<String> tags = new ArrayList<String>();
/** For temporary use, no nested use, setWorld(null) after use, etc. */
@ -86,52 +141,15 @@ public class Visible extends Check {
if (collides) {
// Debug output.
if (data.debug) {
debug(player, "pitch=" + loc.getPitch() + " yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+"));
debug(player, "pitch=" + loc.getPitch() + ",yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+"));
}
// Re-check with flying packets.
final DataPacketFlying[] flyingQueue = flyingHandle.getHandle();
// TODO: Maybe just the latest one does (!).+
LocUtil.set(useLoc, loc);
final float oldPitch = useLoc.getPitch();
final float oldYaw = useLoc.getYaw();
// TODO: Specific max-recheck-count (likely doesn't equal packet count).
for (int i = 0; i < flyingQueue.length; i++) {
final DataPacketFlying packetData = flyingQueue[i];
if (packetData == null) {
// Other checks have pre-selected.
continue;
}
// TODO: Allow if within threshold(s) of last move.
// TODO: Confine by distance.
// Abort/skipping conditions.
// if (packetData.hasPos) {
// break;
// }
if (!packetData.hasLook) {
flyingQueue[i] = null;
continue;
}
// TODO: Might skip last pitch+yaw as well.
if (packetData.getPitch() == oldPitch && packetData.getYaw() == oldYaw) {
flyingQueue[i] = null;
continue;
}
// Run ray-tracing again with updated pitch and yaw.
useLoc.setPitch(packetData.getPitch());
useLoc.setYaw(packetData.getYaw());
direction = useLoc.getDirection(); // TODO: Better.
tags.clear();
tags.add("flying(" + i + ")"); // Interesting if this gets through.
collides = checkRayTracing(eyeX, eyeY, eyeZ, direction.getX(), direction.getY(), direction.getZ(), blockX, blockY, blockZ, face, tags, data.debug);
if (!collides) {
break;
}
flyingQueue[i] = null;
// Debug output.
if (data.debug) {
debug(player, "pitch=" + loc.getPitch() + " yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+"));
}
if (checker.checkFlyingQueue(eyeX, eyeY, eyeZ, useLoc.getYaw(), useLoc.getPitch(),
blockX, blockY, blockZ, flyingHandle, face, tags, data.debug, player)) {
// Check passed.
collides = false;
}
checker.cleanup();
useLoc.setWorld(null);
}
// Cleanup.
@ -155,7 +173,7 @@ public class Visible extends Check {
else {
data.visibleVL *= 0.99;
if (data.debug) {
debug(player, "pitch=" + loc.getPitch() + " yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+"));
debug(player, "pitch=" + loc.getPitch() + ",yaw=" + loc.getYaw() + " tags=" + StringUtil.join(tags, "+"));
}
}

View File

@ -31,4 +31,8 @@ public class FlyingQueueHandle implements IHandle<DataPacketFlying[]> {
return queue;
}
public boolean isFlyingQueueFetched() {
return queue != null;
}
}

View File

@ -0,0 +1,83 @@
package fr.neatmonster.nocheatplus.checks.net;
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
/**
* A simple loop through checker for the flying queue feature.
* @author asofold
*
*/
public abstract class FlyingQueueLookBlockChecker {
/**
* Override to prevent setting elements that can't be used or for which
* check returned false to null.
*/
protected boolean setUnusableToNull = true;
protected abstract boolean check(final double x, final double y, final double z,
final float yaw, final float pitch,
final int blockX, final int blockY, final int blockZ);
/**
* Run check with the given start position (e.g. eye coordinates), but use
* yaw and pitch from the flying queue. Non matching entries are nulled,
* unless setUnusableToNull is set to false.
*
* @param x
* @param y
* @param z
* @param oldYaw
* @param oldPitch
* @param blockX
* @param blockY
* @param blockZ
* @param flyingHandle
* @return True, if check returned true (the first time is returned). False
* if the queue is empty or check has not returned true for any
* contained element. Special return values have to be set
* elsewhere. An empty queue also yields false as return value.
*/
public boolean checkFlyingQueue(final double x, final double y, final double z,
final float oldYaw, final float oldPitch,
final int blockX, final int blockY, final int blockZ, final FlyingQueueHandle flyingHandle) {
final DataPacketFlying[] queue = flyingHandle.getHandle();
if (queue.length == 0) {
return false;
}
for (int i = 0; i < queue.length; i++) {
final DataPacketFlying packetData = queue[i];
if (packetData == null) {
continue;
}
if (!packetData.hasLook) {
if (setUnusableToNull) {
queue[i] = null;
}
continue;
}
final float yaw = packetData.getYaw();
final float pitch = packetData.getPitch();
// Simple heuristic: reduce impact of checking by skipping redundant entries.
// TODO: Other heuristic / what's typical?
if (yaw == oldYaw && pitch == oldPitch) {
if (setUnusableToNull) {
queue[i] = null;
}
continue;
}
// TODO: Consider support some other type of metric (possibly checking positions too?);
if (check(x, y, z, yaw, pitch, blockX, blockY, blockZ)) {
// TODO: Consider to remember index and entry as well?
return true;
}
else {
if (setUnusableToNull) {
queue[i] = null;
}
}
}
return false;
}
}