Search in sources :

Example 21 with PlayerMoveData

use of fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData in project NoCheatPlus by NoCheatPlus.

the class LostGround method lostGroundDescend.

/**
 * Check if a ground-touch has been lost due to event-sending-frequency or
 * other reasons.<br>
 * This is for descending "mildly" only (-0.5 <= yDistance <= 0). Needs last
 * move data.
 *
 * @param player
 * @param from
 * @param to
 * @param hDistance
 * @param yDistance
 * @param sprinting
 * @param data
 * @param cc
 * @return
 */
private static boolean lostGroundDescend(final Player player, final PlayerLocation from, final PlayerLocation to, final double hDistance, final double yDistance, final boolean sprinting, final PlayerMoveData lastMove, final MovingData data, final MovingConfig cc, final Collection<String> tags) {
    // TODO: re-organize for faster exclusions (hDistance, yDistance).
    // TODO: more strict conditions
    final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
    final double setBackYDistance = to.getY() - data.getSetBackY();
    // Note: checking loc should make sense, rather if loc is higher than from?
    if (yDistance < 0.0 && !to.isOnGround() && from.isOnGround(from.getY() - to.getY() + 0.001)) {
        // Test for passability of the entire box, roughly from feet downwards.
        // TODO: Efficiency with Location instances.
        // TODO: Full bounds check (!).
        final Location ref = from.getLocation();
        ref.setY(to.getY());
        // TODO: passable test is obsolete with PassableAxisTracing.
        if (Passable.isPassable(from.getLocation(), ref)) {
            // TODO: Needs new model (store detailed on-ground properties).
            return applyLostGround(player, from, false, thisMove, data, "vcollide", tags);
        }
    }
    if (!lastMove.toIsValid) {
        return false;
    }
    if (data.sfJumpPhase <= 7) {
        // Check for sprinting down blocks etc.
        if (lastMove.yDistance <= yDistance && setBackYDistance < 0 && !to.isOnGround()) {
            // TODO: account for half steps !?
            if (from.isOnGround(0.6, 0.4, 0.0, 0L)) {
                // TODO: Seems to virtually always be preceded by a "vcollide" move.
                return applyLostGround(player, from, true, thisMove, data, "pyramid", tags);
            }
        }
        // Check for jumping up strange blocks like flower pots on top of other blocks.
        if (yDistance == 0.0 && lastMove.yDistance > 0.0 && lastMove.yDistance < 0.25 && data.sfJumpPhase <= Math.max(0, 6 + data.jumpAmplifier * 3.0) && setBackYDistance > 1.0 && setBackYDistance < Math.max(0.0, 1.5 + 0.2 * data.jumpAmplifier) && !to.isOnGround()) {
            // TODO: confine by block types ?
            if (from.isOnGround(0.25, 0.4, 0, 0L)) {
                // data.sfThisAllowBunny = true;
                return applyLostGround(player, from, true, thisMove, data, "ministep", tags);
            }
        }
    }
    // Lost ground while falling onto/over edges of blocks.
    if (yDistance < 0 && hDistance <= 1.5 && lastMove.yDistance < 0.0 && yDistance > lastMove.yDistance && !to.isOnGround()) {
        // if (to.isOnGround(0.5) || from.isOnGround(0.5)) {
        if (from.isOnGround(0.5, 0.2, 0) || to.isOnGround(0.5, Math.min(0.2, 0.01 + hDistance), Math.min(0.1, 0.01 + -yDistance))) {
            return applyLostGround(player, from, true, thisMove, data, "edgedesc", tags);
        }
    }
    // Nothing found.
    return false;
}
Also used : PlayerMoveData(fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData) PlayerLocation(fr.neatmonster.nocheatplus.utilities.location.PlayerLocation) Location(org.bukkit.Location)

Example 22 with PlayerMoveData

use of fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData in project NoCheatPlus by NoCheatPlus.

the class FightListener method handleNormalDamage.

/**
 * A player attacked something with DamageCause ENTITY_ATTACK.
 *
 * @param player
 *            The attacking player.
 * @param damaged
 * @param originalDamage
 *            Damage before applying modifiers.
 * @param finalDamage
 *            Damage after applying modifiers.
 * @param tick
 * @param data
 * @return
 */
