[BLEEDING][BREAKING] Shift entry points for past state bounce.

Lots of issues remain, but vertical push/bounce with pistons is much
improved. Still there are show stoppers (false positives remain, as well
as occasional fall damage).

Use same/similar entry points, like static/classic bounce checking in
MovingListener. SurvivalFly still keeps one exception with the
after-failure Y_POS block move check.

Also check: https://github.com/NoCheatPlus/Issues/issues/5
This commit is contained in:
asofold 2016-12-09 23:50:34 +01:00
parent 9dca93e650
commit 6de231ac54
6 changed files with 316 additions and 64 deletions

View File

@ -822,6 +822,15 @@ public class MovingData extends ACheckData {
verVel.addToFront(entry); verVel.addToFront(entry);
} }
/**
* Get the first element without using it.
*
* @return
*/
public SimpleEntry peekVerticalVelocity(final double amount, final int maxActCount) {
return verVel.peek(amount, maxActCount, TOL_VVEL);
}
public void addVerticalVelocity(final SimpleEntry entry) { public void addVerticalVelocity(final SimpleEntry entry) {
verVel.add(entry); verVel.add(entry);
} }

View File

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
@ -83,6 +84,8 @@ import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.compat.BridgeMisc; import fr.neatmonster.nocheatplus.compat.BridgeMisc;
import fr.neatmonster.nocheatplus.compat.MCAccess; import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker; import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction;
import fr.neatmonster.nocheatplus.components.data.IData; import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.location.SimplePositionWithLook; import fr.neatmonster.nocheatplus.components.location.SimplePositionWithLook;
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess; import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
@ -119,6 +122,28 @@ import fr.neatmonster.nocheatplus.utilities.map.MapUtil;
*/ */
public class MovingListener extends CheckListener implements TickListener, IRemoveData, IHaveCheckType, INotifyReload, INeedConfig, JoinLeaveListener{ public class MovingListener extends CheckListener implements TickListener, IRemoveData, IHaveCheckType, INotifyReload, INeedConfig, JoinLeaveListener{
/**
* Bounce preparation state.
* @author asofold
*
*/
public static enum BounceType {
/** No bounce happened. */
NO_BOUNCE,
/** Ordinary bounce off a static block underneath. */
STATIC,
/**
* Ordinary bounce, due to a slime block having been underneath in the
* past. Rather for logging.
*/
STATIC_PAST,
/**
* A slime block has been underneath, pushing up into the player.
*/
STATIC_PAST_AND_PUSH,
// WEAK_PUSH <- TBD: with edge on slime, or with falling inside of the new slime block position?
}
/** The no fall check. **/ /** The no fall check. **/
public final NoFall noFall = addCheck(new NoFall()); public final NoFall noFall = addCheck(new NoFall());
@ -624,7 +649,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Pre-check checks (hum), either for cf or for sf. // Pre-check checks (hum), either for cf or for sf.
boolean checkNf = true; boolean checkNf = true;
boolean verticalBounce = false; BounceType verticalBounce = BounceType.NO_BOUNCE;
// TODO: More adaptive margin / method (bounding boxes). // TODO: More adaptive margin / method (bounding boxes).
final boolean useBlockChangeTracker = cc.trackBlockMove && (cc.passableCheck || checkSf || checkCf) final boolean useBlockChangeTracker = cc.trackBlockMove && (cc.passableCheck || checkSf || checkCf)
@ -656,22 +681,46 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
// Check jumping on things like slime blocks. // Check jumping on things like slime blocks.
// Detect potential bounce. // Detect bounce type / use prepared bounce.
if (newTo == null) { if (newTo == null) {
// TODO: With past states: What does jump effects do here?
if (to.getY() < from.getY()) { if (to.getY() < from.getY()) {
if ((BlockProperties.getBlockFlags(pTo.getTypeIdBelow()) & BlockProperties.F_BOUNCE25) != 0L // Prepare bounce: The center of the player must be above the block.
&& !survivalFly.isReallySneaking(player) && checkBounceEnvelope(player, pFrom, pTo, data, cc) // Common pre-conditions.
) { // TODO: Check if really leads to calling the method for pistons (checkBounceEnvelope vs. push).
// Prepare bounce: The center of the player must be above the block. if (!survivalFly.isReallySneaking(player)
&& checkBounceEnvelope(player, pFrom, pTo, data, cc)) {
// TODO: Check other side conditions (fluids, web, max. distance to the block top (!)) // TODO: Check other side conditions (fluids, web, max. distance to the block top (!))
verticalBounce = true; // Classic static bounce.
// Skip NoFall. if ((BlockProperties.getBlockFlags(pTo.getTypeIdBelow())
checkNf = false; & BlockProperties.F_BOUNCE25) != 0L) {
/*
* TODO: May need to adapt within this method, if
* "push up" happened and the trigger had been
* ordinary.
*/
verticalBounce = BounceType.STATIC;
checkNf = false; // Skip NoFall.
}
if (verticalBounce == BounceType.NO_BOUNCE && useBlockChangeTracker) {
verticalBounce = checkPastStateBounceDescend(player, pFrom, pTo, thisMove, lastMove,
tick, data, cc);
if (verticalBounce != BounceType.NO_BOUNCE) {
checkNf = false; // Skip NoFall.
}
}
} }
} }
else if (data.verticalBounce != null) { else {
// Prepared bounce support. if (
if (onPreparedBounceSupport(player, from, to, lastMove, tick, data)) { // Prepared bounce support.
data.verticalBounce != null
&& onPreparedBounceSupport(player, from, to, thisMove, lastMove, tick, data)
// Past state bounce (includes prepending velocity / special calls).
|| useBlockChangeTracker
&& thisMove.yDistance >= 0.415 && thisMove.yDistance <= 1.515 // TODO: MAGIC
&& checkPastStateBounceAscend(player, pFrom, pTo, thisMove, lastMove, tick, data, cc)
) {
checkNf = false; checkNf = false;
} }
} }
@ -681,7 +730,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Check passable first to prevent set-back override. // Check passable first to prevent set-back override.
// TODO: Redesign to set set-backs later (queue + invalidate). // TODO: Redesign to set set-backs later (queue + invalidate).
boolean mightSkipNoFall = false; // If to skip nofall check (mainly on violation of other checks). 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)) { 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. // 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, tick, useBlockChangeTracker); newTo = passable.check(player, pFrom, pTo, data, cc, tick, useBlockChangeTracker);
if (newTo != null) { if (newTo != null) {
@ -802,8 +853,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
if (newTo == null) { if (newTo == null) {
// Allowed move. // Allowed move.
// Bounce effects. // Bounce effects.
if (verticalBounce) { if (verticalBounce != BounceType.NO_BOUNCE) {
processBounce(player, pFrom.getY(), pTo.getY(), data, cc); processBounce(player, pFrom.getY(), pTo.getY(), verticalBounce, data, cc);
} }
// Finished move processing. // Finished move processing.
if (processingEvents.containsKey(playerName)) { if (processingEvents.containsKey(playerName)) {
@ -820,6 +871,14 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
return false; return false;
} }
else { else {
if (data.debug) { // TODO: Remove, if not relevant (doesn't look like it was :p).
if (verticalBounce != BounceType.NO_BOUNCE) {
debug(player, "Bounce effect not processed: " + verticalBounce);
}
if (data.verticalBounce != null) {
debug(player, "Bounce effect not used: " + data.verticalBounce);
}
}
// Set-back handling. // Set-back handling.
onSetBack(player, event, newTo, data, cc); onSetBack(player, event, newTo, data, cc);
return true; return true;
@ -827,7 +886,116 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
/** /**
* A slime block is underneath, the player isn't really sneaking. * Only for yDistance < 0 + some bounce envelope checked.
* @param player
* @param from
* @param to
* @param lastMove
* @param lastMove2
* @param tick
* @param data
* @param cc
* @return
*/
private BounceType checkPastStateBounceDescend(
final Player player, final PlayerLocation from, final PlayerLocation to,
final PlayerMoveData thisMove, final PlayerMoveData lastMove, final int tick,
final MovingData data, final MovingConfig cc) {
// TODO: Find more preconditions.
final UUID worldId = from.getWorld().getUID();
// Prepare (normal/extra) bounce.
// TODO: Might later need to override/adapt just the bounce effect set by the ordinary method.
// Typical: a slime block has been there.
final BlockChangeEntry entryBelowAny = blockChangeTracker.getBlockChangeEntryMatchFlags(
data.blockChangeRef, tick, worldId, to.getBlockX(), to.getBlockY() - 1, to.getBlockZ(),
null, BlockProperties.F_BOUNCE25);
if (entryBelowAny != null) {
// TODO: Check preconditions for bouncing here at all (!).
// Check if the/a block below the feet of the player got pushed into the feet of the player.
// TODO: Not sure if this can/should be done on ascending.
final BlockChangeEntry entryBelowY_POS = entryBelowAny.direction == Direction.Y_POS ? entryBelowAny
: blockChangeTracker.getBlockChangeEntryMatchFlags(data.blockChangeRef, tick, worldId,
to.getBlockX(), to.getBlockY() - 1, to.getBlockZ(), Direction.Y_POS,
BlockProperties.F_BOUNCE25);
if (entryBelowY_POS != null) {
// TODO: Can't know if used... data.blockChangeRef.updateSpan(entryBelowY_POS);
// TODO: So far, doesn't seem to be followed by violations.
return BounceType.STATIC_PAST_AND_PUSH;
}
else {
// TODO: Can't know if used... data.blockChangeRef.updateSpan(entryBelowAny);
return BounceType.STATIC_PAST;
}
}
// TODO: ADDITIONAL: A slime block has been pushed up [a) block below counts ? b) into the feet of the player].
/*
* TODO: Can't update span here. If at all, it can be added as side
* condition for using the bounce effect. Probably not worth it.
*/
return BounceType.NO_BOUNCE; // Nothing found, return no bounce.
}
private boolean checkPastStateBounceAscend(
final Player player, final PlayerLocation from, final PlayerLocation to,
final PlayerMoveData thisMove, final PlayerMoveData lastMove, final int tick,
final MovingData data, final MovingConfig cc) {
// TODO: find more preconditions.
final UUID worldId = from.getWorld().getUID();
// Possibly a "lost use of slime".
// TODO: Might need to cover push up, after ordinary slime bounce.
// TODO: Cover push by slime block (center/feet on block).
// TODO: Might need to cover push by slime block (center/feet off block).
// TODO: Low fall distance cases and where no slime block was underneath at descend have to be included here.
// TODO: Note that onPreparedBounceSupport etc. has to be called in here, if needed.
final BlockChangeEntry entryBelowY_POS = blockChangeTracker.getBlockChangeEntryMatchFlags(
data.blockChangeRef, tick, worldId, from.getBlockX(), from.getBlockY() - 1, from.getBlockZ(),
Direction.Y_POS, BlockProperties.F_BOUNCE25);
if (
// Center push.
entryBelowY_POS != null
// Off center push (2x 0.5(015) only, (sum below 1.015 ?)).
|| thisMove.yDistance < 0.5015 && from.matchBlockChangeMatchResultingFlags(blockChangeTracker,
data.blockChangeRef, Direction.Y_POS, Math.min(.415, thisMove.yDistance),
BlockProperties.F_BOUNCE25)
) {
// Always allow the double 0.505 move.
/*
* TODO: May detect the first 0.505 move to match preconditions
* better (roughly 2x 0.5, 0.0 -> 1.5, -0.few -> 1.389). One might
* also nail down to past y-position further. Also consider
* confining to horizontal speed envelope (is that even possible)?
*/
if (data.debug) {
debug(player, "checkPastStateBounceAscend: " + (entryBelowY_POS == null ? "off_center" : "center"));
}
final double amount = Math.max(0.505, thisMove.yDistance);
if (data.peekVerticalVelocity(amount, 2) == null) {
// (Could skip peek for low distances around 0.5.)
if (amount > 1.38) {
// TODO: HACK - More decrease than expected.
// TODO: Consider to detect used velocity with vdistrel exceptions using past move tracking.
data.prependVerticalVelocity(new SimpleEntry(tick, 0.925, 3));
}
data.prependVerticalVelocity(new SimpleEntry(tick, amount, 2));
/*
* TODO: Update span or not ... [could use a class that extends
* SimpleEntry but which also contains a block change id to update
* span with, upon using that entry?]
*/
if (entryBelowY_POS != null) {
data.blockChangeRef.updateSpan(entryBelowY_POS);
}
return true;
}
}
return false;
}
/**
* Pre conditions: A slime block is underneath and the player isn't really
* sneaking.<br>
* *
* @param player * @param player
* @param from * @param from
@ -836,13 +1004,20 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
* @param cc * @param cc
* @return * @return
*/ */
private boolean checkBounceEnvelope(Player player, PlayerLocation from, PlayerLocation to, MovingData data, MovingConfig cc) { private boolean checkBounceEnvelope(final Player player, final PlayerLocation from, final PlayerLocation to,
final MovingData data, final MovingConfig cc) {
/*
* TODO: Likely not conform with getting pushed up, while outside of the
* strict envelope. The advantage of detecting that here could be the
* invalidation mechanics (also consider passable etc.), otherwise the
* move up might miss the already invalidated push up.
*/
return return
// 0: Normal envelope (forestall NoFall). // 0: Normal envelope (forestall NoFall).
( (
// 1: Ordinary. // 1: Ordinary.
to.getY() - to.getBlockY() <= Math.max(cc.yOnGround, cc.noFallyOnGround) to.getY() - to.getBlockY() <= Math.max(cc.yOnGround, cc.noFallyOnGround)
// 1: With carpet. TODO: Magic block id. // 1: With carpet. TODO: Magic block id. Use isCarpet(id) instead.
|| to.getTypeId() == 171 && to.getY() - to.getBlockY() <= 0.9 || to.getTypeId() == 171 && to.getY() - to.getBlockY() <= 0.9
) )
&& MovingUtil.getRealisticFallDistance(player, from.getY(), to.getY(), data) > 1.0 && MovingUtil.getRealisticFallDistance(player, from.getY(), to.getY(), data) > 1.0
@ -860,11 +1035,13 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
* @param from * @param from
* @param to * @param to
* @param lastMove * @param lastMove
* @param lastMove2
* @param tick * @param tick
* @param data * @param data
* @return True, if bounce has been used, i.e. to do without fall damage. * @return True, if bounce has been used, i.e. to do without fall damage.
*/ */
private boolean onPreparedBounceSupport(Player player, Location from, Location to, PlayerMoveData lastMove, int tick, MovingData data) { private boolean onPreparedBounceSupport(final Player player, final Location from, final Location to,
final PlayerMoveData thisMove, final PlayerMoveData lastMove, final int tick, final MovingData data) {
if (to.getY() > from.getY() || to.getY() == from.getY() && data.verticalBounce.value < 0.13) { if (to.getY() > from.getY() || to.getY() == from.getY() && data.verticalBounce.value < 0.13) {
// Apply bounce. // Apply bounce.
if (to.getY() == from.getY()) { if (to.getY() == from.getY()) {
@ -900,7 +1077,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
* @return * @return
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Location checkExtremeMove(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc) { private Location checkExtremeMove(final Player player, final PlayerLocation from, final PlayerLocation to,
final MovingData data, final MovingConfig cc) {
final PlayerMoveData thisMove = data.playerMoves.getCurrentMove(); final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove(); final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove();
// TODO: Latency effects. // TODO: Latency effects.
@ -1003,7 +1181,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
} }
private static double guessFlyNoFlyVelocity(final Player player, final PlayerMoveData thisMove, final PlayerMoveData lastMove, final MovingData data) { private static double guessFlyNoFlyVelocity(final Player player,
final PlayerMoveData thisMove, final PlayerMoveData lastMove, final MovingData data) {
// Default margin: Allow slightly less than the previous speed. // Default margin: Allow slightly less than the previous speed.
final double defaultAmount = lastMove.hDistance * (1.0 + Magic.FRICTION_MEDIUM_AIR) / 2.0; final double defaultAmount = lastMove.hDistance * (1.0 + Magic.FRICTION_MEDIUM_AIR) / 2.0;
// Test for exceptions. // Test for exceptions.
@ -1024,18 +1203,20 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
* on ground. This might be a micro-move onto ground. * on ground. This might be a micro-move onto ground.
* *
* @param player * @param player
* @param verticalBounce
* @param from * @param from
* @param to * @param to
* @param data * @param data
* @param cc * @param cc
*/ */
private void processBounce(final Player player,final double fromY, final double toY, final MovingData data, final MovingConfig cc) { private void processBounce(final Player player,final double fromY, final double toY,
final BounceType bounceType, final MovingData data, final MovingConfig cc) {
// Prepare velocity. // Prepare velocity.
final double fallDistance = MovingUtil.getRealisticFallDistance(player, fromY, toY, data); final double fallDistance = MovingUtil.getRealisticFallDistance(player, fromY, toY, data);
final double base = Math.sqrt(fallDistance) / 3.3; final double base = Math.sqrt(fallDistance) / 3.3;
double effect = Math.min(3.5, base + Math.min(base / 10.0, Magic.GRAVITY_MAX)); // Ancient Greek technology with gravity added. double effect = Math.min(Magic.BOUNCE_VERTICAL_MAX_DIST, base + Math.min(base / 10.0, Magic.GRAVITY_MAX)); // Ancient Greek technology with gravity added.
final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove(); final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove();
if (effect > 0.42 && lastMove.toIsValid) { if (effect > 0.415 && lastMove.toIsValid) {
// Extra cap by last y distance(s). // Extra cap by last y distance(s).
final double max_gain = Math.abs(lastMove.yDistance < 0.0 ? Math.min(lastMove.yDistance, toY - fromY) : (toY - fromY)) - Magic.GRAVITY_SPAN; final double max_gain = Math.abs(lastMove.yDistance < 0.0 ? Math.min(lastMove.yDistance, toY - fromY) : (toY - fromY)) - Magic.GRAVITY_SPAN;
if (max_gain < effect) { if (max_gain < effect) {
@ -1045,9 +1226,17 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
} }
} }
if (bounceType == BounceType.STATIC_PAST_AND_PUSH) {
/*
* TODO: Find out if relevant and handle here (still use maximum
* cap, but not by y-distance.). Could be the push part is only
* necessary if the player is pushed upwards without prepared
* bounce.
*/
}
// (Actually observed max. is near 3.5.) TODO: Why 3.14 then? // (Actually observed max. is near 3.5.) TODO: Why 3.14 then?
if (data.debug) { if (data.debug) {
debug(player, "Bounce effect (dY=" + fallDistance + "): " + effect); debug(player, "Set bounce effect (dY=" + fallDistance + " / " + bounceType + "): " + effect);
} }
data.noFallSkipAirCheck = true; data.noFallSkipAirCheck = true;
data.verticalBounce = new SimpleEntry(effect, 1); data.verticalBounce = new SimpleEntry(effect, 1);
@ -1998,7 +2187,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
* @param to * @param to
* @param mcAccess * @param mcAccess
*/ */
private void outputMoveDebug(final Player player, final PlayerLocation from, final PlayerLocation to, final double maxYOnGround, final MCAccess mcAccess) { private void outputMoveDebug(final Player player, final PlayerLocation from, final PlayerLocation to,
final double maxYOnGround, final MCAccess mcAccess) {
final StringBuilder builder = new StringBuilder(250); final StringBuilder builder = new StringBuilder(250);
final Location loc = player.getLocation(); final Location loc = player.getLocation();
builder.append(CheckUtils.getLogMessagePrefix(player, checkType)); builder.append(CheckUtils.getLogMessagePrefix(player, checkType));

View File

@ -90,6 +90,12 @@ public class Magic {
public static final double Y_ON_GROUND_DEFAULT = 0.016; // Jump upwards, while placing blocks. public static final double Y_ON_GROUND_DEFAULT = 0.016; // Jump upwards, while placing blocks.
// public static final double Y_ON_GROUND_DEFAULT = 0.029; // Bounce off slime blocks. // public static final double Y_ON_GROUND_DEFAULT = 0.029; // Bounce off slime blocks.
/**
* The maximum distance that can be achieved with bouncing back from slime
* blocks.
*/
public static final double BOUNCE_VERTICAL_MAX_DIST = 3.5;
// Other constants. // Other constants.
public static final double PAPER_DIST = 0.01; public static final double PAPER_DIST = 0.01;
/** /**

View File

@ -44,6 +44,7 @@ import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
import fr.neatmonster.nocheatplus.compat.Bridge1_9; import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeEnchant; import fr.neatmonster.nocheatplus.compat.BridgeEnchant;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker; import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction; import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction;
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess; import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
@ -133,14 +134,16 @@ public class SurvivalFly extends Check {
// TODO: Could run a completely different check here (roughly none :p). // TODO: Could run a completely different check here (roughly none :p).
xDistance = yDistance = zDistance = hDistance = 0.0; xDistance = yDistance = zDistance = hDistance = 0.0;
hasHdist = false; hasHdist = false;
} else { }
else {
xDistance = to.getX() - from.getX(); xDistance = to.getX() - from.getX();
yDistance = thisMove.yDistance; yDistance = thisMove.yDistance;
zDistance = to.getZ() - from.getZ(); zDistance = to.getZ() - from.getZ();
if (xDistance == 0.0 && zDistance == 0.0) { if (xDistance == 0.0 && zDistance == 0.0) {
hDistance = 0.0; hDistance = 0.0;
hasHdist = false; hasHdist = false;
} else { }
else {
hasHdist = true; hasHdist = true;
hDistance = thisMove.hDistance; hDistance = thisMove.hDistance;
} }
@ -168,7 +171,8 @@ public class SurvivalFly extends Check {
tags.add("invalidate_lostsprint"); tags.add("invalidate_lostsprint");
if (now <= data.timeSprinting + cc.sprintingGrace) { if (now <= data.timeSprinting + cc.sprintingGrace) {
sprinting = true; sprinting = true;
} else { }
else {
sprinting = false; sprinting = false;
} }
} }
@ -222,7 +226,8 @@ public class SurvivalFly extends Check {
else if (lastMove.toIsValid && lastMove.hDistance > 0.0 && lastMove.yDistance < -0.3) { else if (lastMove.toIsValid && lastMove.hDistance > 0.0 && lastMove.yDistance < -0.3) {
// Note that to is not on ground either. // Note that to is not on ground either.
resetFrom = LostGround.lostGroundStill(player, from, to, hDistance, yDistance, sprinting, lastMove, data, cc, tags); resetFrom = LostGround.lostGroundStill(player, from, to, hDistance, yDistance, sprinting, lastMove, data, cc, tags);
} else { }
else {
resetFrom = false; resetFrom = false;
} }
} }
@ -372,7 +377,8 @@ public class SurvivalFly extends Check {
tags.add("sprintback"); // Might add it anyway. tags.add("sprintback"); // Might add it anyway.
} }
} }
} else { }
else {
/* /*
* TODO: Consider to log and/or remember when this was last time * TODO: Consider to log and/or remember when this was last time
* cleared [add time distance to tags/log on violations]. * cleared [add time distance to tags/log on violations].
@ -454,7 +460,8 @@ public class SurvivalFly extends Check {
outputDebug(player, to, data, cc, hDistance, hAllowedDistance, hFreedom, yDistance, vAllowedDistance, fromOnGround, resetFrom, toOnGround, resetTo); outputDebug(player, to, data, cc, hDistance, hAllowedDistance, hFreedom, yDistance, vAllowedDistance, fromOnGround, resetFrom, toOnGround, resetTo);
tagsLength = tags.size(); tagsLength = tags.size();
data.ws.setJustUsedIds(null); data.ws.setJustUsedIds(null);
} else { }
else {
tagsLength = 0; // JIT vs. IDE. tagsLength = 0; // JIT vs. IDE.
} }
@ -547,7 +554,8 @@ public class SurvivalFly extends Check {
} }
else if (!resetFrom || !resetTo) { else if (!resetFrom || !resetTo) {
data.insideMediumCount = 0; data.insideMediumCount = 0;
} else { }
else {
data.insideMediumCount ++; data.insideMediumCount ++;
} }
@ -599,7 +607,8 @@ public class SurvivalFly extends Check {
// Adjust in-air counters. // Adjust in-air counters.
if (yDistance == 0.0) { if (yDistance == 0.0) {
data.sfZeroVdistRepeat ++; data.sfZeroVdistRepeat ++;
} else { }
else {
data.sfZeroVdistRepeat = 0; data.sfZeroVdistRepeat = 0;
} }
} }
@ -675,29 +684,39 @@ public class SurvivalFly extends Check {
|| yDistance <= 1.015 && to.getY() - to.getBlockY() < 0.015)) { || yDistance <= 1.015 && to.getY() - to.getBlockY() < 0.015)) {
if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS,
Math.min(yDistance, 1.0))) { Math.min(yDistance, 1.0))) {
if (yDistance > 1.0) {
final BlockChangeEntry entry = blockChangeTracker.getBlockChangeEntryMatchFlags(data.blockChangeRef,
tick, from.getWorld().getUID(), from.getBlockX(), from.getBlockY() - 1, from.getBlockZ(),
Direction.Y_POS, BlockProperties.F_BOUNCE25);
if (entry != null) {
data.blockChangeRef.updateSpan(entry);
data.prependVerticalVelocity(new SimpleEntry(tick, 0.5015, 3)); // TODO: HACK
tags.add("past_bounce");
}
}
tags.add("blkmv_y_pos"); tags.add("blkmv_y_pos");
final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ? final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ?
return new double[]{maxDistYPos, 0.0}; return new double[]{maxDistYPos, 0.0};
} }
} }
// (No else.) // (No else.)
if (yDistance <= 1.55) { // if (yDistance <= 1.55) {
// TODO: Edges ca. 0.5 (or 2x 0.5). // // TODO: Edges ca. 0.5 (or 2x 0.5).
// TODO: Center ca. 1.5. With falling height, values increase slightly. // // TODO: Center ca. 1.5. With falling height, values increase slightly.
// Simplified: Always allow 1.5 or less with being pushed up by slime. // // Simplified: Always allow 1.5 or less with being pushed up by slime.
// TODO: // // TODO:
if (from.matchBlockChangeMatchResultingFlags( // if (from.matchBlockChangeMatchResultingFlags(
blockChangeTracker, data.blockChangeRef, Direction.Y_POS, // blockChangeTracker, data.blockChangeRef, Direction.Y_POS,
Math.min(yDistance, 0.42), // Special limit. // Math.min(yDistance, 0.415), // Special limit.
BlockProperties.F_BOUNCE25)) { // BlockProperties.F_BOUNCE25)) {
tags.add("blkmv_y_pos_bounce"); // tags.add("blkmv_y_pos_bounce");
final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ? // final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ?
// TODO: Set bounce effect or something !? // // TODO: Set bounce effect or something !?
// TODO: Bounce effect instead ? // // TODO: Bounce effect instead ?
data.addVerticalVelocity(new SimpleEntry(tick, Math.max(0.515, yDistance - 0.5), 2)); // data.addVerticalVelocity(new SimpleEntry(tick, Math.max(0.515, yDistance - 0.5), 2));
return new double[]{maxDistYPos, 0.0}; // return new double[]{maxDistYPos, 0.0};
} // }
} // }
} }
// Push (/pull) down. // Push (/pull) down.
else if (yDistance < 0.0 && yDistance >= -1.0) { else if (yDistance < 0.0 && yDistance >= -1.0) {
@ -969,7 +988,8 @@ public class SurvivalFly extends Check {
// Hack for boats (coarse: allows minecarts too). // Hack for boats (coarse: allows minecarts too).
if (yDistance > cc.sfStepHeight && yDistance - cc.sfStepHeight < 0.00000003 && to.isOnGroundDueToStandingOnAnEntity()) { if (yDistance > cc.sfStepHeight && yDistance - cc.sfStepHeight < 0.00000003 && to.isOnGroundDueToStandingOnAnEntity()) {
vAllowedDistance = yDistance; vAllowedDistance = yDistance;
} else { }
else {
vAllowedDistance = Math.max(cc.sfStepHeight, maxJumpGain + jumpGainMargin); vAllowedDistance = Math.max(cc.sfStepHeight, maxJumpGain + jumpGainMargin);
} }
} }
@ -1088,7 +1108,8 @@ public class SurvivalFly extends Check {
vDistRelVL = true; vDistRelVL = true;
} }
// else: Allow moving up less. Note: possibility of low jump. // else: Allow moving up less. Note: possibility of low jump.
} else { // if (yDistance < 0.0) // Rather too fast falling. }
else { // if (yDistance < 0.0) // Rather too fast falling.
if (yDistance < -3.0 && lastMove.yDistance < -3.0 && Math.abs(yDistDiffEx) < 5.0 * Magic.GRAVITY_MAX) { if (yDistance < -3.0 && lastMove.yDistance < -3.0 && Math.abs(yDistDiffEx) < 5.0 * Magic.GRAVITY_MAX) {
// Disregard not falling faster at some point (our constants don't match 100%). // Disregard not falling faster at some point (our constants don't match 100%).
} }
@ -1333,7 +1354,8 @@ public class SurvivalFly extends Check {
final float sc0; final float sc0;
if (count0 == cap) { if (count0 == cap) {
sc0 = acc.bucketScore(0); sc0 = acc.bucketScore(0);
} else { }
else {
// Catch extreme changes quick. // Catch extreme changes quick.
sc0 = acc.bucketScore(0) * (float) cap / (float) count0 - Magic.GRAVITY_VACC * (float) (cap - count0); sc0 = acc.bucketScore(0) * (float) cap / (float) count0 - Magic.GRAVITY_VACC * (float) (cap - count0);
} }
@ -1729,7 +1751,8 @@ public class SurvivalFly extends Check {
final double vl2 = Math.abs(yDistAbs - frictDist - (yDistance < 0.0 ? Magic.GRAVITY_MAX + Magic.GRAVITY_SPAN : Magic.GRAVITY_MIN)); final double vl2 = Math.abs(yDistAbs - frictDist - (yDistance < 0.0 ? Magic.GRAVITY_MAX + Magic.GRAVITY_SPAN : Magic.GRAVITY_MIN));
if (vl1 <= vl2) { if (vl1 <= vl2) {
return new double[]{yDistance < 0.0 ? -baseSpeed : baseSpeed, vl1}; return new double[]{yDistance < 0.0 ? -baseSpeed : baseSpeed, vl1};
} else { }
else {
return new double[]{yDistance < 0.0 ? -frictDist - Magic.GRAVITY_MAX - Magic.GRAVITY_SPAN : frictDist - Magic.GRAVITY_MIN, vl2}; return new double[]{yDistance < 0.0 ? -frictDist - Magic.GRAVITY_MAX - Magic.GRAVITY_SPAN : frictDist - Magic.GRAVITY_MIN, vl2};
} }
} }
@ -1772,7 +1795,8 @@ public class SurvivalFly extends Check {
tags.add("climbstep"); tags.add("climbstep");
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance) - maxSpeed); vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance) - maxSpeed);
} }
} else { }
else {
tags.add("climbspeed"); tags.add("climbspeed");
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance) - maxSpeed); vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance) - maxSpeed);
} }
@ -1942,7 +1966,8 @@ public class SurvivalFly extends Check {
if (now - data.sfCobwebTime > 3000) { if (now - data.sfCobwebTime > 3000) {
data.sfCobwebTime = now; data.sfCobwebTime = now;
data.sfCobwebVL = vDistanceAboveLimit * 100D; data.sfCobwebVL = vDistanceAboveLimit * 100D;
} else { }
else {
data.sfCobwebVL += vDistanceAboveLimit * 100D; data.sfCobwebVL += vDistanceAboveLimit * 100D;
} }
if (data.sfCobwebVL < 550) { // Totally random ! if (data.sfCobwebVL < 550) { // Totally random !
@ -1953,7 +1978,8 @@ public class SurvivalFly extends Check {
} }
data.sfJumpPhase = 0; data.sfJumpPhase = 0;
return true; return true;
} else { }
else {
return false; return false;
} }
} }

View File

@ -105,6 +105,27 @@ public class SimpleAxisVelocity {
return null; return null;
} }
/**
* Without checking for invalidation, test if there is a matching entry with
* same or less the activation count.
*
* @param amount
* @param maxActCount
* @param tolerance
* @return
*/
public SimpleEntry peek(final double amount, final int maxActCount, final double tolerance) {
final Iterator<SimpleEntry> it = queued.iterator();
while (it.hasNext()) {
final SimpleEntry entry = it.next();
if (entry.actCount < maxActCount && matchesEntry(entry, amount, tolerance)) {
return entry;
}
}
// None found.
return null;
}
/** /**
* Check if the demanded amount can be covered by this velocity entry. Might * Check if the demanded amount can be covered by this velocity entry. Might
* return an entry with a small value with a different sign, if amount is * return an entry with a small value with a different sign, if amount is
@ -128,15 +149,16 @@ public class SimpleAxisVelocity {
* Remove all entries that have been added before the given tick, or for * Remove all entries that have been added before the given tick, or for
* which the activation count has reached 0. * which the activation count has reached 0.
* *
* @param tick * @param invalidateBeforeTick
* Entries with a smaller tick value get removed.
*/ */
public void removeInvalid(final int tick) { public void removeInvalid(final int invalidateBeforeTick) {
// Note: clear invalidated here, append unused to invalidated. // Note: clear invalidated here, append unused to invalidated.
final Iterator<SimpleEntry> it = queued.iterator(); final Iterator<SimpleEntry> it = queued.iterator();
while (it.hasNext()) { while (it.hasNext()) {
final SimpleEntry entry = it.next(); final SimpleEntry entry = it.next();
entry.actCount --; // Let others optimize this. entry.actCount --; // Let others optimize this.
if (entry.actCount <= 0 || entry.tick < tick) { if (entry.actCount <= 0 || entry.tick < invalidateBeforeTick) {
it.remove(); it.remove();
// Track unused velocity. // Track unused velocity.
if (unusedActive) { if (unusedActive) {

View File

@ -1078,7 +1078,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
* @param ref * @param ref
* the ref * the ref
* @param direction * @param direction
* the direction * Pass null to ignore the direction.
* @param coverDistance * @param coverDistance
* The (always positive) distance to cover. * The (always positive) distance to cover.
* @return Returns true, iff an entry was found. * @return Returns true, iff an entry was found.
@ -1133,7 +1133,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
* @param ref * @param ref
* the ref * the ref
* @param direction * @param direction
* the direction * Pass null to ignore the direction.
* @param coverDistance * @param coverDistance
* The (always positive) distance to cover. * The (always positive) distance to cover.
* @param matchFlags * @param matchFlags
@ -1144,7 +1144,6 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
public boolean matchBlockChangeMatchResultingFlags(final BlockChangeTracker blockChangeTracker, public boolean matchBlockChangeMatchResultingFlags(final BlockChangeTracker blockChangeTracker,
final BlockChangeReference ref, final Direction direction, final double coverDistance, final BlockChangeReference ref, final Direction direction, final double coverDistance,
final long matchFlags) { final long matchFlags) {
// TODO: Remove this method (!). Use a specialized method (here or external) just for bounce.
/* /*
* TODO: Not sure with code duplication. Is it better to run * TODO: Not sure with code duplication. Is it better to run
* BlockChangeTracker.getBlockChangeMatchFlags for the other method too? * BlockChangeTracker.getBlockChangeMatchFlags for the other method too?