From 6de231ac5424c0df8a2757ba16f784b5493f0f45 Mon Sep 17 00:00:00 2001 From: asofold Date: Fri, 9 Dec 2016 23:50:34 +0100 Subject: [PATCH] [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 --- .../nocheatplus/checks/moving/MovingData.java | 9 + .../checks/moving/MovingListener.java | 242 ++++++++++++++++-- .../checks/moving/magic/Magic.java | 6 + .../checks/moving/player/SurvivalFly.java | 90 ++++--- .../moving/velocity/SimpleAxisVelocity.java | 28 +- .../location/RichBoundsLocation.java | 5 +- 6 files changed, 316 insertions(+), 64 deletions(-) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java index e6c0c5aa..99419331 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java @@ -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); } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java index 623df56b..c3b966aa 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java @@ -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.
* * @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)); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/magic/Magic.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/magic/Magic.java index 26ec5359..cdf0a74c 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/magic/Magic.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/magic/Magic.java @@ -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; /** diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java index 820d6611..f25689e3 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java @@ -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; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/velocity/SimpleAxisVelocity.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/velocity/SimpleAxisVelocity.java index ade45da6..d11604b8 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/velocity/SimpleAxisVelocity.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/velocity/SimpleAxisVelocity.java @@ -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 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 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) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java index b7249ec9..49ba1d7e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java @@ -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?