private Location potentialViolation( final Player player, Location loc, final PlayerLocation from, final PlayerLocation to, final int manhattan, String tags, final MovingData data, final MovingConfig cc) { // Moving into a block, possibly a violation. // Check the players location if different from others. // (It provides a better set-back for some exploits.) final int lbX = loc.getBlockX(); final int lbY = loc.getBlockY(); final int lbZ = loc.getBlockZ(); // First check if the player is moving from a passable location. // If not, the move might still be allowed, if moving inside of the same block, or from and to // have head position passable. if (from.isPassable()) { // Put one workaround for 1.5 high blocks here: if (from.isBlockAbove(to) && (BlockProperties.getBlockFlags(to.getTypeId()) & BlockProperties.F_HEIGHT150) != 0) { // Check if the move went from inside of the block. if (BlockProperties.collidesBlock( to.getBlockCache(), from.getX(), from.getY(), from.getZ(), from.getX(), from.getY(), from.getZ(), to.getBlockX(), to.getBlockY(), to.getBlockZ(), to.getTypeId())) { // Allow moving inside of 1.5 high blocks. return null; } } // From should be the set-back. loc = null; tags += "into"; } else if (BlockProperties.isPassable( from.getBlockCache(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(lbX, lbY, lbZ))) { tags += "into_shift"; } // } else if (BlockProperties.isPassableExact(from.getBlockCache(), loc.getX(), loc.getY(), // loc.getZ(), from.getTypeId(lbX, lbY, lbZ))) { // (Mind that this can be the case on the same block theoretically.) // Keep loc as set-back. // } else if (!from.isSameBlock(lbX, lbY, lbZ)) { // Otherwise keep loc as set-back. tags += "cross_shift"; } else if (manhattan == 1 && to.isBlockAbove(from) && BlockProperties.isPassable( from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId( from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) { // else if (to.isBlockAbove(from) && BlockProperties.isPassableExact(from.getBlockCache(), // from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), // from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), // from.getBlockZ()))) { // Allow the move up if the head is free. return null; } else if (manhattan > 0) { // Otherwise keep from as set-back. loc = null; tags += "cross"; } else { // All blocks are the same, allow the move. return null; } // Discard inconsistent locations. // TODO: Might get rid of using the in-between loc - needs use-case checking. if (loc != null && (TrigUtil.distance(from, to) > 0.75 || TrigUtil.distance(from, loc) > 0.125)) { loc = null; } // Prefer the set-back location from the data. if (data.hasSetBack()) { final Location ref = data.getSetBack(to); if (BlockProperties.isPassable(from.getBlockCache(), ref) || loc == null || TrigUtil.distance(from, loc) > 0.13) { // if (BlockProperties.isPassableExact(from.getBlockCache(), ref)) { loc = ref; if (cc.debug) { System.out.println(player.getName() + " Using set-back location for passable."); } } else if (cc.debug) { System.out.println(player.getName() + " Ignoring set-back for passable."); } } // TODO: set data.set-back ? or something: still some aji here. // Return the reset position. data.passableVL += 1d; final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions); if (cc.debug || vd.needsParameters()) { vd.setParameter(ParameterName.BLOCK_ID, "" + to.getTypeId()); if (!tags.isEmpty()) { vd.setParameter(ParameterName.TAGS, tags); } } if (executeActions(vd)) { // TODO: Consider another set back position for this, also keeping track of players moving // around in blocks. final Location newTo; if (loc != null) { // Ensure the given location is cloned. newTo = LocUtil.clone(loc); } else { newTo = from.getLocation(); if (cc.debug) { System.out.println(player.getName() + " Using from location for passable."); } } newTo.setYaw(to.getYaw()); newTo.setPitch(to.getPitch()); return newTo; } else { // No cancel action set. return null; } }
private Location potentialViolation( final Player player, final Location loc, final PlayerLocation from, final PlayerLocation to, final int manhattan, String tags, final MovingData data, final MovingConfig cc) { // Moving into a block, possibly a violation. // Check the players location if different from others. // (It provides a better set-back for some exploits.) final int lbX = loc.getBlockX(); final int lbY = loc.getBlockY(); final int lbZ = loc.getBlockZ(); Location setBackLoc = null; // Alternative to from.getLocation(). // First check if the player is moving from a passable location. // If not, the move might still be allowed, if moving inside of the same block, or from and to // have head position passable. if (from.isPassable()) { // Put one workaround for 1.5 high blocks here: if (from.isBlockAbove(to) && (BlockProperties.getBlockFlags(to.getTypeId()) & BlockProperties.F_HEIGHT150) != 0) { // Check if the move went from inside of the block. if (BlockProperties.collidesBlock( to.getBlockCache(), from.getX(), from.getY(), from.getZ(), from.getX(), from.getY(), from.getZ(), to.getBlockX(), to.getBlockY(), to.getBlockZ(), to.getTypeId())) { // Allow moving inside of 1.5 high blocks. return null; } } // From should be the set-back. tags += "into"; } else if (BlockProperties.isPassable( from.getBlockCache(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(lbX, lbY, lbZ))) { // Keep loc, because it it is passable. tags += "into_shift"; setBackLoc = loc; } // } else if (BlockProperties.isPassableExact(from.getBlockCache(), loc.getX(), loc.getY(), // loc.getZ(), from.getTypeId(lbX, lbY, lbZ))) { // (Mind that this can be the case on the same block theoretically.) // Keep loc as set-back. // } else if (!from.isSameBlock(lbX, lbY, lbZ)) { // Both loc and from are not passable. Use from as set.back (earliest). tags += "cross_shift"; } else if (manhattan == 1 && to.isBlockAbove(from) && BlockProperties.isPassable( from.getBlockCache(), from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), from.getTypeId( from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), from.getBlockZ()))) { // else if (to.isBlockAbove(from) && BlockProperties.isPassableExact(from.getBlockCache(), // from.getX(), from.getY() + player.getEyeHeight(), from.getZ(), // from.getTypeId(from.getBlockX(), Location.locToBlock(from.getY() + player.getEyeHeight()), // from.getBlockZ()))) { // Allow the move up if the head is free. // TODO: Better distinguish ray-tracing (through something thin) or check to-head-passable // too? return null; } else if (manhattan > 0) { // Otherwise keep from as set-back. tags += "cross"; } else { // All blocks are the same, allow the move. return null; } // Discard inconsistent locations. // TODO: Might get rid of using the in-between loc - needs use-case checking. if (setBackLoc != null && (TrigUtil.distance(from, to) > 0.75 || TrigUtil.distance(from, setBackLoc) > 0.125)) { setBackLoc = null; } // Prefer the set-back location from the data. if (data.hasSetBack()) { // TODO: Review or make configurable. final Location ref = data.getSetBack(to); if (BlockProperties.isPassable(from.getBlockCache(), ref) || setBackLoc == null || TrigUtil.distance(from, setBackLoc) > 0.13) { // if (BlockProperties.isPassableExact(from.getBlockCache(), ref)) { setBackLoc = ref; if (data.debug) { NCPAPIProvider.getNoCheatPlusAPI() .getLogManager() .debug( Streams.TRACE_FILE, player.getName() + " Using set-back location for passable."); } } else if (data.debug) { NCPAPIProvider.getNoCheatPlusAPI() .getLogManager() .debug(Streams.TRACE_FILE, player.getName() + " Ignoring set-back for passable."); } } // TODO: set data.set-back ? or something: still some aji here. // Return the reset position. data.passableVL += 1d; final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions); if (data.debug || 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))); // TODO: Consider adding from.getTypeId() too, if blocks differ and non-air. vd.setParameter(ParameterName.BLOCK_ID, "" + to.getTypeId()); if (!tags.isEmpty()) { vd.setParameter(ParameterName.TAGS, tags); } } if (executeActions(vd)) { // TODO: Consider another set back position for this, also keeping track of players moving // around in blocks. final Location newTo; if (setBackLoc != null) { // Ensure the given location is cloned. newTo = LocUtil.clone(setBackLoc); } else { newTo = from.getLocation(); if (data.debug) { NCPAPIProvider.getNoCheatPlusAPI() .getLogManager() .debug(Streams.TRACE_FILE, player.getName() + " Using from location for passable."); } } newTo.setYaw(to.getYaw()); newTo.setPitch(to.getPitch()); return newTo; } else { // No cancel action set. return null; } }
/** * @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 long time, final boolean useBlockChangeTracker) { // Reset tags, just in case. tags.clear(); // Some edge data for this move. final GameMode gameMode = player.getGameMode(); final ModelFlying model = cc.getModelFlying(player, from); 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); // } thisMove.modelFlying = model; final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove(); // 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.ground) { MovingUtil.prepareFullCheck(from, to, thisMove, Math.max(cc.yOnGround, cc.noFallyOnGround)); if (!thisMove.from.onGroundOrResetCond) { if (from.isSamePos(to)) { if (lastMove.toIsValid && lastMove.hDistance > 0.0 && lastMove.yDistance < -0.3 // Copy and paste from sf. && 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 { data.clearActiveHorVel(); // TODO: test/check ! } resultH *= 100.0; // Normalize to % of a block. if (resultH > 0.0) { tags.add("hdist"); } // Vertical move. double limitV = 0.0; // Limit. double resultV = 0.0; // Violation (normalized to 100 * 1 block, applies if > 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.maxHeight + 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"); } resultV *= 100.0; // Normalize to % of a block. if (resultV > 0.0) { tags.add("vdist"); } final double result = Math.max(0.0, resultH) + Math.max(0.0, resultV); if (data.debug) { outpuDebugMove(player, hDistance, limitH, yDistance, limitV, model, tags, data); } // Violation handling. Location setBack = null; // Might get altered below. 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. setBack = data.getSetBack(to); } } else { // Maximum height check (silent set back). if (to.getY() > maximumHeight) { setBack = data.getSetBack(to); } 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 (data.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; } }