Merge pull request #2 from NoCheatPlus/master

Downstream merge
This commit is contained in:
MyPictures 2014-07-26 04:04:32 +02:00
commit d5c7bb7d27
34 changed files with 569 additions and 333 deletions

View File

@ -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).
* <hr>
* This does not allow access-ordered maps, use Collections.synchronizedMap(LinkedHashMap...) for that case.
* @author dev1mc
*
*/
public class LinkedHashMapCOW<K, V> implements Map<K, V> {
private LinkedHashMap<K, V> 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<K, V>(initialCapacity, loadFactor, false);
}
/**
* Uses: 16, 0.75f, false (default settings, insertion ordered).
* @param map
*/
public LinkedHashMapCOW(Map<K, V> map) {
this();
this.map.putAll(map);
}
/**
* Not synchronized: return a copy of the internal map.
* @return
*/
private LinkedHashMap<K, V> copyMap() {
final LinkedHashMap<K, V> newMap = new LinkedHashMap<K, V>(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<java.util.Map.Entry<K, V>> 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<K> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
@Override
public V put(final K key, final V value) {
final V out;
synchronized (this) {
final LinkedHashMap<K, V> 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<K, V> newMap = copyMap();
newMap.putAll(m);
this.map = newMap;
}
}
@Override
public V remove(final Object key) {
final V out;
synchronized (this) {
final LinkedHashMap<K, V> newMap = copyMap();
out = newMap.remove(key);
this.map = newMap;
}
return out;
}
/**
* Remove all given keys.<br>
* 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<K> keys) {
synchronized (this) {
final LinkedHashMap<K, V> 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<V> values() {
return Collections.unmodifiableCollection(map.values());
}
}

View File

@ -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) {

View File

@ -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){

View File

@ -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){

View File

@ -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){

View File

@ -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){

View File

@ -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){

View File

@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String, ActionFrequency> freqMap = new LinkedHashMapCOW<String, ActionFrequency>();
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);
}
}
}

View File

@ -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<PacketType> getPacketTypes() {
// final Collection<PacketType> packetTypes = PacketType.fromName("C03PacketPlayer");
// if (packetTypes.isEmpty()) {
// throw new RuntimeException("Packet types not available.");
// }
// return packetTypes;
// }
private Map<String, ActionFrequency> freqMap = new LinkedHashMap<String, ActionFrequency>();
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<String, ActionFrequency> freqMap = new HashMap<String, ActionFrequency>(this.freqMap);
ActionFrequency freq = new ActionFrequency(5, 1000);
freqMap.put(name, freq);
this.freqMap = freqMap;
return freq;
}
private void removeName(String name) {
Map<String, ActionFrequency> freq = new HashMap<String, ActionFrequency>(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);
}
}
}

View File

@ -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<Class<? extends PacketAdapter>> 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<String> names = new ArrayList<String>(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();
}
}

View File

@ -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<Integer> ints = packetContainer.getIntegers();
if (TrigUtil.distanceSquared(ints.read(0) / 8, ints.read(2) / 8, loc.getX(), loc.getZ()) > distSq) {
event.setCancelled(true);
}
}
}

View File

@ -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());
}
}

View File

@ -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 ?

View File

@ -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;

View File

@ -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<ParameterName, String> getParameterMap(final ViolationData violationData) {
final Map<ParameterName, String> parameters = super.getParameterMap(violationData);
parameters.put(ParameterName.PACKETS, String.valueOf(MovingData.getData(violationData.player).morePacketsPackets));
return parameters;
}
}

View File

@ -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<ParameterName, String> getParameterMap(final ViolationData violationData) {
final Map<ParameterName, String> parameters = super.getParameterMap(violationData);
parameters.put(ParameterName.PACKETS, String.valueOf(MovingData.getData(violationData.player).morePacketsVehiclePackets));
return parameters;
}
}

View File

@ -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);

View File

@ -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<Velocity> hVelActive = new LinkedList<Velocity>();
/** Queued velocity entries (horizontal distance). */
public final List<Velocity> hVelQueued = new LinkedList<Velocity>();
public final List<Velocity> hVelQueued = new LinkedList<Velocity>();
// 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;

View File

@ -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;

View File

@ -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");

View File

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

View File

@ -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.";

View File

@ -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<String, ConfigFile> worldsMap = new HashMap<String, ConfigFile>();
private static Map<String, ConfigFile> worldsMap = new LinkedHashMap<String, ConfigFile>();
private static final WorldConfigProvider<ConfigFile> worldConfigProvider = new WorldConfigProvider<ConfigFile>() {
@ -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<String, ConfigFile> newWorldsMap = new LinkedHashMap<String, ConfigFile>(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

View File

@ -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);

View File

@ -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.
* <hr>
* Not sure about final location and naming... and content :p.
* @author dev1mc
*
*/
public class NetStatic {
/**
* Packet-cheating check, for catching clients that send more packets than
* allowed. Intention is to have a more accurate check than just preventing
* "extreme spamming".
*
* @param packetFreq
* Records the packets. This check will update packetFreq
* according to the given time and packets.
* @param time
* Milliseconds time to update the ActionFrequency instance with.
* @param packets
* Amount to add to packetFreq with time.
* @param maxPackets
* The amount of packets per second (!), that is considered
* legitimate.
* @param idealPackets
* The "ideal" amount of packets per second. Used for "burning"
* time frames by setting them to this amount.
* @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);
}
}

View File

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