Improvements on piston + slime block.

Issues with bunny hopping remain (...), but this might help with
elevators.

Use of velocity entries has been made more strict (directly use, remove
previously queued ones).
This commit is contained in:
asofold 2018-02-05 23:13:06 +01:00
parent c45a31b979
commit 3c9809817a
6 changed files with 133 additions and 45 deletions

View File

@ -14,6 +14,7 @@
*/
package fr.neatmonster.nocheatplus.checks.moving;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@ -43,6 +44,7 @@ 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.checks.moving.velocity.VelocityFlags;
import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeReference;
import fr.neatmonster.nocheatplus.components.data.ICanHandleTimeRunningBackwards;
@ -1303,13 +1305,41 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
* @return If the velocity jump phase is still active (sfDirty).
*/
public boolean resetVelocityJumpPhase() {
if (horVel.hasActive() || horVel.hasQueued()) {
return resetVelocityJumpPhase(null);
}
/**
* See {@link #resetVelocityJumpPhase()}.
* @param tags
* @return
*/
public boolean resetVelocityJumpPhase(final Collection<String> tags) {
if (horVel.hasActive() || horVel.hasQueued()
|| sfDirty && shouldRetainSFDirty(tags)) {
// TODO: What with vertical ?
sfDirty = true;
} else {
sfDirty = false;
return sfDirty = true;
}
return sfDirty;
else {
return sfDirty = false;
}
}
private final boolean shouldRetainSFDirty(final Collection<String> tags) {
final PlayerMoveData thisMove = playerMoves.getLatestValidMove();
if (thisMove == null || !thisMove.toIsValid || thisMove.yDistance >= 0.0) {
final SimpleEntry entry = verVel.peek(
thisMove == null ? 0.05 : thisMove.yDistance, 0, 4, 0.0);
if (entry != null && entry.hasFlag(VelocityFlags.ORIGIN_BLOCK_BOUNCE)
|| thisMove != null && thisMove.verVelUsed != null
&& thisMove.verVelUsed.hasFlag(VelocityFlags.ORIGIN_BLOCK_BOUNCE)) {
// TODO: Strictly, pastground_from/to should rather be skipped instead of this.
if (tags != null) {
tags.add("retain_dirty_bounce"); // +- block/push
}
return true;
}
}
return false;
}
/**
@ -1380,4 +1410,12 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
return playerMoveCount - morePacketsSetBackResetTime;
}
/**
* Remove from start while the flag is present.
* @param originBlockBounce
*/
public void removeLeadingQueuedVerticalVelocityByFlag(final long flag) {
verVel.removeLeadingQueuedVerticalVelocityByFlag(flag);
}
}

View File

