mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-03-02 10:31:25 +01:00
[BLEEDING][BREAKING] Rework much of y-axis handling.
* In addition to the "distance from set-back" check, we have a check of the per-move distance for in-air checks, taking account of friction. * In-air and liquid checks should consume vertical velocity once needed. * Model vertical velocity "exact", i.e. positive and negative, use an entry once a sub-check fails, quite strict invalidation of not matching values, matching against the y-distance directly. * Vertical accounting has been sharpened for the moment. The new per-move checking might make it superfluous. * Remove MediumLiftOff in favor of a LiftOffEnvelope carrying basic lift-off max-gain/max-height/max-phase, enabling to distinguish between normal lift-off and liquid near ground. * Rename others (e.g. sfLastYDist -> lastYDist). Thus breaking internal naming, adding velocity via MovingData still works, but should behave slightly differently. * Fixes (waterwalk with head obstructed, resetting of sfDirty, possibly others). Issues. * Edge cases with velocity, water. * Lava needs friction, at least with velocity. * Lostground_edge(ydist < 0.0) -> bunny with yDistance > 0.0. Need more flags or better model for keeping past moves information. * Plain ground misses (layered snow). * lostground with yDist == 0.0, then seemingly in-air yDist== 0.0, then bunny/lifft-off (similar to above). Needs better modeling of past moves, because several lostgorund cases mean "the move has been on ground". Also includes geting the distance to ground for hack-proof set-back-y. * Vertical velocity is now matched with a margin, because the client seems to add randomly. * Possibly new loopholes/exploits (extreme large moves?). * Cleanup pending.
This commit is contained in:
parent
f3a137709b
commit
8230a13fc0
@ -13,7 +13,7 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.checks.ViolationData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.MediumLiftOff;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
|
||||
import fr.neatmonster.nocheatplus.permissions.Permissions;
|
||||
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
|
||||
@ -60,8 +60,9 @@ public class Critical extends Check {
|
||||
final MovingData dataM = MovingData.getData(player);
|
||||
|
||||
// TODO: Skip near the highest jump height (needs check if head collided with something solid, which also detects low jump).
|
||||
if (!dataM.isVelocityJumpPhase() && (dataM.sfLowJump && !dataM.sfNoLowJump && dataM.mediumLiftOff == MediumLiftOff.GROUND
|
||||
|| mcFallDistance < cc.criticalFallDistance && !BlockProperties.isResetCond(player, loc, mCc.yOnGround))) {
|
||||
if (!dataM.isVelocityJumpPhase() &&
|
||||
(dataM.sfLowJump && !dataM.sfNoLowJump && dataM.liftOffEnvelope == LiftOffEnvelope.NORMAL
|
||||
|| mcFallDistance < cc.criticalFallDistance && !BlockProperties.isResetCond(player, loc, mCc.yOnGround))) {
|
||||
final MovingConfig ccM = MovingConfig.getConfig(player);
|
||||
if (MovingUtil.shouldCheckSurvivalFly(player, dataM, ccM)) {
|
||||
data.criticalVL += 1.0;
|
||||
|
@ -27,12 +27,12 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.checks.combined.Combined;
|
||||
import fr.neatmonster.nocheatplus.checks.combined.Improbable;
|
||||
import fr.neatmonster.nocheatplus.checks.inventory.Items;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocationTrace;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocationTrace.TraceEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.MediumLiftOff;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocationTrace;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocationTrace.TraceEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
|
||||
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
|
||||
import fr.neatmonster.nocheatplus.components.JoinLeaveListener;
|
||||
import fr.neatmonster.nocheatplus.logging.Streams;
|
||||
@ -282,7 +282,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
if (!cancelled && TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) < 4.5){
|
||||
final MovingData mData = MovingData.getData(player);
|
||||
// Check if fly checks is an issue at all, re-check "real sprinting".
|
||||
if (mData.fromX != Double.MAX_VALUE && mData.mediumLiftOff != MediumLiftOff.LIMIT_JUMP){
|
||||
if (mData.fromX != Double.MAX_VALUE && mData.liftOffEnvelope == LiftOffEnvelope.NORMAL) {
|
||||
final double hDist = TrigUtil.distance(loc.getX(), loc.getZ(), mData.fromX, mData.fromZ);
|
||||
if (hDist >= 0.23) {
|
||||
// TODO: Might need to check hDist relative to speed / modifiers.
|
||||
|
@ -131,25 +131,29 @@ public class CreativeFly extends Check {
|
||||
|
||||
resultH *= 100D;
|
||||
|
||||
final double limitV = model.vMod / 100D * ModelFlying.VERTICAL_SPEED; // * data.jumpAmplifier;
|
||||
|
||||
// Super simple, just check distance compared to max distance vertical.
|
||||
// TODO: max descending speed ! [max fall speed, use maximum with speed or added ?]
|
||||
double limitV = model.vMod / 100D * ModelFlying.VERTICAL_SPEED; // * data.jumpAmplifier;
|
||||
|
||||
if (data.lastYDist != Double.MAX_VALUE) {
|
||||
// (Disregard gravity.)
|
||||
double frictionDist = data.lastYDist * SurvivalFly.FRICTION_MEDIUM_AIR;
|
||||
if (!flying) {
|
||||
frictionDist -= SurvivalFly.GRAVITY_MIN;
|
||||
}
|
||||
limitV = Math.max(frictionDist, limitV);
|
||||
}
|
||||
|
||||
final double resultV;
|
||||
if (yDistance > limitV && data.getOrUseVerticalVelocity(yDistance) != null) {
|
||||
resultV = 0.0;
|
||||
} else {
|
||||
resultV = (yDistance - limitV) * 100D;
|
||||
}
|
||||
|
||||
// TODO:_ signum considerations (aligned ...).
|
||||
// double vDistanceAboveLimit = yDistance - data.getVerticalFreedom() - limitV;
|
||||
// if (vDistanceAboveLimit > 0.0) {
|
||||
// // TODO: consume / use vvel
|
||||
// }
|
||||
// final double resultV = (vDistanceAboveLimit - limitV) * 100D;
|
||||
// final double result = Math.max(0.0, resultH) + Math.max(0D, resultV);
|
||||
// Old handling.
|
||||
final double verticalFreedom = data.getVerticalFreedom();
|
||||
final double resultV = (yDistance - verticalFreedom - limitV) * 100D;
|
||||
final double result = Math.max(0.0, resultH) + Math.max(0D, resultV);
|
||||
|
||||
if (cc.debug) {
|
||||
outpuDebugMove(player, hDistance, limitH, yDistance, verticalFreedom, limitV);
|
||||
outpuDebugMove(player, hDistance, limitH, yDistance, limitV, data);
|
||||
}
|
||||
|
||||
// The player went to far, either horizontal or vertical.
|
||||
@ -182,15 +186,21 @@ public class CreativeFly extends Check {
|
||||
// Slowly reduce the violation level with each event.
|
||||
data.creativeFlyVL *= 0.97D;
|
||||
|
||||
// If the event did not get cancelled, define a new setback point.
|
||||
// Adjust the set-back and other last distances.
|
||||
data.setSetBack(to);
|
||||
data.lastHDist = hDistance;
|
||||
data.lastYDist = yDistance;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void outpuDebugMove(final Player player, final double hDistance, final double limitH, final double yDistance, final double verticalFreedom, final double limitV) {
|
||||
private void outpuDebugMove(final Player player, final double hDistance, final double limitH, final double yDistance, final double limitV, final MovingData data) {
|
||||
StringBuilder builder = new StringBuilder(350);
|
||||
builder.append(player.getName());
|
||||
builder.append(" CreativeFly hdist=" + hDistance + " hlimit=" + limitH + " ydist=" + yDistance + " vfreedom=" + verticalFreedom + " vlimit=" + limitV);
|
||||
builder.append(" CreativeFly hdist=" + hDistance + " hlimit=" + limitH + " ydist=" + yDistance + " vlimit=" + limitV);
|
||||
if (data.verVelUsed != null) {
|
||||
builder.append(" vvel_use=" + data.verVelUsed);
|
||||
}
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, builder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,15 +13,16 @@ import fr.neatmonster.nocheatplus.checks.access.CheckDataFactory;
|
||||
import fr.neatmonster.nocheatplus.checks.access.ICheckData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocUtil;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocationTrace;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.MediumLiftOff;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.MoveConsistency;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.FrictionAxisVelocity;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.AccountEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.FrictionAxisVelocity;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleAxisVelocity;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry;
|
||||
import fr.neatmonster.nocheatplus.logging.Streams;
|
||||
import fr.neatmonster.nocheatplus.utilities.ActionAccumulator;
|
||||
import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
|
||||
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
|
||||
import fr.neatmonster.nocheatplus.utilities.StringUtil;
|
||||
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
||||
|
||||
/**
|
||||
@ -29,13 +30,6 @@ import fr.neatmonster.nocheatplus.utilities.TickTask;
|
||||
*/
|
||||
public class MovingData extends ACheckData {
|
||||
|
||||
/**
|
||||
* Assume the player has to move on ground or so to lift off. TODO: Test, might be better ground.
|
||||
*/
|
||||
private static final MediumLiftOff defaultMediumLiftOff = MediumLiftOff.LIMIT_JUMP;
|
||||
|
||||
// private static final long IGNORE_SETBACK_Y = BlockProperties.F_SOLID | BlockProperties.F_GROUND | BlockProperties.F_CLIMBABLE | BlockProperties.F_LIQUID;
|
||||
|
||||
/** The factory creating data. */
|
||||
public static final CheckDataFactory factory = new CheckDataFactory() {
|
||||
@Override
|
||||
@ -59,7 +53,7 @@ public class MovingData extends ACheckData {
|
||||
|
||||
/**
|
||||
* Gets the data of a specified player.
|
||||
*
|
||||
* final CheckDataFactory factory = new CheckDataFactory(
|
||||
* @param player
|
||||
* the player
|
||||
* @return the data
|
||||
@ -99,6 +93,19 @@ public class MovingData extends ACheckData {
|
||||
}
|
||||
}
|
||||
|
||||
// Check specific.
|
||||
|
||||
/**
|
||||
* Default lift-off envelope, used after resetting. <br>
|
||||
* TODO: Test, might be better ground.
|
||||
*/
|
||||
private static final LiftOffEnvelope defaultLiftOffEnvelope = LiftOffEnvelope.NO_JUMP;
|
||||
|
||||
/** Tolerance value for using vertical velocity (the client sends different values than received with fight damage). */
|
||||
private static final double TOL_VVEL = 0.0625;
|
||||
|
||||
// private static final long IGNORE_SETBACK_Y = BlockProperties.F_SOLID | BlockProperties.F_GROUND | BlockProperties.F_CLIMBABLE | BlockProperties.F_LIQUID;
|
||||
|
||||
/////////////////
|
||||
// Not static.
|
||||
/////////////////
|
||||
@ -112,10 +119,20 @@ public class MovingData extends ACheckData {
|
||||
|
||||
// Data shared between the fly checks -----
|
||||
public int bunnyhopDelay;
|
||||
public double jumpAmplifier;
|
||||
public double jumpAmplifier = 0;
|
||||
/** Last time the player was actually sprinting. */
|
||||
public long timeSprinting = 0;
|
||||
public double multSprinting = 1.30000002; // Multiplier at the last time sprinting.
|
||||
/**
|
||||
* Last valid y distance covered by a move. Integer.MAX_VALUE indicates "not set".
|
||||
*/
|
||||
public double lastYDist = Double.MAX_VALUE;
|
||||
/**
|
||||
* Last valid horizontal distance covered by a move. Integer.MAX_VALUE indicates "not set".
|
||||
*/
|
||||
public double lastHDist = Double.MAX_VALUE;
|
||||
/** Only used during processing, to keep track of sub-checks using velocity. Reset in velocityTick, before checks run. */
|
||||
public SimpleEntry verVelUsed = null;
|
||||
|
||||
/** Tick at which walk/fly speeds got changed last time. */
|
||||
public int speedTick = 0;
|
||||
@ -123,13 +140,8 @@ public class MovingData extends ACheckData {
|
||||
public float flySpeed = 0.0f;
|
||||
|
||||
// Velocity handling.
|
||||
// TODO: consider resetting these with clearFlyData and onSetBack.
|
||||
/** Vertical velocity modeled as an axis (positive and negative possible) */
|
||||
//private final FrictionAxisVelocity verVel = new FrictionAxisVelocity();
|
||||
private int verticalVelocityCounter;
|
||||
private double verticalFreedom;
|
||||
private double verticalVelocity;
|
||||
private int verticalVelocityUsed = 0;
|
||||
private final SimpleAxisVelocity verVel = new SimpleAxisVelocity();
|
||||
|
||||
/** Horizontal velocity modeled as an axis (always positive) */
|
||||
private final FrictionAxisVelocity horVel = new FrictionAxisVelocity();
|
||||
@ -147,7 +159,8 @@ public class MovingData extends ACheckData {
|
||||
// sf rather
|
||||
/** To/from was ground or web or assumed to be etc. */
|
||||
public boolean toWasReset, fromWasReset;
|
||||
public MediumLiftOff mediumLiftOff = defaultMediumLiftOff;
|
||||
/** Basic envelope constraints for switching into air. */
|
||||
public LiftOffEnvelope liftOffEnvelope = defaultLiftOffEnvelope;
|
||||
|
||||
// Locations shared between all checks.
|
||||
private Location setBack = null;
|
||||
@ -185,22 +198,17 @@ public class MovingData extends ACheckData {
|
||||
public double passableVL;
|
||||
|
||||
// Data of the survival fly check.
|
||||
public double sfHorizontalBuffer = 0.0; // ineffective: SurvivalFly.hBufMax / 2.0;
|
||||
public double sfHorizontalBuffer = 0.0; // ineffective: SurvivalFly.hBufMax / 2.0;
|
||||
/** Event-counter to cover up for sprinting resetting server side only. Set in the FighListener. */
|
||||
public int lostSprintCount = 0;
|
||||
public int sfJumpPhase = 0;
|
||||
public int lostSprintCount = 0;
|
||||
public int sfJumpPhase = 0;
|
||||
/** "Dirty" flag, for receiving velocity and similar while in air. */
|
||||
private boolean sfDirty = false;
|
||||
private boolean sfDirty = false;
|
||||
|
||||
/** Indicate low jumping descending phase (likely cheating). */
|
||||
public boolean sfLowJump = false;
|
||||
public boolean sfNoLowJump = false; // Hacks.
|
||||
|
||||
/**
|
||||
* Last valid y distance covered by a move. Integer.MAX_VALUE indicates "not set".
|
||||
*/
|
||||
public double sfLastYDist = Double.MAX_VALUE;
|
||||
public double sfLastHDist = Double.MAX_VALUE;
|
||||
/** Counting while the player is not on ground and not moving. A value <0 means not hovering at all. */
|
||||
public int sfHoverTicks = -1;
|
||||
/** First count these down before incrementing sfHoverTicks. Set on join, if configured so. */
|
||||
@ -243,7 +251,7 @@ public class MovingData extends ACheckData {
|
||||
sfJumpPhase = 0;
|
||||
jumpAmplifier = 0;
|
||||
setBack = null;
|
||||
sfLastYDist = sfLastHDist = Double.MAX_VALUE;
|
||||
lastYDist = lastHDist = Double.MAX_VALUE;
|
||||
fromX = toX = Double.MAX_VALUE;
|
||||
toYaw = Float.MAX_VALUE;
|
||||
clearAccounting();
|
||||
@ -255,9 +263,10 @@ public class MovingData extends ACheckData {
|
||||
sfHoverTicks = sfHoverLoginTicks = -1;
|
||||
sfDirty = false;
|
||||
sfLowJump = false;
|
||||
mediumLiftOff = defaultMediumLiftOff;
|
||||
liftOffEnvelope = defaultLiftOffEnvelope;
|
||||
vehicleConsistency = MoveConsistency.INCONSISTENT;
|
||||
lastFrictionHorizontal = lastFrictionVertical = 0.0;
|
||||
verVelUsed = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,7 +296,7 @@ public class MovingData extends ACheckData {
|
||||
sfHoverTicks = -1; // 0 ?
|
||||
sfDirty = false;
|
||||
sfLowJump = false;
|
||||
mediumLiftOff = defaultMediumLiftOff;
|
||||
liftOffEnvelope = defaultLiftOffEnvelope;
|
||||
removeAllVelocity();
|
||||
vehicleConsistency = MoveConsistency.INCONSISTENT; // Not entirely sure here.
|
||||
lastFrictionHorizontal = lastFrictionVertical = 0.0;
|
||||
@ -299,7 +308,7 @@ public class MovingData extends ACheckData {
|
||||
public void prepareSetBack(final Location loc) {
|
||||
clearAccounting();
|
||||
sfJumpPhase = 0;
|
||||
sfLastYDist = sfLastHDist = Double.MAX_VALUE;
|
||||
lastYDist = lastHDist = Double.MAX_VALUE;
|
||||
toWasReset = false;
|
||||
fromWasReset = false;
|
||||
// Remember where we send the player to.
|
||||
@ -352,15 +361,19 @@ public class MovingData extends ACheckData {
|
||||
fromZ = toZ = z;
|
||||
toYaw = yaw;
|
||||
toPitch = pitch;
|
||||
sfLastYDist = sfLastHDist = Double.MAX_VALUE;
|
||||
lastYDist = lastHDist = Double.MAX_VALUE;
|
||||
sfDirty = false;
|
||||
sfLowJump = false;
|
||||
mediumLiftOff = defaultMediumLiftOff;
|
||||
liftOffEnvelope = defaultLiftOffEnvelope;
|
||||
lastFrictionHorizontal = lastFrictionVertical = 0.0;
|
||||
// TODO: other buffers ?
|
||||
// No reset of vehicleConsistency.
|
||||
}
|
||||
|
||||
public void resetLastDistances() {
|
||||
lastHDist = lastYDist = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set positions according to a move (just to and from).
|
||||
* @param from
|
||||
@ -570,8 +583,47 @@ public class MovingData extends ACheckData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add horizontal velocity (distance). <br>
|
||||
* @param vel Assumes positive values always.
|
||||
* Add velocity to internal book-keeping.
|
||||
* @param player
|
||||
* @param data
|
||||
* @param cc
|
||||
* @param vx
|
||||
* @param vy
|
||||
* @param vz
|
||||
*/
|
||||
public void addVelocity(final Player player, final MovingConfig cc, final double vx, final double vy, final double vz) {
|
||||
|
||||
final int tick = TickTask.getTick();
|
||||
removeInvalidVelocity(tick - cc.velocityActivationTicks);
|
||||
|
||||
if (debug) {
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " new velocity: " + vx + ", " + vy + ", " + vz);
|
||||
}
|
||||
|
||||
// Always add vertical velocity.
|
||||
verVel.add(new SimpleEntry(tick, vy, cc.velocityActivationCounter));
|
||||
|
||||
// TODO: Should also switch to adding always.
|
||||
if (vx != 0.0 || vz != 0.0) {
|
||||
final double newVal = Math.sqrt(vx * vx + vz * vz);
|
||||
horVel.add(new AccountEntry(tick, newVal, cc.velocityActivationCounter, Math.max(20, 1 + (int) Math.round(newVal * 10.0))));
|
||||
}
|
||||
|
||||
// Set dirty flag here.
|
||||
sfDirty = true; // TODO: Set on using the velocity, due to latency !
|
||||
sfNoLowJump = true; // TODO: Set on using the velocity, due top latency !
|
||||
|
||||
}
|
||||
|
||||
public void addVerticalVelocity(final SimpleEntry entry) {
|
||||
verVel.add(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add horizontal velocity directly to horizontal-only bookkeeping.
|
||||
*
|
||||
* @param vel
|
||||
* Assumes positive values always.
|
||||
*/
|
||||
public void addHorizontalVelocity(final AccountEntry vel) {
|
||||
horVel.add(vel);
|
||||
@ -582,7 +634,7 @@ public class MovingData extends ACheckData {
|
||||
*/
|
||||
public void removeAllVelocity() {
|
||||
horVel.clear();
|
||||
clearActiveVerVel(); // Until we have a better method.
|
||||
verVel.clear();
|
||||
sfDirty = false;
|
||||
}
|
||||
|
||||
@ -593,7 +645,7 @@ public class MovingData extends ACheckData {
|
||||
*/
|
||||
public void removeInvalidVelocity(final int tick) {
|
||||
horVel.removeInvalid(tick);
|
||||
// verVel.removeInvalid(tick);
|
||||
verVel.removeInvalid(tick);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -624,17 +676,7 @@ public class MovingData extends ACheckData {
|
||||
* @return
|
||||
*/
|
||||
public boolean hasAnyVerVel() {
|
||||
return verticalFreedom >= 0.001 || verticalVelocityCounter > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear active vertical velocity (until recoded, this will remove all vertical velocity).
|
||||
*/
|
||||
public void clearActiveVerVel() {
|
||||
verticalFreedom = 0.0;
|
||||
verticalVelocity = 0.0;
|
||||
verticalVelocityCounter = 0;
|
||||
verticalVelocityUsed = 0;
|
||||
return verVel.hasQueued();
|
||||
}
|
||||
|
||||
// public boolean hasActiveVerVel() {
|
||||
@ -646,43 +688,24 @@ public class MovingData extends ACheckData {
|
||||
// }
|
||||
|
||||
/**
|
||||
* Called for moving events, increase age of velocity, decrease amounts, check which entries are invalid. Both horizontal and vertical.
|
||||
* Called for moving events. Remove invalid entries, increase age of velocity, decrease amounts, check which entries are invalid. Both horizontal and vertical.
|
||||
*/
|
||||
public void velocityTick() {
|
||||
public void velocityTick(final int invalidateBeforeTick) {
|
||||
// Remove invalid velocity.
|
||||
removeInvalidVelocity(invalidateBeforeTick);
|
||||
|
||||
// Horizontal velocity (intermediate concept).
|
||||
horVel.tick();
|
||||
|
||||
// Vertical velocity (new concept).
|
||||
// verVel.tick();
|
||||
if (verticalVelocity <= 0.09) {
|
||||
verticalVelocityUsed ++;
|
||||
verticalVelocityCounter--;
|
||||
}
|
||||
// (Vertical velocity does not tick.)
|
||||
|
||||
if (verticalVelocityCounter > 0) {
|
||||
if (verticalVelocity > 0.09) {
|
||||
verticalVelocityUsed ++;
|
||||
}
|
||||
verticalFreedom += verticalVelocity;
|
||||
verticalVelocity = Math.max(0.0, verticalVelocity -0.09);
|
||||
// TODO: Consider using up counter ? / better use velocity entries / even better use x,y,z entries right away .
|
||||
} else if (verticalFreedom > 0.001) {
|
||||
if (verticalVelocityUsed == 1 && verticalVelocity > 1.0) {
|
||||
// Workarounds.
|
||||
verticalVelocityUsed = 0;
|
||||
verticalVelocity = 0;
|
||||
verticalFreedom = 0;
|
||||
}
|
||||
else{
|
||||
// Counter has run out, now reduce the vertical freedom over time.
|
||||
verticalVelocityUsed ++;
|
||||
verticalFreedom *= 0.5;
|
||||
}
|
||||
}
|
||||
if (!sfDirty && (horVel.hasActive() || horVel.hasQueued() || verticalFreedom > 0.001)) {
|
||||
// Renew the dirty phase.
|
||||
// Renew the dirty phase.
|
||||
if (!sfDirty && (horVel.hasActive() || horVel.hasQueued())) {
|
||||
sfDirty = true;
|
||||
}
|
||||
|
||||
// Reset the "just used" velocity.
|
||||
verVelUsed = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -698,11 +721,15 @@ public class MovingData extends ACheckData {
|
||||
* Amount is the horizontal distance that is to be covered by velocity (active has already been checked).
|
||||
* <br>
|
||||
* If the modeling changes (max instead of sum or similar), then this will be affected.
|
||||
* @param amount The amount used.
|
||||
* @param amount The amount demanded, must be positive.
|
||||
* @return
|
||||
*/
|
||||
public double useHorizontalVelocity(final double amount) {
|
||||
return horVel.use(amount);
|
||||
final double available = horVel.use(amount);
|
||||
if (available >= amount) {
|
||||
sfDirty = true;
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -716,7 +743,50 @@ public class MovingData extends ACheckData {
|
||||
}
|
||||
if (horVel.hasQueued()) {
|
||||
builder.append("\n" + " horizontal velocity (queued):");
|
||||
horVel.AddQueued(builder);
|
||||
horVel.addQueued(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first matching velocity entry (invalidate others). Sets
|
||||
* verVelUsed if available.
|
||||
*
|
||||
* @param amount
|
||||
* @return
|
||||
*/
|
||||
public SimpleEntry useVerticalVelocity(final double amount) {
|
||||
final SimpleEntry available = verVel.use(amount, TOL_VVEL);
|
||||
if (available != null) {
|
||||
sfDirty = true;
|
||||
verVelUsed = available;
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the verVelUsed field and return that if appropriate. Otherwise
|
||||
* call useVerticalVelocity(amount).
|
||||
*
|
||||
* @param amount
|
||||
* @return
|
||||
*/
|
||||
public SimpleEntry getOrUseVerticalVelocity(final double amount) {
|
||||
if (verVelUsed != null) {
|
||||
if (verVel.matchesEntry(verVelUsed, amount, TOL_VVEL)) {
|
||||
return verVelUsed;
|
||||
}
|
||||
}
|
||||
return useVerticalVelocity(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging.
|
||||
* @param builder
|
||||
*/
|
||||
public void addVerticalVelocity(final StringBuilder builder) {
|
||||
if (verVel.hasQueued()) {
|
||||
builder.append("\n" + " vertical velocity (queued):");
|
||||
verVel.addQueued(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -789,6 +859,27 @@ public class MovingData extends ACheckData {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust on set back and similar.
|
||||
* @param loc
|
||||
*/
|
||||
public void adjustLiftOffEnvelope(final PlayerLocation loc) {
|
||||
// Simplified.
|
||||
if (loc.isInWeb()) {
|
||||
liftOffEnvelope = LiftOffEnvelope.NO_JUMP;
|
||||
}
|
||||
else if (loc.isInLiquid()) {
|
||||
// TODO: Distinguish strong limit.
|
||||
liftOffEnvelope = LiftOffEnvelope.LIMIT_LIQUID;
|
||||
}
|
||||
else if (loc.isOnGround()) {
|
||||
liftOffEnvelope = LiftOffEnvelope.NORMAL;
|
||||
}
|
||||
else {
|
||||
liftOffEnvelope = defaultLiftOffEnvelope;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests for a LocationTrace instance being set at all, not for locations having been added.
|
||||
* @return
|
||||
@ -852,53 +943,6 @@ public class MovingData extends ACheckData {
|
||||
trace = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add velocity to internal book-keeping.
|
||||
* @param player
|
||||
* @param data
|
||||
* @param cc
|
||||
* @param vx
|
||||
* @param vy
|
||||
* @param vz
|
||||
*/
|
||||
public void addVelocity(final Player player, final MovingConfig cc, final double vx, final double vy, final double vz) {
|
||||
|
||||
final int tick = TickTask.getTick();
|
||||
removeInvalidVelocity(tick - cc.velocityActivationTicks);
|
||||
|
||||
if (debug) {
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " new velocity: " + vx + ", " + vy + ", " + vz);
|
||||
}
|
||||
|
||||
boolean used = false;
|
||||
if (vy > 0D) {
|
||||
used = true;
|
||||
if (verticalFreedom <= 0.001 && verticalVelocityCounter >= 0) {
|
||||
verticalVelocity = 0;
|
||||
}
|
||||
verticalVelocity += vy;
|
||||
verticalFreedom += verticalVelocity;
|
||||
verticalVelocityCounter = Math.min(100, Math.max(verticalVelocityCounter, cc.velocityGraceTicks ) + 1 + (int) Math.round(vy * 10.0)); // 50;
|
||||
verticalVelocityUsed = 0;
|
||||
}
|
||||
|
||||
|
||||
if (vx != 0.0 || vz != 0.0) {
|
||||
final double newVal = Math.sqrt(vx * vx + vz * vz);
|
||||
used = true;
|
||||
final AccountEntry vel = new AccountEntry(tick, newVal, cc.velocityActivationCounter, Math.max(20, 1 + (int) Math.round(newVal * 10.0)));
|
||||
addHorizontalVelocity(vel);
|
||||
}
|
||||
|
||||
// Set dirty flag here.
|
||||
if (used) {
|
||||
// TODO: Detect when actually used? More complicated, some internal adding needs setting it here.
|
||||
sfDirty = true;
|
||||
sfNoLowJump = true;
|
||||
}
|
||||
// TODO: clear accounting here ?
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if velocity has affected the in-air jumping phase. Keeps set until
|
||||
* reset on-ground or otherwise. Use clearActiveVerVel to force end velocity
|
||||
@ -911,27 +955,6 @@ public class MovingData extends ACheckData {
|
||||
return sfDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refactoring state: Get the classic verticalFreedom.
|
||||
* @return
|
||||
*/
|
||||
public double getVerticalFreedom() {
|
||||
return verticalFreedom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refactoring stage: Fake/override vertical velocity.
|
||||
* @param velocity
|
||||
* @param freedom
|
||||
* @param counter
|
||||
*/
|
||||
public void fakeVerticalFreedom(final double velocity, final double freedom, final int counter) {
|
||||
verticalVelocity = velocity;
|
||||
verticalFreedom = freedom;
|
||||
verticalVelocityCounter = counter;
|
||||
verticalVelocityUsed = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refactoring stage: Test which value sfDirty should have and set
|
||||
* accordingly. This should only be called, if the player reached ground.
|
||||
@ -939,7 +962,8 @@ public class MovingData extends ACheckData {
|
||||
* @return If the velocity jump phase is still active (sfDirty).
|
||||
*/
|
||||
public boolean resetVelocityJumpPhase() {
|
||||
if (verticalFreedom > 0.001 || horVel.hasActive() || horVel.hasQueued()) {
|
||||
if (horVel.hasActive() || horVel.hasQueued()) {
|
||||
// TODO: What with vertical ?
|
||||
sfDirty = true;
|
||||
} else {
|
||||
sfDirty = false;
|
||||
@ -948,37 +972,12 @@ public class MovingData extends ACheckData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to builder with a leading newline, if counter or counter or freedom
|
||||
* is above threshold (0 / 0.001).
|
||||
*
|
||||
* @param builder
|
||||
* Force set the move to be affected by previous speed. Currently
|
||||
* implemented as setting velocity jump phase.
|
||||
*/
|
||||
public void logVerticalFreedom(StringBuilder builder) {
|
||||
if (verticalVelocityCounter > 0 || verticalFreedom >= 0.001) {
|
||||
builder.append("\n vertical freedom: " + StringUtil.fdec3.format(verticalFreedom) + " (vel=" + StringUtil.fdec3.format(verticalVelocity) + "/counter=" + verticalVelocityCounter +"/used=" + verticalVelocityUsed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refactoring stage: Invalidate vertical velocity based on checking vs.
|
||||
* configured grace ticks.
|
||||
*
|
||||
* @param velocityGraceTicks Ticks to check used ticks vs.
|
||||
* @param removeFreedom If to reset verticalFreedom and use count as well.
|
||||
* @return If actually reset.
|
||||
*/
|
||||
public boolean invalidateVerVelGrace(final int velocityGraceTicks, final boolean removeFreedom) {
|
||||
if (verticalVelocityUsed > velocityGraceTicks) {
|
||||
verticalVelocityCounter = 0;
|
||||
verticalVelocity = 0;
|
||||
if (removeFreedom) {
|
||||
verticalVelocityUsed = 0;
|
||||
verticalFreedom = 0;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
public void setFrictionJumpPhase() {
|
||||
// TODO: Better and more reliable modeling.
|
||||
sfDirty = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -54,10 +54,11 @@ import fr.neatmonster.nocheatplus.checks.combined.CombinedData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.LocUtil;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.MoveInfo;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.locations.VehicleSetBack;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.MediumLiftOff;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.MoveConsistency;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.AccountEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry;
|
||||
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
|
||||
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
|
||||
import fr.neatmonster.nocheatplus.components.IData;
|
||||
@ -498,8 +499,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
// Velocity tick (decrease + invalidation).
|
||||
// TODO: Rework to generic (?) queued velocity entries: activation + invalidation
|
||||
final int tick = TickTask.getTick();
|
||||
data.removeInvalidVelocity(tick - cc.velocityActivationTicks);
|
||||
data.velocityTick();
|
||||
data.velocityTick(tick - cc.velocityActivationTicks);
|
||||
|
||||
// Check which fly check to use.
|
||||
final boolean checkCf;
|
||||
@ -606,7 +606,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
mightSkipNoFall = false;
|
||||
}
|
||||
}
|
||||
if (!mightSkipNoFall) {
|
||||
if (!mightSkipNoFall && (!pTo.isResetCond() || !pFrom.isResetCond())) {
|
||||
// (Don't deal damage where no fall damage is possible.)
|
||||
noFall.checkDamage(player, data, Math.min(Math.min(from.getY(), to.getY()), loc.getY()));
|
||||
}
|
||||
}
|
||||
@ -724,6 +725,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
// Reset some data.
|
||||
data.prepareSetBack(newTo);
|
||||
data.resetPositions(newTo); // TODO: Might move into prepareSetBack, experimental here.
|
||||
adjustLiftOffEnvelope(player, newTo, data, cc);
|
||||
|
||||
// Set new to-location.
|
||||
event.setTo(newTo);
|
||||
@ -907,6 +909,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
// If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no".
|
||||
Location to = event.getTo();
|
||||
final Location ref;
|
||||
final MovingConfig cc = MovingConfig.getConfig(player);
|
||||
if (teleported != null && teleported.equals(to)) {
|
||||
// Teleport by NCP.
|
||||
// Prevent cheaters getting rid of flying data (morepackets, other).
|
||||
@ -923,8 +926,8 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
}
|
||||
// TODO: This could be done on MONITOR.
|
||||
data.onSetBack(teleported);
|
||||
adjustLiftOffEnvelope(player, teleported, data, cc);
|
||||
} else {
|
||||
final MovingConfig cc = MovingConfig.getConfig(player);
|
||||
// Only if it wasn't NoCheatPlus, drop data from more packets check.
|
||||
if (to != null && !event.isCancelled()) {
|
||||
// Normal teleport.
|
||||
@ -988,6 +991,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
if (data.hasSetBack() && !data.hasSetBackWorldChanged(to)) {
|
||||
ref = data.getSetBack(to);
|
||||
event.setTo(ref);
|
||||
adjustLiftOffEnvelope(player, ref, data, cc);
|
||||
}
|
||||
else{
|
||||
ref = from; // Player.getLocation ?
|
||||
@ -1014,16 +1018,17 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
// "real" teleport
|
||||
ref = to;
|
||||
double fallDistance = data.noFallFallDistance;
|
||||
final MediumLiftOff oldMLO = data.mediumLiftOff; // Remember for workarounds.
|
||||
final LiftOffEnvelope oldEnv = data.liftOffEnvelope; // Remember for workarounds.
|
||||
data.clearMorePacketsData();
|
||||
data.clearFlyData();
|
||||
data.resetPositions(to);
|
||||
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 MediumLiftOff.
|
||||
data.mediumLiftOff = oldMLO;
|
||||
// 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.
|
||||
@ -1057,10 +1062,26 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
// Reset stuff.
|
||||
Combined.resetYawRate(player, ref.getYaw(), System.currentTimeMillis(), true);
|
||||
data.resetTeleported();
|
||||
data.resetLastDistances();
|
||||
// Prevent further moving processing for nested events.
|
||||
processingEvents.remove(player.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple adjustment after set-back and similar.
|
||||
* @param player
|
||||
* @param loc
|
||||
* @param data
|
||||
* @param cc
|
||||
*/
|
||||
private void adjustLiftOffEnvelope(final Player player, final Location loc, final MovingData data, final MovingConfig cc) {
|
||||
final MoveInfo info = useMoveInfo();
|
||||
info.set(player, loc, null, cc.yOnGround);
|
||||
data.adjustLiftOffEnvelope(info.from);
|
||||
info.cleanup();
|
||||
returnMoveInfo(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Player got a velocity packet. The server can't keep track of actual velocity values (by design), so we have to
|
||||
* try and do that ourselves. Very rough estimates.
|
||||
@ -1320,8 +1341,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
data.resetPositions(loc); // TODO: See above.
|
||||
}
|
||||
|
||||
// Always reset position to this one.
|
||||
// TODO: more fine grained reset?
|
||||
data.resetLastDistances();
|
||||
data.clearMorePacketsData();
|
||||
data.removeAllVelocity();
|
||||
data.resetTrace(loc, tick, cc.traceSize, cc.traceMergeDist); // Might reset to loc instead of set-back ?
|
||||
@ -1332,7 +1352,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
moveInfo.set(player, loc, null, cc.yOnGround);
|
||||
data.toWasReset = moveInfo.from.isOnGroundOrResetCond();
|
||||
if (data.toWasReset && moveInfo.from.isOnGround() && !moveInfo.from.isResetCond()) {
|
||||
data.mediumLiftOff = MediumLiftOff.GROUND;
|
||||
data.liftOffEnvelope = LiftOffEnvelope.NORMAL;
|
||||
}
|
||||
returnMoveInfo(moveInfo);
|
||||
data.fromWasReset = data.toWasReset;
|
||||
@ -1532,8 +1552,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
data.setSetBack(loc);
|
||||
// Give some freedom to allow the "exiting move".
|
||||
data.removeAllVelocity();
|
||||
// TODO: Use-once entries usually are intended to allow one offset, but not jumping/flying on.
|
||||
data.addHorizontalVelocity(new AccountEntry(0.9, 1, 1));
|
||||
data.fakeVerticalFreedom(0.15, 1.2, 1);
|
||||
data.addVerticalVelocity(new SimpleEntry(0.6, 1)); // TODO: Typical margin?
|
||||
useLoc.setWorld(null);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,69 @@
|
||||
package fr.neatmonster.nocheatplus.checks.moving.model;
|
||||
|
||||
/**
|
||||
* Basic preset envelopes for moving off one medium.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public enum LiftOffEnvelope {
|
||||
/** Normal in-air lift off without any restrictions/specialties. */
|
||||
NORMAL(0.42, 1.35, 6, true),
|
||||
/** Weak or no limit moving off liquid near ground. */
|
||||
LIMIT_NEAR_GROUND(0.42, 1.35, 6, false), // TODO: 0.385 / not jump on top of 1 high wall from water.
|
||||
/** Simple calm water surface. */
|
||||
LIMIT_LIQUID(0.1, 0.27, 3, false), // TODO:
|
||||
// /** Flowing water / strong(-est) limit. */
|
||||
// LIMIT_LIQUID_STRONG(...), // TODO
|
||||
/** No jumping at all. */
|
||||
NO_JUMP(0.0, 0.0, 0, false); // TODO
|
||||
;
|
||||
|
||||
private double maxJumpGain;
|
||||
private double maxJumpHeight;
|
||||
private int maxJumpPhase;
|
||||
private boolean jumpEffectApplies;
|
||||
|
||||
private LiftOffEnvelope(double maxJumpGain, double maxJumpHeight, int maxJumpPhase, boolean jumpEffectApplies) {
|
||||
this.maxJumpGain = maxJumpGain;
|
||||
this.maxJumpHeight = maxJumpHeight;
|
||||
this.maxJumpPhase = maxJumpPhase;
|
||||
this.jumpEffectApplies = jumpEffectApplies;
|
||||
}
|
||||
|
||||
public double getMaxJumpGain(double jumpAmplifier) {
|
||||
// TODO: Count in effect level.
|
||||
if (jumpEffectApplies && jumpAmplifier != 0.0) {
|
||||
return Math.max(0.0, maxJumpGain + 0.2 * jumpAmplifier);
|
||||
}
|
||||
else {
|
||||
|
||||
return maxJumpGain;
|
||||
}
|
||||
}
|
||||
|
||||
public double getMaxJumpHeight(double jumpAmplifier) {
|
||||
// TODO: Count in effect level.
|
||||
if (jumpEffectApplies && jumpAmplifier > 0.0) {
|
||||
return 1.35 + 0.6 + jumpAmplifier - 1.0;
|
||||
} // TODO: < 0.0 ?
|
||||
else {
|
||||
|
||||
return maxJumpHeight;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxJumpPhase(double jumpAmplifier) {
|
||||
if (jumpEffectApplies && jumpAmplifier > 0.0) {
|
||||
return (int) Math.round((0.5 + jumpAmplifier) * (double) maxJumpPhase);
|
||||
} // TODO: < 0.0 ?
|
||||
else {
|
||||
return maxJumpPhase;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean jumpEffectApplies() {
|
||||
return jumpEffectApplies;
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package fr.neatmonster.nocheatplus.checks.moving.model;
|
||||
|
||||
/**
|
||||
* This is for tracking what medium a player has been in before lift-off.
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
public enum MediumLiftOff {
|
||||
/** Ordinary ground, normal jumping. */
|
||||
GROUND,
|
||||
/**
|
||||
* Used for reduced jumping height. Until known better this is used for liquids, cobweb.
|
||||
*/
|
||||
LIMIT_JUMP,
|
||||
|
||||
// TODO: Might add NO_JUMP (web?).
|
||||
}
|
@ -180,16 +180,4 @@ public class MovingUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimated lift-off distance for jumping, considering the stored jump
|
||||
* effect.
|
||||
*
|
||||
* @param player
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static double estimateJumpLiftOff(final Player player, final MovingData data, final double add) {
|
||||
return Math.max(0.0, 0.42 + add + 0.2 * data.jumpAmplifier);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ public class FrictionAxisVelocity {
|
||||
* Debugging.
|
||||
* @param builder
|
||||
*/
|
||||
public void AddQueued(StringBuilder builder) {
|
||||
public void addQueued(StringBuilder builder) {
|
||||
addVeloctiy(builder, queued);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple per-axis velocity (positive + negative), only accounting for queuing
|
||||
* and invalidation. Since entries just store values for one time use, no extra
|
||||
* and invalidation. Since entries just wrap values for one time use, no extra
|
||||
* ticking is done, invalidation mechanics and activation count decreasing takes
|
||||
* place in removeInvalid.
|
||||
*
|
||||
@ -15,6 +15,9 @@ import java.util.List;
|
||||
*/
|
||||
public class SimpleAxisVelocity {
|
||||
|
||||
/** Margin for accepting a demanded 0.0 amount, regardless sign. */
|
||||
private static final double marginAcceptZero = 0.005;
|
||||
|
||||
private final List<SimpleEntry> queued = new LinkedList<SimpleEntry>();
|
||||
|
||||
public void add(SimpleEntry entry) {
|
||||
@ -26,51 +29,49 @@ public class SimpleAxisVelocity {
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of the first matching entry. Returns 0.0 if no entry
|
||||
* is available. This will directly invalidate leading entries with the
|
||||
* wrong sign.
|
||||
* Use the next matching entry.
|
||||
*
|
||||
* @param amount
|
||||
* @return
|
||||
* @param tolerance
|
||||
* Allow using entries with less amount (still sign-specific).
|
||||
* Must be equal or greater than 0.0.
|
||||
* @return The first matching entry. Returns null if no entry is available.
|
||||
* This will directly invalidate leading entries with the wrong
|
||||
* sign.
|
||||
*/
|
||||
public double use(final double amount) {
|
||||
public SimpleEntry use(final double amount, final double tolerance) {
|
||||
final Iterator<SimpleEntry> it = queued.iterator();
|
||||
final double absAmount = Math.abs(amount);
|
||||
while (it.hasNext()) {
|
||||
final SimpleEntry entry = it.next();
|
||||
it.remove();
|
||||
if (absAmount <= Math.abs(entry.value)) {
|
||||
if (amount > 0.0 && entry.value > 0.0 || amount < 0.0 && entry.value < 0.0) {
|
||||
// Success. Note that 0.0 Entries are ignored (should not exist anyway).
|
||||
return entry.value;
|
||||
} // else: Wrong sign.
|
||||
} // else: Value too small.
|
||||
if (matchesEntry(entry, amount, tolerance)) {
|
||||
// Success.
|
||||
return entry;
|
||||
}
|
||||
// (Entry can not be used.)
|
||||
// TODO: Note unused velocity.
|
||||
}
|
||||
// None found.
|
||||
return 0.0;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the demanded amount can be covered by this velocity entry. Might
|
||||
* return an entry with a small value with a different sign, if amount is
|
||||
* set to 0.0. Needed also for testing stored entries.
|
||||
*
|
||||
* @return The value of the first matching positive entry. Returns 0.0 if no
|
||||
* entry is available. This will directly invalidate leading entries
|
||||
* with the wrong sign.
|
||||
* @param entry
|
||||
* @param amount
|
||||
* @param tolerance
|
||||
* Allow using entries with less amount (still sign-specific).
|
||||
* Must be equal or greater than 0.0.
|
||||
* @return
|
||||
*/
|
||||
public double usePositive() {
|
||||
return use(Double.MIN_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use any negative amount.
|
||||
*
|
||||
* @return The value of the first matching negative entry. Returns 0.0 if no
|
||||
* entry is available. This will directly invalidate leading entries
|
||||
* with the wrong sign.
|
||||
*/
|
||||
public double useNegative() {
|
||||
return use(-Double.MIN_VALUE);
|
||||
public boolean matchesEntry(final SimpleEntry entry, final double amount, final double tolerance) {
|
||||
return Math.abs(amount) <= Math.abs(entry.value) + tolerance &&
|
||||
(amount > 0.0 && entry.value > 0.0 && amount <= entry.value + tolerance
|
||||
|| amount < 0.0 && entry.value < 0.0 && entry.value - tolerance <= amount
|
||||
|| amount == 0.0 && Math.abs(entry.value) <= marginAcceptZero);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,6 +79,7 @@ public class SimpleAxisVelocity {
|
||||
* @param tick
|
||||
*/
|
||||
public void removeInvalid(final int tick) {
|
||||
// Note: clear invalidated here, append unused to invalidated.
|
||||
final Iterator<SimpleEntry> it = queued.iterator();
|
||||
while (it.hasNext()) {
|
||||
final SimpleEntry entry = it.next();
|
||||
@ -92,4 +94,15 @@ public class SimpleAxisVelocity {
|
||||
queued.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging.
|
||||
* @param builder
|
||||
*/
|
||||
public void addQueued(final StringBuilder builder) {
|
||||
for (final SimpleEntry vel: queued) {
|
||||
builder.append(" ");
|
||||
builder.append(vel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,13 +20,13 @@ public class SimpleEntry {
|
||||
|
||||
// TODO: Add more conditions (max tick, real time ?)
|
||||
|
||||
public SimpleEntry(double value, int actCount, int valCount){
|
||||
public SimpleEntry(double value, int actCount){
|
||||
this.tick = TickTask.getTick();
|
||||
this.value = value;
|
||||
this.actCount = actCount;
|
||||
}
|
||||
|
||||
public SimpleEntry(int tick, double value, int actCount, int valCount){
|
||||
public SimpleEntry(int tick, double value, int actCount){
|
||||
this.tick = tick;
|
||||
this.value = value;
|
||||
this.actCount = actCount;
|
||||
|
@ -1302,17 +1302,39 @@ public class BlockProperties {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Checks for lava and water and only stationary. Long outdated. Subject to removal.
|
||||
* @param blockId
|
||||
* Simple checking method, heavy. No isIllegal check.
|
||||
* @param player
|
||||
* @param location
|
||||
* @param yOnGround
|
||||
* @return
|
||||
*/
|
||||
public static boolean isInWater(final int blockId) {
|
||||
if (blockId == Material.STATIONARY_WATER.getId() || blockId == Material.STATIONARY_LAVA.getId()) {
|
||||
return true;
|
||||
}
|
||||
// TODO: count in water height ?
|
||||
// TODO: lava ?
|
||||
return false;
|
||||
public static boolean isInLiquid(final Player player, final Location location, final double yOnGround) {
|
||||
// Bit fat workaround, maybe put the object through from check listener ?
|
||||
blockCache.setAccess(location.getWorld());
|
||||
pLoc.setBlockCache(blockCache);
|
||||
pLoc.set(location, player, yOnGround);
|
||||
final boolean res = pLoc.isInLiquid();
|
||||
blockCache.cleanup();
|
||||
pLoc.cleanup();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple checking method, heavy. No isIllegal check.
|
||||
* @param player
|
||||
* @param location
|
||||
* @param yOnGround
|
||||
* @return
|
||||
*/
|
||||
public static boolean isInWeb(final Player player, final Location location, final double yOnGround) {
|
||||
// Bit fat workaround, maybe put the object through from check listener ?
|
||||
blockCache.setAccess(location.getWorld());
|
||||
pLoc.setBlockCache(blockCache);
|
||||
pLoc.set(location, player, yOnGround);
|
||||
final boolean res = pLoc.isInWeb();
|
||||
blockCache.cleanup();
|
||||
pLoc.cleanup();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1327,10 +1349,10 @@ public class BlockProperties {
|
||||
blockCache.setAccess(location.getWorld());
|
||||
pLoc.setBlockCache(blockCache);
|
||||
pLoc.set(location, player, yOnGround);
|
||||
final boolean onGround = pLoc.isOnGround();
|
||||
final boolean res = pLoc.isOnGround();
|
||||
blockCache.cleanup();
|
||||
pLoc.cleanup();
|
||||
return onGround;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -665,6 +665,17 @@ public class PlayerLocation {
|
||||
return BlockProperties.collides(blockCache, x - width, y + eyeHeight, z - width, x + width, y + eyeHeight + marginAboveHeight, z + width, BlockProperties.F_GROUND | BlockProperties.F_SOLID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if something solid/ground-like collides within a default margin
|
||||
* above the height of the player. Margin is yOnGround + max(0.0, 2.0 - eyeHeight).
|
||||
*
|
||||
* @param marginAboveHeight
|
||||
* @return
|
||||
*/
|
||||
public boolean isHeadObstructed() {
|
||||
return isHeadObstructed(Math.max(0.0, 2.0 - eyeHeight) + yOnGround);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for testing for either.
|
||||
* @return
|
||||
@ -953,4 +964,19 @@ public class PlayerLocation {
|
||||
this.blockFlags = other.blockFlags; // Assume set.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder(128);
|
||||
builder.append("PlayerLocation(");
|
||||
builder.append(world == null ? "null" : world.getName());
|
||||
builder.append('/');
|
||||
builder.append(Double.toString(x));
|
||||
builder.append(", ");
|
||||
builder.append(Double.toString(y));
|
||||
builder.append(", ");
|
||||
builder.append(Double.toString(z));
|
||||
builder.append(')');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,11 +10,11 @@ public class NoobsTest {
|
||||
public void testSmallDoubles() {
|
||||
double x;
|
||||
x = Double.MIN_VALUE;
|
||||
if (x <= 0.0) {
|
||||
if (x <= 0.0 || !(x > 0.0)) {
|
||||
fail("noob");
|
||||
}
|
||||
x = -Double.MIN_VALUE;
|
||||
if (x >= 0.0) {
|
||||
if (x >= 0.0 || !(x < 0.0)) {
|
||||
fail("noob");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user