From 62dfce532db959c21cb19e2c2fd729d3bfae6cb5 Mon Sep 17 00:00:00 2001 From: Evenprime Date: Mon, 21 Feb 2011 19:23:05 +0100 Subject: [PATCH] Fixed logging + new anticheat mechanism Logging of moving violations (previously reset coordinates of event before logging, therefore the logged information was not useful at all). Added check for building. Building against thin air is not permitted in the client singleplayer game, so why should it be on a server? --- plugin.yml | 2 +- .../bukkit/nocheat/BlockPlacingCheck.java | 24 +++ .../evenprime/bukkit/nocheat/MovingCheck.java | 201 +++++++++--------- .../bukkit/nocheat/NoCheatPlugin.java | 43 +++- .../nocheat/NoCheatPluginBlockListener.java | 19 ++ .../bukkit/nocheat/NoCheatPluginData.java | 26 +++ .../nocheat/NoCheatPluginPlayerListener.java | 53 +---- .../nocheat/NoCheatPluginVehicleListener.java | 4 +- .../bukkit/nocheat/SpeedhackCheck.java | 1 - 9 files changed, 210 insertions(+), 163 deletions(-) create mode 100644 src/cc/co/evenprime/bukkit/nocheat/BlockPlacingCheck.java create mode 100644 src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginBlockListener.java create mode 100644 src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginData.java diff --git a/plugin.yml b/plugin.yml index be14f216..42fe71a6 100644 --- a/plugin.yml +++ b/plugin.yml @@ -3,5 +3,5 @@ name: NoCheatPlugin author: Evenprime main: cc.co.evenprime.bukkit.nocheat.NoCheatPlugin -version: 0.5.2 +version: 0.5.2a diff --git a/src/cc/co/evenprime/bukkit/nocheat/BlockPlacingCheck.java b/src/cc/co/evenprime/bukkit/nocheat/BlockPlacingCheck.java new file mode 100644 index 00000000..6e7cbf2b --- /dev/null +++ b/src/cc/co/evenprime/bukkit/nocheat/BlockPlacingCheck.java @@ -0,0 +1,24 @@ +package cc.co.evenprime.bukkit.nocheat; + +import org.bukkit.Material; +import org.bukkit.event.block.BlockPlaceEvent; + +public class BlockPlacingCheck { + + public static void check(BlockPlaceEvent event) { + + + if(NoCheatPlugin.Permissions != null && NoCheatPlugin.Permissions.has(event.getPlayer(), "nocheat.airbuild")) { + return; + } + else if(NoCheatPlugin.Permissions == null && event.getPlayer().isOp() ) { + return; + } + + if(event.getBlockAgainst().getType() == Material.AIR) { + event.setCancelled(true); + NoCheatPlugin.log.warning("NoCheatPlugin: Airbuild violation: "+event.getPlayer().getName()+" tried to place block " + event.getBlockPlaced().getType() + " against air"); + + } + } +} diff --git a/src/cc/co/evenprime/bukkit/nocheat/MovingCheck.java b/src/cc/co/evenprime/bukkit/nocheat/MovingCheck.java index a53c3b4e..9637fe8c 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/MovingCheck.java +++ b/src/cc/co/evenprime/bukkit/nocheat/MovingCheck.java @@ -6,7 +6,6 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.event.player.PlayerMoveEvent; -import cc.co.evenprime.bukkit.nocheat.NoCheatPluginPlayerListener.NoCheatPluginData; public class MovingCheck { @@ -134,114 +133,109 @@ public class MovingCheck { // First check the distance the player has moved horizontally // TODO: Make this check much more precise - if(!event.isCancelled()) { - double xDistance = Math.abs(from.getX() - to.getX()); - double zDistance = Math.abs(from.getZ() - to.getZ()); + double xDistance = Math.abs(from.getX() - to.getX()); + double zDistance = Math.abs(from.getZ() - to.getZ()); - // How far are we off? - if(xDistance > NoCheatConfiguration.movingDistanceHigh || zDistance > NoCheatConfiguration.movingDistanceHigh) { - vl = vl > HEAVY ? vl : HEAVY; - } - else if(xDistance > NoCheatConfiguration.movingDistanceMed || zDistance > NoCheatConfiguration.movingDistanceMed) { - vl = vl > NORMAL ? vl : NORMAL; - } - else if(xDistance > NoCheatConfiguration.movingDistanceLow || zDistance > NoCheatConfiguration.movingDistanceLow) { - vl = vl > MINOR ? vl : MINOR; - } + // How far are we off? + if(xDistance > NoCheatConfiguration.movingDistanceHigh || zDistance > NoCheatConfiguration.movingDistanceHigh) { + vl = vl > HEAVY ? vl : HEAVY; + } + else if(xDistance > NoCheatConfiguration.movingDistanceMed || zDistance > NoCheatConfiguration.movingDistanceMed) { + vl = vl > NORMAL ? vl : NORMAL; + } + else if(xDistance > NoCheatConfiguration.movingDistanceLow || zDistance > NoCheatConfiguration.movingDistanceLow) { + vl = vl > MINOR ? vl : MINOR; } - // If we didn't already cancel the event, check the vertical movement - if(!event.isCancelled()) { - // pre-calculate boundary values that are needed multiple times in the following checks - // the array each contains [lowerX, higherX, Y, lowerZ, higherZ] - int fromValues[] = {floor_double(from.getX() - 0.3D), (int)Math.floor(from.getX() + 0.3D), from.getBlockY(), floor_double(from.getZ() - 0.3D),(int)Math.floor(from.getZ() + 0.3D) }; - int toValues[] = {floor_double(to.getX() - 0.3D), (int)Math.floor(to.getX() + 0.3D), to.getBlockY(), floor_double(to.getZ() - 0.3D), (int)Math.floor(to.getZ() + 0.3D) }; + // pre-calculate boundary values that are needed multiple times in the following checks + // the array each contains [lowerX, higherX, Y, lowerZ, higherZ] + int fromValues[] = {floor_double(from.getX() - 0.3D), (int)Math.floor(from.getX() + 0.3D), from.getBlockY(), floor_double(from.getZ() - 0.3D),(int)Math.floor(from.getZ() + 0.3D) }; + int toValues[] = {floor_double(to.getX() - 0.3D), (int)Math.floor(to.getX() + 0.3D), to.getBlockY(), floor_double(to.getZ() - 0.3D), (int)Math.floor(to.getZ() + 0.3D) }; - // 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, from); - boolean onGroundTo = playerIsOnGround(from.getWorld(), toValues, to); + // 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, from); + boolean onGroundTo = playerIsOnGround(from.getWorld(), toValues, to); - // Both locations seem to be on solid ground or at a ladder - if(onGroundFrom && onGroundTo) - { - // reset jumping - data.phase = 0; + // Both locations seem to be on solid ground or at a ladder + if(onGroundFrom && onGroundTo) + { + // reset jumping + data.phase = 0; - // Check if the player isn't 'walking' up unrealistically far in one step - // Finally found out why this can happen: - // If a player runs into a wall at an angle from above, the game tries to - // place him above the block he bumped into, by placing him 0.5 m above - // the target block - if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { - - double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; - - if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; - else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; - else vl = vl > MINOR ? vl : MINOR; - } + // Check if the player isn't 'walking' up unrealistically far in one step + // Finally found out why this can happen: + // If a player runs into a wall at an angle from above, the game tries to + // place him above the block he bumped into, by placing him 0.5 m above + // the target block + if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { + + double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; + + if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; + else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; + else vl = vl > MINOR ? vl : MINOR; } - // player is starting to jump (or starting to fall down somewhere) - else if(onGroundFrom && !onGroundTo) - { - // reset jumping - data.phase = 0; + } + // player is starting to jump (or starting to fall down somewhere) + else if(onGroundFrom && !onGroundTo) + { + // reset jumping + data.phase = 0; - // Check if player isn't jumping too high - if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { - - double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; - - if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; - else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; - else vl = vl > MINOR ? vl : MINOR; - } - else if(to.getY() <= from.getY()) { - // Very special case if running over a cliff and then immediately jumping. - // Some sort of "air jump", MC allows it, so we have to do so too. - } - else data.phase++; // Setup next phase of the jump + // Check if player isn't jumping too high + if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { + + double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; + + if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; + else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; + else vl = vl > MINOR ? vl : MINOR; } - // player is probably landing somewhere - else if(!onGroundFrom && onGroundTo) - { - // Check if player isn't landing to high (sounds weird, but has its use) - if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { - - double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; - - if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; - else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; - else vl = vl > MINOR ? vl : MINOR; - } - else { - data.phase = 0; // He is on ground now, so reset the jump - } + else if(to.getY() <= from.getY()) { + // Very special case if running over a cliff and then immediately jumping. + // Some sort of "air jump", MC allows it, so we have to do so too. + } + else data.phase++; // Setup next phase of the jump + } + // player is probably landing somewhere + else if(!onGroundFrom && onGroundTo) + { + // Check if player isn't landing to high (sounds weird, but has its use) + if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { + + double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; + + if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; + else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; + else vl = vl > MINOR ? vl : MINOR; } - // Player is moving through air (during jumping, falling) else { - // May also be at the very edge of a platform (I seem to not be able to reliably tell if that's the case) - if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { - - double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; - - if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; - else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; - else vl = vl > MINOR ? vl : MINOR; - } - else { - data.phase++; // Enter next phase of the flight - } - } - - // do a security check on the jumping phase, such that we don't get - // OutOfArrayBoundsExceptions at long air times (falling off high places) - if(!(data.phase < jumpingPhases.length)) { - data.phase = jumpingPhases.length - 1; + data.phase = 0; // He is on ground now, so reset the jump } } - + // Player is moving through air (during jumping, falling) + else { + // May also be at the very edge of a platform (I seem to not be able to reliably tell if that's the case) + if(!(to.getY() - from.getY() < jumpingPhases[data.phase])) { + + double offset = (to.getY() - from.getY()) - jumpingPhases[data.phase]; + + if(offset > 2D) vl = vl > HEAVY ? vl : HEAVY; + else if(offset > 0.6D) vl = vl > NORMAL ? vl : NORMAL; + else vl = vl > MINOR ? vl : MINOR; + } + else { + data.phase++; // Enter next phase of the flight + } + } + + // do a security check on the jumping phase, such that we don't get + // OutOfArrayBoundsExceptions at long air times (falling off high places) + if(!(data.phase < jumpingPhases.length)) { + data.phase = jumpingPhases.length - 1; + } + // Treat the violation(s) switch(vl) { case MINOR: @@ -273,27 +267,30 @@ public class MovingCheck { } else if(data.minorViolationsInARow % 2 == 0) { // now we need it - resetPlayer(data, event); NoCheatPlugin.log.info("NoCheatPlugin: Moving violation: "+event.getPlayer().getName()+" from " + String.format("(%.5f, %.5f, %.5f) to (%.5f, %.5f, %.5f)", event.getFrom().getX(), event.getFrom().getY(), event.getFrom().getZ(), event.getTo().getX(), event.getTo().getY(), event.getTo().getZ())); + resetPlayer(data, event); } } protected static void normalViolation(NoCheatPluginData data, PlayerMoveEvent event) { - resetPlayer(data, event); - - data.normalViolationsInARow++; // Log the first violation in a row if(data.normalViolationsInARow <= 1) NoCheatPlugin.log.warning("NoCheatPlugin: Moving violation: "+event.getPlayer().getName()+" from " + String.format("(%.5f, %.5f, %.5f) to (%.5f, %.5f, %.5f)", event.getFrom().getX(), event.getFrom().getY(), event.getFrom().getZ(), event.getTo().getX(), event.getTo().getY(), event.getTo().getZ())); - } + + resetPlayer(data, event); + + data.normalViolationsInARow++; + } protected static void heavyViolation(NoCheatPluginData data, PlayerMoveEvent event) { + + if(data.heavyViolationsInARow == 0) + NoCheatPlugin.log.severe("NoCheatPlugin: Moving violation: "+event.getPlayer().getName()+" from " + String.format("(%.5f, %.5f, %.5f) to (%.5f, %.5f, %.5f)", event.getFrom().getX(), event.getFrom().getY(), event.getFrom().getZ(), event.getTo().getX(), event.getTo().getY(), event.getTo().getZ())); + resetPlayer(data, event); data.heavyViolationsInARow++; // Log the first violation in a row - if(data.heavyViolationsInARow <= 1) - NoCheatPlugin.log.severe("NoCheatPlugin: Moving violation: "+event.getPlayer().getName()+" from " + String.format("(%.5f, %.5f, %.5f) to (%.5f, %.5f, %.5f)", event.getFrom().getX(), event.getFrom().getY(), event.getFrom().getZ(), event.getTo().getX(), event.getTo().getY(), event.getTo().getZ())); } protected static void legitimateMove(NoCheatPluginData data, PlayerMoveEvent event) { diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java index 631db67b..536f4593 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java @@ -1,8 +1,11 @@ package cc.co.evenprime.bukkit.nocheat; import java.io.File; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Logger; +import org.bukkit.entity.Player; import org.bukkit.event.Event.Priority; import org.bukkit.event.Event; import org.bukkit.plugin.PluginDescriptionFile; @@ -25,17 +28,39 @@ public class NoCheatPlugin extends JavaPlugin { private final NoCheatPluginPlayerListener playerListener; private final NoCheatPluginVehicleListener vehicleListener; + private final NoCheatPluginBlockListener blockListener; public static Logger log; public static PermissionHandler Permissions = null; + + // Store data between Events + public static Map playerData = new HashMap(); public NoCheatPlugin() { - playerListener = new NoCheatPluginPlayerListener(this); - vehicleListener = new NoCheatPluginVehicleListener(this, playerListener); + playerListener = new NoCheatPluginPlayerListener(); + vehicleListener = new NoCheatPluginVehicleListener(playerListener); + blockListener = new NoCheatPluginBlockListener(); log = NoCheatConfiguration.logger; } + + public static NoCheatPluginData getPlayerData(Player p) { + NoCheatPluginData data = null; + + if((data = playerData.get(p)) == null ) { + synchronized(playerData) { + data = playerData.get(p); + if(data == null) { + // If we have no data for the player, create some + data = new NoCheatPluginData(); + playerData.put(p, data); + } + } + } + + return data; + } public void onDisable() { } @@ -47,6 +72,7 @@ public class NoCheatPlugin extends JavaPlugin { pm.registerEvent(Event.Type.PLAYER_QUIT, playerListener, Priority.Monitor, this); pm.registerEvent(Event.Type.VEHICLE_EXIT, vehicleListener, Priority.Monitor, this); pm.registerEvent(Event.Type.VEHICLE_DAMAGE, vehicleListener, Priority.Monitor, this); + pm.registerEvent(Event.Type.BLOCK_PLACED, blockListener, Priority.Low, this); PluginDescriptionFile pdfFile = this.getDescription(); Logger.getLogger("Minecraft").info( "NoCheat version " + pdfFile.getVersion() + " is enabled!" ); @@ -58,14 +84,11 @@ public class NoCheatPlugin extends JavaPlugin { public void setupPermissions() { Plugin test = this.getServer().getPluginManager().getPlugin("Permissions"); - - if(Permissions == null) { - if(test != null) { - Permissions = ((Permissions)test).getHandler(); - } else { - log.info("Nocheat couldn't find Permissions plugin. Fallback to OP -> all allowed."); - this.getServer().getPluginManager().disablePlugin(this); - } + if(test != null) { + Permissions = ((Permissions)test).getHandler(); + } else { + log.info("Nocheat couldn't find Permissions plugin. Fallback to 'isOp()' equals 'all allowed'."); + this.getServer().getPluginManager().disablePlugin(this); } } diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginBlockListener.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginBlockListener.java new file mode 100644 index 00000000..f7b79b5a --- /dev/null +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginBlockListener.java @@ -0,0 +1,19 @@ +package cc.co.evenprime.bukkit.nocheat; + +import org.bukkit.event.block.BlockListener; +import org.bukkit.event.block.BlockPlaceEvent; + +public class NoCheatPluginBlockListener extends BlockListener { + + + public NoCheatPluginBlockListener() { + + } + + @Override + public void onBlockPlace(BlockPlaceEvent event) { + + if(!event.isCancelled() && NoCheatConfiguration.speedhackCheckActive) + BlockPlacingCheck.check(event); + } +} diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginData.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginData.java new file mode 100644 index 00000000..9aa499b8 --- /dev/null +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginData.java @@ -0,0 +1,26 @@ +package cc.co.evenprime.bukkit.nocheat; + +import org.bukkit.Location; + +/** + * Storage for data persistence between events + * + */ +public class NoCheatPluginData { + + /** + * Don't rely on any of these yet, they are likely going to + * change their name/functionality + */ + int phase = 0; // current jumpingPhase + long lastSpeedHackCheck = System.currentTimeMillis(); // timestamp of last check for speedhacks + int eventsSinceLastSpeedHackCheck = 0; // used to identify speedhacks + int ignoreNextXEvents = 0; + + int minorViolationsInARow = 0; + int normalViolationsInARow = 0; + int heavyViolationsInARow = 0; + Location movingSetBackPoint = null; + + NoCheatPluginData() { } +} \ No newline at end of file diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginPlayerListener.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginPlayerListener.java index 197878d4..fd703381 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginPlayerListener.java +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginPlayerListener.java @@ -1,12 +1,8 @@ package cc.co.evenprime.bukkit.nocheat; -import java.util.HashMap; -import java.util.Map; -import org.bukkit.Location; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerListener; import org.bukkit.event.player.PlayerMoveEvent; @@ -18,47 +14,18 @@ import org.bukkit.event.player.PlayerMoveEvent; public class NoCheatPluginPlayerListener extends PlayerListener { - /** - * Storage for data persistence between events - * - */ - public class NoCheatPluginData { - - /** - * Don't rely on any of these yet, they are likely going to - * change their name/functionality - */ - int phase = 0; // current jumpingPhase - long lastSpeedHackCheck = System.currentTimeMillis(); // timestamp of last check for speedhacks - int eventsSinceLastSpeedHackCheck = 0; // used to identify speedhacks - int ignoreNextXEvents = 0; - - int minorViolationsInARow = 0; - int normalViolationsInARow = 0; - int heavyViolationsInARow = 0; - Location movingSetBackPoint = null; - - private NoCheatPluginData() { } - } - - private final NoCheatPlugin plugin; - - // Store data between Events - private static Map playerData = new HashMap(); - - - public NoCheatPluginPlayerListener(NoCheatPlugin instance) { - plugin = instance; - } + + + public NoCheatPluginPlayerListener() { } @Override public void onPlayerQuit(PlayerEvent event) { - playerData.remove(event.getPlayer()); + NoCheatPlugin.playerData.remove(event.getPlayer()); } public void ingoreNextXEvents(Entity player, int count) { - NoCheatPluginData data = playerData.get(player); + NoCheatPluginData data = NoCheatPlugin.playerData.get(player); if(data != null) { data.ignoreNextXEvents = count; } @@ -68,14 +35,8 @@ public class NoCheatPluginPlayerListener extends PlayerListener { public void onPlayerMove(PlayerMoveEvent event) { // Get the player-specific data - NoCheatPluginData data = null; - - if((data = playerData.get(event.getPlayer())) == null ) { - // If we have no data for the player, create some - data = new NoCheatPluginData(); - playerData.put(event.getPlayer(), data); - } - + NoCheatPluginData data = NoCheatPlugin.getPlayerData(event.getPlayer()); + if(data.ignoreNextXEvents > 0 ) { data.ignoreNextXEvents--; return; diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginVehicleListener.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginVehicleListener.java index 450ea4f1..b3fc1823 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginVehicleListener.java +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPluginVehicleListener.java @@ -12,11 +12,9 @@ import org.bukkit.event.vehicle.VehicleListener; public class NoCheatPluginVehicleListener extends VehicleListener { - private final NoCheatPlugin plugin; private final NoCheatPluginPlayerListener playerListener; - public NoCheatPluginVehicleListener(NoCheatPlugin plugin, NoCheatPluginPlayerListener playerListener) { - this.plugin = plugin; + public NoCheatPluginVehicleListener(NoCheatPluginPlayerListener playerListener) { this.playerListener = playerListener; } diff --git a/src/cc/co/evenprime/bukkit/nocheat/SpeedhackCheck.java b/src/cc/co/evenprime/bukkit/nocheat/SpeedhackCheck.java index 760cd8bb..2beb73c5 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/SpeedhackCheck.java +++ b/src/cc/co/evenprime/bukkit/nocheat/SpeedhackCheck.java @@ -2,7 +2,6 @@ package cc.co.evenprime.bukkit.nocheat; import org.bukkit.event.player.PlayerMoveEvent; -import cc.co.evenprime.bukkit.nocheat.NoCheatPluginPlayerListener.NoCheatPluginData; public class SpeedhackCheck {