diff options
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace')
6 files changed, 464 insertions, 0 deletions
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceCheck.java new file mode 100644 index 000000000..e20a74ca9 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceCheck.java @@ -0,0 +1,99 @@ +package com.earth2me.essentials.anticheat.checks.blockplace; + +import com.earth2me.essentials.anticheat.NoCheat; +import com.earth2me.essentials.anticheat.NoCheatPlayer; +import com.earth2me.essentials.anticheat.actions.ParameterName; +import com.earth2me.essentials.anticheat.checks.Check; +import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore; +import com.earth2me.essentials.anticheat.data.DataStore; +import com.earth2me.essentials.anticheat.data.SimpleLocation; +import java.util.Locale; + + +/** + * Abstract base class for BlockPlace checks, provides some convenience methods for access to data and config that's + * relevant to this checktype + */ +public abstract class BlockPlaceCheck extends Check +{ + private static final String id = "blockplace"; + + public BlockPlaceCheck(NoCheat plugin, String name) + { + super(plugin, id, name); + } + + @Override + public String getParameter(ParameterName wildcard, NoCheatPlayer player) + { + if (wildcard == ParameterName.PLACE_LOCATION) + { + SimpleLocation l = getData(player).blockPlaced; + if (l.isSet()) + { + return String.format(Locale.US, "%d %d %d", l.x, l.y, l.z); + } + else + { + return "null"; + } + } + else if (wildcard == ParameterName.PLACE_AGAINST) + { + SimpleLocation l = getData(player).blockPlacedAgainst; + if (l.isSet()) + { + return String.format(Locale.US, "%d %d %d", l.x, l.y, l.z); + } + else + { + return "null"; + } + } + else + { + return super.getParameter(wildcard, player); + } + } + + /** + * Get the "BlockPlaceData" object that belongs to the player. Will ensure that such a object exists and if not, + * create one + * + * @param player + * @return + */ + public static BlockPlaceData getData(NoCheatPlayer player) + { + DataStore base = player.getDataStore(); + BlockPlaceData data = base.get(id); + if (data == null) + { + data = new BlockPlaceData(); + base.set(id, data); + } + return data; + } + + /** + * Get the BlockPlaceConfig object that belongs to the world that the player currently resides in. + * + * @param player + * @return + */ + public static BlockPlaceConfig getConfig(NoCheatPlayer player) + { + return getConfig(player.getConfigurationStore()); + } + + public static BlockPlaceConfig getConfig(ConfigurationCacheStore cache) + { + BlockPlaceConfig config = cache.get(id); + if (config == null) + { + config = new BlockPlaceConfig(cache.getConfiguration()); + cache.set(id, config); + } + return config; + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceCheckListener.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceCheckListener.java new file mode 100644 index 000000000..253982bd1 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceCheckListener.java @@ -0,0 +1,97 @@ +package com.earth2me.essentials.anticheat.checks.blockplace; + +import java.util.LinkedList; +import java.util.List; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import com.earth2me.essentials.anticheat.EventManager; +import com.earth2me.essentials.anticheat.NoCheat; +import com.earth2me.essentials.anticheat.NoCheatPlayer; +import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore; +import com.earth2me.essentials.anticheat.config.Permissions; + + +/** + * Central location to listen to Block-related events and dispatching them to checks + * + */ +public class BlockPlaceCheckListener implements Listener, EventManager +{ + private final ReachCheck reachCheck; + private final DirectionCheck directionCheck; + private final NoCheat plugin; + + public BlockPlaceCheckListener(NoCheat plugin) + { + + this.plugin = plugin; + + reachCheck = new ReachCheck(plugin); + directionCheck = new DirectionCheck(plugin); + } + + /** + * We listen to BlockPlace events for obvious reasons + * + * @param event the BlockPlace event + */ + @EventHandler(priority = EventPriority.LOWEST) + protected void handleBlockPlaceEvent(BlockPlaceEvent event) + { + + if (event.isCancelled() || event.getBlock() == null || event.getBlockAgainst() == null) + { + return; + } + + boolean cancelled = false; + + final NoCheatPlayer player = plugin.getPlayer(event.getPlayer()); + final BlockPlaceConfig cc = BlockPlaceCheck.getConfig(player); + final BlockPlaceData data = BlockPlaceCheck.getData(player); + + // Remember these locations and put them in a simpler "format" + data.blockPlaced.set(event.getBlock()); + data.blockPlacedAgainst.set(event.getBlockAgainst()); + + // Now do the actual checks + + // First the reach check + if (cc.reachCheck && !player.hasPermission(Permissions.BLOCKPLACE_REACH)) + { + cancelled = reachCheck.check(player, data, cc); + } + + // Second the direction check + if (!cancelled && cc.directionCheck && !player.hasPermission(Permissions.BLOCKPLACE_DIRECTION)) + { + cancelled = directionCheck.check(player, data, cc); + } + + // If one of the checks requested to cancel the event, do so + if (cancelled) + { + event.setCancelled(cancelled); + } + } + + public List<String> getActiveChecks(ConfigurationCacheStore cc) + { + LinkedList<String> s = new LinkedList<String>(); + + BlockPlaceConfig bp = BlockPlaceCheck.getConfig(cc); + + if (bp.reachCheck) + { + s.add("blockplace.reach"); + } + if (bp.directionCheck) + { + s.add("blockplace.direction"); + } + + return s; + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceConfig.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceConfig.java new file mode 100644 index 000000000..26c8d0f6d --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceConfig.java @@ -0,0 +1,37 @@ +package com.earth2me.essentials.anticheat.checks.blockplace; + +import com.earth2me.essentials.anticheat.ConfigItem; +import com.earth2me.essentials.anticheat.actions.types.ActionList; +import com.earth2me.essentials.anticheat.config.ConfPaths; +import com.earth2me.essentials.anticheat.config.NoCheatConfiguration; +import com.earth2me.essentials.anticheat.config.Permissions; + + +/** + * Configurations specific for the "BlockPlace" checks Every world gets one of these assigned to it, or if a world + * doesn't get it's own, it will use the "global" version + * + */ +public class BlockPlaceConfig implements ConfigItem +{ + public final boolean reachCheck; + public final double reachDistance; + public final ActionList reachActions; + public final boolean directionCheck; + public final ActionList directionActions; + public final long directionPenaltyTime; + public final double directionPrecision; + + public BlockPlaceConfig(NoCheatConfiguration data) + { + + reachCheck = data.getBoolean(ConfPaths.BLOCKPLACE_REACH_CHECK); + reachDistance = 535D / 100D; + reachActions = data.getActionList(ConfPaths.BLOCKPLACE_REACH_ACTIONS, Permissions.BLOCKPLACE_REACH); + + directionCheck = data.getBoolean(ConfPaths.BLOCKPLACE_DIRECTION_CHECK); + directionPenaltyTime = data.getInt(ConfPaths.BLOCKPLACE_DIRECTION_PENALTYTIME); + directionPrecision = ((double)data.getInt(ConfPaths.BLOCKPLACE_DIRECTION_PRECISION)) / 100D; + directionActions = data.getActionList(ConfPaths.BLOCKPLACE_DIRECTION_ACTIONS, Permissions.BLOCKPLACE_DIRECTION); + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceData.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceData.java new file mode 100644 index 000000000..47ff9d58a --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/BlockPlaceData.java @@ -0,0 +1,25 @@ +package com.earth2me.essentials.anticheat.checks.blockplace; + +import com.earth2me.essentials.anticheat.DataItem; +import com.earth2me.essentials.anticheat.data.SimpleLocation; + + +/** + * Player specific data for the blockbreak checks + * + */ +public class BlockPlaceData implements DataItem +{ + // Keep track of violation levels for the two checks + public double reachVL = 0.0D; + public double directionVL = 0.0D; + // Used for the penalty time feature of the direction check + public long directionLastViolationTime = 0; + // Have a nicer/simpler way to work with block locations instead of + // Bukkits own "Location" class + public final SimpleLocation blockPlacedAgainst = new SimpleLocation(); + public final SimpleLocation blockPlaced = new SimpleLocation(); + // For logging, remember the reachDistance that was calculated in the + // reach check + public double reachdistance; +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/DirectionCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/DirectionCheck.java new file mode 100644 index 000000000..8aa782d19 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/DirectionCheck.java @@ -0,0 +1,131 @@ +package com.earth2me.essentials.anticheat.checks.blockplace; + +import com.earth2me.essentials.anticheat.NoCheat; +import com.earth2me.essentials.anticheat.NoCheatPlayer; +import com.earth2me.essentials.anticheat.actions.ParameterName; +import com.earth2me.essentials.anticheat.checks.CheckUtil; +import com.earth2me.essentials.anticheat.data.SimpleLocation; +import com.earth2me.essentials.anticheat.data.Statistics.Id; +import java.util.Locale; +import org.bukkit.Location; + + +/** + * The DirectionCheck will find out if a player tried to interact with something that's not in his field of view. + * + */ +public class DirectionCheck extends BlockPlaceCheck +{ + public DirectionCheck(NoCheat plugin) + { + super(plugin, "blockplace.direction"); + } + + public boolean check(NoCheatPlayer player, BlockPlaceData data, BlockPlaceConfig cc) + { + + boolean cancel = false; + + final SimpleLocation blockPlaced = data.blockPlaced; + final SimpleLocation blockPlacedAgainst = data.blockPlacedAgainst; + + // How far "off" is the player with his aim. We calculate from the + // players eye location and view direction to the center of the target + // block. If the line of sight is more too far off, "off" will be + // bigger than 0 + double off = CheckUtil.directionCheck(player, blockPlacedAgainst.x + 0.5D, blockPlacedAgainst.y + 0.5D, blockPlacedAgainst.z + 0.5D, 1D, 1D, cc.directionPrecision); + + // now check if the player is looking at the block from the correct side + double off2 = 0.0D; + + // Find out against which face the player tried to build, and if he + // stood on the correct side of it + Location eyes = player.getPlayer().getEyeLocation(); + if (blockPlaced.x > blockPlacedAgainst.x) + { + off2 = blockPlacedAgainst.x + 0.5D - eyes.getX(); + } + else if (blockPlaced.x < blockPlacedAgainst.x) + { + off2 = -(blockPlacedAgainst.x + 0.5D - eyes.getX()); + } + else if (blockPlaced.y > blockPlacedAgainst.y) + { + off2 = blockPlacedAgainst.y + 0.5D - eyes.getY(); + } + else if (blockPlaced.y < blockPlacedAgainst.y) + { + off2 = -(blockPlacedAgainst.y + 0.5D - eyes.getY()); + } + else if (blockPlaced.z > blockPlacedAgainst.z) + { + off2 = blockPlacedAgainst.z + 0.5D - eyes.getZ(); + } + else if (blockPlaced.z < blockPlacedAgainst.z) + { + off2 = -(blockPlacedAgainst.z + 0.5D - eyes.getZ()); + } + + // If he wasn't on the correct side, add that to the "off" value + if (off2 > 0.0D) + { + off += off2; + } + + final long time = System.currentTimeMillis(); + + if (off < 0.1D) + { + // Player did nothing wrong + // reduce violation counter to reward him + data.directionVL *= 0.9D; + } + else + { + // Player failed the check + // Increment violation counter and statistics + data.directionVL += off; + incrementStatistics(player, Id.BP_DIRECTION, off); + + // Execute whatever actions are associated with this check and the + // violation level and find out if we should cancel the event + cancel = executeActions(player, cc.directionActions, data.directionVL); + + if (cancel) + { + // if we should cancel, remember the current time too + data.directionLastViolationTime = time; + } + } + + // If the player is still in penalty time, cancel the event anyway + if (data.directionLastViolationTime + cc.directionPenaltyTime > time) + { + // A safeguard to avoid people getting stuck in penalty time + // indefinitely in case the system time of the server gets changed + if (data.directionLastViolationTime > time) + { + data.directionLastViolationTime = 0; + } + + // He is in penalty time, therefore request cancelling of the event + return true; + } + + return cancel; + } + + @Override + public String getParameter(ParameterName wildcard, NoCheatPlayer player) + { + + if (wildcard == ParameterName.VIOLATIONS) + { + return String.format(Locale.US, "%d", (int)getData(player).directionVL); + } + else + { + return super.getParameter(wildcard, player); + } + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/ReachCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/ReachCheck.java new file mode 100644 index 000000000..6e13a9348 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockplace/ReachCheck.java @@ -0,0 +1,75 @@ +package com.earth2me.essentials.anticheat.checks.blockplace; + +import com.earth2me.essentials.anticheat.NoCheat; +import com.earth2me.essentials.anticheat.NoCheatPlayer; +import com.earth2me.essentials.anticheat.actions.ParameterName; +import com.earth2me.essentials.anticheat.checks.CheckUtil; +import com.earth2me.essentials.anticheat.data.SimpleLocation; +import com.earth2me.essentials.anticheat.data.Statistics.Id; +import java.util.Locale; + + +/** + * The reach check will find out if a player interacts with something that's too far away + * + */ +public class ReachCheck extends BlockPlaceCheck +{ + public ReachCheck(NoCheat plugin) + { + super(plugin, "blockplace.reach"); + } + + public boolean check(NoCheatPlayer player, BlockPlaceData data, BlockPlaceConfig cc) + { + + boolean cancel = false; + + final SimpleLocation placedAgainstBlock = data.blockPlacedAgainst; + + // Distance is calculated from eye location to center of targeted block + // If the player is further away from his target than allowed, the + // difference will be assigned to "distance" + final double distance = CheckUtil.reachCheck(player, placedAgainstBlock.x + 0.5D, placedAgainstBlock.y + 0.5D, placedAgainstBlock.z + 0.5D, player.isCreative() ? cc.reachDistance + 2 : cc.reachDistance); + + if (distance <= 0D) + { + // Player passed the check, reward him + data.reachVL *= 0.9D; + } + else + { + // He failed, increment violation level and statistics + data.reachVL += distance; + incrementStatistics(player, Id.BP_REACH, distance); + + // Remember how much further than allowed he tried to reach for + // logging, if necessary + data.reachdistance = distance; + + // Execute whatever actions are associated with this check and the + // violation level and find out if we should cancel the event + cancel = executeActions(player, cc.reachActions, data.reachVL); + } + + return cancel; + } + + @Override + public String getParameter(ParameterName wildcard, NoCheatPlayer player) + { + + if (wildcard == ParameterName.VIOLATIONS) + { + return String.format(Locale.US, "%d", (int)getData(player).reachVL); + } + else if (wildcard == ParameterName.REACHDISTANCE) + { + return String.format(Locale.US, "%.2f", getData(player).reachdistance); + } + else + { + return super.getParameter(wildcard, player); + } + } +} |