New movement check model + fixed logging to console

New move model works with absolute limits instead of relative limits
for height checks.
This commit is contained in:
Evenprime 2011-03-15 16:25:11 +01:00
parent c5c67c4f7f
commit 6fd8c775c5
4 changed files with 60 additions and 49 deletions

View File

@ -3,7 +3,7 @@ name: NoCheatPlugin
author: Evenprime author: Evenprime
main: cc.co.evenprime.bukkit.nocheat.NoCheatPlugin main: cc.co.evenprime.bukkit.nocheat.NoCheatPlugin
version: 0.6.9 version: 0.6.9a
commands: commands:
nocheat: nocheat:

View File

@ -4,7 +4,6 @@ import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler; import java.util.logging.FileHandler;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -20,7 +19,7 @@ import org.bukkit.util.config.Configuration;
public class NoCheatConfiguration { public class NoCheatConfiguration {
// Our personal logger // Our personal logger
public static final String loggerName = "cc.co.evenprime.bukkit.nocheat"; public static final String loggerName = "cc.co.evenprime.nocheat";
public static final Logger logger = Logger.getLogger(loggerName); public static final Logger logger = Logger.getLogger(loggerName);
// Which checks are active // Which checks are active
@ -39,7 +38,7 @@ public class NoCheatConfiguration {
public static String speedhackActionNormal = ""; public static String speedhackActionNormal = "";
public static String speedhackActionHeavy = ""; public static String speedhackActionHeavy = "";
public static int movingFreeMoves = 5; public static int movingFreeMoves = 1;
// How should moving violations be treated? // How should moving violations be treated?
public static String movingActionMinor = ""; public static String movingActionMinor = "";
@ -49,14 +48,14 @@ public class NoCheatConfiguration {
// How should airbuild violations be treated? // How should airbuild violations be treated?
public static String airbuildAction = ""; public static String airbuildAction = "";
// The log level above which players with the permission nocheat.notify will get informed about violations // The log level above which information gets logged to the specified logger
public static Level chatLevel = Level.OFF; public static Level chatLevel = Level.OFF;
public static Level ircLevel = Level.OFF; public static Level ircLevel = Level.OFF;
public static Level consoleLevel = Level.OFF;
public static String ircTag = ""; public static String ircTag = "";
// Our two log outputs, the console and a file // Our log output to a file
private static ConsoleHandler ch = null;
private static FileHandler fh = null; private static FileHandler fh = null;
private NoCheatConfiguration() {} private NoCheatConfiguration() {}
@ -75,16 +74,6 @@ public class NoCheatConfiguration {
logger.setLevel(Level.INFO); logger.setLevel(Level.INFO);
logger.setUseParentHandlers(false); logger.setUseParentHandlers(false);
if(ch == null) {
ch = new ConsoleHandler();
ch.setLevel(stringToLevel(c.getString("logging.logtoconsole")));
ch.setFormatter(Logger.getLogger("Minecraft").getHandlers()[0].getFormatter());
logger.addHandler(ch);
}
if(fh == null) { if(fh == null) {
try { try {
@ -101,6 +90,7 @@ public class NoCheatConfiguration {
chatLevel = stringToLevel(c.getString("logging.logtonotify")); // deprecated, will be deleted eventually chatLevel = stringToLevel(c.getString("logging.logtonotify")); // deprecated, will be deleted eventually
chatLevel = stringToLevel(c.getString("logging.logtochat")); chatLevel = stringToLevel(c.getString("logging.logtochat"));
consoleLevel = stringToLevel(c.getString("logging.logtoconsole"));
ircLevel = stringToLevel(c.getString("logging.logtoirc")); ircLevel = stringToLevel(c.getString("logging.logtoirc"));
ircTag = c.getString("logging.logtoirctag", "nocheat"); ircTag = c.getString("logging.logtoirctag", "nocheat");
@ -114,7 +104,7 @@ public class NoCheatConfiguration {
speedhackLimitMed = c.getInt("speedhack.limits.med", 45); speedhackLimitMed = c.getInt("speedhack.limits.med", 45);
speedhackLimitHigh = c.getInt("speedhack.limits.high", 60); speedhackLimitHigh = c.getInt("speedhack.limits.high", 60);
movingFreeMoves = c.getInt("moving.freemoves", 5); movingFreeMoves = c.getInt("moving.freemoves", 1);
movingActionMinor = c.getString("moving.action.low", "loglow reset"); movingActionMinor = c.getString("moving.action.low", "loglow reset");
movingActionNormal = c.getString("moving.action.med", "logmed reset"); movingActionNormal = c.getString("moving.action.med", "logmed reset");
@ -187,7 +177,7 @@ public class NoCheatConfiguration {
w.write("# Moving specific options") ; w.newLine(); w.write("# Moving specific options") ; w.newLine();
w.write("moving:"); w.newLine(); w.write("moving:"); w.newLine();
w.write("# After how many minor violations should the plugin react (minimum 1)"); w.newLine(); w.write("# After how many minor violations should the plugin react (minimum 1)"); w.newLine();
w.write(" freemoves: 5"); w.newLine(); w.write(" freemoves: 1"); w.newLine();
w.write("# Moving Action, one or more of 'loglow logmed loghigh reset'"); w.newLine(); w.write("# Moving Action, one or more of 'loglow logmed loghigh reset'"); w.newLine();
w.write(" action:"); w.newLine(); w.write(" action:"); w.newLine();
w.write(" low: loglow reset"); w.newLine(); w.write(" low: loglow reset"); w.newLine();

View File

@ -40,7 +40,8 @@ public class NoCheatPlugin extends JavaPlugin {
private NoCheatEntityListener entityListener; private NoCheatEntityListener entityListener;
// My main logger // My main logger
private static Logger log; private static Logger consoleLogger;
private static Logger fileLogger;
private static NoCheatPlugin p; private static NoCheatPlugin p;
@ -139,7 +140,8 @@ public class NoCheatPlugin extends JavaPlugin {
blockListener = new NoCheatBlockListener(); blockListener = new NoCheatBlockListener();
entityListener = new NoCheatEntityListener(); entityListener = new NoCheatEntityListener();
log = NoCheatConfiguration.logger; fileLogger = NoCheatConfiguration.logger;
consoleLogger = Logger.getLogger("Minecraft");
PluginManager pm = getServer().getPluginManager(); PluginManager pm = getServer().getPluginManager();
pm.registerEvent(Event.Type.PLAYER_MOVE, playerListener, Priority.Lowest, this); // used for speedhack and moving checks pm.registerEvent(Event.Type.PLAYER_MOVE, playerListener, Priority.Lowest, this); // used for speedhack and moving checks
@ -212,7 +214,8 @@ public class NoCheatPlugin extends JavaPlugin {
if(l != null) { if(l != null) {
logToChat(l, message); logToChat(l, message);
logToIRC(l, message); logToIRC(l, message);
log.log(l, message); logToConsole(l, message);
fileLogger.log(l, message);
} }
} }
@ -232,6 +235,12 @@ public class NoCheatPlugin extends JavaPlugin {
} }
} }
private static void logToConsole(Level l, String message) {
if( NoCheatConfiguration.consoleLevel.intValue() <= l.intValue()) {
consoleLogger.log(l, message);
}
}
public static void logAction(String actions, String message) { public static void logAction(String actions, String message) {
if(actions == null) return; if(actions == null) return;

View File

@ -22,7 +22,9 @@ public class MovingCheck {
// previously-calculated upper bound values for jumps. Minecraft is very deterministic when it comes to jumps // 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. // 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.49D }; static final int jumpingLimit = 3;
static final double jumpingHeightLimit = 1.3D;
static double stepHeight = 0.501D;
// Limits for the moving check // Limits for the moving check
public static double movingDistanceLow = 0.1D; public static double movingDistanceLow = 0.1D;
@ -183,7 +185,7 @@ public class MovingCheck {
// 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, from); boolean onGroundFrom = playerIsOnGround(from.getWorld(), fromValues, from);
boolean onGroundTo = playerIsOnGround(from.getWorld(), toValues, to); boolean onGroundTo = playerIsOnGround(to.getWorld(), toValues, to);
// Both locations seem to be on solid ground or at a ladder // Both locations seem to be on solid ground or at a ladder
@ -194,9 +196,9 @@ public class MovingCheck {
// If a player runs into a wall, the game tries to // If a player runs into a wall, the game tries to
// place him above the block he bumped into, by placing him 0.5 m above // place him above the block he bumped into, by placing him 0.5 m above
// the target block // the target block
if(!(to.getY() - from.getY() < jumpingPhases[0])) { if(!(to.getY() - from.getY() < stepHeight)) {
double offset = (to.getY() - from.getY()) - jumpingPhases[0]; double offset = (to.getY() - from.getY()) - stepHeight;
if(offset > 2D) vl = max(vl, Level.SEVERE); if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING); else if(offset > 0.5D) vl = max(vl, Level.WARNING);
@ -206,17 +208,19 @@ public class MovingCheck {
{ {
// reset jumping // reset jumping
data.movingJumpPhase = 0; data.movingJumpPhase = 0;
data.movingSetBackPoint = event.getTo().clone(); data.movingSetBackPoint = from.clone();
} }
} }
// player is starting to jump (or starting to fall down somewhere) // player is starting to jump (or starting to fall down somewhere)
else if(onGroundFrom && !onGroundTo) else if(onGroundFrom && !onGroundTo)
{ {
// Check if player isn't jumping too high // Check if player isn't jumping too high
if(!(to.getY() - from.getY() < jumpingPhases[0])) { double limit = jumpingHeightLimit;
double offset = (to.getY() - from.getY()) - jumpingPhases[0]; if(to.getY() - from.getY() > limit) {
double offset = (to.getY() - from.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE); if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING); else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO); else vl = max(vl, Level.INFO);
@ -224,16 +228,21 @@ public class MovingCheck {
else { else {
// Setup next phase of the jump // Setup next phase of the jump
data.movingJumpPhase = 1; data.movingJumpPhase = 1;
data.movingSetBackPoint = event.getFrom().clone(); data.movingSetBackPoint = from.clone();
} }
} }
// player is probably landing somewhere // player is probably landing somewhere
else if(!onGroundFrom && onGroundTo) else if(!onGroundFrom && onGroundTo)
{ {
// Check if player isn't landing to high (sounds weird, but has its use) // Check if player isn't landing to high (sounds weird, but has its use)
if(!(to.getY() - from.getY() < Math.max(jumpingPhases[data.movingJumpPhase], 0D))) { Location l = data.movingSetBackPoint;
if(l == null) { l = from; }
double limit = jumpingHeightLimit;
if(to.getY() - l.getY() > limit) {
double offset = (to.getY() - from.getY()) - Math.max(jumpingPhases[data.movingJumpPhase], 0D); double offset = (to.getY() - l.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE); if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING); else if(offset > 0.5D) vl = max(vl, Level.WARNING);
@ -241,32 +250,35 @@ public class MovingCheck {
} }
else { else {
data.movingJumpPhase = 0; // He is on ground now, so reset the jump data.movingJumpPhase = 0; // He is on ground now, so reset the jump
data.movingSetBackPoint = event.getTo().clone(); data.movingSetBackPoint = to.clone();
} }
} }
// Player is moving through air (during jumping, falling) // Player is moving through air (during jumping, falling)
else { else {
// Check if player isn't landing to high (sounds weird, but has its use)
Location l = data.movingSetBackPoint;
if(l == null) { l = from; }
if(!(to.getY() - from.getY() < jumpingPhases[data.movingJumpPhase])) // The expected fall velocity is a rough estimate - players usually accelerate by that value
double limit = (data.movingJumpPhase > jumpingLimit) ? jumpingHeightLimit - (data.movingJumpPhase-jumpingLimit) * 0.2D : jumpingHeightLimit;
if(to.getY() - l.getY() > limit)
{ {
double offset = (to.getY() - from.getY()) - jumpingPhases[data.movingJumpPhase]; double offset = (to.getY() - l.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE); if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 1D) vl = max(vl, Level.WARNING); else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO); else vl = max(vl, Level.INFO);
} }
else { else {
data.movingJumpPhase++; // Enter next phase of the flight data.movingJumpPhase++; // Enter next phase of the flight
// Setback point stays the same // Setback point stays the same. IF we don't have one, take the "from" location as a setback point for now
if(data.movingSetBackPoint == null) {
data.movingSetBackPoint = from.clone();
}
} }
} }
// 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.movingJumpPhase < jumpingPhases.length)) {
data.movingJumpPhase = jumpingPhases.length - 1;
}
if(vl == null && (onGroundFrom || onGroundTo)) { if(vl == null && (onGroundFrom || onGroundTo)) {
legitimateMove(data, event); legitimateMove(data, event);
} }
@ -317,14 +329,14 @@ public class MovingCheck {
data.movingHeavyViolationsInARow++; data.movingHeavyViolationsInARow++;
actions = NoCheatConfiguration.movingActionHeavy; actions = NoCheatConfiguration.movingActionHeavy;
} }
action(event, actions, log); action(event, actions, log);
} }
} }
/** /**
* Perform actions that were specified by the player * Perform actions that were specified by the admin
* @param event * @param event
* @param actions * @param actions
*/ */
@ -347,7 +359,7 @@ public class MovingCheck {
protected static void legitimateMove(NoCheatData data, PlayerMoveEvent event) { protected static void legitimateMove(NoCheatData data, PlayerMoveEvent event) {
// Give some logging about violations if the player hasn't done any for at least two seconds // Give some logging about violations if the player hasn't done any for at least two seconds
if(data.movingLastViolationTime != 0 && data.movingLastViolationTime + 1000L > System.currentTimeMillis()) { if(data.movingLastViolationTime != 0 && data.movingLastViolationTime + 2000L < System.currentTimeMillis()) {
data.movingLastViolationTime = 0; data.movingLastViolationTime = 0;
@ -360,7 +372,7 @@ public class MovingCheck {
NoCheatPlugin.logAction(NoCheatConfiguration.movingActionNormal, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingNormalViolationsInARow); NoCheatPlugin.logAction(NoCheatConfiguration.movingActionNormal, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingNormalViolationsInARow);
data.movingNormalViolationsInARow = 0; data.movingNormalViolationsInARow = 0;
} }
if(data.movingMinorViolationsInARow > NoCheatConfiguration.movingFreeMoves +1) { if(data.movingMinorViolationsInARow > NoCheatConfiguration.movingFreeMoves) {
NoCheatPlugin.logAction(NoCheatConfiguration.movingActionMinor, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingMinorViolationsInARow); NoCheatPlugin.logAction(NoCheatConfiguration.movingActionMinor, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingMinorViolationsInARow);
data.movingMinorViolationsInARow = 0; data.movingMinorViolationsInARow = 0;
} }