[BLEEDING] Add/move typical flags to past move tracking.

Move location-dependent properties from MovingData and MoveData to
LocationData.

Rough list of related Changes:
* Represent from and to positions as LocationData inside of MoveData.
* Have flags for onGround, inLiquid and the like in LocationData.
* Change noFallAssumeGround to touchedGroundWorkaround within MoveData.
* Add touchedGround to MoveData (to|from|workaround).
* (Remove involved properties from MovingData and use MoveData.from/to
instead.)
* Use MoveData and LocationData flags instead of PlayerLocation methods
in more places.
* Adjust various special case pre-conditions, based on past move data.

Other changes made on the fly:
* Allow maximum of jump gain and step height for ground -> ground.
* Add envelopes for vDistAir after teleport/join/respawn.
* Add cases for vdistrel.
* Extend logging on teleport (add cause, log set-back too).
* Reorder/fix vdistsb workaround checking.
* Reorder teleport handling.
* Remove small-range workaround for teleport [uncertain effect].

Immediate future follow ups:
* Attempt to only accept PlayerLocation for various setPositions methods
in MovingData (ensure to set MoveData with extra properties +
simplify/cleanup (public) methods with MoveData/LocationData).
* Relate to past move tracking for more workarounds, either to confine
pre-conditions more (inLiquid instead of toWasReset~somehow), or just to
be able to track a false positive at all (thisMove + 2 past moves
needed).
* (Fixes, etc.)

Distant future follow-up:
* Somehow merge with PlayerLocation, e.g. using LocationData inside
PlayerLocation internally, which means changing raw types to Object
versions, just like it's done inside of PlayerLocation right now.
* Possibly PlayerLocation is transformed to static methods with
BlockCache and LocationData as input.

Expected trouble:
* New/old false positives, due to replacing the fromWasReset and
toWasReset by more distinct flags from past move tracking.
 * A workaround may have prevented other false positives
unintentionally, e.g. had been intended for liquid, but the
to/fromWasReset flags previously did include ground/noFallAssumeGround,
thus the workaround will not cover that case anymore, after the change. 
 * Forgetting something like checking touchedGround and to/from.onGround
or similar as a replacement for xyWasReset.
* Mixing up thisMove and lastMove for touchedGround.
* Mixing up touchedGround and touchedGroundWorkaround in MoveData.
This commit is contained in:
asofold 2015-12-26 21:57:39 +01:00
parent 73cdef5b56
commit 67071b3fad
8 changed files with 436 additions and 289 deletions

View File

