Improved PlayerLocation objects management. Cleaned up the exemption

API.
This commit is contained in:
NeatMonster 2012-08-25 12:20:28 +02:00
parent 5625b61b41
commit 2d5850a547
12 changed files with 429 additions and 485 deletions

View File

@ -190,12 +190,12 @@ public abstract class Check {
* @return true, if the check is enabled * @return true, if the check is enabled
*/ */
public boolean isEnabled(final Player player) { public boolean isEnabled(final Player player) {
if (NCPExemptionManager.isExempted(player, type)) return false;
try { try {
return type.isEnabled(player) && !player.hasPermission(type.getPermission()); if (!type.isEnabled(player) || player.hasPermission(type.getPermission()))
return false;
} catch (final Exception e) { } catch (final Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return false; return !NCPExemptionManager.isExempted(player, type);
} }
} }

View File

@ -44,7 +44,7 @@ public class Color extends Check {
return message; return message;
final ChatConfig cc = ChatConfig.getConfig(player); final ChatConfig cc = ChatConfig.getConfig(player);
if (!isMainThread && (NCPExemptionManager.isExempted(player, type) || !cc.isEnabled(type))) if (!isMainThread && (!cc.isEnabled(type) || NCPExemptionManager.isExempted(player, type)))
// Leave out the permission check. // Leave out the permission check.
return message; return message;

View File

@ -71,7 +71,7 @@ public class NoPwnage extends Check {
final ChatConfig cc = ChatConfig.getConfig(player); final ChatConfig cc = ChatConfig.getConfig(player);
final ChatData data = ChatData.getData(player); final ChatData data = ChatData.getData(player);
if (!isMainThread && (NCPExemptionManager.isExempted(player, type) || !cc.isEnabled(type))) if (!isMainThread && (!cc.isEnabled(type) || NCPExemptionManager.isExempted(player, type)))
return false; return false;
// Keep related to ChatData/NoPwnage/Color used lock. // Keep related to ChatData/NoPwnage/Color used lock.

View File

@ -43,7 +43,8 @@ public class Critical extends Check {
boolean cancel = false; boolean cancel = false;
// We'll need the PlayerLocation to know some important stuff. // We'll need the PlayerLocation to know some important stuff.
final PlayerLocation location = new PlayerLocation(player.getLocation(), player); PlayerLocation location = new PlayerLocation();
location.set(player.getLocation(), player);
// Check if the hit was a critical hit (positive fall distance, entity in the air, not on ladder, not in liquid // Check if the hit was a critical hit (positive fall distance, entity in the air, not on ladder, not in liquid
// and without blindness effect). // and without blindness effect).
@ -70,6 +71,8 @@ public class Critical extends Check {
cancel = executeActions(player, data.criticalVL, delta, cc.criticalActions); cancel = executeActions(player, data.criticalVL, delta, cc.criticalActions);
} }
location = null;
return cancel; return cancel;
} }
} }

View File

@ -163,7 +163,8 @@ public class CreativeFly extends Check {
else if (wildcard == ParameterName.LOCATION_TO) else if (wildcard == ParameterName.LOCATION_TO)
return String.format(Locale.US, "%.2f, %.2f, %.2f", data.to.getX(), data.to.getY(), data.to.getZ()); return String.format(Locale.US, "%.2f, %.2f, %.2f", data.to.getX(), data.to.getY(), data.to.getZ());
else if (wildcard == ParameterName.DISTANCE) else if (wildcard == ParameterName.DISTANCE)
return String.format(Locale.US, "%.2f", data.to.subtract(data.from).lengthSquared()); return String.format(Locale.US, "%.2f", data.to.getLocation().subtract(data.from.getLocation())
.lengthSquared());
else else
return super.getParameter(wildcard, violationData); return super.getParameter(wildcard, violationData);
} }

View File

