- Respect -p parameter of craftbukkit (plugin directory)

- "logfile" config option is now relative to the Nocheat/ folder,
instead of relative to the craftBukkit folder.
- new check blockplace.reach: limit distance at which blocks may be
placed
- new check blockplace.liquid: don't allow block placing onto liquids
- new permission nodes and config options for the new checks
This commit is contained in:
Evenprime 2011-09-02 19:33:32 +02:00
parent 848d2b3623
commit 0283ded357
18 changed files with 348 additions and 27 deletions

View File

@ -3,7 +3,7 @@ name: NoCheat
author: Evenprime
main: cc.co.evenprime.bukkit.nocheat.NoCheat
version: 2.00a
version: 2.01
permissions:
@ -18,6 +18,7 @@ permissions:
children:
nocheat.checks.moving.*: true
nocheat.checks.blockbreak.*: true
nocheat.checks.blockplace.*: true
nocheat.checks.interact.*: true
nocheat.checks.moving.*:
@ -35,6 +36,12 @@ permissions:
children:
nocheat.checks.blockbreak.reach: true
nocheat.checks.blockbreak.direction: true
nocheat.checks.blockplace.*:
description: Allow the player to bypass all blockplace checks
children:
nocheat.checks.blockplace.reach: true
nocheat.checks.blockplace.onliquid: true
nocheat.checks.interact.*:
description: Allow the player to bypass all interact checks
@ -67,6 +74,13 @@ permissions:
description: Allow a player to break blocks that are not in front of them
default: op
nocheat.checks.blockplace.reach:
description: Allow a player to place blocks at maximum range (about 6-7 blocks)
default: op
nocheat.checks.blockplace.onliquid:
description: Allow a player to place non-liquid blocks on liquids
default: op
nocheat.checks.interact.durability:
description: Allow a player to use an infinite durability item hack
default: op

View File

