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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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