@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.CheckData; import fr.neatmonster.nocheatplus.checks.CheckData;
import fr.neatmonster.nocheatplus.checks.CheckDataFactory; import fr.neatmonster.nocheatplus.checks.CheckDataFactory;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/* /*
* M"""""`'"""`YM oo M""""""'YMM dP * M"""""`'"""`YM oo M""""""'YMM dP
@ -49,54 +50,54 @@ public class MovingData implements CheckData {
} }
// Violation levels. // Violation levels.
public double creativeFlyVL = 0D; public double creativeFlyVL = 0D;
public double morePacketsVL = 0D; public double morePacketsVL = 0D;
public double morePacketsVehicleVL = 0D; public double morePacketsVehicleVL = 0D;
public double noFallVL = 0D; public double noFallVL = 0D;
public double survivalFlyVL = 0D; public double survivalFlyVL = 0D;
// Data shared between the fly checks. // Data shared between the fly checks.
public int bunnyhopDelay; public int bunnyhopDelay;
public double horizontalBuffer; public double horizontalBuffer;
public double horizontalFreedom; public double horizontalFreedom;
public double horizontalVelocityCounter; public double horizontalVelocityCounter;
public double jumpAmplifier; public double jumpAmplifier;
public double verticalFreedom; public double verticalFreedom;
public double verticalVelocity; public double verticalVelocity;
public int verticalVelocityCounter; public int verticalVelocityCounter;
// Data of the creative check. // Data of the creative check.
public boolean creativeFlyPreviousRefused; public boolean creativeFlyPreviousRefused;
// Data of the more packets check. // Data of the more packets check.
public int morePacketsBuffer = 50; public int morePacketsBuffer = 50;
public long morePacketsLastTime; public long morePacketsLastTime;
public int morePacketsPackets; public int morePacketsPackets;
public Location morePacketsSetback; public Location morePacketsSetback;
// Data of the more packets vehicle check. // Data of the more packets vehicle check.
public int morePacketsVehicleBuffer = 50; public int morePacketsVehicleBuffer = 50;
public long morePacketsVehicleLastTime; public long morePacketsVehicleLastTime;
public int morePacketsVehiclePackets; public int morePacketsVehiclePackets;
public Location morePacketsVehicleSetback; public Location morePacketsVehicleSetback;
// Data of the no fall check. // Data of the no fall check.
public double noFallFallDistance; public double noFallFallDistance;
public boolean noFallOnGround; public boolean noFallOnGround;
public boolean noFallWasOnGround; public boolean noFallWasOnGround;
// Data of the survival fly check. // Data of the survival fly check.
public int survivalFlyJumpPhase; public int survivalFlyJumpPhase;
public double survivalFlyLastFromY; public double survivalFlyLastFromY;
public int survivalFlyOnIce; public int survivalFlyOnIce;
public boolean survivalFlyWasInBed; public boolean survivalFlyWasInBed;
// Locations shared between all checks. // Locations shared between all checks.
public Location from; public PlayerLocation from = new PlayerLocation();
public Location ground; public Location ground;
public Location setBack; public Location setBack;
public Location teleported; public Location teleported;
public Location to; public PlayerLocation to = new PlayerLocation();
/** /**
* Clear the data of the fly checks. * Clear the data of the fly checks.

View File

@ -29,7 +29,6 @@ import org.bukkit.util.Vector;
import fr.neatmonster.nocheatplus.NoCheatPlus; import fr.neatmonster.nocheatplus.NoCheatPlus;
import fr.neatmonster.nocheatplus.players.Permissions; import fr.neatmonster.nocheatplus.players.Permissions;
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
/* /*
* M"""""`'"""`YM oo * M"""""`'"""`YM oo
@ -330,31 +329,29 @@ public class MovingListener implements Listener {
// Counter has run out, now reduce the vertical freedom over time. // Counter has run out, now reduce the vertical freedom over time.
data.verticalFreedom *= 0.93D; data.verticalFreedom *= 0.93D;
final PlayerLocation from = new PlayerLocation(event.getFrom(), player); data.from.set(event.getFrom(), player);
data.from = from.getLocation(); if (data.from.isOnGround())
if (from.isOnGround()) data.ground = data.from.getLocation();
data.ground = from.getLocation(); data.to.set(event.getTo(), player);
final PlayerLocation to = new PlayerLocation(event.getTo(), player);
data.to = to.getLocation();
Location newTo = null; Location newTo = null;
if ((player.getGameMode() == GameMode.CREATIVE || player.getAllowFlight()) && creativeFly.isEnabled(player)) if ((player.getGameMode() == GameMode.CREATIVE || player.getAllowFlight()) && creativeFly.isEnabled(player))
// If the player is handled by the creative fly check, execute it. // If the player is handled by the creative fly check, execute it.
newTo = creativeFly.check(player, from, to); newTo = creativeFly.check(player, data.from, data.to);
else if (survivalFly.isEnabled(player)) { else if (survivalFly.isEnabled(player)) {
// If he is handled by the survival fly check, execute it. // If he is handled by the survival fly check, execute it.
newTo = survivalFly.check(player, from, to); newTo = survivalFly.check(player, data.from, data.to);
// If don't have a new location and if he is handled by the no fall check, execute it. // If don't have a new location and if he is handled by the no fall check, execute it.
if (newTo == null && noFall.isEnabled(player)) if (newTo == null && noFall.isEnabled(player))
noFall.check(player, from, to); noFall.check(player, data.from, data.to);
} else } else
// He isn't handled by any fly check, clear his data. // He isn't handled by any fly check, clear his data.
data.clearFlyData(); data.clearFlyData();
if (newTo == null && morePackets.isEnabled(player)) if (newTo == null && morePackets.isEnabled(player))
// 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, from, to); newTo = morePackets.check(player, data.from, data.to);
else else
// Otherwise we need to clear his data. // Otherwise we need to clear his data.
data.clearMorePacketsData(); data.clearMorePacketsData();

View File

@ -262,7 +262,8 @@ public class SurvivalFly extends Check {
else if (wildcard == ParameterName.LOCATION_TO) else if (wildcard == ParameterName.LOCATION_TO)
return String.format(Locale.US, "%.2f, %.2f, %.2f", data.to.getX(), data.to.getY(), data.to.getZ()); return String.format(Locale.US, "%.2f, %.2f, %.2f", data.to.getX(), data.to.getY(), data.to.getZ());
else if (wildcard == ParameterName.DISTANCE) else if (wildcard == ParameterName.DISTANCE)
return String.format(Locale.US, "%.2f", data.to.subtract(data.from).lengthSquared()); return String.format(Locale.US, "%.2f", data.to.getLocation().subtract(data.from.getLocation())
.lengthSquared());
else else
return super.getParameter(wildcard, violationData); return super.getParameter(wildcard, violationData);
} }

View File

@ -1,69 +0,0 @@
package fr.neatmonster.nocheatplus.hooks;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import fr.neatmonster.nocheatplus.checks.CheckType;
public class APIUtil {
/**
* Only the children.
*/
private static final Map<CheckType, CheckType[]> childrenMap = new HashMap<CheckType, CheckType[]>();
static{
Map<CheckType, Set<CheckType>> temp = new HashMap<CheckType, Set<CheckType>>();
// uh uh
for (CheckType checkType : CheckType.values()){
Set<CheckType> set = new HashSet<CheckType>();
temp.put(checkType, set);
}
for (CheckType checkType : CheckType.values()){
for (CheckType other : CheckType.values()){
if (isParent(other, checkType)){
temp.get(other).add(checkType);
}
}
}
for (CheckType parent : temp.keySet()){
Set<CheckType> set = temp.get(parent);
CheckType[] a = new CheckType[set.size()];
set.toArray(a);
childrenMap.put(parent, a);
}
}
/**
* Check if propablyParent is ancestor of checkType. Does not check versus checkType directly.
* @param probablyParent
* @param checkType
* @return
*/
public static final boolean isParent(final CheckType probablyParent, final CheckType checkType){
CheckType parent = checkType.getParent();
while (parent != null){
if (parent == probablyParent) return true;
else parent = parent.getParent();
}
return false;
}
/**
* Return an unmodifiable collection of children for the given check type. Always returns a collection, does not contain checkType itself.
* @param checkType
* @return
*/
public static final Collection<CheckType> getChildren(final CheckType checkType){
return Arrays.asList(childrenMap.get(checkType));
}
public static final boolean needsSynchronization(final CheckType checkType){
return checkType == CheckType.CHAT || isParent(CheckType.CHAT, checkType);
}
}

