mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-10-02 16:37:25 +02:00
Optimize FlyingFrequency, add more structure and comments.
* Use a HashMap for data, as we intend to use the primary thread. * Move redundant move checking into a method. * Ignore on-ground, if it is sent too often. * Log if redundant move checking has to be disabled.
This commit is contained in:
parent
a09917571b
commit
f79cda4442
@ -1,5 +1,6 @@
|
|||||||
package fr.neatmonster.nocheatplus.net.protocollib;
|
package fr.neatmonster.nocheatplus.net.protocollib;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -13,11 +14,12 @@ import com.comphenix.protocol.events.PacketEvent;
|
|||||||
|
|
||||||
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||||
import fr.neatmonster.nocheatplus.components.JoinLeaveListener;
|
import fr.neatmonster.nocheatplus.components.JoinLeaveListener;
|
||||||
|
import fr.neatmonster.nocheatplus.logging.Streams;
|
||||||
import fr.neatmonster.nocheatplus.net.NetConfig;
|
import fr.neatmonster.nocheatplus.net.NetConfig;
|
||||||
import fr.neatmonster.nocheatplus.net.NetConfigCache;
|
import fr.neatmonster.nocheatplus.net.NetConfigCache;
|
||||||
import fr.neatmonster.nocheatplus.stats.Counters;
|
import fr.neatmonster.nocheatplus.stats.Counters;
|
||||||
|
import fr.neatmonster.nocheatplus.time.monotonic.Monotonic;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
|
import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
|
||||||
import fr.neatmonster.nocheatplus.utilities.ds.corw.LinkedHashMapCOW;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent extremely fast ticking by just sending packets that don't do anything
|
* Prevent extremely fast ticking by just sending packets that don't do anything
|
||||||
@ -42,12 +44,15 @@ public class FlyingFrequency extends PacketAdapter implements JoinLeaveListener
|
|||||||
public final float[] floats = new float[2]; // yaw, pitch
|
public final float[] floats = new float[2]; // yaw, pitch
|
||||||
//public final boolean[] booleans = new boolean[3]; // ground, hasPos, hasLook
|
//public final boolean[] booleans = new boolean[3]; // ground, hasPos, hasLook
|
||||||
public boolean onGround = false;
|
public boolean onGround = false;
|
||||||
|
public long timeOnGround = 0;
|
||||||
|
public long timeNotOnGround = 0;
|
||||||
|
|
||||||
public FFData(int seconds) {
|
public FFData(int seconds) {
|
||||||
all = new ActionFrequency(seconds, 1000L);
|
all = new ActionFrequency(seconds, 1000L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, FFData> freqMap = new LinkedHashMapCOW<String, FFData>();
|
private final Map<String, FFData> freqMap = new HashMap<String, FFData>();
|
||||||
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
|
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
|
||||||
private final int idSilent = counters.registerKey("packet.flying.silentcancel");
|
private final int idSilent = counters.registerKey("packet.flying.silentcancel");
|
||||||
private final int idRedundant = counters.registerKey("packet.flying.silentcancel.redundant");
|
private final int idRedundant = counters.registerKey("packet.flying.silentcancel.redundant");
|
||||||
@ -73,7 +78,7 @@ public class FlyingFrequency extends PacketAdapter implements JoinLeaveListener
|
|||||||
freqMap.remove(player.getName());
|
freqMap.remove(player.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private FFData getFreq(final String name, final NetConfig cc) {
|
private FFData getData(final String name, final NetConfig cc) {
|
||||||
final FFData freq = this.freqMap.get(name);
|
final FFData freq = this.freqMap.get(name);
|
||||||
if (freq != null) {
|
if (freq != null) {
|
||||||
return freq;
|
return freq;
|
||||||
@ -100,11 +105,11 @@ public class FlyingFrequency extends PacketAdapter implements JoinLeaveListener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final FFData freq = getFreq(player.getName(), cc);
|
final FFData data = getData(player.getName(), cc);
|
||||||
final long t = System.currentTimeMillis();
|
final long t = System.currentTimeMillis();
|
||||||
// Counting all packets.
|
// Counting all packets.
|
||||||
freq.all.add(t, 1f);
|
data.all.add(t, 1f);
|
||||||
final float allScore = freq.all.score(1f);
|
final float allScore = data.all.score(1f);
|
||||||
if (allScore > cc.flyingFrequencyMaxPackets) {
|
if (allScore > cc.flyingFrequencyMaxPackets) {
|
||||||
counters.add(idSilent, 1); // Until it is sure if we can get these async.
|
counters.add(idSilent, 1); // Until it is sure if we can get these async.
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
@ -112,58 +117,91 @@ public class FlyingFrequency extends PacketAdapter implements JoinLeaveListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cancel redundant packets, when frequency is high anyway.
|
// Cancel redundant packets, when frequency is high anyway.
|
||||||
if (!cancelRedundant || !cc.flyingFrequencyCancelRedundant) {
|
if (cancelRedundant && cc.flyingFrequencyCancelRedundant && checkRedundantPackets(event, allScore, data, cc)) {
|
||||||
return;
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
// TODO: Consider to detect if a moving event would fire (...).
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkRedundantPackets(final PacketEvent event, final float allScore, final FFData data, final NetConfig cc) {
|
||||||
|
// TODO: Check vs. MovingData last-to.
|
||||||
|
// TODO: Consider quick return conditions.
|
||||||
|
// TODO: Debug logging (better with integration into DataManager).
|
||||||
|
|
||||||
boolean redundant = true;
|
boolean redundant = true;
|
||||||
final PacketContainer packet = event.getPacket();
|
final PacketContainer packet = event.getPacket();
|
||||||
final List<Boolean> booleans = packet.getBooleans().getValues();
|
final List<Boolean> booleans = packet.getBooleans().getValues();
|
||||||
if (booleans.size() != FFData.numBooleans) {
|
if (booleans.size() != FFData.numBooleans) {
|
||||||
cancelRedundant = false;
|
return packetMismatch();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
final boolean onGround = booleans.get(FFData.indexOnGround).booleanValue();
|
final boolean onGround = booleans.get(FFData.indexOnGround).booleanValue();
|
||||||
if (onGround != freq.onGround) {
|
final boolean hasPos = booleans.get(FFData.indexhasPos);
|
||||||
redundant = false;
|
final boolean hasLook = booleans.get(FFData.indexhasLook);
|
||||||
}
|
|
||||||
freq.onGround = onGround;
|
if (hasPos) {
|
||||||
// TODO: Consider to verify on ground somehow.
|
|
||||||
if (booleans.get(FFData.indexhasPos)) {
|
|
||||||
final List<Double> doubles = packet.getDoubles().getValues();
|
final List<Double> doubles = packet.getDoubles().getValues();
|
||||||
if (doubles.size() != freq.doubles.length) {
|
if (doubles.size() != data.doubles.length) {
|
||||||
cancelRedundant = false;
|
return packetMismatch();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < freq.doubles.length; i++) {
|
for (int i = 0; i < data.doubles.length; i++) {
|
||||||
final double val = doubles.get(i).doubleValue();
|
final double val = doubles.get(i).doubleValue();
|
||||||
if (val != freq.doubles[i]) {
|
if (val != data.doubles[i]) {
|
||||||
redundant = false;
|
redundant = false;
|
||||||
freq.doubles[i] = val;
|
data.doubles[i] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (booleans.get(FFData.indexhasLook)) {
|
|
||||||
|
if (hasLook) {
|
||||||
final List<Float> floats = packet.getFloat().getValues();
|
final List<Float> floats = packet.getFloat().getValues();
|
||||||
if (floats.size() != freq.floats.length) {
|
if (floats.size() != data.floats.length) {
|
||||||
cancelRedundant = false;
|
return packetMismatch();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < freq.floats.length; i++) {
|
for (int i = 0; i < data.floats.length; i++) {
|
||||||
final float val = floats.get(i).floatValue();
|
final float val = floats.get(i).floatValue();
|
||||||
if (val != freq.floats[i]) {
|
if (val != data.floats[i]) {
|
||||||
redundant = false;
|
redundant = false;
|
||||||
freq.floats[i] = val;
|
data.floats[i] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Consider to verify on ground somehow (could tell MovingData the state).
|
||||||
|
if (onGround != data.onGround) {
|
||||||
|
// Regard as not redundant only if sending the same state happened at least a second ago.
|
||||||
|
final long time = Monotonic.millis();
|
||||||
|
final long lastTime;
|
||||||
|
if (onGround) {
|
||||||
|
lastTime = data.timeOnGround;
|
||||||
|
data.timeOnGround = time;
|
||||||
|
} else {
|
||||||
|
lastTime = data.timeNotOnGround;
|
||||||
|
data.timeNotOnGround = time;
|
||||||
|
}
|
||||||
|
if (time - lastTime < 1000) {
|
||||||
|
redundant = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.onGround = onGround;
|
||||||
|
|
||||||
if (redundant) {
|
if (redundant) {
|
||||||
// TODO: Could check first bucket or even just 50 ms to last packet.
|
// TODO: Could check first bucket or even just 50 ms to last packet.
|
||||||
if (allScore / cc.flyingFrequencySeconds > 20f) {
|
if (allScore / cc.flyingFrequencySeconds > 20f) {
|
||||||
counters.add(idRedundant, 1);
|
counters.add(idRedundant, 1);
|
||||||
event.setCancelled(true);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log warning to console, halt checking for redundant packets.
|
||||||
|
* @return Always returns false;
|
||||||
|
*/
|
||||||
|
private boolean packetMismatch() {
|
||||||
|
cancelRedundant = false;
|
||||||
|
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().warning(Streams.STATUS, "[NoCheatPlus] Data mismatch: disable cancelling of redundant packets.");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,19 @@ import org.bukkit.World;
|
|||||||
*/
|
*/
|
||||||
public abstract class WorldConfigCache<C> extends ConfigCache<String, C> {
|
public abstract class WorldConfigCache<C> extends ConfigCache<String, C> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param cow If true, copy-on-write is used (thread-safe), otherwise an ordinary HashMap.
|
||||||
|
*/
|
||||||
public WorldConfigCache(boolean cow) {
|
public WorldConfigCache(boolean cow) {
|
||||||
this(cow, 10);
|
this(cow, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param cow If true, copy-on-write is used (thread-safe), otherwise an ordinary HashMap.
|
||||||
|
* @param initialCapacity
|
||||||
|
*/
|
||||||
public WorldConfigCache(boolean cow, int initialCapacity) {
|
public WorldConfigCache(boolean cow, int initialCapacity) {
|
||||||
super(cow, initialCapacity);
|
super(cow, initialCapacity);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,11 @@ package fr.neatmonster.nocheatplus.net;
|
|||||||
import fr.neatmonster.nocheatplus.config.ConfigFile;
|
import fr.neatmonster.nocheatplus.config.ConfigFile;
|
||||||
import fr.neatmonster.nocheatplus.config.WorldConfigCache;
|
import fr.neatmonster.nocheatplus.config.WorldConfigCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy-on-write per-world configuration cache.
|
||||||
|
* @author web4web1
|
||||||
|
*
|
||||||
|
*/
|
||||||
public class NetConfigCache extends WorldConfigCache<NetConfig> {
|
public class NetConfigCache extends WorldConfigCache<NetConfig> {
|
||||||
|
|
||||||
public NetConfigCache() {
|
public NetConfigCache() {
|
||||||
|
Loading…
Reference in New Issue
Block a user