diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/LinkedHashMapCOW.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/LinkedHashMapCOW.java
new file mode 100644
index 00000000..e76beb31
--- /dev/null
+++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/LinkedHashMapCOW.java
@@ -0,0 +1,173 @@
+package fr.neatmonster.nocheatplus.utilities.ds;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * More cows, more fun: Copy on write for a LinkedHashMap (optimized for fast reading from any thread).
+ *
+ * This does not allow access-ordered maps, use Collections.synchronizedMap(LinkedHashMap...) for that case.
+ * @author dev1mc
+ *
+ */
+public class LinkedHashMapCOW implements Map {
+
+ private LinkedHashMap map;
+
+ private final int initialCapacity;
+ private final float loadFactor;
+
+ /**
+ * Uses: 16, 0.75f, false (default settings, insertion ordered).
+ */
+ public LinkedHashMapCOW() {
+ this(16, 0.75f);
+ }
+
+ /**
+ * Uses extra: 0.75f, false (default settings, insertion ordered).
+ * @param initialCapacity
+ */
+ public LinkedHashMapCOW(int initialCapacity) {
+ this(initialCapacity, 0.75f);
+ }
+
+ /**
+ * Uses extra: false (default settings, insertion ordered).
+ * @param initialCapacity
+ * @param loadFactor
+ */
+ public LinkedHashMapCOW(int initialCapacity, float loadFactor) {
+ this.initialCapacity = initialCapacity;
+ this.loadFactor = loadFactor;
+ this.map = new LinkedHashMap(initialCapacity, loadFactor, false);
+ }
+
+ /**
+ * Uses: 16, 0.75f, false (default settings, insertion ordered).
+ * @param map
+ */
+ public LinkedHashMapCOW(Map map) {
+ this();
+ this.map.putAll(map);
+ }
+
+ /**
+ * Not synchronized: return a copy of the internal map.
+ * @return
+ */
+ private LinkedHashMap copyMap() {
+ final LinkedHashMap newMap = new LinkedHashMap(initialCapacity, loadFactor, false);
+ newMap.putAll(this.map);
+ return newMap;
+ }
+
+ @Override
+ public void clear() {
+ synchronized (this) {
+ this.map.clear();
+ }
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return this.map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return this.map.containsValue(value);
+ }
+
+ /**
+ * Unmodifiable version of the EntrySet. Entry.setValue might be possible, but dangerous :p
+ */
+ @Override
+ public Set> entrySet() {
+ return Collections.unmodifiableSet(map.entrySet());
+ }
+
+ @Override
+ public V get(Object key) {
+ // NOTE: If accessOrder can be true, there needs to be synchronization here, defeating any purpose, better use Collections.synchronizedMap(LinkedHashMap...) for that case.
+ return map.get(key);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * Unmodifiable version of the KeySet.
+ */
+ @Override
+ public Set keySet() {
+ return Collections.unmodifiableSet(map.keySet());
+ }
+
+ @Override
+ public V put(final K key, final V value) {
+ final V out;
+ synchronized (this) {
+ final LinkedHashMap newMap = copyMap();
+ out = newMap.put(key, value);
+ this.map = newMap;
+ }
+ return out;
+ }
+
+ @Override
+ public void putAll(final Map extends K, ? extends V> m) {
+ synchronized (this) {
+ final LinkedHashMap newMap = copyMap();
+ newMap.putAll(m);
+ this.map = newMap;
+ }
+ }
+
+ @Override
+ public V remove(final Object key) {
+ final V out;
+ synchronized (this) {
+ final LinkedHashMap newMap = copyMap();
+ out = newMap.remove(key);
+ this.map = newMap;
+ }
+ return out;
+ }
+
+ /**
+ * Remove all given keys.
+ * Not the most efficient implementation, copying the map and then removing
+ * keys, but still better than iterating remove(key).
+ *
+ * @param keys
+ */
+ public void removeAll(final Collection keys) {
+ synchronized (this) {
+ final LinkedHashMap newMap = copyMap();
+ for (final K key : keys) {
+ newMap.remove(key);
+ }
+ this.map = newMap;
+ }
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ /**
+ * Unmodifiable version of the values (Collection).
+ */
+ @Override
+ public Collection values() {
+ return Collections.unmodifiableCollection(map.values());
+ }
+
+}
diff --git a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java
index 36dbcce3..cf7d8856 100644
--- a/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java
+++ b/NCPCompatBukkit/src/main/java/fr/neatmonster/nocheatplus/compat/bukkit/MCAccessBukkit.java
@@ -210,12 +210,6 @@ public class MCAccessBukkit implements MCAccess, BlockPropertiesSetup{
BlockProperties.setBlockFlags(mat, BlockProperties.getBlockFlags(mat) | flags);
}
}
-
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
@Override
public boolean hasGravity(final Material mat) {
diff --git a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java
index aac825cb..872d9352 100644
--- a/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java
+++ b/NCPCompatCB2512/src/main/java/fr/neatmonster/nocheatplus/compat/cb2512/MCAccessCB2512.java
@@ -145,12 +145,6 @@ public class MCAccessCB2512 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
switch(mat){
diff --git a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java
index 2c2dbd5b..4b8a0c8f 100644
--- a/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java
+++ b/NCPCompatCB2545/src/main/java/fr/neatmonster/nocheatplus/compat/cb2545/MCAccessCB2545.java
@@ -145,12 +145,6 @@ public class MCAccessCB2545 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
switch(mat){
diff --git a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java
index 07d9c53d..80566230 100644
--- a/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java
+++ b/NCPCompatCB2602/src/main/java/fr/neatmonster/nocheatplus/compat/cb2602/MCAccessCB2602.java
@@ -146,13 +146,6 @@ public class MCAccessCB2602 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
-
@Override
public boolean hasGravity(final Material mat) {
switch(mat){
diff --git a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java
index 9c8e27dc..52e87ac1 100644
--- a/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java
+++ b/NCPCompatCB2645/src/main/java/fr/neatmonster/nocheatplus/compat/cb2645/MCAccessCB2645.java
@@ -147,12 +147,6 @@ public class MCAccessCB2645 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
switch(mat){
diff --git a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java
index 6988eaf1..b23f176d 100644
--- a/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java
+++ b/NCPCompatCB2691/src/main/java/fr/neatmonster/nocheatplus/compat/cb2691/MCAccessCB2691.java
@@ -149,12 +149,6 @@ public class MCAccessCB2691 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
switch(mat){
diff --git a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java
index 698a48f6..0663ede7 100644
--- a/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java
+++ b/NCPCompatCB2763/src/main/java/fr/neatmonster/nocheatplus/compat/cb2763/MCAccessCB2763.java
@@ -147,12 +147,6 @@ public class MCAccessCB2763 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: TEST: return mat.hasGravity();
diff --git a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java
index de8828d9..3f3a6cbd 100644
--- a/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java
+++ b/NCPCompatCB2794/src/main/java/fr/neatmonster/nocheatplus/compat/cb2794/MCAccessCB2794.java
@@ -147,12 +147,6 @@ public class MCAccessCB2794 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java
index 6569d10d..df47b905 100644
--- a/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java
+++ b/NCPCompatCB2808/src/main/java/fr/neatmonster/nocheatplus/compat/cb2808/MCAccessCB2808.java
@@ -148,12 +148,6 @@ public class MCAccessCB2808 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java
index 4dc894c4..6976ef56 100644
--- a/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java
+++ b/NCPCompatCB2882/src/main/java/fr/neatmonster/nocheatplus/compat/cb2882/MCAccessCB2882.java
@@ -149,12 +149,6 @@ public class MCAccessCB2882 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java
index 9e5499ac..b70c4538 100644
--- a/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java
+++ b/NCPCompatCB2922/src/main/java/fr/neatmonster/nocheatplus/compat/cb2922/MCAccessCB2922.java
@@ -149,12 +149,6 @@ public class MCAccessCB2922 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java
index 21f6ce64..be87b08a 100644
--- a/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java
+++ b/NCPCompatCB3026/src/main/java/fr/neatmonster/nocheatplus/compat/cb3026/MCAccessCB3026.java
@@ -149,12 +149,6 @@ public class MCAccessCB3026 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java
index 945fd318..ad7114b8 100644
--- a/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java
+++ b/NCPCompatCB3043/src/main/java/fr/neatmonster/nocheatplus/compat/cb3043/MCAccessCB3043.java
@@ -168,12 +168,6 @@ public class MCAccessCB3043 implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java
index 97866e30..4be683b7 100644
--- a/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java
+++ b/NCPCompatCBDev/src/main/java/fr/neatmonster/nocheatplus/compat/cbdev/MCAccessCBDev.java
@@ -170,12 +170,6 @@ public class MCAccessCBDev implements MCAccess{
mcPlayer.dead = true;
}
- @Override
- public long getKeepAliveTime(final Player player) {
- // TODO: Implement if possible.
- return Long.MIN_VALUE;
- }
-
@Override
public boolean hasGravity(final Material mat) {
// TODO: Test/check.
diff --git a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/FlyingFrequency.java b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/FlyingFrequency.java
new file mode 100644
index 00000000..99c74038
--- /dev/null
+++ b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/FlyingFrequency.java
@@ -0,0 +1,68 @@
+package fr.neatmonster.nocheatplus.net.protocollib;
+
+import java.util.Map;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.Plugin;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketAdapter;
+import com.comphenix.protocol.events.PacketEvent;
+
+import fr.neatmonster.nocheatplus.components.JoinLeaveListener;
+import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
+import fr.neatmonster.nocheatplus.utilities.ds.LinkedHashMapCOW;
+
+/**
+ * Prevent extremely fast ticking by just sending packets that don't do anything
+ * new and also don't trigger moving events in CraftBukkit.
+ *
+ * @author dev1mc
+ *
+ */
+public class FlyingFrequency extends PacketAdapter implements Listener, JoinLeaveListener {
+
+ // TODO: Configuration.
+ // TODO: Optimized options (receive only, other?).
+ // TODO: Async version ?
+
+ private final Map freqMap = new LinkedHashMapCOW();
+
+ public FlyingFrequency(Plugin plugin) {
+ // PacketPlayInFlying[3, legacy: 10]
+ super(plugin, PacketType.Play.Client.FLYING); // TODO: How does POS and POS_LOOK relate/translate?
+ }
+
+ @Override
+ public void playerJoins(Player player) {
+ getFreq(player.getName());
+ }
+
+ @Override
+ public void playerLeaves(Player player) {
+ freqMap.remove(player.getName());
+ }
+
+ private ActionFrequency getFreq(final String name) {
+ final ActionFrequency freq = this.freqMap.get(name);
+ if (freq != null) {
+ return freq;
+ } else {
+ final ActionFrequency newFreq = new ActionFrequency(5, 1000);
+ this.freqMap.put(name, newFreq);
+ return newFreq;
+ }
+ }
+
+ @Override
+ public void onPacketReceiving(final PacketEvent event) {
+ // TODO: Add several (at least has look + has pos individually, maybe none/onground)
+ final ActionFrequency freq = getFreq(event.getPlayer().getName());
+ freq.add(System.currentTimeMillis(), 1f);
+ if (freq.score(1f) > 300) {
+ event.setCancelled(true);
+ }
+ }
+
+}
diff --git a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/MoveFrequency.java b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/MoveFrequency.java
deleted file mode 100644
index c660242d..00000000
--- a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/MoveFrequency.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package fr.neatmonster.nocheatplus.net.protocollib;
-
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.bukkit.entity.Player;
-import org.bukkit.event.Listener;
-import org.bukkit.plugin.Plugin;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.PacketType.Protocol;
-import com.comphenix.protocol.PacketType.Sender;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketEvent;
-
-import fr.neatmonster.nocheatplus.components.JoinLeaveListener;
-import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
-
-/**
- * Prevent extremely fast ticking by just sending packets that don't do anything
- * new and also don't trigger moving events in CraftBukkit.
- *
- * @author dev1mc
- *
- */
-public class MoveFrequency extends PacketAdapter implements Listener, JoinLeaveListener {
-
- // TODO: Optimized options (receive only, other?).
- // TODO: Async version ?
-
-// private static Collection getPacketTypes() {
-// final Collection packetTypes = PacketType.fromName("C03PacketPlayer");
-// if (packetTypes.isEmpty()) {
-// throw new RuntimeException("Packet types not available.");
-// }
-// return packetTypes;
-// }
-
- private Map freqMap = new LinkedHashMap();
-
- public MoveFrequency(Plugin plugin) {
- // PacketPlayInFlying[3, legacy: 10]
- super(plugin, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, 3)); //getPacketTypes());
- // TODO: Try to get packet by name first + legacy first.
- }
-
- private ActionFrequency addName(String name) {
- Map freqMap = new HashMap(this.freqMap);
- ActionFrequency freq = new ActionFrequency(5, 1000);
- freqMap.put(name, freq);
- this.freqMap = freqMap;
- return freq;
- }
-
- private void removeName(String name) {
- Map freq = new HashMap(this.freqMap);
- freq.remove(name);
- this.freqMap = freq;
- }
-
- @Override
- public void playerJoins(Player player) {
- addName(player.getName()); // Could spare that one.
- }
-
- @Override
- public void playerLeaves(Player player) {
- removeName(player.getName());
- }
-
- private ActionFrequency getFreq(String name) {
- ActionFrequency freq = this.freqMap.get(name);
- if (freq == null) {
- return addName(name);
- } else {
- return freq;
- }
- }
-
- @Override
- public void onPacketReceiving(PacketEvent event) {
- // TODO: Add several (at least has look + has pos individually, maybe none/onground)
- ActionFrequency freq = getFreq(event.getPlayer().getName());
- freq.add(System.currentTimeMillis(), 1f);
- if (freq.score(1f) > 300) {
- event.setCancelled(true);
- }
- }
-
-}
diff --git a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java
index b9269091..e4035df3 100644
--- a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java
+++ b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/ProtocolLibComponent.java
@@ -1,5 +1,7 @@
package fr.neatmonster.nocheatplus.net.protocollib;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
@@ -11,6 +13,7 @@ import com.comphenix.protocol.events.PacketAdapter;
import fr.neatmonster.nocheatplus.components.DisableListener;
import fr.neatmonster.nocheatplus.logging.LogUtil;
+import fr.neatmonster.nocheatplus.utilities.StringUtil;
/**
* Quick and dirty ProtocolLib setup.
@@ -24,14 +27,30 @@ public class ProtocolLibComponent implements DisableListener{
public ProtocolLibComponent(Plugin plugin) {
// Register with ProtocolLib
final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
- LogUtil.logInfo("[NoCheatPlus] ProtocolLib seems to be available.");
- try {
- PacketAdapter adapter = new MoveFrequency(plugin);
- protocolManager.addPacketListener(adapter);
- registeredPacketAdapters.add(adapter);
- } catch (Throwable t) {
- LogUtil.logWarning("[NoCheatPlus] Could not register some packet-level hook.");
- LogUtil.logWarning(t); // TODO: Maybe temporary.
+ LogUtil.logInfo("[NoCheatPlus] ProtocolLib seems to be available, attempt to add packet level hooks...");
+ // Classes having a constructor with Plugin as argument.
+ List> adapterClasses = Arrays.asList(
+ FlyingFrequency.class,
+ SoundDistance.class // Need too: SPAWN_ENTITY_WEATHER, wither/dragon: WORLD_EVENT
+ );
+ // TODO: Configurability (INotifyConfig, method to set up hooks).
+ for (Class extends PacketAdapter> clazz : adapterClasses) {
+ try {
+ // Construct a new instance using reflection.
+ PacketAdapter adapter = clazz.getDeclaredConstructor(Plugin.class).newInstance(plugin);
+ protocolManager.addPacketListener(adapter);
+ registeredPacketAdapters.add(adapter);
+ } catch (Throwable t) {
+ LogUtil.logWarning("[NoCheatPlus] Could not register packet level hook: " + clazz.getSimpleName());
+ LogUtil.logWarning(t); // TODO: Maybe temporary.
+ }
+ }
+ if (!registeredPacketAdapters.isEmpty()) {
+ List names = new ArrayList(registeredPacketAdapters.size());
+ for (PacketAdapter adapter : registeredPacketAdapters) {
+ names.add(adapter.getClass().getSimpleName());
+ }
+ LogUtil.logInfo("[NoCheatPlus] Available packet level hooks: " + StringUtil.join(names, " | "));
}
}
@@ -42,13 +61,10 @@ public class ProtocolLibComponent implements DisableListener{
try {
protocolManager.removePacketListener(adapter);
} catch (Throwable t) {
- LogUtil.logWarning("[NoCheatPlus] Failed to unregister protocol listener: " + adapter.getClass().getName());
+ LogUtil.logWarning("[NoCheatPlus] Failed to unregister packet level hook: " + adapter.getClass().getName());
}
}
+ registeredPacketAdapters.clear();
}
-
-
-
-
}
diff --git a/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/SoundDistance.java b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/SoundDistance.java
new file mode 100644
index 00000000..3c2b585c
--- /dev/null
+++ b/NCPCompatProtocolLib/src/main/java/fr/neatmonster/nocheatplus/net/protocollib/SoundDistance.java
@@ -0,0 +1,62 @@
+package fr.neatmonster.nocheatplus.net.protocollib;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketAdapter;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.events.PacketEvent;
+import com.comphenix.protocol.reflect.StructureModifier;
+
+import fr.neatmonster.nocheatplus.utilities.TrigUtil;
+
+public class SoundDistance extends PacketAdapter {
+
+ // TODO: Will not be effective with 512 radius, if they add the patch by @Amranth.
+ // TODO: For lower distances more packets might need to be intercepted.
+
+ /** Maximum distance for thunder effects (squared). */
+ private static final double distSq = 512.0 * 512.0; // TODO: Maybe configurable.
+
+ private static final String[] effectNames = new String[] { // Prefix tree?
+ "ambient.weather.thunder",
+ "wither-spawn-sound-radius",
+ "dragon-death-sound-radius"
+ // other ?
+ };
+
+ private static final boolean contains(final String ref) {
+ for (int i = 0; i < effectNames.length; i++) {
+ if (effectNames[i].equals(ref)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public SoundDistance(Plugin plugin) {
+ super(plugin, PacketType.Play.Server.NAMED_SOUND_EFFECT);
+ }
+
+ @Override
+ public void onPacketSending(final PacketEvent event) {
+ final PacketContainer packetContainer = event.getPacket();
+ final Player player = event.getPlayer();
+
+ // Compare sound effect name.
+ if (!contains(packetContainer.getStrings().read(0))) {
+ return;
+ }
+
+ final Location loc = player.getLocation(); // TODO: Use getLocation(useLoc) [synced if async].
+
+ // Compare distance of player to the weather location.
+ final StructureModifier ints = packetContainer.getIntegers();
+ if (TrigUtil.distanceSquared(ints.read(0) / 8, ints.read(2) / 8, loc.getX(), loc.getZ()) > distSq) {
+ event.setCancelled(true);
+ }
+ }
+
+}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java
index 04062b00..a546164a 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/ChatConfig.java
@@ -54,7 +54,7 @@ public class ChatConfig extends AsyncCheckConfig {
synchronized (worldsMap) {
if (!worldsMap.containsKey(player.getWorld().getName()))
worldsMap.put(player.getWorld().getName(),
- new ChatConfig(ConfigManager.getConfigFileSync(player.getWorld().getName())));
+ new ChatConfig(ConfigManager.getConfigFile(player.getWorld().getName())));
return worldsMap.get(player.getWorld().getName());
}
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java
index 5c2428df..66217c38 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/GodMode.java
@@ -128,8 +128,9 @@ public class GodMode extends Check {
// Check for client side lag.
final long now = System.currentTimeMillis();
final long maxAge = cc.godModeLagMaxAge;
- long keepAlive = mcAccess.getKeepAliveTime(player);
- if (keepAlive > now || keepAlive == Long.MIN_VALUE){
+ long keepAlive = Long.MIN_VALUE;
+ // TODO: Get keepAlive from NetData, if available.
+ if (keepAlive > now || keepAlive == Long.MIN_VALUE) {
keepAlive = CheckUtils.guessKeepAliveTime(player, now, maxAge);
}
// TODO: else: still check the other time stamp ?
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java
index 838b6c33..92fa67f8 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/inventory/InventoryListener.java
@@ -224,7 +224,6 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen
boolean resetAll = false;
-
if (event.hasItem()){
final ItemStack item = event.getItem();
final Material type = item.getType();
@@ -234,7 +233,7 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen
// It was a bow, the player starts to pull the string, remember this time.
data.instantBowInteract = (data.instantBowInteract > 0 && now - data.instantBowInteract < 800) ? Math.min(System.currentTimeMillis(), data.instantBowInteract) : System.currentTimeMillis();
}
- else if (type.isEdible() || type == Material.POTION) {
+ else if (type.isEdible() || type == Material.POTION || type == Material.MILK_BUCKET) {
final long now = System.currentTimeMillis();
// It was food, the player starts to eat some food, remember this time and the type of food.
data.instantEatFood = type;
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java
index 2b76ac1a..60164b30 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java
@@ -1,7 +1,5 @@
package fr.neatmonster.nocheatplus.checks.moving;
-import java.util.Map;
-
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -9,25 +7,16 @@ import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
+import fr.neatmonster.nocheatplus.net.NetStatic;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/**
- * 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
* 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 {
-
- /**
- * The usual number of packets per timeframe.
- *
- * 20 would be for perfect internet connections, 22 is good enough.
- */
- private final static int packetsPerTimeframe = 22;
-
+
/**
* Instantiates a new more packets check.
*/
@@ -35,31 +24,33 @@ public class MorePackets extends Check {
super(CheckType.MOVING_MOREPACKETS);
}
- /**
- * Checks a player.
- *
- * Players get assigned a certain amount of "free" packets as a limit initially. Every move packet reduces that
- * limit by 1. If more than 1 second of time passed, the limit gets increased by 22 * time in seconds, up to 50 and
- * 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
- * the player
- * @param from
- * the from
- * @param to
- * the to
- * @return the location
- */
+ /**
+ * Check for speeding by sending too many packets. We assume 22 packets per
+ * second to be legitimate, while 20 would be ideal. See
+ * PlayerData.morePacketsFreq for the monitored amount of time and the
+ * resolution. See NetStatic for the actual check code.
+ *
+ * @param player
+ * the player
+ * @param from
+ * the from
+ * @param to
+ * the to
+ * @return the location
+ */
public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc) {
// Take time once, first:
final long time = System.currentTimeMillis();
-
- Location newTo = null;
-
+// 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()){
- // 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()) {
data.setMorePacketsSetBack(data.getSetBack(to));
}
@@ -67,66 +58,35 @@ public class MorePackets extends Check {
data.setMorePacketsSetBack(from);
}
}
-
- // Take a packet from the buffer.
- data.morePacketsBuffer--;
-
- // Player used up buffer, they fail the check.
- if (data.morePacketsBuffer < 0) {
- data.morePacketsPackets = -data.morePacketsBuffer;
-
+
+ // Check for a violation of the set limits.
+ final double violation = NetStatic.morePacketsCheck(data.morePacketsFreq, time, 1f, cc.morePacketsEPSMax, cc.morePacketsEPSIdeal, data.morePacketsBurstFreq, cc.morePacketsBurstPackets, cc.morePacketsBurstDirect, cc.morePacketsBurstEPM);
+
+ // Process violation result.
+ if (violation > 0.0) {
+
// Increment violation level.
- data.morePacketsVL = -data.morePacketsBuffer;
-
- // Execute whatever actions are associated with this check and the violation level and find out if we should
- // cancel the event.
- if (executeActions(player, data.morePacketsVL, -data.morePacketsBuffer, MovingConfig.getConfig(player).morePacketsActions)){
- newTo = data.getMorePacketsSetBack();
+ data.morePacketsVL = violation; // TODO: Accumulate somehow [e.g. always += 1, decrease with continuous moving without violation]?
+
+ // Violation handling.
+ final ViolationData vd = new ViolationData(this, player, data.morePacketsVL, violation, cc.morePacketsActions);
+ if (cc.debug || vd.needsParameters()) {
+ vd.setParameter(ParameterName.PACKETS, Integer.toString(new Double(violation).intValue()));
}
-
+ if (executeActions(vd)) {
+ // Set to cancel the move.
+ return data.getMorePacketsSetBack();
+ }
+ }
+ else {
+ // Update the set-back location. (CHANGED to only update, if not a violation.)
+ // (Might update whenever newTo == null)
+ data.setMorePacketsSetBack(from);
}
-
- if (data.morePacketsLastTime + 1000 < time) {
- // More than 1 second elapsed, but how many?
- final double seconds = (time - data.morePacketsLastTime) / 1000D;
-
- // For each second, fill the buffer.
- data.morePacketsBuffer += packetsPerTimeframe * seconds;
-
- // If there was a long pause (maybe server lag?), allow buffer to grow up to 100.
- if (seconds > 2) {
- if (data.morePacketsBuffer > 100) {
- data.morePacketsBuffer = 100;
- }
- } else if (data.morePacketsBuffer > 50) {
- // Only allow growth up to 50.
- data.morePacketsBuffer = 50;
- }
- // Set the new "last" time.
- data.morePacketsLastTime = time;
-
- // Set the new "setback" location.
- if (newTo == null) {
- data.setMorePacketsSetBack(from);
- }
- } else if (data.morePacketsLastTime > time) {
- // Security check, maybe system time changed.
- data.morePacketsLastTime = time;
- }
-
- if (newTo == null) {
- return null;
- }
-
- // Compose a new location based on coordinates of "newTo" and viewing direction of "event.getTo()" to allow the
- // player to look somewhere else despite getting pulled back by NoCheatPlus.
- return new Location(player.getWorld(), newTo.getX(), newTo.getY(), newTo.getZ(), to.getYaw(), to.getPitch());
+
+ // No set-back.
+ return null;
+
}
- @Override
- protected Map getParameterMap(final ViolationData violationData) {
- final Map parameters = super.getParameterMap(violationData);
- parameters.put(ParameterName.PACKETS, String.valueOf(MovingData.getData(violationData.player).morePacketsPackets));
- return parameters;
- }
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java
index 2128a95f..b80178b1 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java
@@ -1,7 +1,5 @@
package fr.neatmonster.nocheatplus.checks.moving;
-import java.util.Map;
-
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -70,14 +68,17 @@ public class MorePacketsVehicle extends Check {
// Player used up buffer, they fail the check.
if (data.morePacketsVehicleBuffer < 0) {
- data.morePacketsVehiclePackets = -data.morePacketsVehicleBuffer;
// Increment violation level.
data.morePacketsVehicleVL = -data.morePacketsVehicleBuffer;
// Execute whatever actions are associated with this check and the violation level and find out if we should
// cancel the event.
- if (executeActions(player, data.morePacketsVehicleVL, -data.morePacketsVehicleBuffer, cc.morePacketsVehicleActions)){
+ final ViolationData vd = new ViolationData(this, player, data.morePacketsVehicleVL, -data.morePacketsVehicleBuffer, cc.morePacketsVehicleActions);
+ if (cc.debug || vd.needsParameters()) {
+ vd.setParameter(ParameterName.PACKETS, Integer.toString(-data.morePacketsVehicleBuffer));
+ }
+ if (executeActions(vd)){
newTo = data.getMorePacketsVehicleSetBack();
}
}
@@ -91,34 +92,33 @@ public class MorePacketsVehicle extends Check {
// If there was a long pause (maybe server lag?), allow buffer to grow up to 100.
if (seconds > 2) {
- if (data.morePacketsVehicleBuffer > 100)
- data.morePacketsVehicleBuffer = 100;
- } else if (data.morePacketsVehicleBuffer > 50)
- // Only allow growth up to 50.
+ if (data.morePacketsVehicleBuffer > 100) {
+ data.morePacketsVehicleBuffer = 100;
+ }
+ } else if (data.morePacketsVehicleBuffer > 50) {
+ // Only allow growth up to 50.
data.morePacketsVehicleBuffer = 50;
-
+ }
+
// Set the new "last" time.
data.morePacketsVehicleLastTime = time;
// Set the new "setback" location.
- if (newTo == null)
- data.setMorePacketsVehicleSetBack(from);
- } else if (data.morePacketsVehicleLastTime > time)
- // Security check, maybe system time changed.
+ if (newTo == null) {
+ data.setMorePacketsVehicleSetBack(from);
+ }
+ } else if (data.morePacketsVehicleLastTime > time) {
+ // Security check, maybe system time changed.
data.morePacketsVehicleLastTime = time;
+ }
- if (newTo == null)
- return null;
+ if (newTo == null) {
+ return null;
+ }
// Compose a new location based on coordinates of "newTo" and viewing direction of "event.getTo()" to allow the
// player to look somewhere else despite getting pulled back by NoCheatPlus.
return new Location(player.getWorld(), newTo.getX(), newTo.getY(), newTo.getZ(), to.getYaw(), to.getPitch());
}
- @Override
- protected Map getParameterMap(final ViolationData violationData) {
- final Map parameters = super.getParameterMap(violationData);
- parameters.put(ParameterName.PACKETS, String.valueOf(MovingData.getData(violationData.player).morePacketsVehiclePackets));
- return parameters;
- }
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java
index 65b52b4d..b627aef3 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java
@@ -76,6 +76,13 @@ public class MovingConfig extends ACheckConfig {
public final ActionList creativeFlyActions;
public final boolean morePacketsCheck;
+ /** Assumed number of packets per second under ideal conditions. */
+ public final float morePacketsEPSIdeal;
+ /** The maximum number of packets per second that we accept. */
+ public final float morePacketsEPSMax;
+ public final float morePacketsBurstPackets;
+ public final double morePacketsBurstDirect;
+ public final double morePacketsBurstEPM;
public final ActionList morePacketsActions;
public final boolean morePacketsVehicleCheck;
@@ -167,6 +174,11 @@ public class MovingConfig extends ACheckConfig {
creativeFlyActions = config.getOptimizedActionList(ConfPaths.MOVING_CREATIVEFLY_ACTIONS, Permissions.MOVING_CREATIVEFLY);
morePacketsCheck = config.getBoolean(ConfPaths.MOVING_MOREPACKETS_CHECK);
+ morePacketsEPSIdeal = config.getInt(ConfPaths.MOVING_MOREPACKETS_EPSIDEAL);
+ morePacketsEPSMax = Math.max(morePacketsEPSIdeal, config.getInt(ConfPaths.MOVING_MOREPACKETS_EPSMAX));
+ morePacketsBurstPackets = config.getInt(ConfPaths.MOVING_MOREPACKETS_BURST_EPM);
+ morePacketsBurstDirect = config.getInt(ConfPaths.MOVING_MOREPACKETS_BURST_DIRECT);
+ morePacketsBurstEPM = config.getInt(ConfPaths.MOVING_MOREPACKETS_BURST_EPM);
morePacketsActions = config.getOptimizedActionList(ConfPaths.MOVING_MOREPACKETS_ACTIONS, Permissions.MOVING_MOREPACKETS);
morePacketsVehicleCheck = config.getBoolean(ConfPaths.MOVING_MOREPACKETSVEHICLE_CHECK);
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java
index 8b51844e..eb2aefa3 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java
@@ -14,6 +14,7 @@ import fr.neatmonster.nocheatplus.checks.access.ACheckData;
import fr.neatmonster.nocheatplus.checks.access.CheckDataFactory;
import fr.neatmonster.nocheatplus.checks.access.ICheckData;
import fr.neatmonster.nocheatplus.utilities.ActionAccumulator;
+import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/**
@@ -122,7 +123,7 @@ public class MovingData extends ACheckData {
/** Active velocity entries (horizontal distance). */
public final List hVelActive = new LinkedList();
/** Queued velocity entries (horizontal distance). */
- public final List hVelQueued = new LinkedList();
+ public final List hVelQueued = new LinkedList();
// Coordinates.
/** Last from coordinates. */
@@ -147,15 +148,15 @@ public class MovingData extends ACheckData {
public boolean creativeFlyPreviousRefused;
// Data of the more packets check.
- public int morePacketsBuffer = 50;
- public long morePacketsLastTime;
- public int morePacketsPackets;
+ /** Packet frequency count. */
+ public final ActionFrequency morePacketsFreq = new ActionFrequency(10, 500);
+ /** Burst count. */
+ public final ActionFrequency morePacketsBurstFreq = new ActionFrequency(12, 5000);
private Location morePacketsSetback = null;
// Data of the more packets vehicle check.
public int morePacketsVehicleBuffer = 50;
public long morePacketsVehicleLastTime;
- public int morePacketsVehiclePackets;
private Location morePacketsVehicleSetback = null;
/** Task id of the morepackets set-back task. */
public int morePacketsVehicleTaskId = -1;
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java
index cd5e5f6d..ca1cc8e8 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java
@@ -601,9 +601,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
}
// 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 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.
newTo = morePackets.check(player, pFrom, pTo, data, cc);
} else {
// Otherwise we need to clear their data.
@@ -1003,7 +1003,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
double newVal = velocity.getY();
boolean used = false;
- if (newVal >= 0D) {
+ if (newVal >= 0D) { // TODO: Just >, not >=.
used = true;
if (data.verticalFreedom <= 0.001 && data.verticalVelocityCounter >= 0) {
data.verticalVelocity = 0;
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java
index 22e35e54..6f5e5398 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java
@@ -534,12 +534,6 @@ public class SurvivalFly extends Check {
else {
hAllowedDistance = walkSpeed * modSprint * cc.survivalFlySprintingSpeed / 100D;
}
-
- // Speeding bypass permission (can be combined with other bypasses).
- // TODO: How exactly to bring it on finally.
- if (checkPermissions && player.hasPermission(Permissions.MOVING_SURVIVALFLY_SPEEDING)) {
- hAllowedDistance *= cc.survivalFlySpeedingSpeed / 100D;
- }
}
// Account for flowing liquids (only if needed).
@@ -550,6 +544,7 @@ public class SurvivalFly extends Check {
}
// Short cut.
+ // TODO: Check if a) early return makes sense and b) do it ofr all following parts.
if (hDistance <= hAllowedDistance && !cc.debug) {
// Shortcut for debug disabled.
return hAllowedDistance;
@@ -566,6 +561,11 @@ public class SurvivalFly extends Check {
hAllowedDistance *= 1.0D + 0.2D * (speedAmplifier + 1);
}
+ // Speeding bypass permission (can be combined with other bypasses).
+ if (checkPermissions && player.hasPermission(Permissions.MOVING_SURVIVALFLY_SPEEDING)) {
+ hAllowedDistance *= cc.survivalFlySpeedingSpeed / 100D;
+ }
+
return hAllowedDistance;
}
@@ -999,6 +999,7 @@ public class SurvivalFly extends Check {
// if (vDistanceAboveLimit > 0) tags.add("vclimb");
final double jumpHeight = 1.35 + (data.jumpAmplifier > 0 ? (0.6 + data.jumpAmplifier - 1.0) : 0.0);
// TODO: ladders are ground !
+ // TODO: yDistance < 0.0 ?
if (yDistance > climbSpeed && !from.isOnGround(jumpHeight, 0D, 0D, BlockProperties.F_CLIMBABLE)) {
// Ignore ladders. TODO: Check for false positives...
tags.add("climbspeed");
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java
index b6b3cf2b..16ecf811 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/MCAccess.java
@@ -122,13 +122,6 @@ public interface MCAccess {
*/
public void setDead(Player player, int deathTicks);
- /**
- * Get timestamp of the keep-alive field (not only updated on keep-alive packets).
- * @param player The player for which to get the time.
- * @return Long.MIN_VALUE if not possible.
- */
- public long getKeepAliveTime(Player player);
-
/**
* Usually sand and gravel. Not for fastest access.
* @param type
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java
index 58440013..68c4a41a 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java
@@ -503,6 +503,12 @@ public abstract class ConfPaths {
private static final String MOVING_MOREPACKETS = MOVING + "morepackets.";
public static final String MOVING_MOREPACKETS_CHECK = MOVING_MOREPACKETS + "active";
+ public static final String MOVING_MOREPACKETS_EPSIDEAL = MOVING_MOREPACKETS + "epsideal";
+ public static final String MOVING_MOREPACKETS_EPSMAX = MOVING_MOREPACKETS + "epsmax";
+ private static final String MOVING_MOREPACKETS_BURST = MOVING_MOREPACKETS + "burst.";
+ public static final String MOVING_MOREPACKETS_BURST_PACKETS = MOVING_MOREPACKETS_BURST + "packets";
+ public static final String MOVING_MOREPACKETS_BURST_DIRECT = MOVING_MOREPACKETS_BURST + "directviolation";
+ public static final String MOVING_MOREPACKETS_BURST_EPM = MOVING_MOREPACKETS_BURST + "epmviolation";
public static final String MOVING_MOREPACKETS_ACTIONS = MOVING_MOREPACKETS + "actions";
private static final String MOVING_MOREPACKETSVEHICLE = MOVING + "morepacketsvehicle.";
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java
index 5cdd0dff..92340161 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfigManager.java
@@ -3,6 +3,7 @@ package fr.neatmonster.nocheatplus.config;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -30,7 +31,7 @@ public class ConfigManager {
};
/** The map containing the configuration files per world. */
- private static final Map worldsMap = new HashMap();
+ private static Map worldsMap = new LinkedHashMap();
private static final WorldConfigProvider worldConfigProvider = new WorldConfigProvider() {
@@ -118,7 +119,7 @@ public class ConfigManager {
}
/**
- * Gets the configuration file.
+ * Gets the configuration file. Can be called from any thread.
*
* @return the configuration file
*/
@@ -129,13 +130,15 @@ public class ConfigManager {
/**
* (Synchronized version).
* @return
+ * @deprecated getConfigFile() is thread-safe now.
*/
+ @Deprecated
public static synchronized ConfigFile getConfigFileSync() {
return getConfigFile();
}
/**
- * Gets the configuration file.
+ * Gets the configuration file. Can be called from any thread.
*
* @param worldName
* the world name
@@ -146,14 +149,17 @@ public class ConfigManager {
if (configFile != null){
return configFile;
}
- // Expensive only once, for the rest of runtime the file is returned fast.
+ // Expensive only once per world, for the rest of the runtime the file is returned fast.
synchronized(ConfigManager.class){
// Need to check again.
if (worldsMap.containsKey(worldName)){
return worldsMap.get(worldName);
}
- final ConfigFile globalConfig = getConfigFile();
- worldsMap.put(worldName, globalConfig);
+ // Copy the whole map with the default configuration set for this world.
+ final Map newWorldsMap = new LinkedHashMap(ConfigManager.worldsMap);
+ final ConfigFile globalConfig = newWorldsMap.get(null);
+ newWorldsMap.put(worldName, globalConfig);
+ ConfigManager.worldsMap = newWorldsMap;
return globalConfig;
}
}
@@ -162,13 +168,15 @@ public class ConfigManager {
* (Synchronized version).
* @param worldName
* @return
+ * @deprecated getConfigFile() is thread-safe now.
*/
+ @Deprecated
public static synchronized ConfigFile getConfigFileSync(final String worldName) {
return getConfigFile(worldName);
}
/**
- * Initializes the configuration manager.
+ * Initializes the configuration manager. Must be called in the main thread.
*
* @param plugin
* the instance of NoCheatPlus
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java
index f54adfbb..a854bea8 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java
@@ -355,6 +355,11 @@ public class DefaultConfig extends ConfigFile {
"log:flyshort:3:5:f cancel vl>100 log:flyshort:0:5:if cancel vl>400 log:flylong:0:5:cif cancel");
set(ConfPaths.MOVING_MOREPACKETS_CHECK, true);
+ set(ConfPaths.MOVING_MOREPACKETS_EPSIDEAL, 20);
+ set(ConfPaths.MOVING_MOREPACKETS_EPSMAX, 22);
+ set(ConfPaths.MOVING_MOREPACKETS_BURST_PACKETS, 40);
+ set(ConfPaths.MOVING_MOREPACKETS_BURST_DIRECT, 30);
+ set(ConfPaths.MOVING_MOREPACKETS_BURST_EPM, 120);
set(ConfPaths.MOVING_MOREPACKETS_ACTIONS, "cancel vl>10 log:morepackets:0:2:if cancel vl>100 log:morepackets:0:2:if cancel cmd:kickpackets");
set(ConfPaths.MOVING_MOREPACKETSVEHICLE_CHECK, true);
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/net/NetStatic.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/net/NetStatic.java
new file mode 100644
index 00000000..540d9213
--- /dev/null
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/net/NetStatic.java
@@ -0,0 +1,101 @@
+package fr.neatmonster.nocheatplus.net;
+
+import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
+import fr.neatmonster.nocheatplus.utilities.TickTask;
+
+/**
+ * Static method utility for networking related stuff.
+ *
+ * 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.
+ * @param burstFreq Counting burst events, should be covering a minute or so.
+ * @param burstPackets Packets in the first time window to add to burst count.
+ * @param burstEPM Events per minute to trigger a burst violation.
+ * @return The violation amount, i.e. "count above limit", 0.0 if no violation.
+ */
+ public static double morePacketsCheck(final ActionFrequency packetFreq, final long time, final float packets, final float maxPackets, final float idealPackets, final ActionFrequency burstFreq, final float burstPackets, final double burstDirect, final double burstEPM) {
+ // Pull down stuff.
+ final long winDur = packetFreq.bucketDuration();
+ final int winNum = packetFreq.numberOfBuckets();
+ // Add packet to frequency count.
+ packetFreq.add(time, packets);
+ // TODO: Add a per-bucket violation AF (once first bucket > thresh => pbvAF.add(now, 1f) + if total score > xyz => extra violation).
+
+ // 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) {
+ // TODO: burnStart ++; Fill up all ? ~ e.g. what with filled up half?
+ 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.
+ if (empty > 0) {
+ // 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.
+ // TODO: empty score + trailing score !? max with trailing buckets * ideal (!)
+ 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);
+ }
+
+ double violation = (double) fullCount - (double) (maxPackets * winNum * winDur / 1000f);
+ final float burst = packetFreq.bucketScore(0);
+ if (burst > burstPackets) {
+ // TODO: Account for "just now lag", i.e. time until first occupied one > 0.
+ burstFreq.add(time, 1f); // TODO: Remove float packets or do this properly.
+ violation = Math.max(violation, burst - burstDirect);
+ violation = Math.max(violation, burstEPM * (double) (burstFreq.bucketDuration() * burstFreq.numberOfBuckets()) / 60000.0 - (double) burstFreq.score(0f));
+ }
+ return Math.max(0.0, violation);
+ }
+
+}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java
index 53eba0a0..9cf8fb75 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/TrigUtil.java
@@ -280,6 +280,12 @@ public class TrigUtil {
final double dz = Math.abs(z1 - z2);
return dx * dx + dy * dy + dz * dz;
}
+
+ public static final double distanceSquared(final double x1, final double z1, final double x2, final double z2) {
+ final double dx = Math.abs(x1 - x2);
+ final double dz = Math.abs(z1 - z2);
+ return dx * dx + dz * dz;
+ }
/**
* 2D-distance in x-z plane.