v0.7.8: Fixed config file creation/initialization + Walking on Fence

no longer causes false positives
This commit is contained in:
Evenprime 2011-04-03 22:43:31 +02:00
parent c47c2d98e8
commit 3f7f537fc0
10 changed files with 171 additions and 154 deletions

View File

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

View File

@ -29,9 +29,12 @@ public class NoCheatConfiguration {
public final Logger logger = Logger.getLogger(loggerName);
// The log level above which information gets logged to the specified logger
public Level chatLevel = Level.OFF;
public Level ircLevel = Level.OFF;
public Level consoleLevel = Level.OFF;
public Level chatLevel = Level.WARNING;
public Level ircLevel = Level.WARNING;
public Level consoleLevel = Level.SEVERE;
public Level fileLevel = Level.INFO;
public String fileName = "plugins/NoCheat/nocheat.log";
public String ircTag = "nocheat";
@ -63,10 +66,16 @@ public class NoCheatConfiguration {
logger.setLevel(Level.INFO);
logger.setUseParentHandlers(false);
chatLevel = stringToLevel(c.getString("logging.logtochat"), chatLevel);
consoleLevel = stringToLevel(c.getString("logging.logtoconsole"), consoleLevel);
fileLevel = stringToLevel(c.getString("logging.logtofile"), fileLevel);
ircLevel = stringToLevel(c.getString("logging.logtoirc"), ircLevel);
ircTag = c.getString("logging.logtoirctag", ircTag);
if(fh == null) {
try {
fh = new FileHandler(c.getString("logging.filename"), true);
fh.setLevel(stringToLevel(c.getString("logging.logtofile")));
fh = new FileHandler(fileName, true);
fh.setLevel(fileLevel);
fh.setFormatter(Logger.getLogger("Minecraft").getHandlers()[0].getFormatter());
logger.addHandler(fh);
@ -76,13 +85,6 @@ public class NoCheatConfiguration {
}
}
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");
plugin.speedhackCheck.limits[0] = c.getInt("speedhack.limits.low", plugin.speedhackCheck.limits[0]);
plugin.speedhackCheck.limits[1] = c.getInt("speedhack.limits.med", plugin.speedhackCheck.limits[1]);
plugin.speedhackCheck.limits[2] = c.getInt("speedhack.limits.high", plugin.speedhackCheck.limits[2]);
@ -109,15 +111,14 @@ public class NoCheatConfiguration {
plugin.bedteleportCheck.setActive(c.getBoolean("active.bedteleport", plugin.bedteleportCheck.isActive()));
}
private Action[] stringToActions(String string, Action[] actions) {
if(string == null) return actions;
private Action[] stringToActions(String string, Action[] def) {
if(string == null) return def;
System.out.println(string);
List<Action> as = new LinkedList<Action>();
String[] parts = string.split(" ");
for(String s : parts) {
s = s.trim();
if(s.equals("loglow"))
as.add(LogAction.loglow);
else if(s.equals("logmed"))
@ -136,35 +137,61 @@ public class NoCheatConfiguration {
//as.add(new CustomAction(Integer.parseInt(s.substring(6))));
}
catch(Exception e) {
plugin.log(Level.WARNING, "Couldn't parse number of custom action '" + s + "'");
System.out.println("NC: Couldn't parse number of custom action '" + s + "'");
}
}
else {
plugin.log(Level.WARNING, "Can't parse action "+ s);
System.out.println("NC: Can't parse action "+ s);
}
}
return as.toArray(actions);
return as.toArray(def);
}
private String actionsToString(Action[] actions) {
String s = "";
if(actions != null) {
for(Action a : actions) {
s = s + " " + a.getName();
}
}
return s.trim();
}
/**
* Convert a string into a log level
* @param string
* @return
*/
private static Level stringToLevel(String string) {
private static Level stringToLevel(String string, Level def) {
if(string == null) {
return Level.OFF;
return def;
}
if(string.trim().equals("info") || string.trim().equals("low")) return Level.INFO;
if(string.trim().equals("warn") || string.trim().equals("med")) return Level.WARNING;
if(string.trim().equals("severe")|| string.trim().equals("high")) return Level.SEVERE;
return Level.OFF;
}
private static String levelToString(Level level) {
if(level == null) {
return "off";
}
if(level.equals(Level.INFO)) return "low";
else if(level.equals(Level.WARNING)) return "med";
else if(level.equals(Level.SEVERE)) return "high";
return "off";
}
/**
* Standard configuration file for people who haven't got one yet
* @param f
@ -177,12 +204,12 @@ public class NoCheatConfiguration {
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();
w.write(" logtofile: low"); w.newLine();
w.write(" logtoconsole: high"); w.newLine();
w.write(" logtochat: med"); w.newLine();
w.write(" logtoirc: med"); w.newLine();
w.write(" logtoirctag: nocheat"); w.newLine();
w.write(" filename: "+fileName); w.newLine();
w.write(" logtofile: "+levelToString(fileLevel)); w.newLine();
w.write(" logtoconsole: "+levelToString(consoleLevel)); w.newLine();
w.write(" logtochat: "+levelToString(chatLevel)); w.newLine();
w.write(" logtoirc: "+levelToString(ircLevel)); w.newLine();
w.write(" logtoirctag: "+ircTag); w.newLine();
w.write("# Checks and Bugfixes that are activated (true or false)"); w.newLine();
w.write("active:"); w.newLine();
w.write(" speedhack: "+plugin.speedhackCheck.isActive()); w.newLine();
@ -197,16 +224,16 @@ public class NoCheatConfiguration {
w.write(" high: "+plugin.speedhackCheck.limits[2]); w.newLine();
w.write("# Speedhack Action, one or more of 'loglow logmed loghigh reset'"); w.newLine();
w.write(" action:"); w.newLine();
w.write(" low: "+plugin.speedhackCheck.actions[0]); w.newLine();
w.write(" med: "+plugin.speedhackCheck.actions[1]); w.newLine();
w.write(" high: "+plugin.speedhackCheck.actions[2]); w.newLine();
w.write(" low: "+actionsToString(plugin.speedhackCheck.actions[0])); w.newLine();
w.write(" med: "+actionsToString(plugin.speedhackCheck.actions[1])); w.newLine();
w.write(" high: "+actionsToString(plugin.speedhackCheck.actions[2])); w.newLine();
w.write("# Moving specific options") ; w.newLine();
w.write("moving:"); w.newLine();
w.write("# Moving Action, one or more of 'loglow logmed loghigh reset'"); w.newLine();
w.write(" action:"); w.newLine();
w.write(" low: "+plugin.movingCheck.actions[0]); w.newLine();
w.write(" med: "+plugin.movingCheck.actions[1]); w.newLine();
w.write(" high: "+plugin.movingCheck.actions[2]); w.newLine();
w.write(" low: "+actionsToString(plugin.movingCheck.actions[0])); w.newLine();
w.write(" med: "+actionsToString(plugin.movingCheck.actions[1])); w.newLine();
w.write(" high: "+actionsToString(plugin.movingCheck.actions[2])); 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();
@ -216,9 +243,9 @@ public class NoCheatConfiguration {
w.write(" high: "+plugin.airbuildCheck.limits[2]); w.newLine();
w.write("# Airbuild Action, one or more of 'loglow logmed loghigh deny'"); w.newLine();
w.write(" action:"); w.newLine();
w.write(" low: "+plugin.airbuildCheck.actions[0]); w.newLine();
w.write(" med: "+plugin.airbuildCheck.actions[1]); w.newLine();
w.write(" high: "+plugin.airbuildCheck.actions[2]); w.newLine();
w.write(" low: "+actionsToString(plugin.airbuildCheck.actions[0])); w.newLine();
w.write(" med: "+actionsToString(plugin.airbuildCheck.actions[1])); w.newLine();
w.write(" high: "+actionsToString(plugin.airbuildCheck.actions[2])); w.newLine();
w.write("# Bedteleport specific options (none exist yet)"); w.newLine();
w.write("bedteleport:"); w.newLine();

View File

@ -3,7 +3,6 @@ package cc.co.evenprime.bukkit.nocheat;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.World;
/**
* Storage for data persistence between events
@ -17,15 +16,13 @@ public class NoCheatData {
* Don't rely on any of these yet, they are likely going to change their name/functionality
*/
public int movingJumpPhase = 0; // current jumpingPhase
public int movingJumpPhase = 0;
public int movingViolationsInARow[] = { 0, 0, 0 };
public World movingLastWorld = null;
public double movingHorizFreedom = 0.0D;
public double movingVertFreedom = 0.0D;
public int movingVertFreedomCounter = 0;
public Location movingSetBackPoint = null;
public Location movingLocation = null;
public Runnable movingRunnable = null;
public Runnable movingSummaryTask = null;
public Level movingHighestLogLevel = null;
// WORKAROUND for changed PLAYER_MOVE logic
@ -39,12 +36,7 @@ public class NoCheatData {
public int speedhackViolationsInARow = 0;
public int airbuildPerSecond = 0;
public Runnable airbuildRunnable = null;
public Runnable airbuildSummaryTask = null;
public NoCheatData() { }
}

View File

@ -9,4 +9,6 @@ public abstract class Action {
this.firstAfter = firstAfter;
this.repeat = repeat;
}
public abstract String getName();
}

View File

@ -5,4 +5,8 @@ public class CancelAction extends Action {
public final static CancelAction cancel = new CancelAction();
private CancelAction() { super(1, true); }
public String getName() {
return "cancel";
}
}

View File

@ -9,4 +9,8 @@ public class CustomAction extends Action {
super(firstAfter, repeat);
this.command = command;
}
public String getName() {
return "custom";
}
}

View File

@ -16,4 +16,15 @@ public class LogAction extends Action {
super(firstAfter, repeat);
this.level = level;
}
public String getName() {
if(level.equals(Level.INFO))
return "loglow";
else if(level.equals(Level.WARNING))
return "logmed";
else if(level.equals(Level.SEVERE))
return "loghigh";
else
return "";
}
}

View File

@ -45,19 +45,19 @@ public class AirbuildCheck extends Check {
final NoCheatData data = plugin.getPlayerData(event.getPlayer());
final Player p = event.getPlayer();
if(data.airbuildRunnable == null) {
data.airbuildRunnable = new Runnable() {
if(data.airbuildSummaryTask == null) {
data.airbuildSummaryTask = new Runnable() {
@Override
public void run() {
summary(p, data);
// deleting its own reference
data.airbuildRunnable = null;
data.airbuildSummaryTask = null;
}
};
// Give a summary in 20 ticks ~ 1 second
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, data.airbuildRunnable, 20);
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, data.airbuildSummaryTask, 20);
}
data.airbuildPerSecond++;

View File

@ -19,7 +19,7 @@ public class BedteleportCheck extends Check {
return;
if(event.getFrom().getWorld().getBlockTypeIdAt(event.getFrom()) == Material.BED_BLOCK.getId()) {
double yRest = Math.floor(event.getFrom().getY()) - event.getFrom().getY();
double yRest = event.getFrom().getY() - Math.floor(event.getFrom().getY());
if(yRest > 0.099 && yRest < 0.101)
// Don't allow the teleport
event.setCancelled(true);

View File

@ -1,10 +1,13 @@
package cc.co.evenprime.bukkit.nocheat.checks;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.Vector;
import cc.co.evenprime.bukkit.nocheat.NoCheatData;
@ -53,7 +56,7 @@ public class MovingCheck extends Check {
// Block types that may be treated specially
private enum BlockType {
SOLID, NONSOLID, LADDER, LIQUID, UNKNOWN;
SOLID, NONSOLID, LADDER, LIQUID, UNKNOWN, FENCE;
}
// Until I can think of a better way to determine if a block is solid or not, this is what I'll do
@ -139,7 +142,7 @@ public class MovingCheck extends Check {
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.FENCE.getId()]= BlockType.FENCE;
types[Material.PUMPKIN.getId()]= BlockType.SOLID;
types[Material.NETHERRACK.getId()]= BlockType.SOLID;
types[Material.SOUL_SAND.getId()]= BlockType.UNKNOWN;
@ -161,46 +164,14 @@ public class MovingCheck extends Check {
// Get the two locations of the event
final Location to = event.getTo();
// WORKAROUND for changed PLAYER_MOVE logic
final Location from = data.movingTeleportTo == null ? event.getFrom() : data.movingTeleportTo;
data.movingTeleportTo = null;
// 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
// to the actual target position in the new world
// strange...
// I've no real way to get informed about a world change, therefore I have to
// store the "lastWorld" and compare it to the world of the next event
if(data.movingLastWorld != to.getWorld()) {
data.movingLastWorld = to.getWorld();
// "Forget" previous setback points
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
// Store the destination that this move goes to for later use
data.movingLocation = to.clone();
// the world changed since our last check, therefore I can't check anything
// for this event (reliably)
return;
}
if(data.movingLocation != null && data.movingLocation.equals(to)) {
// If we are still trying to reach that location, accept the move
return;
}
else if(data.movingLocation != null) {
// If we try to go somewhere else, delete the location. It is no longer needed
data.movingLocation = null;
}
// Ignore vehicles
// vehicles are a special case
if(event.getPlayer().isInsideVehicle()) {
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
resetData(data, event.getTo());
return;
}
@ -324,9 +295,7 @@ public class MovingCheck extends Check {
}
}
int vl = max(vl1, vl2);
int vl = vl1 > vl2 ? vl1 : vl2;
if(vl < 0) {
data.movingSetBackPoint = newSetBack == null ? data.movingSetBackPoint : newSetBack;
@ -347,47 +316,57 @@ public class MovingCheck extends Check {
}
}
private void setupSummaryTask(final Player player, final NoCheatData data) {
private void setupSummaryTask(final Player p, final NoCheatData data) {
// Setup task to display summary later
if(data.movingRunnable == null) {
data.movingRunnable = new Runnable() {
if(data.movingSummaryTask == null) {
data.movingSummaryTask = new Runnable() {
@Override
public void run() {
summary(player, data);
if(data.movingHighestLogLevel != null) {
String logString = "Moving summary of last ~" + (ticksBeforeSummary/20) + " seconds: "+p.getName() + " total Violations: ("+ data.movingViolationsInARow[0] + "," + data.movingViolationsInARow[1] + "," + data.movingViolationsInARow[2] + ")";
plugin.log(data.movingHighestLogLevel, logString);
}
// deleting its own reference
data.movingRunnable = null;
data.movingSummaryTask = null;
data.movingViolationsInARow[0] = 0;
data.movingViolationsInARow[1] = 0;
data.movingViolationsInARow[2] = 0;
}
};
// Give a summary in x ticks. 20 ticks ~ 1 second
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, data.movingRunnable, ticksBeforeSummary);
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, data.movingSummaryTask, ticksBeforeSummary);
}
}
public void teleported(PlayerMoveEvent event) {
/**
* Call this when a player got successfully teleported with the corresponding event to set new "setback" points
* and reset data (if necessary)
*
* @param event
*/
public void teleported(PlayerTeleportEvent event) {
NoCheatData data = plugin.getPlayerData(event.getPlayer());
if(data.reset) { // My plugin requested this teleport, so we don't do anything
if(data.reset) { // My plugin requested this teleport while handling another event
data.reset = false;
}
else {
if(!event.isCancelled()) {
// If it wasn't our plugin that ordered the teleport, forget (almost) all our information and start from scratch
// Setback points are created automatically the next time a move event is handled
data.speedhackSetBackPoint = event.getTo().clone();
data.movingSetBackPoint = event.getTo().clone();
data.speedhackEventsSinceLastCheck = 0;
data.movingJumpPhase = 0;
resetData(data, event.getTo());
}
}
// WORKAROUND for changed PLAYER_MOVE logic
// WORKAROUND for changed PLAYER_MOVE logic - I need to remember the "to" location of teleports and use it as a from-Location
// for the move event that comes next
data.movingTeleportTo = event.getTo();
}
/**
* Perform actions that were specified by the admin
* Perform actions that were specified in the config file
* @param event
* @param action
*/
@ -396,7 +375,7 @@ public class MovingCheck extends Check {
if(actions == null) return;
boolean cancelled = false;
// prepare log message if neccessary
// prepare log message if necessary
String logMessage = null;
if(loggingAllowed) {
@ -404,9 +383,10 @@ public class MovingCheck extends Check {
}
for(Action a : actions) {
if(a instanceof LogAction) {
if(loggingAllowed && a instanceof LogAction) {
plugin.log(((LogAction)a).level, logMessage);
data.movingHighestLogLevel = ((LogAction)a).level.intValue() > data.movingHighestLogLevel.intValue() ? ((LogAction)a).level : data.movingHighestLogLevel;
if(data.movingHighestLogLevel == null) data.movingHighestLogLevel = Level.ALL;
if(data.movingHighestLogLevel.intValue() < ((LogAction)a).level.intValue()) data.movingHighestLogLevel = ((LogAction)a).level;
}
else if(!cancelled && a instanceof CancelAction) {
resetPlayer(event, from);
@ -417,13 +397,6 @@ public class MovingCheck extends Check {
}
}
private void summary(Player p, NoCheatData data) {
String logString = "Moving summary of last ~" + (ticksBeforeSummary/20) + " seconds: "+p.getName() + " total Violations: ("+ data.movingViolationsInARow[0] + "," + data.movingViolationsInARow[1] + "," + data.movingViolationsInARow[2] + ")";
plugin.log(data.movingHighestLogLevel, logString);
}
private int limitCheck(double value, double limits[]) {
for(int i = limits.length - 1; i >= 0; i--) {
@ -435,12 +408,6 @@ public class MovingCheck extends Check {
return -1;
}
private static int max(int a, int b) {
if(a > b) {
return a;
}
return b;
}
/**
* Return the player to a stored location or if that is not available,
* the previous location.
@ -455,27 +422,22 @@ public class MovingCheck extends Check {
// on solid ground, but in case it isn't (maybe the ground is gone now) we
// still have to allow the player some freedom with vertical movement due
// to lost vertical momentum to prevent him from getting stuck
data.movingJumpPhase = 0;
data.movingVertFreedom = 0.0D;
Location l = data.movingSetBackPoint;
if(data.movingSetBackPoint == null) data.movingSetBackPoint = from.clone();
// Set a flag that gets used while handling teleport events
data.reset = true;
// If we have stored a location for the player, we put him back there
if(l != null) {
// Lets try it that way. Maybe now people don't "disappear" any longer
event.setFrom(l.clone());
event.setTo(l.clone());
event.getPlayer().teleport(l.clone());
event.setCancelled(true);
}
else {
// If we don't have a setback point, we'll have to use the from location
event.setFrom(from.clone());
event.setTo(from.clone());
event.getPlayer().teleport(from.clone());
event.setCancelled(true);
}
resetData(data, data.movingSetBackPoint);
// Put the player back to the chosen location
event.setFrom(data.movingSetBackPoint.clone());
event.setTo(data.movingSetBackPoint.clone());
event.getPlayer().teleport(data.movingSetBackPoint.clone());
event.setCancelled(true);
}
@ -496,8 +458,8 @@ public class MovingCheck extends Check {
types[w.getBlockTypeIdAt(values[1], values[2]-1, values[4])] != BlockType.NONSOLID )
return true;
// Check if he is hanging onto a ladder
else if(types[w.getBlockTypeIdAt(l.getBlockX(), l.getBlockY(), l.getBlockZ())] == BlockType.LADDER ||
types[w.getBlockTypeIdAt(l.getBlockX(), l.getBlockY()+1, l.getBlockZ())] == BlockType.LADDER)
else if(types[w.getBlockTypeIdAt(l.getBlockX(), values[2], l.getBlockZ())] == BlockType.LADDER ||
types[w.getBlockTypeIdAt(l.getBlockX(), values[2]+1, l.getBlockZ())] == BlockType.LADDER)
return true;
// check if he is standing "in" a block that's potentially solid (we give him the benefit of a doubt and see that as a legit move)
// If it is not legit, the MC server already has a safeguard against that (You'll get "xy moved wrongly" on the console in that case)
@ -524,6 +486,12 @@ public class MovingCheck extends Check {
types[w.getBlockTypeIdAt(values[0], values[2], values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2]+1, values[3]+1)] == BlockType.LIQUID)
return true;
// Running on fences
else if(types[w.getBlockTypeIdAt(values[0], values[2]-2, values[3])] == BlockType.FENCE ||
types[w.getBlockTypeIdAt(values[1], values[2]-2, values[3])] == BlockType.FENCE ||
types[w.getBlockTypeIdAt(values[0], values[2]-2, values[4])] == BlockType.FENCE ||
types[w.getBlockTypeIdAt(values[1], values[2]-2, values[4])] == BlockType.FENCE )
return true;
else
return false;
}
@ -552,6 +520,15 @@ public class MovingCheck extends Check {
return (int) (floor - d4);
}
private void resetData(NoCheatData data, Location l) {
// If it wasn't our plugin that ordered the teleport, forget (almost) all our information and start from scratch
data.speedhackSetBackPoint = l;
data.movingSetBackPoint = l;
data.speedhackEventsSinceLastCheck = 0;
data.movingJumpPhase = 0;
data.movingTeleportTo = null;
}
@Override
public String getName() {
return "moving";