summaryrefslogtreecommitdiffstats
path: root/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak
diff options
context:
space:
mode:
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak')
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheck.java64
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheckListener.java186
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakConfig.java40
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakData.java29
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/DirectionCheck.java100
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/NoswingCheck.java61
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/ReachCheck.java75
7 files changed, 555 insertions, 0 deletions
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheck.java
new file mode 100644
index 000000000..6e349c85c
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheck.java
@@ -0,0 +1,64 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.checks.Check;
+import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore;
+import com.earth2me.essentials.anticheat.data.DataStore;
+
+
+/**
+ * Abstract base class for BlockBreakChecks. Provides some static convenience methods for retrieving data and config
+ * objects for players
+ *
+ */
+public abstract class BlockBreakCheck extends Check
+{
+ private static final String id = "blockbreak";
+
+ public BlockBreakCheck(NoCheat plugin, String name)
+ {
+ super(plugin, id, name);
+ }
+
+ /**
+ * Get the "BlockBreakData" object that belongs to the player. Will ensure that such a object exists and if not,
+ * create one
+ *
+ * @param player
+ * @return
+ */
+ public static BlockBreakData getData(NoCheatPlayer player)
+ {
+ DataStore base = player.getDataStore();
+ BlockBreakData data = base.get(id);
+ if (data == null)
+ {
+ data = new BlockBreakData();
+ base.set(id, data);
+ }
+ return data;
+ }
+
+ /**
+ * Get the BlockBreakConfig object that belongs to the world that the player currently resides in.
+ *
+ * @param player
+ * @return
+ */
+ public static BlockBreakConfig getConfig(NoCheatPlayer player)
+ {
+ return getConfig(player.getConfigurationStore());
+ }
+
+ public static BlockBreakConfig getConfig(ConfigurationCacheStore cache)
+ {
+ BlockBreakConfig config = cache.get(id);
+ if (config == null)
+ {
+ config = new BlockBreakConfig(cache.getConfiguration());
+ cache.set(id, config);
+ }
+ return config;
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheckListener.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheckListener.java
new file mode 100644
index 000000000..2e56adb68
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakCheckListener.java
@@ -0,0 +1,186 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+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.BlockBreakEvent;
+import org.bukkit.event.block.BlockDamageEvent;
+import org.bukkit.event.player.PlayerAnimationEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+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 events that are relevant for the blockbreak checks
+ *
+ */
+public class BlockBreakCheckListener implements Listener, EventManager
+{
+ private final NoswingCheck noswingCheck;
+ private final ReachCheck reachCheck;
+ private final DirectionCheck directionCheck;
+ private final NoCheat plugin;
+
+ public BlockBreakCheckListener(NoCheat plugin)
+ {
+
+ noswingCheck = new NoswingCheck(plugin);
+ reachCheck = new ReachCheck(plugin);
+ directionCheck = new DirectionCheck(plugin);
+
+ this.plugin = plugin;
+ }
+
+ /**
+ * We listen to blockBreak events for obvious reasons
+ *
+ * @param event The blockbreak event
+ */
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void blockBreak(final BlockBreakEvent event)
+ {
+
+ if (event.isCancelled())
+ {
+ return;
+ }
+
+ boolean cancelled = false;
+
+ final NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+ final BlockBreakConfig cc = BlockBreakCheck.getConfig(player);
+ final BlockBreakData data = BlockBreakCheck.getData(player);
+
+ // Remember the location of the block that will be broken
+ data.brokenBlockLocation.set(event.getBlock());
+
+ // Only if the block got damaged directly before, do the check(s)
+ if (!data.brokenBlockLocation.equals(data.lastDamagedBlock))
+ {
+ // Something caused a blockbreak event that's not from the player
+ // Don't check it at all
+ data.lastDamagedBlock.reset();
+ return;
+ }
+
+ // Now do the actual checks, if still needed. It's a good idea to make
+ // computationally cheap checks first, because it may save us from
+ // doing the computationally expensive checks.
+
+ // First NoSwing: Did the arm of the player move before breaking this
+ // block?
+ if (cc.noswingCheck && !player.hasPermission(Permissions.BLOCKBREAK_NOSWING))
+ {
+ cancelled = noswingCheck.check(player, data, cc);
+ }
+
+ // Second Reach: Is the block really in reach distance
+ if (!cancelled && cc.reachCheck && !player.hasPermission(Permissions.BLOCKBREAK_REACH))
+ {
+ cancelled = reachCheck.check(player, data, cc);
+ }
+
+ // Third Direction: Did the player look at the block at all
+ if (!cancelled && cc.directionCheck && !player.hasPermission(Permissions.BLOCKBREAK_DIRECTION))
+ {
+ cancelled = directionCheck.check(player, data, cc);
+ }
+
+ // At least one check failed and demanded to cancel the event
+ if (cancelled)
+ {
+ event.setCancelled(cancelled);
+ }
+ }
+
+ /**
+ * We listen to BlockDamage events to grab the information if it has been an "insta-break". That info may come in
+ * handy later.
+ *
+ * @param event The BlockDamage event
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void blockHit(final BlockDamageEvent event)
+ {
+
+ if (event.isCancelled())
+ {
+ return;
+ }
+
+ NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+ BlockBreakData data = BlockBreakCheck.getData(player);
+
+ // Only interested in insta-break events here
+ if (event.getInstaBreak())
+ {
+ // Remember this location. We handle insta-breaks slightly
+ // different in some of the blockbreak checks.
+ data.instaBrokenBlockLocation.set(event.getBlock());
+ }
+
+ }
+
+ /**
+ * We listen to BlockInteract events to be (at least in many cases) able to distinguish between blockbreak events
+ * that were triggered by players actually digging and events that were artificially created by plugins.
+ *
+ * @param event
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void blockInteract(final PlayerInteractEvent event)
+ {
+
+ if (event.getClickedBlock() == null)
+ {
+ return;
+ }
+
+ NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+ BlockBreakData data = BlockBreakCheck.getData(player);
+ // Remember this location. Only blockbreakevents for this specific
+ // block will be handled at all
+ data.lastDamagedBlock.set(event.getClickedBlock());
+ }
+
+ /**
+ * We listen to PlayerAnimationEvent because it is (currently) equivalent to "player swings arm" and we want to
+ * check if he did that between blockbreaks.
+ *
+ * @param event The PlayerAnimation Event
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void armSwing(final PlayerAnimationEvent event)
+ {
+ // Just set a flag to true when the arm was swung
+ BlockBreakCheck.getData(plugin.getPlayer(event.getPlayer())).armswung = true;
+ }
+
+ public List<String> getActiveChecks(ConfigurationCacheStore cc)
+ {
+ LinkedList<String> s = new LinkedList<String>();
+
+ BlockBreakConfig bb = BlockBreakCheck.getConfig(cc);
+
+ if (bb.directionCheck)
+ {
+ s.add("blockbreak.direction");
+ }
+ if (bb.reachCheck)
+ {
+ s.add("blockbreak.reach");
+ }
+ if (bb.noswingCheck)
+ {
+ s.add("blockbreak.noswing");
+ }
+
+ return s;
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakConfig.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakConfig.java
new file mode 100644
index 000000000..aed4a6a08
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakConfig.java
@@ -0,0 +1,40 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+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 "BlockBreak" 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 BlockBreakConfig implements ConfigItem
+{
+ public final boolean reachCheck;
+ public final double reachDistance;
+ public final ActionList reachActions;
+ public final boolean directionCheck;
+ public final ActionList directionActions;
+ public final double directionPrecision;
+ public final long directionPenaltyTime;
+ public final boolean noswingCheck;
+ public final ActionList noswingActions;
+
+ public BlockBreakConfig(NoCheatConfiguration data)
+ {
+
+ reachCheck = data.getBoolean(ConfPaths.BLOCKBREAK_REACH_CHECK);
+ reachDistance = 535D / 100D;
+ reachActions = data.getActionList(ConfPaths.BLOCKBREAK_REACH_ACTIONS, Permissions.BLOCKBREAK_REACH);
+ directionCheck = data.getBoolean(ConfPaths.BLOCKBREAK_DIRECTION_CHECK);
+ directionPrecision = ((double)data.getInt(ConfPaths.BLOCKBREAK_DIRECTION_PRECISION)) / 100D;
+ directionPenaltyTime = data.getInt(ConfPaths.BLOCKBREAK_DIRECTION_PENALTYTIME);
+ directionActions = data.getActionList(ConfPaths.BLOCKBREAK_DIRECTION_ACTIONS, Permissions.BLOCKBREAK_DIRECTION);
+ noswingCheck = data.getBoolean(ConfPaths.BLOCKBREAK_NOSWING_CHECK);
+ noswingActions = data.getActionList(ConfPaths.BLOCKBREAK_NOSWING_ACTIONS, Permissions.BLOCKBREAK_NOSWING);
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakData.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakData.java
new file mode 100644
index 000000000..dcf39adfc
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/BlockBreakData.java
@@ -0,0 +1,29 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+import com.earth2me.essentials.anticheat.DataItem;
+import com.earth2me.essentials.anticheat.data.SimpleLocation;
+
+
+/**
+ * Player specific data for the blockbreak checks
+ *
+ */
+public class BlockBreakData implements DataItem
+{
+ // Keep track of violation levels for the three checks
+ public double reachVL = 0.0D;
+ public double directionVL = 0.0D;
+ public double noswingVL = 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 instaBrokenBlockLocation = new SimpleLocation();
+ public final SimpleLocation brokenBlockLocation = new SimpleLocation();
+ public final SimpleLocation lastDamagedBlock = new SimpleLocation();
+ // indicate if the player swung his arm since he got checked last time
+ public boolean armswung = true;
+ // 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/blockbreak/DirectionCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/DirectionCheck.java
new file mode 100644
index 000000000..d0c7b10f8
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/DirectionCheck.java
@@ -0,0 +1,100 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+import java.util.Locale;
+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;
+
+
+/**
+ * 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 BlockBreakCheck
+{
+ public DirectionCheck(NoCheat plugin)
+ {
+ super(plugin, "blockbreak.direction");
+ }
+
+ public boolean check(final NoCheatPlayer player, final BlockBreakData data, final BlockBreakConfig ccblockbreak)
+ {
+
+ final SimpleLocation brokenBlock = data.brokenBlockLocation;
+ boolean cancel = false;
+
+ // 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, brokenBlock.x + 0.5D, brokenBlock.y + 0.5D, brokenBlock.z + 0.5D, 1D, 1D, ccblockbreak.directionPrecision);
+
+ final long time = System.currentTimeMillis();
+
+ if (off < 0.1D)
+ {
+ // Player did likely nothing wrong
+ // reduce violation counter to reward him
+ data.directionVL *= 0.9D;
+ }
+ else
+ {
+ // Player failed the check
+ // Increment violation counter
+ if (data.instaBrokenBlockLocation.equals(brokenBlock))
+ {
+ // Instabreak block failures are very common, so don't be as
+ // hard on people failing them
+ off /= 5;
+ }
+
+ // Add to the overall violation level of the check and add to
+ // statistics
+ data.directionVL += off;
+ incrementStatistics(player, Id.BB_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, ccblockbreak.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 + ccblockbreak.directionPenaltyTime > time)
+ {
+ // A saveguard 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/blockbreak/NoswingCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/NoswingCheck.java
new file mode 100644
index 000000000..af53c419f
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/NoswingCheck.java
@@ -0,0 +1,61 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+import java.util.Locale;
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+
+
+/**
+ * We require that the player moves his arm between blockbreaks, this is what gets checked here.
+ *
+ */
+public class NoswingCheck extends BlockBreakCheck
+{
+ public NoswingCheck(NoCheat plugin)
+ {
+ super(plugin, "blockbreak.noswing");
+ }
+
+ public boolean check(NoCheatPlayer player, BlockBreakData data, BlockBreakConfig cc)
+ {
+
+ boolean cancel = false;
+
+ // did he swing his arm before
+ if (data.armswung)
+ {
+ // "consume" the flag
+ data.armswung = false;
+ // reward with lowering of the violation level
+ data.noswingVL *= 0.90D;
+ }
+ else
+ {
+ // he failed, increase vl and statistics
+ data.noswingVL += 1;
+ incrementStatistics(player, Id.BB_NOSWING, 1);
+
+ // 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.noswingActions, data.noswingVL);
+ }
+
+ return cancel;
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", (int)getData(player).noswingVL);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/ReachCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/ReachCheck.java
new file mode 100644
index 000000000..b764eedcb
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/blockbreak/ReachCheck.java
@@ -0,0 +1,75 @@
+package com.earth2me.essentials.anticheat.checks.blockbreak;
+
+import java.util.Locale;
+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;
+
+
+/**
+ * The reach check will find out if a player interacts with something that's too far away
+ *
+ */
+public class ReachCheck extends BlockBreakCheck
+{
+ public ReachCheck(NoCheat plugin)
+ {
+ super(plugin, "blockbreak.reach");
+ }
+
+ public boolean check(NoCheatPlayer player, BlockBreakData data, BlockBreakConfig cc)
+ {
+
+ boolean cancel = false;
+
+ final SimpleLocation brokenBlock = data.brokenBlockLocation;
+
+ // 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, brokenBlock.x + 0.5D, brokenBlock.y + 0.5D, brokenBlock.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.BB_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);
+ }
+ }
+}