Safety fuck up commit.

This commit is contained in:
asofold 2012-11-06 07:56:53 +01:00
parent 81ec563474
commit 3a1e9fe7d8
20 changed files with 1025 additions and 619 deletions

View File

@ -21,8 +21,9 @@ package fr.neatmonster.nocheatplus.actions;
* Some wildcards that are used in commands and log messages. * Some wildcards that are used in commands and log messages.
*/ */
public enum ParameterName { public enum ParameterName {
CHECK("check"),
BLOCK_ID("blockid"), BLOCK_ID("blockid"),
CHECK("check"),
TAGS("tags"),
DISTANCE("distance"), DISTANCE("distance"),
FALL_DISTANCE("falldistance"), FALL_DISTANCE("falldistance"),
FOOD("food"), FOOD("food"),

View File

@ -1,8 +1,5 @@
package fr.neatmonster.nocheatplus.checks; package fr.neatmonster.nocheatplus.checks;
import java.util.HashSet;
import java.util.Set;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.access.CheckConfigFactory; 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.inventory.InventoryData;
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig; import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
import fr.neatmonster.nocheatplus.checks.moving.MovingData; 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; 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). * Type of checks (containing configuration and dataFactory classes, name and permission).
*/ */
public enum CheckType { 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_DIRECTION(BLOCKBREAK, Permissions.BLOCKBREAK_DIRECTION),
BLOCKBREAK_FASTBREAK(BLOCKBREAK, Permissions.BLOCKBREAK_FASTBREAK), BLOCKBREAK_FASTBREAK(BLOCKBREAK, Permissions.BLOCKBREAK_FASTBREAK),
BLOCKBREAK_FREQUENCY(BLOCKBREAK, Permissions.BLOCKBREAK_FREQUENCY), BLOCKBREAK_FREQUENCY(BLOCKBREAK, Permissions.BLOCKBREAK_FREQUENCY),
@ -50,18 +47,18 @@ public enum CheckType {
BLOCKBREAK_REACH(BLOCKBREAK, Permissions.BLOCKBREAK_REACH), BLOCKBREAK_REACH(BLOCKBREAK, Permissions.BLOCKBREAK_REACH),
BLOCKBREAK_WRONGBLOCK(BLOCKBREAK, Permissions.BLOCKBREAK_WRONGBLOCK), 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_DIRECTION(BLOCKINTERACT, Permissions.BLOCKINTERACT_DIRECTION),
BLOCKINTERACT_REACH(BLOCKINTERACT, Permissions.BLOCKINTERACT_REACH), 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_DIRECTION(BLOCKPLACE, Permissions.BLOCKPLACE_DIRECTION),
BLOCKPLACE_FASTPLACE(BLOCKPLACE, Permissions.BLOCKPLACE_FASTPLACE), BLOCKPLACE_FASTPLACE(BLOCKPLACE, Permissions.BLOCKPLACE_FASTPLACE),
BLOCKPLACE_NOSWING(BLOCKPLACE, Permissions.BLOCKPLACE_NOSWING), BLOCKPLACE_NOSWING(BLOCKPLACE, Permissions.BLOCKPLACE_NOSWING),
BLOCKPLACE_REACH(BLOCKPLACE, Permissions.BLOCKBREAK_REACH), BLOCKPLACE_REACH(BLOCKPLACE, Permissions.BLOCKBREAK_REACH),
BLOCKPLACE_SPEED(BLOCKPLACE, Permissions.BLOCKPLACE_SPEED), 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_CAPTCHA(CHAT, Permissions.CHAT_CAPTCHA),
CHAT_COLOR(CHAT, Permissions.CHAT_COLOR), CHAT_COLOR(CHAT, Permissions.CHAT_COLOR),
CHAT_COMMANDS(CHAT, Permissions.CHAT_COMMANDS), CHAT_COMMANDS(CHAT, Permissions.CHAT_COMMANDS),
@ -70,10 +67,10 @@ public enum CheckType {
CHAT_RELOG(CHAT, Permissions.CHAT_RELOG), CHAT_RELOG(CHAT, Permissions.CHAT_RELOG),
COMBINED(CombinedConfig.factory, CombinedData.factory), COMBINED(CombinedConfig.factory, CombinedData.factory, Permissions.COMBINED),
COMBINED_IMPROBABLE(COMBINED, Permissions.COMBINED_IMPROBABLE), 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_ANGLE(FIGHT, Permissions.FIGHT_ANGLE),
FIGHT_CRITICAL(FIGHT, Permissions.FIGHT_CRITICAL), FIGHT_CRITICAL(FIGHT, Permissions.FIGHT_CRITICAL),
FIGHT_DIRECTION(FIGHT, Permissions.FIGHT_DIRECTION), FIGHT_DIRECTION(FIGHT, Permissions.FIGHT_DIRECTION),
@ -84,14 +81,14 @@ public enum CheckType {
FIGHT_SELFHIT(FIGHT, Permissions.FIGHT_SELFHIT), FIGHT_SELFHIT(FIGHT, Permissions.FIGHT_SELFHIT),
FIGHT_SPEED(FIGHT, Permissions.FIGHT_SPEED), 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_DROP(INVENTORY, Permissions.INVENTORY_DROP),
INVENTORY_FASTCLICK(INVENTORY, Permissions.INVENTORY_FASTCLICK), INVENTORY_FASTCLICK(INVENTORY, Permissions.INVENTORY_FASTCLICK),
INVENTORY_INSTANTBOW(INVENTORY, Permissions.INVENTORY_INSTANTBOW), INVENTORY_INSTANTBOW(INVENTORY, Permissions.INVENTORY_INSTANTBOW),
INVENTORY_INSTANTEAT(INVENTORY, Permissions.INVENTORY_INSTANTEAT), INVENTORY_INSTANTEAT(INVENTORY, Permissions.INVENTORY_INSTANTEAT),
INVENTORY_ITEMS(INVENTORY, Permissions.INVENTORY_ITEMS), 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_CREATIVEFLY(MOVING, Permissions.MOVING_CREATIVEFLY),
MOVING_MOREPACKETS(MOVING, Permissions.MOVING_MOREPACKETS), MOVING_MOREPACKETS(MOVING, Permissions.MOVING_MOREPACKETS),
MOVING_MOREPACKETSVEHICLE(MOVING, Permissions.MOVING_MOREPACKETSVEHICLE), MOVING_MOREPACKETSVEHICLE(MOVING, Permissions.MOVING_MOREPACKETSVEHICLE),
@ -101,49 +98,64 @@ public enum CheckType {
UNKNOWN; UNKNOWN;
/** The group. */ /** If not null, this is the check group usually. */
private CheckType parent = null; private final CheckType parent;
/** The configFactory. */ /** The check config factory (access CheckConfig instances by CheckType). */
private CheckConfigFactory configFactory = null; private final CheckConfigFactory configFactory;
/** The dataFactory. */ /** The check data factory (access CheckData instances by CheckType). */
private CheckDataFactory dataFactory = null; private final CheckDataFactory dataFactory;
/** The permission. */ /** The bypass permission. */
private String permission = null; private final String permission;
/** /**
* Instantiates a new check type. * Special purpose check types (likely not actual checks).
*/ */
private CheckType() {} private CheckType() {
this(null, null, null);
/**
* 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;
} }
/** /**
* Instantiates a new check type. * Special purpose for grouping (ALL).
* * @param permission
* @param parent */
* the parent 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);
}
/**
* Constructor for sub-checks grouped under another check type.
* @param parent
* @param permission * @param permission
* the permission
*/ */
private CheckType(final CheckType parent, final String permission) { private CheckType(final CheckType parent, final String permission) {
this(parent, permission, parent.getConfigFactory(), parent.getDataFactory());
}
/**
* 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.parent = parent;
configFactory = parent.getConfigFactory();
dataFactory = parent.getDataFactory();
this.permission = permission; this.permission = permission;
this.configFactory = configFactory;
this.dataFactory = dataFactory;
} }
/** /**
@ -183,7 +195,7 @@ public enum CheckType {
} }
/** /**
* Gets the permission. * Gets the bypass permission for this check type.
* *
* @return the permission * @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.<br> * Remove the player data for a given player and a given check type. CheckType.ALL and null will be interpreted as removing all data.<br>
* @deprecated Will be removed, use DataManager.removeData instead.
* @param playerName * @param playerName
* @param checkType * @param checkType
* @return If any data was present. * @return If any data was present.
*/ */
public static boolean removeData(final String playerName, CheckType checkType) { public static boolean removeData(final String playerName, final CheckType checkType) {
if (checkType == null) checkType = ALL; return DataManager.removeData(playerName, checkType);
// 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<CheckDataFactory> factories = new HashSet<CheckDataFactory>();
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;
} }
} }

View File

@ -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){ public void onEntityDamage(final EntityDamageEvent event){
final Entity entity = event.getEntity(); final Entity entity = event.getEntity();
if (!(entity instanceof Player)) return; if (!(entity instanceof Player)) return;

View File

@ -116,9 +116,9 @@ public class FightListener implements Listener {
cancelled = true; cancelled = true;
} }
if (angle.isEnabled(player)) {
if (!cancelled && angle.isEnabled(player)){ // The "fast turning" checks are checked in any case because they accumulate data.
// Improbable yaw. // Improbable yaw changing.
if (Combined.checkYawRate(player, player.getLocation().getYaw(), now, worldName, cc.yawRateCheck)) { if (Combined.checkYawRate(player, player.getLocation().getYaw(), now, worldName, cc.yawRateCheck)) {
// (Check or just feed). // (Check or just feed).
cancelled = true; cancelled = true;
@ -126,10 +126,6 @@ public class FightListener implements Listener {
// Angle check. // Angle check.
if (angle.check(player, worldChanged)) cancelled = true; if (angle.check(player, worldChanged)) cancelled = true;
} }
else{
// Always feed yaw rate here.
Combined.feedYawRate(player, player.getLocation().getYaw(), now, worldName);
}
if (!cancelled && critical.isEnabled(player) && critical.check(player)) if (!cancelled && critical.isEnabled(player) && critical.check(player))
cancelled = true; cancelled = true;

View File

@ -88,6 +88,8 @@ public class MovingData extends ACheckData {
public double fromX = Double.MAX_VALUE, fromY, fromZ; public double fromX = Double.MAX_VALUE, fromY, fromZ;
/** Last to coordinates. */ /** 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. // Data of the creative check.
public boolean creativeFlyPreviousRefused; public boolean creativeFlyPreviousRefused;
@ -153,6 +155,24 @@ public class MovingData extends ACheckData {
clearNoFallData(); 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() { public void clearAccounting() {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
hDistSum.clear(now); hDistSum.clear(now);

View File

@ -26,6 +26,7 @@ import org.bukkit.event.player.PlayerBedLeaveEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.player.PlayerPortalEvent;
@ -208,9 +209,18 @@ public class MovingListener implements Listener {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
final MovingData data = MovingData.getData(player); final MovingData data = MovingData.getData(player);
if (!creativeFly.isEnabled(player) && survivalFly.isEnabled(player) && survivalFly.check(player) && data.ground != null) if (!creativeFly.isEnabled(player) && survivalFly.isEnabled(player) && survivalFly.check(player)) {
// To cancel the event, we simply teleport the player to his last safe location. // To cancel the event, we simply teleport the player to his last
player.teleport(data.ground); // 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 ?
}
} }
/** /**
@ -352,17 +362,8 @@ public class MovingListener implements Listener {
final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle(); final EntityPlayer mcPlayer = ((CraftPlayer) player).getHandle();
// Potion effect "Jump". // Potion effect "Jump".
final double jumpAmplifier; final double jumpAmplifier = MovingListener.getJumpAmplifier(mcPlayer);
if (mcPlayer.hasEffect(MobEffectList.JUMP)) { if (jumpAmplifier > 0D && cc.debug) System.out.println(player.getName() + " Jump effect: " + data.jumpAmplifier);
// 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; 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 // Just try to estimate velocities over time. Not very precise, but works good enough most of the time. Do
@ -388,11 +389,16 @@ public class MovingListener implements Listener {
Location newTo = null; Location newTo = null;
if (passable.isEnabled(player)) newTo = passable.check(player, pFrom, pTo, data, cc); 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. // Optimized checking, giving creativefly permission precedence over survivalfly.
if (newTo != null); if (!player.hasPermission(Permissions.MOVING_CREATIVEFLY)){
else if (!player.hasPermission(Permissions.MOVING_CREATIVEFLY)){
// Either survivalfly or speed check. // Either survivalfly or speed check.
if ((cc.ignoreCreative || player.getGameMode() != GameMode.CREATIVE) && (cc.ignoreAllowFlight || !player.getAllowFlight()) if ((cc.ignoreCreative || player.getGameMode() != GameMode.CREATIVE) && (cc.ignoreAllowFlight || !player.getAllowFlight())
&& cc.survivalFlyCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_SURVIVALFLY) && !player.hasPermission(Permissions.MOVING_SURVIVALFLY)){ && 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(); else data.clearFlyData();
if (newTo == null if (newTo == null && cc.morePacketsCheck && !NCPExemptionManager.isExempted(player, CheckType.MOVING_MOREPACKETS) && !player.hasPermission(Permissions.MOVING_MOREPACKETS)) {
&& 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. // 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); newTo = morePackets.check(player, pFrom, pTo, data, cc);
else } else {
// Otherwise we need to clear his data. // Otherwise we need to clear his data.
data.clearMorePacketsData(); 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? // Did one of the checks decide we need a new "to"-location?
if (newTo != null) { if (newTo != null) {
@ -427,13 +436,16 @@ public class MovingListener implements Listener {
// Remember where we send the player to. // Remember where we send the player to.
data.teleported = newTo; data.teleported = newTo;
} }
// Set positions. // Set positions.
// TODO: Should these be set on monitor ?
data.fromX = from.getX(); data.fromX = from.getX();
data.fromY = from.getY(); data.fromY = from.getY();
data.fromZ = from.getZ(); data.fromZ = from.getZ();
data.toX = to.getX(); data.toX = to.getX();
data.toY = to.getY(); data.toY = to.getY();
data.toZ = to.getZ(); data.toZ = to.getZ();
// Cleanup. // Cleanup.
moveInfo.cleanup(); moveInfo.cleanup();
parkedInfo.add(moveInfo); parkedInfo.add(moveInfo);
@ -518,13 +530,13 @@ public class MovingListener implements Listener {
} }
/** /**
* When a player respawns, all information related to the moving checks becomes invalid. * When a player respawns, all information related to the moving checks
* becomes invalid.
* *
* @param event * @param event
* the event * the event
*/ */
@EventHandler( @EventHandler(priority = EventPriority.MONITOR)
priority = EventPriority.MONITOR)
public void onPlayerRespawn(final PlayerRespawnEvent event) { public void onPlayerRespawn(final PlayerRespawnEvent event) {
/* /*
* ____ _ ____ * ____ _ ____
@ -534,9 +546,14 @@ public class MovingListener implements Listener {
* |_| |_|\__,_|\__, |\___|_| |_| \_\___||___/ .__/ \__,_| \_/\_/ |_| |_| * |_| |_|\__,_|\__, |\___|_| |_| \_\___||___/ .__/ \__,_| \_/\_/ |_| |_|
* |___/ |_| * |___/ |_|
*/ */
final MovingData data = MovingData.getData(event.getPlayer()); final Player player = event.getPlayer();
final MovingData data = MovingData.getData(player);
data.clearFlyData(); data.clearFlyData();
data.clearMorePacketsData(); data.clearMorePacketsData();
if (survivalFly.isEnabled(player)) {
data.setBack = event.getRespawnLocation();
data.ground = event.getRespawnLocation();
}
} }
/** /**
@ -548,8 +565,7 @@ public class MovingListener implements Listener {
* @param event * @param event
* the event * the event
*/ */
@EventHandler( @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST)
ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerTeleport(final PlayerTeleportEvent event) { public void onPlayerTeleport(final PlayerTeleportEvent event) {
/* /*
* ____ _ _____ _ _ * ____ _ _____ _ _
@ -566,17 +582,22 @@ public class MovingListener implements Listener {
// If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no". // If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no".
final Location to = event.getTo(); final Location to = event.getTo();
if (event.isCancelled() && teleported != null && data.teleported.equals(to)){ if (teleported != null && teleported.equals(to)) {
// Teleport by NCP.
// Prevent cheaters getting rid of flying data (morepackets, other).
// TODO: even more strict enforcing ? // TODO: even more strict enforcing ?
if (event.isCancelled()) {
event.setCancelled(false); event.setCancelled(false);
event.setTo(teleported); event.setTo(teleported);
event.setFrom(teleported); event.setFrom(teleported);
data.clearFlyData();
data.resetPositions(teleported);
} }
else{ else{
// Only if it wasn't NoCheatPlus, drop data from more packets check. If it was NoCheatPlus, we don't // Not cancelled but NCP teleport.
// want players to exploit the fly check teleporting to get rid of the "morepackets" data. }
// 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 ! // TODO: check if to do with cancelled teleports !
data.clearMorePacketsData(); data.clearMorePacketsData();
data.clearFlyData(); data.clearFlyData();
@ -715,6 +736,22 @@ public class MovingListener implements Listener {
// Entity fall-distance should be reset elsewhere. // 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) @EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(final PlayerQuitEvent event){ public void onPlayerQuit(final PlayerQuitEvent event){
noFall.onLeave(event.getPlayer()); noFall.onLeave(event.getPlayer());
@ -724,4 +761,15 @@ public class MovingListener implements Listener {
public void onPlayerKick(final PlayerKickEvent event){ public void onPlayerKick(final PlayerKickEvent event){
noFall.onLeave(event.getPlayer()); 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;
}
} }

View File

@ -19,7 +19,8 @@ public class Passable extends Check {
super(CheckType.MOVING_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. // Simple check.
if (!to.isPassable()) { if (!to.isPassable()) {
Location loc = null; // players location if should be used. Location loc = null; // players location if should be used.
@ -30,23 +31,26 @@ public class Passable extends Check {
final int eyeBlockY = Location.locToBlock(eyeY); final int eyeBlockY = Location.locToBlock(eyeY);
if (eyeBlockY != to.getBlockY()) { if (eyeBlockY != to.getBlockY()) {
if (BlockProperties.isPassable(to.getBlockAccess(), to.getX(), eyeY, to.getZ(), to.getTypeId(to.getBlockX(), eyeBlockY, to.getBlockZ()))) { 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. // Allow moving inside the same block if head is
// free.
return null; return null;
} }
} }
// Only allow moving further out of the block (still allows going round in circles :p) // Only allow moving further out of the block (still allows
// going round in circles :p)
// TODO: account for actual bounding box. // TODO: account for actual bounding box.
final Vector blockMiddle = new Vector(0.5 + from.getBlockX(), 0.5 + from.getBlockY(), 0.5 + from.getBlockZ()); 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 one block towards non-solid
// blocks (closest only ?).
// TODO: Allow moving out of half steps ? // TODO: Allow moving out of half steps ?
// TODO: Allow moving towards non solid blocks. // TODO: Allow moving towards non solid blocks.
if (blockMiddle.distanceSquared(from.getVector()) < blockMiddle.distanceSquared(to.getVector())) { if (blockMiddle.distanceSquared(from.getVector()) < blockMiddle.distanceSquared(to.getVector())) {
// Further check for the players location as possible set back. // Further check for the players location as possible
// set back.
loc = player.getLocation(); loc = player.getLocation();
if (to.isSamePos(loc)) { if (to.isSamePos(loc)) {
loc = null; loc = null;
} } else if (!BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()))) {
else if (!BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()))){
// Allow the move // Allow the move
return null; return null;
} }
@ -54,14 +58,19 @@ public class Passable extends Check {
} }
} }
} }
// 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. // Return the reset position.
data.passableVL += 1d; data.passableVL += 1d;
final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions); final ViolationData vd = new ViolationData(this, player, data.passableVL, 1, cc.passableActions);
if (vd.needsParameters()) vd.setParameter(ParameterName.BLOCK_ID, "" + to.getTypeId()); if (vd.needsParameters()) vd.setParameter(ParameterName.BLOCK_ID, "" + to.getTypeId());
if (executeActions(vd)) { if (executeActions(vd)) {
// TODO: Consider another set back position for this, also keeping track of players moving around in blocks. // TODO: Consider another set back position for this, also
// keeping track of players moving around in blocks.
final Location newTo; final Location newTo;
if (!from.isPassable() && loc == null){ if (loc == null && !from.isPassable()) {
// Check if passable. // Check if passable.
loc = player.getLocation(); 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()))) { if (to.isSamePos(loc) || !BlockProperties.isPassable(from.getBlockAccess(), loc.getX(), loc.getY(), loc.getZ(), from.getTypeId(from.getBlockX(), from.getBlockY(), from.getBlockZ()))) {
@ -74,19 +83,16 @@ public class Passable extends Check {
newTo.setPitch(to.getPitch()); newTo.setPitch(to.getPitch());
return newTo; return newTo;
} }
} } else {
else{
data.passableVL *= 0.99; data.passableVL *= 0.99;
} }
return null; return null;
} }
@Override @Override
protected Map<ParameterName, String> getParameterMap(final ViolationData violationData) { protected Map<ParameterName, String> getParameterMap(final ViolationData violationData)
// TODO Auto-generated method stub {
return super.getParameterMap(violationData); return super.getParameterMap(violationData);
} }
} }

View File

@ -1,5 +1,6 @@
package fr.neatmonster.nocheatplus.checks.moving; package fr.neatmonster.nocheatplus.checks.moving;
import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
import net.minecraft.server.EntityPlayer; 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.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData; import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.players.Permissions; import fr.neatmonster.nocheatplus.players.Permissions;
import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
import fr.neatmonster.nocheatplus.utilities.BlockProperties; import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation; import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/* /*
@ -47,6 +50,9 @@ public class SurvivalFly extends Check {
/** Faster moving down stream (water mainly). */ /** Faster moving down stream (water mainly). */
public static final double modDownStream = 0.19 / swimmingSpeed; public static final double modDownStream = 0.19 / swimmingSpeed;
/** To join some tags with moving check violations. */
private final ArrayList<String> tags = new ArrayList<String>(15);
/** /**
* Instantiates a new survival fly check. * 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) { 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(); final long now = System.currentTimeMillis();
tags.clear();
// A player is considered sprinting if the flag is set and if he has enough food level. // 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; final boolean sprinting = player.isSprinting() && player.getFoodLevel() > 5;
@ -112,49 +118,55 @@ public class SurvivalFly extends Check {
boolean resetFrom = fromOnGround || from.isInLiquid() || from.isOnLadder() || from.isInWeb(); boolean resetFrom = fromOnGround || from.isInLiquid() || from.isOnLadder() || from.isInWeb();
final double setBackYDistance = to.getY() - data.setBack.getY(); 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 the player has touched the ground but it hasn't been noticed by
// the plugin, the workaround is here.
if (!resetFrom) { if (!resetFrom) {
// Don't set "useWorkaround = x()", to avoid potential trouble with reordering to come, and similar. // Don't set "useWorkaround = x()", to avoid potential trouble with
// reordering to come, and similar.
boolean useWorkaround = false; boolean useWorkaround = false;
boolean setBackSafe = false; // Let compiler remove this if necessary.
// Check for moving off stairs. // Check for moving off stairs.
if (!useWorkaround && from.isAboveStairs()) useWorkaround = true; if (!useWorkaround && from.isAboveStairs()) {
// Check for "lost touch", for when moving events were not created, for instance (1/256). useWorkaround = true;
if (!useWorkaround){ setBackSafe = true;
final boolean inconsistent = yDistance > 0 && yDistance < 0.5 && data.survivalFlyLastYDist < 0 }
&& setBackYDistance > 0D && setBackYDistance <= 1.5D; // Check for "lost touch", for when moving events were not created,
if (inconsistent){ // for instance (1/256).
if (cc.debug) System.out.println(player.getName() + " Y-INCONSISTENCY"); if (!useWorkaround && yDistance > 0 && yDistance < 0.5 && data.survivalFlyLastYDist < 0
&& setBackYDistance > 0D && setBackYDistance <= 1.5D) {
if (data.fromX != Double.MAX_VALUE) { if (data.fromX != Double.MAX_VALUE) {
// Interpolate from last to-coordinates to the from coordinates (with some safe-guard). // Interpolate from last to-coordinates to the from
// coordinates (with some safe-guard).
final double dX = from.getX() - data.fromX; final double dX = from.getX() - data.fromX;
final double dY = from.getY() - data.fromY; final double dY = from.getY() - data.fromY;
final double dZ = from.getZ() - data.fromZ; final double dZ = from.getZ() - data.fromZ;
if (dX * dX + dY * dY + dZ * dZ < 0.5){ // TODO: adjust limit maybe. if (dX * dX + dY * dY + dZ * dZ < 0.5) { // TODO: adjust
// limit maybe.
// Check full bounding box since last from. // 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 minY = Math.min(data.toY, Math.min(data.fromY, from.getY()));
final double iY = minY; // TODO ... final double iY = minY; // TODO ...
final double r = from.getWidth() / 2.0; 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 (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()) { if (useWorkaround) { // !toOnGround && to.isAboveStairs()) {
// Set the new setBack and reset the jumpPhase. // Set the new setBack and reset the jumpPhase.
if (setBackSafe) data.setBack = from.getLocation();
// Maybe don't adapt the setback (unless null)! // TODO: This seems dubious !
// data.setBack = from.getLocation();
data.setBack.setY(Location.locToBlock(data.setBack.getY())); data.setBack.setY(Location.locToBlock(data.setBack.getY()));
// data.ground ? // data.ground ?
// ? set jumpphase to height / 0.15 ? // ? set jumpphase to height / 0.15 ?
data.survivalFlyJumpPhase = 0; data.survivalFlyJumpPhase = 0;
data.jumpAmplifier = 0; // Might conflict, should probably fetch. data.jumpAmplifier = MovingListener.getJumpAmplifier(mcPlayer);
data.clearAccounting(); data.clearAccounting();
// Tell NoFall that we assume the player to have been on ground somehow. // Tell NoFall that we assume the player to have been on ground somehow.
data.noFallAssumeGround = true; data.noFallAssumeGround = true;
resetFrom = true; resetFrom = true; // Note: if removing this, other conditions need to check noFallAssume...
if (cc.debug) System.out.println(player.getName() + " Y-INCONSISTENCY WORKAROUND USED"); tags.add("lostground");
} }
} }
@ -190,16 +202,14 @@ public class SurvivalFly extends Check {
hAllowedDistance *= cc.survivalFlySpeedingSpeed/ 100D; hAllowedDistance *= cc.survivalFlySpeedingSpeed/ 100D;
} }
// TODO: Optimize: maybe only do the permission checks and modifiers if the distance is too big. // TODO: More after-failure checks, to prevent unnecessary permission checking etc.
// (Depending on permission plugin, with pex it will be hardly 1000 ns for all moving perms, if all false.) // TODO: Split off not frequently used parts to methods.
// If the player is on ice, give him an higher maximum speed. // If the player is on ice, give him an higher maximum speed.
if (data.survivalFlyOnIce > 0) if (data.survivalFlyOnIce > 0)
hAllowedDistance *= modIce; hAllowedDistance *= modIce;
// Taken directly from Minecraft code, should work. // Speed amplifier.
// player.hasPotionEffect(PotionEffectType.SPEED);
if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT)) if (mcPlayer.hasEffect(MobEffectList.FASTER_MOVEMENT))
hAllowedDistance *= 1.0D + 0.2D * (mcPlayer.getEffect(MobEffectList.FASTER_MOVEMENT).getAmplifier() + 1); 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. // Judge if horizontal speed is above limit.
double hDistanceAboveLimit = hDistance - hAllowedDistance - data.horizontalFreedom; double hDistanceAboveLimit = hDistance - hAllowedDistance - data.horizontalFreedom;
// Tag for simple speed violation (medium), might get overridden.
if (hDistanceAboveLimit > 0) tags.add("hspeed");
// Prevent players from walking on a liquid. // Prevent players from walking on a liquid.
// TODO: yDistance == 0D <- should there not be a tolerance +- or 0...x ? // TODO: yDistance == 0D <- should there not be a tolerance +- or 0...x ?
if (hDistanceAboveLimit <= 0D && hDistance > 0.1D && yDistance == 0D if (hDistanceAboveLimit <= 0D && hDistance > 0.1D && yDistance == 0D && BlockProperties.isLiquid(to.getTypeId()) && !toOnGround && to.getY() % 1D < 0.8D) {
&& BlockProperties.isLiquid(to.getTypeId()) && !toOnGround hDistanceAboveLimit = Math.max(hDistanceAboveLimit, hDistance);
&& to.getY() % 1D < 0.8D) tags.add("waterwalk");
hDistanceAboveLimit = hDistance; }
// Prevent players from sprinting if they're moving backwards. // Prevent players from sprinting if they're moving backwards.
if (hDistanceAboveLimit <= 0D && sprinting) { 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 && yaw > 270F && yaw < 360F || xDistance > 0D && zDistance < 0D && yaw > 0F && yaw < 90F
|| xDistance > 0D && zDistance > 0D && yaw > 90F && yaw < 180F){ || xDistance > 0D && zDistance > 0D && yaw > 90F && yaw < 180F){
// Assumes permission check to be the heaviest (might be mistaken). // 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? // Did he go too far?
if (hDistanceAboveLimit > 0 && sprinting) 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) { if (data.bunnyhopDelay <= 0 && hDistanceAboveLimit > 0.05D && hDistanceAboveLimit < 0.28D) {
data.bunnyhopDelay = 9; data.bunnyhopDelay = 9;
hDistanceAboveLimit = 0D; hDistanceAboveLimit = 0D;
tags.add("bunny"); // TODO: Which here...
} }
if (hDistanceAboveLimit > 0D) { if (hDistanceAboveLimit > 0D) {
@ -244,9 +261,13 @@ public class SurvivalFly extends Check {
hDistanceAboveLimit -= data.horizontalBuffer; hDistanceAboveLimit -= data.horizontalBuffer;
data.horizontalBuffer = 0D; data.horizontalBuffer = 0D;
// Put back the "overconsumed" buffer. // Put back the "over-consumed" buffer.
if (hDistanceAboveLimit < 0D) if (hDistanceAboveLimit < 0D){
data.horizontalBuffer = -hDistanceAboveLimit; data.horizontalBuffer = -hDistanceAboveLimit;
}
if (hDistanceAboveLimit <= 0){
tags.add("hbuffer"); // TODO: ...
}
} else } else
data.horizontalBuffer = Math.min(1D, data.horizontalBuffer - hDistanceAboveLimit); data.horizontalBuffer = Math.min(1D, data.horizontalBuffer - hDistanceAboveLimit);
@ -255,27 +276,22 @@ public class SurvivalFly extends Check {
if (from.isInWeb()){ if (from.isInWeb()){
// Very simple: force players to descend or stay. // Very simple: force players to descend or stay.
vAllowedDistance = from.isOnGround() ? 0.1D : 0; vAllowedDistance = from.isOnGround() ? 0.1D : 0;
data.jumpAmplifier = 0; data.jumpAmplifier = 0; // TODO: later maybe fetch.
vDistanceAboveLimit = yDistance; vDistanceAboveLimit = yDistance;
if (cc.survivalFlyCobwebHack && vDistanceAboveLimit > 0 && hDistanceAboveLimit <= 0){ if (cc.survivalFlyCobwebHack && vDistanceAboveLimit > 0 && hDistanceAboveLimit <= 0){
if (now - data.survivalFlyCobwebTime > 3000){ // TODO: This seemed fixed by latest builds of CraftBukkit, test and remove if appropriate!
data.survivalFlyCobwebTime = now; final Location silentSetBack = hackCobweb(player, data, to, now, vDistanceAboveLimit);
data.survivalFlyCobwebVL = vDistanceAboveLimit * 100D; if (silentSetBack != null){
} if (cc.debug) System.out.println(player.getName()+ " (Cobweb: silent set-back)");
else data.survivalFlyCobwebVL += vDistanceAboveLimit * 100D; return silentSetBack;
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;
} }
} }
if (vDistanceAboveLimit > 0) tags.add("vweb");
} }
// else if (verticalFreedom <= 0.001 && from.isOnLadder) ....
// else if (verticalFreedom <= 0.001 (?) & from.isInFluid
else{ 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; vAllowedDistance = (!(fromOnGround || data.noFallAssumeGround) && !toOnGround ? 1.45D : 1.35D) + data.verticalFreedom;
final int maxJumpPhase; final int maxJumpPhase;
if (data.jumpAmplifier > 0){ if (data.jumpAmplifier > 0){
@ -289,41 +305,39 @@ public class SurvivalFly extends Check {
vDistanceAboveLimit = to.getY() - data.setBack.getY() - vAllowedDistance; vDistanceAboveLimit = to.getY() - data.setBack.getY() - vAllowedDistance;
// System.out.println("vda = " +vDistanceAboveLimit + " / vc = " + data.verticalVelocityCounter + " / vf = " + data.verticalFreedom + " / v = " + player.getVelocity().length()); // Simple-step blocker.
if ((fromOnGround || data.noFallAssumeGround) && toOnGround && Math.abs(yDistance - 1D) <= cc.yStep && vDistanceAboveLimit <= 0D && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_STEP)) {
// 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)); vDistanceAboveLimit = Math.max(vDistanceAboveLimit, Math.abs(yDistance));
tags.add("step");
}
}
if (data.noFallAssumeGround || fromOnGround || toOnGround) {
// Some reset condition.
data.jumpAmplifier = MovingListener.getJumpAmplifier(mcPlayer);
} }
if (data.noFallAssumeGround || fromOnGround || toOnGround)
data.jumpAmplifier = 0D;
if (cc.survivalFlyAccounting && !resetFrom){ final boolean resetTo = toOnGround || to.isInLiquid() || to.isOnLadder()|| to.isInWeb();
final boolean useH = data.horizontalFreedom <= 0.001D;
final boolean useV = data.verticalFreedom <= 0.001D; // Accounting support.
if (useH){ if (cc.survivalFlyAccounting && !resetFrom && !resetTo) {
data.hDistSum.add(now, (float) hDistance); // Currently only for "air" phases.
data.hDistCount.add(now, 1f); // Horizontal.
} if (data.horizontalFreedom <= 0.001D){
if (useV){ // This only checks general speed decrease oncevelocity is smoked up.
data.vDistSum.add(now, (float) (yDistance)); hDistanceAboveLimit = Math.max(hDistanceAboveLimit, doAccounting(now, hDistance, data.hDistSum, data.hDistCount, tags, "hacc"));
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);
} }
// 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");
// }
} }
} }
@ -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() + " 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)":"")); 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) ); 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? // Did the player move in unexpected ways?// Did the player move in unexpected ways?
if (result > 0D) { if (result > 0D) {
// System.out.println(BlockProperties.isStairs(from.getTypeIdBelow()) + " / " + BlockProperties.isStairs(to.getTypeIdBelow()));
// Increment violation counter. // Increment violation counter.
data.survivalFlyVL += result; data.survivalFlyVL += result;
data.clearAccounting(); 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_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.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.DISTANCE, String.format(Locale.US, "%.2f", to.getLocation().distance(from.getLocation())));
vd.setParameter(ParameterName.TAGS, CheckUtils.join(tags, "+")); // Always set.
} }
data.survivalFlyVLTime = now; data.survivalFlyVLTime = now;
if (executeActions(vd)){ if (executeActions(vd)){
data.survivalFlyLastYDist = Double.MAX_VALUE; data.survivalFlyLastYDist = Double.MAX_VALUE;
data.toWasReset = false;
// Compose a new location based on coordinates of "newTo" and viewing direction of "event.getTo()" to // 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. // 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(), 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). // 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){ if (resetTo){
// The player has moved onto ground. // The player has moved onto ground.
data.setBack = to.getLocation(); data.setBack = to.getLocation();
@ -385,4 +401,58 @@ public class SurvivalFly extends Check {
return null; 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.<br>
* 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<String> 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;
}
} }

View File

@ -61,7 +61,7 @@ public class RemovePlayerCommand extends NCPCommand {
if (DataManager.removeExecutionHistory(checkType, playerName)) histRemoved = true; 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){ if (dataRemoved || histRemoved){
String which; String which;

View File

@ -1,7 +1,8 @@
package fr.neatmonster.nocheatplus.components; 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.<br>
* NOTE: For CheckType-specific data removal, IHaveCheckType should be implemented, otherwise this data might get ignored until plugin-disable.
* @author mc_dev * @author mc_dev
* *
*/ */

View File

@ -121,22 +121,19 @@ public class NCPExemptionManager {
public static Listener getListener() { public static Listener getListener() {
return new Listener() { return new Listener() {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@EventHandler( @EventHandler(priority = EventPriority.LOWEST)
ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerJoin(final PlayerJoinEvent event) { public void onPlayerJoin(final PlayerJoinEvent event) {
NCPExemptionManager.registerPlayer(event.getPlayer()); NCPExemptionManager.registerPlayer(event.getPlayer());
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@EventHandler( @EventHandler(priority = EventPriority.MONITOR)
ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPlayerQuit(final PlayerQuitEvent event) { public void onPlayerQuit(final PlayerQuitEvent event) {
NCPExemptionManager.tryToRemove(event.getPlayer()); NCPExemptionManager.tryToRemove(event.getPlayer());
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@EventHandler( @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPlayerKick(final PlayerKickEvent event) { public void onPlayerKick(final PlayerKickEvent event) {
NCPExemptionManager.tryToRemove(event.getPlayer()); NCPExemptionManager.tryToRemove(event.getPlayer());
} }

View File

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

View File

@ -40,6 +40,14 @@ import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager; import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.hooks.APIUtils; import fr.neatmonster.nocheatplus.hooks.APIUtils;
/**
* Central access point for a lot of functionality for managing data, especially removing data for cleanup.<br>
* Originally intended as temporary or intermediate design, this might help reorganizing the API at some point.<br>
* 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{ public class DataManager implements Listener, INotifyReload, INeedConfig, IComponentRegistry{
protected static DataManager instance = null; protected static DataManager instance = null;
@ -65,15 +73,28 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
*/ */
protected final Map<CheckType, Map<String, ExecutionHistory>> executionHistories = new HashMap<CheckType, Map<String,ExecutionHistory>>(); protected final Map<CheckType, Map<String, ExecutionHistory>> executionHistories = new HashMap<CheckType, Map<String,ExecutionHistory>>();
/**
* 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; protected long durExpireData = 0;
/** Data and execution history. */
protected boolean deleteData = true; protected boolean deleteData = true;
/** Violation history and execution history. */
protected boolean deleteHistory = false; protected boolean deleteHistory = false;
/**
* Sets the static instance reference.
*/
public DataManager(){ public DataManager(){
instance = this; instance = this;
} }
/**
* Check the logged out players for if any data can be removed.<br>
* Currently only "dumb" full removal is performed. Later it is thinkable to remove "as much as reasonable".
*/
public void checkExpiration(){ public void checkExpiration(){
if (durExpireData <= 0) return; if (durExpireData <= 0) return;
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
@ -119,6 +140,10 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
onLeave(event.getPlayer()); onLeave(event.getPlayer());
} }
/**
* Quit or kick.
* @param player
*/
private final void onLeave(final Player player) { private final void onLeave(final Player player) {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
lastLogout.put(player.getName(), now); lastLogout.put(player.getName(), now);
@ -127,10 +152,13 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
@Override @Override
public void onReload() { public void onReload() {
// future // present.
adjustSettings(); adjustSettings();
} }
/**
* Fetch settings from the current default config.
*/
private void adjustSettings() { private void adjustSettings() {
final ConfigFile config = ConfigManager.getConfigFile(); final ConfigFile config = ConfigManager.getConfigFile();
durExpireData = config.getLong(ConfPaths.DATA_EXPIRATION_DURATION) * 60000L; // in minutes 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); deleteHistory = config.getBoolean(ConfPaths.DATA_EXPIRATION_HISTORY);
} }
/**
* Used by checks to register the history for external access.<br>
* NOTE: This method is not really meant ot be used from outside NCP.
* @param type
* @param histories
*/
public static void registerExecutionHistory(CheckType type, Map<String, ExecutionHistory> histories) { public static void registerExecutionHistory(CheckType type, Map<String, ExecutionHistory> histories) {
instance.executionHistories.put(type, histories); instance.executionHistories.put(type, histories);
} }
@ -154,6 +188,12 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
return null; 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){ public static boolean removeExecutionHistory(final CheckType type, final String playerName){
boolean removed = false; boolean removed = false;
// TODO: design ... // TODO: design ...
@ -197,9 +237,36 @@ public class DataManager implements Listener, INotifyReload, INeedConfig, ICompo
ViolationHistory.clear(checkType); 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.<br>
* @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<CheckDataFactory> factories = new HashSet<CheckDataFactory>();
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).<br> * Clear player related data, only for registered components (not execution history, violation history, normal check data).<br>
* That should at least go for chat engione data. * That should at least go for chat engine data.
* @param CheckType * @param CheckType
* @param PlayerName * @param PlayerName
* @return If something was removed. * @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() { public void onDisable() {
clearData(CheckType.ALL); clearData(CheckType.ALL);

View File

@ -52,7 +52,7 @@ public class Permissions {
// Permissions for the individual checks. // 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 * 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 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 * 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 = BLOCKBREAK + ".break";
public static final String BLOCKBREAK_BREAK_LIQUID = BLOCKBREAK_BREAK + ".liquid"; public static final String BLOCKBREAK_BREAK_LIQUID = BLOCKBREAK_BREAK + ".liquid";
public static final String BLOCKBREAK_DIRECTION = BLOCKBREAK + ".direction"; 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 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 * 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_DIRECTION = BLOCKINTERACT + ".direction";
public static final String BLOCKINTERACT_REACH = BLOCKINTERACT + ".reach"; public static final String BLOCKINTERACT_REACH = BLOCKINTERACT + ".reach";
@ -89,7 +89,7 @@ public class Permissions {
* 888 88b, 888 Y888 888P Y888 , 888 b 888 888 ,ee 888 Y888 , 888 , * 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" * 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 = 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_AGAINST_LIQUIDS = BLOCKPLACE_AGAINST + ".liquids";
@ -106,7 +106,7 @@ public class Permissions {
* Y888 ,d 888 888 ,ee 888 888 * Y888 ,d 888 888 ,ee 888 888
* "88,d88 888 888 "88 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_CAPTCHA = CHAT + ".captcha";
public static final String CHAT_COLOR = CHAT + ".color"; public static final String CHAT_COLOR = CHAT + ".color";
public static final String CHAT_COMMANDS = CHAT + ".commands"; public static final String CHAT_COMMANDS = CHAT + ".commands";
@ -117,7 +117,7 @@ public class Permissions {
/* /*
* Combined ! * Combined !
*/ */
private static final String COMBINED = CHECKS + ".combined"; public static final String COMBINED = CHECKS + ".combined";
public static final String COMBINED_IMPROBABLE = COMBINED + ".improbable"; public static final String COMBINED_IMPROBABLE = COMBINED + ".improbable";
/* /*
@ -129,7 +129,7 @@ public class Permissions {
* , 88P * , 88P
* "8",P" * "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_ANGLE = FIGHT + ".angle";
public static final String FIGHT_CRITICAL = FIGHT + ".critical"; public static final String FIGHT_CRITICAL = FIGHT + ".critical";
public static final String FIGHT_DIRECTION = FIGHT + ".direction"; public static final String FIGHT_DIRECTION = FIGHT + ".direction";
@ -149,7 +149,7 @@ public class Permissions {
* 888 * 888
* 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_DROP = INVENTORY + ".drop";
public static final String INVENTORY_FASTCLICK = INVENTORY + ".fastclick"; public static final String INVENTORY_FASTCLICK = INVENTORY + ".fastclick";
public static final String INVENTORY_INSTANTBOW = INVENTORY + ".instantbow"; public static final String INVENTORY_INSTANTBOW = INVENTORY + ".instantbow";
@ -165,7 +165,7 @@ public class Permissions {
* , 88P * , 88P
* "8",P" * "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_BOATSANYWHERE = MOVING + ".boatsanywhere";
public static final String MOVING_CREATIVEFLY = MOVING + ".creativefly"; public static final String MOVING_CREATIVEFLY = MOVING + ".creativefly";
public static final String MOVING_MOREPACKETS = MOVING + ".morepackets"; public static final String MOVING_MOREPACKETS = MOVING + ".morepackets";

View File

@ -107,8 +107,14 @@ public class ActionFrequency {
cf *= factor; cf *= factor;
} }
return score; return score;
}
/**
* Get reference time.
* @return
*/
public final long lastAccess(){
return time;
} }
} }

View File

@ -1,8 +1,5 @@
package fr.neatmonster.nocheatplus.utilities; package fr.neatmonster.nocheatplus.utilities;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.server.IBlockAccess; import net.minecraft.server.IBlockAccess;
import net.minecraft.server.Material; import net.minecraft.server.Material;
import net.minecraft.server.TileEntity; import net.minecraft.server.TileEntity;
@ -11,70 +8,25 @@ import net.minecraft.server.Vec3DPool;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import fr.neatmonster.nocheatplus.utilities.ds.CoordMap;
/** /**
* Access to type-ids and data using caching techniques. * Access to type-ids and data using caching techniques.
* @author mc_dev * @author mc_dev
* *
*/ */
public class BlockCache implements IBlockAccess{ 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. * For getting ids.
*/ */
private IBlockAccess access = null; private IBlockAccess access = null;
/** Cached type-ids. */ /** Cached type-ids. */
private final Map<Pos3D, Integer> idMap = new HashMap<Pos3D, Integer>(); private final CoordMap<Integer> idMap = new CoordMap<Integer>();
/** Cahced data values. */
private final Map<Pos3D, Integer> dataMap = new HashMap<Pos3D, Integer>(); /** Cached data values. */
private final CoordMap<Integer> dataMap = new CoordMap<Integer>();
// TODO: maybe make very fast access arrays for the ray tracing checks. // TODO: maybe make very fast access arrays for the ray tracing checks.
// private int[] id = null; // private int[] id = null;
@ -118,20 +70,18 @@ public class BlockCache implements IBlockAccess{
public int getTypeId(final int x, final int y, final int z) { 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(x, y, z);
final Integer pId = idMap.get(pos);
if (pId != null) return pId; if (pId != null) return pId;
final Integer nId = access.getTypeId(x, y, z); final Integer nId = access.getTypeId(x, y, z);
idMap.put(pos, nId); idMap.put(x, y, z, nId);
return nId; return nId;
} }
public int getData(final int x, final int y, final int z) { 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(x, y, z);
final Integer pData = dataMap.get(pos);
if (pData != null) return pData; if (pData != null) return pData;
final Integer nData = access.getData(x, y, z); final Integer nData = access.getData(x, y, z);
dataMap.put(pos, nData); dataMap.put(x, y, z, nData);
return nData; return nData;
} }

View File

@ -261,6 +261,8 @@ public class BlockProperties {
public static final int F_HEIGHT150 = 0x40; public static final int F_HEIGHT150 = 0x40;
/** The player can stand on these, sneaking or not. */ /** The player can stand on these, sneaking or not. */
public static final int F_GROUND = 0x80; public static final int F_GROUND = 0x80;
/** 1 block height. */
public static final int F_HEIGHT100 = 0x100;
static{ static{
init(); init();
@ -328,7 +330,7 @@ public class BlockProperties {
for (final Material mat : new Material[] {Material.NETHER_BRICK_STAIRS, for (final Material mat : new Material[] {Material.NETHER_BRICK_STAIRS,
Material.COBBLESTONE_STAIRS, Material.SMOOTH_STAIRS, Material.BRICK_STAIRS, Material.SANDSTONE_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}){ 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. // WATER.
for (final Material mat : new Material[]{ for (final Material mat : new Material[]{
@ -356,6 +358,7 @@ public class BlockProperties {
}){ }){
blockFlags[mat.getId()] |= F_GROUND; blockFlags[mat.getId()] |= F_GROUND;
} }
blockFlags[Material.ENDER_PORTAL_FRAME.getId()] |= F_HEIGHT100;
// Ignore for passable. // Ignore for passable.
for (final Material mat : new Material[]{ for (final Material mat : new Material[]{
Material.WOOD_PLATE, Material.STONE_PLATE, 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 blocks[Material.SKULL.getId()] = new BlockProps(noTool, 8.5f, secToMs(1.45)); // TODO
blockFlags[Material.SKULL.getId()] |= F_GROUND; blockFlags[Material.SKULL.getId()] |= F_GROUND;
blocks[Material.ANVIL.getId()] = new BlockProps(woodPickaxe, 5f); // TODO blocks[Material.ANVIL.getId()] = new BlockProps(woodPickaxe, 5f); // TODO
blockFlags[Material.FLOWER_POT.getId()] |= F_GROUND;
// Indestructible. // Indestructible.
for (Material mat : new Material[]{ for (Material mat : new Material[]{
@ -978,6 +982,17 @@ public class BlockProperties {
return isPassable(access, loc.getX(), loc.getY(), loc.getZ(), access.getTypeId(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); 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. * API access to read extra properties from files.
* @param config * @param config
@ -1093,7 +1108,7 @@ public class BlockProperties {
final double bmaxY; final double bmaxY;
// if ((blockFlags[id] & F_HEIGHT150) != 0) block.maxY = 1.5; // if ((blockFlags[id] & F_HEIGHT150) != 0) block.maxY = 1.5;
if ((blockFlags[id] & F_HEIGHT150) != 0) bmaxY = 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 else bmaxY = block.y(); // maxY
// if (minX > block.maxX + x || maxX < block.minX + x) return false; // if (minX > block.maxX + x || maxX < block.minX + x) return false;
// else if (minY > block.maxY + y || maxY < block.minY + y) return false; // else if (minY > block.maxY + y || maxY < block.minY + y) return false;

View File

@ -8,6 +8,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
@ -18,7 +19,7 @@ import org.bukkit.entity.Player;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
/** /**
* The Class CheckUtils. * Random auxiliary gear, some might have general quality.
*/ */
public class CheckUtils { public class CheckUtils {
@ -37,7 +38,8 @@ public class CheckUtils {
} }
/** /**
* Check if a player looks at a target of a specific size, with a specific precision value (roughly). * Check if a player looks at a target of a specific size, with a specific
* precision value (roughly).
* *
* @param player * @param player
* the player * the player
@ -55,14 +57,13 @@ public class CheckUtils {
* the precision * the precision
* @return the double * @return the double
*/ */
public static double directionCheck(final Player player, final double targetX, final double targetY, 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)
final double targetZ, final double targetWidth, final double targetHeight, final double precision) { {
// Get the eye location of the player. // Get the eye location of the player.
final Location eyes = player.getEyeLocation(); final Location eyes = player.getEyeLocation();
final double factor = Math.sqrt(Math.pow(eyes.getX() - targetX, 2) + Math.pow(eyes.getY() - targetY, 2) final double factor = Math.sqrt(Math.pow(eyes.getX() - targetX, 2) + Math.pow(eyes.getY() - targetY, 2) + Math.pow(eyes.getZ() - targetZ, 2));
+ Math.pow(eyes.getZ() - targetZ, 2));
// Get the view direction of the player. // Get the view direction of the player.
final Vector direction = eyes.getDirection(); final Vector direction = eyes.getDirection();
@ -81,15 +82,14 @@ public class CheckUtils {
off += Math.max(Math.abs(z - zPrediction) - (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); off += Math.max(Math.abs(y - yPrediction) - (targetHeight / 2 + precision), 0.0D);
if (off > 1) if (off > 1) off = Math.sqrt(off);
off = Math.sqrt(off);
return off; return off;
} }
/** /**
* Calculate the distance between two location, because for Bukkit distance is the distance squared and * Calculate the distance between two location, because for Bukkit distance
* distanceSquared is the distance non-squared. * is the distance squared and distanceSquared is the distance non-squared.
* *
* @param location1 * @param location1
* the location1 * the location1
@ -97,9 +97,9 @@ public class CheckUtils {
* the location2 * the location2
* @return the double * @return the double
*/ */
public static double distance(final Location location1, final Location location2) { 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)); return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) + Math.pow(location2.getY() - location1.getY(), 2) + Math.pow(location2.getZ() - location1.getZ(), 2));
} }
/** /**
@ -113,15 +113,16 @@ public class CheckUtils {
* the minimum value of the correlation coefficient * the minimum value of the correlation coefficient
* @return result true if the two Strings are similar, false otherwise * @return result true if the two Strings are similar, false otherwise
*/ */
public static boolean isSimilar(final String s, final String t, final float threshold) { 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 1.0f - (float) levenshteinDistance(s, t) / Math.max(1.0, Math.max(s.length(), t.length())) > threshold;
} }
/** /**
* Find the Levenshtein distance between two Strings. * 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 * This is the number of changes needed to change one String into another,
* modification (deletion, insertion or substitution). * where each change is a single character modification (deletion, insertion or substitution).
* *
* @param s * @param s
* the first String, must not be null * the first String, must not be null
@ -130,16 +131,13 @@ public class CheckUtils {
* @return result distance * @return result distance
*/ */
private static int levenshteinDistance(CharSequence s, CharSequence t) { private static int levenshteinDistance(CharSequence s, CharSequence t) {
if (s == null || t == null) if (s == null || t == null) throw new IllegalArgumentException("Strings must not be null");
throw new IllegalArgumentException("Strings must not be null");
int n = s.length(); int n = s.length();
int m = t.length(); int m = t.length();
if (n == 0) if (n == 0) return m;
return m; else if (m == 0) return n;
else if (m == 0)
return n;
if (n > m) { if (n > m) {
final CharSequence tmp = s; final CharSequence tmp = s;
@ -182,11 +180,13 @@ public class CheckUtils {
/** /**
* Join parts with link. * Join parts with link.
*
* @param input * @param input
* @param link * @param link
* @return * @return
*/ */
public static <O extends Object> String join(final Collection<O> input, final String link){ public static <O extends Object> String join(final Collection<O> input, final String link)
{
final StringBuilder builder = new StringBuilder(Math.max(300, input.size() * 10)); final StringBuilder builder = new StringBuilder(Math.max(300, input.size() * 10));
boolean first = true; boolean first = true;
for (final Object obj : input) { for (final Object obj : input) {
@ -199,11 +199,13 @@ public class CheckUtils {
/** /**
* Convenience method. * Convenience method.
*
* @param parts * @param parts
* @param link * @param link
* @return * @return
*/ */
public static <O extends Object> boolean scheduleOutputJoined(final List<O> parts, String link){ public static <O extends Object> boolean scheduleOutputJoined(final List<O> parts, String link)
{
return scheduleOutput(join(parts, link)); return scheduleOutput(join(parts, link));
} }
@ -307,6 +309,7 @@ public class CheckUtils {
/** /**
* Get the height from getLocation().getY() to head / above head.<br> * Get the height from getLocation().getY() to head / above head.<br>
* NOTE: Currently this is pretty much useless, it returns 1.0 most of the time. * NOTE: Currently this is pretty much useless, it returns 1.0 most of the time.
*
* @param entity * @param entity
* @return * @return
*/ */
@ -315,7 +318,6 @@ public class CheckUtils {
final double entityHeight = mcEntity.height; final double entityHeight = mcEntity.height;
if (entity instanceof LivingEntity) { if (entity instanceof LivingEntity) {
return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight); return Math.max(((LivingEntity) entity).getEyeHeight(), entityHeight);
} } else return mcEntity.height;
else return mcEntity.height;
} }
} }

View File

@ -334,10 +334,13 @@ public class PlayerLocation {
onGround = BlockProperties.isOnGround(getBlockAccess(), minX, minY - yOnGround, minZ, maxX, maxY + 0.25, maxZ); onGround = BlockProperties.isOnGround(getBlockAccess(), minX, minY - yOnGround, minZ, maxX, maxY + 0.25, maxZ);
if (!onGround){ if (!onGround){
// TODO: Probably check other ids too before doing this ? // 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); final AxisAlignedBB box = useBox.b(minX - d0, minY - getyOnGround() - d0, minZ - d0, maxX + d0, maxY + d0, maxZ + d0);
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
List list = worldServer.getEntities(entity, box); final List list = worldServer.getEntities(entity, box);
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
Iterator iterator = list.iterator(); Iterator iterator = list.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {

View File

@ -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.<br>
* This implementation is not thread safe, though changing values and get/contains should work if the map stays unchanged.
* @author mc_dev
*
* @param <V>
*/
public class CoordMap<V> {
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<V>{
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<Entry<V>>[] 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. <br>
* 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.<br>
* 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<Entry<V>> bucket = entries[slot];
if (bucket == null) return null;;
for (final Entry<V> 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<Entry<V>> bucket = entries[slot];
if (bucket != null){
for (final Entry<V> 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<Entry<V>>();
entries[slot] = bucket;
}
entries[slot] = bucket;
bucket.add(new Entry<V>(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<Entry<V>>[] newEntries = new List[newCapacity];
for (int oldSlot = 0; oldSlot < entries.length; oldSlot++){
final List<Entry<V>> oldBucket = entries[oldSlot];
if (oldBucket != null){
for (final Entry<V> entry : oldBucket){
final int newSlot = Math.abs(entry.hash) % newCapacity;
List<Entry<V>> newBucket = newEntries[oldSlot];
if (newBucket == null){
newBucket = new LinkedList<Entry<V>>();
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 ?
}
}