View File

@ -0,0 +1,86 @@
package fr.neatmonster.nocheatplus.hooks;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import fr.neatmonster.nocheatplus.checks.CheckType;
/*
* MMP"""""""MM MM"""""""`YM M""M M""MMMMM""M dP oo dP
* M' .mmmm MM MM mmmmm M M M M MMMMM M 88 88
* M `M M' .M M M M MMMMM M d8888P dP 88 .d8888b.
* M MMMMM MM MM MMMMMMMM M M M MMMMM M 88 88 88 Y8ooooo.
* M MMMMM MM MM MMMMMMMM M M M `MMM' M 88 88 88 88
* M MMMMM MM MM MMMMMMMM M M Mb dM dP dP dP `88888P'
* MMMMMMMMMMMM MMMMMMMMMMMM MMMM MMMMMMMMMMM
*/
/**
* A class providing utilities to the NoCheatPlus API.
*
* @author asofold
*/
public class APIUtils {
/** Only the children. */
private static final Map<CheckType, CheckType[]> childrenMap = new HashMap<CheckType, CheckType[]>();
static {
final Map<CheckType, Set<CheckType>> map = new HashMap<CheckType, Set<CheckType>>();
for (final CheckType type : CheckType.values())
map.put(type, new HashSet<CheckType>());
for (final CheckType type : CheckType.values())
for (final CheckType other : CheckType.values())
if (isParent(other, type))
map.get(other).add(type);
for (final CheckType parent : map.keySet())
childrenMap.put(parent, map.get(parent).toArray(new CheckType[] {}));
}
/**
* Return an unmodifiable collection of children for the given check type. Always returns a collection, does not
* contain check type itself.
*
* @param type
* the check type
* @return the children
*/
public static final Collection<CheckType> getChildren(final CheckType type) {
return Arrays.asList(childrenMap.get(type));
}
/**
* Check if the supposed parent is ancestor of the supposed child. Does not check versus the supposed child
* directly.
*
* @param supposedParent
* the supposed parent
* @param supposedChild
* the supposed child
* @return true, if is parent
*/
public static final boolean isParent(final CheckType supposedParent, final CheckType supposedChild) {
CheckType parent = supposedChild.getParent();
while (parent != null)
if (parent == supposedParent)
return true;
else
parent = parent.getParent();
return false;
}
/**
* Return if the check type requires synchronization.
*
* @param type
* the check type
* @return true, if successful
*/
public static final boolean needsSynchronization(final CheckType type) {
return type == CheckType.CHAT || isParent(CheckType.CHAT, type);
}
}

View File

