Move the new packet frequency check to a static utility, minor cleanups.

* Always use lag.
* Let the morepackets check decide how to treat "just look" events.
* Optimizations.
* Renames.
This commit is contained in:
asofold 2014-07-18 14:18:00 +02:00
parent 3d5bbbbedd
commit 1441859ced
3 changed files with 120 additions and 76 deletions

View File

@ -7,16 +7,14 @@ import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData; import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.net.NetStatic;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation; import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
import fr.neatmonster.nocheatplus.utilities.TickTask; import fr.neatmonster.nocheatplus.utilities.TrigUtil;
/** /**
* The MorePackets check (previously called Speedhack check) will try to identify players that send more than the usual * The MorePackets check will try to identify players that send more than the usual
* amount of move-packets to the server to be able to move faster than normal, without getting caught by the other * amount of move-packets to the server to be able to move faster than normal, without getting caught by the other
* checks (flying/running). * checks (flying/running).
*
* It monitors the number of packets sent to the server within 1 second and compares it to the "legal" number of packets
* for that timeframe (22).
*/ */
public class MorePackets extends Check { public class MorePackets extends Check {
@ -34,12 +32,10 @@ public class MorePackets extends Check {
} }
/** /**
* Checks a player. * Check for speeding by sending too many packets. We assume 22 packets per
* * second to be legitimate, while 20 would be ideal. See
* Players get assigned a certain amount of "free" packets as a limit initially. Every move packet reduces that * PlayerData.morePacketsFreq for the monitored amount of time and the
* limit by 1. If more than 1 second of time passed, the limit gets increased by 22 * time in seconds, up to 50 and * resolution. See NetStatic for the actual check code.
* they get a new "setback" location. If the player reaches limit = 0 -> teleport them back to "setback". If there was
* a long pause (maybe lag), limit may be up to 100.
* *
* @param player * @param player
* the player * the player
@ -53,8 +49,15 @@ public class MorePackets extends Check {
// Take time once, first: // Take time once, first:
final long time = System.currentTimeMillis(); final long time = System.currentTimeMillis();
if (from.isSamePos(to)) {
// Ignore moves with "just look" for now.
// TODO: Extra ActionFrequency for "just look" + use to burn, maybe also check individually.
return null;
}
// Ensure we have a set-back location.
if (!data.hasMorePacketsSetBack()){ if (!data.hasMorePacketsSetBack()){
// TODO: Check if other set-back is appropriate or if to set on other events. // TODO: Check if other set-back is appropriate or if to set/reset on other events.
if (data.hasSetBack()) { if (data.hasSetBack()) {
data.setMorePacketsSetBack(data.getSetBack(to)); data.setMorePacketsSetBack(data.getSetBack(to));
} }
@ -63,54 +66,10 @@ public class MorePackets extends Check {
} }
} }
// Add packet to frequency count. // Check for a violation of the set limits.
data.morePacketsFreq.add(time, 1f); final double violation = NetStatic.morePacketsCheck(data.morePacketsFreq, time, 1f, maxPackets, idealPackets);
// Fill up all "used" time windows (minimum we can do without other events). // Process violation result.
final float burnScore = (float) idealPackets * (float) data.morePacketsFreq.bucketDuration() / 1000f;
// Find index.
int i;
int empty = 0;
boolean used = false;
for (i = 1; i < data.morePacketsFreq.numberOfBuckets(); i++) {
if (data.morePacketsFreq.bucketScore(i) > 0f) {
if (used) {
for (int j = i; j < data.morePacketsFreq.numberOfBuckets(); j ++) {
if (data.morePacketsFreq.bucketScore(j) == 0f) {
empty += 1;
}
}
break;
} else {
used = true;
}
}
}
// Adjust empty based on server side lag.
final float lag = cc.lag ? TickTask.getLag(data.morePacketsFreq.bucketDuration() * data.morePacketsFreq.numberOfBuckets(), true): 1f;
if (lag >= 1f) {
// This is more strict than without lag adaption (!).
empty = Math.min(empty, (int) Math.round((lag - 1f) * data.morePacketsFreq.numberOfBuckets()));
}
final double fullCount;
if (i < data.morePacketsFreq.numberOfBuckets()) {
// Assume all following time windows are burnt.
final float trailing = Math.max(data.morePacketsFreq.trailingScore(i, 1f), burnScore * (data.morePacketsFreq.numberOfBuckets() - i - empty));
final float leading = data.morePacketsFreq.leadingScore(i, 1f);
fullCount = leading + trailing;
} else {
// All time windows are used.
fullCount = data.morePacketsFreq.score(1f);
}
final double violation = (double) fullCount - (double) (maxPackets * data.morePacketsFreq.numberOfBuckets() * data.morePacketsFreq.bucketDuration() / 1000f);
// TODO: Burn time windows based on other activity counting [e.g. same resolution ActinFrequency with keep-alive].
// Player used up buffer, they fail the check.
if (violation > 0.0) { if (violation > 0.0) {
// Increment violation level. // Increment violation level.
@ -122,11 +81,12 @@ public class MorePackets extends Check {
vd.setParameter(ParameterName.PACKETS, Integer.toString(new Double(violation).intValue())); vd.setParameter(ParameterName.PACKETS, Integer.toString(new Double(violation).intValue()));
} }
if (executeActions(vd)) { if (executeActions(vd)) {
// Set to cancel the move.
return data.getMorePacketsSetBack(); return data.getMorePacketsSetBack();
} }
} }
else { else {
// Set the new "setback" location. (CHANGED to only update, if not a violation.) // Update the set-back location. (CHANGED to only update, if not a violation.)
// (Might update whenever newTo == null) // (Might update whenever newTo == null)
data.setMorePacketsSetBack(from); data.setMorePacketsSetBack(from);
} }

View File

@ -601,12 +601,10 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
} }
// Morepackets. // Morepackets.
// TODO: Also update counters if newTo == null?
if (newTo == null && cc.morePacketsCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_MOREPACKETS) && !player.hasPermission(Permissions.MOVING_MOREPACKETS)) { if (newTo == null && cc.morePacketsCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_MOREPACKETS) && !player.hasPermission(Permissions.MOVING_MOREPACKETS)) {
// If it hasn't been stopped by any other check and is handled by the more packets check, execute it. // If it hasn't been stopped by any other check and is handled by the more packets check, execute it.
// TODO: Still feed morepackets even if cancelled.
if (TrigUtil.distanceSquared(pFrom, pTo) > 0.0) {
newTo = morePackets.check(player, pFrom, pTo, data, cc); newTo = morePackets.check(player, pFrom, pTo, data, cc);
}
} else { } else {
// Otherwise we need to clear their data. // Otherwise we need to clear their data.
data.clearMorePacketsData(); data.clearMorePacketsData();

View File

@ -0,0 +1,86 @@
package fr.neatmonster.nocheatplus.net;
import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
import fr.neatmonster.nocheatplus.utilities.TickTask;
/**
* Static method utility for networking related stuff.
* <hr>
* Not sure about final location and naming... and content :p.
* @author dev1mc
*
*/
public class NetStatic {
/**
* Packet-cheating check, for catching clients that send more packets than
* allowed. Intention is to have a more accurate check than just preventing
* "extreme spamming".
*
* @param packetFreq
* Records the packets. This check will update packetFreq
* according to the given time and packets.
* @param time
* Milliseconds time to update the ActionFrequency instance with.
* @param packets
* Amount to add to packetFreq with time.
* @param maxPackets
* The amount of packets per second (!), that is considered
* legitimate.
* @param idealPackets
* The "ideal" amount of packets per second. Used for "burning"
* time frames by setting them to this amount.
* @return The violation amount, i.e. "count above limit".
*/
public static double morePacketsCheck(final ActionFrequency packetFreq, final long time, final float packets, final float maxPackets, final float idealPackets) {
// Pull down stuff.
final long winDur = packetFreq.bucketDuration();
final int winNum = packetFreq.numberOfBuckets();
// Add packet to frequency count.
packetFreq.add(time, packets);
// Fill up all "used" time windows (minimum we can do without other events).
final float burnScore = (float) idealPackets * (float) winDur / 1000f;
// Find index.
int burnStart;
int empty = 0;
boolean used = false;
for (burnStart = 1; burnStart < winNum; burnStart ++) {
if (packetFreq.bucketScore(burnStart) > 0f) {
if (used) {
for (int j = burnStart; j < winNum; j ++) {
if (packetFreq.bucketScore(j) == 0f) {
empty += 1;
}
}
break;
} else {
used = true;
}
}
}
// TODO: Burn time windows based on other activity counting [e.g. same resolution ActinFrequency with keep-alive].
// Adjust empty based on server side lag, this makes the check more strict.
// TODO: Consider to add a config flag for skipping the lag adaption (e.g. strict).
final float lag = TickTask.getLag(winDur * winNum, true);
// TODO: Consider increasing the allowed maximum, for extreme server-side lag.
empty = Math.min(empty, (int) Math.round((lag - 1f) * winNum));
final double fullCount;
if (burnStart < winNum) {
// Assume all following time windows are burnt.
final float trailing = Math.max(packetFreq.trailingScore(burnStart, 1f), burnScore * (winNum - burnStart - empty));
final float leading = packetFreq.leadingScore(burnStart, 1f);
fullCount = leading + trailing;
} else {
// All time windows are used.
fullCount = packetFreq.score(1f);
}
return (double) fullCount - (double) (maxPackets * winNum * winDur / 1000f);
}
}