private boolean handleNormalDamage(final Player player, final boolean attackerIsFake, final Entity damaged, final boolean damagedIsFake, final double originalDamage, final double finalDamage, final int tick, final FightData data, final IPlayerData pData, final IPenaltyList penaltyList) {
    final FightConfig cc = pData.getGenericInstance(FightConfig.class);
    // Illegal enchantments hotfix check.
    if (Items.checkIllegalEnchantmentsAllHands(player, pData)) {
        return true;
    }
    final boolean debug = pData.isDebugActive(checkType);
    boolean cancelled = false;
    final String worldName = player.getWorld().getName();
    final long now = System.currentTimeMillis();
    final boolean worldChanged = !worldName.equals(data.lastWorld);
    final Location loc = player.getLocation(useLoc1);
    // // Bad pitch/yaw, just in case.
    // if (LocUtil.needsDirectionCorrection(useLoc1.getYaw(), useLoc1.getPitch())) {
    // mcAccess.correctDirection(player);
    // player.getLocation(useLoc1);
    // }
    final Location damagedLoc = damaged.getLocation(useLoc2);
    // final double targetDist = CheckUtils.distance(loc, targetLoc); // TODO: Calculate distance as is done in fight.reach !
    final double targetMove;
    final int tickAge;
    // Milliseconds the ticks actually took.
    final long msAge;
    // Blocks per second.
    final double normalizedMove;
    // TODO: Use trace for this ?
    if (data.lastAttackedX == Double.MAX_VALUE || tick < data.lastAttackTick || worldChanged || tick - data.lastAttackTick > 20) {
        // TODO: 20 ?
        tickAge = 0;
        targetMove = 0.0;
        normalizedMove = 0.0;
        msAge = 0;
    } else {
        tickAge = tick - data.lastAttackTick;
        // TODO: Maybe use 3d distance if dy(normalized) is too big.
        targetMove = TrigUtil.distance(data.lastAttackedX, data.lastAttackedZ, damagedLoc.getX(), damagedLoc.getZ());
        msAge = (long) (50f * TickTask.getLag(50L * tickAge, true) * (float) tickAge);
        normalizedMove = msAge == 0 ? targetMove : targetMove * Math.min(20.0, 1000.0 / (double) msAge);
    }
    // TODO: calculate factor for dists: ticks * 50 * lag
    // TODO: dist < width => skip some checks (direction, ..)
    final LocationTrace damagedTrace;
    final Player damagedPlayer;
    if (damaged instanceof Player) {
        damagedPlayer = (Player) damaged;
        // Log.
        if (debug && DataManager.getPlayerData(damagedPlayer).hasPermission(Permissions.ADMINISTRATION_DEBUG, damagedPlayer)) {
            damagedPlayer.sendMessage("Attacked by " + player.getName() + ": inv=" + mcAccess.getHandle().getInvulnerableTicks(damagedPlayer) + " ndt=" + damagedPlayer.getNoDamageTicks());
        }
        // Check for self hit exploits (mind that projectiles are excluded from this.)
        if (selfHit.isEnabled(player, pData) && selfHit.check(player, damagedPlayer, data, cc)) {
            cancelled = true;
        }
        // Get+update the damaged players.
        // TODO: Problem with NPCs: data stays (not a big problem).
        // (This is done even if the event has already been cancelled, to keep track, if the player is on a horse.)
        damagedTrace = DataManager.getPlayerData(damagedPlayer).getGenericInstance(MovingData.class).updateTrace(damagedPlayer, damagedLoc, tick, damagedIsFake ? null : mcAccess.getHandle());
    } else {
        // TODO: This is a temporary workaround.
        damagedPlayer = null;
        // Use a fake trace.
        // TODO: Provide for entities too? E.g. one per player, or a fully fledged bookkeeping thing (EntityData).
        // final MovingConfig mcc = MovingConfig.getConfig(damagedLoc.getWorld().getName());
        // new LocationTrace(mcc.traceSize, mcc.traceMergeDist);
        damagedTrace = null;
    // damagedTrace.addEntry(tick, damagedLoc.getX(), damagedLoc.getY(), damagedLoc.getZ());
    }
    // Log generic properties of this attack.
    if (debug) {
        debug(player, "Attacks " + (damagedPlayer == null ? ("entity " + damaged.getType()) : ("player" + damagedPlayer.getName())) + " damage=" + (finalDamage == originalDamage ? finalDamage : (originalDamage + "/" + finalDamage)));
    }
    // Can't fight dead.
    if (cc.cancelDead) {
        if (damaged.isDead()) {
            cancelled = true;
        }
        // Only allow damaging others if taken damage this tick.
        if (player.isDead() && data.damageTakenByEntityTick != TickTask.getTick()) {
            cancelled = true;
        }
    }
    // LEGACY: 1.9: sweep attack.
    if (BridgeHealth.DAMAGE_SWEEP == null) {
        // TODO: Account for charge/meter thing?
        final int locHashCode = LocUtil.hashCode(loc);
        if (originalDamage == 1.0) {
            // Might be a sweep attack.
            if (tick == data.sweepTick && locHashCode == data.sweepLocationHashCode) {
                // Could further guard by checking equality of loc to last location.
                if (debug) {
                    debug(player, "(Assume sweep attack follow up damage.)");
                }
                return cancelled;
            }
        } else {
            // TODO: More side conditions for a sweep attack.
            data.sweepTick = tick;
            data.sweepLocationHashCode = locHashCode;
        }
    }
    // LEGACY: thorns.
    if (BridgeHealth.DAMAGE_THORNS == null && originalDamage <= 4.0 && tick == data.damageTakenByEntityTick && data.thornsId != Integer.MIN_VALUE && data.thornsId == damaged.getEntityId()) {
        // Don't handle further, but do respect selfhit/canceldead.
        // TODO: Remove soon, at least version-dependent.
        data.thornsId = Integer.MIN_VALUE;
        return cancelled;
    } else {
        data.thornsId = Integer.MIN_VALUE;
    }
    // TODO: Add something on packet level already.
    if (pData.isCheckActive(CheckType.FIGHT_WRONGTURN, player) && wrongTurn.check(player, loc, data, cc)) {
        cancelled = true;
    }
    // Run through the main checks.
    if (!cancelled && speed.isEnabled(player, pData)) {
        if (speed.check(player, now, data, cc, pData)) {
            cancelled = true;
            // Still feed the improbable.
            if (data.speedVL > 50) {
                Improbable.check(player, 2f, now, "fight.speed", pData);
            } else {
                Improbable.feed(player, 2f, now);
            }
        } else if (normalizedMove > 2.0 && Improbable.check(player, 1f, now, "fight.speed", pData)) {
            // Feed improbable in case of ok-moves too.
            // TODO: consider only feeding if attacking with higher average speed (!)
            cancelled = true;
        }
    }
    if (!cancelled && critical.isEnabled(player, pData) && critical.check(player, loc, data, cc, pData, penaltyList)) {
        // TODO: Check config for settings.
        cancelled = true;
    }
    if (!cancelled && noSwing.isEnabled(player, pData) && noSwing.check(player, data, cc)) {
        cancelled = true;
    }
    if (!cancelled && player.isBlocking() && !pData.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING, player)) {
        // TODO: Permission ?
        cancelled = true;
    }
    if (!cancelled) {
        final boolean reachEnabled = reach.isEnabled(player, pData);
        final boolean directionEnabled = direction.isEnabled(player, pData);
        if (reachEnabled || directionEnabled) {
            if (damagedTrace != null) {
                // Checks that use the LocationTrace instance of the attacked entity/player.
                cancelled = locationTraceChecks(player, loc, data, cc, pData, damaged, damagedIsFake, damagedLoc, damagedTrace, tick, now, debug, reachEnabled, directionEnabled);
            } else {
                // Still use the classic methods for non-players. maybe[]
                if (reachEnabled && reach.check(player, loc, damaged, damagedIsFake, damagedLoc, data, cc, pData)) {
                    cancelled = true;
                }
                if (directionEnabled && direction.check(player, loc, damaged, damagedIsFake, damagedLoc, data, cc)) {
                    cancelled = true;
                }
            }
        }
    }
    // TODO: Actual angle needs to be related to the best matching trace element(s) (loop checks).
    if (angle.isEnabled(player, pData)) {
        // Improbable yaw changing: Moving events might be missing up to a ten degrees change.
        if (Combined.checkYawRate(player, loc.getYaw(), now, worldName, pData.isCheckActive(CheckType.COMBINED_YAWRATE, player), pData)) {
            // (Check or just feed).
            // TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then).
            cancelled = true;
        }
        // Angle check.
        if (angle.check(player, loc, damaged, worldChanged, data, cc)) {
            if (!cancelled && debug) {
                debug(player, "FIGHT_ANGLE cancel without yawrate cancel.");
            }
            cancelled = true;
        }
    }
    // Set values.
    data.lastWorld = worldName;
    data.lastAttackTick = tick;
    data.lastAttackedX = damagedLoc.getX();
    data.lastAttackedY = damagedLoc.getY();
    data.lastAttackedZ = damagedLoc.getZ();
    // TODO: Evaluate if moving traces can help here.
    if (!cancelled && TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) < 4.5) {
        final MovingData mData = pData.getGenericInstance(MovingData.class);
        // Check if fly checks is an issue at all, re-check "real sprinting".
        final PlayerMoveData lastMove = mData.playerMoves.getFirstPastMove();
        if (lastMove.valid && mData.liftOffEnvelope == LiftOffEnvelope.NORMAL) {
            final double hDist = TrigUtil.xzDistance(loc, lastMove.from);
            if (hDist >= 0.23) {
                // TODO: Might need to check hDist relative to speed / modifiers.
                final MovingConfig mCc = pData.getGenericInstance(MovingConfig.class);
                final PlayerMoveInfo moveInfo = auxMoving.usePlayerMoveInfo();
                moveInfo.set(player, loc, null, mCc.yOnGround);
                if (now <= mData.timeSprinting + mCc.sprintingGrace && MovingUtil.shouldCheckSurvivalFly(player, moveInfo.from, mData, mCc, pData)) {
                    // Judge as "lost sprint" problem.
                    // TODO: What would mData.lostSprintCount > 0  mean here?
                    mData.lostSprintCount = 7;
                    if ((debug || pData.isDebugActive(CheckType.MOVING)) && BuildParameters.debugLevel > 0) {
                        debug(player, "lostsprint: hDist to last from: " + hDist + " | targetdist=" + TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) + " | sprinting=" + player.isSprinting() + " | food=" + player.getFoodLevel() + " | hbuf=" + mData.sfHorizontalBuffer);
                    }
                }
                auxMoving.returnPlayerMoveInfo(moveInfo);
            }
        }
    }
    // (Cancel after sprinting hacks, because of potential fp).
    if (!cancelled && data.attackPenalty.isPenalty(now)) {
        cancelled = true;
        if (debug) {
            debug(player, "~ attack penalty.");
        }
    }
    // Cleanup.
    useLoc1.setWorld(null);
    useLoc2.setWorld(null);
    return cancelled;
}
Also used : Player(org.bukkit.entity.Player) MovingData(fr.neatmonster.nocheatplus.checks.moving.MovingData) PlayerMoveInfo(fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveInfo) PlayerMoveData(fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData) MovingConfig(fr.neatmonster.nocheatplus.checks.moving.MovingConfig) LocationTrace(fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace) Location(org.bukkit.Location)

