summaryrefslogtreecommitdiffstats
path: root/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight
diff options
context:
space:
mode:
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight')
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/DirectionCheck.java120
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheck.java69
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheckListener.java291
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightConfig.java58
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightData.java40
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/GodmodeCheck.java151
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/InstanthealCheck.java94
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/NoswingCheck.java67
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/ReachCheck.java113
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/SpeedCheck.java81
10 files changed, 1084 insertions, 0 deletions
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/DirectionCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/DirectionCheck.java
new file mode 100644
index 000000000..93ce58221
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/DirectionCheck.java
@@ -0,0 +1,120 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+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.config.Permissions;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+import java.util.Locale;
+import net.minecraft.server.Entity;
+import net.minecraft.server.EntityComplex;
+import net.minecraft.server.EntityComplexPart;
+
+
+/**
+ * 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 FightCheck
+{
+ public DirectionCheck(NoCheat plugin)
+ {
+ super(plugin, "fight.direction", Permissions.FIGHT_DIRECTION);
+ }
+
+ public boolean check(NoCheatPlayer player, FightData data, FightConfig cc)
+ {
+
+ boolean cancel = false;
+
+ final long time = System.currentTimeMillis();
+
+ // Get the damagee (entity that got hit)
+ Entity entity = data.damagee;
+
+ // Safeguard, if entity is complex, this check will fail
+ // due to giant and hard to define hitboxes
+ if (entity instanceof EntityComplex || entity instanceof EntityComplexPart)
+ {
+ return false;
+ }
+
+ // Find out how wide the entity is
+ final float width = entity.length > entity.width ? entity.length : entity.width;
+ // entity.height is broken and will always be 0, therefore
+ // calculate height instead based on boundingBox
+ final double height = entity.boundingBox.e - entity.boundingBox.b;
+
+ // 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
+ // entity. If the line of sight is more too far off, "off" will be
+ // bigger than 0
+ final double off = CheckUtil.directionCheck(player, entity.locX, entity.locY + (height / 2D), entity.locZ, width, height, cc.directionPrecision);
+
+ if (off < 0.1D)
+ {
+ // Player did probably nothing wrong
+ // reduce violation counter to reward him
+ data.directionVL *= 0.80D;
+ }
+ else
+ {
+ // Player failed the check
+ // Increment violation counter and statistics, but only if there
+ // wasn't serious lag
+ if (!plugin.skipCheck())
+ {
+ double sqrt = Math.sqrt(off);
+ data.directionVL += sqrt;
+ incrementStatistics(player, Id.FI_DIRECTION, sqrt);
+ }
+
+ // 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 boolean isEnabled(FightConfig cc)
+ {
+ return cc.directionCheck;
+ }
+
+ @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/fight/FightCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheck.java
new file mode 100644
index 000000000..f8dd4e3db
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheck.java
@@ -0,0 +1,69 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+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 Fight checks, provides some convenience methods for access to data and config that's relevant
+ * to this checktype
+ */
+public abstract class FightCheck extends Check
+{
+ private static final String id = "fight";
+ public final String permission;
+
+ public FightCheck(NoCheat plugin, String name, String permission)
+ {
+ super(plugin, id, name);
+ this.permission = permission;
+ }
+
+ public abstract boolean check(NoCheatPlayer player, FightData data, FightConfig cc);
+
+ public abstract boolean isEnabled(FightConfig cc);
+
+ /**
+ * Get the "FightData" object that belongs to the player. Will ensure that such a object exists and if not, create
+ * one
+ *
+ * @param player
+ * @return
+ */
+ public static FightData getData(NoCheatPlayer player)
+ {
+ DataStore base = player.getDataStore();
+ FightData data = base.get(id);
+ if (data == null)
+ {
+ data = new FightData();
+ base.set(id, data);
+ }
+ return data;
+ }
+
+ /**
+ * Get the FightConfig object that belongs to the world that the player currently resides in.
+ *
+ * @param player
+ * @return
+ */
+ public static FightConfig getConfig(NoCheatPlayer player)
+ {
+ return getConfig(player.getConfigurationStore());
+ }
+
+ public static FightConfig getConfig(ConfigurationCacheStore cache)
+ {
+ FightConfig config = cache.get(id);
+ if (config == null)
+ {
+ config = new FightConfig(cache.getConfiguration());
+ cache.set(id, config);
+ }
+ return config;
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheckListener.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheckListener.java
new file mode 100644
index 000000000..fc1ea160c
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightCheckListener.java
@@ -0,0 +1,291 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import org.bukkit.craftbukkit.entity.CraftEntity;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
+import org.bukkit.event.entity.EntityDeathEvent;
+import org.bukkit.event.entity.EntityRegainHealthEvent;
+import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
+import org.bukkit.event.player.PlayerAnimationEvent;
+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;
+
+
+/**
+ * Central location to listen to events that are relevant for the fight checks
+ *
+ */
+public class FightCheckListener implements Listener, EventManager
+{
+ private final List<FightCheck> checks;
+ private final GodmodeCheck godmodeCheck;
+ private final InstanthealCheck instanthealCheck;
+ private final NoCheat plugin;
+
+ public FightCheckListener(NoCheat plugin)
+ {
+
+ this.checks = new ArrayList<FightCheck>(4);
+
+ // Keep these in a list, because they can be executed in a bundle
+ this.checks.add(new SpeedCheck(plugin));
+ this.checks.add(new NoswingCheck(plugin));
+ this.checks.add(new DirectionCheck(plugin));
+ this.checks.add(new ReachCheck(plugin));
+
+ this.godmodeCheck = new GodmodeCheck(plugin);
+ this.instanthealCheck = new InstanthealCheck(plugin);
+
+ this.plugin = plugin;
+ }
+
+ /**
+ * We listen to EntityDamage events for obvious reasons
+ *
+ * @param event The EntityDamage Event
+ */
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void entityDamage(final EntityDamageEvent event)
+ {
+
+ // Filter some unwanted events right now
+ if (event.isCancelled() || !(event instanceof EntityDamageByEntityEvent))
+ {
+ return;
+ }
+
+ final EntityDamageByEntityEvent e = (EntityDamageByEntityEvent)event;
+ if (!(e.getDamager() instanceof Player))
+ {
+ return;
+ }
+
+ if (e.getCause() == DamageCause.ENTITY_ATTACK)
+ {
+ normalDamage(e);
+ }
+ else if (e.getCause() == DamageCause.CUSTOM)
+ {
+ customDamage(e);
+ }
+ }
+
+ /**
+ * We listen to EntityDamage events (again) for obvious reasons
+ *
+ * @param event The EntityDamage Event
+ */
+ @EventHandler(priority = EventPriority.LOW)
+ public void entityDamageForGodmodeCheck(final EntityDamageEvent event)
+ {
+
+ if (event.isCancelled())
+ {
+ return;
+ }
+
+ // Filter unwanted events right here
+ final Entity entity = event.getEntity();
+ if (!(entity instanceof Player) || entity.isDead())
+ {
+ return;
+ }
+
+ NoCheatPlayer player = plugin.getPlayer((Player)entity);
+ FightConfig cc = FightCheck.getConfig(player);
+
+ if (!godmodeCheck.isEnabled(cc) || player.hasPermission(godmodeCheck.permission))
+ {
+ return;
+ }
+
+ FightData data = FightCheck.getData(player);
+
+ // Run the godmode check on the attacked player
+ boolean cancelled = godmodeCheck.check(plugin.getPlayer((Player)entity), data, cc);
+
+ // It requested to "cancel" the players invulnerability, so set his
+ // noDamageTicks to 0
+ if (cancelled)
+ {
+ // Remove the invulnerability from the player
+ player.getPlayer().setNoDamageTicks(0);
+ }
+ }
+
+ /**
+ * We listen to EntityRegainHealth events of type "Satiated" for instantheal check
+ *
+ * @param event The EntityRegainHealth Event
+ */
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void satiatedRegen(final EntityRegainHealthEvent event)
+ {
+
+ if (!(event.getEntity() instanceof Player) || event.isCancelled() || event.getRegainReason() != RegainReason.SATIATED)
+ {
+ return;
+ }
+
+ boolean cancelled = false;
+
+ NoCheatPlayer player = plugin.getPlayer((Player)event.getEntity());
+ FightConfig config = FightCheck.getConfig(player);
+
+ if (!instanthealCheck.isEnabled(config) || player.hasPermission(instanthealCheck.permission))
+ {
+ return;
+ }
+
+ FightData data = FightCheck.getData(player);
+
+ cancelled = instanthealCheck.check(player, data, config);
+
+ if (cancelled)
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ /**
+ * A player attacked something with DamageCause ENTITY_ATTACK. That's most likely what we want to really check.
+ *
+ * @param event The EntityDamageByEntityEvent
+ */
+ private void normalDamage(final EntityDamageByEntityEvent event)
+ {
+
+ final Player damager = (Player)event.getDamager();
+
+ final NoCheatPlayer player = plugin.getPlayer(damager);
+ final FightConfig cc = FightCheck.getConfig(player);
+ final FightData data = FightCheck.getData(player);
+
+ // For some reason we decided to skip this event anyway
+ if (data.skipNext)
+ {
+ data.skipNext = false;
+ return;
+ }
+
+ boolean cancelled = false;
+
+ // Get the attacked entity and remember it
+ data.damagee = ((CraftEntity)event.getEntity()).getHandle();
+
+ // Run through the four main checks
+ for (FightCheck check : checks)
+ {
+ // If it should be executed, do it
+ if (!cancelled && check.isEnabled(cc) && !player.hasPermission(check.permission))
+ {
+ cancelled = check.check(player, data, cc);
+ }
+ }
+
+ // Forget the attacked entity (to allow garbage collecting etc.
+ data.damagee = null;
+
+ // One of the checks requested the event to be cancelled, so do it
+ if (cancelled)
+ {
+ event.setCancelled(cancelled);
+ }
+ }
+
+ /**
+ * There is an unofficial agreement that if a plugin wants an attack to not get checked by NoCheat, it either has to
+ * use a Damage type different from ENTITY_ATTACK or fire an event with damage type CUSTOM and damage 0 directly
+ * before the to-be-ignored event.
+ *
+ * @param event The EntityDamageByEntityEvent
+ */
+ private void customDamage(final EntityDamageByEntityEvent event)
+ {
+
+ final Player damager = (Player)event.getDamager();
+ final NoCheatPlayer player = plugin.getPlayer(damager);
+
+ final FightData data = FightCheck.getData(player);
+
+ // Skip the next damage event, because it is with high probability
+ // something from the Heroes plugin
+ data.skipNext = true;
+
+ return;
+ }
+
+ /**
+ * We listen to death events to prevent a very specific method of doing godmode.
+ *
+ * @param event The EntityDeathEvent
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ protected void death(final EntityDeathEvent event)
+ {
+ // Only interested in dying players
+ if (!(event.getEntity() instanceof CraftPlayer))
+ {
+ return;
+ }
+
+ godmodeCheck.death((CraftPlayer)event.getEntity());
+ }
+
+ /**
+ * We listen to PlayerAnimationEvent because it is used for arm swinging
+ *
+ * @param event The PlayerAnimationEvent
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ protected void armSwing(final PlayerAnimationEvent event)
+ {
+ // Set a flag telling us that the arm has been swung
+ FightCheck.getData(plugin.getPlayer(event.getPlayer())).armswung = true;
+ }
+
+ public List<String> getActiveChecks(ConfigurationCacheStore cc)
+ {
+ LinkedList<String> s = new LinkedList<String>();
+
+ FightConfig f = FightCheck.getConfig(cc);
+
+ if (f.directionCheck)
+ {
+ s.add("fight.direction");
+ }
+ if (f.noswingCheck)
+ {
+ s.add("fight.noswing");
+ }
+ if (f.reachCheck)
+ {
+ s.add("fight.reach");
+ }
+ if (f.speedCheck)
+ {
+ s.add("fight.speed");
+ }
+ if (f.godmodeCheck)
+ {
+ s.add("fight.godmode");
+ }
+ if (f.instanthealCheck)
+ {
+ s.add("fight.instantHeal");
+ }
+ return s;
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightConfig.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightConfig.java
new file mode 100644
index 000000000..9a36128ae
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightConfig.java
@@ -0,0 +1,58 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+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 "Fight" 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 FightConfig implements ConfigItem
+{
+ public final boolean directionCheck;
+ public final double directionPrecision;
+ public final ActionList directionActions;
+ public final long directionPenaltyTime;
+ public final boolean noswingCheck;
+ public final ActionList noswingActions;
+ public final boolean reachCheck;
+ public final double reachLimit;
+ public final long reachPenaltyTime;
+ public final ActionList reachActions;
+ public final int speedAttackLimit;
+ public final ActionList speedActions;
+ public final boolean speedCheck;
+ public final boolean godmodeCheck;
+ public final ActionList godmodeActions;
+ public final boolean instanthealCheck;
+ public final ActionList instanthealActions;
+
+ public FightConfig(NoCheatConfiguration data)
+ {
+
+ directionCheck = data.getBoolean(ConfPaths.FIGHT_DIRECTION_CHECK);
+ directionPrecision = ((double)(data.getInt(ConfPaths.FIGHT_DIRECTION_PRECISION))) / 100D;
+ directionPenaltyTime = data.getInt(ConfPaths.FIGHT_DIRECTION_PENALTYTIME);
+ directionActions = data.getActionList(ConfPaths.FIGHT_DIRECTION_ACTIONS, Permissions.FIGHT_DIRECTION);
+ noswingCheck = data.getBoolean(ConfPaths.FIGHT_NOSWING_CHECK);
+ noswingActions = data.getActionList(ConfPaths.FIGHT_NOSWING_ACTIONS, Permissions.FIGHT_NOSWING);
+ reachCheck = data.getBoolean(ConfPaths.FIGHT_REACH_CHECK);
+ reachLimit = ((double)(data.getInt(ConfPaths.FIGHT_REACH_LIMIT))) / 100D;
+ reachPenaltyTime = data.getInt(ConfPaths.FIGHT_REACH_PENALTYTIME);
+ reachActions = data.getActionList(ConfPaths.FIGHT_REACH_ACTIONS, Permissions.FIGHT_REACH);
+ speedCheck = data.getBoolean(ConfPaths.FIGHT_SPEED_CHECK);
+ speedActions = data.getActionList(ConfPaths.FIGHT_SPEED_ACTIONS, Permissions.FIGHT_SPEED);
+ speedAttackLimit = data.getInt(ConfPaths.FIGHT_SPEED_ATTACKLIMIT);
+
+ godmodeCheck = data.getBoolean(ConfPaths.FIGHT_GODMODE_CHECK);
+ godmodeActions = data.getActionList(ConfPaths.FIGHT_GODMODE_ACTIONS, Permissions.FIGHT_GODMODE);
+
+ instanthealCheck = data.getBoolean(ConfPaths.FIGHT_INSTANTHEAL_CHECK);
+ instanthealActions = data.getActionList(ConfPaths.FIGHT_INSTANTHEAL_ACTIONS, Permissions.FIGHT_INSTANTHEAL);
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightData.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightData.java
new file mode 100644
index 000000000..9f3a5a5d4
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/FightData.java
@@ -0,0 +1,40 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+import com.earth2me.essentials.anticheat.DataItem;
+import net.minecraft.server.Entity;
+
+
+/**
+ * Player specific data for the fight checks
+ *
+ */
+public class FightData implements DataItem
+{
+ // Keep track of the violation levels of the checks
+ public double directionVL;
+ public double noswingVL;
+ public double reachVL;
+ public int speedVL;
+ public double godmodeVL;
+ public double instanthealVL;
+ // For checks that have penalty time
+ public long directionLastViolationTime;
+ public long reachLastViolationTime;
+ // godmode check needs to know these
+ public long godmodeLastDamageTime;
+ public int godmodeLastAge;
+ public int godmodeBuffer = 40;
+ // last time player regenerated health by satiation
+ public long instanthealLastRegenTime;
+ // three seconds buffer to smooth out lag
+ public long instanthealBuffer = 3000;
+ // While handling an event, use this to keep the attacked entity
+ public Entity damagee;
+ // The player swung his arm
+ public boolean armswung = true;
+ // For some reason the next event should be ignored
+ public boolean skipNext = false;
+ // Keep track of time and amount of attacks
+ public long speedTime;
+ public int speedAttackCount;
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/GodmodeCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/GodmodeCheck.java
new file mode 100644
index 000000000..cd0fd6aaa
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/GodmodeCheck.java
@@ -0,0 +1,151 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.config.Permissions;
+import com.earth2me.essentials.anticheat.data.Statistics;
+import java.util.Locale;
+import net.minecraft.server.EntityPlayer;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+
+
+/**
+ * The Godmode Check will find out if a player tried to stay invulnerable after being hit or after dying
+ *
+ */
+public class GodmodeCheck extends FightCheck
+{
+ public GodmodeCheck(NoCheat plugin)
+ {
+ super(plugin, "fight.godmode", Permissions.FIGHT_GODMODE);
+ }
+
+ @Override
+ public boolean check(NoCheatPlayer player, FightData data, FightConfig cc)
+ {
+
+ boolean cancelled = false;
+
+ long time = System.currentTimeMillis();
+
+ // Check at most once a second
+ if (data.godmodeLastDamageTime + 1000L < time)
+ {
+ data.godmodeLastDamageTime = time;
+
+ // How old is the player now?
+ int age = player.getTicksLived();
+ // How much older did he get?
+ int ageDiff = Math.max(0, age - data.godmodeLastAge);
+ // Is he invulnerable?
+ int nodamageTicks = player.getPlayer().getNoDamageTicks();
+
+ if (nodamageTicks > 0 && ageDiff < 15)
+ {
+ // He is invulnerable and didn't age fast enough, that costs
+ // some points
+ data.godmodeBuffer -= (15 - ageDiff);
+
+ // Still points left?
+ if (data.godmodeBuffer <= 0)
+ {
+ // No, that means VL and statistics increased
+ data.godmodeVL -= data.godmodeBuffer;
+ incrementStatistics(player, Statistics.Id.FI_GODMODE, -data.godmodeBuffer);
+
+ // 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.godmodeActions, data.godmodeVL);
+ }
+ }
+ else
+ {
+ // Give some new points, once a second
+ data.godmodeBuffer += 15;
+ data.godmodeVL *= 0.95;
+ }
+
+ if (data.godmodeBuffer < 0)
+ {
+ // Can't have less than 0
+ data.godmodeBuffer = 0;
+ }
+ else if (data.godmodeBuffer > 30)
+ {
+ // And 30 is enough for simple lag situations
+ data.godmodeBuffer = 30;
+ }
+
+ // Start age counting from a new time
+ data.godmodeLastAge = age;
+ }
+
+ return cancelled;
+ }
+
+ @Override
+ public boolean isEnabled(FightConfig cc)
+ {
+ return cc.godmodeCheck;
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", (int)getData(player).godmodeVL);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+
+ /**
+ * If a player apparently died, make sure he really dies after some time if he didn't already, by setting up a
+ * Bukkit task
+ *
+ * @param player The player
+ */
+ public void death(CraftPlayer player)
+ {
+ // First check if the player is really dead (e.g. another plugin could
+ // have just fired an artificial event)
+ if (player.getHealth() <= 0 && player.isDead())
+ {
+ try
+ {
+ final EntityPlayer entity = player.getHandle();
+
+ // Schedule a task to be executed in roughly 1.5 seconds
+ Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ // Check again if the player should be dead, and
+ // if the game didn't mark him as dead
+ if (entity.getHealth() <= 0 && !entity.dead)
+ {
+ // Artifically "kill" him
+ entity.deathTicks = 19;
+ entity.a(true);
+ }
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }, 30);
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/InstanthealCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/InstanthealCheck.java
new file mode 100644
index 000000000..33fcbfd3a
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/InstanthealCheck.java
@@ -0,0 +1,94 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.config.Permissions;
+import com.earth2me.essentials.anticheat.data.Statistics;
+import java.util.Locale;
+
+
+/**
+ * The instantheal Check should find out if a player tried to artificially accellerate the health regeneration by food
+ *
+ */
+public class InstanthealCheck extends FightCheck
+{
+ public InstanthealCheck(NoCheat plugin)
+ {
+ super(plugin, "fight.instantheal", Permissions.FIGHT_INSTANTHEAL);
+ }
+
+ @Override
+ public boolean check(NoCheatPlayer player, FightData data, FightConfig cc)
+ {
+
+ boolean cancelled = false;
+
+ long time = System.currentTimeMillis();
+
+ // security check if system time ran backwards
+ if (data.instanthealLastRegenTime > time)
+ {
+ data.instanthealLastRegenTime = 0;
+ return false;
+ }
+
+ long difference = time - (data.instanthealLastRegenTime + 3500L);
+
+ data.instanthealBuffer += difference;
+
+ if (data.instanthealBuffer < 0)
+ {
+ // Buffer has been fully consumed
+ // Increase vl and statistics
+ double vl = data.instanthealVL -= data.instanthealBuffer / 1000;
+ incrementStatistics(player, Statistics.Id.FI_INSTANTHEAL, vl);
+
+ data.instanthealBuffer = 0;
+
+ // 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.instanthealActions, data.instanthealVL);
+ }
+ else
+ {
+ // vl gets decreased
+ data.instanthealVL *= 0.9;
+ }
+
+ // max 2 seconds buffer
+ if (data.instanthealBuffer > 2000L)
+ {
+ data.instanthealBuffer = 2000L;
+ }
+
+ if (!cancelled)
+ {
+ // New reference time
+ data.instanthealLastRegenTime = time;
+ }
+
+ return cancelled;
+ }
+
+ @Override
+ public boolean isEnabled(FightConfig cc)
+ {
+ return cc.instanthealCheck;
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", (int)getData(player).instanthealVL);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/NoswingCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/NoswingCheck.java
new file mode 100644
index 000000000..99d7ac1fd
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/NoswingCheck.java
@@ -0,0 +1,67 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.config.Permissions;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+import java.util.Locale;
+
+
+/**
+ * We require that the player moves his arm between attacks, this is what gets checked here.
+ *
+ */
+public class NoswingCheck extends FightCheck
+{
+ public NoswingCheck(NoCheat plugin)
+ {
+ super(plugin, "fight.noswing", Permissions.FIGHT_NOSWING);
+ }
+
+ public boolean check(NoCheatPlayer player, FightData data, FightConfig cc)
+ {
+
+ boolean cancel = false;
+
+ // did he swing his arm before?
+ if (data.armswung)
+ {
+ // Yes, reward him with reduction of his vl
+ data.armswung = false;
+ data.noswingVL *= 0.90D;
+ }
+ else
+ {
+ // No, increase vl and statistics
+ data.noswingVL += 1;
+ incrementStatistics(player, Id.FI_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 boolean isEnabled(FightConfig cc)
+ {
+ return cc.noswingCheck;
+ }
+
+ @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/fight/ReachCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/ReachCheck.java
new file mode 100644
index 000000000..c56caed08
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/ReachCheck.java
@@ -0,0 +1,113 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+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.config.Permissions;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+import java.util.Locale;
+import net.minecraft.server.Entity;
+import net.minecraft.server.EntityComplex;
+import net.minecraft.server.EntityComplexPart;
+
+
+/**
+ * The reach check will find out if a player interacts with something that's too far away
+ *
+ */
+public class ReachCheck extends FightCheck
+{
+ public ReachCheck(NoCheat plugin)
+ {
+ super(plugin, "fight.reach", Permissions.FIGHT_REACH);
+ }
+
+ public boolean check(NoCheatPlayer player, FightData data, FightConfig cc)
+ {
+
+ boolean cancel = false;
+
+ final long time = System.currentTimeMillis();
+
+ // Get the width of the damagee
+ Entity entity = data.damagee;
+
+ // Safeguard, if entity is Giant or Ender Dragon, this check will fail
+ // due to giant and hard to define hitboxes
+ if (entity instanceof EntityComplex || entity instanceof EntityComplexPart)
+ {
+ return false;
+ }
+
+ // Distance is calculated from eye location to center of targeted
+ // If the player is further away from his target than allowed, the
+ // difference will be assigned to "distance"
+ final double off = CheckUtil.reachCheck(player, entity.locX, entity.locY + 1.0D, entity.locZ, cc.reachLimit);
+
+ if (off < 0.1D)
+ {
+ // Player did probably nothing wrong
+ // reduce violation counter to reward him
+ data.reachVL *= 0.80D;
+ }
+ else
+ {
+ // Player failed the check
+ // Increment violation counter and statistics
+ // This is influenced by lag, so don't do it if there was lag
+ if (!plugin.skipCheck())
+ {
+ double sqrt = Math.sqrt(off);
+ data.reachVL += sqrt;
+ incrementStatistics(player, Id.FI_REACH, sqrt);
+ }
+
+ // 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);
+
+ if (cancel)
+ {
+ // if we should cancel, remember the current time too
+ data.reachLastViolationTime = time;
+ }
+ }
+
+ // If the player is still in penalty time, cancel the event anyway
+ if (data.reachLastViolationTime + cc.reachPenaltyTime > time)
+ {
+ // A safeguard to avoid people getting stuck in penalty time
+ // indefinitely in case the system time of the server gets changed
+ if (data.reachLastViolationTime > time)
+ {
+ data.reachLastViolationTime = 0;
+ }
+
+ // He is in penalty time, therefore request cancelling of the event
+ return true;
+ }
+
+ return cancel;
+ }
+
+ @Override
+ public boolean isEnabled(FightConfig cc)
+ {
+ return cc.reachCheck;
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", (int)getData(player).reachVL);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/SpeedCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/SpeedCheck.java
new file mode 100644
index 000000000..baa7db9c5
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight/SpeedCheck.java
@@ -0,0 +1,81 @@
+package com.earth2me.essentials.anticheat.checks.fight;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.config.Permissions;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+import java.util.Locale;
+
+
+/**
+ * The speed check will find out if a player interacts with something that's too far away
+ *
+ */
+public class SpeedCheck extends FightCheck
+{
+ public SpeedCheck(NoCheat plugin)
+ {
+ super(plugin, "fight.speed", Permissions.FIGHT_SPEED);
+ }
+
+ public boolean check(NoCheatPlayer player, FightData data, FightConfig cc)
+ {
+
+ boolean cancel = false;
+
+ final long time = System.currentTimeMillis();
+
+ // Check if one second has passed and reset counters and vl in that case
+ if (data.speedTime + 1000L <= time)
+ {
+ data.speedTime = time;
+ data.speedAttackCount = 0;
+ data.speedVL = 0;
+ }
+
+ // count the attack
+ data.speedAttackCount++;
+
+ // too many attacks
+ if (data.speedAttackCount > cc.speedAttackLimit)
+ {
+ // if there was lag, don't count it towards statistics and vl
+ if (!plugin.skipCheck())
+ {
+ data.speedVL += 1;
+ incrementStatistics(player, Id.FI_SPEED, 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.speedActions, data.speedVL);
+ }
+
+ return cancel;
+ }
+
+ @Override
+ public boolean isEnabled(FightConfig cc)
+ {
+ return cc.speedCheck;
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", getData(player).speedVL);
+ }
+ else if (wildcard == ParameterName.LIMIT)
+ {
+ return String.format(Locale.US, "%d", getConfig(player.getConfigurationStore()).speedAttackLimit);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+}