@ -286,7 +286,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// Check if fly checks is an issue at all, re-check "real sprinting". // Check if fly checks is an issue at all, re-check "real sprinting".
final MoveData lastMove = mData.moveData.getFirst(); final MoveData lastMove = mData.moveData.getFirst();
if (lastMove.valid && mData.liftOffEnvelope == LiftOffEnvelope.NORMAL) { if (lastMove.valid && mData.liftOffEnvelope == LiftOffEnvelope.NORMAL) {
final double hDist = TrigUtil.distance(loc.getX(), loc.getZ(), lastMove.fromX, lastMove.fromZ); final double hDist = TrigUtil.distance(loc.getX(), loc.getZ(), lastMove.from.x, lastMove.from.z);
if (hDist >= 0.23) { if (hDist >= 0.23) {
// TODO: Might need to check hDist relative to speed / modifiers. // TODO: Might need to check hDist relative to speed / modifiers.
final MovingConfig mc = MovingConfig.getConfig(player); final MovingConfig mc = MovingConfig.getConfig(player);

View File

@ -162,8 +162,6 @@ public class MovingData extends ACheckData {
private LocationTrace trace = null; private LocationTrace trace = null;
// sf rather // sf rather
/** To/from was ground or web or assumed to be etc. */
public boolean toWasReset, fromWasReset;
/** Basic envelope constraints for switching into air. */ /** Basic envelope constraints for switching into air. */
public LiftOffEnvelope liftOffEnvelope = defaultLiftOffEnvelope; public LiftOffEnvelope liftOffEnvelope = defaultLiftOffEnvelope;
/** Count how many moves have been made inside a medium (other than air). */ /** Count how many moves have been made inside a medium (other than air). */
@ -197,8 +195,6 @@ public class MovingData extends ACheckData {
public float noFallFallDistance = 0; public float noFallFallDistance = 0;
/** Last y coordinate from when the player was on ground. */ /** Last y coordinate from when the player was on ground. */
public double noFallMaxY = 0; public double noFallMaxY = 0;
/** Indicate that NoFall should assume the player to be on ground. */
public boolean noFallAssumeGround = false;
/** Indicate that NoFall is not to use next damage event for checking on-ground properties. */ /** Indicate that NoFall is not to use next damage event for checking on-ground properties. */
public boolean noFallSkipAirCheck = false; public boolean noFallSkipAirCheck = false;
// Passable check. // Passable check.
@ -224,7 +220,7 @@ public class MovingData extends ACheckData {
public int sfHoverTicks = -1; public int sfHoverTicks = -1;
/** First count these down before incrementing sfHoverTicks. Set on join, if configured so. */ /** First count these down before incrementing sfHoverTicks. Set on join, if configured so. */
public int sfHoverLoginTicks = 0; public int sfHoverLoginTicks = 0;
public int sfOnIce = 0; public int sfOnIce = 0; // TODO: Replace by allowed speed + friction.
public long sfCobwebTime = 0; public long sfCobwebTime = 0;
public double sfCobwebVL = 0; public double sfCobwebVL = 0;
public long sfVLTime = 0; public long sfVLTime = 0;
@ -265,6 +261,7 @@ public class MovingData extends ACheckData {
private void invalidateMoveData() { private void invalidateMoveData() {
final Iterator<MoveData> it = moveData.iterator(); final Iterator<MoveData> it = moveData.iterator();
while (it.hasNext()) { while (it.hasNext()) {
// TODO: If using many elements ever, stop at the first already invalidated one.
it.next().invalidate(); it.next().invalidate();
} }
thisMove.invalidate(); thisMove.invalidate();
@ -295,7 +292,6 @@ public class MovingData extends ACheckData {
removeAllVelocity(); removeAllVelocity();
sfHorizontalBuffer = 0.0; sfHorizontalBuffer = 0.0;
lostSprintCount = 0; lostSprintCount = 0;
toWasReset = fromWasReset = false; // TODO: true maybe
sfHoverTicks = sfHoverLoginTicks = -1; sfHoverTicks = sfHoverLoginTicks = -1;
sfDirty = false; sfDirty = false;
sfLowJump = false; sfLowJump = false;
@ -330,7 +326,6 @@ public class MovingData extends ACheckData {
// keep jump phase. // keep jump phase.
sfHorizontalBuffer = 0.0; sfHorizontalBuffer = 0.0;
lostSprintCount = 0; lostSprintCount = 0;
toWasReset = fromWasReset = false; // TODO: true maybe
sfHoverTicks = -1; // 0 ? sfHoverTicks = -1; // 0 ?
sfDirty = false; sfDirty = false;
sfLowJump = false; sfLowJump = false;
@ -350,8 +345,6 @@ public class MovingData extends ACheckData {
clearAccounting(); clearAccounting();
sfJumpPhase = 0; sfJumpPhase = 0;
sfZeroVdist = 0; sfZeroVdist = 0;
toWasReset = false;
fromWasReset = false;
verticalBounce = null; verticalBounce = null;
// Remember where we send the player to. // Remember where we send the player to.
setTeleported(loc); setTeleported(loc);
@ -359,7 +352,7 @@ public class MovingData extends ACheckData {
} }
/** /**
* Adjust properties that relate to the mediu, called on set back and * Adjust properties that relate to the medium, called on set back and
* similar. <br> * similar. <br>
* Currently: liftOffEnvelope, nextFriction. * Currently: liftOffEnvelope, nextFriction.
* *

View File

@ -407,7 +407,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
final MoveData lastMove = data.moveData.getFirst(); final MoveData lastMove = data.moveData.getFirst();
// TODO: On pistons pulling the player back: -1.15 yDistance for split move 1 (untracked position > 0.5 yDistance!). // TODO: On pistons pulling the player back: -1.15 yDistance for split move 1 (untracked position > 0.5 yDistance!).
if (TrigUtil.isSamePos(from, loc) if (TrigUtil.isSamePos(from, loc)
|| lastMove.valid && TrigUtil.isSamePos(loc, lastMove.fromX, lastMove.fromY, lastMove.fromZ) || lastMove.valid && TrigUtil.isSamePos(loc, lastMove.from.x, lastMove.from.y, lastMove.from.z)
// Could also be other envelopes (0.9 velocity upwards), too tedious to research. // Could also be other envelopes (0.9 velocity upwards), too tedious to research.
//&& data.lastYDist < -SurvivalFly.GRAVITY_MIN && data.lastYDist > -SurvivalFly.GRAVITY_MAX - SurvivalFly.GRAVITY_MIN //&& data.lastYDist < -SurvivalFly.GRAVITY_MIN && data.lastYDist > -SurvivalFly.GRAVITY_MAX - SurvivalFly.GRAVITY_MIN
) { ) {
@ -462,7 +462,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Set up data / caching. // Set up data / caching.
// TODO: Data resetting above ? // TODO: Data resetting above ?
data.noFallAssumeGround = false;
data.resetTeleported(); data.resetTeleported();
// Debug. // Debug.
if (data.debug) { if (data.debug) {
@ -664,6 +663,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
pTo.collectBlockFlags(maxYNoFall); pTo.collectBlockFlags(maxYNoFall);
} }
// Set basic properties for past move bookkeeping.
data.thisMove.setExtraProperties(pFrom, pTo);
// Hack: Add velocity for transitions between creativefly and survivalfly. // Hack: Add velocity for transitions between creativefly and survivalfly.
if (lastMove.toIsValid && lastMove.flyCheck == CheckType.MOVING_CREATIVEFLY) { if (lastMove.toIsValid && lastMove.flyCheck == CheckType.MOVING_CREATIVEFLY) {
workaroundFlyNoFlyTransition(tick, data); workaroundFlyNoFlyTransition(tick, data);
@ -681,7 +683,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
if (newTo == null) { if (newTo == null) {
// Hover. // Hover.
// TODO: Could reset for from-on-ground as well, for not too big moves. // TODO: Could reset for from-on-ground as well, for not too big moves.
if (cc.sfHoverCheck && !data.toWasReset && !pTo.isOnGround()) { if (cc.sfHoverCheck
&& !(lastMove.toIsValid && lastMove.to.extraPropertiesValid && lastMove.to.onGroundOrResetCond)
&& !pTo.isOnGround()) {
// Start counting ticks. // Start counting ticks.
hoverTicks.add(playerName); hoverTicks.add(playerName);
data.sfHoverTicks = 0; data.sfHoverTicks = 0;
@ -738,12 +742,13 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Reset jump amplifier if needed. // Reset jump amplifier if needed.
if ((checkSf || checkCf) && jumpAmplifier != data.jumpAmplifier) { if ((checkSf || checkCf) && jumpAmplifier != data.jumpAmplifier) {
if (data.noFallAssumeGround || pFrom.isOnGround() || pTo.isOnGround()) { // TODO: General cool-down for latency?
if (data.thisMove.touchedGround || !checkSf && (pFrom.isOnGround() || pTo.isOnGround())) {
// (No need to check from/to for onGround, if SurvivalFly is to be checked.)
data.jumpAmplifier = jumpAmplifier; data.jumpAmplifier = jumpAmplifier;
} }
} }
if (newTo == null) { if (newTo == null) {
// Allowed move. // Allowed move.
// Bounce effects. // Bounce effects.
@ -1007,7 +1012,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
else { else {
// TODO: Detect differing location (a teleport event would follow). // TODO: Detect differing location (a teleport event would follow).
final MoveData lastMove = mData.moveData.getFirst(); final MoveData lastMove = mData.moveData.getFirst();
if (!lastMove.toIsValid || !TrigUtil.isSamePos(to, lastMove.toX, lastMove.toY, lastMove.toZ)) { if (!lastMove.toIsValid || !TrigUtil.isSamePos(to, lastMove.to.x, lastMove.to.y, lastMove.to.z)) {
// Something odd happened. // Something odd happened.
mData.resetPositions(to); mData.resetPositions(to);
} else { } else {
@ -1066,11 +1071,15 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
final MovingData data = MovingData.getData(player); final MovingData data = MovingData.getData(player);
final Location teleported = data.getTeleported(); final Location teleported = data.getTeleported();
// Prevent further moving processing for nested events.
processingEvents.remove(player.getName());
// Invalidate first-move thing. // Invalidate first-move thing.
data.joinOrRespawn = false; data.joinOrRespawn = false; // TODO: Might conflict with 'moved wrongly' on join.
// If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no". // If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no".
Location to = event.getTo(); Location to = event.getTo();
final TeleportCause cause = event.getCause();
final Location ref; final Location ref;
final MovingConfig cc = MovingConfig.getConfig(player); final MovingConfig cc = MovingConfig.getConfig(player);
if (teleported != null && teleported.equals(to)) { if (teleported != null && teleported.equals(to)) {
@ -1090,49 +1099,33 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// TODO: This could be done on MONITOR. // TODO: This could be done on MONITOR.
data.onSetBack(teleported); data.onSetBack(teleported);
adjustLiftOffEnvelope(player, teleported, data, cc); adjustLiftOffEnvelope(player, teleported, data, cc);
if (data.debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP " + cause + " (set-back): " + to);
}
} else { } else {
// Only if it wasn't NoCheatPlus, drop data from more packets check. // Ignore if cancelled or if end point is set.
if (to != null && !event.isCancelled()) { if (to == null || event.isCancelled()) {
// Normal teleport. // Cancelled, not a set back, ignore it, basically.
// Better reset teleported (compatibility). Might have drawbacks.
data.resetTeleported();
if (data.debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP " + cause + " (already cancelled): " + to);
}
return;
}
// Detect small distance teleports. // Normal teleport.
boolean smallRange = false;
boolean cancel = false; boolean cancel = false;
// boolean pass = false; // boolean pass = false;
final double margin = 0.67;
final Location from = event.getFrom(); final Location from = event.getFrom();
if (cause == TeleportCause.ENDER_PEARL) {
final TeleportCause cause = event.getCause();
if (cause == TeleportCause.UNKNOWN) {
// Check special small range teleports (server moves players out of blocks).
if (from != null && from.getWorld().equals(to.getWorld())) {
if (TrigUtil.distance(from, to) < margin) {
smallRange = true;
}
else if (data.hasSetBack()) {
// (Removed demand for a past move to be present - remember on issues.)
final Location setBack = data.getSetBack(to);
if (TrigUtil.distance(to.getX(), to.getY(), to.getZ(), setBack.getX(), setBack.getY(), setBack.getZ()) < margin) {
smallRange = true;
}
}
// Override smallRange, if the teleport seems ok.
if (smallRange && BlockProperties.isOnGroundOrResetCond(player, to, cc.yOnGround) && BlockProperties.isPassable(to)) {
// TODO: Consider to remove the smallRange workaround and re-evaluate [Block jump on protected region into fence].
smallRange = false;
}
}
}
else if (cause == TeleportCause.ENDER_PEARL) {
if (CombinedConfig.getConfig(player).enderPearlCheck && !BlockProperties.isPassable(to)) { // || !BlockProperties.isOnGroundOrResetCond(player, to, 1.0)) { if (CombinedConfig.getConfig(player).enderPearlCheck && !BlockProperties.isPassable(to)) { // || !BlockProperties.isOnGroundOrResetCond(player, to, 1.0)) {
// Not check on-ground: Check the second throw. // Not check on-ground: Check the second throw.
// TODO: Bounding box check or onGround as replacement?
cancel = true; cancel = true;
} }
else {
// pass = true;
}
} }
else if (cause == TeleportCause.COMMAND) { else if (cause == TeleportCause.COMMAND) {
// Attempt to prevent teleporting to players inside of blocks at untracked coordinates. // Attempt to prevent teleporting to players inside of blocks at untracked coordinates.
@ -1144,19 +1137,15 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Adjust the teleport to go to the last tracked to-location of the other player. // Adjust the teleport to go to the last tracked to-location of the other player.
to = newTo; to = newTo;
event.setTo(newTo); event.setTo(newTo);
cancel = smallRange = false; cancel = false;
// TODO: Consider console, consider data.debug. // TODO: Consider console, consider data.debug.
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.TRACE_FILE, player.getName() + " correct untracked teleport destination (" + to + " corrected to " + newTo + ")."); NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.TRACE_FILE, player.getName() + " correct untracked teleport destination (" + to + " corrected to " + newTo + ").");
} }
} }
} }
// if (pass) {
// ref = to;
// }
// else
if (cancel) { if (cancel) {
// Cancel! // NCP actively prevents this teleport.
if (data.hasSetBack() && !data.hasSetBackWorldChanged(to)) { if (data.hasSetBack() && !data.hasSetBackWorldChanged(to)) {
ref = data.getSetBack(to); ref = data.getSetBack(to);
event.setTo(ref); event.setTo(ref);
@ -1168,29 +1157,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
event.setCancelled(true); event.setCancelled(true);
} }
} }
else if (smallRange) {
// Very small range teleport, keep set back etc.
ref = to;
// if (data.hasSetBack() && !data.hasSetBackWorldChanged(to)) {
// final Location setBack = data.getSetBack(from);
// Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, new Runnable() {
// @Override
// public void run() {
// if (!data.hasSetBackWorldChanged(setBack)) { // && data.isSetBack(setBack)) {
// player.sendMessage("SETBACK FROM MC DERP.");
// player.teleport(setBack, TeleportCause.PLUGIN);
// }
// }
// });
// }
final MoveData lastMove = data.moveData.getFirst();
if (lastMove.toIsValid) {
// TODO: Could keep on ground
lastMove.set(lastMove.toX, lastMove.toY, lastMove.toZ, ref.getYaw(), ref.getPitch());
} else {
data.resetPositions(ref);
}
}
else { else {
// "real" teleport // "real" teleport
ref = to; ref = to;
@ -1211,9 +1177,11 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
if (!cc.noFallTpReset) { if (!cc.noFallTpReset) {
// (Set fall distance if set to not reset.) // (Set fall distance if set to not reset.)
player.setFallDistance((float) fallDistance); player.setFallDistance((float) fallDistance);
} else if (fallDistance >= 3.0) {
data.noFallSkipAirCheck = true;
} }
} }
if (event.getCause() == TeleportCause.ENDER_PEARL) { if (cause == TeleportCause.ENDER_PEARL) {
// Prevent NoFall violations for ender-pearls. // Prevent NoFall violations for ender-pearls.
data.noFallSkipAirCheck = true; data.noFallSkipAirCheck = true;
} }
@ -1221,26 +1189,16 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
data.resetPositions(ref); data.resetPositions(ref);
} }
if (data.debug && BuildParameters.debugLevel > 0) { if (data.debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP" + (smallRange ? " (small-range)" : "") + (cancel ? " (cancelled)" : "") + ": " + to); NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP " + cause + " " + (cancel ? " (cancelled)" : "") + ": " + to);
}
}
else {
// Cancelled, not a set back, ignore it, basically.
// Better reset teleported (compatibility). Might have drawbacks.
data.resetTeleported();
if (data.debug && BuildParameters.debugLevel > 0) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP (cancelled): " + to);
}
return;
} }
} }
// Reset stuff. // Reset stuff.
Combined.resetYawRate(player, ref.getYaw(), System.currentTimeMillis(), true); // TODO: Not sure. Combined.resetYawRate(player, ref.getYaw(), System.currentTimeMillis(), true); // TODO: Not sure.
data.resetTeleported(); data.resetTeleported();
// Prevent further moving processing for nested events.
processingEvents.remove(player.getName());
} }
/** /**
@ -1466,6 +1424,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
public void onPlayerRespawn(final PlayerRespawnEvent event) { public void onPlayerRespawn(final PlayerRespawnEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
final MovingData data = MovingData.getData(player); final MovingData data = MovingData.getData(player);
// TODO: Prevent/cancel scheduled teleport (use PlayerData/task for teleport, or a sequence count).
data.clearFlyData(); data.clearFlyData();
data.resetSetBack(); // To force dataOnJoin to set it to loc. data.resetSetBack(); // To force dataOnJoin to set it to loc.
// Handle respawn like join. // Handle respawn like join.
@ -1507,7 +1466,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
data.clearFlyData(); data.clearFlyData();
data.setSetBack(loc); data.setSetBack(loc);
data.resetPositions(loc); data.resetPositions(loc);
data.joinOrRespawn = true; data.joinOrRespawn = true; // TODO: Review if to always set (!).
} else { } else {
// TODO: Check consistency/distance. // TODO: Check consistency/distance.
//final Location setBack = data.getSetBack(loc); //final Location setBack = data.getSetBack(loc);
@ -1525,10 +1484,10 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
data.vDistAcc.clear(); data.vDistAcc.clear();
final MoveInfo moveInfo = useMoveInfo(); final MoveInfo moveInfo = useMoveInfo();
moveInfo.set(player, loc, null, cc.yOnGround); moveInfo.set(player, loc, null, cc.yOnGround);
data.toWasReset = moveInfo.from.isOnGroundOrResetCond(); final MoveData lastMove = data.moveData.getFirst();
lastMove.from.setExtraProperties(moveInfo.from);
data.adjustMediumProperties(moveInfo.from); data.adjustMediumProperties(moveInfo.from);
returnMoveInfo(moveInfo); returnMoveInfo(moveInfo);
data.fromWasReset = data.toWasReset;
// Enforcing the location. // Enforcing the location.
if (cc.enforceLocation) { if (cc.enforceLocation) {
@ -1536,7 +1495,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
// Hover. // Hover.
initHover(player, data, cc, data.toWasReset); // isOnGroundOrResetCond initHover(player, data, cc, lastMove.from.onGroundOrResetCond); // isOnGroundOrResetCond
// // Bad pitch/yaw, just in case. // // Bad pitch/yaw, just in case.
// if (LocUtil.needsDirectionCorrection(useLoc.getYaw(), useLoc.getPitch())) { // if (LocUtil.needsDirectionCorrection(useLoc.getYaw(), useLoc.getPitch())) {
@ -1580,11 +1539,12 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
if (!player.isSleeping() && !player.isDead()) { if (!player.isSleeping() && !player.isDead()) {
// Check for missed moves. // Check for missed moves.
// TODO: Force-load chunks [log if (!)] ?
// TODO: Consider to catch all, at least (debug-) logging-wise. // TODO: Consider to catch all, at least (debug-) logging-wise.
if (!BlockProperties.isPassable(loc)) { if (!BlockProperties.isPassable(loc)) {
final MoveData lastMove = data.moveData.getFirst(); final MoveData lastMove = data.moveData.getFirst();
if (lastMove.toIsValid) { if (lastMove.toIsValid) {
final Location refLoc = new Location(loc.getWorld(), lastMove.toX, lastMove.toY, lastMove.toZ); final Location refLoc = new Location(loc.getWorld(), lastMove.to.x, lastMove.to.y, lastMove.to.z);
final double d = refLoc.distanceSquared(loc); final double d = refLoc.distanceSquared(loc);
if (d > 0.0) { if (d > 0.0) {
// TODO: Consider to always set back here. Might skip on big distances. // TODO: Consider to always set back here. Might skip on big distances.
@ -1705,7 +1665,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
if (data.vehicleConsistency != MoveConsistency.INCONSISTENT) { if (data.vehicleConsistency != MoveConsistency.INCONSISTENT) {
final MoveData lastMove = data.moveData.getFirst(); final MoveData lastMove = data.moveData.getFirst();
if (lastMove.toIsValid) { if (lastMove.toIsValid) {
final Location oldLoc = new Location(pLoc.getWorld(), lastMove.toX, lastMove.toY, lastMove.toZ); final Location oldLoc = new Location(pLoc.getWorld(), lastMove.to.x, lastMove.to.y, lastMove.to.z);
if (MoveConsistency.getConsistency(oldLoc, null, pLoc) != MoveConsistency.INCONSISTENT) { if (MoveConsistency.getConsistency(oldLoc, null, pLoc) != MoveConsistency.INCONSISTENT) {
loc = oldLoc; loc = oldLoc;
} }
@ -1849,7 +1809,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
private Location enforceLocation(final Player player, final Location loc, final MovingData data) { private Location enforceLocation(final Player player, final Location loc, final MovingData data) {
final MoveData lastMove = data.moveData.getFirst(); final MoveData lastMove = data.moveData.getFirst();
if (lastMove.toIsValid && TrigUtil.distanceSquared(lastMove.toX, lastMove.toY, lastMove.toZ, loc.getX(), loc.getY(), loc.getZ()) > 1.0 / 256.0) { if (lastMove.toIsValid && TrigUtil.distanceSquared(lastMove.to.x, lastMove.to.y, lastMove.to.z, loc.getX(), loc.getY(), loc.getZ()) > 1.0 / 256.0) {
// Teleport back. // Teleport back.
// TODO: Add history / alert? // TODO: Add history / alert?
//player.sendMessage(ChatColor.RED + "NCP: enforce location !"); // TODO: DEBUG - REMOVE. //player.sendMessage(ChatColor.RED + "NCP: enforce location !"); // TODO: DEBUG - REMOVE.
@ -1858,7 +1818,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Could use a flexible set-back policy (switch to in-air on login). // Could use a flexible set-back policy (switch to in-air on login).
return data.getSetBack(loc); return data.getSetBack(loc);
} else { } else {
return new Location(player.getWorld(), lastMove.toX, lastMove.toY, lastMove.toZ, loc.getYaw(), loc.getPitch()); return new Location(player.getWorld(), lastMove.to.x, lastMove.to.y, lastMove.to.z, loc.getYaw(), loc.getPitch());
} }
} else { } else {
return null; return null;

View File

@ -10,6 +10,8 @@ import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import fr.neatmonster.nocheatplus.NCPAPIProvider; import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.moving.model.LocationData;
import fr.neatmonster.nocheatplus.checks.moving.model.MoveData;
import fr.neatmonster.nocheatplus.compat.BridgeHealth; import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager; import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.logging.Streams; import fr.neatmonster.nocheatplus.logging.Streams;
@ -122,10 +124,14 @@ public class NoFall extends Check {
* @param to * @param to
* the to * the to
*/ */
public void check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc) { public void check(final Player player, final PlayerLocation pFrom, final PlayerLocation pTo, final MovingData data, final MovingConfig cc) {
final double fromY = from.getY(); final MoveData thisMove = data.thisMove;
final double toY = to.getY(); final LocationData from = thisMove.from;
final LocationData to = thisMove.to;
final double fromY = from.y;
final double toY = to.y;
final double yDiff = toY - fromY; final double yDiff = toY - fromY;
@ -133,21 +139,23 @@ public class NoFall extends Check {
// Reset-cond is not touched by yOnGround. // Reset-cond is not touched by yOnGround.
// TODO: Distinguish water depth vs. fall distance ? // TODO: Distinguish water depth vs. fall distance ?
final boolean fromReset = from.isResetCond(); final boolean fromReset = from.resetCond;
final boolean toReset = to.isResetCond(); final boolean toReset = to.resetCond;
final boolean fromOnGround, toOnGround;
// Adapt yOnGround if necessary (sf uses another setting). // Adapt yOnGround if necessary (sf uses another setting).
if (yDiff < 0 && cc.yOnGround < cc.noFallyOnGround) { if (yDiff < 0 && cc.yOnGround < cc.noFallyOnGround) {
// In fact this is somewhat heuristic, but it seems to work well. // In fact this is somewhat heuristic, but it seems to work well.
// Missing on-ground seems to happen with running down pyramids rather. // Missing on-ground seems to happen with running down pyramids rather.
// TODO: Should be obsolete. // TODO: Should be obsolete.
adjustYonGround(from, to , cc.noFallyOnGround); adjustYonGround(pFrom, pTo , cc.noFallyOnGround);
fromOnGround = pFrom.isOnGround();
toOnGround = pTo.isOnGround();
} else {
fromOnGround = from.onGround;
toOnGround = to.onGround;
} }
final boolean fromOnGround = from.isOnGround();
final boolean toOnGround = to.isOnGround();
// TODO: early returns (...) // TODO: early returns (...)
final double minY = Math.min(fromY, toY); final double minY = Math.min(fromY, toY);
@ -155,8 +163,12 @@ public class NoFall extends Check {
if (fromReset) { if (fromReset) {
// Just reset. // Just reset.
data.clearNoFallData(); data.clearNoFallData();
// Ensure very big/strange moves don't yield violations.
if (toY - fromY <= -3.0) {
data.noFallSkipAirCheck = true;
} }
else if (fromOnGround || data.noFallAssumeGround) { }
else if (fromOnGround || !toOnGround && thisMove.touchedGround) {
// Check if to deal damage (fall back damage check). // Check if to deal damage (fall back damage check).
if (cc.noFallDealDamage) { if (cc.noFallDealDamage) {
handleOnGround(player, minY, true, data, cc); handleOnGround(player, minY, true, data, cc);
@ -164,6 +176,10 @@ public class NoFall extends Check {
else { else {
adjustFallDistance(player, minY, true, data, cc); adjustFallDistance(player, minY, true, data, cc);
} }
// Ensure very big/strange moves don't yield violations.
if (toY - fromY <= -3.0) {
data.noFallSkipAirCheck = true;
}
} }
else if (toReset) { else if (toReset) {
// Just reset. // Just reset.
@ -199,7 +215,7 @@ public class NoFall extends Check {
if (!toReset && !toOnGround && yDiff < 0) { if (!toReset && !toOnGround && yDiff < 0) {
data.noFallFallDistance -= yDiff; data.noFallFallDistance -= yDiff;
} }
else if (cc.noFallAntiCriticals && (toReset || toOnGround || (fromReset || fromOnGround || data.noFallAssumeGround) && yDiff >= 0)) { else if (cc.noFallAntiCriticals && (toReset || toOnGround || (fromReset || fromOnGround || thisMove.touchedGround) && yDiff >= 0)) {
final double max = Math.max(data.noFallFallDistance, mcFallDistance); final double max = Math.max(data.noFallFallDistance, mcFallDistance);
if (max > 0.0 && max < 0.75) { // (Ensure this does not conflict with deal-damage set to false.) if (max > 0.0 && max < 0.75) { // (Ensure this does not conflict with deal-damage set to false.)
if (data.debug) { if (data.debug) {

View File

@ -240,8 +240,9 @@ public class SurvivalFly extends Check {
// Note: if not setting resetFrom, other places have to check assumeGround... // Note: if not setting resetFrom, other places have to check assumeGround...
} }
if (data.noFallAssumeGround) { if (thisMove.touchedGround && !thisMove.from.onGround && !thisMove.to.onGround) {
// Lost ground workaround has just been applied, check resetting of the dirty flag. // Lost ground workaround has just been applied, check resetting of the dirty flag.
// TODO: Always/never reset with any ground touched?
data.resetVelocityJumpPhase(); data.resetVelocityJumpPhase();
} }
@ -487,7 +488,7 @@ public class SurvivalFly extends Check {
else if (from.isInWeb()) { else if (from.isInWeb()) {
data.liftOffEnvelope = LiftOffEnvelope.NO_JUMP; // TODO: Test. data.liftOffEnvelope = LiftOffEnvelope.NO_JUMP; // TODO: Test.
} }
else if (resetFrom || data.noFallAssumeGround) { else if (resetFrom || thisMove.touchedGround) {
// TODO: Where exactly to put noFallAssumeGround ? // TODO: Where exactly to put noFallAssumeGround ?
data.liftOffEnvelope = LiftOffEnvelope.NORMAL; data.liftOffEnvelope = LiftOffEnvelope.NORMAL;
} }
@ -552,8 +553,6 @@ public class SurvivalFly extends Check {
data.clearActiveHorVel(); data.clearActiveHorVel();
} }
// Adjust data. // Adjust data.
data.toWasReset = resetTo || data.noFallAssumeGround;
data.fromWasReset = resetFrom || data.noFallAssumeGround;
data.lastFrictionHorizontal = data.nextFrictionHorizontal; data.lastFrictionHorizontal = data.nextFrictionHorizontal;
data.lastFrictionVertical = data.nextFrictionVertical; data.lastFrictionVertical = data.nextFrictionVertical;
if (yDistance == 0.0 && hDistance < 0.125) { if (yDistance == 0.0 && hDistance < 0.125) {
@ -883,23 +882,27 @@ public class SurvivalFly extends Check {
vAllowedDistance = lastMove.yDistance * data.lastFrictionVertical - GRAVITY_MIN; // Upper bound. vAllowedDistance = lastMove.yDistance * data.lastFrictionVertical - GRAVITY_MIN; // Upper bound.
strictVdistRel = true; strictVdistRel = true;
} }
else if (resetFrom) { else if (resetFrom || data.thisMove.touchedGroundWorkaround) {
// TODO: More concise conditions? Some workaround may allow more. // TODO: More concise conditions? Some workaround may allow more.
if (toOnGround) { // TODO: Might have to use max if resetto. if (toOnGround) {
vAllowedDistance = cc.sfStepHeight; vAllowedDistance = Math.max(cc.sfStepHeight, maxJumpGain + jumpGainMargin);
} else { }
else {
// Code duplication with the absolute limit below. // Code duplication with the absolute limit below.
if (yDistance < 0.0 || yDistance > cc.sfStepHeight || !tags.contains("lostground_couldstep")) { if (yDistance < 0.0 || yDistance > cc.sfStepHeight || !tags.contains("lostground_couldstep")) {
vAllowedDistance = maxJumpGain + jumpGainMargin; vAllowedDistance = maxJumpGain + jumpGainMargin;
} else { }
else {
// lostground_couldstep // lostground_couldstep
// TODO: Other conditions / envelopes?
vAllowedDistance = yDistance; vAllowedDistance = yDistance;
} }
} }
strictVdistRel = false; strictVdistRel = false;
} }
else if (lastMove.toIsValid) { else if (lastMove.toIsValid) {
if (lastMove.yDistance >= -Math.max(GRAVITY_MAX / 2.0, 1.3 * Math.abs(yDistance)) && lastMove.yDistance <= 0.0 && data.fromWasReset) { if (lastMove.yDistance >= -Math.max(GRAVITY_MAX / 2.0, 1.3 * Math.abs(yDistance)) && lastMove.yDistance <= 0.0
&& (lastMove.touchedGround || lastMove.to.extraPropertiesValid && lastMove.to.resetCond)) {
if (resetTo) { // TODO: Might have to use max if resetto. if (resetTo) { // TODO: Might have to use max if resetto.
vAllowedDistance = cc.sfStepHeight; vAllowedDistance = cc.sfStepHeight;
} }
@ -908,12 +911,28 @@ public class SurvivalFly extends Check {
vAllowedDistance = maxJumpGain + jumpGainMargin; vAllowedDistance = maxJumpGain + jumpGainMargin;
} }
strictVdistRel = false; strictVdistRel = false;
} else { }
else {
// Friction.
// TODO: data.lastFrictionVertical (see above). // TODO: data.lastFrictionVertical (see above).
vAllowedDistance = lastMove.yDistance * data.lastFrictionVertical - GRAVITY_MIN; // Upper bound. vAllowedDistance = lastMove.yDistance * data.lastFrictionVertical - GRAVITY_MIN; // Upper bound.
strictVdistRel = true; strictVdistRel = true;
} }
}
else if (lastMove.valid) {
// Teleport/join/respawn.
if (data.thisMove.from.onGround) {
vAllowedDistance = maxJumpGain + jumpGainMargin;
if (data.thisMove.to.onGround) {
vAllowedDistance = Math.max(cc.sfStepHeight, vAllowedDistance);
}
} else { } else {
// Allow 0 y-distance once.
vAllowedDistance = 0.0;
}
strictVdistRel = false;
}
else {
// Problematic point (thinking of "ncp remove ..."). // Problematic point (thinking of "ncp remove ...").
// Ensure: data.resetLastDistances() must be used on teleport/join etc. // Ensure: data.resetLastDistances() must be used on teleport/join etc.
vAllowedDistance = yDistance; vAllowedDistance = yDistance;
@ -932,7 +951,7 @@ public class SurvivalFly extends Check {
} }
else if (yDistDiffEx > 0.0) { // Upper bound violation. else if (yDistDiffEx > 0.0) { // Upper bound violation.
// && (yDistance > 0.0 || (!resetTo && !data.noFallAssumeGround)) // && (yDistance > 0.0 || (!resetTo && !data.noFallAssumeGround))
if (yDistance <= 0.0 && (resetTo || data.noFallAssumeGround)) { if (yDistance <= 0.0 && (resetTo || data.thisMove.touchedGround)) {
// Allow falling shorter than expected, if onto ground. // Allow falling shorter than expected, if onto ground.
// Note resetFrom should usually mean that allowed dist is > 0 ? // Note resetFrom should usually mean that allowed dist is > 0 ?
} }
@ -955,7 +974,8 @@ public class SurvivalFly extends Check {
// Special jump (water/edges/assume-ground), too small decrease. // Special jump (water/edges/assume-ground), too small decrease.
} }
else if (yDistDiffEx < GRAVITY_MIN && data.sfJumpPhase == 1 else if (yDistDiffEx < GRAVITY_MIN && data.sfJumpPhase == 1
&& data.liftOffEnvelope != LiftOffEnvelope.NORMAL && data.fromWasReset && data.liftOffEnvelope != LiftOffEnvelope.NORMAL
&& lastMove.from.extraPropertiesValid && lastMove.from.inLiquid
&& lastMove.yDistance < -GRAVITY_ODD / 2.0 && lastMove.yDistance > -GRAVITY_MAX - GRAVITY_SPAN && lastMove.yDistance < -GRAVITY_ODD / 2.0 && lastMove.yDistance > -GRAVITY_MAX - GRAVITY_SPAN
&& yDistance < lastMove.yDistance - 0.001) { && yDistance < lastMove.yDistance - 0.001) {
// Odd decrease with water. // Odd decrease with water.
@ -1012,11 +1032,11 @@ public class SurvivalFly extends Check {
if (yDistance < -3.0 && lastMove.yDistance < -3.0 && Math.abs(yDistDiffEx) < 5.0 * GRAVITY_MAX) { if (yDistance < -3.0 && lastMove.yDistance < -3.0 && Math.abs(yDistDiffEx) < 5.0 * 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%).
} }
else if (resetTo && (yDistDiffEx > -GRAVITY_SPAN || !fromOnGround && !data.noFallAssumeGround && yDistChange >= 0.0)) { else if (resetTo && (yDistDiffEx > -GRAVITY_SPAN || !fromOnGround && !data.thisMove.touchedGround && yDistChange >= 0.0)) {
// Moving onto ground allows a shorter move. // Moving onto ground allows a shorter move.
// TODO: Any lost-ground cases? // TODO: Any lost-ground cases?
} }
else if (yDistance > lastMove.yDistance - GRAVITY_MAX - GRAVITY_SPAN && (resetTo || data.noFallAssumeGround)) { else if (yDistance > lastMove.yDistance - GRAVITY_MAX - GRAVITY_SPAN && (resetTo || data.thisMove.touchedGround)) {
// Mirrored case for yDistance > yAllowedDistance, hitting ground. // Mirrored case for yDistance > yAllowedDistance, hitting ground.
// TODO: Needs more efficient structure. // TODO: Needs more efficient structure.
} }
@ -1025,8 +1045,9 @@ public class SurvivalFly extends Check {
// TODO: Margins !? // TODO: Margins !?
} }
else if (data.liftOffEnvelope == LiftOffEnvelope.LIMIT_LIQUID else if (data.liftOffEnvelope == LiftOffEnvelope.LIMIT_LIQUID
&& data.sfJumpPhase == 1 && data.sfJumpPhase == 1 && lastMove.toIsValid
&& data.fromWasReset && !data.toWasReset && !resetFrom && resetTo // TODO: There might be other cases (possibly wrong bounding box). && lastMove.from.inLiquid && !(lastMove.to.extraPropertiesValid && lastMove.to.inLiquid)
&& !resetFrom && resetTo // TODO: There might be other cases (possibly wrong bounding box).
&& lastMove.yDistance > 0.0 && lastMove.yDistance < 0.5 * GRAVITY_ODD // TODO: There might be cases with < 0.2 ! && lastMove.yDistance > 0.0 && lastMove.yDistance < 0.5 * GRAVITY_ODD // TODO: There might be cases with < 0.2 !
&& yDistance < 0.0 && Math.abs(Math.abs(yDistance) - lastMove.yDistance) < GRAVITY_SPAN / 2.0 && yDistance < 0.0 && Math.abs(Math.abs(yDistance) - lastMove.yDistance) < GRAVITY_SPAN / 2.0
) { ) {
@ -1058,33 +1079,38 @@ public class SurvivalFly extends Check {
} }
// Absolute y-distance to set back. // Absolute y-distance to set back.
if (yDistance > 0.0 && !data.isVelocityJumpPhase() if (yDistance > 0.0 && !data.isVelocityJumpPhase()) {
&& !((fromOnGround || data.noFallAssumeGround) && toOnGround && yDistance <= cc.sfStepHeight)) {
// TODO: Maintain a value in data, adjusting to velocity? // TODO: Maintain a value in data, adjusting to velocity?
// TODO: LIMIT_JUMP // TODO: LIMIT_JUMP
final double vAllowedAbsoluteDistance = data.liftOffEnvelope.getMaxJumpHeight(data.jumpAmplifier); final double vAllowedAbsoluteDistance = data.liftOffEnvelope.getMaxJumpHeight(data.jumpAmplifier);
final double totalVDistViolation = to.getY() - data.getSetBackY() - vAllowedAbsoluteDistance; final double totalVDistViolation = to.getY() - data.getSetBackY() - vAllowedAbsoluteDistance;
if (totalVDistViolation > 0.0) { if (totalVDistViolation > 0.0) {
// Skip if the player could step up. // Skip actually stepping up.
if (yDistance > cc.sfStepHeight || !tags.contains("lostground_couldstep")) { if ((fromOnGround || data.thisMove.touchedGroundWorkaround || lastMove.touchedGround)
if (data.getOrUseVerticalVelocity(yDistance) == null) { && toOnGround && yDistance <= cc.sfStepHeight) {
// Last minute special case. // Ignore: Legitimate step.
}
// Skip if the player could step up by lostground_couldstep.
else if (yDistance <= cc.sfStepHeight && data.thisMove.touchedGroundWorkaround && tags.contains("lostground_couldstep")) {
// Ignore: Envelope already checked.
}
// Teleport to in-air (PaperSpigot 1.7.10). // Teleport to in-air (PaperSpigot 1.7.10).
if (!lastMove.toIsValid && data.sfJumpPhase == 0 && mightBeMultipleMoves else if (!lastMove.toIsValid && data.sfJumpPhase == 0 && mightBeMultipleMoves
&& Math.abs(totalVDistViolation) < 0.01 && yDistance > 0.0 && yDistance < 0.01 && Math.abs(totalVDistViolation) < 0.01 && yDistance > 0.0 && yDistance < 0.01
&& !resetFrom && !resetTo && !data.noFallAssumeGround && !resetFrom && !resetTo && !data.thisMove.touchedGround
) { ) {
// TODO: Confine to PaperSpigot? // TODO: Confine to PaperSpigot?
// TODO: Confine to from at block level (offest 0)? // TODO: Confine to from at block level (offset 0)?
tags.add("skip_paper"); tags.add("skip_paper");
} else { }
// Attempt to use velocity.
else if (data.getOrUseVerticalVelocity(yDistance) == null) {
// Violation.
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, totalVDistViolation); vDistanceAboveLimit = Math.max(vDistanceAboveLimit, totalVDistViolation);
tags.add("vdistsb"); tags.add("vdistsb");
} }
} }
} }
}
}
if (data.sfLowJump) { if (data.sfLowJump) {
tags.add("lowjump"); tags.add("lowjump");
@ -1099,11 +1125,11 @@ public class SurvivalFly extends Check {
// Simple-step blocker. // Simple-step blocker.
// TODO: Complex step blocker: distance to set-back + low jump + accounting info // TODO: Complex step blocker: distance to set-back + low jump + accounting info
if ((resetFrom || data.noFallAssumeGround) && resetTo && vDistanceAboveLimit <= 0D && if ((resetFrom || lastMove.touchedGround) && resetTo && vDistanceAboveLimit <= 0D &&
yDistance > cc.sfStepHeight && yDistance > maxJumpGain + 0.1) { yDistance > cc.sfStepHeight && yDistance > maxJumpGain + 0.1) {
boolean step = true; boolean step = true;
// Exclude a lost-ground case. // Exclude a lost-ground case.
if (data.noFallAssumeGround && lastMove.toIsValid && lastMove.yDistance <= 0.0 && yDistance > 0.0 && if (data.thisMove.touchedGroundWorkaround && lastMove.toIsValid && lastMove.yDistance <= 0.0 && yDistance > 0.0 &&
yDistance + Math.abs(lastMove.yDistance) <= 2.0 * (maxJumpGain + 0.1)) { yDistance + Math.abs(lastMove.yDistance) <= 2.0 * (maxJumpGain + 0.1)) {
step = false; step = false;
} }
@ -1209,6 +1235,7 @@ public class SurvivalFly extends Check {
} }
// TODO: data.lastFrictionVertical (see vDistAir). // TODO: data.lastFrictionVertical (see vDistAir).
final double frictDist = lastYDist * lastFrictionVertical - GRAVITY_MIN; final double frictDist = lastYDist * lastFrictionVertical - GRAVITY_MIN;
// TODO: Extra amount: distinguish pos/neg?
return yDistance <= frictDist + extraGravity && yDistance > frictDist - GRAVITY_SPAN - extraGravity; return yDistance <= frictDist + extraGravity && yDistance > frictDist - GRAVITY_SPAN - extraGravity;
} }
@ -1308,14 +1335,18 @@ public class SurvivalFly extends Check {
&& yDistance < GRAVITY_ODD && yDistance > -2.0 * GRAVITY_MAX - GRAVITY_ODD / 2.0 && yDistance < GRAVITY_ODD && yDistance > -2.0 * GRAVITY_MAX - GRAVITY_ODD / 2.0
// 1: Head is obstructed. // 1: Head is obstructed.
// TODO: Cover this in a more generic way elsewhere (<= friction envelope + obstructed). // TODO: Cover this in a more generic way elsewhere (<= friction envelope + obstructed).
|| lastMove.yDistance >= 0.0 && (from.isHeadObstructed(from.getyOnGround()) || data.fromWasReset && from.isHeadObstructed()) || lastMove.yDistance >= 0.0 && yDistance < GRAVITY_ODD
&& (data.thisMove.headObstructed || lastMove.headObstructed)
// 1: Break the block underneath. // 1: Break the block underneath.
|| lastMove.yDistance < 0.0 && data.toWasReset // TODO: Also assumeGround? Should have more precise flags. || lastMove.yDistance < 0.0 && lastMove.to.extraPropertiesValid && lastMove.to.onGround
&& yDistance >= -GRAVITY_MAX - GRAVITY_SPAN && yDistance <= GRAVITY_MIN && yDistance >= -GRAVITY_MAX - GRAVITY_SPAN && yDistance <= GRAVITY_MIN
// 1: Slope with slimes (also near ground without velocityJumpPhase, rather lowjump but not always). // 1: Slope with slimes (also near ground without velocityJumpPhase, rather lowjump but not always).
|| lastMove.yDistance < -GRAVITY_MAX && yDistChange < - GRAVITY_ODD / 2.0 && yDistChange > -GRAVITY_MIN || lastMove.yDistance < -GRAVITY_MAX && yDistChange < - GRAVITY_ODD / 2.0 && yDistChange > -GRAVITY_MIN
// 1: Near ground (slime block). // 1: Near ground (slime block).
|| lastMove.yDistance == 0.0 && yDistance < -GRAVITY_ODD / 2.5 && yDistance > -GRAVITY_MIN && to.isOnGround(GRAVITY_MIN) || lastMove.yDistance == 0.0 && yDistance < -GRAVITY_ODD / 2.5 && yDistance > -GRAVITY_MIN && to.isOnGround(GRAVITY_MIN)
// 1: Start to fall after touching ground somehow (possibly too slowly).
|| (lastMove.touchedGround || lastMove.to.resetCond) && lastMove.yDistance <= GRAVITY_MIN && lastMove.yDistance >= - GRAVITY_MAX
&& yDistance < lastMove.yDistance - GRAVITY_SPAN && yDistance < GRAVITY_ODD && yDistance > lastMove.yDistance - GRAVITY_MAX
) )
// 0: With velocity. // 0: With velocity.
|| data.isVelocityJumpPhase() || data.isVelocityJumpPhase()
@ -1353,7 +1384,8 @@ public class SurvivalFly extends Check {
&& yDistChange < -GRAVITY_SPAN && yDistChange < -GRAVITY_SPAN
// 0: Another near 0 yDistance case. // 0: Another near 0 yDistance case.
// TODO: Inaugurate into some more generic envelope. // TODO: Inaugurate into some more generic envelope.
|| lastMove.yDistance > -GRAVITY_MAX && lastMove.yDistance < GRAVITY_MIN && !data.toWasReset || lastMove.yDistance > -GRAVITY_MAX && lastMove.yDistance < GRAVITY_MIN
&& !(lastMove.touchedGround || lastMove.to.extraPropertiesValid && lastMove.to.onGroundOrResetCond)
&& yDistance < lastMove.yDistance - GRAVITY_MIN / 2.0 && yDistance > lastMove.yDistance - GRAVITY_MAX - 0.5 * GRAVITY_MIN && yDistance < lastMove.yDistance - GRAVITY_MIN / 2.0 && yDistance > lastMove.yDistance - GRAVITY_MAX - 0.5 * GRAVITY_MIN
// 0: Reduced jumping envelope. // 0: Reduced jumping envelope.
|| data.liftOffEnvelope != LiftOffEnvelope.NORMAL || data.liftOffEnvelope != LiftOffEnvelope.NORMAL
@ -1483,12 +1515,12 @@ public class SurvivalFly extends Check {
// TODO: Clear active vertical velocity here ? // TODO: Clear active vertical velocity here ?
// TODO: Demand consuming queued velocity for valid change (!). // TODO: Demand consuming queued velocity for valid change (!).
// Increase // Increase
if (data.toWasReset) { if (lastMove.touchedGround || lastMove.to.extraPropertiesValid && lastMove.to.resetCond) {
tags.add("ychinc"); tags.add("ychinc");
} }
else { else {
// Moving upwards after falling without having touched the ground. // Moving upwards after falling without having touched the ground.
if (data.bunnyhopDelay < 9 && !(data.fromWasReset && lastMove.yDistance == 0D) && data.getOrUseVerticalVelocity(yDistance) == null) { if (data.bunnyhopDelay < 9 && !((lastMove.touchedGround || lastMove.from.onGroundOrResetCond) && lastMove.yDistance == 0D) && data.getOrUseVerticalVelocity(yDistance) == null) {
// TODO: adjust limit for bunny-hop. // TODO: adjust limit for bunny-hop.
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance)); vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance));
tags.add("ychincfly"); tags.add("ychincfly");
@ -1681,9 +1713,9 @@ public class SurvivalFly extends Check {
} }
// 2x horizontal speed increase detection. // 2x horizontal speed increase detection.
// TODO: Walk speed (static or not) is not a good reference, switch to need normal/base speed instead.
if (!allowHop && hDistance - lastMove.hDistance >= hDistanceBaseRef * 0.5 && hopTime == 1) { if (!allowHop && hDistance - lastMove.hDistance >= hDistanceBaseRef * 0.5 && hopTime == 1) {
if (lastMove.yDistance >= -GRAVITY_MAX / 2.0 && lastMove.yDistance <= 0.0 && (data.fromWasReset || data.toWasReset) && yDistance >= 0.4) { if (lastMove.yDistance >= -GRAVITY_MAX / 2.0 && lastMove.yDistance <= 0.0 && yDistance >= 0.4
&& lastMove.touchedGround) {
// TODO: Confine to increasing set back y ? // TODO: Confine to increasing set back y ?
tags.add(DOUBLE_BUNNY); tags.add(DOUBLE_BUNNY);
allowHop = double_bunny = true; allowHop = double_bunny = true;
@ -1691,7 +1723,7 @@ public class SurvivalFly extends Check {
} }
// Allow hop for special cases. // Allow hop for special cases.
if (!allowHop && (from.isOnGround() || data.noFallAssumeGround)) { if (!allowHop && (from.isOnGround() || thisMove.touchedGroundWorkaround)) {
// TODO: Better reset delay in this case ? // TODO: Better reset delay in this case ?
if (data.bunnyhopDelay <= 6 || yDistance >= 0.0 && data.thisMove.headObstructed) { // || to.isHeadObstructed()) { if (data.bunnyhopDelay <= 6 || yDistance >= 0.0 && data.thisMove.headObstructed) { // || to.isHeadObstructed()) {
// TODO: headObstructed: check always and set a flag in data + consider regain buffer? // TODO: headObstructed: check always and set a flag in data + consider regain buffer?
@ -1724,11 +1756,19 @@ public class SurvivalFly extends Check {
|| data.thisMove.headObstructed || lastMove.toIsValid && lastMove.headObstructed && lastMove.yDistance <= 0.0 || data.thisMove.headObstructed || lastMove.toIsValid && lastMove.headObstructed && lastMove.yDistance <= 0.0
// 1: Hop without y distance increase at moderate h-speed. // 1: Hop without y distance increase at moderate h-speed.
// TODO: 2nd below: demand next move to jump. Relate to stored past moves. // TODO: 2nd below: demand next move to jump. Relate to stored past moves.
|| (cc.sfGroundHop || yDistance == 0.0 && !data.fromWasReset) || (cc.sfGroundHop || yDistance == 0.0 && !lastMove.touchedGroundWorkaround && !lastMove.from.onGround)
&& hDistanceBaseRef > 0.0 && hDistance / hDistanceBaseRef < 1.35 && hDistanceBaseRef > 0.0 && hDistance / hDistanceBaseRef < 1.35
) )
// 0: Bad auto indent. // 0: Ground + jump phase conditions.
&& (data.sfJumpPhase == 0 && from.isOnGround() || data.sfJumpPhase <= 1 && (data.noFallAssumeGround || data.fromWasReset) || double_bunny) && (
// 1: Ordinary/obvious lift-off.
data.sfJumpPhase == 0 && from.isOnGround()
// 1: Touched ground somehow.
|| data.sfJumpPhase <= 1 && (thisMove.touchedGroundWorkaround ||
lastMove.touchedGround && !lastMove.bunnyHop)
// 1: Double bunny.
|| double_bunny)
// 0: Don't allow bunny to run out of liquid.
&& !from.isResetCond() && !to.isResetCond() // TODO: !to.isResetCond() should be reviewed. && !from.isResetCond() && !to.isResetCond() // TODO: !to.isResetCond() should be reviewed.
) { ) {
// TODO: Jump effect might allow more strictness. // TODO: Jump effect might allow more strictness.
@ -1736,6 +1776,7 @@ public class SurvivalFly extends Check {
// TODO: Speed effect affects hDistanceAboveLimit? // TODO: Speed effect affects hDistanceAboveLimit?
data.bunnyhopDelay = bunnyHopMax; data.bunnyhopDelay = bunnyHopMax;
hDistanceAboveLimit = 0D; hDistanceAboveLimit = 0D;
thisMove.bunnyHop = true;
tags.add("bunnyhop"); tags.add("bunnyhop");
} }
else { else {
@ -1850,7 +1891,7 @@ public class SurvivalFly extends Check {
else if (lastMove.yDistance >= GRAVITY_MAX / 2.0 && lastMove.yDistance <= GRAVITY_MAX + GRAVITY_MIN / 2.0 else if (lastMove.yDistance >= GRAVITY_MAX / 2.0 && lastMove.yDistance <= GRAVITY_MAX + GRAVITY_MIN / 2.0
&& yDistance < 0.0 && yDistance > -2.0 * GRAVITY_MAX - GRAVITY_MIN / 2.0 && yDistance < 0.0 && yDistance > -2.0 * GRAVITY_MAX - GRAVITY_MIN / 2.0
&& data.isVelocityJumpPhase() && to.isInLiquid() // TODO: Might skip the liquid check, though. && data.isVelocityJumpPhase() && to.isInLiquid() // TODO: Might skip the liquid check, though.
&& data.fromWasReset && data.toWasReset && lastMove.from.inLiquid && lastMove.to.extraPropertiesValid && lastMove.to.inLiquid // TODO: in water only?
) { ) {
return new double[]{yDistance, 0.0}; return new double[]{yDistance, 0.0};
} }
@ -1931,7 +1972,7 @@ public class SurvivalFly extends Check {
} }
} }
if (yDistance > 0) { if (yDistance > 0) {
if (!fromOnGround && !toOnGround && !data.noFallAssumeGround) { if (!data.thisMove.touchedGround) {
// Check if player may climb up. // Check if player may climb up.
// (This does exclude ladders.) // (This does exclude ladders.)
if (!from.canClimbUp(jumpHeight)) { if (!from.canClimbUp(jumpHeight)) {
@ -2110,7 +2151,7 @@ public class SurvivalFly extends Check {
* @return * @return
*/ */
private final boolean lostGroundEdgeAsc(final Player player, final BlockCache blockCache, final World world, final double x1, final double y1, final double z1, final double width, final double yOnGround, final MoveData lastMove, final MovingData data, final String tag) { private final boolean lostGroundEdgeAsc(final Player player, final BlockCache blockCache, final World world, final double x1, final double y1, final double z1, final double width, final double yOnGround, final MoveData lastMove, final MovingData data, final String tag) {
return lostGroundEdgeAsc(player, blockCache, world, x1, y1, z1, lastMove.fromX, lastMove.fromY, lastMove.fromZ, lastMove.hDistance, width, yOnGround, data, tag); return lostGroundEdgeAsc(player, blockCache, world, x1, y1, z1, lastMove.from.x, lastMove.from.y, lastMove.from.z, lastMove.hDistance, width, yOnGround, data, tag);
} }
private final boolean lostGroundEdgeAsc(final Player player, final BlockCache blockCache, final World world, final double x1, final double y1, final double z1, double x2, final double y2, double z2, final double hDistance2, final double width, final double yOnGround, final MovingData data, final String tag) { private final boolean lostGroundEdgeAsc(final Player player, final BlockCache blockCache, final World world, final double x1, final double y1, final double z1, double x2, final double y2, double z2, final double hDistance2, final double width, final double yOnGround, final MovingData data, final String tag) {
@ -2312,7 +2353,8 @@ public class SurvivalFly extends Check {
data.jumpAmplifier = getJumpAmplifier(player); data.jumpAmplifier = getJumpAmplifier(player);
data.clearAccounting(); data.clearAccounting();
// Tell NoFall that we assume the player to have been on ground somehow. // Tell NoFall that we assume the player to have been on ground somehow.
data.noFallAssumeGround = true; data.thisMove.touchedGround = true;
data.thisMove.touchedGroundWorkaround = true;
tags.add("lostground_" + tag); tags.add("lostground_" + tag);
return true; return true;
} }
@ -2470,7 +2512,7 @@ public class SurvivalFly extends Check {
final String hBuf = (data.sfHorizontalBuffer < 1.0 ? ((" hbuf=" + StringUtil.fdec3.format(data.sfHorizontalBuffer))) : ""); final String hBuf = (data.sfHorizontalBuffer < 1.0 ? ((" hbuf=" + StringUtil.fdec3.format(data.sfHorizontalBuffer))) : "");
final String lostSprint = (data.lostSprintCount > 0 ? (" lostSprint=" + data.lostSprintCount) : ""); final String lostSprint = (data.lostSprintCount > 0 ? (" lostSprint=" + data.lostSprintCount) : "");
final String hVelUsed = hFreedom > 0 ? " hVelUsed=" + StringUtil.fdec3.format(hFreedom) : ""; final String hVelUsed = hFreedom > 0 ? " hVelUsed=" + StringUtil.fdec3.format(hFreedom) : "";
builder.append(player.getName() + " SurvivalFly\nground: " + (data.noFallAssumeGround ? "(assumeonground) " : "") + (fromOnGround ? "onground -> " : (resetFrom ? "resetcond -> " : "--- -> ")) + (toOnGround ? "onground" : (resetTo ? "resetcond" : "---")) + ", jumpphase: " + data.sfJumpPhase + ", liftoff: " + data.liftOffEnvelope.name() + "(" + data.insideMediumCount + ")"); builder.append(player.getName() + " SurvivalFly\nground: " + (data.thisMove.touchedGroundWorkaround ? "(assumeonground) " : "") + (fromOnGround ? "onground -> " : (resetFrom ? "resetcond -> " : "--- -> ")) + (toOnGround ? "onground" : (resetTo ? "resetcond" : "---")) + ", jumpphase: " + data.sfJumpPhase + ", liftoff: " + data.liftOffEnvelope.name() + "(" + data.insideMediumCount + ")");
final String dHDist = (lastMove.toIsValid && Math.abs(lastMove.hDistance - hDistance) > 0.0005) ? ("(" + (hDistance > lastMove.hDistance ? "+" : "") + StringUtil.fdec3.format(hDistance - lastMove.hDistance) + ")") : ""; final String dHDist = (lastMove.toIsValid && Math.abs(lastMove.hDistance - hDistance) > 0.0005) ? ("(" + (hDistance > lastMove.hDistance ? "+" : "") + StringUtil.fdec3.format(hDistance - lastMove.hDistance) + ")") : "";
builder.append("\n" + " hDist: " + StringUtil.fdec3.format(hDistance) + dHDist + " / " + StringUtil.fdec3.format(hAllowedDistance) + hBuf + lostSprint + hVelUsed + " , vDist: " + StringUtil.fdec3.format(yDistance) + (!lastMove.toIsValid ? "" : (" (" + (yDistance > lastMove.yDistance ? "+" : "") + StringUtil.fdec3.format(yDistance - lastMove.yDistance) + ")")) + " / " + StringUtil.fdec3.format(vAllowedDistance) + ", sby=" + (data.hasSetBack() ? (data.getSetBackY() + " (" + StringUtil.fdec3.format(to.getY() - data.getSetBackY()) + " / " + data.liftOffEnvelope.getMaxJumpHeight(data.jumpAmplifier) + ")") : "?")); builder.append("\n" + " hDist: " + StringUtil.fdec3.format(hDistance) + dHDist + " / " + StringUtil.fdec3.format(hAllowedDistance) + hBuf + lostSprint + hVelUsed + " , vDist: " + StringUtil.fdec3.format(yDistance) + (!lastMove.toIsValid ? "" : (" (" + (yDistance > lastMove.yDistance ? "+" : "") + StringUtil.fdec3.format(yDistance - lastMove.yDistance) + ")")) + " / " + StringUtil.fdec3.format(vAllowedDistance) + ", sby=" + (data.hasSetBack() ? (data.getSetBackY() + " (" + StringUtil.fdec3.format(to.getY() - data.getSetBackY()) + " / " + data.liftOffEnvelope.getMaxJumpHeight(data.jumpAmplifier) + ")") : "?"));
if (data.verVelUsed != null) { if (data.verVelUsed != null) {

View File

@ -0,0 +1,124 @@
package fr.neatmonster.nocheatplus.checks.moving.model;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/**
* Some useful data about a location. Used in MoveData to keep track of
* past-move properties.
*
* @author asofold
*
*/
public class LocationData {
public double x, y, z;
public float yaw, pitch;
/** Must be checked before using any of the flags. */
public boolean extraPropertiesValid = false;
/** Basic environmental properties. */
public boolean onClimbable, inWeb, inLava, inWater, inLiquid, onGround;
/** Aggregate properties (reset means potentially resetting fall damage). */
public boolean resetCond, onGroundOrResetCond;
/**
* Set same as other.
* @param other
*/
public void set(final LocationData other) {
setLocation(other.x, other.y, other.z, other.yaw, other.pitch);
setExtraProperties(other);
}
/**
* Set all that can be set.
* @param loc
*/
public void set(final PlayerLocation loc) {
setLocation(loc);
setExtraProperties(loc);
}
/**
* Set location data.
* @param loc
*/
public void setLocation(final PlayerLocation loc) {
setLocation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
}
/**
* Set location data.
* @param x
* @param y
* @param z
* @param yaw
* @param pitch
*/
public void setLocation(final double x, final double y, final double z, final float yaw, final float pitch) {
this.x = x;
this.y = y;
this.z = z;
this.yaw = yaw;
this.pitch = pitch;
}
/**
* Set extra properties based on the given PlayerLocation instance.
*
* @param loc
*/
public void setExtraProperties(final PlayerLocation loc) {
loc.collectBlockFlags(); // Just ensure.
onClimbable = loc.isOnClimbable();
inWeb = loc.isInWeb();
inLiquid = loc.isInLiquid();
if (inLiquid) {
inLava = loc.isInLava();
inWater = loc.isInWater();
}
else {
inLava = inWater = false;
}
onGround = loc.isOnGround();
resetCond = inLiquid || inWeb || onClimbable;
onGroundOrResetCond = onGround || resetCond;
// Set valid flag last.
extraPropertiesValid = true;
}
/**
* Set extra properties same as the given LocationData instance.
*
* @param loc
*/
public void setExtraProperties(final LocationData other) {
if (other.extraPropertiesValid) {
onClimbable = other.onClimbable;
inWeb = other.inWeb;
inLiquid = other.inLiquid;
inLava = other.inLava;
inWater = other.inWater;
onGround = other.onGround;
// Use aggregate properties 1:1, allowing for hacks.
resetCond = other.resetCond;
onGroundOrResetCond = other.onGroundOrResetCond;
}
// Set valid flag last.
extraPropertiesValid = other.extraPropertiesValid;
}
public void resetExtraProperties() {
extraPropertiesValid = false;
onClimbable = false;
inWeb = false;
inLiquid = false;
inLava = false;
inWater = false;
onGround = false;
resetCond = false;
onGroundOrResetCond = false;
}
}

View File

@ -14,8 +14,6 @@ import fr.neatmonster.nocheatplus.utilities.TrigUtil;
*/ */
public class MoveData { public class MoveData {
// TODO: Use objects for from and to (could lead to redesign, think of PlayerLocation)?
/** /**
* Not enforced, but meant to be an invalidated MoveData instance. * Not enforced, but meant to be an invalidated MoveData instance.
*/ */
@ -31,11 +29,9 @@ public class MoveData {
public boolean valid = false; // Must initialize. public boolean valid = false; // Must initialize.
/** /**
* Start position coordinates. * Start point of a move, or a static location (join/teleport).
*/ */
public double fromX, fromY, fromZ; public final LocationData from = new LocationData();
/** Looking direction of the start position. */
public float fromYaw, fromPitch;
/** /**
* Indicate if coordinates for a move end-point and distances are present. * Indicate if coordinates for a move end-point and distances are present.
@ -49,11 +45,10 @@ public class MoveData {
// Coordinates and distances. // Coordinates and distances.
/** End-point of a move. Only valid if toIsValid is set to true. */ /**
public double toX, toY, toZ; * End point of a move.
*/
/** Looking direction of a move end-point. Only valid if toIsValid is set to true. */ public final LocationData to = new LocationData();
public float toYaw, toPitch;
/** /**
* The vertical distance covered by a move. Note the sign for moving up or * The vertical distance covered by a move. Note the sign for moving up or
@ -79,7 +74,7 @@ public class MoveData {
*/ */
public double walkSpeed; public double walkSpeed;
// Special properties of the environment. // Properties involving the environment.
/** /**
* Head is obstructed. Should expect descending next move, if in air. <br> * Head is obstructed. Should expect descending next move, if in air. <br>
@ -94,6 +89,20 @@ public class MoveData {
*/ */
public boolean downStream; public boolean downStream;
/**
* Somehow the player has touched ground with this move (including
* workarounds), thus the client might move up next move. This flag is only
* updated by from/to.onGround, if MoveData.setExtraProperties is called for
* this instance.
*/
public boolean touchedGround;
/**
* Set if touchedGround has been set due to applying a workaround
* exclusively.
*/
public boolean touchedGroundWorkaround;
// Bounds set by checks. // Bounds set by checks.
/** /**
@ -108,6 +117,11 @@ public class MoveData {
*/ */
public double hAllowedDistance; public double hAllowedDistance;
/** This move was a bunny hop. */
public boolean bunnyHop;
// TODO: verVel/horvel used?
// Meta stuff. // Meta stuff.
/** /**
@ -116,35 +130,11 @@ public class MoveData {
*/ */
public CheckType flyCheck; public CheckType flyCheck;
// TODO: ground/reset/web/... private void setPositions(final PlayerLocation from, final PlayerLocation to) {
this.from.setLocation(from);
/** this.to.setLocation(to);
* yDistance = this.to.y - this.from.y;
* @param fromX hDistance = TrigUtil.distance(this.from.x, this.from.z, this.to.x, this.to.z);
* @param fromY
* @param fromZ
* @param fromYaw
* @param fromPitch
* @param toX
* @param toY
* @param toZ
* @param toYaw
* @param toPitch
*/
private void setPositions(final double fromX, final double fromY, final double fromZ, final float fromYaw, final float fromPitch,
final double toX, final double toY, final double toZ, final float toYaw, final float toPitch) {
yDistance = toY - fromY;
hDistance = TrigUtil.distance(fromX, fromZ, toX, toZ);
this.fromX = fromX;
this.fromY = fromY;
this.fromZ = fromZ;
this.fromYaw = fromYaw;
this.fromPitch = fromPitch;
this.toX = toX;
this.toY = toY;
this.toZ = toZ;
this.toYaw = toYaw;
this.toPitch = toPitch;
toIsValid = true; toIsValid = true;
} }
@ -157,20 +147,22 @@ public class MoveData {
* @param pitch * @param pitch
*/ */
private void setPositions(final double x, final double y, final double z, final float yaw, final float pitch) { private void setPositions(final double x, final double y, final double z, final float yaw, final float pitch) {
this.fromX = x; from.setLocation(x, y, z, yaw, pitch);
this.fromY = y;
this.fromZ = z;
this.fromYaw = yaw;
this.fromPitch = pitch;
toIsValid = false; toIsValid = false;
} }
private void resetBase() { private void resetBase() {
// Reset extra properties.
from.extraPropertiesValid = false;
to.extraPropertiesValid = false;
// Properties of the player. // Properties of the player.
walkSpeed = 0.2; walkSpeed = 0.2;
// Special properties of the environment. // Properties involving the environment.
headObstructed = false; headObstructed = false;
downStream = false; downStream = false;
touchedGround = false;
touchedGroundWorkaround = false;
bunnyHop = false;
// Bounds set by checks. // Bounds set by checks.
hAllowedDistanceBase = 0.0; hAllowedDistanceBase = 0.0;
hAllowedDistance = 0.0; hAllowedDistance = 0.0;
@ -181,19 +173,22 @@ public class MoveData {
} }
/** /**
* Set some basic data and reset all other properties properly. * Set some basic data and reset all other properties properly. Does not set
* extra properties for locations.
* *
* @param from * @param from
* @param to * @param to
*/ */
public void set(final PlayerLocation from, final PlayerLocation to) { public void set(final PlayerLocation from, final PlayerLocation to) {
setPositions(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch(), setPositions(from, to);
to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
resetBase(); resetBase();
// TODO: this.from/this.to setExtraProperties ?
} }
/** /**
* Set with join / teleport / set-back. * Set with join / teleport / set-back. Does not set extra properties for
* locations.
*
* @param x * @param x
* @param y * @param y
* @param z * @param z
@ -205,12 +200,29 @@ public class MoveData {
resetBase(); resetBase();
} }
/**
* Update extra properties (onGround and other) within LocationData (from,
* to), update touchedGround.
*
* @param from
* @param to
*/
public void setExtraProperties(final PlayerLocation from, final PlayerLocation to) {
this.from.setExtraProperties(from);
this.to.setExtraProperties(to);
if (this.from.onGround || this.to.onGround) {
this.touchedGround = true;
}
}
/** /**
* Fast invalidation: just set the flags. * Fast invalidation: just set the flags.
*/ */
public void invalidate() { public void invalidate() {
valid = false; valid = false;
toIsValid = false; toIsValid = false;
from.extraPropertiesValid = false;
to.extraPropertiesValid = false;
} }
} }

View File

@ -156,7 +156,7 @@ public class MovingUtil {
// TODO: Consider counting as tracked? // TODO: Consider counting as tracked?
continue; continue;
} }
else if (TrigUtil.isSamePos(refLoc, otherLastMove.toX, otherLastMove.toY, otherLastMove.toZ)) { else if (TrigUtil.isSamePos(refLoc, otherLastMove.to.x, otherLastMove.to.y, otherLastMove.to.z)) {
// Tracked. // Tracked.
return null; return null;
} }
@ -165,7 +165,7 @@ public class MovingUtil {
// TODO: Discard locations in the same block, if passable. // TODO: Discard locations in the same block, if passable.
// TODO: Sanity check distance? // TODO: Sanity check distance?
// More leniency: allow moving inside of the same block. // More leniency: allow moving inside of the same block.
if (TrigUtil.isSameBlock(loc, otherLastMove.toX, otherLastMove.toY, otherLastMove.toZ) && !BlockProperties.isPassable(refLoc.getWorld(), otherLastMove.toX, otherLastMove.toY, otherLastMove.toZ)) { if (TrigUtil.isSameBlock(loc, otherLastMove.to.x, otherLastMove.to.y, otherLastMove.to.z) && !BlockProperties.isPassable(refLoc.getWorld(), otherLastMove.to.x, otherLastMove.to.y, otherLastMove.to.z)) {
continue; continue;
} }
untrackedData = otherData; untrackedData = otherData;
@ -179,7 +179,7 @@ public class MovingUtil {
else { else {
// TODO: Count and log to TRACE_FILE, if multiple locations would match (!). // TODO: Count and log to TRACE_FILE, if multiple locations would match (!).
final MoveData lastMove = untrackedData.moveData.getFirst(); final MoveData lastMove = untrackedData.moveData.getFirst();
return new Location(loc.getWorld(), lastMove.toX, lastMove.toY, lastMove.toZ, loc.getYaw(), loc.getPitch()); return new Location(loc.getWorld(), lastMove.to.x, lastMove.to.y, lastMove.to.z, loc.getYaw(), loc.getPitch());
} }
} }