diff --git a/src/fr/neatmonster/nocheatplus/actions/ParameterName.java b/src/fr/neatmonster/nocheatplus/actions/ParameterName.java
index f546d625..01f78a59 100644
--- a/src/fr/neatmonster/nocheatplus/actions/ParameterName.java
+++ b/src/fr/neatmonster/nocheatplus/actions/ParameterName.java
@@ -21,8 +21,9 @@ package fr.neatmonster.nocheatplus.actions;
* Some wildcards that are used in commands and log messages.
*/
public enum ParameterName {
- CHECK("check"),
BLOCK_ID("blockid"),
+ CHECK("check"),
+ TAGS("tags"),
DISTANCE("distance"),
FALL_DISTANCE("falldistance"),
FOOD("food"),
diff --git a/src/fr/neatmonster/nocheatplus/checks/CheckType.java b/src/fr/neatmonster/nocheatplus/checks/CheckType.java
index e51e170f..d67dadcf 100644
--- a/src/fr/neatmonster/nocheatplus/checks/CheckType.java
+++ b/src/fr/neatmonster/nocheatplus/checks/CheckType.java
@@ -1,8 +1,5 @@
package fr.neatmonster.nocheatplus.checks;
-import java.util.HashSet;
-import java.util.Set;
-
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.access.CheckConfigFactory;
@@ -23,7 +20,7 @@ import fr.neatmonster.nocheatplus.checks.inventory.InventoryConfig;
import fr.neatmonster.nocheatplus.checks.inventory.InventoryData;
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
-import fr.neatmonster.nocheatplus.hooks.APIUtils;
+import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.Permissions;
/*
@@ -40,9 +37,9 @@ import fr.neatmonster.nocheatplus.players.Permissions;
* Type of checks (containing configuration and dataFactory classes, name and permission).
*/
public enum CheckType {
- ALL,
+ ALL(Permissions.CHECKS),
- BLOCKBREAK(BlockBreakConfig.factory, BlockBreakData.factory),
+ BLOCKBREAK(BlockBreakConfig.factory, BlockBreakData.factory, Permissions.BLOCKBREAK),
BLOCKBREAK_DIRECTION(BLOCKBREAK, Permissions.BLOCKBREAK_DIRECTION),
BLOCKBREAK_FASTBREAK(BLOCKBREAK, Permissions.BLOCKBREAK_FASTBREAK),
BLOCKBREAK_FREQUENCY(BLOCKBREAK, Permissions.BLOCKBREAK_FREQUENCY),
@@ -50,18 +47,18 @@ public enum CheckType {
BLOCKBREAK_REACH(BLOCKBREAK, Permissions.BLOCKBREAK_REACH),
BLOCKBREAK_WRONGBLOCK(BLOCKBREAK, Permissions.BLOCKBREAK_WRONGBLOCK),
- BLOCKINTERACT(BlockInteractConfig.factory, BlockInteractData.factory),
+ BLOCKINTERACT(BlockInteractConfig.factory, BlockInteractData.factory, Permissions.BLOCKINTERACT),
BLOCKINTERACT_DIRECTION(BLOCKINTERACT, Permissions.BLOCKINTERACT_DIRECTION),
BLOCKINTERACT_REACH(BLOCKINTERACT, Permissions.BLOCKINTERACT_REACH),
- BLOCKPLACE(BlockPlaceConfig.factory, BlockPlaceData.factory),
+ BLOCKPLACE(BlockPlaceConfig.factory, BlockPlaceData.factory, Permissions.BLOCKPLACE),
BLOCKPLACE_DIRECTION(BLOCKPLACE, Permissions.BLOCKPLACE_DIRECTION),
BLOCKPLACE_FASTPLACE(BLOCKPLACE, Permissions.BLOCKPLACE_FASTPLACE),
BLOCKPLACE_NOSWING(BLOCKPLACE, Permissions.BLOCKPLACE_NOSWING),
BLOCKPLACE_REACH(BLOCKPLACE, Permissions.BLOCKBREAK_REACH),
BLOCKPLACE_SPEED(BLOCKPLACE, Permissions.BLOCKPLACE_SPEED),
- CHAT(ChatConfig.factory, ChatData.factory),
+ CHAT(ChatConfig.factory, ChatData.factory, Permissions.CHAT),
CHAT_CAPTCHA(CHAT, Permissions.CHAT_CAPTCHA),
CHAT_COLOR(CHAT, Permissions.CHAT_COLOR),
CHAT_COMMANDS(CHAT, Permissions.CHAT_COMMANDS),
@@ -70,10 +67,10 @@ public enum CheckType {
CHAT_RELOG(CHAT, Permissions.CHAT_RELOG),
- COMBINED(CombinedConfig.factory, CombinedData.factory),
+ COMBINED(CombinedConfig.factory, CombinedData.factory, Permissions.COMBINED),
COMBINED_IMPROBABLE(COMBINED, Permissions.COMBINED_IMPROBABLE),
- FIGHT(FightConfig.factory, FightData.factory),
+ FIGHT(FightConfig.factory, FightData.factory, Permissions.FIGHT),
FIGHT_ANGLE(FIGHT, Permissions.FIGHT_ANGLE),
FIGHT_CRITICAL(FIGHT, Permissions.FIGHT_CRITICAL),
FIGHT_DIRECTION(FIGHT, Permissions.FIGHT_DIRECTION),
@@ -84,14 +81,14 @@ public enum CheckType {
FIGHT_SELFHIT(FIGHT, Permissions.FIGHT_SELFHIT),
FIGHT_SPEED(FIGHT, Permissions.FIGHT_SPEED),
- INVENTORY(InventoryConfig.factory, InventoryData.factory),
+ INVENTORY(InventoryConfig.factory, InventoryData.factory, Permissions.INVENTORY),
INVENTORY_DROP(INVENTORY, Permissions.INVENTORY_DROP),
INVENTORY_FASTCLICK(INVENTORY, Permissions.INVENTORY_FASTCLICK),
INVENTORY_INSTANTBOW(INVENTORY, Permissions.INVENTORY_INSTANTBOW),
INVENTORY_INSTANTEAT(INVENTORY, Permissions.INVENTORY_INSTANTEAT),
INVENTORY_ITEMS(INVENTORY, Permissions.INVENTORY_ITEMS),
- MOVING(MovingConfig.factory, MovingData.factory),
+ MOVING(MovingConfig.factory, MovingData.factory, Permissions.MOVING),
MOVING_CREATIVEFLY(MOVING, Permissions.MOVING_CREATIVEFLY),
MOVING_MOREPACKETS(MOVING, Permissions.MOVING_MOREPACKETS),
MOVING_MOREPACKETSVEHICLE(MOVING, Permissions.MOVING_MOREPACKETSVEHICLE),
@@ -101,50 +98,65 @@ public enum CheckType {
UNKNOWN;
- /** The group. */
- private CheckType parent = null;
+ /** If not null, this is the check group usually. */
+ private final CheckType parent;
- /** The configFactory. */
- private CheckConfigFactory configFactory = null;
+ /** The check config factory (access CheckConfig instances by CheckType). */
+ private final CheckConfigFactory configFactory;
- /** The dataFactory. */
- private CheckDataFactory dataFactory = null;
+ /** The check data factory (access CheckData instances by CheckType). */
+ private final CheckDataFactory dataFactory;
- /** The permission. */
- private String permission = null;
+ /** The bypass permission. */
+ private final String permission;
- /**
- * Instantiates a new check type.
- */
- private CheckType() {}
+ /**
+ * Special purpose check types (likely not actual checks).
+ */
+ private CheckType() {
+ this(null, null, null);
+ }
+
+ /**
+ * Special purpose for grouping (ALL).
+ * @param permission
+ */
+ private CheckType(final String permission){
+ this(null, null, permission);
+ }
+
+ /**
+ * Constructor for root checks or check groups, that are not grouped under another check type.
+ * @param configFactory
+ * @param dataFactory
+ * @param permission
+ */
+ private CheckType(final CheckConfigFactory configFactory, final CheckDataFactory dataFactory, final String permission) {
+ this(null, permission, configFactory, dataFactory);
+ }
- /**
- * Instantiates a new check type.
- *
- * @param configFactory
- * the configFactory
- * @param dataFactory
- * the dataFactory
- */
- private CheckType(final CheckConfigFactory configFactory, final CheckDataFactory dataFactory) {
- this.configFactory = configFactory;
- this.dataFactory = dataFactory;
- }
+ /**
+ * Constructor for sub-checks grouped under another check type.
+ * @param parent
+ * @param permission
+ */
+ private CheckType(final CheckType parent, final String permission) {
+ this(parent, permission, parent.getConfigFactory(), parent.getDataFactory());
+ }
- /**
- * Instantiates a new check type.
- *
- * @param parent
- * the parent
- * @param permission
- * the permission
- */
- private CheckType(final CheckType parent, final String permission) {
- this.parent = parent;
- configFactory = parent.getConfigFactory();
- dataFactory = parent.getDataFactory();
- this.permission = permission;
- }
+ /**
+ * Root constructor.
+ * @param parent Super check type (usually the group).
+ * @param permission Bypass permission.
+ * @param configFactory Check config factory.
+ * @param dataFactory Check data factory.
+ */
+ private CheckType(final CheckType parent, final String permission, final CheckConfigFactory configFactory, final CheckDataFactory dataFactory) {
+ this.parent = parent;
+ this.permission = permission;
+ this.configFactory = configFactory;
+ this.dataFactory = dataFactory;
+ }
/**
* Gets the configFactory.
@@ -183,7 +195,7 @@ public enum CheckType {
}
/**
- * Gets the permission.
+ * Gets the bypass permission for this check type.
*
* @return the permission
*/
@@ -223,29 +235,12 @@ public enum CheckType {
/**
* Remove the player data for a given player and a given check type. CheckType.ALL and null will be interpreted as removing all data.
+ * @deprecated Will be removed, use DataManager.removeData instead.
* @param playerName
* @param checkType
* @return If any data was present.
*/
- public static boolean removeData(final String playerName, CheckType checkType) {
- if (checkType == null) checkType = ALL;
-
- // Attempt for direct removal.
- CheckDataFactory dataFactory = checkType.getDataFactory();
- if (dataFactory != null) return dataFactory.removeData(playerName) != null;
-
- // Remove all for which it seems necessary.
- final Set factories = new HashSet();
- for (CheckType otherType : CheckType.values()){
- if (checkType == ALL || APIUtils.isParent(checkType, otherType)){
- final CheckDataFactory otherFactory = otherType.getDataFactory();
- if (otherFactory != null) factories.add(otherFactory);
- }
- }
- boolean had = false;
- for (final CheckDataFactory otherFactory : factories){
- if (otherFactory.removeData(playerName) != null) had = true;
- }
- return had;
+ public static boolean removeData(final String playerName, final CheckType checkType) {
+ return DataManager.removeData(playerName, checkType);
}
}
\ No newline at end of file
diff --git a/src/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java b/src/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java
index ac5d8d8c..950b528f 100644
--- a/src/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java
+++ b/src/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java
@@ -54,7 +54,7 @@ public class CombinedListener implements Listener {
}
}
- @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onEntityDamage(final EntityDamageEvent event){
final Entity entity = event.getEntity();
if (!(entity instanceof Player)) return;
diff --git a/src/fr/neatmonster/nocheatplus/checks/fight/FightListener.java b/src/fr/neatmonster/nocheatplus/checks/fight/FightListener.java
index e1a9da50..a04772c6 100644
--- a/src/fr/neatmonster/nocheatplus/checks/fight/FightListener.java
+++ b/src/fr/neatmonster/nocheatplus/checks/fight/FightListener.java
@@ -116,20 +116,16 @@ public class FightListener implements Listener {
cancelled = true;
}
-
- if (!cancelled && angle.isEnabled(player)){
- // Improbable yaw.
- if (Combined.checkYawRate(player, player.getLocation().getYaw(), now, worldName, cc.yawRateCheck)){
- // (Check or just feed).
- cancelled = true;
- }
- // Angle check.
- if (angle.check(player, worldChanged)) cancelled = true;
- }
- else{
- // Always feed yaw rate here.
- Combined.feedYawRate(player, player.getLocation().getYaw(), now, worldName);
- }
+ if (angle.isEnabled(player)) {
+ // The "fast turning" checks are checked in any case because they accumulate data.
+ // Improbable yaw changing.
+ if (Combined.checkYawRate(player, player.getLocation().getYaw(), now, worldName, cc.yawRateCheck)) {
+ // (Check or just feed).
+ cancelled = true;
+ }
+ // Angle check.
+ if (angle.check(player, worldChanged)) cancelled = true;
+ }
if (!cancelled && critical.isEnabled(player) && critical.check(player))
cancelled = true;
diff --git a/src/fr/neatmonster/nocheatplus/checks/moving/MovingData.java b/src/fr/neatmonster/nocheatplus/checks/moving/MovingData.java
index fc647d0b..aec7f64d 100644
--- a/src/fr/neatmonster/nocheatplus/checks/moving/MovingData.java
+++ b/src/fr/neatmonster/nocheatplus/checks/moving/MovingData.java
@@ -87,7 +87,9 @@ public class MovingData extends ACheckData {
/** Last from coordinates. */
public double fromX = Double.MAX_VALUE, fromY, fromZ;
/** Last to coordinates. */
- public double toX = Double.MAX_VALUE, toY, toZ;
+ public double toX = Double.MAX_VALUE, toY, toZ;
+ /** To was ground or web etc. */
+ public boolean toWasReset;
// Data of the creative check.
public boolean creativeFlyPreviousRefused;
@@ -152,6 +154,24 @@ public class MovingData extends ACheckData {
clearAccounting();
clearNoFallData();
}
+
+ /**
+ * Mildly reset the flying data without losing any important information.
+ *
+ * @param setBack
+ */
+ public void onSetBack(final Location setBack) {
+ // Reset positions
+ resetPositions(teleported);
+ this.setBack = teleported;
+ clearAccounting(); // Might be more safe to do this.
+ // Keep no-fall data.
+ // Fly data: problem is we don't remember the settings for the set back location.
+ // Assume the player to start falling from there rather, or be on ground.
+ // Keep jump amplifier
+ // Keep bunny-hop delay (?)
+ // keep jump phase.
+ }
public void clearAccounting() {
final long now = System.currentTimeMillis();
diff --git a/src/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/src/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java
index 69fab1db..67805ba1 100644
--- a/src/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java
+++ b/src/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java
@@ -26,6 +26,7 @@ import org.bukkit.event.player.PlayerBedLeaveEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPortalEvent;
@@ -208,9 +209,18 @@ public class MovingListener implements Listener {
final Player player = event.getPlayer();
final MovingData data = MovingData.getData(player);
- if (!creativeFly.isEnabled(player) && survivalFly.isEnabled(player) && survivalFly.check(player) && data.ground != null)
- // To cancel the event, we simply teleport the player to his last safe location.
- player.teleport(data.ground);
+ if (!creativeFly.isEnabled(player) && survivalFly.isEnabled(player) && survivalFly.check(player)) {
+ // To cancel the event, we simply teleport the player to his last
+ // safe location.
+ Location target = null;
+ if (data.ground != null) target = data.ground;
+ else if (data.setBack != null) target = data.setBack;
+// else target = player.getLocation(); // TODO
+
+
+ if (target != null) player.teleport(target);// TODO: schedule / other measures ?
+ }
+
}
/**
@@ -350,20 +360,11 @@ public class MovingListener implements Listener {
data.noFallAssumeGround = false;
data.teleported = null;
- final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle();
- // Potion effect "Jump".
- final double jumpAmplifier;
- if (mcPlayer.hasEffect(MobEffectList.JUMP)) {
-// final int amplifier = mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier();
-// if (amplifier > 20)
-// jumpAmplifier = 1.5D * (amplifier + 1D);
-// else
-// jumpAmplifier = 1.2D * (amplifier + 1D);
- jumpAmplifier = 1D + mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier();
- if (cc.debug) System.out.println(player.getName() + " Jump effect: " + data.jumpAmplifier);
- }
- else jumpAmplifier = 1D;
- if (jumpAmplifier > data.jumpAmplifier) data.jumpAmplifier = jumpAmplifier;
+ final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle();
+ // Potion effect "Jump".
+ final double jumpAmplifier = MovingListener.getJumpAmplifier(mcPlayer);
+ if (jumpAmplifier > 0D && cc.debug) System.out.println(player.getName() + " Jump effect: " + data.jumpAmplifier);
+ if (jumpAmplifier > data.jumpAmplifier) data.jumpAmplifier = jumpAmplifier;
// Just try to estimate velocities over time. Not very precise, but works good enough most of the time. Do
// general data modifications one for each event.
@@ -386,13 +387,18 @@ public class MovingListener implements Listener {
}
- Location newTo = null;
-
- if (passable.isEnabled(player)) newTo = passable.check(player, pFrom, pTo, data, cc);
+ Location newTo = null;
+
+ final Location passableTo;
+ // Check passable in any case (!)
+ if (cc.passableCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_PASSABLE) && !player.hasPermission(Permissions.MOVING_PASSABLE)) {
+ // Passable is checked first to get the original set-back locations from the other checks, if needed.
+ passableTo = passable.check(player, pFrom, pTo, data, cc);
+ }
+ else passableTo = null;
// Optimized checking, giving creativefly permission precedence over survivalfly.
- if (newTo != null);
- else if (!player.hasPermission(Permissions.MOVING_CREATIVEFLY)){
+ if (!player.hasPermission(Permissions.MOVING_CREATIVEFLY)){
// Either survivalfly or speed check.
if ((cc.ignoreCreative || player.getGameMode() != GameMode.CREATIVE) && (cc.ignoreAllowFlight || !player.getAllowFlight())
&& cc.survivalFlyCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_SURVIVALFLY) && !player.hasPermission(Permissions.MOVING_SURVIVALFLY)){
@@ -411,13 +417,16 @@ public class MovingListener implements Listener {
}
else data.clearFlyData();
- if (newTo == null
- && cc.morePacketsCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_MOREPACKETS) && !player.hasPermission(Permissions.MOVING_MOREPACKETS))
- // If he hasn't been stopped by any other check and is handled by the more packets check, execute it.
- newTo = morePackets.check(player, pFrom, pTo, data, cc);
- else
- // Otherwise we need to clear his data.
- data.clearMorePacketsData();
+ if (newTo == null && cc.morePacketsCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_MOREPACKETS) && !player.hasPermission(Permissions.MOVING_MOREPACKETS)) {
+ // If he hasn't been stopped by any other check and is handled by the more packets check, execute it.
+ newTo = morePackets.check(player, pFrom, pTo, data, cc);
+ } else {
+ // Otherwise we need to clear his data.
+ data.clearMorePacketsData();
+ }
+
+ // Prefer the location returned by passable.
+ if (passableTo != null) newTo = passableTo;
// Did one of the checks decide we need a new "to"-location?
if (newTo != null) {
@@ -427,18 +436,21 @@ public class MovingListener implements Listener {
// Remember where we send the player to.
data.teleported = newTo;
}
+
// Set positions.
+ // TODO: Should these be set on monitor ?
data.fromX = from.getX();
data.fromY = from.getY();
data.fromZ = from.getZ();
data.toX = to.getX();
data.toY = to.getY();
data.toZ = to.getZ();
+
// Cleanup.
moveInfo.cleanup();
parkedInfo.add(moveInfo);
}
-
+
/**
* A workaround for cancelled PlayerMoveEvents.
*
@@ -517,15 +529,15 @@ public class MovingListener implements Listener {
data.clearMorePacketsData();
}
- /**
- * When a player respawns, all information related to the moving checks becomes invalid.
- *
- * @param event
- * the event
- */
- @EventHandler(
- priority = EventPriority.MONITOR)
- public void onPlayerRespawn(final PlayerRespawnEvent event) {
+ /**
+ * When a player respawns, all information related to the moving checks
+ * becomes invalid.
+ *
+ * @param event
+ * the event
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onPlayerRespawn(final PlayerRespawnEvent event) {
/*
* ____ _ ____
* | _ \| | __ _ _ _ ___ _ __ | _ \ ___ ___ _ __ __ ___ ___ __
@@ -534,10 +546,15 @@ public class MovingListener implements Listener {
* |_| |_|\__,_|\__, |\___|_| |_| \_\___||___/ .__/ \__,_| \_/\_/ |_| |_|
* |___/ |_|
*/
- final MovingData data = MovingData.getData(event.getPlayer());
- data.clearFlyData();
- data.clearMorePacketsData();
- }
+ final Player player = event.getPlayer();
+ final MovingData data = MovingData.getData(player);
+ data.clearFlyData();
+ data.clearMorePacketsData();
+ if (survivalFly.isEnabled(player)) {
+ data.setBack = event.getRespawnLocation();
+ data.ground = event.getRespawnLocation();
+ }
+ }
/**
* If a player gets teleported, it may have two reasons. Either it was NoCheat or another plugin. If it was
@@ -548,8 +565,7 @@ public class MovingListener implements Listener {
* @param event
* the event
*/
- @EventHandler(
- ignoreCancelled = true, priority = EventPriority.HIGHEST)
+ @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST)
public void onPlayerTeleport(final PlayerTeleportEvent event) {
/*
* ____ _ _____ _ _
@@ -559,29 +575,34 @@ public class MovingListener implements Listener {
* |_| |_|\__,_|\__, |\___|_| |_|\___|_|\___| .__/ \___/|_| \__|
* |___/ |_|
*/
- final Player player = event.getPlayer();
- final MovingData data = MovingData.getData(player);
-
- final Location teleported = data.teleported;
-
- // If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no".
- final Location to = event.getTo();
- if (event.isCancelled() && teleported != null && data.teleported.equals(to)){
- // TODO: even more strict enforcing ?
- event.setCancelled(false);
- event.setTo(teleported);
- event.setFrom(teleported);
- data.clearFlyData();
- data.resetPositions(teleported);
- }
- else{
- // Only if it wasn't NoCheatPlus, drop data from more packets check. If it was NoCheatPlus, we don't
- // want players to exploit the fly check teleporting to get rid of the "morepackets" data.
- // TODO: check if to do with cancelled teleports !
- data.clearMorePacketsData();
- data.clearFlyData();
- data.resetPositions(event.isCancelled() ? event.getFrom() : to);
- }
+ final Player player = event.getPlayer();
+ final MovingData data = MovingData.getData(player);
+
+ final Location teleported = data.teleported;
+
+ // If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no".
+ final Location to = event.getTo();
+ if (teleported != null && teleported.equals(to)) {
+ // Teleport by NCP.
+ // Prevent cheaters getting rid of flying data (morepackets, other).
+ // TODO: even more strict enforcing ?
+ if (event.isCancelled()) {
+ event.setCancelled(false);
+ event.setTo(teleported);
+ event.setFrom(teleported);
+ }
+ else{
+ // Not cancelled but NCP teleport.
+ }
+ // TODO: This could be done on MONITOR.
+ data.onSetBack(teleported);
+ } else {
+ // Only if it wasn't NoCheatPlus, drop data from more packets check.
+ // TODO: check if to do with cancelled teleports !
+ data.clearMorePacketsData();
+ data.clearFlyData();
+ data.resetPositions(event.isCancelled() ? event.getFrom() : to);
+ }
// Always drop data from fly checks, as it always loses its validity after teleports. Always!
@@ -715,6 +736,22 @@ public class MovingListener implements Listener {
// Entity fall-distance should be reset elsewhere.
}
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onPlayerJoin(final PlayerJoinEvent event) {
+ final Player player = event.getPlayer();
+ if (survivalFly.isEnabled(player)){
+ final MovingData data = MovingData.getData(player);
+ // TODO: on existing set back: detect world changes and loss of world on join (+ set up some paradigm).
+ if (data.setBack == null){
+ data.setBack = player.getLocation();
+ }
+ if (data.fromX == Double.MAX_VALUE && data.toX == Double.MAX_VALUE){
+ // TODO: re-think: more fine grained reset?
+ data.resetPositions(data.setBack);
+ }
+ }
+ }
+
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(final PlayerQuitEvent event){
noFall.onLeave(event.getPlayer());
@@ -724,4 +761,15 @@ public class MovingListener implements Listener {
public void onPlayerKick(final PlayerKickEvent event){
noFall.onLeave(event.getPlayer());
}
+
+ /**
+ * Determine "some jump amplifier": 1 is jump boost, 2 is jump boost II.
+ * @param mcPlayer
+ * @return
+ */
+ public static final double getJumpAmplifier(final EntityPlayer mcPlayer) {
+ if (mcPlayer.hasEffect(MobEffectList.JUMP)) {
+ return 1D + mcPlayer.getEffect(MobEffectList.JUMP).getAmplifier();
+ } else return 0D;
+ }
}
diff --git a/src/fr/neatmonster/nocheatplus/checks/moving/Passable.java b/src/fr/neatmonster/nocheatplus/checks/moving/Passable.java
index 6532f288..7761bd6c 100644
--- a/src/fr/neatmonster/nocheatplus/checks/moving/Passable.java
+++ b/src/fr/neatmonster/nocheatplus/checks/moving/Passable.java
@@ -18,75 +18,81 @@ public class Passable extends Check {
public Passable() {
super(CheckType.MOVING_PASSABLE);
}
-
- public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc){
+
+ public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc)
+ {
// Simple check.
- if (!to.isPassable()){
- Location loc = null; // players location if should be used.
+ if (!to.isPassable()) {
+ Location loc = null; // players location if should be used.
// Allow moving into the same block.
- if (from.isSameBlock(to)){
- if (!from.isPassable()){
- final double eyeY = to.getY() + player.getEyeHeight();
- final int eyeBlockY = Location.locToBlock(eyeY);
- if (eyeBlockY != to.getBlockY()){
- if (BlockProperties.isPassable(to.getBlockAccess(), to.getX(), eyeY, to.getZ(), to.getTypeId(to.getBlockX(), eyeBlockY, to.getBlockZ()))){
- // Allow moving inside the same block if head is free.
- return null;
- }
- }
- // Only allow moving further out of the block (still allows going round in circles :p)
- // TODO: account for actual bounding box.
- final Vector blockMiddle = new Vector(0.5 + from.getBlockX(), 0.5 + from.getBlockY(), 0.5 + from.getBlockZ());
- // TODO: Allow moving out of one block towards non-solid blocks (closest only ?).
- // TODO: Allow moving out of half steps ?
- // TODO: Allow moving towards non solid blocks.
- if (blockMiddle.distanceSquared(from.getVector()) < blockMiddle.distanceSquared(to.getVector())) {
- // Further check for the players location as possible set back.
- loc = player.getLocation();
- if (to.isSamePos(loc) ){
- loc = null;
- }
- else if (!BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()))){
- // Allow the move
- return null;
- }
- // else is passable: use the location instead of from.
- }
+ if (from.isSameBlock(to)) {
+ if (!from.isPassable()) {
+ final double eyeY = to.getY() + player.getEyeHeight();
+ final int eyeBlockY = Location.locToBlock(eyeY);
+ if (eyeBlockY != to.getBlockY()) {
+ if (BlockProperties.isPassable(to.getBlockAccess(), to.getX(), eyeY, to.getZ(), to.getTypeId(to.getBlockX(), eyeBlockY, to.getBlockZ()))) {
+ // Allow moving inside the same block if head is
+ // free.
+ return null;
+ }
+ }
+ // Only allow moving further out of the block (still allows
+ // going round in circles :p)
+ // TODO: account for actual bounding box.
+ final Vector blockMiddle = new Vector(0.5 + from.getBlockX(), 0.5 + from.getBlockY(), 0.5 + from.getBlockZ());
+ // TODO: Allow moving out of one block towards non-solid
+ // blocks (closest only ?).
+ // TODO: Allow moving out of half steps ?
+ // TODO: Allow moving towards non solid blocks.
+ if (blockMiddle.distanceSquared(from.getVector()) < blockMiddle.distanceSquared(to.getVector())) {
+ // Further check for the players location as possible
+ // set back.
+ loc = player.getLocation();
+ if (to.isSamePos(loc)) {
+ loc = null;
+ } else if (!BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()))) {
+ // Allow the move
+ return null;
+ }
+ // else is passable: use the location instead of from.
+ }
}
}
+
+ // Prefer the set-back location from the data.
+ if (data.setBack != null && BlockProperties.isPassable(from.getBlockAccess(), data.setBack)) loc = data.setBack;
+
// Return the reset position.
data.passableVL += 1d;
final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions);
if (vd.needsParameters()) vd.setParameter(ParameterName.BLOCK_ID, "" + to.getTypeId());
- if (executeActions(vd)){
- // TODO: Consider another set back position for this, also keeping track of players moving around in blocks.
+ if (executeActions(vd)) {
+ // TODO: Consider another set back position for this, also
+ // keeping track of players moving around in blocks.
final Location newTo;
- if (!from.isPassable() && loc == null){
- // Check if passable.
- loc = player.getLocation();
- if (to.isSamePos(loc) || !BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(from.getBlockX(), from.getBlockY(), from.getBlockZ()))){
- loc = null;
- }
+ if (loc == null && !from.isPassable()) {
+ // Check if passable.
+ loc = player.getLocation();
+ if (to.isSamePos(loc) || !BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(from.getBlockX(), from.getBlockY(), from.getBlockZ()))) {
+ loc = null;
+ }
}
if (loc != null) newTo = loc;
else newTo = from.getLocation();
newTo.setYaw(to.getYaw());
newTo.setPitch(to.getPitch());
return newTo;
- }
- }
- else{
+ }
+ } else {
data.passableVL *= 0.99;
}
return null;
}
@Override
- protected Map getParameterMap(final ViolationData violationData) {
- // TODO Auto-generated method stub
+ protected Map getParameterMap(final ViolationData violationData)
+ {
return super.getParameterMap(violationData);
}
-
-
}
diff --git a/src/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java b/src/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java
index c2d31c5f..7dcd4c31 100644
--- a/src/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java
+++ b/src/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java
@@ -1,5 +1,6 @@
package fr.neatmonster.nocheatplus.checks.moving;
+import java.util.ArrayList;
import java.util.Locale;
import net.minecraft.server.EntityPlayer;
@@ -13,7 +14,9 @@ import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.players.Permissions;
+import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
+import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/*
@@ -47,6 +50,9 @@ public class SurvivalFly extends Check {
/** Faster moving down stream (water mainly). */
public static final double modDownStream = 0.19 / swimmingSpeed;
+ /** To join some tags with moving check violations. */
+ private final ArrayList tags = new ArrayList(15);
+
/**
* Instantiates a new survival fly check.
*/
@@ -91,7 +97,7 @@ public class SurvivalFly extends Check {
*/
public Location check(final Player player, final EntityPlayer mcPlayer, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc) {
final long now = System.currentTimeMillis();
-
+ tags.clear();
// A player is considered sprinting if the flag is set and if he has enough food level.
final boolean sprinting = player.isSprinting() && player.getFoodLevel() > 5;
@@ -108,55 +114,61 @@ public class SurvivalFly extends Check {
// If we don't have any setBack, choose the location the player comes from.
if (data.setBack == null)
data.setBack = from.getLocation();
-
- boolean resetFrom = fromOnGround || from.isInLiquid() || from.isOnLadder() || from.isInWeb();
-
- final double setBackYDistance = to.getY() - data.setBack.getY();
- // If the player has touched the ground but it hasn't been noticed by the plugin, the workaround is here.
- if (!resetFrom){
- // Don't set "useWorkaround = x()", to avoid potential trouble with reordering to come, and similar.
- boolean useWorkaround = false;
- // Check for moving off stairs.
- if (!useWorkaround && from.isAboveStairs()) useWorkaround = true;
- // Check for "lost touch", for when moving events were not created, for instance (1/256).
- if (!useWorkaround){
- final boolean inconsistent = yDistance > 0 && yDistance < 0.5 && data.survivalFlyLastYDist < 0
- && setBackYDistance > 0D && setBackYDistance <= 1.5D;
- if (inconsistent){
- if (cc.debug) System.out.println(player.getName() + " Y-INCONSISTENCY");
- if (data.fromX != Double.MAX_VALUE){
- // Interpolate from last to-coordinates to the from coordinates (with some safe-guard).
- final double dX = from.getX() - data.fromX;
- final double dY = from.getY() - data.fromY;
- final double dZ = from.getZ() - data.fromZ;
- if (dX * dX + dY * dY + dZ * dZ < 0.5){ // TODO: adjust limit maybe.
- // Check full bounding box since last from.
- if (cc.debug) System.out.println(player.getName() + " CONSIDER WORKAROUND");
- final double minY = Math.min(data.toY, Math.min(data.fromY, from.getY()));
- final double iY = minY; // TODO ...
- final double r = from.getWidth() / 2.0;
- if (BlockProperties.isOnGround(from.getBlockAccess(), Math.min(data.fromX, from.getX()) - r, iY - cc.yOnGround, Math.min(data.fromZ, from.getZ()) - r, Math.max(data.fromX, from.getX()) + r, iY + 0.25, Math.max(data.fromZ, from.getZ()) + r)) useWorkaround = true;
- }
- }
- }
- }
- if (useWorkaround){ // !toOnGround && to.isAboveStairs()) {
- // Set the new setBack and reset the jumpPhase.
-
- // Maybe don't adapt the setback (unless null)!
-// data.setBack = from.getLocation();
- data.setBack.setY(Location.locToBlock(data.setBack.getY()));
- // data.ground ?
- // ? set jumpphase to height / 0.15 ?
- data.survivalFlyJumpPhase = 0;
- data.jumpAmplifier = 0; // Might conflict, should probably fetch.
- data.clearAccounting();
- // Tell NoFall that we assume the player to have been on ground somehow.
- data.noFallAssumeGround = true;
- resetFrom = true;
- if (cc.debug) System.out.println(player.getName() + " Y-INCONSISTENCY WORKAROUND USED");
- }
- }
+
+ boolean resetFrom = fromOnGround || from.isInLiquid() || from.isOnLadder() || from.isInWeb();
+
+ final double setBackYDistance = to.getY() - data.setBack.getY();
+ // If the player has touched the ground but it hasn't been noticed by
+ // the plugin, the workaround is here.
+ if (!resetFrom) {
+ // Don't set "useWorkaround = x()", to avoid potential trouble with
+ // reordering to come, and similar.
+ boolean useWorkaround = false;
+ boolean setBackSafe = false; // Let compiler remove this if necessary.
+ // Check for moving off stairs.
+ if (!useWorkaround && from.isAboveStairs()) {
+ useWorkaround = true;
+ setBackSafe = true;
+ }
+ // Check for "lost touch", for when moving events were not created,
+ // for instance (1/256).
+ if (!useWorkaround && yDistance > 0 && yDistance < 0.5 && data.survivalFlyLastYDist < 0
+ && setBackYDistance > 0D && setBackYDistance <= 1.5D) {
+ if (data.fromX != Double.MAX_VALUE) {
+ // Interpolate from last to-coordinates to the from
+ // coordinates (with some safe-guard).
+ final double dX = from.getX() - data.fromX;
+ final double dY = from.getY() - data.fromY;
+ final double dZ = from.getZ() - data.fromZ;
+ if (dX * dX + dY * dY + dZ * dZ < 0.5) { // TODO: adjust
+ // limit maybe.
+ // Check full bounding box since last from.
+ final double minY = Math.min(data.toY, Math.min(data.fromY, from.getY()));
+ final double iY = minY; // TODO ...
+ final double r = from.getWidth() / 2.0;
+ if (BlockProperties.isOnGround(from.getBlockAccess(), Math.min(data.fromX, from.getX()) - r, iY - cc.yOnGround, Math.min(data.fromZ, from.getZ()) - r, Math.max(data.fromX, from.getX()) + r, iY + 0.25, Math.max(data.fromZ, from.getZ()) + r)) {
+ useWorkaround = true;
+ setBackSafe = true;
+ }
+ }
+ }
+ }
+ if (useWorkaround) { // !toOnGround && to.isAboveStairs()) {
+ // Set the new setBack and reset the jumpPhase.
+ if (setBackSafe) data.setBack = from.getLocation();
+ // TODO: This seems dubious !
+ data.setBack.setY(Location.locToBlock(data.setBack.getY()));
+ // data.ground ?
+ // ? set jumpphase to height / 0.15 ?
+ data.survivalFlyJumpPhase = 0;
+ data.jumpAmplifier = MovingListener.getJumpAmplifier(mcPlayer);
+ data.clearAccounting();
+ // Tell NoFall that we assume the player to have been on ground somehow.
+ data.noFallAssumeGround = true;
+ resetFrom = true; // Note: if removing this, other conditions need to check noFallAssume...
+ tags.add("lostground");
+ }
+ }
// Player on ice? Give him higher max speed.
if (from.isOnIce() || to.isOnIce())
@@ -190,16 +202,14 @@ public class SurvivalFly extends Check {
hAllowedDistance *= cc.survivalFlySpeedingSpeed/ 100D;
}
- // TODO: Optimize: maybe only do the permission checks and modifiers if the distance is too big.
- // (Depending on permission plugin, with pex it will be hardly 1000 ns for all moving perms, if all false.)
+ // TODO: More after-failure checks, to prevent unnecessary permission checking etc.
+ // TODO: Split off not frequently used parts to methods.
// If the player is on ice, give him an higher maximum speed.
if (data.survivalFlyOnIce > 0)
hAllowedDistance *= modIce;
- // Taken directly from Minecraft code, should work.
-
-// player.hasPotionEffect(PotionEffectType.SPEED);
+ // Speed amplifier.
if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT))
hAllowedDistance *= 1.0D + 0.2D * (mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier() + 1);
@@ -211,12 +221,15 @@ public class SurvivalFly extends Check {
// Judge if horizontal speed is above limit.
double hDistanceAboveLimit = hDistance - hAllowedDistance - data.horizontalFreedom;
- // Prevent players from walking on a liquid.
- // TODO: yDistance == 0D <- should there not be a tolerance +- or 0...x ?
- if (hDistanceAboveLimit <= 0D && hDistance > 0.1D && yDistance == 0D
- && BlockProperties.isLiquid(to.getTypeId()) && !toOnGround
- && to.getY() % 1D < 0.8D)
- hDistanceAboveLimit = hDistance;
+ // Tag for simple speed violation (medium), might get overridden.
+ if (hDistanceAboveLimit > 0) tags.add("hspeed");
+
+ // Prevent players from walking on a liquid.
+ // TODO: yDistance == 0D <- should there not be a tolerance +- or 0...x ?
+ if (hDistanceAboveLimit <= 0D && hDistance > 0.1D && yDistance == 0D && BlockProperties.isLiquid(to.getTypeId()) && !toOnGround && to.getY() % 1D < 0.8D) {
+ hDistanceAboveLimit = Math.max(hDistanceAboveLimit, hDistance);
+ tags.add("waterwalk");
+ }
// Prevent players from sprinting if they're moving backwards.
if (hDistanceAboveLimit <= 0D && sprinting) {
@@ -225,7 +238,10 @@ public class SurvivalFly extends Check {
&& yaw > 270F && yaw < 360F || xDistance > 0D && zDistance < 0D && yaw > 0F && yaw < 90F
|| xDistance > 0D && zDistance > 0D && yaw > 90F && yaw < 180F){
// Assumes permission check to be the heaviest (might be mistaken).
- if (!player.hasPermission(Permissions.MOVING_SURVIVALFLY_SPRINTING)) hDistanceAboveLimit = hDistance;
+ if (!player.hasPermission(Permissions.MOVING_SURVIVALFLY_SPRINTING)){
+ hDistanceAboveLimit = Math.max(hDistanceAboveLimit, hDistance);
+ tags.add("sprintback");
+ }
}
}
@@ -233,10 +249,11 @@ public class SurvivalFly extends Check {
// Did he go too far?
if (hDistanceAboveLimit > 0 && sprinting)
- // Try to treat it as a the "bunnyhop" problem.
+ // Try to treat it as a the "bunny-hop" problem.
if (data.bunnyhopDelay <= 0 && hDistanceAboveLimit > 0.05D && hDistanceAboveLimit < 0.28D) {
data.bunnyhopDelay = 9;
hDistanceAboveLimit = 0D;
+ tags.add("bunny"); // TODO: Which here...
}
if (hDistanceAboveLimit > 0D) {
@@ -244,9 +261,13 @@ public class SurvivalFly extends Check {
hDistanceAboveLimit -= data.horizontalBuffer;
data.horizontalBuffer = 0D;
- // Put back the "overconsumed" buffer.
- if (hDistanceAboveLimit < 0D)
- data.horizontalBuffer = -hDistanceAboveLimit;
+ // Put back the "over-consumed" buffer.
+ if (hDistanceAboveLimit < 0D){
+ data.horizontalBuffer = -hDistanceAboveLimit;
+ }
+ if (hDistanceAboveLimit <= 0){
+ tags.add("hbuffer"); // TODO: ...
+ }
} else
data.horizontalBuffer = Math.min(1D, data.horizontalBuffer - hDistanceAboveLimit);
@@ -255,27 +276,22 @@ public class SurvivalFly extends Check {
if (from.isInWeb()){
// Very simple: force players to descend or stay.
vAllowedDistance = from.isOnGround() ? 0.1D : 0;
- data.jumpAmplifier = 0;
+ data.jumpAmplifier = 0; // TODO: later maybe fetch.
vDistanceAboveLimit = yDistance;
if (cc.survivalFlyCobwebHack && vDistanceAboveLimit > 0 && hDistanceAboveLimit <= 0){
- if (now - data.survivalFlyCobwebTime > 3000){
- data.survivalFlyCobwebTime = now;
- data.survivalFlyCobwebVL = vDistanceAboveLimit * 100D;
- }
- else data.survivalFlyCobwebVL += vDistanceAboveLimit * 100D;
- if (data.survivalFlyCobwebVL < 550) { // Totally random !
- if (cc.debug) System.out.println(player.getName()+ " (Cobweb: silent set-back-)");
- // Silently set back.
- if (data.setBack == null) data.setBack = player.getLocation();
- data.survivalFlyJumpPhase = 0;
- data.setBack.setYaw(to.getYaw());
- data.setBack.setPitch(to.getPitch());
- data.survivalFlyLastYDist = Double.MAX_VALUE;
- return data.setBack;
+ // TODO: This seemed fixed by latest builds of CraftBukkit, test and remove if appropriate!
+ final Location silentSetBack = hackCobweb(player, data, to, now, vDistanceAboveLimit);
+ if (silentSetBack != null){
+ if (cc.debug) System.out.println(player.getName()+ " (Cobweb: silent set-back)");
+ return silentSetBack;
}
}
+ if (vDistanceAboveLimit > 0) tags.add("vweb");
}
+ // else if (verticalFreedom <= 0.001 && from.isOnLadder) ....
+ // else if (verticalFreedom <= 0.001 (?) & from.isInFluid
else{
+ // Check traveled y-distance, orientation is air + jumping + velocity (as far as it gets).
vAllowedDistance = (!(fromOnGround || data.noFallAssumeGround) && !toOnGround ? 1.45D : 1.35D) + data.verticalFreedom;
final int maxJumpPhase;
if (data.jumpAmplifier > 0){
@@ -287,46 +303,44 @@ public class SurvivalFly extends Check {
vAllowedDistance -= Math.max(0, (data.survivalFlyJumpPhase - maxJumpPhase) * 0.15D);
}
- vDistanceAboveLimit = to.getY() - data.setBack.getY() - vAllowedDistance;
-
-// System.out.println("vda = " +vDistanceAboveLimit + " / vc = " + data.verticalVelocityCounter + " / vf = " + data.verticalFreedom + " / v = " + player.getVelocity().length());
+ vDistanceAboveLimit = to.getY() - data.setBack.getY() - vAllowedDistance;
- // Step can also be blocked.
- if ((fromOnGround || data.noFallAssumeGround) && toOnGround && Math.abs(yDistance - 1D) <= cc.yStep && vDistanceAboveLimit <= 0D
- && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_STEP))
- vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance));
+ // Simple-step blocker.
+ if ((fromOnGround || data.noFallAssumeGround) && toOnGround && Math.abs(yDistance - 1D) <= cc.yStep && vDistanceAboveLimit <= 0D && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_STEP)) {
+ vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance));
+ tags.add("step");
+ }
+ }
+
+ if (data.noFallAssumeGround || fromOnGround || toOnGround) {
+ // Some reset condition.
+ data.jumpAmplifier = MovingListener.getJumpAmplifier(mcPlayer);
+ }
+
+ final boolean resetTo = toOnGround || to.isInLiquid() || to.isOnLadder()|| to.isInWeb();
+
+ // Accounting support.
+ if (cc.survivalFlyAccounting && !resetFrom && !resetTo) {
+ // Currently only for "air" phases.
+ // Horizontal.
+ if (data.horizontalFreedom <= 0.001D){
+ // This only checks general speed decrease oncevelocity is smoked up.
+ hDistanceAboveLimit = Math.max(hDistanceAboveLimit, doAccounting(now, hDistance, data.hDistSum, data.hDistCount, tags, "hacc"));
+ }
+ // Vertical.
+ if (data.verticalFreedom <= 0.001D) { // && ! resetTo) {
+ // Here yDistance can be negative and positive (!).
+ // TODO: Might demand resetting on some direction changes (bunny,)
+ vDistanceAboveLimit = Math.max(vDistanceAboveLimit, doAccounting(now, yDistance, data.vDistSum, data.vDistCount, tags, "vacc"));
+// // Check if y-direction is going upwards without speed / ground.
+// if (yDistance >= 0 && data.survivalFlyLastYDist < 0 && !data.toWasReset) {
+// // Moving upwards without having touched the ground.
+// vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance));
+// tags.add("ychange");
+// }
+ }
+ }
- }
- if (data.noFallAssumeGround || fromOnGround || toOnGround)
- data.jumpAmplifier = 0D;
-
- if (cc.survivalFlyAccounting && !resetFrom){
- final boolean useH = data.horizontalFreedom <= 0.001D;
- final boolean useV = data.verticalFreedom <= 0.001D;
- if (useH){
- data.hDistSum.add(now, (float) hDistance);
- data.hDistCount.add(now, 1f);
- }
- if (useV){
- data.vDistSum.add(now, (float) (yDistance));
- data.vDistCount.add(now, 1f);
- }
- if (useH && data.hDistCount.getScore(2) > 0 && data.hDistCount.getScore(1) > 0){
- final float hsc0 = data.hDistSum.getScore(1);
- final float hsc1 = data.hDistSum.getScore(2);
- if (hsc0 < hsc1 || hDistance < 3.9 && hsc0 == hsc1){
- hDistanceAboveLimit = Math.max(hDistanceAboveLimit, hsc0 - hsc1);
- }
- }
- if (useV && data.vDistCount.getScore(2) > 0 && data.vDistCount.getScore(1) > 0){
- final float vsc0 = data.vDistSum.getScore(1);
- final float vsc1 = data.vDistSum.getScore(2);
- if (vsc0 < vsc1 || yDistance < 3.9 && vsc0 == vsc1){
- vDistanceAboveLimit = Math.max(vDistanceAboveLimit, vsc0 - vsc1);
- }
- }
- }
-
final double result = (Math.max(hDistanceAboveLimit, 0D) + Math.max(vDistanceAboveLimit, 0D)) * 100D;
data.survivalFlyJumpPhase++;
@@ -336,11 +350,11 @@ public class SurvivalFly extends Check {
System.out.println(player.getName() + " hDist: " + hDistance + " / " + hAllowedDistance + " , vDist: " + (yDistance) + " ("+player.getVelocity().getY()+")" + " / " + vAllowedDistance);
System.out.println(player.getName() + " y" + (fromOnGround?"(onground)":"") + (data.noFallAssumeGround?"(assumeonground)":"") + ": " + from.getY() +"(" + player.getLocation().getY() + ") -> " + to.getY()+ (toOnGround?"(onground)":""));
if (cc.survivalFlyAccounting) System.out.println(player.getName() + " h=" + data.hDistSum.getScore(1f)+"/" + data.hDistSum.getScore(1) + " , v=" + data.vDistSum.getScore(1f)+"/"+data.vDistSum.getScore(1) );
+ System.out.println(player.getName() + " tags: " + CheckUtils.join(tags, "+"));
}
// Did the player move in unexpected ways?// Did the player move in unexpected ways?
if (result > 0D) {
-// System.out.println(BlockProperties.isStairs(from.getTypeIdBelow()) + " / " + BlockProperties.isStairs(to.getTypeIdBelow()));
// Increment violation counter.
data.survivalFlyVL += result;
data.clearAccounting();
@@ -352,10 +366,12 @@ public class SurvivalFly extends Check {
vd.setParameter(ParameterName.LOCATION_FROM, String.format(Locale.US, "%.2f, %.2f, %.2f", from.getX(), from.getY(), from.getZ()));
vd.setParameter(ParameterName.LOCATION_TO, String.format(Locale.US, "%.2f, %.2f, %.2f", to.getX(), to.getY(), to.getZ()));
vd.setParameter(ParameterName.DISTANCE, String.format(Locale.US, "%.2f", to.getLocation().distance(from.getLocation())));
+ vd.setParameter(ParameterName.TAGS, CheckUtils.join(tags, "+")); // Always set.
}
data.survivalFlyVLTime = now;
if (executeActions(vd)){
data.survivalFlyLastYDist = Double.MAX_VALUE;
+ data.toWasReset = false;
// 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(), data.setBack.getX(), data.setBack.getY(), data.setBack.getZ(),
@@ -368,7 +384,7 @@ public class SurvivalFly extends Check {
}
// Violation or not, apply reset conditions (cancel would have returned above).
- final boolean resetTo = toOnGround || to.isInLiquid() || to.isOnLadder()|| to.isInWeb();
+ data.toWasReset = resetTo || data.noFallAssumeGround;
if (resetTo){
// The player has moved onto ground.
data.setBack = to.getLocation();
@@ -384,5 +400,59 @@ public class SurvivalFly extends Check {
data.survivalFlyLastYDist = yDistance;
return null;
}
-
+
+ /**
+ * Allow accumulating some vls and silently set the player back.
+ *
+ * @param player
+ * @param data
+ * @param cc
+ * @param to
+ * @param now
+ * @param vDistanceAboveLimit
+ * @return
+ */
+ private final Location hackCobweb(final Player player, final MovingData data, final PlayerLocation to,
+ final long now, final double vDistanceAboveLimit)
+ {
+ if (now - data.survivalFlyCobwebTime > 3000) {
+ data.survivalFlyCobwebTime = now;
+ data.survivalFlyCobwebVL = vDistanceAboveLimit * 100D;
+ } else data.survivalFlyCobwebVL += vDistanceAboveLimit * 100D;
+ if (data.survivalFlyCobwebVL < 550) { // Totally random !
+ // Silently set back.
+ if (data.setBack == null) data.setBack = player.getLocation();
+ data.survivalFlyJumpPhase = 0;
+ data.setBack.setYaw(to.getYaw());
+ data.setBack.setPitch(to.getPitch());
+ data.survivalFlyLastYDist = Double.MAX_VALUE;
+ return data.setBack;
+ } else return null;
+ }
+
+ /**
+ * Keep track of values, demanding that with time the values decrease.
+ * The ActionFrequency objects have 3 buckets.
+ * @param now
+ * @param value
+ * @param sum
+ * @param count
+ * @param tags
+ * @param tag
+ * @return
+ */
+ private static final double doAccounting(final long now, final double value, final ActionFrequency sum, final ActionFrequency count, final ArrayList tags, String tag)
+ {
+ sum.add(now, (float) value);
+ count.add(now, 1f);
+ if (count.getScore(2) > 0 && count.getScore(1) > 0) {
+ final float sc0 = sum.getScore(1);
+ final float sc1 = sum.getScore(2);
+ if (sc0 < sc1 || value < 3.9 && sc0 == sc1) {
+ tags.add(tag);
+ return sc0 - sc1;
+ }
+ }
+ return 0;
+ }
}
diff --git a/src/fr/neatmonster/nocheatplus/command/admin/RemovePlayerCommand.java b/src/fr/neatmonster/nocheatplus/command/admin/RemovePlayerCommand.java
index 9142cdf6..74ddc234 100644
--- a/src/fr/neatmonster/nocheatplus/command/admin/RemovePlayerCommand.java
+++ b/src/fr/neatmonster/nocheatplus/command/admin/RemovePlayerCommand.java
@@ -61,7 +61,7 @@ public class RemovePlayerCommand extends NCPCommand {
if (DataManager.removeExecutionHistory(checkType, playerName)) histRemoved = true;
- final boolean dataRemoved = CheckType.removeData(playerName, checkType) || DataManager.clearComponentData(checkType, playerName);
+ final boolean dataRemoved = DataManager.removeData(playerName, checkType);
if (dataRemoved || histRemoved){
String which;
diff --git a/src/fr/neatmonster/nocheatplus/components/IRemoveData.java b/src/fr/neatmonster/nocheatplus/components/IRemoveData.java
index 839b5e79..02440f05 100644
--- a/src/fr/neatmonster/nocheatplus/components/IRemoveData.java
+++ b/src/fr/neatmonster/nocheatplus/components/IRemoveData.java
@@ -1,7 +1,8 @@
package fr.neatmonster.nocheatplus.components;
/**
- * Interface for component registration to allow cleanup for player data.
+ * Interface for component registration to allow cleanup for player data.
+ * NOTE: For CheckType-specific data removal, IHaveCheckType should be implemented, otherwise this data might get ignored until plugin-disable.
* @author mc_dev
*
*/
diff --git a/src/fr/neatmonster/nocheatplus/hooks/NCPExemptionManager.java b/src/fr/neatmonster/nocheatplus/hooks/NCPExemptionManager.java
index ef9d41c4..75ea2fdc 100644
--- a/src/fr/neatmonster/nocheatplus/hooks/NCPExemptionManager.java
+++ b/src/fr/neatmonster/nocheatplus/hooks/NCPExemptionManager.java
@@ -111,37 +111,34 @@ public class NCPExemptionManager {
exemptPermanently(player.getEntityId(), checkType);
}
- /**
- * This should be registered before all other listeners of NoCheatPlus.
- *
- * NOTE: For internal use only, DO NOT CALL FROM OUTSIDE.
- *
- * @return the listener
- */
- public static Listener getListener() {
- return new Listener() {
- @SuppressWarnings("unused")
- @EventHandler(
- ignoreCancelled = true, priority = EventPriority.LOWEST)
- public void onPlayerJoin(final PlayerJoinEvent event) {
- NCPExemptionManager.registerPlayer(event.getPlayer());
- }
+ /**
+ * This should be registered before all other listeners of NoCheatPlus.
+ *
+ * NOTE: For internal use only, DO NOT CALL FROM OUTSIDE.
+ *
+ * @return the listener
+ */
+ public static Listener getListener() {
+ return new Listener() {
+ @SuppressWarnings("unused")
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onPlayerJoin(final PlayerJoinEvent event) {
+ NCPExemptionManager.registerPlayer(event.getPlayer());
+ }
- @SuppressWarnings("unused")
- @EventHandler(
- ignoreCancelled = true, priority = EventPriority.MONITOR)
- public void onPlayerQuit(final PlayerQuitEvent event) {
- NCPExemptionManager.tryToRemove(event.getPlayer());
- }
-
- @SuppressWarnings("unused")
- @EventHandler(
- ignoreCancelled = true, priority = EventPriority.MONITOR)
- public void onPlayerKick(final PlayerKickEvent event) {
- NCPExemptionManager.tryToRemove(event.getPlayer());
- }
- };
- }
+ @SuppressWarnings("unused")
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onPlayerQuit(final PlayerQuitEvent event) {
+ NCPExemptionManager.tryToRemove(event.getPlayer());
+ }
+
+ @SuppressWarnings("unused")
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onPlayerKick(final PlayerKickEvent event) {
+ NCPExemptionManager.tryToRemove(event.getPlayer());
+ }
+ };
+ }
/**
* Check if an entity is exempted from a check right now by entity id.
diff --git a/src/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java b/src/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java
new file mode 100644
index 00000000..bb655d0f
--- /dev/null
+++ b/src/fr/neatmonster/nocheatplus/permissions/PermissionUtil.java
@@ -0,0 +1,67 @@
+package fr.neatmonster.nocheatplus.permissions;
+
+import java.util.Set;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.SimpleCommandMap;
+import org.bukkit.craftbukkit.CraftServer;
+import org.bukkit.permissions.Permission;
+import org.bukkit.permissions.PermissionDefault;
+import org.bukkit.plugin.PluginManager;
+
+public class PermissionUtil {
+
+ public static SimpleCommandMap getCommandMap(){
+ return (((CraftServer) Bukkit.getServer()).getCommandMap());
+ }
+
+ /**
+ * TODO: Return undo info.
+ * @param permissionBase
+ * @param ignoredCommands
+ * @param ops
+ */
+ public static void alterCommandPermissions(String permissionBase, Set ignoredCommands, boolean invertIgnored, boolean ops){
+ PluginManager pm = Bukkit.getPluginManager();
+ Permission rootPerm = pm.getPermission(permissionBase);
+ if (rootPerm == null){
+ rootPerm = new Permission(permissionBase);
+ pm.addPermission(rootPerm);
+ }
+ SimpleCommandMap map = getCommandMap();
+ for (Command command : map.getCommands()){
+ String lcLabel = command.getLabel().trim().toLowerCase();
+ if (ignoredCommands != null){
+ if (ignoredCommands.contains(lcLabel)){
+ if (!invertIgnored) continue;
+ }
+ else if (invertIgnored) continue;
+ }
+ // Set the permission for the command.
+ String cmdPermName = command.getPermission();
+ boolean cmdHadPerm;
+ if (cmdPermName == null){
+ // Set a permission.
+ cmdPermName = permissionBase + "." + lcLabel;
+ command.setPermission(cmdPermName);
+ cmdHadPerm = false;
+ }
+ else cmdHadPerm = true;
+ // Set permission default behavior.
+ Permission cmdPerm = pm.getPermission(cmdPermName);
+ if (cmdPerm == null){
+ if (!cmdHadPerm){
+ cmdPerm = new Permission(cmdPermName);
+ cmdPerm.addParent(rootPerm, true);
+ cmdPerm.setDefault(ops ? PermissionDefault.OP : PermissionDefault.FALSE);
+ pm.addPermission(cmdPerm);
+ }
+ }
+ else{
+ // Change default of the permission.
+ cmdPerm.setDefault(ops ? PermissionDefault.OP : PermissionDefault.FALSE);
+ }
+ }
+ }
+}
diff --git a/src/fr/neatmonster/nocheatplus/players/DataManager.java b/src/fr/neatmonster/nocheatplus/players/DataManager.java
index 1011c057..a5539dfb 100644
--- a/src/fr/neatmonster/nocheatplus/players/DataManager.java
+++ b/src/fr/neatmonster/nocheatplus/players/DataManager.java
@@ -40,6 +40,14 @@ import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.hooks.APIUtils;
+/**
+ * Central access point for a lot of functionality for managing data, especially removing data for cleanup.
+ * Originally intended as temporary or intermediate design, this might help reorganizing the API at some point.
+ * However i could not yet find a pleasing way for generic configuration access for a centralized data management (all in one),
+ * so this might just be a workarounds class for coping with the current design, until somehow resolved in another way.
+ * @author mc_dev
+ *
+ */
public class DataManager implements Listener, INotifyReload, INeedConfig, IComponentRegistry{
protected static DataManager instance = null;
@@ -65,15 +73,28 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
*/
protected final Map> executionHistories = new HashMap>();
+ /**
+ * Duration in milliseconds for expiration of logged off players data.
+ * Disabled with 0, in the config minutes are used as unit.
+ */
protected long durExpireData = 0;
+ /** Data and execution history. */
protected boolean deleteData = true;
+ /** Violation history and execution history. */
protected boolean deleteHistory = false;
+ /**
+ * Sets the static instance reference.
+ */
public DataManager(){
instance = this;
}
+ /**
+ * Check the logged out players for if any data can be removed.
+ * Currently only "dumb" full removal is performed. Later it is thinkable to remove "as much as reasonable".
+ */
public void checkExpiration(){
if (durExpireData <= 0) return;
final long now = System.currentTimeMillis();
@@ -119,6 +140,10 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
onLeave(event.getPlayer());
}
+ /**
+ * Quit or kick.
+ * @param player
+ */
private final void onLeave(final Player player) {
final long now = System.currentTimeMillis();
lastLogout.put(player.getName(), now);
@@ -127,10 +152,13 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
@Override
public void onReload() {
- // future
+ // present.
adjustSettings();
}
+ /**
+ * Fetch settings from the current default config.
+ */
private void adjustSettings() {
final ConfigFile config = ConfigManager.getConfigFile();
durExpireData = config.getLong(ConfPaths.DATA_EXPIRATION_DURATION) * 60000L; // in minutes
@@ -138,6 +166,12 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
deleteHistory = config.getBoolean(ConfPaths.DATA_EXPIRATION_HISTORY);
}
+ /**
+ * Used by checks to register the history for external access.
+ * NOTE: This method is not really meant ot be used from outside NCP.
+ * @param type
+ * @param histories
+ */
public static void registerExecutionHistory(CheckType type, Map histories) {
instance.executionHistories.put(type, histories);
}
@@ -154,6 +188,12 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
return null;
}
+ /**
+ * Remove the execution history for a player for the given check type.
+ * @param type
+ * @param playerName
+ * @return
+ */
public static boolean removeExecutionHistory(final CheckType type, final String playerName){
boolean removed = false;
// TODO: design ...
@@ -197,9 +237,36 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
ViolationHistory.clear(checkType);
}
+ /**
+ * Remove the player data for a given player and a given check type. CheckType.ALL and null will be interpreted as removing all data.
+ * @param playerName Exact player name.
+ * @param checkType Check type to remove data for, null is regarded as ALL.
+ * @return If any data was present.
+ */
+ public static boolean removeData(final String playerName, CheckType checkType) {
+ if (checkType == null) checkType = CheckType.ALL;
+ boolean had = false;
+
+ // Check extended registered components.
+ if (clearComponentData(checkType, playerName)) had = true;
+
+ // Collect factories.
+ final Set factories = new HashSet();
+ for (CheckType otherType : APIUtils.getWithChildren(checkType)){
+ final CheckDataFactory otherFactory = otherType.getDataFactory();
+ if (otherFactory != null) factories.add(otherFactory);
+ }
+ // Remove data.
+ for (final CheckDataFactory otherFactory : factories){
+ if (otherFactory.removeData(playerName) != null) had = true;
+ }
+
+ return had;
+ }
+
/**
* Clear player related data, only for registered components (not execution history, violation history, normal check data).
- * That should at least go for chat engione data.
+ * That should at least go for chat engine data.
* @param CheckType
* @param PlayerName
* @return If something was removed.
@@ -249,7 +316,7 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
}
/**
- * Cleanup method.
+ * Cleanup method, removes all data and config, but does not call ConfigManager.cleanup.
*/
public void onDisable() {
clearData(CheckType.ALL);
diff --git a/src/fr/neatmonster/nocheatplus/players/Permissions.java b/src/fr/neatmonster/nocheatplus/players/Permissions.java
index c0d94c52..506b1820 100644
--- a/src/fr/neatmonster/nocheatplus/players/Permissions.java
+++ b/src/fr/neatmonster/nocheatplus/players/Permissions.java
@@ -52,7 +52,7 @@ public class Permissions {
// Permissions for the individual checks.
- private static final String CHECKS = NOCHEATPLUS + ".checks";
+ public static final String CHECKS = NOCHEATPLUS + ".checks";
/*
* 888 88b, 888 888 888 88b, 888
@@ -61,7 +61,7 @@ public class Permissions {
* 888 88b, 888 Y888 888P Y888 , 888 b 888 88b, 888 888 , ,ee 888 888 b
* 888 88P' 888 "88 88" "88,e8' 888 8b 888 88P' 888 "YeeP" "88 888 888 8b
*/
- private static final String BLOCKBREAK = CHECKS + ".blockbreak";
+ public static final String BLOCKBREAK = CHECKS + ".blockbreak";
public static final String BLOCKBREAK_BREAK = BLOCKBREAK + ".break";
public static final String BLOCKBREAK_BREAK_LIQUID = BLOCKBREAK_BREAK + ".liquid";
public static final String BLOCKBREAK_DIRECTION = BLOCKBREAK + ".direction";
@@ -78,7 +78,7 @@ public class Permissions {
* 888 88b, 888 Y888 888P Y888 , 888 b 888 888 888 888 888 , 888 ,ee 888 Y888 , 888
* 888 88P' 888 "88 88" "88,e8' 888 8b 888 888 888 888 "YeeP" 888 "88 888 "88,e8' 888
*/
- private static final String BLOCKINTERACT = CHECKS + ".blockinteract";
+ public static final String BLOCKINTERACT = CHECKS + ".blockinteract";
public static final String BLOCKINTERACT_DIRECTION = BLOCKINTERACT + ".direction";
public static final String BLOCKINTERACT_REACH = BLOCKINTERACT + ".reach";
@@ -89,9 +89,9 @@ public class Permissions {
* 888 88b, 888 Y888 888P Y888 , 888 b 888 888 ,ee 888 Y888 , 888 ,
* 888 88P' 888 "88 88" "88,e8' 888 8b 888 888 "88 888 "88,e8' "YeeP"
*/
- private static final String BLOCKPLACE = CHECKS + ".blockplace";
+ public static final String BLOCKPLACE = CHECKS + ".blockplace";
public static final String BLOCKPLACE_AGAINST = BLOCKPLACE + ".against";
- public static final String BLOCKPLACE_AGAINST_AIR = BLOCKPLACE_AGAINST + ".air";
+ public static final String BLOCKPLACE_AGAINST_AIR = BLOCKPLACE_AGAINST + ".air";
public static final String BLOCKPLACE_AGAINST_LIQUIDS = BLOCKPLACE_AGAINST + ".liquids";
public static final String BLOCKPLACE_DIRECTION = BLOCKPLACE + ".direction";
public static final String BLOCKPLACE_FASTPLACE = BLOCKPLACE + ".fastplace";
@@ -106,19 +106,19 @@ public class Permissions {
* Y888 ,d 888 888 ,ee 888 888
* "88,d88 888 888 "88 888 888
*/
- private static final String CHAT = CHECKS + ".chat";
+ public static final String CHAT = CHECKS + ".chat";
public static final String CHAT_CAPTCHA = CHAT + ".captcha";
public static final String CHAT_COLOR = CHAT + ".color";
public static final String CHAT_COMMANDS = CHAT + ".commands";
- public static final String CHAT_TEXT = CHAT + ".text";
+ public static final String CHAT_TEXT = CHAT + ".text";
public static final String CHAT_LOGINS = CHAT + ".logins";
public static final String CHAT_RELOG = CHAT + ".relog";
/*
* Combined !
*/
- private static final String COMBINED = CHECKS + ".combined";
- public static final String COMBINED_IMPROBABLE = COMBINED + ".improbable";
+ public static final String COMBINED = CHECKS + ".combined";
+ public static final String COMBINED_IMPROBABLE = COMBINED + ".improbable";
/*
* 888'Y88 ,e, 888 d8
@@ -129,7 +129,7 @@ public class Permissions {
* , 88P
* "8",P"
*/
- private static final String FIGHT = CHECKS + ".fight";
+ public static final String FIGHT = CHECKS + ".fight";
public static final String FIGHT_ANGLE = FIGHT + ".angle";
public static final String FIGHT_CRITICAL = FIGHT + ".critical";
public static final String FIGHT_DIRECTION = FIGHT + ".direction";
@@ -149,7 +149,7 @@ public class Permissions {
* 888
* 888
*/
- private static final String INVENTORY = CHECKS + ".inventory";
+ public static final String INVENTORY = CHECKS + ".inventory";
public static final String INVENTORY_DROP = INVENTORY + ".drop";
public static final String INVENTORY_FASTCLICK = INVENTORY + ".fastclick";
public static final String INVENTORY_INSTANTBOW = INVENTORY + ".instantbow";
@@ -165,7 +165,7 @@ public class Permissions {
* , 88P
* "8",P"
*/
- private static final String MOVING = CHECKS + ".moving";
+ public static final String MOVING = CHECKS + ".moving";
public static final String MOVING_BOATSANYWHERE = MOVING + ".boatsanywhere";
public static final String MOVING_CREATIVEFLY = MOVING + ".creativefly";
public static final String MOVING_MOREPACKETS = MOVING + ".morepackets";
diff --git a/src/fr/neatmonster/nocheatplus/utilities/ActionFrequency.java b/src/fr/neatmonster/nocheatplus/utilities/ActionFrequency.java
index da22dda8..99055abd 100644
--- a/src/fr/neatmonster/nocheatplus/utilities/ActionFrequency.java
+++ b/src/fr/neatmonster/nocheatplus/utilities/ActionFrequency.java
@@ -107,8 +107,14 @@ public class ActionFrequency {
cf *= factor;
}
return score;
-
-
+ }
+
+ /**
+ * Get reference time.
+ * @return
+ */
+ public final long lastAccess(){
+ return time;
}
}
diff --git a/src/fr/neatmonster/nocheatplus/utilities/BlockCache.java b/src/fr/neatmonster/nocheatplus/utilities/BlockCache.java
index 43a67900..d063f074 100644
--- a/src/fr/neatmonster/nocheatplus/utilities/BlockCache.java
+++ b/src/fr/neatmonster/nocheatplus/utilities/BlockCache.java
@@ -1,8 +1,5 @@
package fr.neatmonster.nocheatplus.utilities;
-import java.util.HashMap;
-import java.util.Map;
-
import net.minecraft.server.IBlockAccess;
import net.minecraft.server.Material;
import net.minecraft.server.TileEntity;
@@ -11,70 +8,25 @@ import net.minecraft.server.Vec3DPool;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
+import fr.neatmonster.nocheatplus.utilities.ds.CoordMap;
+
/**
* Access to type-ids and data using caching techniques.
* @author mc_dev
*
*/
public class BlockCache implements IBlockAccess{
- /**
- * TODO: Make a map for faster queries (without object creation).
- * TODO: Not sure the prime numbers are too big for normal use.
- * @author mc_dev
- *
- */
- private static class Pos3D{
- private static final int p1 = 73856093;
- private static final int p2 = 19349663;
- private static final int p3 = 83492791;
- // Cube coordinates:
- public final int x;
- public final int y;
- public final int z;
- public final int hash;
- /**
- *
- * @param x
- * @param y
- * @param z
- * @param size
- */
- public Pos3D (final int x, final int y, final int z){
- // Cube related coordinates:
- this.x = x;
- this.y = y;
- this.z = z;
- // Hash
- hash = getHash(this.x, this.y, this.z);
- }
-
- @Override
- public final boolean equals(final Object obj) {
- if (obj instanceof Pos3D){
- final Pos3D other = (Pos3D) obj;
- return other.x == x && other.y == y && other.z == z;
- }
- else return false;
- }
-
- @Override
- public final int hashCode() {
- return hash;
- }
-
- public static final int getHash(final int x, final int y, final int z) {
- return p1 * x ^ p2 * y ^ p3 * z;
- }
- }
/**
* For getting ids.
*/
private IBlockAccess access = null;
+
/** Cached type-ids. */
- private final Map idMap = new HashMap();
- /** Cahced data values. */
- private final Map dataMap = new HashMap();
+ private final CoordMap idMap = new CoordMap();
+
+ /** Cached data values. */
+ private final CoordMap dataMap = new CoordMap();
// TODO: maybe make very fast access arrays for the ray tracing checks.
// private int[] id = null;
@@ -117,23 +69,21 @@ public class BlockCache implements IBlockAccess{
}
- public int getTypeId(final int x, final int y, final int z){
- final Pos3D pos = new Pos3D(x, y, z);
- final Integer pId = idMap.get(pos);
- if (pId != null) return pId;
- final Integer nId = access.getTypeId(x, y, z);
- idMap.put(pos, nId);
- return nId;
- }
-
- public int getData(final int x, final int y, final int z){
- final Pos3D pos = new Pos3D(x, y, z);
- final Integer pData = dataMap.get(pos);
- if (pData != null) return pData;
- final Integer nData = access.getData(x, y, z);
- dataMap.put(pos, nData);
- return nData;
- }
+ public int getTypeId(final int x, final int y, final int z) {
+ final Integer pId = idMap.get(x, y, z);
+ if (pId != null) return pId;
+ final Integer nId = access.getTypeId(x, y, z);
+ idMap.put(x, y, z, nId);
+ return nId;
+ }
+
+ public int getData(final int x, final int y, final int z) {
+ final Integer pData = dataMap.get(x, y, z);
+ if (pData != null) return pData;
+ final Integer nData = access.getData(x, y, z);
+ dataMap.put(x, y, z, nData);
+ return nData;
+ }
/**
* Not Optimized.
diff --git a/src/fr/neatmonster/nocheatplus/utilities/BlockProperties.java b/src/fr/neatmonster/nocheatplus/utilities/BlockProperties.java
index 70fc9770..879843e0 100644
--- a/src/fr/neatmonster/nocheatplus/utilities/BlockProperties.java
+++ b/src/fr/neatmonster/nocheatplus/utilities/BlockProperties.java
@@ -261,6 +261,8 @@ public class BlockProperties {
public static final int F_HEIGHT150 = 0x40;
/** The player can stand on these, sneaking or not. */
public static final int F_GROUND = 0x80;
+ /** 1 block height. */
+ public static final int F_HEIGHT100 = 0x100;
static{
init();
@@ -328,7 +330,7 @@ public class BlockProperties {
for (final Material mat : new Material[] {Material.NETHER_BRICK_STAIRS,
Material.COBBLESTONE_STAIRS, Material.SMOOTH_STAIRS, Material.BRICK_STAIRS, Material.SANDSTONE_STAIRS,
Material.WOOD_STAIRS, Material.SPRUCE_WOOD_STAIRS, Material.BIRCH_WOOD_STAIRS, Material.JUNGLE_WOOD_STAIRS}){
- blockFlags[mat.getId()] |= F_STAIRS;
+ blockFlags[mat.getId()] |= F_STAIRS | F_HEIGHT100;
}
// WATER.
for (final Material mat : new Material[]{
@@ -356,6 +358,7 @@ public class BlockProperties {
}){
blockFlags[mat.getId()] |= F_GROUND;
}
+ blockFlags[Material.ENDER_PORTAL_FRAME.getId()] |= F_HEIGHT100;
// Ignore for passable.
for (final Material mat : new Material[]{
Material.WOOD_PLATE, Material.STONE_PLATE,
@@ -502,6 +505,7 @@ public class BlockProperties {
blocks[Material.SKULL.getId()] = new BlockProps(noTool, 8.5f, secToMs(1.45)); // TODO
blockFlags[Material.SKULL.getId()] |= F_GROUND;
blocks[Material.ANVIL.getId()] = new BlockProps(woodPickaxe, 5f); // TODO
+ blockFlags[Material.FLOWER_POT.getId()] |= F_GROUND;
// Indestructible.
for (Material mat : new Material[]{
@@ -977,6 +981,17 @@ public class BlockProperties {
final IBlockAccess access = ((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle();
return isPassable(access, loc.getX(), loc.getY(), loc.getZ(), access.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
+
+ /**
+ * Convenience method to allow using an already fetched or prepared IBlockAccess.
+ * @param access
+ * @param loc
+ * @return
+ */
+ public static final boolean isPassable(final IBlockAccess access, final Location loc)
+ {
+ return isPassable(access, loc.getX(), loc.getY(), loc.getZ(), access.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
+ }
/**
* API access to read extra properties from files.
@@ -1093,7 +1108,7 @@ public class BlockProperties {
final double bmaxY;
// if ((blockFlags[id] & F_HEIGHT150) != 0) block.maxY = 1.5;
if ((blockFlags[id] & F_HEIGHT150) != 0) bmaxY = 1.5;
- else if (isStairs(id)) bmaxY = 1.0;
+ else if ((blockFlags[id] & F_HEIGHT100) != 0) bmaxY = 1.0;
else bmaxY = block.y(); // maxY
// if (minX > block.maxX + x || maxX < block.minX + x) return false;
// else if (minY > block.maxY + y || maxY < block.minY + y) return false;
diff --git a/src/fr/neatmonster/nocheatplus/utilities/CheckUtils.java b/src/fr/neatmonster/nocheatplus/utilities/CheckUtils.java
index 7c79e702..d2157b73 100644
--- a/src/fr/neatmonster/nocheatplus/utilities/CheckUtils.java
+++ b/src/fr/neatmonster/nocheatplus/utilities/CheckUtils.java
@@ -8,6 +8,7 @@ import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
+
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
@@ -18,194 +19,195 @@ import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
/**
- * The Class CheckUtils.
+ * Random auxiliary gear, some might have general quality.
*/
public class CheckUtils {
- /** The file logger. */
- public static Logger fileLogger = null;
-
- /** Decimal format for "#.###" */
- public static final DecimalFormat fdec3 = new DecimalFormat();
-
- static{
- DecimalFormatSymbols sym = fdec3.getDecimalFormatSymbols();
- sym.setDecimalSeparator('.');
- fdec3.setDecimalFormatSymbols(sym);
- fdec3.setMaximumFractionDigits(3);
- fdec3.setMinimumIntegerDigits(1);
- }
+ /** The file logger. */
+ public static Logger fileLogger = null;
- /**
- * Check if a player looks at a target of a specific size, with a specific precision value (roughly).
- *
- * @param player
- * the player
- * @param targetX
- * the target x
- * @param targetY
- * the target y
- * @param targetZ
- * the target z
- * @param targetWidth
- * the target width
- * @param targetHeight
- * the target height
- * @param precision
- * the precision
- * @return the double
- */
- public static double directionCheck(final Player player, final double targetX, final double targetY,
- final double targetZ, final double targetWidth, final double targetHeight, final double precision) {
+ /** Decimal format for "#.###" */
+ public static final DecimalFormat fdec3 = new DecimalFormat();
- // Get the eye location of the player.
- final Location eyes = player.getEyeLocation();
+ static {
+ DecimalFormatSymbols sym = fdec3.getDecimalFormatSymbols();
+ sym.setDecimalSeparator('.');
+ fdec3.setDecimalFormatSymbols(sym);
+ fdec3.setMaximumFractionDigits(3);
+ fdec3.setMinimumIntegerDigits(1);
+ }
- final double factor = Math.sqrt(Math.pow(eyes.getX() - targetX, 2) + Math.pow(eyes.getY() - targetY, 2)
- + Math.pow(eyes.getZ() - targetZ, 2));
+ /**
+ * Check if a player looks at a target of a specific size, with a specific
+ * precision value (roughly).
+ *
+ * @param player
+ * the player
+ * @param targetX
+ * the target x
+ * @param targetY
+ * the target y
+ * @param targetZ
+ * the target z
+ * @param targetWidth
+ * the target width
+ * @param targetHeight
+ * the target height
+ * @param precision
+ * the precision
+ * @return the double
+ */
+ public static double directionCheck(final Player player, final double targetX, final double targetY, final double targetZ, final double targetWidth, final double targetHeight, final double precision)
+ {
- // Get the view direction of the player.
- final Vector direction = eyes.getDirection();
+ // Get the eye location of the player.
+ final Location eyes = player.getEyeLocation();
- final double x = targetX - eyes.getX();
- final double y = targetY - eyes.getY();
- final double z = targetZ - eyes.getZ();
+ final double factor = Math.sqrt(Math.pow(eyes.getX() - targetX, 2) + Math.pow(eyes.getY() - targetY, 2) + Math.pow(eyes.getZ() - targetZ, 2));
- final double xPrediction = factor * direction.getX();
- final double yPrediction = factor * direction.getY();
- final double zPrediction = factor * direction.getZ();
+ // Get the view direction of the player.
+ final Vector direction = eyes.getDirection();
- double off = 0.0D;
+ final double x = targetX - eyes.getX();
+ final double y = targetY - eyes.getY();
+ final double z = targetZ - eyes.getZ();
- off += Math.max(Math.abs(x - xPrediction) - (targetWidth / 2 + precision), 0.0D);
- off += Math.max(Math.abs(z - zPrediction) - (targetWidth / 2 + precision), 0.0D);
- off += Math.max(Math.abs(y - yPrediction) - (targetHeight / 2 + precision), 0.0D);
+ final double xPrediction = factor * direction.getX();
+ final double yPrediction = factor * direction.getY();
+ final double zPrediction = factor * direction.getZ();
- if (off > 1)
- off = Math.sqrt(off);
+ double off = 0.0D;
- return off;
- }
+ off += Math.max(Math.abs(x - xPrediction) - (targetWidth / 2 + precision), 0.0D);
+ off += Math.max(Math.abs(z - zPrediction) - (targetWidth / 2 + precision), 0.0D);
+ off += Math.max(Math.abs(y - yPrediction) - (targetHeight / 2 + precision), 0.0D);
- /**
- * Calculate the distance between two location, because for Bukkit distance is the distance squared and
- * distanceSquared is the distance non-squared.
- *
- * @param location1
- * the location1
- * @param location2
- * the location2
- * @return the double
- */
- public static double distance(final Location location1, final Location location2) {
- return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2)
- + Math.pow(location2.getY() - location1.getY(), 2) + Math.pow(location2.getZ() - location1.getZ(), 2));
- }
+ if (off > 1) off = Math.sqrt(off);
- /**
- * Return if the two Strings are similar based on the given threshold.
- *
- * @param s
- * the first String, must not be null
- * @param t
- * the second String, must not be null
- * @param threshold
- * the minimum value of the correlation coefficient
- * @return result true if the two Strings are similar, false otherwise
- */
- public static boolean isSimilar(final String s, final String t, final float threshold) {
- return 1.0f - (float) levenshteinDistance(s, t) / Math.max(1.0, Math.max(s.length(), t.length())) > threshold;
- }
+ return off;
+ }
- /**
- * Find the Levenshtein distance between two Strings.
- *
- * This is the number of changes needed to change one String into another, where each change is a single character
- * modification (deletion, insertion or substitution).
- *
- * @param s
- * the first String, must not be null
- * @param t
- * the second String, must not be null
- * @return result distance
- */
- private static int levenshteinDistance(CharSequence s, CharSequence t) {
- if (s == null || t == null)
- throw new IllegalArgumentException("Strings must not be null");
+ /**
+ * Calculate the distance between two location, because for Bukkit distance
+ * is the distance squared and distanceSquared is the distance non-squared.
+ *
+ * @param location1
+ * the location1
+ * @param location2
+ * the location2
+ * @return the double
+ */
+ public static double distance(final Location location1, final Location location2)
+ {
+ return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) + Math.pow(location2.getY() - location1.getY(), 2) + Math.pow(location2.getZ() - location1.getZ(), 2));
+ }
- int n = s.length();
- int m = t.length();
+ /**
+ * Return if the two Strings are similar based on the given threshold.
+ *
+ * @param s
+ * the first String, must not be null
+ * @param t
+ * the second String, must not be null
+ * @param threshold
+ * the minimum value of the correlation coefficient
+ * @return result true if the two Strings are similar, false otherwise
+ */
+ public static boolean isSimilar(final String s, final String t, final float threshold)
+ {
+ return 1.0f - (float) levenshteinDistance(s, t) / Math.max(1.0, Math.max(s.length(), t.length())) > threshold;
+ }
- if (n == 0)
- return m;
- else if (m == 0)
- return n;
+ /**
+ * Find the Levenshtein distance between two Strings.
+ *
+ * This is the number of changes needed to change one String into another,
+ * where each change is a single character modification (deletion, insertion or substitution).
+ *
+ * @param s
+ * the first String, must not be null
+ * @param t
+ * the second String, must not be null
+ * @return result distance
+ */
+ private static int levenshteinDistance(CharSequence s, CharSequence t) {
+ if (s == null || t == null) throw new IllegalArgumentException("Strings must not be null");
- if (n > m) {
- final CharSequence tmp = s;
- s = t;
- t = tmp;
- n = m;
- m = t.length();
- }
+ int n = s.length();
+ int m = t.length();
- int p[] = new int[n + 1];
- int d[] = new int[n + 1];
- int _d[];
+ if (n == 0) return m;
+ else if (m == 0) return n;
- int i;
- int j;
+ if (n > m) {
+ final CharSequence tmp = s;
+ s = t;
+ t = tmp;
+ n = m;
+ m = t.length();
+ }
- char t_j;
+ int p[] = new int[n + 1];
+ int d[] = new int[n + 1];
+ int _d[];
- int cost;
+ int i;
+ int j;
- for (i = 0; i <= n; i++)
- p[i] = i;
+ char t_j;
- for (j = 1; j <= m; j++) {
- t_j = t.charAt(j - 1);
- d[0] = j;
+ int cost;
- for (i = 1; i <= n; i++) {
- cost = s.charAt(i - 1) == t_j ? 0 : 1;
- d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
- }
+ for (i = 0; i <= n; i++)
+ p[i] = i;
- _d = p;
- p = d;
- d = _d;
- }
+ for (j = 1; j <= m; j++) {
+ t_j = t.charAt(j - 1);
+ d[0] = j;
- return p[n];
- }
-
- /**
- * Join parts with link.
- * @param input
- * @param link
- * @return
- */
- public static String join(final Collection input, final String link){
- final StringBuilder builder = new StringBuilder(Math.max(300, input.size() * 10));
- boolean first = true;
- for (final Object obj : input){
- if (!first) builder.append(link);
- builder.append(obj.toString());
- first = false;
- }
- return builder.toString();
- }
-
- /**
- * Convenience method.
- * @param parts
- * @param link
- * @return
- */
- public static boolean scheduleOutputJoined(final List parts, String link){
- return scheduleOutput(join(parts, link));
- }
+ for (i = 1; i <= n; i++) {
+ cost = s.charAt(i - 1) == t_j ? 0 : 1;
+ d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
+ }
+
+ _d = p;
+ p = d;
+ d = _d;
+ }
+
+ return p[n];
+ }
+
+ /**
+ * Join parts with link.
+ *
+ * @param input
+ * @param link
+ * @return
+ */
+ public static String join(final Collection input, final String link)
+ {
+ final StringBuilder builder = new StringBuilder(Math.max(300, input.size() * 10));
+ boolean first = true;
+ for (final Object obj : input) {
+ if (!first) builder.append(link);
+ builder.append(obj.toString());
+ first = false;
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Convenience method.
+ *
+ * @param parts
+ * @param link
+ * @return
+ */
+ public static boolean scheduleOutputJoined(final List parts, String link)
+ {
+ return scheduleOutput(join(parts, link));
+ }
/**
* Schedule a message to be output by the bukkit logger.
@@ -307,15 +309,15 @@ public class CheckUtils {
/**
* Get the height from getLocation().getY() to head / above head.
* NOTE: Currently this is pretty much useless, it returns 1.0 most of the time.
+ *
* @param entity
* @return
*/
- public static double getHeight(final Entity entity) {
- final net.minecraft.server.Entity mcEntity = ((CraftEntity)entity).getHandle();
- final double entityHeight = mcEntity.height;
- if (entity instanceof LivingEntity){
- return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight);
- }
- else return mcEntity.height;
- }
+ public static double getHeight(final Entity entity) {
+ final net.minecraft.server.Entity mcEntity = ((CraftEntity) entity).getHandle();
+ final double entityHeight = mcEntity.height;
+ if (entity instanceof LivingEntity) {
+ return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight);
+ } else return mcEntity.height;
+ }
}
diff --git a/src/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java b/src/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java
index 8ff4420c..c0db195c 100644
--- a/src/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java
+++ b/src/fr/neatmonster/nocheatplus/utilities/PlayerLocation.java
@@ -334,10 +334,13 @@ public class PlayerLocation {
onGround = BlockProperties.isOnGround(getBlockAccess(), minX, minY - yOnGround, minZ, maxX, maxY + 0.25, maxZ);
if (!onGround){
// TODO: Probably check other ids too before doing this ?
- final double d0 = 0.25D;
+ final double d0 = 1D; //0.25D;
+// for (org.bukkit.entity.Entity test : entity.getBukkitEntity().getNearbyEntities(1, 1, 1)){
+// System.out.println("*" + test.getType());
+// }
final AxisAlignedBB box = useBox.b(minX - d0, minY - getyOnGround() - d0, minZ - d0, maxX + d0, maxY + d0, maxZ + d0);
@SuppressWarnings("rawtypes")
- List list = worldServer.getEntities(entity, box);
+ final List list = worldServer.getEntities(entity, box);
@SuppressWarnings("rawtypes")
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
diff --git a/src/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java b/src/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java
new file mode 100644
index 00000000..79924ea6
--- /dev/null
+++ b/src/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java
@@ -0,0 +1,162 @@
+package fr.neatmonster.nocheatplus.utilities.ds;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Map int coordinates to values (just for fun). Intended for Minecraft coordinates, probably not for too high values.
+ * This implementation is not thread safe, though changing values and get/contains should work if the map stays unchanged.
+ * @author mc_dev
+ *
+ * @param
+ */
+public class CoordMap {
+
+ private static final int p1 = 73856093;
+ private static final int p2 = 19349663;
+ private static final int p3 = 83492791;
+
+ private static final int getHash(final int x, final int y, final int z) {
+ return p1 * x ^ p2 * y ^ p3 * z;
+ }
+
+ private static final class Entry{
+ protected final int x;
+ protected final int y;
+ protected final int z;
+ protected V value;
+ protected final int hash;
+ public Entry(final int x, final int y, final int z, final V value, final int hash){
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.value = value;
+ this.hash = hash;
+ }
+ }
+
+ // Core data.
+ private final float loadFactor;
+ private List>[] entries;
+ /** Current size. */
+ private int size = 0;
+
+ public CoordMap(){
+ this(10, 0.75f);
+ }
+
+ public CoordMap(final int initialCapacity){
+ this(initialCapacity, 0.75f);
+ }
+
+ /**
+ *
+ * @param initialCapacity Initial internal array size.
+ * TODO: change to expected number of elements (len = cap/load).
+ * @param loadFactor
+ */
+ @SuppressWarnings("unchecked")
+ public CoordMap(final int initialCapacity, float loadFactor){
+ this.loadFactor = loadFactor;
+ entries = new List[initialCapacity];
+ }
+
+ /**
+ * Check if the map contains a value for the given coordinates.
+ * NOTE: Delegates to get, use get for fastest checks.
+ * @param x
+ * @param y
+ * @param z
+ * @return
+ */
+ public final boolean contains(final int x, final int y, final int z){
+ return get(x,y,z) != null;
+ }
+
+ /**
+ * Get the value if there is a mapping for the given coordinates.
+ * @param x
+ * @param y
+ * @param z
+ * @return
+ */
+ public final V get(final int x, final int y, final int z){
+ final int hash = getHash(x, y, z);
+ final int slot = Math.abs(hash) % entries.length;
+ final List> bucket = entries[slot];
+ if (bucket == null) return null;;
+ for (final Entry entry : bucket){
+ if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) return entry.value;
+ }
+ return null;
+ }
+
+ /**
+ * Add value with the coordinates + hash from the last contains call.
+ * @param value
+ * @return If a value was replaced.
+ */
+ public final boolean put(final int x, final int y, final int z, final V value){
+ final int hash = getHash(x, y, z);
+ final int absHash = Math.abs(hash);
+ int slot = absHash % entries.length;
+ List> bucket = entries[slot];
+ if (bucket != null){
+ for (final Entry entry : bucket){
+ if (hash == entry.hash && x == entry.x && y == entry.z && z == entry.y){
+ entry.value = value;
+ return true;
+ }
+ }
+ }
+ else if (size + 1 > entries.length * loadFactor){
+ resize(size + 1);
+ slot = absHash % entries.length;
+ bucket = entries[slot];
+ }
+ if (bucket == null) {
+ // TODO: use array list ?
+ bucket = new LinkedList>();
+ entries[slot] = bucket;
+ }
+ entries[slot] = bucket;
+ bucket.add(new Entry(x, y, z, value, hash));
+ size ++;
+ return false;
+ }
+
+ private final void resize(final int size) {
+ // TODO: other capacity?
+ final int newCapacity = (int) ((size + 4) / loadFactor);
+ @SuppressWarnings("unchecked")
+ final List>[] newEntries = new List[newCapacity];
+ for (int oldSlot = 0; oldSlot < entries.length; oldSlot++){
+ final List> oldBucket = entries[oldSlot];
+ if (oldBucket != null){
+ for (final Entry entry : oldBucket){
+ final int newSlot = Math.abs(entry.hash) % newCapacity;
+ List> newBucket = newEntries[oldSlot];
+ if (newBucket == null){
+ newBucket = new LinkedList>();
+ newEntries[newSlot] = newBucket;
+ }
+ newBucket.add(entry);
+ }
+ oldBucket.clear();
+ }
+ entries[oldSlot] = null;
+ }
+ entries = newEntries;
+ }
+
+ public final int size(){
+ return size;
+ }
+
+ public void clear(){
+ size = 0;
+ Arrays.fill(entries, null);
+ // TODO: resize ?
+ }
+}