Refine fighting checks [ongoing]

This commit is contained in:
asofold 2012-09-19 02:20:21 +02:00
parent bb24ecaa93
commit 836f3af19d
11 changed files with 140 additions and 49 deletions

View File

@ -4,7 +4,7 @@
<!-- Informations -->
<name>NoCheatPlus</name>
<version>3.7.3-beta</version>
<version>3.7.3</version>
<description>Detect and fight the exploitation of various flaws/bugs in Minecraft.</description>
<url>http://dev.bukkit.org/server-mods/nocheatplus</url>

View File

@ -84,7 +84,8 @@ public class Combined {
// Add time
final float amount = ((total - threshold) / threshold * 1000f);
data.timeFreeze = Math.max(data.timeFreeze, now + (long) amount);
if (cc.yawRateImprobable && Improbable.check(player, 5f * amount / 1000f, now))
// TODO: balance (100 ... 200 ) ?
if (cc.yawRateImprobable && Improbable.check(player, amount / 100f, now))
cancel = true;
}
if (now < data.timeFreeze) cancel = true;

View File

@ -43,14 +43,13 @@ public class Critical extends Check {
boolean cancel = false;
// We'll need the PlayerLocation to know some important stuff.
PlayerLocation location = new PlayerLocation();
final PlayerLocation location = new PlayerLocation();
location.set(player.getLocation(), player);
// Check if the hit was a critical hit (positive fall distance, entity in the air, not on ladder, not in liquid
// and without blindness effect).
if (player.getFallDistance() > 0f && !location.isOnGround() && !location.isOnLadder() && !location.isInLiquid()
&& !player.hasPotionEffect(PotionEffectType.BLINDNESS))
&& !player.hasPotionEffect(PotionEffectType.BLINDNESS)){
// It was a critical hit, now check if the player has jumped or has sent a packet to mislead the server.
if (player.getFallDistance() < cc.criticalFallDistance
|| Math.abs(player.getVelocity().getY()) < cc.criticalVelocity) {
@ -70,8 +69,8 @@ public class Critical extends Check {
// should cancel the event.
cancel = executeActions(player, data.criticalVL, delta, cc.criticalActions);
}
}
location = null;
return cancel;
}

View File