Example 23 with PlayerMoveData

use of fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData in project NoCheatPlus by NoCheatPlus.

the class CreativeFly method check.

/**
 * @param player
 * @param from
 * @param to
 * @param data
 * @param cc
 * @param time Milliseconds.
 * @return
 */
public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc, final IPlayerData pData, final long time, final int tick, final boolean useBlockChangeTracker) {
    // Reset tags, just in case.
    tags.clear();
    final boolean debug = pData.isDebugActive(type);
    // Some edge data for this move.
    final GameMode gameMode = player.getGameMode();
    final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
    // if (!data.thisMove.from.extraPropertiesValid) {
    // // TODO: Confine by model config flag or just always do [if the latter: do it in the listener]?
    // data.thisMove.setExtraProperties(from, to);
    // }
    final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove();
    final ModelFlying model = thisMove.modelFlying;
    // Proactive reset of elytraBoost (MC 1.11.2).
    if (data.fireworksBoostDuration > 0) {
        if (!lastMove.valid || lastMove.flyCheck != CheckType.MOVING_CREATIVEFLY || lastMove.modelFlying != model || data.fireworksBoostTickExpire < tick) {
            data.fireworksBoostDuration = 0;
        } else {
            data.fireworksBoostDuration--;
        }
    }
    // Calculate some distances.
    final double yDistance = thisMove.yDistance;
    final double hDistance = thisMove.hDistance;
    final boolean flying = gameMode == BridgeMisc.GAME_MODE_SPECTATOR || player.isFlying();
    final boolean sprinting = time <= data.timeSprinting + cc.sprintingGrace;
    // Lost ground, if set so.
    if (model.getGround()) {
        MovingUtil.prepareFullCheck(from, to, thisMove, Math.max(cc.yOnGround, cc.noFallyOnGround));
        if (!thisMove.from.onGroundOrResetCond) {
            if (from.isSamePos(to)) {
                if (// Copy and paste from sf.
                lastMove.toIsValid && lastMove.hDistance > 0.0 && lastMove.yDistance < -0.3 && LostGround.lostGroundStill(player, from, to, hDistance, yDistance, sprinting, lastMove, data, cc, tags)) {
                // Nothing to do.
                }
            } else if (LostGround.lostGround(player, from, to, hDistance, yDistance, sprinting, lastMove, data, cc, useBlockChangeTracker ? blockChangeTracker : null, tags)) {
            // Nothing to do.
            }
        }
    }
    // Horizontal distance check.
    double[] resH = hDist(player, from, to, hDistance, yDistance, sprinting, flying, lastMove, time, model, data, cc);
    double limitH = resH[0];
    double resultH = resH[1];
    // Check velocity.
    if (resultH > 0) {
        double hFreedom = data.getHorizontalFreedom();
        if (hFreedom < resultH) {
            // Use queued velocity if possible.
            hFreedom += data.useHorizontalVelocity(resultH - hFreedom);
        }
        if (hFreedom > 0.0) {
            resultH = Math.max(0.0, resultH - hFreedom);
            if (resultH <= 0.0) {
                limitH = hDistance;
            }
            tags.add("hvel");
        }
    } else {
        // TODO: test/check !
        data.clearActiveHorVel();
    }
    // Normalize to % of a block.
    resultH *= 100.0;
    if (resultH > 0.0) {
        tags.add("hdist");
    }
    // Vertical move.
    // Limit.
    double limitV = 0.0;
    // Violation (normalized to 100 * 1 block, applies if > 0.0).
    double resultV = 0.0;
    // Distinguish checking method by y-direction of the move.
    if (yDistance > 0.0) {
        // Ascend.
        double[] res = vDistAscend(from, to, yDistance, flying, thisMove, lastMove, model, data, cc);
        resultV = Math.max(resultV, res[1]);
        limitV = res[0];
    } else if (yDistance < 0.0) {
        // Descend.
        double[] res = vDistDescend(from, to, yDistance, flying, lastMove, model, data, cc);
        resultV = Math.max(resultV, res[1]);
        limitV = res[0];
    } else {
        // Keep altitude.
        double[] res = vDistZero(from, to, yDistance, flying, lastMove, model, data, cc);
        resultV = Math.max(resultV, res[1]);
        limitV = res[0];
    }
    // Velocity.
    if (resultV > 0.0 && data.getOrUseVerticalVelocity(yDistance) != null) {
        resultV = 0.0;
        tags.add("vvel");
    }
    // Add tag for maximum height check (silent set back).
    final double maximumHeight = model.getMaxHeight() + player.getWorld().getMaxHeight();
    if (to.getY() > maximumHeight) {
        // TODO: Allow use velocity there (would need a flag to signal the actual check below)?
        tags.add("maxheight");
    }
    // Normalize to % of a block.
    resultV *= 100.0;
    if (resultV > 0.0) {
        tags.add("vdist");
    }
    final double result = Math.max(0.0, resultH) + Math.max(0.0, resultV);
    if (debug) {
        outpuDebugMove(player, hDistance, limitH, yDistance, limitV, model, tags, data);
    }
    // Violation handling.
    // Might get altered below.
    Location setBack = null;
    if (result > 0.0) {
        // Increment violation level.
        data.creativeFlyVL += result;
        // Execute whatever actions are associated with this check and the violation level and find out if we
        // should cancel the event.
        final ViolationData vd = new ViolationData(this, player, data.creativeFlyVL, result, cc.creativeFlyActions);
        if (vd.needsParameters()) {
            vd.setParameter(ParameterName.LOCATION_FROM, String.format(Locale.US, "%.2f, %.2f, %.2f", from.getX(), from.getY(), from.getZ()));
            vd.setParameter(ParameterName.LOCATION_TO, String.format(Locale.US, "%.2f, %.2f, %.2f", to.getX(), to.getY(), to.getZ()));
            vd.setParameter(ParameterName.DISTANCE, String.format(Locale.US, "%.2f", TrigUtil.distance(from, to)));
            if (!tags.isEmpty()) {
                vd.setParameter(ParameterName.TAGS, StringUtil.join(tags, "+"));
            }
        }
        if (executeActions(vd).willCancel()) {
            // Compose a new location based on coordinates of "newTo" and viewing direction of "event.getTo()"
            // to allow the player to look somewhere else despite getting pulled back by NoCheatPlus.
            // (OK)
            setBack = data.getSetBack(to);
        }
    } else {
        // Maximum height check (silent set back).
        if (to.getY() > maximumHeight) {
            // (OK)
            setBack = data.getSetBack(to);
            if (debug) {
                debug(player, "Maximum height exceeded, silent set-back.");
            }
        }
        if (setBack == null) {
            // Slowly reduce the violation level with each event.
            data.creativeFlyVL *= 0.97;
        }
    }
    // Return setBack, if set.
    if (setBack != null) {
        // Check for max height of the set back.
        if (setBack.getY() > maximumHeight) {
            // Correct the y position.
            setBack.setY(getCorrectedHeight(maximumHeight, setBack.getWorld()));
            if (debug) {
                debug(player, "Maximum height exceeded by set back, correct to: " + setBack.getY());
            }
        }
        data.sfJumpPhase = 0;
        return setBack;
    } else {
        // Adjust the set back and other last distances.
        data.setSetBack(to);
        // Adjust jump phase.
        if (!thisMove.from.onGroundOrResetCond && !thisMove.to.onGroundOrResetCond) {
            data.sfJumpPhase++;
        } else if (thisMove.touchedGround && !thisMove.to.onGroundOrResetCond) {
            data.sfJumpPhase = 1;
        } else {
            data.sfJumpPhase = 0;
        }
        return null;
    }
}
Also used : GameMode(org.bukkit.GameMode) PlayerMoveData(fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData) ModelFlying(fr.neatmonster.nocheatplus.checks.moving.model.ModelFlying) ViolationData(fr.neatmonster.nocheatplus.checks.ViolationData) PlayerLocation(fr.neatmonster.nocheatplus.utilities.location.PlayerLocation) Location(org.bukkit.Location)

