mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-12-27 02:48:04 +01:00
Add target-location heuristic to reduce false positives with improbable.
This commit is contained in:
parent
3348865288
commit
d8701a7ea6
@ -69,6 +69,7 @@ public class CombinedListener extends CheckListener {
|
|||||||
Integer modifier = cc.invulnerableModifiers.get(cause);
|
Integer modifier = cc.invulnerableModifiers.get(cause);
|
||||||
if (modifier == null) modifier = cc.invulnerableModifierDefault;
|
if (modifier == null) modifier = cc.invulnerableModifierDefault;
|
||||||
final CombinedData data = CombinedData.getData(player);
|
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;
|
if (TickTask.getTick() >= data.invulnerableTick + modifier.intValue()) return;
|
||||||
// Still invulnerable.
|
// Still invulnerable.
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
@ -79,11 +79,14 @@ public class FightData extends ACheckData {
|
|||||||
public double reachVL;
|
public double reachVL;
|
||||||
public double speedVL;
|
public double speedVL;
|
||||||
|
|
||||||
public long damageTakenByEntityTick;
|
|
||||||
|
|
||||||
// Shared
|
// 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.
|
// Data of the angle check.
|
||||||
public TreeMap<Long, Location> angleHits = new TreeMap<Long, Location>();
|
public TreeMap<Long, Location> angleHits = new TreeMap<Long, Location>();
|
||||||
@ -91,10 +94,18 @@ public class FightData extends ACheckData {
|
|||||||
// Data of the direction check.
|
// Data of the direction check.
|
||||||
public long directionLastViolationTime;
|
public long directionLastViolationTime;
|
||||||
|
|
||||||
// Data of the god mode check.
|
// Old god mode check.
|
||||||
public int godModeBuffer;
|
public int godModeBuffer;
|
||||||
public int godModeLastAge;
|
public int godModeLastAge;
|
||||||
public long godModeLastTime;
|
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.
|
// Data of the knockback check.
|
||||||
public long knockbackSprintTime;
|
public long knockbackSprintTime;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package fr.neatmonster.nocheatplus.checks.fight;
|
package fr.neatmonster.nocheatplus.checks.fight;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
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;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
||||||
import org.bukkit.event.entity.EntityDeathEvent;
|
import org.bukkit.event.entity.EntityDeathEvent;
|
||||||
|
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||||
import org.bukkit.event.player.PlayerAnimationEvent;
|
import org.bukkit.event.player.PlayerAnimationEvent;
|
||||||
import org.bukkit.event.player.PlayerToggleSprintEvent;
|
import org.bukkit.event.player.PlayerToggleSprintEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
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.combined.Improbable;
|
||||||
import fr.neatmonster.nocheatplus.checks.inventory.Items;
|
import fr.neatmonster.nocheatplus.checks.inventory.Items;
|
||||||
import fr.neatmonster.nocheatplus.permissions.Permissions;
|
import fr.neatmonster.nocheatplus.permissions.Permissions;
|
||||||
|
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
||||||
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -88,7 +91,35 @@ public class FightListener extends CheckListener {
|
|||||||
boolean cancelled = false;
|
boolean cancelled = false;
|
||||||
|
|
||||||
final String worldName = player.getWorld().getName();
|
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)
|
// Check for self hit exploits (mind that projectiles should be excluded)
|
||||||
if (damaged instanceof Player){
|
if (damaged instanceof Player){
|
||||||
final Player damagedPlayer = (Player) damaged;
|
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.
|
// Run through the main checks.
|
||||||
if (!cancelled && speed.isEnabled(player)){
|
if (!cancelled && speed.isEnabled(player)){
|
||||||
if (speed.check(player, now)){
|
if (speed.check(player, now)){
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
// Still feed the improbable.
|
// 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.
|
// Feed improbable in case of ok-moves too.
|
||||||
// TODO: consider only feeding if attacking with higher average speed (!)
|
// TODO: consider only feeding if attacking with higher average speed (!)
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
@ -128,7 +157,7 @@ public class FightListener extends CheckListener {
|
|||||||
if (angle.isEnabled(player)) {
|
if (angle.isEnabled(player)) {
|
||||||
// The "fast turning" checks are checked in any case because they accumulate data.
|
// The "fast turning" checks are checked in any case because they accumulate data.
|
||||||
// Improbable yaw changing.
|
// 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).
|
// (Check or just feed).
|
||||||
// TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then).
|
// TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then).
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
@ -155,7 +184,14 @@ public class FightListener extends CheckListener {
|
|||||||
if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING))
|
if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING))
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
|
||||||
|
// Set values.
|
||||||
data.lastWorld = worldName;
|
data.lastWorld = worldName;
|
||||||
|
data.lastAttackTick = tick;
|
||||||
|
data.lastAttackedX = targetLoc.getX();
|
||||||
|
data.lastAttackedY = targetLoc.getY();
|
||||||
|
data.lastAttackedZ = targetLoc.getZ();
|
||||||
|
// data.lastAttackedDist = targetDist;
|
||||||
|
|
||||||
return cancelled;
|
return cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,8 +201,7 @@ public class FightListener extends CheckListener {
|
|||||||
* @param event
|
* @param event
|
||||||
* the event
|
* the event
|
||||||
*/
|
*/
|
||||||
@EventHandler(
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||||
ignoreCancelled = true, priority = EventPriority.LOWEST)
|
|
||||||
public void onEntityDamage(final EntityDamageEvent event) {
|
public void onEntityDamage(final EntityDamageEvent event) {
|
||||||
/*
|
/*
|
||||||
* _____ _ _ _ ____
|
* _____ _ _ _ ____
|
||||||
@ -182,9 +217,10 @@ public class FightListener extends CheckListener {
|
|||||||
final boolean damagedIsDead = damaged.isDead();
|
final boolean damagedIsDead = damaged.isDead();
|
||||||
if (damagedIsPlayer && !damagedIsDead) {
|
if (damagedIsPlayer && !damagedIsDead) {
|
||||||
final Player player = (Player) event.getEntity();
|
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.
|
// It requested to "cancel" the players invulnerability, so set his noDamageTicks to 0.
|
||||||
player.setNoDamageTicks(0);
|
player.setNoDamageTicks(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Attacking entities.
|
// Attacking entities.
|
||||||
if (event instanceof EntityDamageByEntityEvent) {
|
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.
|
* 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
|
* @param event
|
||||||
* the event
|
* the event
|
||||||
*/
|
*/
|
||||||
@EventHandler(
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
priority = EventPriority.MONITOR)
|
|
||||||
protected void onEntityDeathEvent(final EntityDeathEvent event) {
|
protected void onEntityDeathEvent(final EntityDeathEvent event) {
|
||||||
/*
|
/*
|
||||||
* _____ _ _ _ ____ _ _
|
* _____ _ _ _ ____ _ _
|
||||||
@ -234,8 +283,7 @@ public class FightListener extends CheckListener {
|
|||||||
* @param event
|
* @param event
|
||||||
* the event
|
* the event
|
||||||
*/
|
*/
|
||||||
@EventHandler(
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
priority = EventPriority.MONITOR)
|
|
||||||
protected void onPlayerAnimation(final PlayerAnimationEvent event) {
|
protected void onPlayerAnimation(final PlayerAnimationEvent event) {
|
||||||
/*
|
/*
|
||||||
* ____ _ _ _ _ _
|
* ____ _ _ _ _ _
|
||||||
@ -255,8 +303,7 @@ public class FightListener extends CheckListener {
|
|||||||
* @param event
|
* @param event
|
||||||
* the event
|
* the event
|
||||||
*/
|
*/
|
||||||
@EventHandler(
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
ignoreCancelled = true, priority = EventPriority.MONITOR)
|
|
||||||
public void onPlayerToggleSprint(final PlayerToggleSprintEvent event) {
|
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();
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user