@ -91,6 +91,7 @@ public class FightConfig extends ACheckConfig {
public final boolean reachCheck;
public final long reachPenalty;
public final boolean reachPrecision;
public final ActionList reachActions;
public final boolean selfHitCheck;
@ -98,9 +99,17 @@ public class FightConfig extends ACheckConfig {
public final boolean speedCheck;
public final int speedLimit;
public final int speedBuckets;
public final long speedBucketDur;
public final float speedBucketFactor;
public final int speedShortTermLimit;
public final int speedShortTermTicks;
public final ActionList speedActions;
public final boolean yawRateCheck;
// Special flags:
public final boolean yawRateCheck;
public final boolean cancelDead;
/**
* Instantiates a new fight configuration.
@ -137,6 +146,7 @@ public class FightConfig extends ACheckConfig {
reachCheck = data.getBoolean(ConfPaths.FIGHT_REACH_CHECK);
reachPenalty = data.getLong(ConfPaths.FIGHT_REACH_PENALTY);
reachPrecision = data.getBoolean(ConfPaths.FIGHT_REACH_PRECISION);
reachActions = data.getActionList(ConfPaths.FIGHT_REACH_ACTIONS, Permissions.FIGHT_REACH);
selfHitCheck = data.getBoolean(ConfPaths.FIGHT_SELFHIT_CHECK);
@ -144,9 +154,16 @@ public class FightConfig extends ACheckConfig {
speedCheck = data.getBoolean(ConfPaths.FIGHT_SPEED_CHECK);
speedLimit = data.getInt(ConfPaths.FIGHT_SPEED_LIMIT);
speedBuckets = data.getInt(ConfPaths.FIGHT_SPEED_BUCKETS_N, 6);
speedBucketDur = data.getLong(ConfPaths.FIGHT_SPEED_BUCKETS_DUR, 333);
speedBucketFactor = (float) data.getDouble(ConfPaths.FIGHT_SPEED_BUCKETS_FACTOR, 1f);
speedShortTermLimit = data.getInt(ConfPaths.FIGHT_SPEED_SHORTTERM_LIMIT);
speedShortTermTicks = data.getInt(ConfPaths.FIGHT_SPEED_SHORTTERM_TICKS);
speedActions = data.getActionList(ConfPaths.FIGHT_SPEED_ACTIONS, Permissions.FIGHT_SPEED);
yawRateCheck = data.getBoolean(ConfPaths.FIGHT_YAWRATE_CHECK, true);
cancelDead = data.getBoolean(ConfPaths.FIGHT_CANCELDEAD);
}
/* (non-Javadoc)

View File

@ -52,7 +52,7 @@ public class FightData extends ACheckData {
*/
public static FightData getData(final Player player) {
if (!playersMap.containsKey(player.getName()))
playersMap.put(player.getName(), new FightData());
playersMap.put(player.getName(), new FightData(FightConfig.getConfig(player)));
return playersMap.get(player.getName());
}
@ -73,6 +73,8 @@ public class FightData extends ACheckData {
public boolean skipNext;
public long damageTakenTick;
// Shared
public String lastWorld = "";
@ -104,7 +106,13 @@ public class FightData extends ACheckData {
// Data of the SelfHit check.
public ActionFrequency selfHitVL = new ActionFrequency(6, 5000);
// Data of the speed check.
public int speedAttacks;
public long speedTime;
// Data of the frequency check.
public final ActionFrequency speedBuckets;
public int speedShortTermCount;
public int speedShortTermTick;
public FightData(final FightConfig cc){
speedBuckets = new ActionFrequency(cc.speedBuckets, cc.speedBucketDur);
}
}

View File

@ -18,6 +18,7 @@ import org.bukkit.event.player.PlayerToggleSprintEvent;
import fr.neatmonster.nocheatplus.checks.combined.Combined;
import fr.neatmonster.nocheatplus.checks.combined.Improbable;
import fr.neatmonster.nocheatplus.players.Permissions;
import fr.neatmonster.nocheatplus.utilities.TickTask;
/*
* MM""""""""`M oo dP dP M""MMMMMMMM oo dP
@ -96,7 +97,7 @@ public class FightListener implements Listener {
data.skipNext = false;
return;
}
boolean cancelled = false;
final String worldName = player.getWorld().getName();
@ -104,7 +105,8 @@ public class FightListener implements Listener {
// Check for self hit exploits (mind that projectiles should be excluded)
final Entity cbEntity = event.getEntity();
if (cbEntity instanceof Player){
if (selfHit.isEnabled(player) && selfHit.check(player, cbEntity, data, cc))
final Player damagedPlayer = (Player) cbEntity;
if (selfHit.isEnabled(player) && selfHit.check(player, damagedPlayer, data, cc))
cancelled = true;
else{
// // Check if improbable
@ -114,6 +116,14 @@ public class FightListener implements Listener {
}
}
if (cc.cancelDead){
if (cbEntity.isDead()) cancelled = true;
// Only allow damaging others if taken damage this tick.
if (player.isDead() && data.damageTakenTick != TickTask.getTick()){
cancelled = true;
}
}
final long now = System.currentTimeMillis();
final boolean worldChanged = !worldName.equals(data.lastWorld);
@ -131,6 +141,9 @@ public class FightListener implements Listener {
final net.minecraft.server.Entity damaged = ((CraftEntity) cbEntity).getHandle();
// Run through the main checks.
if (!cancelled && speed.isEnabled(player) && speed.check(player, now))
cancelled = true;
if (!cancelled && angle.isEnabled(player) && angle.check(player, worldChanged))
cancelled = true;
@ -146,10 +159,7 @@ public class FightListener implements Listener {
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))
if (!cancelled && reach.isEnabled(player) && reach.check(player, cbEntity))
cancelled = true;
if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING))
@ -182,11 +192,18 @@ public class FightListener implements Listener {
// 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);
final Entity damaged = e.getEntity();
if (damaged instanceof Player){
FightData.getData((Player) damaged).damageTakenTick = TickTask.getTick();
}
if (e.getDamager() instanceof Player){
if (e.getCause() == DamageCause.ENTITY_ATTACK){
handleNormalDamage(e);
}
else if (e.getCause() == DamageCause.CUSTOM)
handleCustomDamage(e);
}
}
}

View File

@ -1,15 +1,17 @@
package fr.neatmonster.nocheatplus.checks.fight;
import net.minecraft.server.Entity;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.EnderDragon;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.combined.Improbable;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.LagMeasureTask;
/*
@ -55,15 +57,35 @@ public class Reach extends Check {
boolean cancel = false;
final double distanceLimit = player.getGameMode() == GameMode.SURVIVAL ? SURVIVAL_DISTANCE : CREATIVE_DISTANCE;
// Reference locations to check distance for.
// TODO: improve reference location: depending on height difference choose min(foot/hitbox, attacker)
final Location dRef;
if (damaged instanceof LivingEntity){
dRef = ((LivingEntity) damaged).getEyeLocation();
}
else dRef = damaged.getLocation();
final Location pRef = player.getEyeLocation();
final Vector pRel = dRef.toVector().subtract(pRef.toVector());
final double mod;
if (cc.reachPrecision){
// Calculate how fast they are closing in or moving away from each other.
mod = 1D;
// final Vector vRel = damaged.getVelocity().subtract(player.getVelocity());
// System.out.println(vRel.angle(pRel)); // TODO: NaN
// final double radial = vRel.length() * Math.cos((vRel.angle(pRel)));
// mod = (radial < 0 ? 0.8 : 1.0);
// System.out.println(player.getName() + " -> " + pRel.length() + ": " + radial + " => " + mod);
}
else mod = 1D;
// 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".
double distance = CheckUtils.distance(player.getEyeLocation(),
damaged.getBukkitEntity().getLocation().add(0D, damaged.getHeadHeight(), 0D))
- distanceLimit;
double distance = pRel.length() - distanceLimit * mod;
// Handle the EnderDragon differently.
if (damaged.getBukkitEntity() instanceof EnderDragon)
if (damaged instanceof EnderDragon)
distance -= 6.5D;
if (distance > 0) {
@ -82,11 +104,12 @@ public class Reach extends Check {
// Player passed the check, reward him.
data.reachVL *= 0.8D;
// Check if improbable
if (distance > -0.3){
if (Improbable.check(player, 2.0f, System.currentTimeMillis()))
cancel = true;
}
}
// Check if improbable.
if (cancel || distance > -1.25){
if (Improbable.check(player, (float) (distance + 1.25) / 2f, System.currentTimeMillis()))
cancel = true;
}

View File

@ -1,6 +1,5 @@
package fr.neatmonster.nocheatplus.checks.fight;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.Check;
@ -12,8 +11,8 @@ public class SelfHit extends Check {
super(CheckType.FIGHT_SELFHIT);
}
public boolean check(final Player damager, final Entity damaged, final FightData data, final FightConfig cc){
if (!damager.getName().equals(((Player) damaged).getName())) return false;
public boolean check(final Player damager, final Player damaged, final FightData data, final FightConfig cc){
if (!damager.getName().equals(damaged.getName())) return false;
boolean cancel = false;
// Treat self hitting as instant violation.

View File

@ -9,6 +9,7 @@ import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.utilities.LagMeasureTask;
import fr.neatmonster.nocheatplus.utilities.TickTask;
/*
* MP""""""`MM dP
@ -21,7 +22,7 @@ import fr.neatmonster.nocheatplus.utilities.LagMeasureTask;
* dP
*/
/**
* The Frequency check is used to detect players who are attacking entities too quickly.
* The Speed check is used to detect players who are attacking entities too quickly.
*/
public class Speed extends Check {
@ -37,34 +38,47 @@ public class Speed extends Check {
*
* @param player
* the player
* @param now
* @return true, if successful
*/
public boolean check(final Player player) {
public boolean check(final Player player, final long now) {
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;
// Add to frequency.
data.speedBuckets.add(now, 1f);
// Medium term (normalized to one second).
final float total = data.speedBuckets.getScore(cc.speedBucketFactor) * 1000f / (float) (cc.speedBucketDur * cc.speedBuckets);
// Short term.
final int tick = TickTask.getTick();
if (tick - data.speedShortTermTick < cc.speedShortTermTicks){
// Within range, add.
data.speedShortTermCount ++;
}
// Count the attack.
data.speedAttacks++;
else{
data.speedShortTermTick = tick;
data.speedShortTermCount = 1;
}
final float shortTerm = (float ) data.speedShortTermCount * 1000f / (50f * cc.speedShortTermTicks);
final float max = Math.max(shortTerm, total);
// Too many attacks?
if (data.speedAttacks > cc.speedLimit) {
if (max > cc.speedLimit) {
// If there was lag, don't count it towards violation level.
if (!LagMeasureTask.skipCheck())
data.speedVL += 1;
data.speedVL += total - cc.speedLimit;
// Execute whatever actions are associated with this check and the violation level and find out if we should
// cancel the event.
cancel = executeActions(player, data.speedVL, 1D, cc.speedActions);
cancel = executeActions(player, data.speedVL, total - cc.speedLimit, cc.speedActions);
}
else data.speedVL *= 0.96;
return cancel;
}

View File

@ -352,6 +352,7 @@ public abstract class ConfPaths {
private static final String FIGHT_REACH = FIGHT + "reach.";
public static final String FIGHT_REACH_CHECK = FIGHT_REACH + "active";
public static final String FIGHT_REACH_PENALTY = FIGHT_REACH + "penalty";
public static final String FIGHT_REACH_PRECISION = FIGHT_REACH + "precision";
public static final String FIGHT_REACH_ACTIONS = FIGHT_REACH + "actions";
public static final String FIGHT_SELFHIT = FIGHT + "selfhit.";
@ -362,10 +363,18 @@ public abstract class ConfPaths {
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";
private static final String FIGHT_SPEED_BUCKETS = FIGHT_SPEED + "buckets.";
public static final String FIGHT_SPEED_BUCKETS_N = FIGHT_SPEED_BUCKETS + "number";
public static final String FIGHT_SPEED_BUCKETS_DUR = FIGHT_SPEED_BUCKETS + "duration";
public static final String FIGHT_SPEED_BUCKETS_FACTOR = FIGHT_SPEED_BUCKETS + "factor";
private static final String FIGHT_SPEED_SHORTTERM = FIGHT_SPEED + "shortterm.";
public static final String FIGHT_SPEED_SHORTTERM_LIMIT = FIGHT_SPEED_SHORTTERM + "limit";
public static final String FIGHT_SPEED_SHORTTERM_TICKS = FIGHT_SPEED_SHORTTERM + "ticks";
public static final String FIGHT_SPEED_ACTIONS = FIGHT_SPEED + "actions";
private static final String FIGHT_YAWRATE = FIGHT + "yawrate.";
public static final String FIGHT_YAWRATE_CHECK = FIGHT_YAWRATE + "active";
public static final String FIGHT_CANCELDEAD = FIGHT + "canceldead";
/*
* 888 d8

View File

@ -240,6 +240,7 @@ public class DefaultConfig extends ConfigFile {
* , 88P
* "8",P"
*/
set(ConfPaths.FIGHT_CANCELDEAD, true);
set(ConfPaths.FIGHT_YAWRATE_CHECK, true);
set(ConfPaths.FIGHT_ANGLE_CHECK, true);
@ -271,6 +272,7 @@ public class DefaultConfig extends ConfigFile {
set(ConfPaths.FIGHT_REACH_CHECK, true);
set(ConfPaths.FIGHT_REACH_PENALTY, 500);
set(ConfPaths.FIGHT_REACH_PRECISION, true);
set(ConfPaths.FIGHT_REACH_ACTIONS, "cancel vl>10 log:freach:2:5:if cancel");
set(ConfPaths.FIGHT_SELFHIT_CHECK, true);
@ -279,6 +281,8 @@ public class DefaultConfig extends ConfigFile {
set(ConfPaths.FIGHT_SPEED_CHECK, true);
set(ConfPaths.FIGHT_SPEED_LIMIT, 15);
set(ConfPaths.FIGHT_SPEED_ACTIONS, "log:fspeed:0:5:if cancel");
set(ConfPaths.FIGHT_SPEED_SHORTTERM_TICKS, 7);
set(ConfPaths.FIGHT_SPEED_SHORTTERM_LIMIT, 6);
/*
* 888 d8