diff options
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/fight')
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); + } + } +} |