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
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 enum BlockType {
SOLID, NONSOLID, LADDER, LIQUID, UNKNOWN;
}
/**
* 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.
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
private static double maxX = 0.5D;
private static double maxZ = 0.5D;
private static final long timeFrameForSpeedHackCheck = 2000;
private static final long eventLimitForSpeedHackCheck = 50;
private static final long eventLimitForSpeedHackCheck = 60;
// Store data between Events
private static Map<String, NoCheatPluginData> playerData = new HashMap<String, NoCheatPluginData>();
@ -66,17 +167,6 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
@Override
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
NoCheatPluginData data = null;
@ -86,6 +176,19 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
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
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
// 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 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 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.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
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
// 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) {
data.violations++;
@ -238,19 +341,23 @@ public class NoCheatPluginPlayerListener extends PlayerListener {
*/
private boolean playerIsOnGround(World w, int values[]) {
if((w.getBlockAt(values[0], values[2]-1, values[3]).getType() != Material.AIR ||
w.getBlockAt(values[0], values[2]-1, values[4]).getType() != Material.AIR ||
w.getBlockAt(values[0], values[2], values[3]).getType() != Material.AIR ||
w.getBlockAt(values[0], values[2], values[4]).getType() != Material.AIR ||
w.getBlockAt(values[0], values[2]+1, values[3]).getType() == Material.LADDER ||
w.getBlockAt(values[0], values[2]+1, values[4]).getType() == Material.LADDER) ||
(values[0] != values[1] && // May save some time by skipping half of the tests
(w.getBlockAt(values[1], values[2]-1, values[3]).getType() != Material.AIR ||
w.getBlockAt(values[1], values[2]-1, values[4]).getType() != Material.AIR ||
w.getBlockAt(values[1], values[2], values[3]).getType() != Material.AIR ||
w.getBlockAt(values[1], values[2], values[4]).getType() != Material.AIR ||
w.getBlockAt(values[1], values[2]+1, values[3]).getType() == Material.LADDER ||
w.getBlockAt(values[1], values[2]+1, values[4]).getType() == Material.LADDER)))
// Completely revamped collision detection
// What it does:
// Check the blocks below the player. If they aren't not solid (sic!) and the blocks directly above
// them aren't solid, The player is considered to be standing on the lower block
// Plus the player can hang onto a ladder that is one field above him
if( (types[w.getBlockTypeIdAt(values[0], values[2]-1, values[3])] != BlockType.NONSOLID &&
types[w.getBlockTypeIdAt(values[0], values[2], values[3])] != BlockType.SOLID) ||
(types[w.getBlockTypeIdAt(values[1], values[2]-1, values[3])] != BlockType.NONSOLID &&
types[w.getBlockTypeIdAt(values[1], values[2], values[3])] != BlockType.SOLID) ||
(types[w.getBlockTypeIdAt(values[0], values[2]-1, values[4])] != BlockType.NONSOLID &&
types[w.getBlockTypeIdAt(values[0], values[2], values[4])] != BlockType.SOLID) ||
(types[w.getBlockTypeIdAt(values[1], values[2]-1, values[4])] != BlockType.NONSOLID &&
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;
else
return false;