diff --git a/plugin.yml b/plugin.yml index 89284013..fbaab60b 100644 --- a/plugin.yml +++ b/plugin.yml @@ -3,7 +3,7 @@ name: NoCheatPlugin author: Evenprime main: cc.co.evenprime.bukkit.nocheat.NoCheatPlugin -version: 0.7.3 +version: 0.7.3a commands: nocheat: diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatConfiguration.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatConfiguration.java index 02fa2649..752e907d 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/NoCheatConfiguration.java +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatConfiguration.java @@ -46,7 +46,15 @@ public class NoCheatConfiguration { public static String movingActionHeavy = ""; // How should airbuild violations be treated? - public static String airbuildAction = ""; + public static String airbuildActionLow = ""; + public static String airbuildActionMed = ""; + public static String airbuildActionHigh = ""; + + public static Runnable airbuildRunnable = null; + + public static int airbuildLimitLow = 1; + public static int airbuildLimitMed = 3; + public static int airbuildLimitHigh = 10; // The log level above which information gets logged to the specified logger public static Level chatLevel = Level.OFF; @@ -87,37 +95,42 @@ public class NoCheatConfiguration { e.printStackTrace(); } } - + chatLevel = stringToLevel(c.getString("logging.logtonotify")); // deprecated, will be deleted eventually chatLevel = stringToLevel(c.getString("logging.logtochat")); consoleLevel = stringToLevel(c.getString("logging.logtoconsole")); - + ircLevel = stringToLevel(c.getString("logging.logtoirc")); ircTag = c.getString("logging.logtoirctag", "nocheat"); - + speedhackCheckActive = c.getBoolean("active.speedhack", true); movingCheckActive = c.getBoolean("active.moving", true); airbuildCheckActive = c.getBoolean("active.airbuild", false); bedteleportCheckActive = c.getBoolean("active.bedteleport", true); - + speedhackLimitLow = c.getInt("speedhack.limits.low", 30); speedhackLimitMed = c.getInt("speedhack.limits.med", 45); speedhackLimitHigh = c.getInt("speedhack.limits.high", 60); - + movingFreeMoves = c.getInt("moving.freemoves", 1); - + movingActionMinor = c.getString("moving.action.low", "loglow reset"); movingActionNormal = c.getString("moving.action.med", "logmed reset"); movingActionHeavy = c.getString("moving.action.high", "loghigh reset"); - + speedhackActionMinor = c.getString("speedhack.action.low", "loglow reset"); speedhackActionNormal = c.getString("speedhack.action.med", "logmed reset"); speedhackActionHeavy = c.getString("speedhack.action.high", "loghigh reset"); + + airbuildLimitLow = c.getInt("airbuild.limits.low", 1); + airbuildLimitMed = c.getInt("airbuild.limits.med", 3); + airbuildLimitHigh = c.getInt("airbuild.limits.high", 10); - airbuildAction = c.getString("airbuild.action", "logmed deny"); - - System.out.println(airbuildAction); - + airbuildActionLow = c.getString("airbuild.action.low", "loglow deny"); + airbuildActionMed = c.getString("airbuild.action.med", "logmed deny"); + airbuildActionHigh = c.getString("airbuild.action.high", "loghigh deny"); + + // 1 is minimum. This is needed to smooth over some minecraft bugs like // when a minecart gets broken while a player is inside it (which causes the player to "move" // up 1.0D which is much more than a usual jump would allow in 1 event @@ -140,7 +153,7 @@ public class NoCheatConfiguration { if(string.trim().equals("severe")|| string.trim().equals("high")) return Level.SEVERE; return Level.OFF; } - + /** * Standard configuration file for people who haven't got one yet * @param f @@ -150,7 +163,7 @@ public class NoCheatConfiguration { f.getParentFile().mkdirs(); f.createNewFile(); BufferedWriter w = new BufferedWriter(new FileWriter(f)); - + w.write("# Logging: potential log levels are low (info), med (warn), high (severe), off"); w.newLine(); w.write("logging:"); w.newLine(); w.write(" filename: plugins/NoCheat/nocheat.log"); w.newLine(); @@ -187,8 +200,16 @@ public class NoCheatConfiguration { w.write(" high: loghigh reset"); w.newLine(); w.write("# Airbuild specific options"); w.newLine(); w.write("airbuild:"); w.newLine(); + w.write("# How many blocks per second are placed by the player in midair (determines log level)"); w.newLine(); + w.write(" limits:"); w.newLine(); + w.write(" low: 1"); w.newLine(); + w.write(" med: 3"); w.newLine(); + w.write(" high: 10"); w.newLine(); w.write("# Airbuild Action, one or more of 'loglow logmed loghigh deny'"); w.newLine(); - w.write(" action: logmed deny"); w.newLine(); + w.write(" action:"); w.newLine(); + w.write(" low: loglow deny"); w.newLine(); + w.write(" med: logmed deny"); w.newLine(); + w.write(" high: loghigh deny"); w.newLine(); w.write("# Bedteleport specific options (none exist yet)"); w.newLine(); w.write("bedteleport:"); w.newLine(); diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatData.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatData.java index 5a0dabb0..da769098 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/NoCheatData.java +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatData.java @@ -21,17 +21,19 @@ public class NoCheatData { public int movingNormalViolationsInARow = 0; public int movingHeavyViolationsInARow = 0; public World movingLastWorld = null; - - public boolean reset = false; - public int movingHorizFreeMoves = 4; public Location movingSetBackPoint = null; - + public Location movingLocation = null; + public boolean reset = false; + public long speedhackLastCheck = System.currentTimeMillis(); // timestamp of last check for speedhacks public Location speedhackSetBackPoint = null; public int speedhackEventsSinceLastCheck = 0; // used to identify speedhacks public int speedhackViolationsInARow = 0; - public Location movingLocation = null; + + public int airbuildPerSecond = 0; + public Runnable airbuildRunnable = null; + NoCheatData() { } } \ No newline at end of file diff --git a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java index 98536e25..9f0d2053 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java +++ b/src/cc/co/evenprime/bukkit/nocheat/NoCheatPlugin.java @@ -43,7 +43,7 @@ public class NoCheatPlugin extends JavaPlugin { private static Logger consoleLogger; private static Logger fileLogger; - private static NoCheatPlugin p; + public static NoCheatPlugin p; // Permissions 2.0, if available public static PermissionHandler Permissions = null; diff --git a/src/cc/co/evenprime/bukkit/nocheat/checks/AirbuildCheck.java b/src/cc/co/evenprime/bukkit/nocheat/checks/AirbuildCheck.java index ec0ce79f..698c7c3d 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/checks/AirbuildCheck.java +++ b/src/cc/co/evenprime/bukkit/nocheat/checks/AirbuildCheck.java @@ -2,9 +2,11 @@ package cc.co.evenprime.bukkit.nocheat.checks; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.entity.Player; import org.bukkit.event.block.BlockPlaceEvent; import cc.co.evenprime.bukkit.nocheat.NoCheatConfiguration; +import cc.co.evenprime.bukkit.nocheat.NoCheatData; import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin; @@ -23,19 +25,61 @@ public class AirbuildCheck { if(NoCheatPlugin.hasPermission(event.getPlayer(), "nocheat.airbuild")) return; - // Are all 6 sides "air-blocks" -> cancel the event - if(event.getBlockAgainst().getType() == Material.AIR) - action(NoCheatConfiguration.airbuildAction, event); - - } + if(event.getBlockAgainst().getType() == Material.AIR) { + final NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer()); + final Player p = event.getPlayer(); + + if(data.airbuildRunnable == null) { + data.airbuildRunnable = new Runnable() { - private static void action(String action, BlockPlaceEvent event) { - + @Override + public void run() { + summary(p, data); + // deleting its own reference + data.airbuildRunnable = null; + } + }; + + // Give a summary in 50 ticks ~ 1 second + NoCheatPlugin.p.getServer().getScheduler().scheduleAsyncDelayedTask(NoCheatPlugin.p, data.airbuildRunnable, 50); + } + + data.airbuildPerSecond++; + + boolean log = false; + // Only explicitly log certain "milestones" + if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitHigh) { + if(data.airbuildPerSecond == NoCheatConfiguration.airbuildLimitHigh) { + log = true; + } + action(NoCheatConfiguration.airbuildActionHigh, event, log); + } + else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitMed) { + if(data.airbuildPerSecond == NoCheatConfiguration.airbuildLimitMed) { + log = true; + } + action(NoCheatConfiguration.airbuildActionMed, event, log); + } + else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitLow) { + if(data.airbuildPerSecond == NoCheatConfiguration.airbuildLimitLow) { + log = true; + } + action(NoCheatConfiguration.airbuildActionLow, event, log); + } + else + { + // ignore for now + } + } + } + + private static void action(String action, BlockPlaceEvent event, boolean log) { + // LOG IF NEEDED - if(action.contains("log")) { + if(log && action.contains("log")) { Location l = event.getBlockPlaced().getLocation(); - NoCheatPlugin.logAction(action, "NoCheatPlugin: Airbuild violation: "+event.getPlayer().getName()+" tried to place block " + event.getBlockPlaced().getType() + " in the air at " + l.getBlockX() + "," + l.getBlockY() +"," + l.getBlockZ()); + NoCheatPlugin.logAction(action, "Airbuild violation: "+event.getPlayer().getName()+" tried to place block " + event.getBlockPlaced().getType() + " in the air at " + l.getBlockX() + "," + l.getBlockY() +"," + l.getBlockZ()); } // DENY IF NEEDED @@ -43,4 +87,22 @@ public class AirbuildCheck { event.setCancelled(true); } } + + private static void summary(Player player, NoCheatData data) { + + String logLine = "Airbuild violation summary: " +player.getName() + " total events per second: " + data.airbuildPerSecond; + + // Give a summary according to the highest violation level we encountered in that second + if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitHigh) { + NoCheatPlugin.logAction(NoCheatConfiguration.airbuildActionHigh, logLine); + } + else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitMed) { + NoCheatPlugin.logAction(NoCheatConfiguration.airbuildActionMed, logLine); + } + else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitLow) { + NoCheatPlugin.logAction(NoCheatConfiguration.airbuildActionLow, logLine); + } + + data.airbuildPerSecond = 0; + } } diff --git a/src/cc/co/evenprime/bukkit/nocheat/checks/MovingCheck.java b/src/cc/co/evenprime/bukkit/nocheat/checks/MovingCheck.java index 9385d2e6..18f0d14a 100644 --- a/src/cc/co/evenprime/bukkit/nocheat/checks/MovingCheck.java +++ b/src/cc/co/evenprime/bukkit/nocheat/checks/MovingCheck.java @@ -22,7 +22,7 @@ public class MovingCheck { // previously-calculated upper bound values for jumps. Minecraft is very deterministic when it comes to jumps // Each entry represents the maximum gain in height per move event. - static final int jumpingLimit = 4; + static final int jumpingLimit = 1; static final double jumpingHeightLimit = 1.3D; static double stepHeight = 0.501D; @@ -132,7 +132,7 @@ public class MovingCheck { types[Material.CAKE_BLOCK.getId()]= BlockType.UNKNOWN; } - public static void check(PlayerMoveEvent event) { + public static void check(final PlayerMoveEvent event) { // Should we check at all if(NoCheatPlugin.hasPermission(event.getPlayer(), "nocheat.moving")) @@ -140,7 +140,7 @@ public class MovingCheck { // Get the player-specific data NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer()); - + // Notice to myself: How world changes with e.g. command /world work: // 1. TeleportEvent from the players current position to another position in the _same_ world // 2. MoveEvent(s) (yes, multiple events can be triggered) from that position in the _new_ world @@ -175,12 +175,19 @@ public class MovingCheck { data.movingLocation = null; } - + // Ignore vehicles if(event.getPlayer().isInsideVehicle()) { data.movingSetBackPoint = null; data.speedhackSetBackPoint = null; return; } + + // The server believes the player should be moved upward, so we ignore this + if(event.getPlayer().getVelocity().getY() > 0) { + data.movingSetBackPoint = null; + data.speedhackSetBackPoint = null; + return; + } // The actual movingCheck starts here // Get the two locations of the event