}
// TODO: calculate factor for dists: ticks * 50 * lag
// TODO: dist < width => skip some checks (direction, ..)
final LocationTrace damagedTrace;
final Player damagedPlayer;
if (damaged instanceof Player){
damagedPlayer = (Player) damaged;
// // Bad pitch/yaw, just in case.
// if (LocUtil.needsDirectionCorrection(useLoc2.getYaw(), useLoc2.getPitch())) {
// mcAccess.correctDirection(damagedPlayer);
// damagedPlayer.getLocation(useLoc2);
// }
// Log.
if (cc.debug && damagedPlayer.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
damagedPlayer.sendMessage("Attacked by " + player.getName() + ": inv=" + mcAccess.getInvulnerableTicks(damagedPlayer) + " ndt=" + damagedPlayer.getNoDamageTicks());
}
// Check for self hit exploits (mind that projectiles are excluded from this.)
if (selfHit.isEnabled(player) && selfHit.check(player, damagedPlayer, data, cc)) {
cancelled = true;
}
// Get+update the damaged players.
// TODO: Problem with NPCs: data stays (not a big problem).
// (This is done even if the event has already been cancelled, to keep track, if the player is on a horse.)
damagedTrace = MovingData.getData(damagedPlayer).updateTrace(damagedPlayer, damagedLoc, tick);
} else {
damagedPlayer = null; // TODO: This is a temporary workaround.
// Use a fake trace.
// TODO: Provide for entities too? E.g. one per player, or a fully fledged bookkeeping thing (EntityData).
//final MovingConfig mcc = MovingConfig.getConfig(damagedLoc.getWorld().getName());
damagedTrace = null; //new LocationTrace(mcc.traceSize, mcc.traceMergeDist);
//damagedTrace.addEntry(tick, damagedLoc.getX(), damagedLoc.getY(), damagedLoc.getZ());
}
if (cc.cancelDead){
if (damaged.isDead()) {
cancelled = true;
}
// Only allow damaging others if taken damage this tick.
if (player.isDead() && data.damageTakenByEntityTick != TickTask.getTick()){
cancelled = true;
}
}
if (damage <= 4.0 && tick == data.damageTakenByEntityTick && data.thornsId != Integer.MIN_VALUE && data.thornsId == damaged.getEntityId()){
// Don't handle further, but do respect selfhit/canceldead.
// TODO: Remove soon.
data.thornsId = Integer.MIN_VALUE;
return cancelled;
}
else {
data.thornsId = Integer.MIN_VALUE;
}
// Run through the main checks.
if (!cancelled && speed.isEnabled(player)){
if (speed.check(player, now)){
cancelled = true;
// Still feed the improbable.
if (data.speedVL > 50){
Improbable.check(player, 2f, now, "fight.speed");
}
else{
Improbable.feed(player, 2f, now);
}
}
else if (normalizedMove > 2.0 && Improbable.check(player, 1f, now, "fight.speed")){
// Feed improbable in case of ok-moves too.
// TODO: consider only feeding if attacking with higher average speed (!)
cancelled = true;
}
}
if (!cancelled && critical.isEnabled(player) && critical.check(player, loc, data, cc)) {
cancelled = true;
}
if (!cancelled && knockback.isEnabled(player) && knockback.check(player, data, cc)) {
cancelled = true;
}
if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player, data, cc)) {
cancelled = true;
}
if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING)) {
cancelled = true;
}
// TODO: Order of all these checks ...
// Checks that use LocationTrace.
// TODO: Later optimize (...), should reverse check window ?
// First loop through reach and direction, to determine a window.
final boolean reachEnabled = !cancelled && reach.isEnabled(player);
final boolean directionEnabled = !cancelled && direction.isEnabled(player);
if (reachEnabled || directionEnabled) {
if (damagedPlayer != null) {
// TODO: Move to a method (trigonometric checks).
final ReachContext reachContext = reachEnabled ? reach.getContext(player, loc, damaged, damagedLoc, data, cc) : null;
final DirectionContext directionContext = directionEnabled ? direction.getContext(player, loc, damaged, damagedLoc, data, cc) : null;
final long traceOldest = tick; // - damagedTrace.getMaxSize(); // TODO: Set by window.
// TODO: Iterating direction: could also start from latest, be it on occasion.
Iterator<TraceEntry> traceIt = damagedTrace.maxAgeIterator(traceOldest);
boolean violation = true; // No tick with all checks passed.
boolean reachPassed = !reachEnabled; // Passed individually for some tick.
boolean directionPassed = !directionEnabled; // Passed individually for some tick.
// TODO: Maintain a latency estimate + max diff and invalidate completely (i.e. iterate from latest NEXT time)], or just max latency.