[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".
final MoveData lastMove = mData.moveData.getFirst();
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) {
// TODO: Might need to check hDist relative to speed / modifiers.
final MovingConfig mc = MovingConfig.getConfig(player);

View File

@ -162,8 +162,6 @@ public class MovingData extends ACheckData {
private LocationTrace trace = null;
// sf rather
/** To/from was ground or web or assumed to be etc. */
public boolean toWasReset, fromWasReset;
/** Basic envelope constraints for switching into air. */
public LiftOffEnvelope liftOffEnvelope = defaultLiftOffEnvelope;
/** 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;
/** Last y coordinate from when the player was on ground. */
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. */
public boolean noFallSkipAirCheck = false;
// Passable check.
@ -224,7 +220,7 @@ public class MovingData extends ACheckData {
public int sfHoverTicks = -1;
/** First count these down before incrementing sfHoverTicks. Set on join, if configured so. */
public int sfHoverLoginTicks = 0;
public int sfOnIce = 0;
public int sfOnIce = 0; // TODO: Replace by allowed speed + friction.
public long sfCobwebTime = 0;
public double sfCobwebVL = 0;
public long sfVLTime = 0;
@ -265,6 +261,7 @@ public class MovingData extends ACheckData {
private void invalidateMoveData() {
final Iterator<MoveData> it = moveData.iterator();
while (it.hasNext()) {
// TODO: If using many elements ever, stop at the first already invalidated one.
it.next().invalidate();
}
thisMove.invalidate();
@ -295,7 +292,6 @@ public class MovingData extends ACheckData {
removeAllVelocity();
sfHorizontalBuffer = 0.0;
lostSprintCount = 0;
toWasReset = fromWasReset = false; // TODO: true maybe
sfHoverTicks = sfHoverLoginTicks = -1;
sfDirty = false;
sfLowJump = false;
@ -330,7 +326,6 @@ public class MovingData extends ACheckData {
// keep jump phase.
sfHorizontalBuffer = 0.0;
lostSprintCount = 0;
toWasReset = fromWasReset = false; // TODO: true maybe
sfHoverTicks = -1; // 0 ?
sfDirty = false;
sfLowJump = false;
@ -350,8 +345,6 @@ public class MovingData extends ACheckData {
clearAccounting();
sfJumpPhase = 0;
sfZeroVdist = 0;
toWasReset = false;
fromWasReset = false;
verticalBounce = null;
// Remember where we send the player to.
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>
* Currently: liftOffEnvelope, nextFriction.
*

View File

@ -407,7 +407,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
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!).
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.
//&& 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.
// TODO: Data resetting above ?
data.noFallAssumeGround = false;
data.resetTeleported();
// Debug.
if (data.debug) {
@ -664,6 +663,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
pTo.collectBlockFlags(maxYNoFall);
}
// Set basic properties for past move bookkeeping.
data.thisMove.setExtraProperties(pFrom, pTo);
// Hack: Add velocity for transitions between creativefly and survivalfly.
if (lastMove.toIsValid && lastMove.flyCheck == CheckType.MOVING_CREATIVEFLY) {
workaroundFlyNoFlyTransition(tick, data);
@ -681,7 +683,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
if (newTo == null) {
// Hover.
// 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.
hoverTicks.add(playerName);
data.sfHoverTicks = 0;
@ -738,12 +742,13 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Reset jump amplifier if needed.
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;
}
}
if (newTo == null) {
// Allowed move.
// Bounce effects.
@ -1007,7 +1012,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
else {
// TODO: Detect differing location (a teleport event would follow).
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.
mData.resetPositions(to);
} else {
@ -1066,11 +1071,15 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
final MovingData data = MovingData.getData(player);
final Location teleported = data.getTeleported();
// Prevent further moving processing for nested events.
processingEvents.remove(player.getName());
// 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".
Location to = event.getTo();
final TeleportCause cause = event.getCause();
final Location ref;
final MovingConfig cc = MovingConfig.getConfig(player);
if (teleported != null && teleported.equals(to)) {
@ -1090,157 +1099,106 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// TODO: This could be done on MONITOR.
data.onSetBack(teleported);
adjustLiftOffEnvelope(player, teleported, data, cc);
} else {
// Only if it wasn't NoCheatPlus, drop data from more packets check.
if (to != null && !event.isCancelled()) {
// Normal teleport.
// Detect small distance teleports.
boolean smallRange = false;
boolean cancel = false;
// boolean pass = false;
final double margin = 0.67;
final Location from = event.getFrom();
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)) {
// Not check on-ground: Check the second throw.
cancel = true;
}
else {
// pass = true;
}
}
else if (cause == TeleportCause.COMMAND) {
// Attempt to prevent teleporting to players inside of blocks at untracked coordinates.
// TODO: Consider checking this on low or lowest (!).
// TODO: Other like TeleportCause.PLUGIN?
if (cc.passableUntrackedTeleportCheck && MovingUtil.shouldCheckUntrackedLocation(player, to)) {
final Location newTo = MovingUtil.checkUntrackedLocation(to);
if (newTo != null) {
// Adjust the teleport to go to the last tracked to-location of the other player.
to = newTo;
event.setTo(newTo);
cancel = smallRange = false;
// TODO: Consider console, consider data.debug.
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.TRACE_FILE, player.getName() + " correct untracked teleport destination (" + to + " corrected to " + newTo + ").");
}
}
}
// if (pass) {
// ref = to;
// }
// else
if (cancel) {
// Cancel!
if (data.hasSetBack() && !data.hasSetBackWorldChanged(to)) {
ref = data.getSetBack(to);
event.setTo(ref);
adjustLiftOffEnvelope(player, ref, data, cc);
data.resetPositions(ref);
}
else {
ref = from;
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 {
// "real" teleport
ref = to;
double fallDistance = data.noFallFallDistance;
final LiftOffEnvelope oldEnv = data.liftOffEnvelope; // Remember for workarounds.
data.clearMorePacketsData();
data.clearFlyData();
if (TrigUtil.maxDistance(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()) <= 12.0) {
// TODO: Might happen with bigger distances (mainly ender pearl thrown at others).
// Keep old lift-off envelope.
data.liftOffEnvelope = oldEnv;
}
data.setSetBack(to);
adjustLiftOffEnvelope(player, to, data, cc);
// TODO: How to account for plugins that reset the fall distance here?
if (fallDistance > 1.0 && fallDistance - player.getFallDistance() > 0.0) {
// Reset fall distance if set so in the config.
if (!cc.noFallTpReset) {
// (Set fall distance if set to not reset.)
player.setFallDistance((float) fallDistance);
}
}
if (event.getCause() == TeleportCause.ENDER_PEARL) {
// Prevent NoFall violations for ender-pearls.
data.noFallSkipAirCheck = true;
}
data.sfHoverTicks = -1; // Important against concurrent modification exception.
data.resetPositions(ref);
}
if (data.debug && BuildParameters.debugLevel > 0) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP" + (smallRange ? " (small-range)" : "") + (cancel ? " (cancelled)" : "") + ": " + to);
}
if (data.debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP " + cause + " (set-back): " + to);
}
else {
} else {
// Ignore if cancelled or if end point is set.
if (to == null || event.isCancelled()) {
// 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);
if (data.debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP " + cause + " (already cancelled): " + to);
}
return;
}
// Normal teleport.
boolean cancel = false;
// boolean pass = false;
final Location from = event.getFrom();
if (cause == TeleportCause.ENDER_PEARL) {
if (CombinedConfig.getConfig(player).enderPearlCheck && !BlockProperties.isPassable(to)) { // || !BlockProperties.isOnGroundOrResetCond(player, to, 1.0)) {
// Not check on-ground: Check the second throw.
// TODO: Bounding box check or onGround as replacement?
cancel = true;
}
}
else if (cause == TeleportCause.COMMAND) {
// Attempt to prevent teleporting to players inside of blocks at untracked coordinates.
// TODO: Consider checking this on low or lowest (!).
// TODO: Other like TeleportCause.PLUGIN?
if (cc.passableUntrackedTeleportCheck && MovingUtil.shouldCheckUntrackedLocation(player, to)) {
final Location newTo = MovingUtil.checkUntrackedLocation(to);
if (newTo != null) {
// Adjust the teleport to go to the last tracked to-location of the other player.
to = newTo;
event.setTo(newTo);
cancel = false;
// TODO: Consider console, consider data.debug.
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.TRACE_FILE, player.getName() + " correct untracked teleport destination (" + to + " corrected to " + newTo + ").");
}
}
}
if (cancel) {
// NCP actively prevents this teleport.
if (data.hasSetBack() && !data.hasSetBackWorldChanged(to)) {
ref = data.getSetBack(to);
event.setTo(ref);
adjustLiftOffEnvelope(player, ref, data, cc);
data.resetPositions(ref);
}
else {
ref = from;
event.setCancelled(true);
}
}
else {
// "real" teleport
ref = to;
double fallDistance = data.noFallFallDistance;
final LiftOffEnvelope oldEnv = data.liftOffEnvelope; // Remember for workarounds.
data.clearMorePacketsData();
data.clearFlyData();
if (TrigUtil.maxDistance(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()) <= 12.0) {
// TODO: Might happen with bigger distances (mainly ender pearl thrown at others).
// Keep old lift-off envelope.
data.liftOffEnvelope = oldEnv;
}
data.setSetBack(to);
adjustLiftOffEnvelope(player, to, data, cc);
// TODO: How to account for plugins that reset the fall distance here?
if (fallDistance > 1.0 && fallDistance - player.getFallDistance() > 0.0) {
// Reset fall distance if set so in the config.
if (!cc.noFallTpReset) {
// (Set fall distance if set to not reset.)
player.setFallDistance((float) fallDistance);
} else if (fallDistance >= 3.0) {
data.noFallSkipAirCheck = true;
}
}
if (cause == TeleportCause.ENDER_PEARL) {
// Prevent NoFall violations for ender-pearls.
data.noFallSkipAirCheck = true;
}
data.sfHoverTicks = -1; // Important against concurrent modification exception.
data.resetPositions(ref);
}
if (data.debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " TP " + cause + " " + (cancel ? " (cancelled)" : "") + ": " + to);
}
}
// Reset stuff.
Combined.resetYawRate(player, ref.getYaw(), System.currentTimeMillis(), true); // TODO: Not sure.
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) {
final Player player = event.getPlayer();
final MovingData data = MovingData.getData(player);
// TODO: Prevent/cancel scheduled teleport (use PlayerData/task for teleport, or a sequence count).
data.clearFlyData();
data.resetSetBack(); // To force dataOnJoin to set it to loc.
// Handle respawn like join.
@ -1507,7 +1466,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
data.clearFlyData();
data.setSetBack(loc);
data.resetPositions(loc);
data.joinOrRespawn = true;
data.joinOrRespawn = true; // TODO: Review if to always set (!).
} else {
// TODO: Check consistency/distance.
//final Location setBack = data.getSetBack(loc);
@ -1525,10 +1484,10 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
data.vDistAcc.clear();
final MoveInfo moveInfo = useMoveInfo();
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);
returnMoveInfo(moveInfo);
data.fromWasReset = data.toWasReset;
// Enforcing the location.
if (cc.enforceLocation) {
@ -1536,7 +1495,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
}
// Hover.
initHover(player, data, cc, data.toWasReset); // isOnGroundOrResetCond
initHover(player, data, cc, lastMove.from.onGroundOrResetCond); // isOnGroundOrResetCond
// // Bad pitch/yaw, just in case.
// if (LocUtil.needsDirectionCorrection(useLoc.getYaw(), useLoc.getPitch())) {
@ -1580,11 +1539,12 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
}
if (!player.isSleeping() && !player.isDead()) {
// Check for missed moves.
// TODO: Force-load chunks [log if (!)] ?
// TODO: Consider to catch all, at least (debug-) logging-wise.
if (!BlockProperties.isPassable(loc)) {
final MoveData lastMove = data.moveData.getFirst();
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);
if (d > 0.0) {
// 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) {
final MoveData lastMove = data.moveData.getFirst();
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) {
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) {
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.
// TODO: Add history / alert?
//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).
return data.getSetBack(loc);
} 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 {
return null;

View File

@ -10,6 +10,8 @@ import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.Check;
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.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.logging.Streams;
@ -122,10 +124,14 @@ public class NoFall extends Check {
* @param 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 double toY = to.getY();
final MoveData thisMove = data.thisMove;
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;
@ -133,21 +139,23 @@ public class NoFall extends Check {
// Reset-cond is not touched by yOnGround.
// TODO: Distinguish water depth vs. fall distance ?
final boolean fromReset = from.isResetCond();
final boolean toReset = to.isResetCond();
final boolean fromReset = from.resetCond;
final boolean toReset = to.resetCond;
final boolean fromOnGround, toOnGround;
// Adapt yOnGround if necessary (sf uses another setting).
if (yDiff < 0 && cc.yOnGround < cc.noFallyOnGround) {
// In fact this is somewhat heuristic, but it seems to work well.
// Missing on-ground seems to happen with running down pyramids rather.
// 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 (...)
final double minY = Math.min(fromY, toY);
@ -155,8 +163,12 @@ public class NoFall extends Check {
if (fromReset) {
// Just reset.
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).
if (cc.noFallDealDamage) {
handleOnGround(player, minY, true, data, cc);
@ -164,6 +176,10 @@ public class NoFall extends Check {
else {
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) {
// Just reset.
@ -199,7 +215,7 @@ public class NoFall extends Check {
if (!toReset && !toOnGround && yDiff < 0) {
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);
if (max > 0.0 && max < 0.75) { // (Ensure this does not conflict with deal-damage set to false.)
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...
}
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.
// TODO: Always/never reset with any ground touched?
data.resetVelocityJumpPhase();
}
@ -487,7 +488,7 @@ public class SurvivalFly extends Check {
else if (from.isInWeb()) {
data.liftOffEnvelope = LiftOffEnvelope.NO_JUMP; // TODO: Test.
}
else if (resetFrom || data.noFallAssumeGround) {
else if (resetFrom || thisMove.touchedGround) {
// TODO: Where exactly to put noFallAssumeGround ?
data.liftOffEnvelope = LiftOffEnvelope.NORMAL;
}
@ -552,8 +553,6 @@ public class SurvivalFly extends Check {
data.clearActiveHorVel();
}
// Adjust data.
data.toWasReset = resetTo || data.noFallAssumeGround;
data.fromWasReset = resetFrom || data.noFallAssumeGround;
data.lastFrictionHorizontal = data.nextFrictionHorizontal;
data.lastFrictionVertical = data.nextFrictionVertical;
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.
strictVdistRel = true;
}
else if (resetFrom) {
else if (resetFrom || data.thisMove.touchedGroundWorkaround) {
// TODO: More concise conditions? Some workaround may allow more.
if (toOnGround) { // TODO: Might have to use max if resetto.
vAllowedDistance = cc.sfStepHeight;
} else {
if (toOnGround) {
vAllowedDistance = Math.max(cc.sfStepHeight, maxJumpGain + jumpGainMargin);
}
else {
// Code duplication with the absolute limit below.
if (yDistance < 0.0 || yDistance > cc.sfStepHeight || !tags.contains("lostground_couldstep")) {
vAllowedDistance = maxJumpGain + jumpGainMargin;
} else {
}
else {
// lostground_couldstep
// TODO: Other conditions / envelopes?
vAllowedDistance = yDistance;
}
}
strictVdistRel = false;
}
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.
vAllowedDistance = cc.sfStepHeight;
}
@ -908,12 +911,28 @@ public class SurvivalFly extends Check {
vAllowedDistance = maxJumpGain + jumpGainMargin;
}
strictVdistRel = false;
} else {
}
else {
// Friction.
// TODO: data.lastFrictionVertical (see above).
vAllowedDistance = lastMove.yDistance * data.lastFrictionVertical - GRAVITY_MIN; // Upper bound.
strictVdistRel = true;
}
} else {
}
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 {
// Allow 0 y-distance once.
vAllowedDistance = 0.0;
}
strictVdistRel = false;
}
else {
// Problematic point (thinking of "ncp remove ...").
// Ensure: data.resetLastDistances() must be used on teleport/join etc.
vAllowedDistance = yDistance;
@ -932,7 +951,7 @@ public class SurvivalFly extends Check {
}
else if (yDistDiffEx > 0.0) { // Upper bound violation.
// && (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.
// 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.
}
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
&& yDistance < lastMove.yDistance - 0.001) {
// 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) {
// 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.
// 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.
// TODO: Needs more efficient structure.
}
@ -1025,8 +1045,9 @@ public class SurvivalFly extends Check {
// TODO: Margins !?
}
else if (data.liftOffEnvelope == LiftOffEnvelope.LIMIT_LIQUID
&& data.sfJumpPhase == 1
&& data.fromWasReset && !data.toWasReset && !resetFrom && resetTo // TODO: There might be other cases (possibly wrong bounding box).
&& data.sfJumpPhase == 1 && lastMove.toIsValid
&& 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 !
&& yDistance < 0.0 && Math.abs(Math.abs(yDistance) - lastMove.yDistance) < GRAVITY_SPAN / 2.0
) {
@ -1058,30 +1079,35 @@ public class SurvivalFly extends Check {
}
// Absolute y-distance to set back.
if (yDistance > 0.0 && !data.isVelocityJumpPhase()
&& !((fromOnGround || data.noFallAssumeGround) && toOnGround && yDistance <= cc.sfStepHeight)) {
if (yDistance > 0.0 && !data.isVelocityJumpPhase()) {
// TODO: Maintain a value in data, adjusting to velocity?
// TODO: LIMIT_JUMP
final double vAllowedAbsoluteDistance = data.liftOffEnvelope.getMaxJumpHeight(data.jumpAmplifier);
final double totalVDistViolation = to.getY() - data.getSetBackY() - vAllowedAbsoluteDistance;
if (totalVDistViolation > 0.0) {
// Skip if the player could step up.
if (yDistance > cc.sfStepHeight || !tags.contains("lostground_couldstep")) {
if (data.getOrUseVerticalVelocity(yDistance) == null) {
// Last minute special case.
// Teleport to in-air (PaperSpigot 1.7.10).
if (!lastMove.toIsValid && data.sfJumpPhase == 0 && mightBeMultipleMoves
&& Math.abs(totalVDistViolation) < 0.01 && yDistance > 0.0 && yDistance < 0.01
&& !resetFrom && !resetTo && !data.noFallAssumeGround
) {
// TODO: Confine to PaperSpigot?
// TODO: Confine to from at block level (offest 0)?
tags.add("skip_paper");
} else {
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, totalVDistViolation);
tags.add("vdistsb");
}
}
// Skip actually stepping up.
if ((fromOnGround || data.thisMove.touchedGroundWorkaround || lastMove.touchedGround)
&& toOnGround && yDistance <= cc.sfStepHeight) {
// 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).
else if (!lastMove.toIsValid && data.sfJumpPhase == 0 && mightBeMultipleMoves
&& Math.abs(totalVDistViolation) < 0.01 && yDistance > 0.0 && yDistance < 0.01
&& !resetFrom && !resetTo && !data.thisMove.touchedGround
) {
// TODO: Confine to PaperSpigot?
// TODO: Confine to from at block level (offset 0)?
tags.add("skip_paper");
}
// Attempt to use velocity.
else if (data.getOrUseVerticalVelocity(yDistance) == null) {
// Violation.
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, totalVDistViolation);
tags.add("vdistsb");
}
}
}
@ -1099,11 +1125,11 @@ public class SurvivalFly extends Check {
// Simple-step blocker.
// 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) {
boolean step = true;
// 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)) {
step = false;
}
@ -1209,6 +1235,7 @@ public class SurvivalFly extends Check {
}
// TODO: data.lastFrictionVertical (see vDistAir).
final double frictDist = lastYDist * lastFrictionVertical - GRAVITY_MIN;
// TODO: Extra amount: distinguish pos/neg?
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
// 1: Head is 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.
|| 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
// 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
// 1: Near ground (slime block).
|| 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.
|| data.isVelocityJumpPhase()
@ -1353,7 +1384,8 @@ public class SurvivalFly extends Check {
&& yDistChange < -GRAVITY_SPAN
// 0: Another near 0 yDistance case.
// 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
// 0: Reduced jumping envelope.
|| data.liftOffEnvelope != LiftOffEnvelope.NORMAL
@ -1483,12 +1515,12 @@ public class SurvivalFly extends Check {
// TODO: Clear active vertical velocity here ?
// TODO: Demand consuming queued velocity for valid change (!).
// Increase
if (data.toWasReset) {
if (lastMove.touchedGround || lastMove.to.extraPropertiesValid && lastMove.to.resetCond) {
tags.add("ychinc");
}
else {
// 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.
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance));
tags.add("ychincfly");
@ -1681,9 +1713,9 @@ public class SurvivalFly extends Check {
}
// 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 (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 ?
tags.add(DOUBLE_BUNNY);
allowHop = double_bunny = true;
@ -1691,7 +1723,7 @@ public class SurvivalFly extends Check {
}
// Allow hop for special cases.
if (!allowHop && (from.isOnGround() || data.noFallAssumeGround)) {
if (!allowHop && (from.isOnGround() || thisMove.touchedGroundWorkaround)) {
// TODO: Better reset delay in this case ?
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?
@ -1724,18 +1756,27 @@ public class SurvivalFly extends Check {
|| data.thisMove.headObstructed || lastMove.toIsValid && lastMove.headObstructed && lastMove.yDistance <= 0.0
// 1: Hop without y distance increase at moderate h-speed.
// 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
)
// 0: Bad auto indent.
&& (data.sfJumpPhase == 0 && from.isOnGround() || data.sfJumpPhase <= 1 && (data.noFallAssumeGround || data.fromWasReset) || double_bunny)
&& !from.isResetCond() && !to.isResetCond() // TODO: !to.isResetCond() should be reviewed.
// 0: Ground + jump phase conditions.
&& (
// 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.
) {
// TODO: Jump effect might allow more strictness.
// TODO: Expected minimum gain depends on last speed (!).
// TODO: Speed effect affects hDistanceAboveLimit?
data.bunnyhopDelay = bunnyHopMax;
hDistanceAboveLimit = 0D;
thisMove.bunnyHop = true;
tags.add("bunnyhop");
}
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
&& yDistance < 0.0 && yDistance > -2.0 * GRAVITY_MAX - GRAVITY_MIN / 2.0
&& 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};
}
@ -1931,7 +1972,7 @@ public class SurvivalFly extends Check {
}
}
if (yDistance > 0) {
if (!fromOnGround && !toOnGround && !data.noFallAssumeGround) {
if (!data.thisMove.touchedGround) {
// Check if player may climb up.
// (This does exclude ladders.)
if (!from.canClimbUp(jumpHeight)) {
@ -2110,7 +2151,7 @@ public class SurvivalFly extends Check {
* @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) {
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) {
@ -2312,7 +2353,8 @@ public class SurvivalFly extends Check {
data.jumpAmplifier = getJumpAmplifier(player);
data.clearAccounting();
// 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);
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 lostSprint = (data.lostSprintCount > 0 ? (" lostSprint=" + data.lostSprintCount) : "");
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) + ")") : "";
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) {

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 {
// TODO: Use objects for from and to (could lead to redesign, think of PlayerLocation)?
/**
* Not enforced, but meant to be an invalidated MoveData instance.
*/
@ -31,11 +29,9 @@ public class MoveData {
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;
/** Looking direction of the start position. */
public float fromYaw, fromPitch;
public final LocationData from = new LocationData();
/**
* Indicate if coordinates for a move end-point and distances are present.
@ -49,11 +45,10 @@ public class MoveData {
// Coordinates and distances.
/** End-point of a move. Only valid if toIsValid is set to true. */
public double toX, toY, toZ;
/** Looking direction of a move end-point. Only valid if toIsValid is set to true. */
public float toYaw, toPitch;
/**
* End point of a move.
*/
public final LocationData to = new LocationData();
/**
* The vertical distance covered by a move. Note the sign for moving up or
@ -79,7 +74,7 @@ public class MoveData {
*/
public double walkSpeed;
// Special properties of the environment.
// Properties involving the environment.
/**
* Head is obstructed. Should expect descending next move, if in air. <br>
@ -94,6 +89,20 @@ public class MoveData {
*/
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.
/**
@ -108,6 +117,11 @@ public class MoveData {
*/
public double hAllowedDistance;
/** This move was a bunny hop. */
public boolean bunnyHop;
// TODO: verVel/horvel used?
// Meta stuff.
/**
@ -116,35 +130,11 @@ public class MoveData {
*/
public CheckType flyCheck;
// TODO: ground/reset/web/...
/**
*
* @param fromX
* @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;
private void setPositions(final PlayerLocation from, final PlayerLocation to) {
this.from.setLocation(from);
this.to.setLocation(to);
yDistance = this.to.y - this.from.y;
hDistance = TrigUtil.distance(this.from.x, this.from.z, this.to.x, this.to.z);
toIsValid = true;
}
@ -157,20 +147,22 @@ public class MoveData {
* @param pitch
*/
private void setPositions(final double x, final double y, final double z, final float yaw, final float pitch) {
this.fromX = x;
this.fromY = y;
this.fromZ = z;
this.fromYaw = yaw;
this.fromPitch = pitch;
from.setLocation(x, y, z, yaw, pitch);
toIsValid = false;
}
private void resetBase() {
// Reset extra properties.
from.extraPropertiesValid = false;
to.extraPropertiesValid = false;
// Properties of the player.
walkSpeed = 0.2;
// Special properties of the environment.
// Properties involving the environment.
headObstructed = false;
downStream = false;
touchedGround = false;
touchedGroundWorkaround = false;
bunnyHop = false;
// Bounds set by checks.
hAllowedDistanceBase = 0.0;
hAllowedDistance = 0.0;
@ -181,36 +173,56 @@ 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 to
*/
public void set(final PlayerLocation from, final PlayerLocation to) {
setPositions(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch(),
to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
setPositions(from, to);
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 y
* @param z
* @param yaw
* @param pitch
* @param yaw
* @param pitch
*/
public void set(final double x, final double y, final double z, final float yaw, final float pitch) {
setPositions(x, y, z, yaw, pitch);
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.
*/
public void invalidate() {
valid = false;
toIsValid = false;
from.extraPropertiesValid = false;
to.extraPropertiesValid = false;
}
}

View File

@ -156,7 +156,7 @@ public class MovingUtil {
// TODO: Consider counting as tracked?
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.
return null;
}
@ -165,7 +165,7 @@ public class MovingUtil {
// TODO: Discard locations in the same block, if passable.
// TODO: Sanity check distance?
// 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;
}
untrackedData = otherData;
@ -179,7 +179,7 @@ public class MovingUtil {
else {
// TODO: Count and log to TRACE_FILE, if multiple locations would match (!).
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());
}
}