Refactoring: seperate listeners for different checks + instancing

instead of static for almost anything
This commit is contained in:
Evenprime 2011-03-25 16:47:37 +01:00
parent 6a6cb9e247
commit 77f347e182
16 changed files with 991 additions and 918 deletions

View File

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

View File

@ -17,79 +17,52 @@ import org.bukkit.util.config.Configuration;
*
*/
public class NoCheatConfiguration {
// Our personal logger
public static final String loggerName = "cc.co.evenprime.nocheat";
public static final Logger logger = Logger.getLogger(loggerName);
// Which checks are active
public static boolean speedhackCheckActive;
public static boolean movingCheckActive;
public static boolean airbuildCheckActive;
public static boolean bedteleportCheckActive;
// Limits for the speedhack check
public static int speedhackLimitLow;
public static int speedhackLimitMed;
public static int speedhackLimitHigh;
// How should speedhack violations be treated?
public static String speedhackActionMinor = "";
public static String speedhackActionNormal = "";
public static String speedhackActionHeavy = "";
public static int movingFreeMoves = 1;
// How should moving violations be treated?
public static String movingActionMinor = "";
public static String movingActionNormal = "";
public static String movingActionHeavy = "";
// How should airbuild violations be treated?
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;
private final String loggerName = "cc.co.evenprime.nocheat";
public final Logger logger = Logger.getLogger(loggerName);
// The log level above which information gets logged to the specified logger
public static Level chatLevel = Level.OFF;
public static Level ircLevel = Level.OFF;
public static Level consoleLevel = Level.OFF;
public static String ircTag = "";
public Level chatLevel = Level.OFF;
public Level ircLevel = Level.OFF;
public Level consoleLevel = Level.OFF;
public String ircTag = "";
// Our log output to a file
private static FileHandler fh = null;
private NoCheatConfiguration() {}
private FileHandler fh = null;
private final NoCheatPlugin plugin;
public NoCheatConfiguration(File configurationFile, NoCheatPlugin plugin) {
this.plugin = plugin;
this.config(configurationFile);
}
/**
* Read the configuration file and assign either standard values or whatever is declared in the file
* @param configurationFile
*/
public static void config(File configurationFile) {
public void config(File configurationFile) {
if(!configurationFile.exists()) {
createStandardConfigFile(configurationFile);
}
Configuration c = new Configuration(configurationFile);
c.load();
logger.setLevel(Level.INFO);
logger.setUseParentHandlers(false);
if(fh == null) {
try {
fh = new FileHandler(c.getString("logging.filename"), true);
fh.setLevel(stringToLevel(c.getString("logging.logtofile")));
fh.setFormatter(Logger.getLogger("Minecraft").getHandlers()[0].getFormatter());
logger.addHandler(fh);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -103,51 +76,43 @@ public class NoCheatConfiguration {
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);
plugin.speedhackCheck.limitLow = c.getInt("speedhack.limits.low", plugin.speedhackCheck.limitLow);
plugin.speedhackCheck.limitMed = c.getInt("speedhack.limits.med", plugin.speedhackCheck.limitMed);
plugin.speedhackCheck.limitHigh = c.getInt("speedhack.limits.high", plugin.speedhackCheck.limitHigh);
speedhackLimitLow = c.getInt("speedhack.limits.low", 30);
speedhackLimitMed = c.getInt("speedhack.limits.med", 45);
speedhackLimitHigh = c.getInt("speedhack.limits.high", 60);
plugin.movingCheck.actionLow = c.getString("moving.action.low", plugin.movingCheck.actionLow);
plugin.movingCheck.actionMed = c.getString("moving.action.med", plugin.movingCheck.actionMed);
plugin.movingCheck.actionHigh = c.getString("moving.action.high", plugin.movingCheck.actionHigh);
movingFreeMoves = c.getInt("moving.freemoves", 1);
plugin.speedhackCheck.actionLow = c.getString("speedhack.action.low", plugin.speedhackCheck.actionLow);
plugin.speedhackCheck.actionMed = c.getString("speedhack.action.med", plugin.speedhackCheck.actionMed);
plugin.speedhackCheck.actionHigh = c.getString("speedhack.action.high", plugin.speedhackCheck.actionHigh);
movingActionMinor = c.getString("moving.action.low", "loglow reset");
movingActionNormal = c.getString("moving.action.med", "logmed reset");
movingActionHeavy = c.getString("moving.action.high", "loghigh reset");
plugin.airbuildCheck.limitLow = c.getInt("airbuild.limits.low", plugin.airbuildCheck.limitLow);
plugin.airbuildCheck.limitMed = c.getInt("airbuild.limits.med", plugin.airbuildCheck.limitMed);
plugin.airbuildCheck.limitHigh = c.getInt("airbuild.limits.high", plugin.airbuildCheck.limitHigh);
speedhackActionMinor = c.getString("speedhack.action.low", "loglow reset");
speedhackActionNormal = c.getString("speedhack.action.med", "logmed reset");
speedhackActionHeavy = c.getString("speedhack.action.high", "loghigh reset");
plugin.airbuildCheck.actionLow = c.getString("airbuild.action.low", plugin.airbuildCheck.actionLow);
plugin.airbuildCheck.actionMed = c.getString("airbuild.action.med", plugin.airbuildCheck.actionMed);
plugin.airbuildCheck.actionHigh = c.getString("airbuild.action.high", plugin.airbuildCheck.actionHigh);
airbuildLimitLow = c.getInt("airbuild.limits.low", 1);
airbuildLimitMed = c.getInt("airbuild.limits.med", 3);
airbuildLimitHigh = c.getInt("airbuild.limits.high", 10);
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
if(movingFreeMoves < 1) movingFreeMoves = 1;
plugin.speedhackCheck.setActive(c.getBoolean("active.speedhack", plugin.speedhackCheck.isActive()));
plugin.movingCheck.setActive(c.getBoolean("active.moving", plugin.movingCheck.isActive()));
plugin.airbuildCheck.setActive(c.getBoolean("active.airbuild", plugin.airbuildCheck.isActive()));
plugin.bedteleportCheck.setActive(c.getBoolean("active.bedteleport", plugin.bedteleportCheck.isActive()));
}
/**
* Convert a string into a log level
* @param string
* @return
*/
private static Level stringToLevel(String string) {
if(string == null) {
return Level.OFF;
}
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;
@ -158,7 +123,7 @@ public class NoCheatConfiguration {
* Standard configuration file for people who haven't got one yet
* @param f
*/
private static void createStandardConfigFile(File f) {
private void createStandardConfigFile(File f) {
try {
f.getParentFile().mkdirs();
f.createNewFile();
@ -174,42 +139,40 @@ public class NoCheatConfiguration {
w.write(" logtoirctag: nocheat"); w.newLine();
w.write("# Checks and Bugfixes that are activated (true or false)"); w.newLine();
w.write("active:"); w.newLine();
w.write(" speedhack: true"); w.newLine();
w.write(" moving: true"); w.newLine();
w.write(" airbuild: false"); w.newLine();
w.write(" bedteleport: true"); w.newLine();
w.write(" speedhack: "+plugin.speedhackCheck.isActive()); w.newLine();
w.write(" moving: "+plugin.movingCheck.isActive()); w.newLine();
w.write(" airbuild: "+plugin.airbuildCheck.isActive()); w.newLine();
w.write(" bedteleport: "+plugin.bedteleportCheck.isActive()); w.newLine();
w.write("# Speedhack specific options"); w.newLine();
w.write("speedhack:"); w.newLine();
w.write(" limits:"); w.newLine();
w.write(" low: 30"); w.newLine();
w.write(" med: 45"); w.newLine();
w.write(" high: 60"); w.newLine();
w.write(" low: "+plugin.speedhackCheck.limitLow); w.newLine();
w.write(" med: "+plugin.speedhackCheck.limitMed); w.newLine();
w.write(" high: "+plugin.speedhackCheck.limitHigh); w.newLine();
w.write("# Speedhack Action, one or more of 'loglow logmed loghigh reset'"); w.newLine();
w.write(" action:"); w.newLine();
w.write(" low: loglow reset"); w.newLine();
w.write(" med: logmed reset"); w.newLine();
w.write(" high: loghigh reset"); w.newLine();
w.write(" low: "+plugin.speedhackCheck.actionLow); w.newLine();
w.write(" med: "+plugin.speedhackCheck.actionMed); w.newLine();
w.write(" high: "+plugin.speedhackCheck.actionHigh); w.newLine();
w.write("# Moving specific options") ; w.newLine();
w.write("moving:"); w.newLine();
w.write("# After how many minor violations should the plugin react (minimum 1)"); w.newLine();
w.write(" freemoves: 1"); w.newLine();
w.write("# Moving Action, one or more of 'loglow logmed loghigh reset'"); w.newLine();
w.write(" action:"); w.newLine();
w.write(" low: loglow reset"); w.newLine();
w.write(" med: logmed reset"); w.newLine();
w.write(" high: loghigh reset"); w.newLine();
w.write(" low: "+plugin.movingCheck.actionLow); w.newLine();
w.write(" med: "+plugin.movingCheck.actionMed); w.newLine();
w.write(" high: "+plugin.movingCheck.actionHigh); 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(" low: "+plugin.airbuildCheck.limitLow); w.newLine();
w.write(" med: "+plugin.airbuildCheck.limitMed); w.newLine();
w.write(" high: "+plugin.airbuildCheck.limitHigh); w.newLine();
w.write("# Airbuild Action, one or more of 'loglow logmed loghigh 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(" low: "+plugin.airbuildCheck.actionLow); w.newLine();
w.write(" med: "+plugin.airbuildCheck.actionMed); w.newLine();
w.write(" high: "+plugin.airbuildCheck.actionHigh); w.newLine();
w.write("# Bedteleport specific options (none exist yet)"); w.newLine();
w.write("bedteleport:"); w.newLine();

View File

@ -25,7 +25,7 @@ public class NoCheatData {
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

View File

@ -15,9 +15,15 @@ import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.PluginManager;
import cc.co.evenprime.bukkit.nocheat.listeners.NoCheatBlockListener;
import cc.co.evenprime.bukkit.nocheat.listeners.NoCheatPlayerListener;
import cc.co.evenprime.bukkit.nocheat.listeners.NoCheatPlayerListenerMonitor;
import cc.co.evenprime.bukkit.nocheat.checks.AirbuildCheck;
import cc.co.evenprime.bukkit.nocheat.checks.BedteleportCheck;
import cc.co.evenprime.bukkit.nocheat.checks.MovingCheck;
import cc.co.evenprime.bukkit.nocheat.checks.SpeedhackCheck;
import cc.co.evenprime.bukkit.nocheat.listeners.AirbuildListener;
import cc.co.evenprime.bukkit.nocheat.listeners.BedteleportListener;
import cc.co.evenprime.bukkit.nocheat.listeners.MovingListener;
import cc.co.evenprime.bukkit.nocheat.listeners.MovingMonitor;
import cc.co.evenprime.bukkit.nocheat.listeners.SpeedhackListener;
import com.ensifera.animosity.craftirc.CraftIRC;
import com.nijikokun.bukkit.Permissions.Permissions;
@ -25,50 +31,52 @@ import com.nijiko.permissions.PermissionHandler;
import org.bukkit.plugin.Plugin;
/**
*
* NoCheatPlugin
*
* Check various player events for their plausibilty and log/deny them based on configuration
*
* @author Evenprime
*/
*
* NoCheatPlugin
*
* Check various player events for their plausibility and log/deny them based on configuration
*
* @author Evenprime
*/
public class NoCheatPlugin extends JavaPlugin {
public final MovingCheck movingCheck;
public final BedteleportCheck bedteleportCheck;
public final SpeedhackCheck speedhackCheck;
public final AirbuildCheck airbuildCheck;
// Various listeners needed for different Checks
private NoCheatPlayerListener playerListener;
private NoCheatPlayerListenerMonitor playerListenerMonitor;
private NoCheatBlockListener blockListener;
private NoCheatConfiguration config;
// My main logger
private static Logger consoleLogger;
private static Logger fileLogger;
public static NoCheatPlugin p;
// Permissions 2.0, if available
public static PermissionHandler Permissions = null;
// CraftIRC 2.0, if available
public static CraftIRC Irc = null;
// Store data between Events
public static Map<Player, NoCheatData> playerData = new HashMap<Player, NoCheatData>();
// Permissions 2.x, if available
private PermissionHandler permissions;
// CraftIRC 2.x, if available
private CraftIRC irc;
// Store data between Events
private final Map<Player, NoCheatData> playerData = new HashMap<Player, NoCheatData>();
public NoCheatPlugin() {
movingCheck = new MovingCheck(this);
bedteleportCheck = new BedteleportCheck(this);
speedhackCheck = new SpeedhackCheck(this);
airbuildCheck = new AirbuildCheck(this);
// parse the nocheat.yml config file
setupConfig();
}
/**
* Main access to data that needs to be stored between different events.
* Always returns a NoCheatData object, because if there isn't one
* for the specified player, one will be created.
*
* @param p
* @return
*/
public NoCheatData getPlayerData(Player p) {
NoCheatData data = null;
public NoCheatPlugin() {
p = this;
}
/**
* Main access to data that needs to be stored between different events.
* Always returns a NoCheatData object, because if there isn't one
* for the specified player, one will be created.
*
* @param p
* @return
*/
public static NoCheatData getPlayerData(Player p) {
NoCheatData data = null;
if((data = playerData.get(p)) == null ) {
synchronized(playerData) {
data = playerData.get(p);
@ -79,174 +87,178 @@ public class NoCheatPlugin extends JavaPlugin {
}
}
}
return data;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String commandLabel, String[] args)
{
if(sender instanceof Player) {
if(!hasPermission((Player)sender, "nocheat.p")) {
sender.sendMessage("NC: You are not allowed to use this command.");
return false;
}
}
if(args.length == 0) {
sender.sendMessage("NC: Using "+ ((Permissions == null) ? "isOp()" : "Permissions") + ". Activated checks/bugfixes: " + getActiveChecksAsString());
return true;
}
else if(args.length == 1 && args[0] != null && args[0].trim().equals("-p")) {
if(sender instanceof Player) {
Player p = (Player) sender;
sender.sendMessage("NC: You have permissions: " + getPermissionsForPlayerAsString(p));
return true;
}
else {
sender.sendMessage("NC: You have to be a player to use this command");
return true;
}
}
else if(args.length == 2 && args[0] != null && args[0].trim().equals("-p")) {
Player p = getServer().getPlayer(args[1]);
if(p != null) {
sender.sendMessage("NC: "+p.getName() + " has permissions: " + getPermissionsForPlayerAsString(p));
return true;
}
else {
sender.sendMessage("NC: Player " + args[1] + " was not found.");
return true;
}
}
return false;
}
return data;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String commandLabel, String[] args)
{
if(sender instanceof Player) {
if(!hasPermission((Player)sender, "nocheat.p")) {
sender.sendMessage("NC: You are not allowed to use this command.");
return false;
}
}
if(args.length == 0) {
sender.sendMessage("NC: Using "+ ((permissions == null) ? "isOp()" : "Permissions") + ". Activated checks/bugfixes: " + getActiveChecksAsString());
return true;
}
else if(args.length == 1 && args[0] != null && args[0].trim().equals("-p")) {
if(sender instanceof Player) {
Player p = (Player) sender;
sender.sendMessage("NC: You have permissions: " + getPermissionsForPlayerAsString(p));
return true;
}
else {
sender.sendMessage("NC: You have to be a player to use this command");
return true;
}
}
else if(args.length == 2 && args[0] != null && args[0].trim().equals("-p")) {
Player p = getServer().getPlayer(args[1]);
if(p != null) {
sender.sendMessage("NC: "+p.getName() + " has permissions: " + getPermissionsForPlayerAsString(p));
return true;
}
else {
sender.sendMessage("NC: Player " + args[1] + " was not found.");
return true;
}
}
return false;
}
public void onDisable() {
PluginDescriptionFile pdfFile = this.getDescription();
Logger.getLogger("Minecraft").info( "[NoCheatPlugin] version [" + pdfFile.getVersion() + "] is disabled.");
}
PluginDescriptionFile pdfFile = this.getDescription();
Logger.getLogger("Minecraft").info( "[NoCheatPlugin] version [" + pdfFile.getVersion() + "] is disabled.");
}
public void onEnable() {
// Create our listeners and feed them with neccessary information
playerListener = new NoCheatPlayerListener();
playerListenerMonitor = new NoCheatPlayerListenerMonitor();
blockListener = new NoCheatBlockListener();
public void onEnable() {
// Create our listeners and feed them with neccessary information
fileLogger = NoCheatConfiguration.logger;
consoleLogger = Logger.getLogger("Minecraft");
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_QUIT, playerListener, Priority.Monitor, this); // used to delete old data of users
pm.registerEvent(Event.Type.BLOCK_PLACED, blockListener, Priority.Low, this); // used for airbuild check
pm.registerEvent(Event.Type.PLAYER_TELEPORT, playerListener, Priority.Lowest, this); // used for teleportfrombed check
pm.registerEvent(Event.Type.PLAYER_TELEPORT, playerListenerMonitor, Priority.Monitor, this); // used for moving, speedhack check
PluginManager pm = getServer().getPluginManager();
PluginDescriptionFile pdfFile = this.getDescription();
// parse the nocheat.yml config file
setupConfig();
// Get, if available, the Permissions and irc plugin
setupPermissions();
setupIRC();
Logger.getLogger("Minecraft").info( "[NoCheatPlugin] version [" + pdfFile.getVersion() + "] is enabled with the following checks: "+getActiveChecksAsString());
}
// parse the nocheat.yml config file
setupConfig();
// Register listeners for moving check
pm.registerEvent(Event.Type.PLAYER_MOVE, new MovingListener(movingCheck), Priority.Lowest, this);
pm.registerEvent(Event.Type.PLAYER_TELEPORT, new MovingMonitor(movingCheck), Priority.Monitor, this);
// Register listeners for speedhack check
pm.registerEvent(Event.Type.PLAYER_MOVE, new SpeedhackListener(speedhackCheck), Priority.High, this);
// Register listeners for airbuild check
pm.registerEvent(Event.Type.BLOCK_PLACED, new AirbuildListener(airbuildCheck), Priority.Low, this);
// Register listeners for bedteleport check
pm.registerEvent(Event.Type.PLAYER_TELEPORT, new BedteleportListener(bedteleportCheck), Priority.Lowest, this);
PluginDescriptionFile pdfFile = this.getDescription();
// Get, if available, the Permissions and irc plugin
setupPermissions();
setupIRC();
Logger.getLogger("Minecraft").info( "[NoCheatPlugin] version [" + pdfFile.getVersion() + "] is enabled with the following checks: "+getActiveChecksAsString());
}
/**
* Get, if available, a reference to the Permissions-plugin
*/
public void setupPermissions() {
PermissionHandler p = null;
* Get, if available, a reference to the Permissions-plugin
*/
private void setupPermissions() {
PermissionHandler p = null;
Plugin test = this.getServer().getPluginManager().getPlugin("Permissions");
Plugin test = this.getServer().getPluginManager().getPlugin("Permissions");
if(test != null && test instanceof Permissions) {
p = ((Permissions)test).getHandler();
if(p == null) {
this.getServer().getPluginManager().enablePlugin(test);
}
p = ((Permissions)test).getHandler();
}
if(test != null && test instanceof Permissions) {
p = ((Permissions)test).getHandler();
if(p == null) {
this.getServer().getPluginManager().enablePlugin(test);
}
p = ((Permissions)test).getHandler();
}
if(p == null) {
PluginDescriptionFile pdfFile = this.getDescription();
Logger.getLogger("Minecraft").warning("[NoCheatPlugin] version [" + pdfFile.getVersion() + "] couldn't find Permissions plugin. Fallback to 'isOp()' equals 'nocheat.*'");
}
if(p == null) {
PluginDescriptionFile pdfFile = this.getDescription();
Logger.getLogger("Minecraft").warning("[NoCheatPlugin] version [" + pdfFile.getVersion() + "] couldn't find Permissions plugin. Fallback to 'isOp()' equals 'nocheat.*'");
}
Permissions = p;
}
/**
* Get, if available, a reference to the Permissions-plugin
*/
public void setupIRC() {
CraftIRC p = null;
permissions = p;
}
Plugin test = this.getServer().getPluginManager().getPlugin("CraftIRC");
/**
* Get, if available, a reference to the Permissions-plugin
*/
private void setupIRC() {
CraftIRC p = null;
if(test != null && test instanceof CraftIRC) {
p = (CraftIRC)test;
}
if(p == null) {
PluginDescriptionFile pdfFile = this.getDescription();
Logger.getLogger("Minecraft").warning("[NoCheatPlugin] version [" + pdfFile.getVersion() + "] couldn't find CrafTIRC plugin. Disabling logging to IRC.");
}
Plugin test = this.getServer().getPluginManager().getPlugin("CraftIRC");
if(test != null && test instanceof CraftIRC) {
p = (CraftIRC)test;
}
if(p == null) {
PluginDescriptionFile pdfFile = this.getDescription();
Logger.getLogger("Minecraft").warning("[NoCheatPlugin] version [" + pdfFile.getVersion() + "] couldn't find CrafTIRC plugin. Disabling logging to IRC.");
}
irc = p;
}
/**
* Log a violation message to all locations declared in the config file
* @param message
*/
private void log(Level l, String message) {
if(l != null) {
logToChat(l, message);
logToIRC(l, message);
logToConsole(l, message);
config.logger.log(l, message);
}
}
private void logToChat(Level l, String message) {
if(config.chatLevel.intValue() <= l.intValue()) {
for(Player player : getServer().getOnlinePlayers()) {
if(hasPermission(player, "nocheat.notify")) {
player.sendMessage("["+l.getName()+"] " + message);
}
}
}
}
private void logToIRC(Level l, String message) {
if(irc != null && config.ircLevel.intValue() <= l.intValue()) {
irc.sendMessageToTag("["+l.getName()+"] " + message , config.ircTag);
}
}
private void logToConsole(Level l, String message) {
if( config.consoleLevel.intValue() <= l.intValue()) {
Logger.getLogger("Minecraft").log(l, message);
}
}
public void logAction(String actions, String message) {
if(actions == null) return;
Irc = p;
}
/**
* Log a violation message to all locations declared in the config file
* @param message
*/
private static void log(Level l, String message) {
if(l != null) {
logToChat(l, message);
logToIRC(l, message);
logToConsole(l, message);
fileLogger.log(l, message);
}
}
private static void logToChat(Level l, String message) {
if(NoCheatConfiguration.chatLevel.intValue() <= l.intValue()) {
for(Player player : p.getServer().getOnlinePlayers()) {
if(hasPermission(player, "nocheat.notify")) {
player.sendMessage("["+l.getName()+"] " + message);
}
}
}
}
private static void logToIRC(Level l, String message) {
if(Irc != null && NoCheatConfiguration.ircLevel.intValue() <= l.intValue()) {
Irc.sendMessageToTag("["+l.getName()+"] " + message , NoCheatConfiguration.ircTag);
}
}
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) {
if(actions == null) return;
// LOGGING IF NEEDED AND WHERE NEEDED
Level logLevel = null;
if(actions.contains("loglow")) {
logLevel = Level.INFO;
}
@ -256,48 +268,50 @@ public class NoCheatPlugin extends JavaPlugin {
if(actions.contains("loghigh")) {
logLevel = Level.SEVERE;
}
if(logLevel != null) {
NoCheatPlugin.log(logLevel, "NC: "+message);
log(logLevel, "NC: "+message);
}
}
public static boolean hasPermission(Player player, String permission) {
if(player == null || permission == null) {
return false;
}
if(NoCheatPlugin.Permissions != null && NoCheatPlugin.Permissions.has(player, permission))
return true;
else if(NoCheatPlugin.Permissions == null && player.isOp())
return true;
else
return false;
}
/**
* Read the config file
*/
private void setupConfig() {
NoCheatConfiguration.config(new File("plugins/NoCheat/nocheat.yml"));
}
private String getActiveChecksAsString() {
return (NoCheatConfiguration.movingCheckActive ? "moving ": "") +
(NoCheatConfiguration.speedhackCheckActive ? "speedhack " : "") +
(NoCheatConfiguration.airbuildCheckActive ? "airbuild " : "") +
(NoCheatConfiguration.bedteleportCheckActive ? "bedteleport " : "");
}
public boolean hasPermission(Player player, String permission) {
if(player == null || permission == null) {
return false;
}
if(permissions != null && permissions.has(player, permission))
return true;
else if(permissions == null && player.isOp())
return true;
else
return false;
}
/**
* Read the config file
*/
private void setupConfig() {
if(this.config == null)
this.config = new NoCheatConfiguration(new File("plugins/NoCheat/nocheat.yml"), this);
else
this.config.config(new File("plugins/NoCheat/nocheat.yml"));
}
private String getActiveChecksAsString() {
return (movingCheck.isActive() ? movingCheck.getName() + " " : "") +
(speedhackCheck.isActive() ? speedhackCheck.getName() + " " : "") +
(airbuildCheck.isActive() ? airbuildCheck.getName() + " " : "") +
(bedteleportCheck.isActive() ? bedteleportCheck.getName() + " " : "");
}
private String getPermissionsForPlayerAsString(Player p) {
return (!NoCheatConfiguration.movingCheckActive ? "moving* ": (hasPermission(p, "nocheat.moving") ? "moving " : "") +
(!NoCheatConfiguration.speedhackCheckActive ? "speedhack* " : (hasPermission(p, "nocheat.speedhack") ? "speedhack " : "")) +
(!NoCheatConfiguration.airbuildCheckActive ? "airbuild* " : (hasPermission(p, "nocheat.airbuild") ? "airbuild " : "")) +
(!NoCheatConfiguration.bedteleportCheckActive ? "bedteleport* " : (hasPermission(p, "nocheat.bedteleport") ? "bedteleport " : "")) +
(hasPermission(p, "nocheat.notify") ? "notify " : ""));
return (!movingCheck.isActive() ? movingCheck.getName() + "* " : (hasPermission(p, "nocheat.moving") ? movingCheck.getName() + " " : "") +
(!speedhackCheck.isActive() ? speedhackCheck.getName() + "* " : (hasPermission(p, "nocheat.speedhack") ? speedhackCheck.getName() + " " : "")) +
(!airbuildCheck.isActive() ? airbuildCheck.getName() + "* " : (hasPermission(p, "nocheat.airbuild") ? airbuildCheck.getName() + " " : "")) +
(!bedteleportCheck.isActive() ? bedteleportCheck.getName() + "* " : (hasPermission(p, "nocheat.bedteleport") ? bedteleportCheck.getName() + " " : "")) +
(hasPermission(p, "nocheat.notify") ? "notify " : ""));
}
}

View File

@ -5,7 +5,6 @@ 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;
@ -16,23 +15,36 @@ import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
* @author Evenprime
*
*/
public class AirbuildCheck {
public class AirbuildCheck extends Check {
// How should airbuild violations be treated?
public String actionLow = "loglow deny";
public String actionMed = "logmed deny";
public String actionHigh = "loghigh deny";
public int limitLow = 1;
public int limitMed = 3;
public int limitHigh = 10;
public static void check(BlockPlaceEvent event) {
public AirbuildCheck(NoCheatPlugin plugin) {
super(plugin);
setActive(false);
}
public void check(BlockPlaceEvent event) {
// Should we check at all?
if(NoCheatPlugin.hasPermission(event.getPlayer(), "nocheat.airbuild"))
if(plugin.hasPermission(event.getPlayer(), "nocheat.airbuild"))
return;
// Are all 6 sides "air-blocks" -> cancel the event
if(event.getBlockAgainst().getType() == Material.AIR) {
final NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer());
final NoCheatData data = plugin.getPlayerData(event.getPlayer());
final Player p = event.getPlayer();
if(data.airbuildRunnable == null) {
data.airbuildRunnable = new Runnable() {
@Override
public void run() {
summary(p, data);
@ -40,32 +52,32 @@ public class AirbuildCheck {
data.airbuildRunnable = null;
}
};
// Give a summary in 50 ticks ~ 1 second
NoCheatPlugin.p.getServer().getScheduler().scheduleAsyncDelayedTask(NoCheatPlugin.p, data.airbuildRunnable, 50);
plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, data.airbuildRunnable, 50);
}
data.airbuildPerSecond++;
boolean log = false;
// Only explicitly log certain "milestones"
if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitHigh) {
if(data.airbuildPerSecond == NoCheatConfiguration.airbuildLimitHigh) {
if(data.airbuildPerSecond >= limitHigh) {
if(data.airbuildPerSecond == limitHigh) {
log = true;
}
action(NoCheatConfiguration.airbuildActionHigh, event, log);
action(actionHigh, event, log);
}
else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitMed) {
if(data.airbuildPerSecond == NoCheatConfiguration.airbuildLimitMed) {
else if(data.airbuildPerSecond >= limitMed) {
if(data.airbuildPerSecond == limitMed) {
log = true;
}
action(NoCheatConfiguration.airbuildActionMed, event, log);
action(actionMed, event, log);
}
else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitLow) {
if(data.airbuildPerSecond == NoCheatConfiguration.airbuildLimitLow) {
else if(data.airbuildPerSecond >= limitLow) {
if(data.airbuildPerSecond == limitLow) {
log = true;
}
action(NoCheatConfiguration.airbuildActionLow, event, log);
action(actionLow, event, log);
}
else
{
@ -74,35 +86,40 @@ public class AirbuildCheck {
}
}
private static void action(String action, BlockPlaceEvent event, boolean log) {
private void action(String action, BlockPlaceEvent event, boolean log) {
// LOG IF NEEDED
if(log && action.contains("log")) {
Location l = event.getBlockPlaced().getLocation();
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());
plugin.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
if(action.contains("deny")) {
event.setCancelled(true);
}
}
private static void summary(Player player, NoCheatData data) {
private 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);
if(data.airbuildPerSecond >= limitHigh) {
plugin.logAction(actionHigh, logLine);
}
else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitMed) {
NoCheatPlugin.logAction(NoCheatConfiguration.airbuildActionMed, logLine);
else if(data.airbuildPerSecond >= limitMed) {
plugin.logAction(actionMed, logLine);
}
else if(data.airbuildPerSecond >= NoCheatConfiguration.airbuildLimitLow) {
NoCheatPlugin.logAction(NoCheatConfiguration.airbuildActionLow, logLine);
else if(data.airbuildPerSecond >= limitLow) {
plugin.logAction(actionLow, logLine);
}
data.airbuildPerSecond = 0;
}
@Override
public String getName() {
return "airbuild";
}
}

View File

@ -5,20 +5,29 @@ import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
public class BedteleportCheck {
public class BedteleportCheck extends Check {
public BedteleportCheck(NoCheatPlugin plugin) {
super(plugin);
setActive(true);
}
public void check(PlayerMoveEvent event) {
public static void check(PlayerMoveEvent event) {
// Should we check at all?
if(NoCheatPlugin.hasPermission(event.getPlayer(), "nocheat.bedteleport"))
if(plugin.hasPermission(event.getPlayer(), "nocheat.bedteleport"))
return;
if(event.getFrom().getWorld().getBlockTypeIdAt(event.getFrom()) == Material.BED_BLOCK.getId()) {
double yRest = Math.floor(event.getFrom().getY()) - event.getFrom().getY();
if(yRest > 0.099 && yRest < 0.101)
// Don't allow the teleport
event.setCancelled(true);
}
if(event.getFrom().getWorld().getBlockTypeIdAt(event.getFrom()) == Material.BED_BLOCK.getId()) {
double yRest = Math.floor(event.getFrom().getY()) - event.getFrom().getY();
if(yRest > 0.099 && yRest < 0.101)
// Don't allow the teleport
event.setCancelled(true);
}
}
@Override
public String getName() {
return "bedteleport";
}
}

View File

@ -0,0 +1,24 @@
package cc.co.evenprime.bukkit.nocheat.checks;
import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
public abstract class Check {
public Check(NoCheatPlugin plugin) {
this.plugin = plugin;
}
private boolean active = true;
protected NoCheatPlugin plugin;
public boolean isActive() {
return active;
}
public void setActive(boolean a) {
active = a;
}
public abstract String getName();
}

View File

@ -8,7 +8,6 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.NoCheatConfiguration;
import cc.co.evenprime.bukkit.nocheat.NoCheatData;
import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
@ -18,385 +17,400 @@ import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
* @author Evenprime
*
*/
public class MovingCheck {
public class MovingCheck extends Check {
public MovingCheck(NoCheatPlugin plugin) {
super(plugin);
setActive(true);
}
// How many move events can a player have in air before he is expected to lose altitude (or land somewhere)
static final int jumpingLimit = 4;
// How many move events can a player have in air before he is expected to lose altitude (or land somewhere)
static final int jumpingLimit = 3;
// How high may a player get compared to his last location with ground contact
static final double jumpingHeightLimit = 1.3D;
// How high may a player move in one event on ground
static double stepHeight = 0.501D;
// Limits for the horizontal moving check
public static double movingDistanceLow = 0.1D;
public static double movingDistanceMed = 2.0D;
public static double movingDistanceHigh = 5.0D;
// How should moving violations be treated?
public String actionLow = "loglow reset";
public String actionMed = "logmed reset";
public String actionHigh = "loghigh reset";
final static double magic = 0.30000001192092896D;
final static double magic2 = 0.69999998807907103D;
// Block types that may be treated specially
private enum BlockType {
// Block types that may be treated specially
private enum BlockType {
SOLID, NONSOLID, LADDER, LIQUID, UNKNOWN;
}
// 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.UNKNOWN;
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;
}
public static void check(final PlayerMoveEvent event) {
// Should we check at all
if(NoCheatPlugin.hasPermission(event.getPlayer(), "nocheat.moving"))
return;
// 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 {
// Get the player-specific data
NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer());
for(int i = 0; i < types.length; i++) {
types[i] = BlockType.UNKNOWN;
}
// 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
// Fun fact: Move event locations always have the same world in from/to, therefore
// it doesn't matter which one I use
if(data.movingLastWorld != event.getFrom().getWorld()) {
data.movingLastWorld = event.getFrom().getWorld();
// "Forget" previous setback points
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
// Store the destination that this move goes to for later use
data.movingLocation = event.getTo();
// 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(event.getTo())) {
// 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
if(event.getPlayer().isInsideVehicle()) {
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
return;
}
// The server believes the player should be moving up, so we ignore this event
if(event.getPlayer().getVelocity().getY() > 0) {
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
return;
}
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.UNKNOWN;
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;
}
// The actual movingCheck starts here
public void check(final PlayerMoveEvent event) {
// Get the two locations of the event
Location from = event.getFrom();
Location to = event.getTo();
// First check the distance the player has moved horizontally
// TODO: Make this check much more precise
double xDistance = Math.abs(from.getX() - to.getX());
double zDistance = Math.abs(from.getZ() - to.getZ());
double combined = xDistance * xDistance + zDistance * zDistance;
// Should we check at all
if(plugin.hasPermission(event.getPlayer(), "nocheat.moving"))
return;
// Get the player-specific data
NoCheatData data = plugin.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
// 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
// Fun fact: Move event locations always have the same world in from/to, therefore
// it doesn't matter which one I use
if(data.movingLastWorld != event.getFrom().getWorld()) {
data.movingLastWorld = event.getFrom().getWorld();
// "Forget" previous setback points
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
// Store the destination that this move goes to for later use
data.movingLocation = event.getTo();
// 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(event.getTo())) {
// 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
if(event.getPlayer().isInsideVehicle()) {
data.movingSetBackPoint = null;
data.speedhackSetBackPoint = null;
return;
}
// The server believes the player should be moving up, so we ignore this event
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
Location from = event.getFrom();
Location to = event.getTo();
// First check the distance the player has moved horizontally
// TODO: Make this check much more precise
double xDistance = Math.abs(from.getX() - to.getX());
double zDistance = Math.abs(from.getZ() - to.getZ());
double combined = xDistance * xDistance + zDistance * zDistance;
// If the target is a bed and distance not too big, allow it
if(to.getWorld().getBlockTypeIdAt(to) == Material.BED_BLOCK.getId() && xDistance < 5.0D && zDistance < 5.0D) {
return; // players are allowed to "teleport" into a bed over short distances
}
if(to.getWorld().getBlockTypeIdAt(to) == Material.BED_BLOCK.getId() && xDistance < 5.0D && zDistance < 5.0D) {
return; // players are allowed to "teleport" into a bed over short distances
}
Level vl = null; // The violation level (none, minor, normal, heavy)
// How far are we off?
if(combined > movingDistanceHigh) {
vl = max(vl, Level.SEVERE);
}
else if(combined > movingDistanceMed) {
vl = max(vl, Level.WARNING);
}
else if(combined > movingDistanceLow) {
if(data.movingHorizFreeMoves > 0) {
data.movingHorizFreeMoves--;
}
else vl = max(vl, Level.INFO);
}
else{
data.movingHorizFreeMoves = 4;
}
// pre-calculate boundary values that are needed multiple times in the following checks
// the array each contains [lowerX, higherX, Y, lowerZ, higherZ]
int fromValues[] = {lowerBorder(from.getX()), upperBorder(from.getX()), from.getBlockY(), lowerBorder(from.getZ()),upperBorder(from.getZ()) };
int toValues[] = {lowerBorder(to.getX()), upperBorder(to.getX()), to.getBlockY(), lowerBorder(to.getZ()), upperBorder(to.getZ()) };
// 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(to.getWorld(), toValues, to);
// How far are we off?
if(combined > movingDistanceHigh) {
vl = max(vl, Level.SEVERE);
}
else if(combined > movingDistanceMed) {
vl = max(vl, Level.WARNING);
}
else if(combined > movingDistanceLow) {
if(data.movingHorizFreeMoves > 0) {
data.movingHorizFreeMoves--;
}
else vl = max(vl, Level.INFO);
}
else{
data.movingHorizFreeMoves = 4;
}
// Both locations seem to be on solid ground or at a ladder
if(onGroundFrom && onGroundTo)
{
// 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, 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() < stepHeight)) {
// pre-calculate boundary values that are needed multiple times in the following checks
// the array each contains [lowerX, higherX, Y, lowerZ, higherZ]
int fromValues[] = {lowerBorder(from.getX()), upperBorder(from.getX()), from.getBlockY(), lowerBorder(from.getZ()),upperBorder(from.getZ()) };
int toValues[] = {lowerBorder(to.getX()), upperBorder(to.getX()), to.getBlockY(), lowerBorder(to.getZ()), upperBorder(to.getZ()) };
double offset = (to.getY() - from.getY()) - stepHeight;
// 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(to.getWorld(), toValues, to);
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else
{
// reset jumping
data.movingJumpPhase = 0;
data.movingSetBackPoint = from.clone();
}
}
// player is starting to jump (or starting to fall down somewhere)
else if(onGroundFrom && !onGroundTo)
{
// Check if player isn't jumping too high
double limit = jumpingHeightLimit;
if(to.getY() - from.getY() > limit) {
// Both locations seem to be on solid ground or at a ladder
if(onGroundFrom && onGroundTo)
{
// 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, 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() < stepHeight)) {
double offset = (to.getY() - from.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else {
// Setup next phase of the jump
data.movingJumpPhase = 1;
data.movingSetBackPoint = from.clone();
}
}
// player is probably landing somewhere
else if(!onGroundFrom && onGroundTo)
{
// Check if player isn't landing to high (sounds weird, but has its use)
Location l = data.movingSetBackPoint;
if(l == null) { l = from; }
double limit = jumpingHeightLimit + stepHeight;
if(to.getY() - l.getY() > limit) {
double offset = (to.getY() - from.getY()) - stepHeight;
double offset = (to.getY() - l.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else
{
// reset jumping
data.movingJumpPhase = 0;
data.movingSetBackPoint = from.clone();
}
}
// player is starting to jump (or starting to fall down somewhere)
else if(onGroundFrom && !onGroundTo)
{
// Check if player isn't jumping too high
double limit = jumpingHeightLimit;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else {
data.movingJumpPhase = 0; // He is on ground now, so reset the jump
data.movingSetBackPoint = to.clone();
}
}
// Player is moving through air (during jumping, falling)
else {
Location l = data.movingSetBackPoint;
if(l == null) { l = from; }
// 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() - l.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else {
data.movingJumpPhase++; // Enter next phase of the flight
// 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();
}
}
}
if(to.getY() - from.getY() > limit) {
if(vl == null && (onGroundFrom || onGroundTo)) {
legitimateMove(data, event);
}
else if(vl != null) {
data.movingLastViolationTime = System.currentTimeMillis();
// If we haven't already got a setback point, make this location the new setback point
if(data.movingSetBackPoint == null) {
data.movingSetBackPoint = event.getFrom().clone();
}
String actions = null;
boolean log = true;
// Find out with what actions to treat the violation(s)
if(Level.INFO.equals(vl)) {
data.movingMinorViolationsInARow++;
double offset = (to.getY() - from.getY()) - limit;
actions = NoCheatConfiguration.movingActionMinor;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else {
// Setup next phase of the jump
data.movingJumpPhase = 1;
data.movingSetBackPoint = from.clone();
}
}
// player is probably landing somewhere
else if(!onGroundFrom && onGroundTo)
{
// Check if player isn't landing to high (sounds weird, but has its use)
Location l = data.movingSetBackPoint;
if(l == null) { l = from; }
// React only after the freebee illegal moves have all been used
if(data.movingMinorViolationsInARow <= NoCheatConfiguration.movingFreeMoves) {
vl = null;
actions = null;
}
if(data.movingMinorViolationsInARow > NoCheatConfiguration.movingFreeMoves+1) {
log = false;
}
double limit = jumpingHeightLimit + stepHeight;
// after a set number of minor violations a normal violation gets thrown
if(data.movingMinorViolationsInARow % 40 == 0) {
vl = Level.WARNING;
log = true;
}
}
if(Level.WARNING.equals(vl)) {
if(data.movingNormalViolationsInARow > 0) log = false;
data.movingNormalViolationsInARow++;
actions = NoCheatConfiguration.movingActionNormal;
}
if(to.getY() - l.getY() > limit) {
if(Level.SEVERE.equals(vl)) {
if(data.movingHeavyViolationsInARow > 0) log = false;
data.movingHeavyViolationsInARow++;
actions = NoCheatConfiguration.movingActionHeavy;
}
action(event, actions, log);
}
double offset = (to.getY() - l.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else {
data.movingJumpPhase = 0; // He is on ground now, so reset the jump
data.movingSetBackPoint = to.clone();
}
}
// Player is moving through air (during jumping, falling)
else {
Location l = data.movingSetBackPoint;
if(l == null) { l = from; }
// 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() - l.getY()) - limit;
if(offset > 2D) vl = max(vl, Level.SEVERE);
else if(offset > 0.5D) vl = max(vl, Level.WARNING);
else vl = max(vl, Level.INFO);
}
else {
data.movingJumpPhase++; // Enter next phase of the flight
// 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();
}
}
}
if(vl == null && (onGroundFrom || onGroundTo)) {
legitimateMove(data, event);
}
else if(vl != null) {
data.movingLastViolationTime = System.currentTimeMillis();
// If we haven't already got a setback point, make this location the new setback point
if(data.movingSetBackPoint == null) {
data.movingSetBackPoint = event.getFrom().clone();
}
String actions = null;
boolean log = true;
// Find out with what actions to treat the violation(s)
if(Level.INFO.equals(vl)) {
if(data.movingMinorViolationsInARow > 0) log = false;
data.movingMinorViolationsInARow++;
actions = actionLow;
// after a set number of minor violations a normal violation gets thrown
if(data.movingMinorViolationsInARow % 40 == 0) {
vl = Level.WARNING;
log = true;
}
}
if(Level.WARNING.equals(vl)) {
if(data.movingNormalViolationsInARow > 0) log = false;
data.movingNormalViolationsInARow++;
actions = actionMed;
}
if(Level.SEVERE.equals(vl)) {
if(data.movingHeavyViolationsInARow > 0) log = false;
data.movingHeavyViolationsInARow++;
actions = actionHigh;
}
action(event, actions, log);
}
}
public void teleported(PlayerMoveEvent event) {
NoCheatData data = plugin.getPlayerData(event.getPlayer());
if(data.reset) { // My plugin requested this teleport, so we don't do anything
data.reset = false;
}
else {
if(!event.isCancelled()) {
// If it wasn't our plugin that ordered the teleport, forget all our information and start from scratch at the new location
data.speedhackSetBackPoint = event.getTo().clone();
data.movingSetBackPoint = event.getTo().clone();
data.movingJumpPhase = 0;
}
}
}
/**
* Perform actions that were specified by the admin
* @param event
* @param actions
*/
private static void action(PlayerMoveEvent event, String actions, boolean log) {
private void action(PlayerMoveEvent event, String actions, boolean log) {
if(actions == null) return;
// LOGGING IF NEEDED
if(log && actions.contains("log")) {
NoCheatPlugin.logAction(actions, "Moving violation: "+event.getPlayer().getName()+" from " + String.format("%s (%.5f, %.5f, %.5f) to %s (%.5f, %.5f, %.5f)", event.getFrom().getWorld().getName(), event.getFrom().getX(), event.getFrom().getY(), event.getFrom().getZ(), event.getTo().getWorld().getName(), event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()));
plugin.logAction(actions, "Moving violation: "+event.getPlayer().getName()+" from " + String.format("%s (%.5f, %.5f, %.5f) to %s (%.5f, %.5f, %.5f)", event.getFrom().getWorld().getName(), event.getFrom().getX(), event.getFrom().getY(), event.getFrom().getZ(), event.getTo().getWorld().getName(), event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()));
}
// RESET IF NEEDED
if(actions.contains("reset")) {
resetPlayer(event);
@ -405,31 +419,31 @@ public class MovingCheck {
protected static void legitimateMove(NoCheatData data, PlayerMoveEvent event) {
protected void legitimateMove(NoCheatData data, PlayerMoveEvent event) {
// Give some logging about violations if the player hasn't done any for at least two seconds
if(data.movingLastViolationTime != 0 && data.movingLastViolationTime + 2000L < System.currentTimeMillis()) {
data.movingLastViolationTime = 0;
// Give some additional logs about now ending violations
if(data.movingHeavyViolationsInARow > 0) {
NoCheatPlugin.logAction(NoCheatConfiguration.movingActionHeavy, "Moving violation ended: "+event.getPlayer().getName() + " total Events: "+ data.movingHeavyViolationsInARow);
plugin.logAction(actionHigh, "Moving violation ended: "+event.getPlayer().getName() + " total Events: "+ data.movingHeavyViolationsInARow);
data.movingHeavyViolationsInARow = 0;
}
if(data.movingNormalViolationsInARow > 0) {
NoCheatPlugin.logAction(NoCheatConfiguration.movingActionNormal, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingNormalViolationsInARow);
plugin.logAction(actionMed, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingNormalViolationsInARow);
data.movingNormalViolationsInARow = 0;
}
if(data.movingMinorViolationsInARow > NoCheatConfiguration.movingFreeMoves) {
NoCheatPlugin.logAction(NoCheatConfiguration.movingActionMinor, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingMinorViolationsInARow);
if(data.movingMinorViolationsInARow > 0) {
plugin.logAction(actionLow, "Moving violation ended: "+event.getPlayer().getName()+ " total Events: "+ data.movingMinorViolationsInARow);
data.movingMinorViolationsInARow = 0;
}
data.movingMinorViolationsInARow = 0;
}
}
private static Level max(Level l1, Level l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
@ -442,10 +456,10 @@ public class MovingCheck {
* @param data
* @param event
*/
private static void resetPlayer(PlayerMoveEvent event) {
NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer());
private void resetPlayer(PlayerMoveEvent event) {
NoCheatData data = plugin.getPlayerData(event.getPlayer());
// Reset the jumpphase. We choose the setback-point such that it should be
// 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
@ -453,7 +467,7 @@ public class MovingCheck {
data.movingJumpPhase = 0;
Location l = data.movingSetBackPoint;
data.reset = true;
// If we have stored a location for the player, we put him back there
if(l != null) {
@ -471,88 +485,81 @@ public class MovingCheck {
}
}
/**
* Check the four edges of the player's approximated Bounding Box for blocks or ladders,
* at his own height (values[2]) and below his feet (values[2]-1). Also, check at his "head"
* for ladders.
*
* If there is one, the player is considered as standing on it/hanging to it.
*
* Not perfect at all and will produce some false negatives. Probably will be refined
* later.
*
* @param w The world the coordinates belong to
* @param values The coordinates [lowerX, higherX, Y, lowerZ, higherZ]
* @return
*/
private static boolean playerIsOnGround(World w, int values[], Location l) {
// 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
// Check the four borders of the players hitbox for something he could be standing on
if(types[w.getBlockTypeIdAt(values[0], values[2]-1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2]-1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[0], values[2]-1, values[4])] != BlockType.NONSOLID ||
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)
return true;
// check if he is standing "in" an 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)
else if(types[w.getBlockTypeIdAt(values[0], values[2], values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2], values[3])] != BlockType.NONSOLID||
types[w.getBlockTypeIdAt(values[0], values[2], values[4])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2], values[4])] != BlockType.NONSOLID)
return true;
// check if his head is "stuck" in an 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)
else if(types[w.getBlockTypeIdAt(values[0], values[2]+1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2]+1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[0], values[2]+1, values[4])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2]+1, values[4])] != BlockType.NONSOLID)
return true;
// Allow using a bug called "water elevator"
else if(types[w.getBlockTypeIdAt(values[0]+1, values[2]-1, values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2], values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2]+1, values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2]-1, values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2], values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2]+1, values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2]-1, values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2], values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2]+1, values[3])] == BlockType.LIQUID)
return true;
else
return false;
}
public static int floor_double(double d)
{
int i = (int)d;
return d > (double)i ? i : i - 1;
}
public static int lowerBorder(double d1) {
/**
* Check the four edges of the player's approximated Bounding Box for blocks or ladders,
* at his own height (values[2]) and below his feet (values[2]-1). Also, check at his "head"
* for ladders.
*
* If there is one, the player is considered as standing on it/hanging to it.
*
* Not perfect at all and will produce some false negatives. Probably will be refined
* later.
*
* @param w The world the coordinates belong to
* @param values The coordinates [lowerX, higherX, Y, lowerZ, higherZ]
* @return
*/
private static boolean playerIsOnGround(World w, int values[], Location l) {
// 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
// Check the four borders of the players hitbox for something he could be standing on
if(types[w.getBlockTypeIdAt(values[0], values[2]-1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2]-1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[0], values[2]-1, values[4])] != BlockType.NONSOLID ||
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)
return true;
// check if he is standing "in" an 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)
else if(types[w.getBlockTypeIdAt(values[0], values[2], values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2], values[3])] != BlockType.NONSOLID||
types[w.getBlockTypeIdAt(values[0], values[2], values[4])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2], values[4])] != BlockType.NONSOLID)
return true;
// check if his head is "stuck" in an 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)
else if(types[w.getBlockTypeIdAt(values[0], values[2]+1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2]+1, values[3])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[0], values[2]+1, values[4])] != BlockType.NONSOLID ||
types[w.getBlockTypeIdAt(values[1], values[2]+1, values[4])] != BlockType.NONSOLID)
return true;
// Allow using a bug called "water elevator"
else if(types[w.getBlockTypeIdAt(values[0]+1, values[2]-1, values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2], values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2]+1, values[3]+1)] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2]-1, values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2], values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0]+1, values[2]+1, values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2]-1, values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2], values[3])] == BlockType.LIQUID ||
types[w.getBlockTypeIdAt(values[0], values[2]+1, values[3])] == BlockType.LIQUID)
return true;
else
return false;
}
private static int lowerBorder(double d1) {
double floor = Math.floor(d1);
double d4 = floor + magic;
if(d4 <= d1)
d4 = 0;
else
d4 = 1;
return (int) (floor - d4);
}
public static int upperBorder(double d1) {
private static int upperBorder(double d1) {
double floor = Math.floor(d1);
double d4 = floor + magic2;
@ -563,4 +570,9 @@ public class MovingCheck {
return (int) (floor - d4);
}
@Override
public String getName() {
return "moving";
}
}

View File

@ -3,7 +3,6 @@ package cc.co.evenprime.bukkit.nocheat.checks;
import org.bukkit.Location;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.NoCheatConfiguration;
import cc.co.evenprime.bukkit.nocheat.NoCheatData;
import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
@ -13,39 +12,53 @@ import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
* @author Evenprime
*
*/
public class SpeedhackCheck {
public class SpeedhackCheck extends Check {
public SpeedhackCheck(NoCheatPlugin plugin) {
super(plugin);
setActive(true);
}
private static final long interval = 1000;
private static final int violationsLimit = 3;
// Limits for the speedhack check
public int limitLow = 30;
public int limitMed = 45;
public int limitHigh = 60;
// How should speedhack violations be treated?
public String actionLow = "loglow reset";
public String actionMed = "logmed reset";
public String actionHigh = "loghigh reset";
public void check(PlayerMoveEvent event) {
private static final long interval = 1000;
private static final int violationsLimit = 3;
public static void check(PlayerMoveEvent event) {
// Should we check at all?
if(NoCheatPlugin.hasPermission(event.getPlayer(), "nocheat.speedhack"))
if(plugin.hasPermission(event.getPlayer(), "nocheat.speedhack"))
return;
// Get the player-specific data
NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer());
NoCheatData data = plugin.getPlayerData(event.getPlayer());
// Get the time of the server
long time = System.currentTimeMillis();
// Is it time for a speedhack check now?
if(time > interval + data.speedhackLastCheck ) {
// Yes
// TODO: Needs some better handling for server lag
String action = null;
int limitLow = (int)((NoCheatConfiguration.speedhackLimitLow * (time - data.speedhackLastCheck)) / interval);
int limitMed = (int)((NoCheatConfiguration.speedhackLimitMed * (time - data.speedhackLastCheck)) / interval);
int limitHigh = (int)((NoCheatConfiguration.speedhackLimitHigh * (time - data.speedhackLastCheck)) / interval);
int low = (int)((limitLow * (time - data.speedhackLastCheck)) / interval);
int med = (int)((limitMed * (time - data.speedhackLastCheck)) / interval);
int high = (int)((limitHigh * (time - data.speedhackLastCheck)) / interval);
if(data.speedhackEventsSinceLastCheck > high) action = actionLow;
else if(data.speedhackEventsSinceLastCheck > med) action = actionMed;
else if(data.speedhackEventsSinceLastCheck > low) action = actionHigh;
if(data.speedhackEventsSinceLastCheck > limitHigh) action = NoCheatConfiguration.speedhackActionHeavy;
else if(data.speedhackEventsSinceLastCheck > limitMed) action = NoCheatConfiguration.speedhackActionNormal;
else if(data.speedhackEventsSinceLastCheck > limitLow) action = NoCheatConfiguration.speedhackActionMinor;
if(action == null) {
data.speedhackSetBackPoint = event.getFrom().clone();
data.speedhackViolationsInARow = 0;
@ -57,37 +70,37 @@ public class SpeedhackCheck {
}
data.speedhackViolationsInARow++;
}
if(data.speedhackViolationsInARow >= violationsLimit) {
action(action, event, data);
}
// Reset values for next check
data.speedhackEventsSinceLastCheck = 0;
data.speedhackLastCheck = time;
}
data.speedhackEventsSinceLastCheck++;
}
private static void action(String actions, PlayerMoveEvent event, NoCheatData data) {
private void action(String actions, PlayerMoveEvent event, NoCheatData data) {
if(actions == null) return;
// LOGGING IF NEEDED
if(actions.contains("log")) {
NoCheatPlugin.logAction(actions, event.getPlayer().getName()+" sent "+ data.speedhackEventsSinceLastCheck + " move events, but only "+NoCheatConfiguration.speedhackLimitLow+ " were allowed. Speedhack?");
plugin.logAction(actions, event.getPlayer().getName()+" sent "+ data.speedhackEventsSinceLastCheck + " move events, but only "+limitLow+ " were allowed. Speedhack?");
}
// RESET IF NEEDED
if(actions.contains("reset")) {
resetPlayer(event, data);
}
}
private static void resetPlayer(PlayerMoveEvent event, NoCheatData data) {
Location l = data.speedhackSetBackPoint;
data.reset = true;
// If we have stored a location for the player, we put him back there
if(l != null) {
@ -103,4 +116,9 @@ public class SpeedhackCheck {
event.getPlayer().teleportTo(event.getFrom());
}
}
@Override
public String getName() {
return "speedhack";
}
}

View File

@ -3,7 +3,6 @@ package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.block.BlockListener;
import org.bukkit.event.block.BlockPlaceEvent;
import cc.co.evenprime.bukkit.nocheat.NoCheatConfiguration;
import cc.co.evenprime.bukkit.nocheat.checks.AirbuildCheck;
/**
@ -12,17 +11,17 @@ import cc.co.evenprime.bukkit.nocheat.checks.AirbuildCheck;
* @author Evenprime
*
*/
public class NoCheatBlockListener extends BlockListener {
public NoCheatBlockListener() {
public class AirbuildListener extends BlockListener {
private AirbuildCheck check;
public AirbuildListener(AirbuildCheck check) {
this.check = check;
}
@Override
public void onBlockPlace(BlockPlaceEvent event) {
if(!event.isCancelled() && NoCheatConfiguration.airbuildCheckActive)
AirbuildCheck.check(event);
if(!event.isCancelled() && check.isActive())
check.check(event);
}
}

View File

@ -0,0 +1,23 @@
package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.checks.BedteleportCheck;
public class BedteleportListener extends PlayerListener {
private BedteleportCheck check;
public BedteleportListener(BedteleportCheck check) {
this.check = check;
}
@Override
public void onPlayerTeleport(PlayerMoveEvent event) {
if(!event.isCancelled() && check.isActive()) {
check.check(event);
}
}
}

View File

@ -0,0 +1,28 @@
package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.checks.MovingCheck;
/**
* Handle events for all Player related events
*
* @author Evenprime
*/
public class MovingListener extends PlayerListener {
private MovingCheck check;
public MovingListener(MovingCheck check) {
this.check = check;
}
@Override
public void onPlayerMove(PlayerMoveEvent event) {
if(!event.isCancelled() && check.isActive())
check.check(event);
}
}

View File

@ -0,0 +1,21 @@
package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.checks.MovingCheck;
public class MovingMonitor extends PlayerListener {
private MovingCheck check;
public MovingMonitor(MovingCheck check) {
this.check = check;
}
@Override
public void onPlayerTeleport(PlayerMoveEvent event) {
check.teleported(event);
}
}

View File

@ -1,49 +0,0 @@
package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.NoCheatConfiguration;
import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
import cc.co.evenprime.bukkit.nocheat.checks.BedteleportCheck;
import cc.co.evenprime.bukkit.nocheat.checks.MovingCheck;
import cc.co.evenprime.bukkit.nocheat.checks.SpeedhackCheck;
/**
* Handle events for all Player related events
*
* @author Evenprime
*/
public class NoCheatPlayerListener extends PlayerListener {
public NoCheatPlayerListener() { }
@Override
public void onPlayerQuit(PlayerEvent event) {
NoCheatPlugin.playerData.remove(event.getPlayer());
}
@Override
public void onPlayerMove(PlayerMoveEvent event) {
if(!event.isCancelled() && NoCheatConfiguration.speedhackCheckActive)
SpeedhackCheck.check(event);
if(!event.isCancelled() && NoCheatConfiguration.movingCheckActive)
MovingCheck.check(event);
}
@Override
public void onPlayerTeleport(PlayerMoveEvent event) {
if(!event.isCancelled() && NoCheatConfiguration.bedteleportCheckActive) {
BedteleportCheck.check(event);
}
}
}

View File

@ -1,28 +0,0 @@
package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.NoCheatData;
import cc.co.evenprime.bukkit.nocheat.NoCheatPlugin;
public class NoCheatPlayerListenerMonitor extends PlayerListener {
@Override
public void onPlayerTeleport(PlayerMoveEvent event) {
NoCheatData data = NoCheatPlugin.getPlayerData(event.getPlayer());
if(data.reset) { // My plugin requested this teleport, so we don't do anything
data.reset = false;
}
else {
if(!event.isCancelled()) {
// If it wasn't our plugin that ordered the teleport, forget all our information and start from scratch at the new location
data.speedhackSetBackPoint = event.getTo().clone();
data.movingSetBackPoint = event.getTo().clone();
data.movingJumpPhase = 0;
}
}
}
}

View File

@ -0,0 +1,22 @@
package cc.co.evenprime.bukkit.nocheat.listeners;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import cc.co.evenprime.bukkit.nocheat.checks.SpeedhackCheck;
public class SpeedhackListener extends PlayerListener {
private SpeedhackCheck check;
public SpeedhackListener(SpeedhackCheck check) {
this.check = check;
}
@Override
public void onPlayerMove(PlayerMoveEvent event) {
if(!event.isCancelled() && check.isActive())
check.check(event);
}
}