@ -718,7 +718,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// TODO: Mixed ground (e.g. slime blocks + slabs), specifically on pushing.
// TODO: More on fall damage. What with sneaking + past states?
// TODO: With past states: What does jump effects do here?
if (to.getY() < from.getY()) {
if (thisMove.yDistance < 0.0) {
// 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).
@ -737,9 +737,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
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) {
if (checkPastStateBounceDescend(player, pFrom, pTo, thisMove, lastMove,
tick, data, cc) != BounceType.NO_BOUNCE) {
// Not set verticalBounce, as this is ascending and it's already force used.
checkNf = false; // Skip NoFall.
}
}
@ -754,9 +754,12 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|| useBlockChangeTracker
// 0-dist moves count in: && thisMove.yDistance >= 0.415
&& thisMove.yDistance <= 1.515 // TODO: MAGIC
&& checkPastStateBounceAscend(player, pFrom, pTo, thisMove, lastMove, tick, data, cc)
) {
checkNf = false;
verticalBounce = checkPastStateBounceAscend(player, pFrom, pTo,
thisMove, lastMove, tick, data, cc);
if (verticalBounce != BounceType.NO_BOUNCE) {
checkNf = false;
}
}
}
}
@ -1018,10 +1021,11 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
return BounceType.NO_BOUNCE; // Nothing found, return no bounce.
}
private boolean checkPastStateBounceAscend(
private BounceType 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: More preconditions.
// TODO: Nail down to more precise side conditions for larger jumps, if possible.
final UUID worldId = from.getWorld().getUID();
@ -1037,15 +1041,19 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Center push.
entryBelowY_POS != null
// Off center push.
|| thisMove.yDistance < 1.015 && from.matchBlockChangeMatchResultingFlags(blockChangeTracker,
|| thisMove.yDistance < 1.515 && from.matchBlockChangeMatchResultingFlags(blockChangeTracker,
data.blockChangeRef, Direction.Y_POS, Math.min(.415, thisMove.yDistance),
BlockProperties.F_BOUNCE25)
) {
if (data.debug) {
debug(player, "Direct block push with bounce (" + (entryBelowY_POS == null ? "off_center)." : "center)."));
}
amount = Math.min(Math.max(0.505, 1.0 + (double) from.getBlockY() - from.getY() + 1.515),
2.525); // TODO: EXACT MAGIC.
amount = Math.min(
Math.max(0.505, 1.0 + (double) from.getBlockY() - from.getY() + 1.515),
2.525);
/*
* TODO: EXACT MAGIC.
*/
if (entryBelowY_POS != null) {
data.blockChangeRef.updateSpan(entryBelowY_POS);
}
@ -1080,39 +1088,41 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
* TODO: USE EXISTING velocity with bounce flag set first, then peek
* / add. (might while peek -> has bounce flag: remove velocity)
*/
if (data.peekVerticalVelocity(amount, 2, 3) == null) {
/*
* TODO: Concepts for limiting... max amount based on side
* conditions such as block height+1.5, max coordinate, max
* amount per use, ALLOW_ZERO flag/boolean and set in
* constructor, demand max. 1 zero dist during validity. Bind
* use to initial xz coordinates... Too precise = better with
* past move tracking, or a sub-class of SimpleEntry with better
* access signatures including thisMove.
*/
/*
* TODO: Also account for current yDistance here? E.g. Add two
* entries, split based on current yDistance?
*/
final SimpleEntry vel = new SimpleEntry(tick, amount, FLAGS_VELOCITY_BOUNCE_BLOCK_MOVE_ASCEND, 4);
data.verticalBounce = vel;
data.useVerticalBounce(player);
if (data.debug) {
debug(player, "checkPastStateBounceAscend: add velocity: " + vel);
}
return true;
}
else if (data.debug) {
debug(player, "checkPastStateBounceAscend: Don't add velocity.");
data.removeLeadingQueuedVerticalVelocityByFlag(VelocityFlags.ORIGIN_BLOCK_BOUNCE);
/*
* TODO: Concepts for limiting... max amount based on side
* conditions such as block height+1.5, max coordinate, max
* amount per use, ALLOW_ZERO flag/boolean and set in
* constructor, demand max. 1 zero dist during validity. Bind
* use to initial xz coordinates... Too precise = better with
* past move tracking, or a sub-class of SimpleEntry with better
* access signatures including thisMove.
*/
/*
* TODO: Also account for current yDistance here? E.g. Add two
* entries, split based on current yDistance?
*/
final SimpleEntry vel = new SimpleEntry(tick, amount,
FLAGS_VELOCITY_BOUNCE_BLOCK_MOVE_ASCEND, 4);
data.verticalBounce = vel;
data.useVerticalBounce(player);
data.useVerticalVelocity(thisMove.yDistance);
//if (thisMove.yDistance > 0.42) {
// data.setFrictionJumpPhase();
//}
if (data.debug) {
debug(player, "checkPastStateBounceAscend: set velocity: " + vel);
}
// TODO: Exact type to return.
return BounceType.STATIC_PAST_AND_PUSH;
}
// TODO: There is a special case with 1.0 up on pistons pushing horizontal only (!).
return false;
return BounceType.NO_BOUNCE;
}
/**
* Pre conditions: A slime block is underneath and the player isn't really
* sneaking.<br>
* sneaking. This does not account for pistons pushing (slime) blocks.<br>
*
* @param player
* @param from

View File

@ -135,4 +135,20 @@ public class MoveTrace <MD extends MoveData> {
currentMove.invalidate();
}
/**
* Return the current move if valid, or the first past move if valid, or
* null if neither is valid.
*
* @return
*/
public MD getLatestValidMove() {
if (currentMove.valid) {
return currentMove;
}
else {
final MD move = pastMoves.get(0);
return move.valid ? move : null;
}
}
}

View File

@ -249,7 +249,7 @@ public class SurvivalFly extends Check {
if (!thisMove.from.onGround && !thisMove.to.onGround) {
// Lost ground workaround has just been applied, check resetting of the dirty flag.
// TODO: Always/never reset with any ground touched?
data.resetVelocityJumpPhase();
data.resetVelocityJumpPhase(tags);
}
else if (multiMoveCount == 0 && thisMove.from.onGround && !lastMove.touchedGround
&& TrigUtil.isSamePosAndLook(thisMove.from, lastMove.to)) {
@ -262,7 +262,7 @@ public class SurvivalFly extends Check {
}
// Renew the "dirty"-flag (in-air phase affected by velocity).
if (data.isVelocityJumpPhase() || data.resetVelocityJumpPhase()) {
if (data.isVelocityJumpPhase() || data.resetVelocityJumpPhase(tags)) {
// (Reset is done after checks run.)
tags.add("dirty");
}
@ -611,7 +611,7 @@ public class SurvivalFly extends Check {
data.sfLowJump = false;
}
if (hFreedom <= 0.0 && thisMove.verVelUsed == null) {
data.resetVelocityJumpPhase();
data.resetVelocityJumpPhase(tags);
}
}
else if (resetFrom) {
@ -623,7 +623,7 @@ public class SurvivalFly extends Check {
// not resetting nolowjump (?)...
// Don't reset velocity phase unless moving into resetcond.
// if (hFreedom <= 0.0 && data.verVelUsed == null && (!data.noFallAssumeGround || fromOnGround)) {
// data.resetVelocityJumpPhase();
// data.resetVelocityJumpPhase(tags);
// }
}
else {

View File

@ -265,4 +265,24 @@ public class SimpleAxisVelocity {
this.unusedActive = unusedActive;
}
/**
* Remove from start while the flag is present.
* @param originBlockBounce
*/
public void removeLeadingQueuedVerticalVelocityByFlag(final long flag) {
if (queued.isEmpty()) {
return;
}
final Iterator<SimpleEntry> it = queued.iterator();
while (it.hasNext()) {
final SimpleEntry entry = it.next();
if (entry.hasFlag(flag)) {
it.remove();
}
else {
break;
}
}
}
}

View File

@ -59,6 +59,10 @@ public class SimpleEntry {
this.flags = flags;
}
public boolean hasFlag(final long flag) {
return (flags & flag) == flag;
}
public String toString() {
return "SimpleEntry(tick=" + tick + " value=" + value + " flags=" + flags + " activate=" + actCount + "/" + initialActCount + ")";
}