Add target-location heuristic to reduce false positives with improbable.

This commit is contained in:
asofold 2013-01-29 02:51:51 +01:00
parent 3348865288
commit d8701a7ea6
3 changed files with 89 additions and 20 deletions

View File

@ -69,6 +69,7 @@ public class CombinedListener extends CheckListener {
Integer modifier = cc.invulnerableModifiers.get(cause);
if (modifier == null) modifier = cc.invulnerableModifierDefault;
final CombinedData data = CombinedData.getData(player);
// TODO: account for tick task reset ? [it should not though, due to data resetting too, but API would allow it]
if (TickTask.getTick() >= data.invulnerableTick + modifier.intValue()) return;
// Still invulnerable.
event.setCancelled(true);

View File

@ -79,11 +79,14 @@ public class FightData extends ACheckData {
public double reachVL;
public double speedVL;
public long damageTakenByEntityTick;
// Shared
public String lastWorld = "";
public String lastWorld = "";
public int lastAttackTick = 0;
public double lastAttackedX = Integer.MAX_VALUE;
public double lastAttackedY;
public double lastAttackedZ;
// public double lastAttackedDist = 0.0;
public long damageTakenByEntityTick;
// Data of the angle check.
public TreeMap<Long, Location> angleHits = new TreeMap<Long, Location>();
@ -91,10 +94,18 @@ public class FightData extends ACheckData {
// Data of the direction check.
public long directionLastViolationTime;
// Data of the god mode check.
// Old god mode check.
public int godModeBuffer;
public int godModeLastAge;
public long godModeLastTime;
// New god mode check [in progress].
public int godModeHealthDecreaseTick = 0;
public int godModeHealth = 0;
public int lastDamageTick = 0;
public int lastNoDamageTicks = 0;
/** Accumulator. */
public int godModeAcc = 0;
// Data of the knockback check.
public long knockbackSprintTime;

View File

@ -1,5 +1,6 @@
package fr.neatmonster.nocheatplus.checks.fight;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -8,6 +9,7 @@ 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.player.PlayerAnimationEvent;
import org.bukkit.event.player.PlayerToggleSprintEvent;
import org.bukkit.inventory.ItemStack;
@ -18,6 +20,7 @@ import fr.neatmonster.nocheatplus.checks.combined.Combined;
import fr.neatmonster.nocheatplus.checks.combined.Improbable;
import fr.neatmonster.nocheatplus.checks.inventory.Items;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.TickTask;
/*
@ -88,7 +91,35 @@ public class FightListener extends CheckListener {
boolean cancelled = false;
final String worldName = player.getWorld().getName();
final int tick = TickTask.getTick();
final long now = System.currentTimeMillis();
final boolean worldChanged = !worldName.equals(data.lastWorld);
final Location loc = player.getLocation();
final Location targetLoc = damaged.getLocation();
// final double targetDist = CheckUtils.distance(loc, targetLoc);
final double targetMove;
final int tickAge;
final long msAge; // Milliseconds the ticks actually took.
final double normalizedMove; // Blocks per second.
// TODO: relative distance (player - target)!
if (data.lastAttackedX == Integer.MAX_VALUE || tick < data.lastAttackTick || worldChanged || tick - data.lastAttackTick > 20){
// TODO: 20 ?
tickAge = 0;
targetMove = 0.0;
normalizedMove = 0.0;
msAge = 0;
}
else{
tickAge = tick - data.lastAttackTick;
targetMove = CheckUtils.distance(data.lastAttackedX, data.lastAttackedY, data.lastAttackedZ, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ());
msAge = (long) (50f * TickTask.getLag(50L * tickAge) * (float) tickAge);
normalizedMove = msAge == 0 ? targetMove : targetMove * 1000.0 / (double) msAge;
}
// TODO: calculate factor for dists: ticks * 50 * lag
// TODO: dist < width => skip some checks (direction, ..)
// Check for self hit exploits (mind that projectiles should be excluded)
if (damaged instanceof Player){
final Player damagedPlayer = (Player) damaged;
@ -107,18 +138,16 @@ public class FightListener extends CheckListener {
}
}
final long now = System.currentTimeMillis();
final boolean worldChanged = !worldName.equals(data.lastWorld);
// Run through the main checks.
if (!cancelled && speed.isEnabled(player)){
if (speed.check(player, now)){
cancelled = true;
// Still feed the improbable.
Improbable.feed(player, 1f, now);
Improbable.feed(player, 2f, now);
}
else if (Improbable.check(player, 1f, now)){
else if (normalizedMove > 2.0 && Improbable.check(player, 1f, now)){
// Feed improbable in case of ok-moves too.
// TODO: consider only feeding if attacking with higher average speed (!)
cancelled = true;
@ -128,7 +157,7 @@ public class FightListener extends CheckListener {
if (angle.isEnabled(player)) {
// The "fast turning" checks are checked in any case because they accumulate data.
// Improbable yaw changing.
if (Combined.checkYawRate(player, player.getLocation().getYaw(), now, worldName, cc.yawRateCheck)) {
if (Combined.checkYawRate(player, loc.getYaw(), now, worldName, cc.yawRateCheck)) {
// (Check or just feed).
// TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then).
cancelled = true;
@ -155,7 +184,14 @@ public class FightListener extends CheckListener {
if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING))
cancelled = true;
// Set values.
data.lastWorld = worldName;
data.lastAttackTick = tick;
data.lastAttackedX = targetLoc.getX();
data.lastAttackedY = targetLoc.getY();
data.lastAttackedZ = targetLoc.getZ();
// data.lastAttackedDist = targetDist;
return cancelled;
}
@ -165,8 +201,7 @@ public class FightListener extends CheckListener {
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = true, priority = EventPriority.LOWEST)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamage(final EntityDamageEvent event) {
/*
* _____ _ _ _ ____
@ -182,9 +217,10 @@ public class FightListener extends CheckListener {
final boolean damagedIsDead = damaged.isDead();
if (damagedIsPlayer && !damagedIsDead) {
final Player player = (Player) event.getEntity();
if (godMode.isEnabled(player) && godMode.check(player))
if (godMode.isEnabled(player) && godMode.check(player)){ // , event.getDamage())){
// It requested to "cancel" the players invulnerability, so set his noDamageTicks to 0.
player.setNoDamageTicks(0);
}
}
// Attacking entities.
if (event instanceof EntityDamageByEntityEvent) {
@ -202,6 +238,20 @@ public class FightListener extends CheckListener {
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamageMonitor(final EntityDamageEvent event) {
final Entity damaged = event.getEntity();
if (damaged instanceof Player){
final Player player = (Player) damaged;
final FightData data = FightData.getData(player);
final int ndt = player.getNoDamageTicks();
if (data.lastDamageTick == TickTask.getTick() && data.lastNoDamageTicks != ndt){
// Plugin compatibility thing.
data.lastNoDamageTicks = ndt;
}
}
}
/**
* We listen to death events to prevent a very specific method of doing godmode.
@ -209,8 +259,7 @@ public class FightListener extends CheckListener {
* @param event
* the event
*/
@EventHandler(
priority = EventPriority.MONITOR)
@EventHandler(priority = EventPriority.MONITOR)
protected void onEntityDeathEvent(final EntityDeathEvent event) {
/*
* _____ _ _ _ ____ _ _
@ -234,8 +283,7 @@ public class FightListener extends CheckListener {
* @param event
* the event
*/
@EventHandler(
priority = EventPriority.MONITOR)
@EventHandler(priority = EventPriority.MONITOR)
protected void onPlayerAnimation(final PlayerAnimationEvent event) {
/*
* ____ _ _ _ _ _
@ -255,8 +303,7 @@ public class FightListener extends CheckListener {
* @param event
* the event
*/
@EventHandler(
ignoreCancelled = true, priority = EventPriority.MONITOR)
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPlayerToggleSprint(final PlayerToggleSprintEvent event) {
/*
* ____ _ _____ _ ____ _ _
@ -268,4 +315,14 @@ public class FightListener extends CheckListener {
*/
if (event.isSprinting()) FightData.getData(event.getPlayer()).knockbackSprintTime = System.currentTimeMillis();
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onEntityRegainHealth(final EntityRegainHealthEvent event){
final Entity entity = event.getEntity();
if (!(entity instanceof Player)) return;
final Player player = (Player) entity;
final FightData data = FightData.getData(player);
// Set health to maximum.
data.godModeHealth = Math.max(data.godModeHealth, player.getHealth() + event.getAmount());
}
}