Example 24 with PlayerMoveData

use of fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData in project NoCheatPlus by NoCheatPlus.

the class NoFall method check.

/**
 * Checks a player. Expects from and to using cc.yOnGround.
 *
 * @param player
 *            the player
 * @param from
 *            the from
 * @param to
 *            the to
 * @param previousSetBackY
 *            The set back y from lift-off. If not present:
 *            Double.NEGATIVE_INFINITY.
 */
public void check(final Player player, final PlayerLocation pFrom, final PlayerLocation pTo, final double previousSetBackY, final MovingData data, final MovingConfig cc, final IPlayerData pData) {
    final boolean debug = pData.isDebugActive(type);
    final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
    final LocationData from = thisMove.from;
    final LocationData to = thisMove.to;
    final double fromY = from.getY();
    final double toY = to.getY();
    final double yDiff = toY - fromY;
    final double oldNFDist = data.noFallFallDistance;
    // Reset-cond is not touched by yOnGround.
    // TODO: Distinguish water depth vs. fall distance ?
    /*
         * TODO: Account for flags instead (F_FALLDIST_ZERO and
         * F_FALLDIST_HALF). Resetcond as trigger: if (resetFrom) { ...
         */
    // TODO: Also handle from and to independently (rather fire twice than wait for next time).
    final boolean fromReset = from.resetCond;
    final boolean toReset = to.resetCond;
    final boolean fromOnGround, toOnGround;
    // Adapt yOnGround if necessary (sf uses another setting).
    if (yDiff < 0 && cc.yOnGround < cc.noFallyOnGround) {
        // In fact this is somewhat heuristic, but it seems to work well.
        // Missing on-ground seems to happen with running down pyramids rather.
        // TODO: Should be obsolete.
        adjustYonGround(pFrom, pTo, cc.noFallyOnGround);
        fromOnGround = pFrom.isOnGround();
        toOnGround = pTo.isOnGround();
    } else {
        fromOnGround = from.onGround;
        toOnGround = to.onGround;
    }
    // TODO: early returns (...)
    final double minY = Math.min(fromY, toY);
    if (fromReset) {
        // Just reset.
        data.clearNoFallData();
        // Ensure very big/strange moves don't yield violations.
        if (toY - fromY <= -Magic.FALL_DAMAGE_DIST) {
            data.noFallSkipAirCheck = true;
        }
    } else if (fromOnGround || !toOnGround && thisMove.touchedGround) {
        // Check if to deal damage (fall back damage check).
        // Includes the current y-distance on descend!
        touchDown(player, minY, previousSetBackY, data, cc, pData);
        // Ensure very big/strange moves don't yield violations.
        if (toY - fromY <= -Magic.FALL_DAMAGE_DIST) {
            data.noFallSkipAirCheck = true;
        }
    } else if (toReset) {
        // Just reset.
        data.clearNoFallData();
    } else if (toOnGround) {
        // Check if to deal damage.
        if (yDiff < 0) {
            // In this case the player has traveled further: add the difference.
            data.noFallFallDistance -= yDiff;
        }
        touchDown(player, minY, previousSetBackY, data, cc, pData);
    } else {
    // Ensure fall distance is correct, or "anyway"?
    }
    // Set reference y for nofall (always).
    /*
         * TODO: Consider setting this before handleOnGround (at least for
         * resetTo). This is after dealing damage, needs to be done differently.
         */
    data.noFallMaxY = Math.max(Math.max(fromY, toY), data.noFallMaxY);
    // TODO: fall distance might be behind (!)
    // TODO: should be the data.noFallMaxY be counted in ?
    // Note: it has to be fetched here.
    final float mcFallDistance = player.getFallDistance();
    // Add y distance.
    if (!toReset && !toOnGround && yDiff < 0) {
        data.noFallFallDistance -= yDiff;
    } else if (cc.noFallAntiCriticals && (toReset || toOnGround || (fromReset || fromOnGround || thisMove.touchedGround) && yDiff >= 0)) {
        final double max = Math.max(data.noFallFallDistance, mcFallDistance);
        if (max > 0.0 && max < 0.75) {
            // (Ensure this does not conflict with deal-damage set to false.)
            if (debug) {
                debug(player, "NoFall: Reset fall distance (anticriticals): mc=" + mcFallDistance + " / nf=" + data.noFallFallDistance);
            }
            if (data.noFallFallDistance > 0) {
                data.noFallFallDistance = 0;
            }
            if (mcFallDistance > 0f) {
                player.setFallDistance(0f);
            }
        }
    }
    if (debug) {
        debug(player, "NoFall: mc=" + mcFallDistance + " / nf=" + data.noFallFallDistance + (oldNFDist < data.noFallFallDistance ? " (+" + (data.noFallFallDistance - oldNFDist) + ")" : "") + " | ymax=" + data.noFallMaxY);
    }
}
Also used : LocationData(fr.neatmonster.nocheatplus.checks.moving.model.LocationData) PlayerMoveData(fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData)

