Opportunistic passable checking.

This commit is contained in:
asofold 2016-11-25 23:06:06 +01:00
parent 892af50209
commit dec6ef1412
8 changed files with 152 additions and 47 deletions

View File

@ -674,7 +674,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
boolean mightSkipNoFall = false; // If to skip nofall check (mainly on violation of other checks).
if (newTo == null && cc.passableCheck && player.getGameMode() != BridgeMisc.GAME_MODE_SPECTATOR && !NCPExemptionManager.isExempted(player, CheckType.MOVING_PASSABLE) && !player.hasPermission(Permissions.MOVING_PASSABLE)) {
// Passable is checked first to get the original set-back locations from the other checks, if needed.
newTo = passable.check(player, pFrom, pTo, data, cc);
newTo = passable.check(player, pFrom, pTo, data, cc, tick);
if (newTo != null) {
// Check if to skip the nofall check.
mightSkipNoFall = true;
@ -697,7 +697,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Actual check.
if (newTo == null) {
// Only check if passable has not already set back.
newTo = survivalFly.check(player, pFrom, pTo, mightBeMultipleMoves, data, cc, time);
newTo = survivalFly.check(player, pFrom, pTo, mightBeMultipleMoves, data, cc, tick, time);
}
// Only check NoFall, if not already vetoed.
if (checkNf) {

View File

@ -19,6 +19,7 @@ import java.util.Locale;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
@ -26,6 +27,7 @@ import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
import fr.neatmonster.nocheatplus.checks.moving.location.LocUtil;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker;
import fr.neatmonster.nocheatplus.utilities.collision.ICollidePassable;
import fr.neatmonster.nocheatplus.utilities.collision.PassableAxisTracing;
import fr.neatmonster.nocheatplus.utilities.collision.PassableRayTracing;
@ -60,26 +62,28 @@ public class Passable extends Check {
// TODO: Store both and select on check (with config then).
private final ICollidePassable rayTracingLegacy = new PassableRayTracing();
private final ICollidePassable rayTracingActual = new PassableAxisTracing();
private final BlockChangeTracker blockTracker;
public Passable() {
super(CheckType.MOVING_PASSABLE);
// TODO: Configurable maxSteps?
rayTracingLegacy.setMaxSteps(60);
rayTracingActual.setMaxSteps(60);
blockTracker = NCPAPIProvider.getNoCheatPlusAPI().getBlockChangeTracker();
}
public Location check(final Player player, final PlayerLocation from, final PlayerLocation to,
final MovingData data, final MovingConfig cc) {
final MovingData data, final MovingConfig cc, final int tick) {
if (rt_legacy) {
return checkLegacy(player, from, to, data, cc);
}
else {
return checkActual(player, from, to, data, cc);
return checkActual(player, from, to, data, cc, tick);
}
}
private Location checkActual(final Player player, final PlayerLocation from, final PlayerLocation to,
final MovingData data, final MovingConfig cc) {
final MovingData data, final MovingConfig cc, final int tick) {
// TODO: Distinguish feet vs. box.
// Block distances (sum, max) for from-to (not for loc!).
@ -87,7 +91,7 @@ public class Passable extends Check {
// General condition check for using ray-tracing.
if (cc.passableRayTracingCheck && (!cc.passableRayTracingBlockChangeOnly || manhattan > 0)) {
final String newTag = checkRayTracing(player, from, to, manhattan, data, cc);
final String newTag = checkRayTracing(player, from, to, manhattan, data, cc, tick);
if (newTag != null) {
// Direct return.
return potentialViolation(player, from, to, manhattan, newTag, data, cc);
@ -107,11 +111,14 @@ public class Passable extends Check {
}
private String checkRayTracing(final Player player, final PlayerLocation from, final PlayerLocation to,
final int manhattan, final MovingData data, final MovingConfig cc) {
final int manhattan, final MovingData data, final MovingConfig cc, final int tick) {
String tags = null;
setNormalMargins(rayTracingActual, from);
rayTracingActual.set(from, to);
rayTracingActual.setIgnoreInitiallyColliding(true);
if (cc.trackBlockMove) { // TODO: Extra flag for 'any' block changes.
rayTracingActual.setBlockChangeTracker(blockTracker, data.blockChangeRef, tick, from.getWorld().getUID());
}
//rayTracing.setCutOppositeDirectionMargin(true);
rayTracingActual.loop();
rayTracingActual.setIgnoreInitiallyColliding(false);

View File

@ -43,7 +43,6 @@ import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeEnchant;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.Direction;
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
@ -51,7 +50,6 @@ import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionAccumulator;
import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation;
import fr.neatmonster.nocheatplus.utilities.location.TrigUtil;
@ -115,7 +113,9 @@ public class SurvivalFly extends Check {
* @param isSamePos
* @return the location
*/
public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final boolean mightBeMultipleMoves, final MovingData data, final MovingConfig cc, final long now) {
public Location check(final Player player, final PlayerLocation from, final PlayerLocation to,
final boolean mightBeMultipleMoves, final MovingData data, final MovingConfig cc,
final int tick, final long now) {
tags.clear();
if (data.debug) {
justUsedWorkarounds.clear();
@ -610,7 +610,7 @@ public class SurvivalFly extends Check {
// TODO: Pull down tick / timing data (perhaps add an API object for millis + source + tick + sequence count (+ source of sequence count).
if (data.debug) {
// TODO: Only update, if velocity is queued at all.
data.getVerticalVelocityTracker().updateBlockedState(TickTask.getTick(),
data.getVerticalVelocityTracker().updateBlockedState(tick,
// Assume blocked with being in web/water, despite not entirely correct.
thisMove.headObstructed || thisMove.from.resetCond,
// (Similar here.)
@ -655,9 +655,8 @@ public class SurvivalFly extends Check {
// Extreme case: 1.51 blocks up (details pending).
|| yDistance <= 1.015 && to.getY() - to.getBlockY() < 0.015)) {
// TODO: Other conditions? [some will be in passable later].
final BlockChangeEntry entryYPos = from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, Math.min(yDistance, 1.0));
if (entryYPos != null) {
data.blockChangeRef.updateFinal(entryYPos, to);
if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, Math.min(yDistance, 1.0))) {
data.blockChangeRef.updateFinal(to); // TODO: -> MovingListener (!).
tags.add("blkmv_y_pos");
final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ?
return new double[]{maxDistYPos, 0.0};
@ -666,9 +665,8 @@ public class SurvivalFly extends Check {
// Push (/pull) down.
else if (yDistance < 0.0 && yDistance >= -1.0) {
// TODO: Other conditions? [some will be in passable later].
final BlockChangeEntry entryYNeg = from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_NEG, -yDistance);
if (entryYNeg != null) {
data.blockChangeRef.updateFinal(entryYNeg, to);
if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_NEG, -yDistance)) {
data.blockChangeRef.updateFinal(to); // TODO: -> MovingListener (!).
tags.add("blkmv_y_neg");
final double maxDistYNeg = yDistance; // from.getY() - from.getBlockY(); // TODO: Margin ?
return new double[]{maxDistYNeg, 0.0};

View File

@ -212,7 +212,15 @@ public class BlockChangeTracker {
* one check covering multiple blocks.
*/
/** Last used block change entry, highest id.*/
/** First used (oldest) entry during checking. */
public BlockChangeEntry firstSpanEntry = null;
/** Last used (newest) entry during checking. */
public BlockChangeEntry lastSpanEntry = null;
/**
* Last used block change entry, set after a complete iteration of
* checking, update with updateFinal.
*/
public BlockChangeEntry lastUsedEntry = null;
/**
@ -246,26 +254,43 @@ public class BlockChangeTracker {
}
/**
* Update lastUsedEntry by the given entry, assuming <i>to</i> to be the
* Update the span during checking. Ensure to test canUpdateWith(entry)
* before calling this.
*
* @param entry
*/
public void updateSpan(final BlockChangeEntry entry) {
if (firstSpanEntry == null || entry.id < firstSpanEntry.id) {
firstSpanEntry = entry;
}
if (lastSpanEntry == null || entry.id > lastSpanEntry.id) {
lastSpanEntry = entry;
}
}
/**
* Update lastUsedEntry by the set span, assuming <i>to</i> to be the
* move end-point to continue from next time. This is meant to finalize
* prepared changes/span for use with the next move.
*
* @param entry
* @param to
* If not null, allows keeping the latest entry valid, if
* intersecting with the bounding box of <i>to</i>.
*/
public void updateFinal(final BlockChangeEntry entry, final RichBoundsLocation to) {
// TODO: updateBlockChangeReference ... Span(entry, to !?)|Final()
if (lastUsedEntry == null || lastUsedEntry.id < entry.id) { // Here use the id for fastest comparison.
lastUsedEntry = entry; // Unchecked.
if (to != null && to.isBlockIntersecting(entry.x, entry.y, entry.z)) {
public void updateFinal(final RichBoundsLocation to) {
if (firstSpanEntry == null) {
return;
}
if (lastSpanEntry != null && (lastUsedEntry == null || lastSpanEntry.id > lastUsedEntry.id)) {
lastUsedEntry = lastSpanEntry;
if (to != null && to.isBlockIntersecting(lastSpanEntry.x, lastSpanEntry.y, lastSpanEntry.z)) {
valid = true;
}
else {
valid = false;
}
}
firstSpanEntry = lastSpanEntry = null;
}
/**
@ -275,18 +300,32 @@ public class BlockChangeTracker {
*/
public BlockChangeReference copy() {
final BlockChangeReference copy = new BlockChangeReference();
copy.firstSpanEntry = this.firstSpanEntry;
copy.lastSpanEntry = this.lastSpanEntry;
copy.lastUsedEntry = this.lastUsedEntry;
copy.valid = this.valid;
return copy;
}
public void clear() {
firstSpanEntry = lastSpanEntry = lastUsedEntry = null;
valid = false;
}
@Override
public boolean equals(final Object obj) {
if (obj == null || !(obj instanceof BlockChangeReference)) {
return false;
}
final BlockChangeReference other = (BlockChangeReference) obj;
return valid == other.valid && (lastUsedEntry != null && lastUsedEntry.equals(other.lastUsedEntry) || lastUsedEntry == null && other.lastUsedEntry == null);
return valid == other.valid &&
(lastUsedEntry != null && lastUsedEntry.equals(other.lastUsedEntry)
|| lastUsedEntry == null && other.lastUsedEntry == null)
&& (firstSpanEntry != null && firstSpanEntry.equals(other.firstSpanEntry)
|| firstSpanEntry == null && other.firstSpanEntry == null)
&& (lastSpanEntry != null && lastSpanEntry.equals(other.lastSpanEntry)
|| lastSpanEntry == null && other.lastSpanEntry == null)
;
}
}
@ -576,7 +615,7 @@ public class BlockChangeTracker {
* @param ref
* Reference for checking the validity of BlockChangeEntry
* instances. No changes are made to the passed instance,
* canUpdateWith is called.
* canUpdateWith is called. Pass null to skip further validation.
* @param tick
* The current tick. Used for lazy expiration.
* @param worldId
@ -604,7 +643,7 @@ public class BlockChangeTracker {
* @param ref
* Reference for checking the validity of BlockChangeEntry
* instances. No changes are made to the passed instance,
* canUpdateWith is called.
* canUpdateWith is called. Pass null to skip further validation.
* @param tick
* The current tick. Used for lazy expiration.
* @param worldNode
@ -643,7 +682,7 @@ public class BlockChangeTracker {
it.remove();
}
else {
if (ref.canUpdateWith(entry) && (direction == null || entry.direction == direction)) {
if (ref == null || ref.canUpdateWith(entry) && (direction == null || entry.direction == direction)) {
return entry;
}
}

View File

@ -14,6 +14,10 @@
*/
package fr.neatmonster.nocheatplus.utilities.collision;
import java.util.UUID;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference;
import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation;
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
@ -24,11 +28,26 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
*
*/
public interface ICollidePassable extends ICollideBlocks, ISetMargins {
// TODO: Add a super interface (+ asbtract impl.) for BlockCache based stuff including BlockChangeTracker.
public void setBlockCache(BlockCache blockCache);
public BlockCache getBlockCache();
// TODO: public void (bool?) setBlockChangeReference(tracker, reference).
/**
* Allows testing for past states. Should be reset in cleanup. Will do
* nothing, if not supported.
* <hr>
* <b>Method signature subject to change (tick).</b>
*
* @param blockChangeTracker
* @param blockChangeReference
* @param tick
* The tick right now (timing info).
* @param worldId
* the UUID of the world this takes place in.
*/
public void setBlockChangeTracker(BlockChangeTracker blockChangeTracker,
BlockChangeReference blockChangeReference, int tick, UUID worldId);
/**
* Convenience: Call set and setBlockCache with the data from the
@ -52,8 +71,8 @@ public interface ICollidePassable extends ICollideBlocks, ISetMargins {
public boolean mightNeedSplitAxisHandling();
/**
* Remove reference to objects passed from outside (BlockCache, but not
* calling their cleanup methods).
* Remove reference to objects passed from outside (BlockCache,
* BlockChangeTracker and similar, but not calling their cleanup methods).
*/
public void cleanup();

View File

@ -14,6 +14,11 @@
*/
package fr.neatmonster.nocheatplus.utilities.collision;
import java.util.UUID;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference;
import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation;
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
@ -21,6 +26,10 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
public class PassableAxisTracing extends AxisTracing implements ICollidePassable {
private BlockCache blockCache;
private BlockChangeTracker blockChangeTracker = null;
private BlockChangeReference blockChangeRef = null;
private int tick;
private UUID worldId;
// TODO: Might need another option for margins (option to skip margin for the axis-start point, or alter ignoreFirst behavior).
// TODO: Consider an iteration margin as well (0.5 below for fences).
@ -33,6 +42,15 @@ public class PassableAxisTracing extends AxisTracing implements ICollidePassable
this.blockCache = blockCache;
}
@Override
public void setBlockChangeTracker(BlockChangeTracker blockChangeTracker,
BlockChangeReference blockChangeReference, int tick, UUID worldId) {
this.blockChangeTracker = blockChangeTracker;
this.blockChangeRef = blockChangeReference;
this.tick = tick;
this.worldId = worldId;
}
@Override
protected void collectInitiallyCollidingBlocks(double minX, double minY, double minZ, double maxX, double maxY,
double maxZ, BlockPositionContainer results) {
@ -56,16 +74,18 @@ public class PassableAxisTracing extends AxisTracing implements ICollidePassable
*/
return true;
}
// TODO: if (blockChangeTracker != null && -- check with BlockChangeTracker and BlockChangeReference --
else {
collides = true;
return false;
// Recovery attempt via the BlockChangeTracker.
if (blockChangeTracker != null) {
// Opportunistic (FCFS, no consistency).
final BlockChangeEntry entry = blockChangeTracker.getBlockChangeEntry(blockChangeRef, tick, worldId, blockX, blockY, blockZ, null);
if (entry != null) {
blockChangeRef.updateSpan(entry);
return true;
}
}
}
@Override
public void set(double x0, double y0, double z0, double x1, double y1, double z1) {
super.set(x0, y0, z0, x1, y1, z1);
// No condition for passing through found.
collides = true;
return false;
}
@Override
@ -82,6 +102,9 @@ public class PassableAxisTracing extends AxisTracing implements ICollidePassable
@Override
public void cleanup() {
blockCache = null;
blockChangeTracker = null;
blockChangeRef = null;
worldId = null;
}
}

View File

@ -14,6 +14,10 @@
*/
package fr.neatmonster.nocheatplus.utilities.collision;
import java.util.UUID;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference;
import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation;
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
@ -32,6 +36,12 @@ public class PassableRayTracing extends RayTracing implements ICollidePassable {
this.blockCache = blockCache;
}
@Override
public void setBlockChangeTracker(BlockChangeTracker blockChangeTracker,
BlockChangeReference blockChangeReference, int tick, UUID worldId) {
// (Not supported.)
}
@Override
public void set(final PlayerLocation from, final PlayerLocation to){
set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ());

View File

@ -1013,8 +1013,11 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
/**
* Check for tracked block changes, having moved a block into a certain
* direction, using the full bounding box (pistons). The given
* BlockChangeReference is not changed, it has to be updated externally.
* direction, using the full bounding box (pistons).
* BlockChangeReference.updateSpan is called with the earliest entry found
* (updateFinal has to be called extra). This is an opportunistic version
* without any consistency checking done, just updating the span by the
* earliest entry found.
*
* @param blockChangeTracker
* the block change tracker
@ -1024,10 +1027,10 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
* the direction
* @param coverDistance
* The (always positive) distance to cover.
* @return A matching BlockChangeEntry with the minimal id. If no entry was
* found, null is returned.
* @return Returns true, iff an entry was found.
*/
public BlockChangeEntry matchBlockChange(final BlockChangeTracker blockChangeTracker, final BlockChangeReference ref, final Direction direction, final double coverDistance) {
public boolean matchBlockChange(final BlockChangeTracker blockChangeTracker, final BlockChangeReference ref, final Direction direction, final double coverDistance) {
// TODO: update span instead !
final int tick = TickTask.getTick();
final UUID worldId = world.getUID();
final int iMinX = Location.locToBlock(minX);
@ -1050,7 +1053,13 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
}
}
}
return minEntry;
if (minEntry == null) {
return false;
}
else {
ref.updateSpan(minEntry);
return true;
}
}
/**