@ -15,212 +15,244 @@ import org.bukkit.event.player.PlayerQuitEvent;
import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.CheckType;
/*
* M"""""""`YM MM'""""'YMM MM"""""""`YM MM""""""""`M dP oo
* M mmmm. M M' .mmm. `M MM mmmmm M MM mmmmmmmM 88
* M MMMMM M M MMMMMooM M' .M M` MMMM dP. .dP .d8888b. 88d8b.d8b. 88d888b. d8888P dP .d8888b. 88d888b.
* M MMMMM M M MMMMMMMM MM MMMMMMMM MM MMMMMMMM `8bd8' 88ooood8 88'`88'`88 88' `88 88 88 88' `88 88' `88
* M MMMMM M M. `MMM' .M MM MMMMMMMM MM MMMMMMMM .d88b. 88. ... 88 88 88 88. .88 88 88 88. .88 88 88
* M MMMMM M MM. .dM MM MMMMMMMM MM .M dP' `dP `88888P' dP dP dP 88Y888P' dP dP `88888P' dP dP
* MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM MMMMMMMMMMMM 88
* dP
* M"""""`'"""`YM
* M mm. mm. M
* M MMM MMM M .d8888b. 88d888b. .d8888b. .d8888b. .d8888b. 88d888b.
* M MMM MMM M 88' `88 88' `88 88' `88 88' `88 88ooood8 88' `88
* M MMM MMM M 88. .88 88 88 88. .88 88. .88 88. ... 88
* M MMM MMM M `88888P8 dP dP `88888P8 `8888P88 `88888P' dP
* MMMMMMMMMMMMMM .88
* d8888P
*/
/** /**
* API for exempting players of checks, checked before calculations are done. * API for exempting players of checks, checked before calculations are done.
* *
* @author mc_dev * @author asofold
*
*/ */
public class NCPExemptionManager { public class NCPExemptionManager {
/** /** A map associating a check type with the entity ids of its exempted players. */
* CheckType -> Entity id -> Exemption info. private static final Map<CheckType, Set<Integer>> exempted = new HashMap<CheckType, Set<Integer>>();
*
* TODO: opt: move these to checks individually for even faster access.
*/
private static final Map<CheckType, Set<Integer>> exempted = new HashMap<CheckType, Set<Integer>>();
/** /** A map associating the registred player with their entity id. */
* Registered players (exact name) -> entity id (last time registered). private static final Map<String, Integer> registeredPlayers = new HashMap<String, Integer>();
*/
private static final Map<String, Integer> registeredPlayers = new HashMap<String, Integer>();
static{ static {
clear(); clear();
} }
/** /**
* Check if a player is exempted from a check right now. * Remove all exemptions.
* */
* @param player public static final void clear() {
* The player to exempt from checks. registeredPlayers.clear();
* @param checkType // Use put with a new map to keep entries to stay thread safe.
* The type of check to exempt the player from. This can be individual check types, as well as a check group like MOVING or ALL. for (final CheckType type : CheckType.values())
* @return if (APIUtils.needsSynchronization(type))
* If the player is exempted from the check right now. exempted.put(type, Collections.synchronizedSet(new HashSet<Integer>()));
*/ else
public static final boolean isExempted(final Player player, final CheckType checkType){ exempted.put(type, new HashSet<Integer>());
return isExempted(player.getEntityId(), checkType); }
}
/** /**
* Check if an entity is exempted from a check by entity id right now. * Exempt an entity from all checks permanently.
* <hr> *
* This might help exempting NPCs from checks for all time, making performance a lot better. A future purpose might be to exempt vehicles and similar (including passengers) from checks. * @param entityId
* @param id * the entity id
* Entity id to exempt from checks. */
* @param checkType public static final void exemptPermanently(final int entityId) {
* The type of check to exempt the player from. This can be individual check types, as well as a check group like MOVING or ALL. exemptPermanently(entityId, CheckType.ALL);
* @return }
* If the entity is exempted from checks right now.
*/
public static final boolean isExempted(final int id, final CheckType checkType){
return exempted.get(checkType).contains(id);
}
/** /**
* Remove all exemptions. * Exempt an entity from the given check or check group permanently (only until restart).
*/ *
public static final void clear(){ * @param entityId
registeredPlayers.clear(); * the entity id
// Use put with a new map to keep entries to stay thread safe. * @param checkType
for (final CheckType checkType : CheckType.values()){ * the check type
if (APIUtil.needsSynchronization(checkType)) */
exempted.put(checkType, Collections.synchronizedSet(new HashSet<Integer>(10))); public static final void exemptPermanently(final int entityId, final CheckType checkType) {
else final Integer id = entityId;
exempted.put(checkType, new HashSet<Integer>(10)); exempted.get(checkType).add(id);
} for (final CheckType child : APIUtils.getChildren(checkType))
exempted.get(child).add(id);
}
} /**
* Exempt a player form all checks permanently.
*
* @param player
* the player
*/
public static final void exemptPermanently(final Player player) {
exemptPermanently(player, CheckType.ALL);
}
/** /**
* This should be registered before all other listeners of NCP (!). * Exempt a player from a check or check group permanently.
* *
* NOTE: For internal use only, DO NOT CALL FROM OUTSIDE. * @param player
* @return * the player
*/ * @param type
public static Listener getListener(){ * the check type
return new Listener() { */
@SuppressWarnings("unused") public static final void exemptPermanently(final Player player, final CheckType type) {
@EventHandler(priority=EventPriority.LOWEST) exemptPermanently(player.getEntityId(), type);
final void onJoin(final PlayerJoinEvent event){ }
NCPExemptionManager.registerPlayer(event.getPlayer());
}
@SuppressWarnings("unused")
@EventHandler(priority=EventPriority.MONITOR)
final void onJoin(final PlayerQuitEvent event){
NCPExemptionManager.checkRemovePlayer(event.getPlayer());
}
};
}
/** /**
* Check if the registeredPlayers mapping can be removed for a player, i.e. no exemptions are present. * This should be registered before all other listeners of NoCheatPlus.
* @param player *
*/ * NOTE: For internal use only, DO NOT CALL FROM OUTSIDE.
protected static final void checkRemovePlayer(final Player player) { *
if (!registeredPlayers.containsKey(player.getName())) return; * @return the listener
final Integer id = player.getEntityId(); */
for (final CheckType checkType : CheckType.values()){ public static Listener getListener() {
// Check if player is exempted from something. return new Listener() {
if (isExempted(id, checkType)) return; @SuppressWarnings("unused")
} @EventHandler(
// No return = remove player. ignoreCancelled = true, priority = EventPriority.LOWEST)
registeredPlayers.remove(player.getName()); public void onPlayerJoin(final PlayerJoinEvent event) {
} NCPExemptionManager.registerPlayer(event.getPlayer());
}
/** @SuppressWarnings("unused")
* Register current entity id for the player. @EventHandler(
* @param player ignoreCancelled = true, priority = EventPriority.MONITOR)
*/ public void onPlayerQuit(final PlayerQuitEvent event) {
public static final void registerPlayer(final Player player) { NCPExemptionManager.tryToRemove(event.getPlayer());
final int newId = player.getEntityId(); }
final String name = player.getName(); };
}
final Integer registeredId = registeredPlayers.get(name); /**
if (registeredId == null){ * Check if an entity is exempted from a check right now.
// Was not registered. * <hr>
registeredPlayers.put(name, newId); * This might help exempting NPCs from checks for all time, making performance a lot better. A future purpose might
} * be to exempt vehicles and similar (including passengers) from checks.
else if (newId == registeredId.intValue()){ *
// No change. * @param entityId
} * the entity id to exempt from checks
else { * @param type
// Player was registered under another id (needs exchange). * the type of check to exempt the player from. This can be individual check types, as well as a check
for (final Set<Integer> set : exempted.values()){ * group like MOVING or ALL
if (set.remove(registeredId)){ * @return if the entity is exempted from checks right now
// replace. */
set.add(newId); public static final boolean isExempted(final int entityId, final CheckType type) {
} return exempted.get(type).contains(entityId);
} }
registeredPlayers.put(name, newId);
}
}
/** /**
* Remove all exempting a player. * Check if a player is exempted from a check right now.
* @param player *
*/ * @param player
public static final void unExempt(final Player player){ * the player to exempt from checks
unExempt(player, CheckType.ALL); * @param type
} * the type of check to exempt the player from. This can be individual check types, as well as a check
* group like MOVING or ALL
* @return if the player is exempted from the check right now
*/
public static final boolean isExempted(final Player player, final CheckType type) {
return isExempted(player.getEntityId(), type);
}
/** /**
* Undo exempting a player form a certain check, or check group, as given. * Register current entity id for the player.
* @param player *
* @param checkType * @param player
*/ * the player
public static final void unExempt(final Player player, final CheckType checkType){ */
unExempt(player.getEntityId(), checkType); public static final void registerPlayer(final Player player) {
} final int entityId = player.getEntityId();
final String name = player.getName();
/** final Integer registeredId = registeredPlayers.get(name);
* Undo exempting an entity by entity id from all checks. if (registeredId == null)
* @param entityId // Player wasn't registered.
*/ registeredPlayers.put(name, entityId);
public static final void unExempt(final int entityId){ else if (entityId != registeredId.intValue()) {
unExempt(entityId, CheckType.ALL); // Player was registered under another id (needs exchange).
} for (final Set<Integer> set : exempted.values())
if (set.remove(registeredId))
// Replace.
set.add(entityId);
registeredPlayers.put(name, entityId);
}
}
/** /**
* Undo exempting an entity by entity id from a certain check type, also check groups, etc. * Check if the registeredPlayers mapping can be removed for a player, i.e. no exemptions are present.
* @param entityId *
* @param checkType * @param player
*/ * the player
public static final void unExempt(final int entityId, final CheckType checkType){ */
final Integer id = entityId; protected static final void tryToRemove(final Player player) {
exempted.get(checkType).remove(id); if (!registeredPlayers.containsKey(player.getName()))
for (final CheckType child : APIUtil.getChildren(checkType)){ return;
exempted.get(child).remove(id); final Integer entityId = player.getEntityId();
} for (final CheckType type : CheckType.values())
} // Check if player is exempted from something.
if (isExempted(entityId, type))
// If he is, we can't remove him so we return.
return;
registeredPlayers.remove(player.getName());
}
/** /**
* Exempt a player form all checks. * Undo exempting an entity from all checks.
* @param player *
*/ * @param entityId
public static final void exemptPermanently(final Player player){ * the entity id
exemptPermanently(player, CheckType.ALL); */
} public static final void unexempt(final int entityId) {
unexempt(entityId, CheckType.ALL);
}
/** /**
* Exempt a player from a check or check group permanently. * Undo exempting an entity from a certain check, or check group, as given.
* @param player *
* @param checkType * @param entityId
*/ * the entity id
public static final void exemptPermanently(final Player player, final CheckType checkType){ * @param type
exemptPermanently(player.getEntityId(), checkType); * the check type
} */
public static final void unexempt(final int entityId, final CheckType type) {
final Integer id = entityId;
exempted.get(type).remove(id);
for (final CheckType child : APIUtils.getChildren(type))
exempted.get(child).remove(id);
}
/** /**
* exempt an entity from all checks, by entity id. * Undo exempting a player from all checks.
* @param entityId *
*/ * @param player
public static final void exemptPermanently(final int entityId){ * the player
exemptPermanently(entityId, CheckType.ALL); */
} public static final void unexempt(final Player player) {
unexempt(player, CheckType.ALL);
}
/** /**
* Exempt an entity by entity id from the given check or check group permanently (only until restart). * Undo exempting a player form a certain check, or check group, as given.
* @param entityId *
* @param checkType * @param player
*/ * the player
public static final void exemptPermanently(final int entityId, final CheckType checkType){ * @param checkType
final Integer id = entityId; * the check type
exempted.get(checkType).add(id); */
for (final CheckType child : APIUtil.getChildren(checkType)){ public static final void unexempt(final Player player, final CheckType checkType) {
exempted.get(child).add(id); unexempt(player.getEntityId(), checkType);
} }
}
} }

