[Development] Fight checks rewritten, now they need testing and

improvements.
This commit is contained in:
NeatMonster 2012-08-05 18:12:51 +02:00
parent 4bb82a4a20
commit e706558cc1
8 changed files with 312 additions and 0 deletions

View File

@ -21,6 +21,7 @@ package fr.neatmonster.nocheatplus.actions;
* Some wildcards that are used in commands and log messages.
*/
public enum ParameterName {
ATTACKS_LIMIT("attackslimit"),
CHECK("check"),
DISTANCE("distance"),
FALLDISTANCE("falldistance"),

View File

@ -81,6 +81,10 @@ public class FightConfig {
public final long reachPenalty;
public final ActionList reachActions;
public final boolean speedCheck;
public final int speedLimit;
public final ActionList speedActions;
/**
* Instantiates a new fight configuration.
*
@ -117,5 +121,9 @@ public class FightConfig {
reachCheck = data.getBoolean(ConfPaths.FIGHT_REACH_CHECK);
reachPenalty = data.getLong(ConfPaths.FIGHT_REACH_PENALTY);
reachActions = data.getActionList(ConfPaths.FIGHT_REACH_ACTIONS, Permissions.FIGHT_REACH);
speedCheck = data.getBoolean(ConfPaths.FIGHT_SPEED_CHECK);
speedLimit = data.getInt(ConfPaths.FIGHT_SPEED_LIMIT);
speedActions = data.getActionList(ConfPaths.FIGHT_SPEED_ACTIONS, Permissions.FIGHT_SPEED);
}
}

View File

@ -47,6 +47,9 @@ public class FightData {
public double knockbackVL;
public double noSwingVL;
public double reachVL;
public double speedVL;
public boolean skipNext;
// Data of the angle check.
public TreeMap<Long, Location> angleHits = new TreeMap<Long, Location>();
@ -72,4 +75,8 @@ public class FightData {
// Data of the reach check.
public long reachLastViolationTime;
// Data of the speed check.
public int speedAttacks;
public long speedTime;
}

View File

@ -1,6 +1,20 @@
package fr.neatmonster.nocheatplus.checks.fight;
import org.bukkit.craftbukkit.entity.CraftEntity;
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 org.bukkit.event.player.PlayerToggleSprintEvent;
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
/*
* MM""""""""`M oo dP dP M""MMMMMMMM oo dP
@ -16,5 +30,172 @@ import org.bukkit.event.Listener;
* Central location to listen to events that are relevant for the fight checks.
*/
public class FightListener implements Listener {
private final Angle angle = new Angle();
private final Critical critical = new Critical();
private final Direction direction = new Direction();
private final GodMode godMode = new GodMode();
private final InstantHeal instantHeal = new InstantHeal();
private final Knockback knockback = new Knockback();
private final NoSwing noSwing = new NoSwing();
private final Reach reach = new Reach();
private final Speed speed = new Speed();
/**
* There is an unofficial agreement that if a plugin wants an attack to not get checked by NoCheatPlus, 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 event
*/
private void handleCustomDamage(final EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Player)
// Skip the next damage event, because it is with high probability something from the Heroes plugin.
FightData.getData((Player) event.getDamager()).skipNext = 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 handleNormalDamage(final EntityDamageByEntityEvent event) {
final Player player = (Player) event.getDamager();
FightConfig.getConfig(player);
final FightData data = FightData.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.
final net.minecraft.server.Entity damaged = ((CraftEntity) event.getEntity()).getHandle();
// Run through the main checks.
if (angle.isEnabled(player) && angle.check(player))
cancelled = true;
if (!cancelled && critical.isEnabled(player) && critical.check(player))
cancelled = true;
if (!cancelled && direction.isEnabled(player) && direction.check(player, damaged))
cancelled = true;
if (!cancelled && knockback.isEnabled(player) && knockback.check(player))
cancelled = true;
if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player))
cancelled = true;
if (!cancelled && reach.isEnabled(player) && reach.check(player, damaged))
cancelled = true;
if (!cancelled && speed.isEnabled(player) && speed.check(player))
cancelled = true;
if (!cancelled && !MovingConfig.getConfig(player).survivalFlyAllowFastBlocking && player.isBlocking())
cancelled = true;
// One of the checks requested the event to be cancelled, so do it.
if (cancelled)
event.setCancelled(cancelled);
}
/**
* We listen to EntityDamage events for obvious reasons.
*
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamage(final EntityDamageEvent event) {
// Filter some unwanted events right now.
if (event instanceof EntityDamageByEntityEvent) {
final EntityDamageByEntityEvent e = (EntityDamageByEntityEvent) event;
if (e.getDamager() instanceof Player)
if (e.getCause() == DamageCause.ENTITY_ATTACK)
handleNormalDamage(e);
else if (e.getCause() == DamageCause.CUSTOM)
handleCustomDamage(e);
}
}
/**
* We listen to EntityDamage events (again) for obvious reasons.
*
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = true, priority = EventPriority.LOW)
public void onEntityDamage_(final EntityDamageEvent event) {
// Filter unwanted events right here.
if (event.getEntity() instanceof Player && !event.getEntity().isDead()) {
final Player player = (Player) event.getEntity();
if (godMode.isEnabled(player) && godMode.check(player))
// It requested to "cancel" the players invulnerability, so set his noDamageTicks to 0.
player.setNoDamageTicks(0);
}
}
/**
* We listen to death events to prevent a very specific method of doing godmode.
*
* @param event
* the event
*/
@EventHandler(
priority = EventPriority.MONITOR)
protected void onEntityDeathEvent(final EntityDeathEvent event) {
// Only interested in dying players.
if (event.getEntity() instanceof Player)
godMode.death((Player) event.getEntity());
}
/**
* We listen to EntityRegainHealth events of type "satiated" for InstantHeal check.
*
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityRegainHealth(final EntityRegainHealthEvent event) {
if (event.getEntity() instanceof Player && event.getRegainReason() == RegainReason.SATIATED) {
final Player player = (Player) event.getEntity();
if (instantHeal.isEnabled(player) && instantHeal.check(player))
event.setCancelled(true);
}
}
/**
* We listen to PlayerAnimation events because it is used for arm swinging.
*
* @param event
* the event
*/
@EventHandler(
priority = EventPriority.MONITOR)
protected void onPlayerAnimation(final PlayerAnimationEvent event) {
// Set a flag telling us that the arm has been swung.
FightData.getData(event.getPlayer()).noSwingArmSwung = true;
}
/**
* We listen to the PlayerToggleSprint events for the Knockback check.
*
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerToggleSprint(final PlayerToggleSprintEvent event) {
FightData.getData(event.getPlayer()).knockbackSprintTime = System.currentTimeMillis();
}
}

View File

@ -0,0 +1,104 @@
package fr.neatmonster.nocheatplus.checks.fight;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckEvent;
import fr.neatmonster.nocheatplus.players.Permissions;
import fr.neatmonster.nocheatplus.utilities.LagMeasureTask;
/*
* MP""""""`MM dP
* M mmmmm..M 88
* M. `YM 88d888b. .d8888b. .d8888b. .d888b88
* MMMMMMM. M 88' `88 88ooood8 88ooood8 88' `88
* M. .MMM' M 88. .88 88. ... 88. ... 88. .88
* Mb. .dM 88Y888P' `88888P' `88888P' `88888P8
* MMMMMMMMMMM 88
* dP
*/
/**
* The Speed check is used to detect players who are attacking entities too quickly.
*/
public class Speed extends Check {
/**
* The event triggered by this check.
*/
public class SpeedEvent extends CheckEvent {
/**
* Instantiates a new speed event.
*
* @param player
* the player
*/
public SpeedEvent(final Player player) {
super(player);
}
}
/**
* Checks a player.
*
* @param player
* the player
* @return true, if successful
*/
public boolean check(final Player player) {
final FightConfig cc = FightConfig.getConfig(player);
final FightData data = FightData.getData(player);
boolean cancel = false;
// Has one second passed? Reset counters and violation level in that case.
if (data.speedTime + 1000L <= System.currentTimeMillis()) {
data.speedTime = System.currentTimeMillis();
data.speedAttacks = 0;
data.speedVL = 0D;
}
// Count the attack.
data.speedAttacks++;
// Too many attacks?
if (data.speedAttacks > cc.speedLimit) {
// If there was lag, don't count it towards violation level.
if (!LagMeasureTask.skipCheck())
data.speedVL += 1;
// Dispatch a speed event (API).
final SpeedEvent e = new SpeedEvent(player);
Bukkit.getPluginManager().callEvent(e);
// Execute whatever actions are associated with this check and the violation level and find out if we should
// cancel the event.
cancel = !e.isCancelled() && executeActions(player, cc.speedActions, data.speedVL);
}
return cancel;
}
/* (non-Javadoc)
* @see fr.neatmonster.nocheatplus.checks.Check#getParameter(fr.neatmonster.nocheatplus.actions.ParameterName, org.bukkit.entity.Player)
*/
@Override
public String getParameter(final ParameterName wildcard, final Player player) {
if (wildcard == ParameterName.VIOLATIONS)
return String.valueOf(Math.round(FightData.getData(player).speedVL));
else if (wildcard == ParameterName.ATTACKS_LIMIT)
return String.valueOf(Math.round(FightConfig.getConfig(player).speedLimit));
else
return super.getParameter(wildcard, player);
}
/* (non-Javadoc)
* @see fr.neatmonster.nocheatplus.checks.Check#isEnabled(org.bukkit.entity.Player)
*/
@Override
protected boolean isEnabled(final Player player) {
return !player.hasPermission(Permissions.FIGHT_SPEED) && FightConfig.getConfig(player).speedCheck;
}
}

View File

@ -235,6 +235,11 @@ public abstract class ConfPaths {
public static final String FIGHT_REACH_PENALTY = FIGHT_REACH + "penalty";
public static final String FIGHT_REACH_ACTIONS = FIGHT_REACH + "actions";
private static final String FIGHT_SPEED = FIGHT + "speed.";
public static final String FIGHT_SPEED_CHECK = FIGHT_SPEED + "active";
public static final String FIGHT_SPEED_LIMIT = FIGHT_SPEED + "limit";
public static final String FIGHT_SPEED_ACTIONS = FIGHT_SPEED + "actions";
/*
* e e ,e,
* d8b d8b e88 88e Y8b Y888P " 888 8e e88 888

View File

@ -208,6 +208,10 @@ public class DefaultConfig extends ConfigFile {
set(ConfPaths.FIGHT_REACH_PENALTY, 500);
set(ConfPaths.FIGHT_REACH_ACTIONS, "cancel vl>10 log:freach:2:5:if cancel");
set(ConfPaths.FIGHT_SPEED_CHECK, true);
set(ConfPaths.FIGHT_SPEED_LIMIT, 15);
set(ConfPaths.FIGHT_SPEED_ACTIONS, "log:fspeed:0:5:if cancel");
/*
* e e ,e,
* d8b d8b e88 88e Y8b Y888P " 888 8e e88 888
@ -277,6 +281,7 @@ public class DefaultConfig extends ConfigFile {
set(ConfPaths.STRINGS + ".flylong", start
+ "tried to move from [locationfrom] to [locationto] over a distance of [distance] block(s)" + end);
set(ConfPaths.STRINGS + ".freach", start + "tried to attack entity out of reach" + end);
set(ConfPaths.STRINGS + ".fspeed", start + "tried to attack more than [attackslimit] times per second" + end);
set(ConfPaths.STRINGS + ".godmode", start + "avoided taking damage or lagging" + end);
set(ConfPaths.STRINGS + ".instantheal", start + "tried to regenerate health faster than normal" + end);
set(ConfPaths.STRINGS + ".kick", "kick [player]");

View File

@ -121,6 +121,7 @@ public class Permissions {
public static final String FIGHT_KNOCKBACK = FIGHT + ".knockback";
public static final String FIGHT_NOSWING = FIGHT + ".noswing";
public static final String FIGHT_REACH = FIGHT + ".reach";
public static final String FIGHT_SPEED = FIGHT + ".speed";
/*
* e e ,e,