Hot fix for bounding box problems.

This commit is contained in:
asofold 2012-11-08 06:02:22 +01:00
parent 22f2a5c248
commit 462aeed412
5 changed files with 447 additions and 383 deletions

View File

@ -5,6 +5,7 @@ import org.bukkit.potion.PotionEffectType;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.LagMeasureTask;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
@ -45,6 +46,11 @@ public class Critical extends Check {
// We'll need the PlayerLocation to know some important stuff.
final PlayerLocation location = new PlayerLocation();
location.set(player.getLocation(), player);
if (location.isIllegal()) {
location.cleanup();
CheckUtils.onIllegalMove(player);
return true;
}
// Check if the hit was a critical hit (positive fall distance, entity in the air, not on ladder, not in liquid
// and without blindness effect).

View File

@ -45,6 +45,7 @@ import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.utilities.BlockCache;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/*
@ -355,8 +356,30 @@ public class MovingListener implements Listener {
final MovingConfig cc = MovingConfig.getConfig(player);
moveInfo.set(player, from, to, cc.yOnGround);
final MovingData data = MovingData.getData(player);
if (pFrom.isIllegal() || pTo.isIllegal()) {
moveInfo.cleanup();
parkedInfo.add(moveInfo);
CheckUtils.onIllegalMove(player);
if (data.setBack != null){
event.setFrom(data.setBack);
event.setTo(data.setBack);
}
else{
pFrom.set(player.getLocation(), player);
if (!pFrom.isIllegal()){
event.setFrom(pFrom.getLocation());
event.setTo(pFrom.getLocation());
}
else{
NoCheatPlus.denyLogin(player.getName(), 24L*60L*60L*1000L);
CheckUtils.logSevere("[NCP] could not restore location for " + player.getName() + " deny login for 24 hours");
}
pFrom.cleanup();
}
return;
}
data.noFallAssumeGround = false;
data.teleported = null;

View File

@ -863,6 +863,11 @@ public class BlockProperties {
// return blockId != 0 && net.minecraft.server.Block.byId[blockId].//.c();// d();
// Bit fat workaround, maybe put the object through from check listener ?
pLoc.set(location, player, 0.3);
if (pLoc.isIllegal()) {
pLoc.cleanup();
CheckUtils.onIllegalMove(player);
return false;
}
final boolean onGround = pLoc.isOnGround();
pLoc.cleanup();
return onGround;

View File

@ -328,4 +328,9 @@ public class CheckUtils {
return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight);
} else return mcEntity.height;
}
public static void onIllegalMove(final Player player){
player.kickPlayer("Illegal move.");
logWarning("[NCP] Disconnect " + player.getName() + " due to illegal move!");
}
}

View File

@ -37,424 +37,431 @@ import org.bukkit.util.Vector;
* MMMMMMMMMMM
*/
/**
* An utility class used to know a lot of things for a player and a location given.
* An utility class used to know a lot of things for a player and a location
* given.
*/
public class PlayerLocation {
/** Box for one time use, no nesting, no extra storing this(!). */
protected static final AxisAlignedBB useBox = AxisAlignedBB.a(0, 0, 0, 0, 0, 0);
/** Type id of the block at the position. */
private Integer typeId;
/** Type id of the block below. */
private Integer typeIdBelow;
private Integer data;
/** Type id of the block at the position. */
private Integer typeId;
/** Is the player above stairs? */
private Boolean aboveStairs;
/** Type id of the block below. */
private Integer typeIdBelow;
/** Is the player in lava? */
private Boolean inLava;
private Integer data;
/** Is the player in water? */
private Boolean inWater;
/** Is the player above stairs? */
private Boolean aboveStairs;
/** Is the player is web? */
private Boolean inWeb;
/** Is the player in lava? */
private Boolean inLava;
/** Is the player on the ground? */
private Boolean onGround;
/** Is the player in water? */
private Boolean inWater;
/** Is the player on ice? */
private Boolean onIce;
/** Is the player is web? */
private Boolean inWeb;
/** Is the player on ladder? */
private Boolean onLadder;
/** Simple test if the exact position is passable. */
private Boolean passable;
/** Y parameter for growing the bounding box with the isOnGround check.*/
private double yOnGround = 0.001;
/** Is the player on the ground? */
private Boolean onGround;
/** The block coordinates. */
private int blockX, blockY, blockZ;
/** The exact coordinates. */
private double x,y,z;
private float yaw, pitch;
// Members that need cleanup:
/** The entityPlayer player. */
private EntityPlayer entityPlayer;
/** Bounding box of the player. */
private double minX, maxX, minY, maxY, minZ, maxZ;
/** Is the player on ice? */
private Boolean onIce;
/** Bukkit world. */
private World world;
/** The worldServer. */
private WorldServer worldServer;
/** Optional block property cache. */
private BlockCache blockCache;
/** Is the player on ladder? */
private Boolean onLadder;
/**
* Gets the location.
*
* @return the location
*/
public Location getLocation() {
return new Location(world, x, y, z);
}
/** Simple test if the exact position is passable. */
private Boolean passable;
/**
* Gets the blockX.
*
* @return the blockX
*/
public double getX() {
return x;
}
/** Y parameter for growing the bounding box with the isOnGround check. */
private double yOnGround = 0.001;
/**
* Gets the boundY.
*
* @return the boundY
*/
public double getY() {
return y;
}
/**
* Gets the blockZ.
*
* @return the blockZ
*/
public double getZ() {
return z;
}
/** The block coordinates. */
private int blockX, blockY, blockZ;
/**
* Gets the yaw.
*
* @return the yaw
*/
public float getYaw() {
return yaw;
}
/**
* Gets the pitch.
*
* @return the pitch
*/
public float getPitch() {
return pitch;
}
public Vector getVector() {
return new Vector(x, y, z);
}
public double getWidth(){
return entityPlayer.width;
}
public int getBlockX(){
return blockX;
}
public int getBlockY(){
return blockY;
}
public int getBlockZ(){
return blockZ;
}
/**
* Compares block coordinates (not the world).
* @param other
* @return
*/
public final boolean isSameBlock(final PlayerLocation other) {
return blockX == other.getBlockX() && blockZ == other.getBlockZ() && blockY == other.getBlockY();
}
/**
* Compares exact coordinates (not the world).
* @param loc
* @return
*/
public boolean isSamePos(final PlayerLocation loc) {
return x == loc.getX() && z == loc.getZ() && y == loc.getY();
}
/**
* Compares exact coordinates (not the world).
* @param loc
* @return
*/
public boolean isSamePos(final Location loc) {
return x == loc.getX() && z == loc.getZ() && y == loc.getY();
}
/** The exact coordinates. */
private double x, y, z;
/**
* Checks if the player is above stairs.
*
* @return true, if the player above on stairs
*/
public boolean isAboveStairs() {
if (aboveStairs == null){
// aboveStairs = BlockProperties.isStairs(getTypeIdBelow().intValue());
// TODO: maybe distinguish upside down stairs and normal stairs !
final double diff = getWidth() + 0.001;
aboveStairs = BlockProperties.collides(getBlockAccess(), x - diff, y + 0.25, z - diff, x + diff, y - 1.0, z + diff, BlockProperties.F_STAIRS);
}
return aboveStairs;
}
private float yaw, pitch;
/**
* Checks if the player is in lava.
*
* @return true, if the player is in lava
*/
public boolean isInLava() {
if (inLava == null) {
final double dX = -0.10000000149011612D;
final double dY = -0.40000000596046448D;
final double dZ = dX;
inLava = BlockProperties.collides(getBlockAccess(), minX - dX, minY - dY, minZ - dZ, maxX + dX, maxY + dY, maxZ + dZ, BlockProperties.F_LAVA);
}
return inLava;
}
/**
* Checks if the player is in water.
*
* @return true, if the player is in water
*/
public boolean isInWater() {
if (inWater == null) {
final double dX = -0.001D;
final double dY = -0.40000000596046448D - 0.001D;
final double dZ = -0.001D;
inWater = BlockProperties.collides(getBlockAccess(), minX - dX, minY - dY, minZ - dZ, maxX + dX, maxY + dY, maxZ + dZ, BlockProperties.F_WATER);
}
return inWater;
}
// Members that need cleanup:
/**
* Checks if the player is in a liquid.
*
* @return true, if the player is in a liquid
*/
public boolean isInLiquid() {
// TODO: optimize (check liquid first and only if liquid check further)
return isInLava() || isInWater();
}
/** The entityPlayer player. */
private EntityPlayer entityPlayer;
/**
* 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;
}
/** Bounding box of the player. */
private double minX, maxX, minY, maxY, minZ, maxZ;
/**
* Checks if the player is on a ladder or vine.
*
* @return If so.
*/
public boolean isOnLadder() {
if (onLadder == null){
final int typeId = getTypeId();
onLadder = typeId == Material.LADDER.getId() || typeId == Material.VINE.getId();
}
return onLadder;
}
/**
* Checks if the player is above a ladder or vine.<br>
* Does not save back value to field.
*
* @return If so.
*/
public boolean isAboveLadder() {
final int typeId = getTypeIdBelow();
return typeId == Material.LADDER.getId() || typeId == Material.VINE.getId();
}
/** Bukkit world. */
private World world;
/**
* 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;
}
/** The worldServer. */
private WorldServer worldServer;
/**
* Checks if the player is on ground.
*
* @return true, if the player is on ground
*/
public boolean isOnGround() {
if (onGround == null) {
final double d0 = 0.01D;
onGround = BlockProperties.isOnGround(getBlockAccess(), minX - d0, minY - yOnGround, minZ - d0, maxX + d0, minY + 0.25, maxZ + d0);
if (!onGround){
// TODO: Probably check other ids too before doing this ?
final double d1 = 0.25D;
final AxisAlignedBB box = useBox.b(minX - d1, minY - getyOnGround() - d1, minZ - d1, maxX + d1, minY + 0.25 + d1, maxZ + d1);
@SuppressWarnings("rawtypes")
final List list = worldServer.getEntities(entityPlayer, box);
@SuppressWarnings("rawtypes")
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
final Entity entity = (Entity) iterator.next();
final EntityType type = entity.getBukkitEntity().getType();
if (type != EntityType.BOAT && type != EntityType.MINECART) continue;
final AxisAlignedBB otherBox = entity.boundingBox;
if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue;
else {
onGround = true;
break;
}
}
}
}
return onGround;
}
/** Optional block property cache. */
private BlockCache blockCache;
/**
* Gets the location.
*
* @return the location
*/
public Location getLocation() {
return new Location(world, x, y, z);
}
/**
* Gets the blockX.
*
* @return the blockX
*/
public double getX() {
return x;
}
/**
* Gets the boundY.
*
* @return the boundY
*/
public double getY() {
return y;
}
/**
* Gets the blockZ.
*
* @return the blockZ
*/
public double getZ() {
return z;
}
/**
* Gets the yaw.
*
* @return the yaw
*/
public float getYaw() {
return yaw;
}
/**
* Gets the pitch.
*
* @return the pitch
*/
public float getPitch() {
return pitch;
}
public Vector getVector() {
return new Vector(x, y, z);
}
public double getWidth() {
return entityPlayer.width;
}
public int getBlockX() {
return blockX;
}
public int getBlockY() {
return blockY;
}
public int getBlockZ() {
return blockZ;
}
/**
* Compares block coordinates (not the world).
*
* @param other
* @return
*/
public final boolean isSameBlock(final PlayerLocation other) {
return blockX == other.getBlockX() && blockZ == other.getBlockZ() && blockY == other.getBlockY();
}
/**
* Compares exact coordinates (not the world).
*
* @param loc
* @return
*/
public boolean isSamePos(final PlayerLocation loc) {
return x == loc.getX() && z == loc.getZ() && y == loc.getY();
}
/**
* Compares exact coordinates (not the world).
*
* @param loc
* @return
*/
public boolean isSamePos(final Location loc) {
return x == loc.getX() && z == loc.getZ() && y == loc.getY();
}
/**
* Checks if the player is above stairs.
*
* @return true, if the player above on stairs
*/
public boolean isAboveStairs() {
if (aboveStairs == null) {
// aboveStairs =
// BlockProperties.isStairs(getTypeIdBelow().intValue());
// TODO: maybe distinguish upside down stairs and normal stairs !
final double diff = getWidth() + 0.001;
aboveStairs = BlockProperties.collides(getBlockAccess(), x - diff, y + 0.25, z - diff, x + diff, y - 1.0, z + diff, BlockProperties.F_STAIRS);
}
return aboveStairs;
}
/**
* Checks if the player is in lava.
*
* @return true, if the player is in lava
*/
public boolean isInLava() {
if (inLava == null) {
final double dX = -0.10000000149011612D;
final double dY = -0.40000000596046448D;
final double dZ = dX;
inLava = BlockProperties.collides(getBlockAccess(), minX - dX, minY - dY, minZ - dZ, maxX + dX, maxY + dY, maxZ + dZ, BlockProperties.F_LAVA);
}
return inLava;
}
/**
* Checks if the player is in water.
*
* @return true, if the player is in water
*/
public boolean isInWater() {
if (inWater == null) {
final double dX = -0.001D;
final double dY = -0.40000000596046448D - 0.001D;
final double dZ = -0.001D;
inWater = BlockProperties.collides(getBlockAccess(), minX - dX, minY - dY, minZ - dZ, maxX + dX, maxY + dY, maxZ + dZ, BlockProperties.F_WATER);
}
return inWater;
}
/**
* Checks if the player is in a liquid.
*
* @return true, if the player is in a liquid
*/
public boolean isInLiquid() {
// TODO: optimize (check liquid first and only if liquid check further)
return isInLava() || isInWater();
}
/**
* 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 a ladder or vine.
*
* @return If so.
*/
public boolean isOnLadder() {
if (onLadder == null) {
final int typeId = getTypeId();
onLadder = typeId == Material.LADDER.getId() || typeId == Material.VINE.getId();
}
return onLadder;
}
/**
* Checks if the player is above a ladder or vine.<br>
* Does not save back value to field.
*
* @return If so.
*/
public boolean isAboveLadder() {
final int typeId = getTypeIdBelow();
return typeId == Material.LADDER.getId() || typeId == Material.VINE.getId();
}
/**
* 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;
}
/**
* Checks if the player is on ground.
*
* @return true, if the player is on ground
*/
public boolean isOnGround() {
if (onGround == null) {
final double d0 = 0.01D;
onGround = BlockProperties.isOnGround(getBlockAccess(), minX - d0, minY - yOnGround, minZ - d0, maxX + d0, minY + 0.25, maxZ + d0);
if (!onGround) {
// TODO: Probably check other ids too before doing this ?
final double d1 = 0.25D;
final AxisAlignedBB box = useBox.b(minX - d1, minY - getyOnGround() - d1, minZ - d1, maxX + d1, minY + 0.25 + d1, maxZ + d1);
@SuppressWarnings("rawtypes")
final List list = worldServer.getEntities(entityPlayer, box);
@SuppressWarnings("rawtypes")
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
final Entity entity = (Entity) iterator.next();
final EntityType type = entity.getBukkitEntity().getType();
if (type != EntityType.BOAT && type != EntityType.MINECART) continue;
final AxisAlignedBB otherBox = entity.boundingBox;
if (box.a > otherBox.d || box.d < otherBox.a || box.b > otherBox.e || box.e < otherBox.b || box.c > otherBox.f || box.f < otherBox.c) continue;
else {
onGround = true;
break;
}
}
}
}
return onGround;
}
public double getyOnGround() {
return yOnGround;
}
return yOnGround;
}
public void setyOnGround(final double yOnGround) {
this.yOnGround = yOnGround;
this.onGround = null;
}
/**
* Simple at the spot passability test, no bounding boxes.
* @return
*/
public boolean isPassable(){
if (passable == null) passable = BlockProperties.isPassable(getBlockAccess(), x, y, z, getTypeId());
return passable;
}
/**
* Convenience method: delegate to BlockProperties.isDoppwnStream .
* @param xDistance
* @param zDistance
* @return
*/
public boolean isDownStream(final double xDistance, final double zDistance){
return BlockProperties.isDownStream(getBlockAccess(), blockX, blockY, blockZ, getData(), xDistance, zDistance);
}
public void setyOnGround(final double yOnGround) {
this.yOnGround = yOnGround;
this.onGround = null;
}
/**
* Simple at the spot passability test, no bounding boxes.
*
* @return
*/
public boolean isPassable() {
if (passable == null) passable = BlockProperties.isPassable(getBlockAccess(), x, y, z, getTypeId());
return passable;
}
/**
* Convenience method: delegate to BlockProperties.isDoppwnStream .
*
* @param xDistance
* @param zDistance
* @return
*/
public boolean isDownStream(final double xDistance, final double zDistance)
{
return BlockProperties.isDownStream(getBlockAccess(), blockX, blockY, blockZ, getData(), xDistance, zDistance);
}
public Integer getTypeId() {
if (typeId == null) typeId = getTypeId(blockX, blockY, blockZ);
return typeId;
}
public Integer getTypeIdBelow() {
if (typeIdBelow == null) typeIdBelow = getTypeId(blockX, blockY - 1, blockZ);
return typeIdBelow;
}
public Integer getData(){
if (data == null) data = getData(blockX, blockY, blockZ);
return data;
}
/**
* Uses id cache if present.
* @param x
* @param y
* @param z
* @return
*/
public final int getTypeId(final int x, final int y, final int z){
return blockCache == null ? worldServer.getTypeId(x, y, z) : blockCache.getTypeId(x, y, z);
public Integer getData() {
if (data == null) data = getData(blockX, blockY, blockZ);
return data;
}
/**
* Uses id cache if present.
*
* @param x
* @param y
* @param z
* @return
*/
public final int getData(final int x, final int y, final int z){
return blockCache == null ? worldServer.getData(x, y, z) : blockCache.getData(x, y, z);
}
public WorldServer getWorldServer() {
return worldServer;
}
/**
* Set the id cache for faster id getting.
* @param cache
*/
public void setBlockCache(final BlockCache cache) {
this.blockCache = cache;
}
/**
*
* @return
*/
public final IBlockAccess getBlockAccess() {
return blockCache == null ? worldServer : blockCache;
}
/**
* Sets the player location object.
*
* @param location
* the location
* @param player
* the player
*/
public void set(final Location location, final Player player){
set(location, player, 0.001);
}
public final int getTypeId(final int x, final int y, final int z) {
return blockCache == null ? worldServer.getTypeId(x, y, z) : blockCache.getTypeId(x, y, z);
}
/**
* Uses id cache if present.
*
* @param x
* @param y
* @param z
* @return
*/
public final int getData(final int x, final int y, final int z) {
return blockCache == null ? worldServer.getData(x, y, z) : blockCache.getData(x, y, z);
}
public WorldServer getWorldServer() {
return worldServer;
}
/**
* Set the id cache for faster id getting.
*
* @param cache
*/
public void setBlockCache(final BlockCache cache) {
this.blockCache = cache;
}
/**
*
* @return
*/
public final IBlockAccess getBlockAccess() {
return blockCache == null ? worldServer : blockCache;
}
/**
* Sets the player location object.
*
* @param location
* the location
* @param player
* the player
*/
public void set(final Location location, final Player player) {
set(location, player, 0.001);
}
/**
* Sets the player location object. Does not set or reset blockCache.
*
@ -498,19 +505,37 @@ public class PlayerLocation {
typeId = typeIdBelow = data = null;
aboveStairs = inLava = inWater = inWeb = onGround = onIce = onLadder = passable = null;
// TODO: Consider blockCache.setAccess? <- currently rather not, because it might be anything.
// TODO: Consider blockCache.setAccess? <- currently rather not, because
// it might be anything.
this.setyOnGround(yFreedom);
}
/**
* Set some references to null.
*/
public void cleanup(){
entityPlayer = null;
world = null;
worldServer = null;
blockCache = null; // No reset here.
}
/**
* Set some references to null.
*/
public void cleanup() {
entityPlayer = null;
world = null;
worldServer = null;
blockCache = null; // No reset here.
}
/**
* Attempt to check for some exploits (!).
*
* @return
*/
public boolean isIllegal() {
if (entityPlayer.dead) return false;
if (!entityPlayer.isSleeping()){
// This can not really test stance but height of bounding box.
final double dY = Math.abs(maxY - minY);
if (dY > 1.8) return true; // dY > 1.65D ||
if (dY < 0.1D) return true;
}
if (Math.abs(minX) > 3.2E7D || Math.abs(maxX) > 3.2E7D || Math.abs(minZ) > 3.2E7D || Math.abs(maxZ) > 3.2E7D) return true;
return false;
}
}