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 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 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.