Example 25 with PlayerMoveData

use of fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData in project NoCheatPlus by NoCheatPlus.

the class MagicLiquid method liquidWorkarounds.

/**
 * @param from
 * @param to
 * @param baseSpeed
 * @param frictDist
 * @param thisMove
 * @param lastMove
 * @param data
 * @return The allowed distance for reference, in case the move is allowed.
 *         If no workaround applies, null is returned.
 */
public static Double liquidWorkarounds(final PlayerLocation from, final PlayerLocation to, final double baseSpeed, final double frictDist, final PlayerMoveData lastMove, final MovingData data) {
    final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
    final double yDistance = thisMove.yDistance;
    // Special cases.
    // TODO: Re-arrange.
    final PlayerMoveData pastMove1 = data.playerMoves.getSecondPastMove();
    if (yDistance >= 0) {
        // TODO: Conditions seem warped.
        if (yDistance <= 0.5) {
            if (lastMove.toIsValid && yDistance < lastMove.yDistance && lastMove.yDistance - yDistance > Math.max(0.001, yDistance - baseSpeed)) {
                // Decrease more than difference to baseSpeed.
                return yDistance;
            }
            if (yDistance <= data.liftOffEnvelope.getMaxJumpGain(data.jumpAmplifier) && !BlockProperties.isLiquid(from.getTypeIdAbove()) || // TODO: What now !?
            !// TODO: impossible !?
            to.isInLiquid() || (thisMove.to.onGround || lastMove.toIsValid && lastMove.yDistance - yDistance >= 0.010 || to.isAboveStairs())) {
                double vAllowedDistance = baseSpeed + 0.5;
                double vDistanceAboveLimit = yDistance - vAllowedDistance;
                if (vDistanceAboveLimit <= 0.0) {
                    return vAllowedDistance;
                }
            }
        }
        if (lastMove.toIsValid) {
            // (Observed on 'dirty' phase.)
            if (Math.abs(frictDist - yDistance) <= 2.0 * Magic.GRAVITY_MAX && yDistance < lastMove.yDistance - 4.0 * Math.abs(frictDist - yDistance)) {
                return yDistance;
            }
            // Jumping with velocity into water from below, just slightly more decrease than gravity, twice.
            if (yDistance > frictDist && yDistance < lastMove.yDistance - Magic.GRAVITY_MAX && data.insideMediumCount <= 1) {
                // (dirty seems to be set/kept reliably)
                return yDistance;
            }
            if (pastMove1.toIsValid && pastMove1.to.extraPropertiesValid) {
                // Splash move with space space pressed (this move leaving liquid).
                if (pastMove1.yDistance > 0.0 && thisMove.yDistance > 0.0 && pastMove1.yDistance - Magic.GRAVITY_MAX > lastMove.yDistance && lastMove.yDistance - Magic.GRAVITY_ODD > thisMove.yDistance && Magic.intoLiquid(lastMove) && Magic.leavingLiquid(thisMove)) {
                    return yDistance;
                }
                // Velocity use in lastMove, keep air friction roughly.
                if (!Magic.resetCond(pastMove1) && lastMove.yDistance - Magic.GRAVITY_MAX > thisMove.yDistance && Magic.intoLiquid(lastMove) && Magic.leavingLiquid(thisMove)) {
                    return yDistance;
                }
            }
        }
    } else // Otherwise, only if last move is available.
    if (lastMove.toIsValid) {
        // Falling into water, mid-speed (second move after diving in).
        if (yDistance > -0.9 && yDistance < lastMove.yDistance && Math.abs(yDistance - lastMove.yDistance) <= Magic.GRAVITY_MAX + Magic.GRAVITY_MIN && yDistance - lastMove.yDistance < -Magic.GRAVITY_MIN) // && !BlockProperties.isLiquid(to.getTypeId(to.getBlockX(), Location.locToBlock(to.getY() + to.getEyeHeight()), to.getBlockZ()))
        {
            return lastMove.yDistance - Magic.GRAVITY_MAX - Magic.GRAVITY_MIN;
        } else // Increase speed slightly on second in-medium move (dirty flag may have been reset).
        if (data.insideMediumCount <= 1 && // (No strong decrease:)
        yDistance > lastMove.yDistance - Magic.GRAVITY_MAX && (// Ordinary (some old case).
        lastMove.yDistance < 0.8 && yDistance < lastMove.yDistance - Magic.GRAVITY_SPAN || // Check with three moves, rather shortly touching water.
        // Arbitrary, actually observed has been < -1.0
        lastMove.yDistance < -0.5 && pastMove1.toIsValid && pastMove1.to.extraPropertiesValid && Math.abs(pastMove1.yDistance - lastMove.yDistance) < Magic.GRAVITY_MIN && yDistance <= lastMove.yDistance && Magic.inLiquid(lastMove) && Magic.intoLiquid(pastMove1))) {
            return yDistance;
        } else // In-water rough near-0-inversion from allowed speed to a negative amount, little more than allowed (magic -0.2 roughly).
        if (lastMove.yDistance >= Magic.GRAVITY_MAX / 10.0 && lastMove.yDistance <= Magic.GRAVITY_MAX + Magic.GRAVITY_MIN / 2.0 && yDistance < 0.0 && yDistance > -2.0 * Magic.GRAVITY_MAX - Magic.GRAVITY_MIN / 2.0 && // TODO: Might skip the liquid check, though.
        to.isInLiquid() && lastMove.from.inLiquid && lastMove.to.extraPropertiesValid && // TODO: in water only?
        lastMove.to.inLiquid) {
            return yDistance;
        } else // Lava rather.
        if (// (Random, but smaller than water.)
        data.lastFrictionVertical < 0.65 && (// Moving downstream.
        lastMove.yDistance < 0.0 && yDistance > -0.5 && yDistance < lastMove.yDistance && lastMove.yDistance - yDistance < Magic.GRAVITY_MIN && BlockProperties.isDownStream(from, to) || // Mix of gravity and base speed [careful: relates to water base speed].
        lastMove.yDistance < 0.0 && yDistance > -baseSpeed - Magic.GRAVITY_MAX && yDistance < lastMove.yDistance && lastMove.yDistance - yDistance > Magic.GRAVITY_SPAN && Math.abs(lastMove.yDistance + baseSpeed) < 0.25 * baseSpeed || // Falling slightly too fast in lava.
        data.insideMediumCount == 1 || data.insideMediumCount == 2 && lastMove.yDistance < 0.0 && yDistance < lastMove.yDistance && yDistance - lastMove.yDistance > -Magic.GRAVITY_MIN && yDistance > -0.65)) {
            return yDistance;
        }
    }
    return null;
}
Also used : PlayerMoveData(fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData)

