mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-01-04 23:07:44 +01:00
[Development] Fight checks rewritten, now they need testing and
improvements.
This commit is contained in:
parent
4bb82a4a20
commit
e706558cc1
@ -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"),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
104
src/fr/neatmonster/nocheatplus/checks/fight/Speed.java
Normal file
104
src/fr/neatmonster/nocheatplus/checks/fight/Speed.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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]");
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user