/** * Check if a player may climb upwards (isOnClimbable returned true, player does not move from/to * ground).<br> * Having checked the other stuff is prerequisite for calling this (!). * * @param jumpHeigth Height the player is allowed to have jumped. * @return */ public boolean canClimbUp(double jumpHeigth) { // TODO: distinguish vines. if (BlockProperties.isAttachedClimbable(getTypeId())) { // Check if vine is attached to something solid if (BlockProperties.canClimbUp(blockCache, blockX, blockY, blockZ)) { return true; } // Check the block at head height. final int headY = Location.locToBlock(y + eyeHeight); if (headY > blockY) { for (int cy = blockY + 1; cy <= headY; cy++) { if (BlockProperties.canClimbUp(blockCache, blockX, cy, blockZ)) { return true; } } } // Finally check possible jump height. // TODO: This too is inaccurate. if (isOnGround(jumpHeigth)) { // Here ladders are ok. return true; } return false; } return true; }
/** * Checks if the player is on ice. * * @return true, if the player is on ice */ public boolean isOnIce() { if (onIce == null) { final org.bukkit.entity.Player entity = this.entityPlayer.getBukkitEntity(); if (entity.isSneaking() || entity.isBlocking()) onIce = getTypeId(blockX, Location.locToBlock(minY - 0.1D), blockZ) == Material.ICE.getId(); else onIce = getTypeIdBelow().intValue() == Material.ICE.getId(); } return onIce; }
/** * Checks if the player is on ice. * * @return true, if the player is on ice */ public boolean isOnIce() { if (onIce == null) { // TODO: Use a box here too ? // TODO: check if player is really sneaking (refactor from survivalfly to static access in // Combined ?)! if (blockFlags != null && (blockFlags.longValue() & BlockProperties.F_ICE) == 0) { // TODO: check onGroundMinY !? onIce = false; } else { final int id; if (player.isSneaking() || player.isBlocking()) { id = getTypeId(blockX, Location.locToBlock(minY - 0.1D), blockZ); } else { id = getTypeIdBelow().intValue(); } onIce = BlockProperties.isIce(id); } } return onIce; }
/** * Checks if the player is in web. * * @return true, if the player is in web */ public boolean isInWeb() { final int webId = Material.WEB.getId(); if (inWeb == null) { for (int blockX = Location.locToBlock(minX + 0.001D); blockX <= Location.locToBlock(maxX - 0.001D); blockX++) { for (int blockY = Location.locToBlock(minY + 0.001D); blockY <= Location.locToBlock(maxY - 0.001D); blockY++) { for (int blockZ = Location.locToBlock(minZ + 0.001D); blockZ <= Location.locToBlock(maxZ - 0.001D); blockZ++) { if (getTypeId(blockX, blockY, blockZ) == webId) { inWeb = true; return true; } } } } inWeb = false; } return inWeb; }
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; } }
/** * Checks if the player is on ground, including entities such as Minecart, Boat. * * @return true, if the player is on ground */ public boolean isOnGround() { if (onGround != null) { return onGround; } // Check cached values and simplifications. if (notOnGroundMaxY >= yOnGround) onGround = false; else if (onGroundMinY <= yOnGround) onGround = true; else { // Shortcut check (currently needed for being stuck + sf). if (blockFlags == null || (blockFlags.longValue() & BlockProperties.F_GROUND) != 0) { // TODO: Consider dropping this shortcut. final int bY = Location.locToBlock(y - yOnGround); final int id = bY == blockY ? getTypeId() : (bY == blockY - 1 ? getTypeIdBelow() : blockCache.getTypeId(blockX, bY, blockZ)); final long flags = BlockProperties.getBlockFlags(id); // TODO: Might remove check for variable ? if ((flags & BlockProperties.F_GROUND) != 0 && (flags & BlockProperties.F_VARIABLE) == 0) { final double[] bounds = blockCache.getBounds(blockX, bY, blockZ); // Check collision if not inside of the block. [Might be a problem for cauldron or similar // + something solid above.] // TODO: Might need more refinement. if (bounds != null && y - bY >= bounds[4] && BlockProperties.collidesBlock( blockCache, x, minY - yOnGround, z, x, minY, z, blockX, bY, blockZ, id, bounds, flags)) { // TODO: BlockHeight is needed for fences, use right away (above)? if (!BlockProperties.isPassableWorkaround( blockCache, blockX, bY, blockZ, minX - blockX, minY - yOnGround - bY, minZ - blockZ, id, maxX - minX, yOnGround, maxZ - minZ, 1.0) || (flags & BlockProperties.F_GROUND_HEIGHT) != 0 && BlockProperties.getGroundMinHeight( blockCache, blockX, bY, blockZ, id, bounds, flags) <= y - bY) { // // NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, "*** // onground SHORTCUT"); onGround = true; } } } if (onGround == null) { // // NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, "*** fetch // onground std"); // Full on-ground check (blocks). // Note: Might check for half-block height too (getTypeId), but that is much more seldom. onGround = BlockProperties.isOnGround( blockCache, minX, minY - yOnGround, minZ, maxX, minY, maxZ, 0L); } } else onGround = false; } if (onGround) onGroundMinY = Math.min(onGroundMinY, yOnGround); else { // NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, "*** // onground check entities"); // TODO: further confine this ? notOnGroundMaxY = Math.max(notOnGroundMaxY, yOnGround); final double d1 = 0.25D; onGround = blockCache.standsOnEntity( player, minX - d1, minY - yOnGround - d1, minZ - d1, maxX + d1, minY + 0.25 + d1, maxZ + d1); } return onGround; }
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; } }