Aggregations

PlayerMoveData (fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData)25 Location (org.bukkit.Location)11 PlayerLocation (fr.neatmonster.nocheatplus.utilities.location.PlayerLocation)9 IPlayerData (fr.neatmonster.nocheatplus.players.IPlayerData)5 MovingData (fr.neatmonster.nocheatplus.checks.moving.MovingData)3 PlayerMoveInfo (fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveInfo)3 SimpleEntry (fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry)3 Player (org.bukkit.entity.Player)3 ViolationData (fr.neatmonster.nocheatplus.checks.ViolationData)2 MovingConfig (fr.neatmonster.nocheatplus.checks.moving.MovingConfig)2 AccountEntry (fr.neatmonster.nocheatplus.checks.moving.velocity.AccountEntry)2 RichBoundsLocation (fr.neatmonster.nocheatplus.utilities.location.RichBoundsLocation)2 ActionList (fr.neatmonster.nocheatplus.actions.ActionList)1 Check (fr.neatmonster.nocheatplus.checks.Check)1 CombinedData (fr.neatmonster.nocheatplus.checks.combined.CombinedData)1 LocationTrace (fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace)1 LiftOffEnvelope (fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope)1 LocationData (fr.neatmonster.nocheatplus.checks.moving.model.LocationData)1 ModelFlying (fr.neatmonster.nocheatplus.checks.moving.model.ModelFlying)1 CountableLocation (fr.neatmonster.nocheatplus.checks.net.model.CountableLocation)1