View File

@ -36,112 +36,45 @@ import org.bukkit.entity.Player;
*/ */
public class PlayerLocation { public class PlayerLocation {
/** private static final Material[] STAIRS = new Material[] {Material.WOOD_STAIRS, Material.COBBLESTONE_STAIRS,
* Another utility class used to manipulate differently booleans.
*/
private class CustomBoolean {
/** Is the boolean set? */
private boolean isSet = false;
/** What is its value? */
private boolean value = false;
/**
* Gets the boolean.
*
* @return the value
*/
public boolean get() {
return value;
}
/**
* Checks if the boolean is set.
*
* @return true, if the boolean is set
*/
public boolean isSet() {
return isSet;
}
/**
* Sets the boolean.
*
* @param value
* the value
*/
public void set(final boolean value) {
this.value = value;
isSet = true;
}
}
private static final Material[] STAIRS = new Material[] {Material.WOOD_STAIRS, Material.COBBLESTONE_STAIRS,
Material.BRICK_STAIRS, Material.SMOOTH_STAIRS, Material.NETHER_BRICK_STAIRS, Material.SANDSTONE_STAIRS, Material.BRICK_STAIRS, Material.SMOOTH_STAIRS, Material.NETHER_BRICK_STAIRS, Material.SANDSTONE_STAIRS,
Material.SPRUCE_WOOD_STAIRS, Material.BIRCH_WOOD_STAIRS, Material.JUNGLE_WOOD_STAIRS}; Material.SPRUCE_WOOD_STAIRS, Material.BIRCH_WOOD_STAIRS, Material.JUNGLE_WOOD_STAIRS};
/** The original location. */ /** The original location. */
private final Location location; private Location location;
/** Is the player above stairs? */ /** Is the player above stairs? */
private final CustomBoolean aboveStairs = new CustomBoolean(); private Boolean aboveStairs;
/** Is the player in lava? */ /** Is the player in lava? */
private final CustomBoolean inLava = new CustomBoolean(); private Boolean inLava;
/** Is the player in water? */ /** Is the player in water? */
private final CustomBoolean inWater = new CustomBoolean(); private Boolean inWater;
/** Is the player is web? */ /** Is the player is web? */
private final CustomBoolean inWeb = new CustomBoolean(); private Boolean inWeb;
/** Is the player on the ground? */ /** Is the player on the ground? */
private final CustomBoolean onGround = new CustomBoolean(); private Boolean onGround;
/** Is the player on ice? */ /** Is the player on ice? */
private final CustomBoolean onIce = new CustomBoolean(); private Boolean onIce;
/** Is the player on ladder? */ /** Is the player on ladder? */
private final CustomBoolean onLadder = new CustomBoolean(); private Boolean onLadder;
/** Is the player on ladder (ignoring unclimbable vines)? **/
private final CustomBoolean onLadderBis = new CustomBoolean();
/** Is the player on soul sand? */
private final CustomBoolean onSoulSand = new CustomBoolean();
/** The bounding box of the player. */ /** The bounding box of the player. */
private final AxisAlignedBB boundingBox; private AxisAlignedBB boundingBox;
/** The entity player. */ /** The entity player. */
private final EntityPlayer entity; private EntityPlayer entity;
/** The x, y and z coordinates. */ /** The x, y and z coordinates. */
private final int x, y, z; private int x, y, z;
/** The world. */ /** The world. */
private final WorldServer world; private WorldServer world;
/**
* Instantiates a new player location.
*
* @param location
* the location
* @param player
* the player
*/
public PlayerLocation(final Location location, final Player player) {
this.location = location;
entity = ((CraftPlayer) player).getHandle();
boundingBox = entity.boundingBox.clone().d(location.getX() - entity.locX, location.getY() - entity.locY,
location.getZ() - entity.locZ);
x = (int) Math.floor(location.getX());
y = (int) Math.floor(boundingBox.b);
z = (int) Math.floor(location.getZ());
world = ((CraftWorld) location.getWorld()).getHandle();
}
/** /**
* Gets the location. * Gets the location.
@ -203,9 +136,9 @@ public class PlayerLocation {
* @return true, if the player above on stairs * @return true, if the player above on stairs
*/ */
public boolean isAboveStairs() { public boolean isAboveStairs() {
if (!aboveStairs.isSet()) if (aboveStairs == null)
aboveStairs.set(Arrays.asList(STAIRS).contains(Material.getMaterial(world.getTypeId(x, y - 1, z)))); aboveStairs = Arrays.asList(STAIRS).contains(Material.getMaterial(world.getTypeId(x, y - 1, z)));
return aboveStairs.get(); return aboveStairs;
} }
/** /**
@ -214,12 +147,12 @@ public class PlayerLocation {
* @return true, if the player is in lava * @return true, if the player is in lava
*/ */
public boolean isInLava() { public boolean isInLava() {
if (!inLava.isSet()) { if (inLava == null) {
AxisAlignedBB boundingBoxLava = boundingBox.clone(); AxisAlignedBB boundingBoxLava = boundingBox.clone();
boundingBoxLava = boundingBoxLava.grow(-0.10000000149011612D, -0.40000000596046448D, -0.10000000149011612D); boundingBoxLava = boundingBoxLava.grow(-0.10000000149011612D, -0.40000000596046448D, -0.10000000149011612D);
inLava.set(world.a(boundingBoxLava, net.minecraft.server.Material.LAVA)); inLava = world.a(boundingBoxLava, net.minecraft.server.Material.LAVA);
} }
return inLava.get(); return inLava;
} }
/** /**
@ -237,13 +170,13 @@ public class PlayerLocation {
* @return true, if the player is in water * @return true, if the player is in water
*/ */
public boolean isInWater() { public boolean isInWater() {
if (!inWater.isSet()) { if (inWater == null) {
AxisAlignedBB boundingBoxWater = boundingBox.clone(); AxisAlignedBB boundingBoxWater = boundingBox.clone();
boundingBoxWater = boundingBoxWater.grow(0.0D, -0.40000000596046448D, 0.0D); boundingBoxWater = boundingBoxWater.grow(0.0D, -0.40000000596046448D, 0.0D);
boundingBoxWater = boundingBoxWater.shrink(0.001D, 0.001D, 0.001D); boundingBoxWater = boundingBoxWater.shrink(0.001D, 0.001D, 0.001D);
inWater.set(world.a(boundingBoxWater, net.minecraft.server.Material.WATER, entity)); inWater = world.a(boundingBoxWater, net.minecraft.server.Material.WATER, entity);
} }
return inWater.get(); return inWater;
} }
/** /**
@ -252,19 +185,19 @@ public class PlayerLocation {
* @return true, if the player is in web * @return true, if the player is in web
*/ */
public boolean isInWeb() { public boolean isInWeb() {
if (!inWeb.isSet()) { if (inWeb == null) {
for (int blockX = (int) Math.floor(boundingBox.a + 0.001D); blockX <= (int) Math for (int blockX = (int) Math.floor(boundingBox.a + 0.001D); blockX <= (int) Math
.floor(boundingBox.d - 0.001D); blockX++) .floor(boundingBox.d - 0.001D); blockX++)
for (int blockY = (int) Math.floor(boundingBox.b + 0.001D); blockY <= (int) Math for (int blockY = (int) Math.floor(boundingBox.b + 0.001D); blockY <= (int) Math
.floor(boundingBox.e - 0.001D); blockY++) .floor(boundingBox.e - 0.001D); blockY++)
for (int blockZ = (int) Math.floor(boundingBox.c + 0.001D); blockZ <= (int) Math for (int blockZ = (int) Math.floor(boundingBox.c + 0.001D); blockZ <= (int) Math
.floor(boundingBox.f - 0.001D); blockZ++) .floor(boundingBox.f - 0.001D); blockZ++)
if (!inWeb.get() && world.getTypeId(blockX, blockY, blockZ) == Block.WEB.id) if (world.getTypeId(blockX, blockY, blockZ) == Block.WEB.id)
inWeb.set(true); inWeb = true;
if (!inWeb.isSet()) if (inWeb == null)
inWeb.set(false); inWeb = false;
} }
return inWeb.get(); return inWeb;
} }
/** /**
@ -273,12 +206,12 @@ public class PlayerLocation {
* @return true, if the player is on ground * @return true, if the player is on ground
*/ */
public boolean isOnGround() { public boolean isOnGround() {
if (!onGround.isSet()) { if (onGround == null) {
AxisAlignedBB boundingBoxGround = boundingBox.clone(); AxisAlignedBB boundingBoxGround = boundingBox.clone();
boundingBoxGround = boundingBoxGround.d(0D, -0.001D, 0D); boundingBoxGround = boundingBoxGround.d(0D, -0.001D, 0D);
onGround.set(world.getCubes(entity, boundingBoxGround).size() > 0); onGround = world.getCubes(entity, boundingBoxGround).size() > 0;
} }
return onGround.get(); return onGround;
} }
/** /**
@ -287,12 +220,12 @@ public class PlayerLocation {
* @return true, if the player is on ice * @return true, if the player is on ice
*/ */
public boolean isOnIce() { public boolean isOnIce() {
if (!onIce.isSet()) if (onIce == null)
if (entity.getBukkitEntity().isSneaking() || entity.getBukkitEntity().isBlocking()) if (entity.getBukkitEntity().isSneaking() || entity.getBukkitEntity().isBlocking())
onIce.set(world.getTypeId(x, (int) Math.floor(boundingBox.b - 0.1D), z) == Block.ICE.id); onIce = world.getTypeId(x, (int) Math.floor(boundingBox.b - 0.1D), z) == Block.ICE.id;
else else
onIce.set(world.getTypeId(x, y - 1, z) == Block.ICE.id); onIce = world.getTypeId(x, y - 1, z) == Block.ICE.id;
return onIce.get(); return onIce;
} }
/** /**
@ -301,71 +234,30 @@ public class PlayerLocation {
* @return true, if the player is on a ladder * @return true, if the player is on a ladder
*/ */
public boolean isOnLadder() { public boolean isOnLadder() {
return isOnLadder(false); if (onLadder == null)
onLadder = world.getTypeId(x, y, z) == Block.LADDER.id || world.getTypeId(x, y, z) == Block.VINE.id;
return onLadder;
} }
/** /**
* Checks if the player is on a ladder. * Sets the player location object.
* *
* @param ignoreUnclimbableVines * @param location
* ignore unclimbable vines or not? * the location
* @return true, if the player is on a ladder * @param player
* the player
*/ */
public boolean isOnLadder(final boolean ignoreUnclimbableVines) { public void set(final Location location, final Player player) {
if (ignoreUnclimbableVines) { this.location = location;
if (!onLadderBis.isSet())
if (world.getTypeId(x, y, z) == Block.LADDER.id)
onLadderBis.set(true);
else if (world.getTypeId(x, y, z) == Block.VINE.id) {
final int data = world.getData(x, y, z);
if ((data & 1) != 0) {
final int id = world.getTypeId(x, y, z + 1);
if (id != 0 && Block.byId[id].c() && Block.byId[id].material.isSolid())
onLadderBis.set(true);
}
if (!onLadder.isSet() && (data & 2) != 0) {
final int id = world.getTypeId(x - 1, y, z);
if (id != 0 && Block.byId[id].c() && Block.byId[id].material.isSolid())
onLadderBis.set(true);
}
if (!onLadder.isSet() && (data & 4) != 0) {
final int id = world.getTypeId(x, y, z - 1);
if (id != 0 && Block.byId[id].c() && Block.byId[id].material.isSolid())
onLadderBis.set(true);
}
if (!onLadder.isSet() && (data & 8) != 0) {
final int id = world.getTypeId(x + 1, y, z);
if (id != 0 && Block.byId[id].c() && Block.byId[id].material.isSolid())
onLadderBis.set(true);
}
}
return onLadderBis.get();
}
if (!onLadder.isSet())
onLadder.set(world.getTypeId(x, y, z) == Block.LADDER.id || world.getTypeId(x, y, z) == Block.VINE.id);
return onLadder.get();
}
/** entity = ((CraftPlayer) player).getHandle();
* Checks if the player is on soul sand. boundingBox = entity.boundingBox.clone().d(location.getX() - entity.locX, location.getY() - entity.locY,
* location.getZ() - entity.locZ);
* @return true, if the player is on soul sand x = (int) Math.floor(location.getX());
*/ y = (int) Math.floor(boundingBox.b);
public boolean isOnSoulSand() { z = (int) Math.floor(location.getZ());
if (!onSoulSand.isSet()) { world = ((CraftWorld) location.getWorld()).getHandle();
AxisAlignedBB boundingBoxGround = boundingBox.clone();
boundingBoxGround = boundingBoxGround.d(0D, -0.001D, 0D); aboveStairs = inLava = inWater = inWeb = onGround = onIce = onLadder = null;
for (final Object object : world.getCubes(entity, boundingBoxGround)) {
final AxisAlignedBB aabbCube = (AxisAlignedBB) object;
final int blockX = (int) Math.floor(aabbCube.a);
final int blockY = (int) Math.floor(aabbCube.b);
final int blockZ = (int) Math.floor(aabbCube.c);
if (!onSoulSand.get() && world.getTypeId(blockX, blockY, blockZ) == Block.SOUL_SAND.id)
onSoulSand.set(true);
}
if (!onSoulSand.isSet())
onSoulSand.set(false);
}
return onSoulSand.get();
} }
} }