@ -38,7 +38,7 @@ public class DefaultConfiguration {
root.add(loggingNode);
loggingNode.add(new BooleanOption("active", true, true));
loggingNode.add(new MediumStringOption("filename", "plugins/NoCheat/nocheat.log"));
loggingNode.add(new MediumStringOption("filename", "nocheat.log"));
loggingNode.add(new LogLevelOption("filelevel", LogLevel.LOW));
loggingNode.add(new LogLevelOption("consolelevel", LogLevel.HIGH));
loggingNode.add(new LogLevelOption("chatlevel", LogLevel.MED));
@ -172,6 +172,45 @@ public class DefaultConfiguration {
}
}
/****** BLOCKPLACE ******/
{
ParentOption blockPlaceNode = new ParentOption("blockplace");
root.add(blockPlaceNode);
blockPlaceNode.add(new BooleanOption("check", true, true));
/**** BLOCKPLACE.REACH ****/
{
ParentOption reachNode = new ParentOption("reach");
blockPlaceNode.add(reachNode);
reachNode.add(new BooleanOption("check", true, true));
reachNode.add(new IntegerOption("reachlimit", 485));
ActionListOption actions = new ActionListOption("actions");
reachNode.add(actions);
actions.add(0, "reachLog blockplaceCancel");
}
/**** BLOCKPLACE.ONLIQUID ****/
{
ParentOption onliquidNode = new ParentOption("onliquid");
blockPlaceNode.add(onliquidNode);
onliquidNode.add(new BooleanOption("check", true, true));
ActionListOption actions = new ActionListOption("actions");
onliquidNode.add(actions);
actions.add(0, "onliquidLog blockplaceCancel");
}
}
/****** INTERACT ******/
{
ParentOption interactNode = new ParentOption("interact");
@ -258,9 +297,10 @@ public class DefaultConfiguration {
w(w, "");
w(w, "# Some other log messages that are limited a bit by default, to avoid too extreme spam");
w(w, "log noclipLog 0 1 high NC: [player] failed [check]: at [location] to [locationto].");
w(w, "log reachLog 0 1 med NC: [player] failed [check]: tried to destroy a block over distance [distance].");
w(w, "log reachLog 0 1 med NC: [player] failed [check]: tried to interact with a block over distance [distance].");
w(w, "log directionLog 2 1 med NC: [player] failed [check]: tried to destroy a block out of line of sight.");
w(w, "log durabilityLog 0 1 med NC: [player] failed [check]: tried to use infinity durability hack.");
w(w, "log onliquidLog 2 1 med NC: [player] failed [check]: tried to place a block on liquids.");
w(w, "");
w(w, "# SPECIAL Actions: They will do something check dependant, usually cancel an event.");
w(w, "# - They start with the word 'special'");
@ -272,6 +312,7 @@ public class DefaultConfiguration {
w(w, "# Cancels the event in case of an violation. Always. No delay. These are equivalent. The different names are just for better readability");
w(w, "special moveCancel 0 0");
w(w, "special blockbreakCancel 0 0");
w(w, "special blockplaceCancel 0 0");
w(w, "special interactCancel 0 0");
w(w, "");
w(w, "# CONSOLECOMMAND Actions: They will execute a command as if it were typed into the console.");

View File

@ -44,7 +44,7 @@ public class Explainations {
set("moving.noclip.check", "If true, check if a player is moving into a solid wall. EXPERIMENTAL! DOESN'T WORK RELIABLY! USE WITH CAUTION AND ONLY FOR NOTIFICATIONS!");
set("moving.noclip.actions", "What should be done if a player moves into a wall.\nUnit is number of walls a player walks into/through.");
set("blockbreak.check", "If true, do various checks on PlayerInteract events.");
set("blockbreak.check", "If true, do various checks on BlockBreak events.");
set("blockbreak.reach.check", "If true, check if a player is breaking blocks that are too far away.");
set("blockbreak.reach.reachlimit", "Set the distance limit for breaking blocks.\nUnit is 1/100 of a block, default is 485");
@ -53,6 +53,17 @@ public class Explainations {
set("blockbreak.direction.check", "If true, check if a player is looking at the block that he's breaking.");
set("blockbreak.direction.actions", "What should be done if a player is breaking blocks that are not in his line of sight.\nUnit is number of break(attempt)s outside the line of sight.");
set("blockplace.check", "If true, do various checks on BlockPlace events.");
set("blockplace.reach.check", "If true, check if a player is placing blocks at locations too far away.");
set("blockplace.reach.reachlimit", "Set the distance limit for placing blocks.\nUnit is 1/100 of a block, default is 485");
set("blockplace.reach.actions", "What should be done if a player is placing blocks that are too far away.\nUnit is number of place(attempt)s beyond the limit.");
set("blockplace.onliquid.check", "If true, check if a player is trying to place non-liquid blocks against liquid blocks");
set("blockplace.onliquid.actions", "What should be done if a player is is trying to place non-liquid blocks against liquid blocks.\nUnit is number of place(attempt)s.");
set("interact.check", "If true, do various checks on PlayerInteract events.");
set("interact.durability.check", "If true, check if a player is using a hack that provides infinite durability items.");
set("interact.durability.actions", "What should be done if a player is trying to use the hack.\nUnit is number of uses or attempts to use the hack.");
}

View File

@ -33,9 +33,8 @@ public class NoCheat extends JavaPlugin {
private PlayerMoveEventManager eventPlayerMoveManager;
private PlayerTeleportEventManager eventPlayerTeleportManager;
private BlockBreakEventManager eventBlockBreakManager;
private PlayerInteractEventManager eventPlayerInteractManager;
private BlockPlaceEventManager eventBlockPlaceManager;
private PlayerInteractEventManager eventPlayerInteractManager;
private ActionManager action;
@ -60,15 +59,15 @@ public class NoCheat extends JavaPlugin {
this.data = new DataManager();
this.action = new ActionManager(log);
// parse the nocheat.yml config file
this.conf = new ConfigurationManager(ConfigurationManager.rootConfigFolder, action);
this.conf = new ConfigurationManager(this.getDataFolder().getPath(), action);
eventPlayerMoveManager = new PlayerMoveEventManager(this);
eventPlayerTeleportManager = new PlayerTeleportEventManager(this);
eventBlockBreakManager = new BlockBreakEventManager(this);
eventPlayerInteractManager = new PlayerInteractEventManager(this);
eventBlockPlaceManager = new BlockPlaceEventManager(this);
eventPlayerInteractManager = new PlayerInteractEventManager(this);
PluginDescriptionFile pdfFile = this.getDescription();

View File

@ -12,6 +12,7 @@ public class Permissions {
private final static String _CHECKS = _NOCHEAT + ".checks";
private final static String _MOVE = _CHECKS + ".moving";
private final static String _BLOCKBREAK = _CHECKS + ".blockbreak";
public static final String _BLOCKPLACE = _CHECKS + ".blockplace";
public final static String _INTERACT = _CHECKS + ".interact";
public final static String MOVE = _CHECKS + ".moving.*";
@ -27,7 +28,11 @@ public class Permissions {
public final static String BLOCKBREAK_DIRECTION = _BLOCKBREAK + ".direction";
public final static String INTERACT = _CHECKS + ".interact.*";
public final static String DURABILITY = _INTERACT + ".durability";
public final static String INTERACT_DURABILITY = _INTERACT + ".durability";
public final static String BLOCKPLACE = _CHECKS + ".blockplace.*";
public final static String BLOCKPLACE_ONLIQUID = _BLOCKPLACE + ".onliquid";
public static final String BLOCKPLACE_REACH = _BLOCKPLACE + ".reach";
private final static String _ADMIN = _NOCHEAT + ".admin";

View File

@ -0,0 +1,45 @@
package cc.co.evenprime.bukkit.nocheat.checks.blockplace;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.Permissions;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.BlockPlaceData;
/**
*
* @author Evenprime
*
*/
public class BlockPlaceCheck {
private final ReachCheck reachCheck;
private final OnLiquidCheck onLiquidCheck;
public BlockPlaceCheck(NoCheat plugin) {
reachCheck = new ReachCheck(plugin);
onLiquidCheck = new OnLiquidCheck(plugin);
}
public boolean check(Player player, Block blockPlaced, Block blockPlacedAgainst, BlockPlaceData data, ConfigurationCache cc) {
boolean cancel = false;
// Which checks are going to be executed?
final boolean onliquid = cc.blockplace.onliquidCheck && !player.hasPermission(Permissions.BLOCKPLACE_ONLIQUID);
final boolean reach = cc.blockplace.reachCheck && !player.hasPermission(Permissions.BLOCKPLACE_REACH);
if(!cancel && reach) {
cancel = reachCheck.check(player, blockPlaced, blockPlacedAgainst, data, cc);
}
if(!cancel && onliquid) {
cancel = onLiquidCheck.check(player, blockPlaced, blockPlacedAgainst, data, cc);
}
return cancel;
}
}

View File

@ -0,0 +1,47 @@
package cc.co.evenprime.bukkit.nocheat.checks.blockplace;
import java.util.HashMap;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutor;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutorWithHistory;
import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.BlockPlaceData;
/**
*
* @author Evenprime
*
*/
public class OnLiquidCheck {
private ActionExecutor action;
public OnLiquidCheck(NoCheat plugin) {
action = new ActionExecutorWithHistory(plugin);
}
public boolean check(Player player, Block blockPlaced, Block blockPlacedAgainst, BlockPlaceData data, ConfigurationCache cc) {
boolean cancel = false;
if(blockPlaced == null || blockPlaced.isLiquid() || blockPlaced.isEmpty()) {
// all ok
} else if(blockPlacedAgainst != null && (blockPlacedAgainst.isLiquid() || blockPlacedAgainst.isEmpty())) {
data.onliquidViolationLevel += 1;
HashMap<String, String> params = new HashMap<String, String>();
params.put(LogAction.CHECK, "blockplace.onliquid");
cancel = action.executeActions(player, cc.blockplace.onliquidActions, (int) data.onliquidViolationLevel, params, cc);
}
data.onliquidViolationLevel *= 0.95D; // Reduce level over time
return cancel;
}
}

View File

@ -0,0 +1,63 @@
package cc.co.evenprime.bukkit.nocheat.checks.blockplace;
import java.util.HashMap;
import java.util.Locale;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutor;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutorWithHistory;
import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.BlockPlaceData;
/**
* The reach check will find out if a player interacts with something that's too
* far away
*
* @author Evenprime
*
*/
public class ReachCheck {
private ActionExecutor action;
public ReachCheck(NoCheat plugin) {
this.action = new ActionExecutorWithHistory(plugin);
}
public boolean check(Player player, Block blockPlaced, Block placedAgainstBlock, BlockPlaceData data, ConfigurationCache cc) {
boolean cancel = false;
Location eyes = player.getEyeLocation();
final double x1 = ((double) placedAgainstBlock.getX()) - eyes.getX();
final double y1 = ((double) placedAgainstBlock.getY()) - eyes.getY();
final double z1 = ((double) placedAgainstBlock.getZ()) - eyes.getZ();
double distance = new Vector(x1 + 0.5, y1 + + 0.5, z1 + + 0.5).length();
if(distance > cc.blockplace.reachDistance) {
// Player failed the check
// Increment violation counter
data.reachViolationLevel += 1;
// Prepare some event-specific values for logging and custom actions
HashMap<String, String> params = new HashMap<String, String>();
params.put(LogAction.CHECK, "blockplace.reach");
params.put(LogAction.DISTANCE, String.format(Locale.US, "%.2f", distance));
cancel = action.executeActions(player, cc.blockplace.reachActions, (int) data.reachViolationLevel, params, cc);
} else {
data.reachViolationLevel *= 0.9D;
}
return cancel;
}
}

View File

@ -10,7 +10,7 @@ import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutor;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutorWithHistory;
import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.events.InteractData;
import cc.co.evenprime.bukkit.nocheat.data.InteractData;
/**
*
@ -29,7 +29,7 @@ public class InteractCheck {
boolean cancel = false;
final boolean durability = cc.moving.morePacketsCheck && !player.hasPermission(Permissions.DURABILITY);
final boolean durability = cc.moving.morePacketsCheck && !player.hasPermission(Permissions.INTERACT_DURABILITY);
if(durability) {
// It's so simple, I'll just do the check in place

View File

@ -43,7 +43,7 @@ public class ConfigurationManager {
private final static String defaultActionFileName = "default_actions.txt";
private final static String descriptionsFileName = "descriptions.txt";
public final static String rootConfigFolder = "plugins/NoCheat/";
public final static String rootConfigFolder = "plugins/NoCheat/"; // default
private final Map<String, ConfigurationCache> worldnameToConfigCacheMap = new HashMap<String, ConfigurationCache>();
@ -128,7 +128,7 @@ public class ConfigurationManager {
// Create a corresponding Configuration Cache
// put the global config on the config map
worldnameToConfigCacheMap.put(null, new ConfigurationCache(root, setupFileLogger(new File(root.getString("logging.filename")))));
worldnameToConfigCacheMap.put(null, new ConfigurationCache(root, setupFileLogger(new File(rootConfigFolder, root.getString("logging.filename")))));
// Try to find world-specific config files
Map<String, File> worldFiles = getWorldSpecificConfigFiles(rootConfigFolder);
@ -139,7 +139,7 @@ public class ConfigurationManager {
try {
ConfigurationTree world = createPartialConfigurationTree(root, worldConfigFile);
worldnameToConfigCacheMap.put(worldName, createConfigurationCache(world));
worldnameToConfigCacheMap.put(worldName, createConfigurationCache(rootConfigFolder, world));
// write the config file back to disk immediately
writeConfigFile(worldFiles.get(worldName), world);
@ -152,9 +152,9 @@ public class ConfigurationManager {
writeDescriptionFile(new File(rootConfigFolder, descriptionsFileName), defaultTree);
}
private ConfigurationCache createConfigurationCache(Configuration configProvider) {
private ConfigurationCache createConfigurationCache(String rootConfigFolder, Configuration configProvider) {
return new ConfigurationCache(configProvider, setupFileLogger(new File(configProvider.getString("logging.filename"))));
return new ConfigurationCache(configProvider, setupFileLogger(new File(rootConfigFolder, configProvider.getString("logging.filename"))));
}
@ -273,6 +273,11 @@ public class ConfigurationManager {
if(fh == null) {
try {
try {
logfile.getParentFile().mkdirs();
} catch(Exception e) {
e.printStackTrace();
}
fh = new FileHandler(logfile.getCanonicalPath(), true);
// We decide before logging what gets logged there anyway
// because different worlds may use this filehandler and

View File

@ -0,0 +1,32 @@
package cc.co.evenprime.bukkit.nocheat.config.cache;
import cc.co.evenprime.bukkit.nocheat.actions.ActionList;
import cc.co.evenprime.bukkit.nocheat.config.Configuration;
/**
*
* @author Evenprime
*
*/
public class CCBlockPlace {
public final boolean check;
public final boolean onliquidCheck;
public final ActionList onliquidActions;
public final boolean reachCheck;
public final double reachDistance;
public final ActionList reachActions;
public CCBlockPlace(Configuration data) {
check = data.getBoolean("blockplace.check");
onliquidCheck = data.getBoolean("blockplace.onliquid.check");
onliquidActions = data.getActionList("blockplace.onliquid.actions");
reachCheck = data.getBoolean("blockplace.reach.check");
reachDistance = data.getInteger("blockplace.reach.reachlimit");
reachActions = data.getActionList("blockplace.reach.actions");
}
}

View File

@ -17,6 +17,7 @@ public class ConfigurationCache {
public final CCLogging logging;
public final CCBlockBreak blockbreak;
public final CCInteract interact;
public final CCBlockPlace blockplace;
/**
* Instantiate a config cache and populate it with the data of a
@ -28,6 +29,7 @@ public class ConfigurationCache {
moving = new CCMoving(data);
blockbreak = new CCBlockBreak(data);
blockplace = new CCBlockPlace(data);
interact = new CCInteract(data);
logging = new CCLogging(data, worldSpecificFileLogger);

View File

@ -0,0 +1,12 @@
package cc.co.evenprime.bukkit.nocheat.data;
/**
*
* @author Evenprime
*
*/
public class BlockPlaceData {
public double onliquidViolationLevel = 0.0D;
public double reachViolationLevel = 0.0D;
}

View File

@ -5,7 +5,6 @@ import java.util.Map;
import org.bukkit.entity.Player;
import cc.co.evenprime.bukkit.nocheat.events.InteractData;
/**
* Provide secure access to player-specific data objects for various checks or
@ -20,6 +19,7 @@ public class DataManager {
private final Map<Player, MovingData> movingData = new HashMap<Player, MovingData>();
private final Map<Player, BlockBreakData> blockbreakData = new HashMap<Player, BlockBreakData>();
private final Map<Player, InteractData> interactData = new HashMap<Player, InteractData>();
private Map<Player, BlockPlaceData> blockPlaceData = new HashMap<Player, BlockPlaceData>();
public DataManager() {
@ -71,4 +71,19 @@ public class DataManager {
return data;
}
public BlockPlaceData getBlockPlaceData(Player player) {
BlockPlaceData data;
// intentionally not thread-safe, because bukkit events are handled
// in sequence anyway, so zero chance of two events of the same
// player being handled at the same time
data = blockPlaceData.get(player);
if(data == null) {
data = new BlockPlaceData();
blockPlaceData.put(player, data);
}
return data;
}
}

View File

@ -1,4 +1,4 @@
package cc.co.evenprime.bukkit.nocheat.events;
package cc.co.evenprime.bukkit.nocheat.data;
/**

View File

@ -9,9 +9,12 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.plugin.PluginManager;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.Permissions;
import cc.co.evenprime.bukkit.nocheat.checks.blockplace.BlockPlaceCheck;
import cc.co.evenprime.bukkit.nocheat.checks.moving.MovingCheck;
import cc.co.evenprime.bukkit.nocheat.config.ConfigurationManager;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.DataManager;
import cc.co.evenprime.bukkit.nocheat.data.MovingData;
/**
* Central location to listen to Block-related events and dispatching them to
@ -22,30 +25,56 @@ import cc.co.evenprime.bukkit.nocheat.data.MovingData;
*/
public class BlockPlaceEventManager extends BlockListener {
private final MovingCheck movingCheck;
private final DataManager data;
private final MovingCheck movingCheck;
private final BlockPlaceCheck blockPlaceCheck;
private final DataManager data;
private final ConfigurationManager config;
public BlockPlaceEventManager(NoCheat plugin) {
this.data = plugin.getDataManager();
this.config = plugin.getConfigurationManager();
this.movingCheck = new MovingCheck(plugin);
this.blockPlaceCheck = new BlockPlaceCheck(plugin);
PluginManager pm = Bukkit.getServer().getPluginManager();
pm.registerEvent(Event.Type.BLOCK_PLACE, this, Priority.Monitor, plugin);
pm.registerEvent(Event.Type.BLOCK_PLACE, this, Priority.Lowest, plugin);
// This is part of a workaround for the moving check
pm.registerEvent(Event.Type.BLOCK_PLACE, new BlockListener() {
@Override
public void onBlockPlace(BlockPlaceEvent event) {
if(!event.isCancelled()) {
final Player player = event.getPlayer();
// Get the player-specific stored data that applies here
movingCheck.blockPlaced(player, data.getMovingData(player), event.getBlockPlaced());
}
}
}, Priority.Monitor, plugin);
}
@Override
public void onBlockPlace(BlockPlaceEvent event) {
if(!event.isCancelled()) {
boolean cancel = false;
final Player player = event.getPlayer();
// Get the player-specific stored data that applies here
final MovingData data = this.data.getMovingData(player);
final ConfigurationCache cc = config.getConfigurationCacheForWorld(player.getWorld().getName());
movingCheck.blockPlaced(player, data, event.getBlockPlaced());
// Find out if checks need to be done for that player
if(cc.blockplace.check && !player.hasPermission(Permissions.BLOCKPLACE)) {
cancel = blockPlaceCheck.check(player, event.getBlockPlaced(), event.getBlockAgainst(), data.getBlockPlaceData(player), cc);
}
if(cancel) {
event.setCancelled(true);
}
}
}
}

View File

@ -14,6 +14,7 @@ import cc.co.evenprime.bukkit.nocheat.checks.interact.InteractCheck;
import cc.co.evenprime.bukkit.nocheat.config.ConfigurationManager;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.DataManager;
import cc.co.evenprime.bukkit.nocheat.data.InteractData;
/**
*

View File

@ -106,7 +106,7 @@ public class PlayerMoveEventManager extends PlayerListener {
Vector v = event.getVelocity();
double newVal = v.getY();
if(newVal > 0.0D) {
if(newVal >= 0.0D) {
mdata.vertVelocity += newVal;
mdata.vertFreedom += mdata.vertVelocity;
mdata.vertVelocityCounter = 50;