diff options
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory')
7 files changed, 545 insertions, 0 deletions
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/DropCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/DropCheck.java new file mode 100644 index 000000000..2e9d030f7 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/DropCheck.java @@ -0,0 +1,71 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +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; +import java.util.Locale; + + +/** + * The DropCheck will find out if a player drops too many items within a short amount of time + * + */ +public class DropCheck extends InventoryCheck +{ + public DropCheck(NoCheat plugin) + { + super(plugin, "inventory.drop"); + } + + public boolean check(NoCheatPlayer player, InventoryData data, InventoryConfig cc) + { + + boolean cancel = false; + + final long time = System.currentTimeMillis(); + + // Has the configured time passed? If so, reset the counter + if (data.dropLastTime + cc.dropTimeFrame <= time) + { + data.dropLastTime = time; + data.dropCount = 0; + data.dropVL = 0; + } + // Security check, if the system time changes + else if (data.dropLastTime > time) + { + data.dropLastTime = Integer.MIN_VALUE; + } + + data.dropCount++; + + // The player dropped more than he should + if (data.dropCount > cc.dropLimit) + { + // Set vl and increment statistics + data.dropVL = data.dropCount - cc.dropLimit; + incrementStatistics(player, Id.INV_DROP, 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.dropActions, data.dropVL); + } + + return cancel; + } + + @Override + public String getParameter(ParameterName wildcard, NoCheatPlayer player) + { + + if (wildcard == ParameterName.VIOLATIONS) + { + return String.format(Locale.US, "%d", getData(player).dropVL); + } + else + { + return super.getParameter(wildcard, player); + } + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InstantBowCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InstantBowCheck.java new file mode 100644 index 000000000..7d4fcf3bb --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InstantBowCheck.java @@ -0,0 +1,72 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +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; +import java.util.Locale; +import org.bukkit.event.entity.EntityShootBowEvent; + + +/** + * The InstantBowCheck will find out if a player pulled the string of his bow too fast + */ +public class InstantBowCheck extends InventoryCheck +{ + public InstantBowCheck(NoCheat plugin) + { + super(plugin, "inventory.instantbow"); + } + + public boolean check(NoCheatPlayer player, EntityShootBowEvent event, InventoryData data, InventoryConfig cc) + { + + boolean cancelled = false; + + long time = System.currentTimeMillis(); + + // How fast will the arrow be? + float bowForce = event.getForce(); + + // Rough estimation of how long pulling the string should've taken + long expectedTimeWhenStringDrawn = data.lastBowInteractTime + (int)(bowForce * bowForce * 700F); + + if (expectedTimeWhenStringDrawn < time) + { + // The player was slow enough, reward him by lowering the vl + data.instantBowVL *= 0.90D; + } + else if (data.lastBowInteractTime > time) + { + // Security check if time ran backwards, reset + data.lastBowInteractTime = 0; + } + else + { + // Player was too fast, increase violation level and statistics + int vl = ((int)(expectedTimeWhenStringDrawn - time)) / 100; + data.instantBowVL += vl; + incrementStatistics(player, Id.INV_BOW, vl); + + // Execute whatever actions are associated with this check and the + // violation level and find out if we should cancel the event + cancelled = executeActions(player, cc.bowActions, data.instantBowVL); + } + + return cancelled; + } + + @Override + public String getParameter(ParameterName wildcard, NoCheatPlayer player) + { + + if (wildcard == ParameterName.VIOLATIONS) + { + return String.format(Locale.US, "%d", getData(player).instantBowVL); + } + else + { + return super.getParameter(wildcard, player); + } + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InstantEatCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InstantEatCheck.java new file mode 100644 index 000000000..05a668dd7 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InstantEatCheck.java @@ -0,0 +1,78 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +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; +import java.util.Locale; +import org.bukkit.event.entity.FoodLevelChangeEvent; + + +/** + * The InstantEatCheck will find out if a player eats his food too fast + */ +public class InstantEatCheck extends InventoryCheck +{ + public InstantEatCheck(NoCheat plugin) + { + super(plugin, "inventory.instanteat"); + } + + public boolean check(NoCheatPlayer player, FoodLevelChangeEvent event, InventoryData data, InventoryConfig cc) + { + + // Hunger level change seems to not be the result of eating + if (data.foodMaterial == null || event.getFoodLevel() <= player.getPlayer().getFoodLevel()) + { + return false; + } + + boolean cancelled = false; + + long time = System.currentTimeMillis(); + // rough estimation about how long it should take to eat + long expectedTimeWhenEatingFinished = data.lastEatInteractTime + 700; + + if (expectedTimeWhenEatingFinished < time) + { + // Acceptable, reduce VL to reward the player + data.instantEatVL *= 0.60D; + } + else if (data.lastEatInteractTime > time) + { + // Security test, if time ran backwards, reset + data.lastEatInteractTime = 0; + } + else + { + // Player was too fast, increase violation level and statistics + int vl = ((int)(expectedTimeWhenEatingFinished - time)) / 100; + data.instantEatVL += vl; + incrementStatistics(player, Id.INV_EAT, vl); + + // Execute whatever actions are associated with this check and the + // violation level and find out if we should cancel the event + cancelled = executeActions(player, cc.eatActions, data.instantEatVL); + } + + return cancelled; + } + + @Override + public String getParameter(ParameterName wildcard, NoCheatPlayer player) + { + + if (wildcard == ParameterName.VIOLATIONS) + { + return String.format(Locale.US, "%d", (int)getData(player).instantEatVL); + } + else if (wildcard == ParameterName.FOOD) + { + return getData(player).foodMaterial.toString(); + } + else + { + return super.getParameter(wildcard, player); + } + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryCheck.java new file mode 100644 index 000000000..ad60ffa3e --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryCheck.java @@ -0,0 +1,63 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +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 Inventory checks, provides some convenience methods for access to data and config that's + * relevant to this checktype + */ +public abstract class InventoryCheck extends Check +{ + private static final String id = "inventory"; + + public InventoryCheck(NoCheat plugin, String name) + { + super(plugin, id, name); + } + + /** + * Get the "InventoryData" object that belongs to the player. Will ensure that such a object exists and if not, + * create one + * + * @param player + * @return + */ + public static InventoryData getData(NoCheatPlayer player) + { + DataStore base = player.getDataStore(); + InventoryData data = base.get(id); + if (data == null) + { + data = new InventoryData(); + base.set(id, data); + } + return data; + } + + /** + * Get the InventoryConfig object that belongs to the world that the player currently resides in. + * + * @param player + * @return + */ + public static InventoryConfig getConfig(NoCheatPlayer player) + { + return getConfig(player.getConfigurationStore()); + } + + public static InventoryConfig getConfig(ConfigurationCacheStore cache) + { + InventoryConfig config = cache.get(id); + if (config == null) + { + config = new InventoryConfig(cache.getConfiguration()); + cache.set(id, config); + } + return config; + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryCheckListener.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryCheckListener.java new file mode 100644 index 000000000..f42a37185 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryCheckListener.java @@ -0,0 +1,196 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +import com.earth2me.essentials.anticheat.EventManager; +import com.earth2me.essentials.anticheat.NoCheat; +import com.earth2me.essentials.anticheat.NoCheatPlayer; +import com.earth2me.essentials.anticheat.checks.CheckUtil; +import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore; +import com.earth2me.essentials.anticheat.config.Permissions; +import java.util.LinkedList; +import java.util.List; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; + + +/** + * Central location to listen to events that are relevant for the inventory checks + * + */ +public class InventoryCheckListener implements Listener, EventManager +{ + private final DropCheck dropCheck; + private final InstantBowCheck instantBowCheck; + private final InstantEatCheck instantEatCheck; + private final NoCheat plugin; + + public InventoryCheckListener(NoCheat plugin) + { + + this.dropCheck = new DropCheck(plugin); + this.instantBowCheck = new InstantBowCheck(plugin); + this.instantEatCheck = new InstantEatCheck(plugin); + + this.plugin = plugin; + } + + /** + * We listen to DropItem Event for the dropCheck + * + * @param event The PlayerDropItem Event + */ + @EventHandler(priority = EventPriority.LOWEST) + protected void handlePlayerDropItemEvent(final PlayerDropItemEvent event) + { + + if (event.isCancelled() || event.getPlayer().isDead()) + { + return; + } + + boolean cancelled = false; + + final NoCheatPlayer player = plugin.getPlayer(event.getPlayer()); + final InventoryConfig cc = InventoryCheck.getConfig(player); + final InventoryData data = InventoryCheck.getData(player); + + // If it should be executed, do it + if (cc.dropCheck && !player.hasPermission(Permissions.INVENTORY_DROP)) + { + cancelled = dropCheck.check(player, data, cc); + } + + if (cancelled) + { + // Cancelling drop events is not save (in certain circumstances + // items will disappear completely). So don't do it and kick + // players instead by default + // event.setCancelled(true); + } + } + + /** + * We listen to PlayerInteractEvent for the instantEat and instantBow checks + * + * @param event The PlayerInteractEvent + */ + @EventHandler(priority = EventPriority.LOWEST) + public void interact(final PlayerInteractEvent event) + { + + // Only interested in right-clicks while holding an item + if (!event.hasItem() || !(event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK)) + { + return; + } + + NoCheatPlayer player = plugin.getPlayer(event.getPlayer()); + final InventoryData data = InventoryCheck.getData(player); + + if (event.getItem().getType() == Material.BOW) + { + // It was a bow, the player starts to pull the string + // Remember this time + data.lastBowInteractTime = System.currentTimeMillis(); + } + else if (CheckUtil.isFood(event.getItem())) + { + // It was food, the player starts to eat some food + // Remember this time and the type of food + data.foodMaterial = event.getItem().getType(); + data.lastEatInteractTime = System.currentTimeMillis(); + } + else + { + // Nothing that we are interested in, reset data + data.lastBowInteractTime = 0; + data.lastEatInteractTime = 0; + data.foodMaterial = null; + } + } + + /** + * We listen to FoodLevelChange Event because Bukkit doesn't provide a PlayerFoodEating Event (or whatever it would + * be called). + * + * @param event The FoodLevelChangeEvent + */ + @EventHandler(priority = EventPriority.LOWEST) + public void foodchanged(final FoodLevelChangeEvent event) + { + // Only if a player ate food + if (!event.isCancelled() && event.getEntity() instanceof Player) + { + final NoCheatPlayer player = plugin.getPlayer((Player)event.getEntity()); + final InventoryConfig cc = InventoryCheck.getConfig(player); + final InventoryData data = InventoryCheck.getData(player); + + // Only if he should get checked + if (cc.eatCheck && !player.hasPermission(Permissions.INVENTORY_INSTANTEAT)) + { + + boolean cancelled = instantEatCheck.check(player, event, data, cc); + + // The check requested the foodlevelchange to get cancelled + event.setCancelled(cancelled); + } + + // Forget the food material, as the info is no longer needed + data.foodMaterial = null; + } + + } + + /** + * We listen to EntityShootBowEvent for the instantbow check + * + * @param event The EntityShootBowEvent + */ + @EventHandler(priority = EventPriority.LOWEST) + public void bowfired(final EntityShootBowEvent event) + { + // Only if a player shot the arrow + if (!event.isCancelled() && event.getEntity() instanceof Player) + { + final NoCheatPlayer player = plugin.getPlayer((Player)event.getEntity()); + final InventoryConfig cc = InventoryCheck.getConfig(player); + + // Only if he should get checked + if (cc.bowCheck && !player.hasPermission(Permissions.INVENTORY_INSTANTBOW)) + { + final InventoryData data = InventoryCheck.getData(player); + boolean cancelled = instantBowCheck.check(player, event, data, cc); + + // The check requested the bowshooting to get cancelled + event.setCancelled(cancelled); + } + } + } + + public List<String> getActiveChecks(ConfigurationCacheStore cc) + { + LinkedList<String> s = new LinkedList<String>(); + + InventoryConfig i = InventoryCheck.getConfig(cc); + if (i.dropCheck) + { + s.add("inventory.dropCheck"); + } + if (i.bowCheck) + { + s.add("inventory.instantbow"); + } + if (i.eatCheck) + { + s.add("inventory.instanteat"); + } + return s; + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryConfig.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryConfig.java new file mode 100644 index 000000000..44f59ff04 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryConfig.java @@ -0,0 +1,40 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +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 "Inventory" 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 InventoryConfig implements ConfigItem +{ + public final boolean dropCheck; + public final long dropTimeFrame; + public final int dropLimit; + public final ActionList dropActions; + public final boolean bowCheck; + public final ActionList bowActions; + public final boolean eatCheck; + public final ActionList eatActions; + + public InventoryConfig(NoCheatConfiguration data) + { + + dropCheck = data.getBoolean(ConfPaths.INVENTORY_DROP_CHECK); + dropTimeFrame = data.getInt(ConfPaths.INVENTORY_DROP_TIMEFRAME) * 1000; + dropLimit = data.getInt(ConfPaths.INVENTORY_DROP_LIMIT); + dropActions = data.getActionList(ConfPaths.INVENTORY_DROP_ACTIONS, Permissions.INVENTORY_DROP); + + bowCheck = data.getBoolean(ConfPaths.INVENTORY_INSTANTBOW_CHECK); + bowActions = data.getActionList(ConfPaths.INVENTORY_INSTANTBOW_ACTIONS, Permissions.INVENTORY_INSTANTBOW); + + eatCheck = data.getBoolean(ConfPaths.INVENTORY_INSTANTEAT_CHECK); + eatActions = data.getActionList(ConfPaths.INVENTORY_INSTANTEAT_ACTIONS, Permissions.INVENTORY_INSTANTEAT); + } +} diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryData.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryData.java new file mode 100644 index 000000000..daeef8679 --- /dev/null +++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/inventory/InventoryData.java @@ -0,0 +1,25 @@ +package com.earth2me.essentials.anticheat.checks.inventory; + +import com.earth2me.essentials.anticheat.DataItem; +import org.bukkit.Material; + + +/** + * Player specific data for the inventory checks + * + */ +public class InventoryData implements DataItem +{ + // Keep track of the violation levels of the three checks + public int dropVL; + public int instantBowVL; + public double instantEatVL; + // Time and amount of dropped items + public long dropLastTime; + public int dropCount; + // Times when bow shootinhg and eating started + public long lastBowInteractTime; + public long lastEatInteractTime; + // What the player is eating + public Material foodMaterial; +} |