Complete rewrite of the "onGround" check. Now it is much more

reliable.

OPs are now no longer filtered. (I use player.isOp() to check for
OP-status until I have a better system.

Increased threshold for speedhack from 50 to 60 to reduce false
positives. This value will at some point be configurable by the server
admin.
This commit is contained in:
Evenprime 2011-02-17 16:07:56 +01:00
parent 79c8fd6060
commit bf07aa16d7
2 changed files with 136 additions and 29 deletions

View File

@ -3,5 +3,5 @@ name: NoCheatPlugin
author: Evenprime author: Evenprime
main: cc.co.evenprime.bukkit.nocheat.NoCheatPlugin main: cc.co.evenprime.bukkit.nocheat.NoCheatPlugin
version: 0.3 version: 0.3.5

View File

@ -18,6 +18,11 @@ import org.bukkit.event.player.PlayerMoveEvent;
public class NoCheatPluginPlayerListener extends PlayerListener { public class NoCheatPluginPlayerListener extends PlayerListener {
public enum BlockType {
SOLID, NONSOLID, LADDER, LIQUID, UNKNOWN;
}
/** /**
* Storage for data persistence between events * Storage for data persistence between events
* *
@ -43,12 +48,108 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
// Each entry represents the maximum gain in height per move event. // Each entry represents the maximum gain in height per move event.
private static double jumpingPhases[] = new double[]{ 0.501D, 0.34D, 0.26D, 0.17D, 0.09D, 0.02D, 0.00D, -0.07D, -0.15D, -0.22D, -0.29D, -0.36D, -0.43D, -0.50D }; private static double jumpingPhases[] = new double[]{ 0.501D, 0.34D, 0.26D, 0.17D, 0.09D, 0.02D, 0.00D, -0.07D, -0.15D, -0.22D, -0.29D, -0.36D, -0.43D, -0.50D };
// Until I can think of a better way to determine if a block is solid or not, this is what I'll do
private static BlockType types[] = new BlockType[256];
static {
for(int i = 0; i < types.length; i++) {
types[i] = BlockType.UNKNOWN;
}
types[Material.AIR.getId()] = BlockType.NONSOLID;
types[Material.STONE.getId()] = BlockType.SOLID;
types[Material.GRASS.getId()] = BlockType.SOLID;
types[Material.DIRT.getId()] = BlockType.SOLID;
types[Material.COBBLESTONE.getId()] = BlockType.SOLID;
types[Material.WOOD.getId()] = BlockType.SOLID;
types[Material.SAPLING.getId()] = BlockType.NONSOLID;
types[Material.BEDROCK.getId()] = BlockType.SOLID;
types[Material.WATER.getId()] = BlockType.LIQUID;
types[Material.STATIONARY_WATER.getId()] = BlockType.LIQUID;
types[Material.LAVA.getId()] = BlockType.LIQUID;
types[Material.STATIONARY_LAVA.getId()] = BlockType.LIQUID;
types[Material.SAND.getId()] = BlockType.SOLID;
types[Material.GRAVEL.getId()] = BlockType.SOLID;
types[Material.GOLD_ORE.getId()] = BlockType.SOLID;
types[Material.IRON_ORE.getId()] = BlockType.SOLID;
types[Material.COAL_ORE.getId()] = BlockType.SOLID;
types[Material.LOG.getId()] = BlockType.SOLID;
types[Material.LEAVES.getId()] = BlockType.SOLID;
types[Material.SPONGE.getId()] = BlockType.SOLID;
types[Material.GLASS.getId()] = BlockType.SOLID;
types[Material.LAPIS_ORE.getId()] = BlockType.SOLID;
types[Material.LAPIS_BLOCK.getId()] = BlockType.SOLID;
types[Material.DISPENSER.getId()] = BlockType.SOLID;
types[Material.SANDSTONE.getId()] = BlockType.SOLID;
types[Material.NOTE_BLOCK.getId()]= BlockType.SOLID;
types[Material.WOOL.getId()]= BlockType.SOLID;
types[Material.YELLOW_FLOWER.getId()]= BlockType.NONSOLID;
types[Material.RED_ROSE.getId()]= BlockType.NONSOLID;
types[Material.BROWN_MUSHROOM.getId()]= BlockType.NONSOLID;
types[Material.RED_MUSHROOM.getId()]= BlockType.NONSOLID;
types[Material.GOLD_BLOCK.getId()]= BlockType.SOLID;
types[Material.IRON_BLOCK.getId()]= BlockType.SOLID;
types[Material.DOUBLE_STEP.getId()]= BlockType.UNKNOWN;
types[Material.STEP.getId()]= BlockType.UNKNOWN;
types[Material.BRICK.getId()]= BlockType.SOLID;
types[Material.TNT.getId()]= BlockType.SOLID;
types[Material.BOOKSHELF.getId()]= BlockType.SOLID;
types[Material.MOSSY_COBBLESTONE.getId()] = BlockType.SOLID;
types[Material.OBSIDIAN.getId()]= BlockType.SOLID;
types[Material.TORCH.getId()]= BlockType.NONSOLID;
types[Material.FIRE.getId()]= BlockType.NONSOLID;
types[Material.MOB_SPAWNER.getId()]= BlockType.SOLID;
types[Material.WOOD_STAIRS.getId()]= BlockType.UNKNOWN;
types[Material.CHEST.getId()]= BlockType.SOLID;
types[Material.REDSTONE_WIRE.getId()]= BlockType.NONSOLID;
types[Material.DIAMOND_ORE.getId()]= BlockType.SOLID;
types[Material.DIAMOND_BLOCK.getId()]= BlockType.SOLID;
types[Material.WORKBENCH.getId()]= BlockType.SOLID;
types[Material.CROPS.getId()]= BlockType.NONSOLID;
types[Material.SOIL.getId()]= BlockType.SOLID;
types[Material.FURNACE.getId()]= BlockType.SOLID;
types[Material.BURNING_FURNACE.getId()]= BlockType.SOLID;
types[Material.SIGN_POST.getId()]= BlockType.NONSOLID;
types[Material.WOODEN_DOOR.getId()]= BlockType.NONSOLID;
types[Material.LADDER.getId()]= BlockType.LADDER;
types[Material.RAILS.getId()]= BlockType.NONSOLID;
types[Material.COBBLESTONE_STAIRS.getId()]= BlockType.UNKNOWN;
types[Material.WALL_SIGN.getId()]= BlockType.NONSOLID;
types[Material.LEVER.getId()]= BlockType.NONSOLID;
types[Material.STONE_PLATE.getId()]= BlockType.NONSOLID;
types[Material.IRON_DOOR_BLOCK.getId()]= BlockType.NONSOLID;
types[Material.WOOD_PLATE.getId()]= BlockType.NONSOLID;
types[Material.REDSTONE_ORE.getId()]= BlockType.SOLID;
types[Material.GLOWING_REDSTONE_ORE.getId()]= BlockType.SOLID;
types[Material.REDSTONE_TORCH_OFF.getId()]= BlockType.NONSOLID;
types[Material.REDSTONE_TORCH_ON.getId()]= BlockType.NONSOLID;
types[Material.STONE_BUTTON.getId()]= BlockType.NONSOLID;
types[Material.SNOW.getId()]= BlockType.UNKNOWN;
types[Material.ICE.getId()]= BlockType.UNKNOWN;
types[Material.SNOW_BLOCK.getId()]= BlockType.SOLID;
types[Material.CACTUS.getId()]= BlockType.SOLID;
types[Material.CLAY.getId()]= BlockType.SOLID;
types[Material.SUGAR_CANE_BLOCK.getId()]= BlockType.NONSOLID;
types[Material.JUKEBOX.getId()]= BlockType.SOLID;
types[Material.FENCE.getId()]= BlockType.UNKNOWN;
types[Material.PUMPKIN.getId()]= BlockType.SOLID;
types[Material.NETHERRACK.getId()]= BlockType.SOLID;
types[Material.SOUL_SAND.getId()]= BlockType.UNKNOWN;
types[Material.GLOWSTONE.getId()]= BlockType.SOLID;
types[Material.PORTAL.getId()]= BlockType.NONSOLID;
types[Material.JACK_O_LANTERN.getId()]= BlockType.SOLID;
types[Material.CAKE_BLOCK.getId()]= BlockType.UNKNOWN;
}
// Very rough estimates // Very rough estimates
private static double maxX = 0.5D; private static double maxX = 0.5D;
private static double maxZ = 0.5D; private static double maxZ = 0.5D;
private static final long timeFrameForSpeedHackCheck = 2000; private static final long timeFrameForSpeedHackCheck = 2000;
private static final long eventLimitForSpeedHackCheck = 50; private static final long eventLimitForSpeedHackCheck = 60;
// Store data between Events // Store data between Events
private static Map<String, NoCheatPluginData> playerData = new HashMap<String, NoCheatPluginData>(); private static Map<String, NoCheatPluginData> playerData = new HashMap<String, NoCheatPluginData>();
@ -66,17 +167,6 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
@Override @Override
public void onPlayerMove(PlayerMoveEvent event) { public void onPlayerMove(PlayerMoveEvent event) {
// If someone cancelled the event already, ignore it
// If the player is inside a vehicle, ignore it for now
if(event.isCancelled() || event.getPlayer().isInsideVehicle()) {
return;
}
// Get the two locations of the event
Location from = event.getFrom();
Location to = event.getTo();
// Get the player-specific data // Get the player-specific data
NoCheatPluginData data = null; NoCheatPluginData data = null;
@ -86,6 +176,19 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
playerData.put(event.getPlayer().getName(), data); playerData.put(event.getPlayer().getName(), data);
} }
// If someone cancelled the event already, ignore it
// If the player is inside a vehicle, ignore it for now
// If the player is OP, ignore it for now
if(event.isCancelled() || event.getPlayer().isInsideVehicle() || event.getPlayer().isOp()) {
data.phase = 0;
return;
}
// Get the two locations of the event
Location from = event.getFrom();
Location to = event.getTo();
// Get the time of the server // Get the time of the server
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
@ -124,8 +227,8 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
// pre-calculate boundary values that are needed multiple times in the following checks // pre-calculate boundary values that are needed multiple times in the following checks
// the array each contains [lowerX, higherX, Y, lowerZ, higherZ] // the array each contains [lowerX, higherX, Y, lowerZ, higherZ]
int fromValues[] = {(int)Math.floor(from.getX() - 0.299999999D), (int)Math.floor(from.getX() + 0.299999999D), from.getBlockY(), (int)Math.floor(from.getZ() - 0.299999999D),(int)Math.floor(from.getZ() + 0.299999999D) }; int fromValues[] = {(int)Math.floor(from.getX() - 0.3001D), (int)Math.floor(from.getX() + 0.3001D), from.getBlockY(), (int)Math.floor(from.getZ() - 0.3001D),(int)Math.floor(from.getZ() + 0.3001D) };
int toValues[] = {(int)Math.floor(to.getX() - 0.299999999D), (int)Math.floor(to.getX() + 0.299999999D), to.getBlockY(), (int)Math.floor(to.getZ() - 0.299999999D),(int)Math.floor(to.getZ() + 0.299999999D) }; int toValues[] = {(int)Math.floor(to.getX() - 0.3001D), (int)Math.floor(to.getX() + 0.3001D), to.getBlockY(), (int)Math.floor(to.getZ() - 0.3001D),(int)Math.floor(to.getZ() + 0.3001D) };
// compare locations to the world to guess if the player is standing on the ground, a half-block or next to a ladder // compare locations to the world to guess if the player is standing on the ground, a half-block or next to a ladder
boolean onGroundFrom = playerIsOnGround(from.getWorld(), fromValues); boolean onGroundFrom = playerIsOnGround(from.getWorld(), fromValues);
@ -207,7 +310,7 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
// To prevent players from getting stuck in an infinite loop, needs probably more testing // To prevent players from getting stuck in an infinite loop, needs probably more testing
// TODO: Find a better solution // TODO: Find a better solution
if(data.phase > 7) data.phase = 7; if(data.phase > 6) data.phase = 6;
} }
else if(event.isCancelled() && data.lastWasInvalid) { else if(event.isCancelled() && data.lastWasInvalid) {
data.violations++; data.violations++;
@ -238,19 +341,23 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
*/ */
private boolean playerIsOnGround(World w, int values[]) { private boolean playerIsOnGround(World w, int values[]) {
if((w.getBlockAt(values[0], values[2]-1, values[3]).getType() != Material.AIR || // Completely revamped collision detection
w.getBlockAt(values[0], values[2]-1, values[4]).getType() != Material.AIR || // What it does:
w.getBlockAt(values[0], values[2], values[3]).getType() != Material.AIR || // Check the blocks below the player. If they aren't not solid (sic!) and the blocks directly above
w.getBlockAt(values[0], values[2], values[4]).getType() != Material.AIR || // them aren't solid, The player is considered to be standing on the lower block
w.getBlockAt(values[0], values[2]+1, values[3]).getType() == Material.LADDER || // Plus the player can hang onto a ladder that is one field above him
w.getBlockAt(values[0], values[2]+1, values[4]).getType() == Material.LADDER) || if( (types[w.getBlockTypeIdAt(values[0], values[2]-1, values[3])] != BlockType.NONSOLID &&
(values[0] != values[1] && // May save some time by skipping half of the tests types[w.getBlockTypeIdAt(values[0], values[2], values[3])] != BlockType.SOLID) ||
(w.getBlockAt(values[1], values[2]-1, values[3]).getType() != Material.AIR || (types[w.getBlockTypeIdAt(values[1], values[2]-1, values[3])] != BlockType.NONSOLID &&
w.getBlockAt(values[1], values[2]-1, values[4]).getType() != Material.AIR || types[w.getBlockTypeIdAt(values[1], values[2], values[3])] != BlockType.SOLID) ||
w.getBlockAt(values[1], values[2], values[3]).getType() != Material.AIR || (types[w.getBlockTypeIdAt(values[0], values[2]-1, values[4])] != BlockType.NONSOLID &&
w.getBlockAt(values[1], values[2], values[4]).getType() != Material.AIR || types[w.getBlockTypeIdAt(values[0], values[2], values[4])] != BlockType.SOLID) ||
w.getBlockAt(values[1], values[2]+1, values[3]).getType() == Material.LADDER || (types[w.getBlockTypeIdAt(values[1], values[2]-1, values[4])] != BlockType.NONSOLID &&
w.getBlockAt(values[1], values[2]+1, values[4]).getType() == Material.LADDER))) types[w.getBlockTypeIdAt(values[1], values[2], values[4])] != BlockType.SOLID) ||
(types[w.getBlockTypeIdAt(values[0], values[2]+1, values[3])] == BlockType.LADDER ||
types[w.getBlockTypeIdAt(values[0], values[2]+1, values[4])] == BlockType.LADDER ||
types[w.getBlockTypeIdAt(values[1], values[2]+1, values[3])] == BlockType.LADDER ||
types[w.getBlockTypeIdAt(values[1], values[2]+1, values[4])] == BlockType.LADDER))
return true; return true;
else else
return false; return false;