mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-01-04 23:07:44 +01:00
Improved PlayerLocation objects management. Cleaned up the exemption
API.
This commit is contained in:
parent
5625b61b41
commit
2d5850a547
@ -190,12 +190,12 @@ public abstract class Check {
|
||||
* @return true, if the check is enabled
|
||||
*/
|
||||
public boolean isEnabled(final Player player) {
|
||||
if (NCPExemptionManager.isExempted(player, type)) return false;
|
||||
try {
|
||||
return type.isEnabled(player) && !player.hasPermission(type.getPermission());
|
||||
if (!type.isEnabled(player) || player.hasPermission(type.getPermission()))
|
||||
return false;
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
return !NCPExemptionManager.isExempted(player, type);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class Color extends Check {
|
||||
return message;
|
||||
|
||||
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.
|
||||
return message;
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class NoPwnage extends Check {
|
||||
final ChatConfig cc = ChatConfig.getConfig(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;
|
||||
|
||||
// Keep related to ChatData/NoPwnage/Color used lock.
|
||||
|
@ -43,7 +43,8 @@ public class Critical extends Check {
|
||||
boolean cancel = false;
|
||||
|
||||
// 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
|
||||
// and without blindness effect).
|
||||
@ -70,6 +71,8 @@ public class Critical extends Check {
|
||||
cancel = executeActions(player, data.criticalVL, delta, cc.criticalActions);
|
||||
}
|
||||
|
||||
location = null;
|
||||
|
||||
return cancel;
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,8 @@ public class CreativeFly extends Check {
|
||||
else if (wildcard == ParameterName.LOCATION_TO)
|
||||
return String.format(Locale.US, "%.2f, %.2f, %.2f", data.to.getX(), data.to.getY(), data.to.getZ());
|
||||
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
|
||||
return super.getParameter(wildcard, violationData);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.CheckData;
|
||||
import fr.neatmonster.nocheatplus.checks.CheckDataFactory;
|
||||
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
|
||||
|
||||
/*
|
||||
* M"""""`'"""`YM oo M""""""'YMM dP
|
||||
@ -49,54 +50,54 @@ public class MovingData implements CheckData {
|
||||
}
|
||||
|
||||
// Violation levels.
|
||||
public double creativeFlyVL = 0D;
|
||||
public double morePacketsVL = 0D;
|
||||
public double morePacketsVehicleVL = 0D;
|
||||
public double noFallVL = 0D;
|
||||
public double survivalFlyVL = 0D;
|
||||
public double creativeFlyVL = 0D;
|
||||
public double morePacketsVL = 0D;
|
||||
public double morePacketsVehicleVL = 0D;
|
||||
public double noFallVL = 0D;
|
||||
public double survivalFlyVL = 0D;
|
||||
|
||||
// Data shared between the fly checks.
|
||||
public int bunnyhopDelay;
|
||||
public double horizontalBuffer;
|
||||
public double horizontalFreedom;
|
||||
public double horizontalVelocityCounter;
|
||||
public double jumpAmplifier;
|
||||
public double verticalFreedom;
|
||||
public double verticalVelocity;
|
||||
public int verticalVelocityCounter;
|
||||
public int bunnyhopDelay;
|
||||
public double horizontalBuffer;
|
||||
public double horizontalFreedom;
|
||||
public double horizontalVelocityCounter;
|
||||
public double jumpAmplifier;
|
||||
public double verticalFreedom;
|
||||
public double verticalVelocity;
|
||||
public int verticalVelocityCounter;
|
||||
|
||||
// Data of the creative check.
|
||||
public boolean creativeFlyPreviousRefused;
|
||||
public boolean creativeFlyPreviousRefused;
|
||||
|
||||
// Data of the more packets check.
|
||||
public int morePacketsBuffer = 50;
|
||||
public long morePacketsLastTime;
|
||||
public int morePacketsPackets;
|
||||
public Location morePacketsSetback;
|
||||
public int morePacketsBuffer = 50;
|
||||
public long morePacketsLastTime;
|
||||
public int morePacketsPackets;
|
||||
public Location morePacketsSetback;
|
||||
|
||||
// Data of the more packets vehicle check.
|
||||
public int morePacketsVehicleBuffer = 50;
|
||||
public long morePacketsVehicleLastTime;
|
||||
public int morePacketsVehiclePackets;
|
||||
public Location morePacketsVehicleSetback;
|
||||
public int morePacketsVehicleBuffer = 50;
|
||||
public long morePacketsVehicleLastTime;
|
||||
public int morePacketsVehiclePackets;
|
||||
public Location morePacketsVehicleSetback;
|
||||
|
||||
// Data of the no fall check.
|
||||
public double noFallFallDistance;
|
||||
public boolean noFallOnGround;
|
||||
public boolean noFallWasOnGround;
|
||||
public double noFallFallDistance;
|
||||
public boolean noFallOnGround;
|
||||
public boolean noFallWasOnGround;
|
||||
|
||||
// Data of the survival fly check.
|
||||
public int survivalFlyJumpPhase;
|
||||
public double survivalFlyLastFromY;
|
||||
public int survivalFlyOnIce;
|
||||
public boolean survivalFlyWasInBed;
|
||||
public int survivalFlyJumpPhase;
|
||||
public double survivalFlyLastFromY;
|
||||
public int survivalFlyOnIce;
|
||||
public boolean survivalFlyWasInBed;
|
||||
|
||||
// Locations shared between all checks.
|
||||
public Location from;
|
||||
public Location ground;
|
||||
public Location setBack;
|
||||
public Location teleported;
|
||||
public Location to;
|
||||
public PlayerLocation from = new PlayerLocation();
|
||||
public Location ground;
|
||||
public Location setBack;
|
||||
public Location teleported;
|
||||
public PlayerLocation to = new PlayerLocation();
|
||||
|
||||
/**
|
||||
* Clear the data of the fly checks.
|
||||
|
@ -29,7 +29,6 @@ import org.bukkit.util.Vector;
|
||||
|
||||
import fr.neatmonster.nocheatplus.NoCheatPlus;
|
||||
import fr.neatmonster.nocheatplus.players.Permissions;
|
||||
import fr.neatmonster.nocheatplus.utilities.PlayerLocation;
|
||||
|
||||
/*
|
||||
* M"""""`'"""`YM oo
|
||||
@ -330,31 +329,29 @@ public class MovingListener implements Listener {
|
||||
// Counter has run out, now reduce the vertical freedom over time.
|
||||
data.verticalFreedom *= 0.93D;
|
||||
|
||||
final PlayerLocation from = new PlayerLocation(event.getFrom(), player);
|
||||
data.from = from.getLocation();
|
||||
if (from.isOnGround())
|
||||
data.ground = from.getLocation();
|
||||
final PlayerLocation to = new PlayerLocation(event.getTo(), player);
|
||||
data.to = to.getLocation();
|
||||
data.from.set(event.getFrom(), player);
|
||||
if (data.from.isOnGround())
|
||||
data.ground = data.from.getLocation();
|
||||
data.to.set(event.getTo(), player);
|
||||
|
||||
Location newTo = null;
|
||||
|
||||
if ((player.getGameMode() == GameMode.CREATIVE || player.getAllowFlight()) && creativeFly.isEnabled(player))
|
||||
// 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)) {
|
||||
// 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 (newTo == null && noFall.isEnabled(player))
|
||||
noFall.check(player, from, to);
|
||||
noFall.check(player, data.from, data.to);
|
||||
} else
|
||||
// He isn't handled by any fly check, clear his data.
|
||||
data.clearFlyData();
|
||||
|
||||
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.
|
||||
newTo = morePackets.check(player, from, to);
|
||||
newTo = morePackets.check(player, data.from, data.to);
|
||||
else
|
||||
// Otherwise we need to clear his data.
|
||||
data.clearMorePacketsData();
|
||||
|
@ -262,7 +262,8 @@ public class SurvivalFly extends Check {
|
||||
else if (wildcard == ParameterName.LOCATION_TO)
|
||||
return String.format(Locale.US, "%.2f, %.2f, %.2f", data.to.getX(), data.to.getY(), data.to.getZ());
|
||||
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
|
||||
return super.getParameter(wildcard, violationData);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
86
src/fr/neatmonster/nocheatplus/hooks/APIUtils.java
Normal file
86
src/fr/neatmonster/nocheatplus/hooks/APIUtils.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -15,212 +15,244 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
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.
|
||||
*
|
||||
* @author mc_dev
|
||||
*
|
||||
* @author asofold
|
||||
*/
|
||||
public class NCPExemptionManager {
|
||||
|
||||
/**
|
||||
* CheckType -> Entity id -> Exemption info.
|
||||
*
|
||||
* TODO: opt: move these to checks individually for even faster access.
|
||||
*/
|
||||
private static final Map<CheckType, Set<Integer>> exempted = new HashMap<CheckType, Set<Integer>>();
|
||||
|
||||
/**
|
||||
* Registered players (exact name) -> entity id (last time registered).
|
||||
*/
|
||||
private static final Map<String, Integer> registeredPlayers = new HashMap<String, Integer>();
|
||||
|
||||
static{
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player is exempted from a check right now.
|
||||
*
|
||||
* @param player
|
||||
* The player to exempt from checks.
|
||||
* @param checkType
|
||||
* 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 checkType){
|
||||
return isExempted(player.getEntityId(), checkType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity is exempted from a check by entity id right now.
|
||||
* <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 id
|
||||
* Entity id to exempt from checks.
|
||||
* @param checkType
|
||||
* 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 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.
|
||||
*/
|
||||
public static final void clear(){
|
||||
registeredPlayers.clear();
|
||||
// Use put with a new map to keep entries to stay thread safe.
|
||||
for (final CheckType checkType : CheckType.values()){
|
||||
if (APIUtil.needsSynchronization(checkType))
|
||||
exempted.put(checkType, Collections.synchronizedSet(new HashSet<Integer>(10)));
|
||||
else
|
||||
exempted.put(checkType, new HashSet<Integer>(10));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be registered before all other listeners of NCP (!).
|
||||
*
|
||||
* NOTE: For internal use only, DO NOT CALL FROM OUTSIDE.
|
||||
* @return
|
||||
*/
|
||||
public static Listener getListener(){
|
||||
return new Listener() {
|
||||
@SuppressWarnings("unused")
|
||||
@EventHandler(priority=EventPriority.LOWEST)
|
||||
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.
|
||||
* @param player
|
||||
*/
|
||||
protected static final void checkRemovePlayer(final Player player) {
|
||||
if (!registeredPlayers.containsKey(player.getName())) return;
|
||||
final Integer id = player.getEntityId();
|
||||
for (final CheckType checkType : CheckType.values()){
|
||||
// Check if player is exempted from something.
|
||||
if (isExempted(id, checkType)) return;
|
||||
}
|
||||
// No return = remove player.
|
||||
registeredPlayers.remove(player.getName());
|
||||
}
|
||||
/** A map associating a check type with the entity ids of its exempted players. */
|
||||
private static final Map<CheckType, Set<Integer>> exempted = new HashMap<CheckType, Set<Integer>>();
|
||||
|
||||
/** A map associating the registred player with their entity id. */
|
||||
private static final Map<String, Integer> registeredPlayers = new HashMap<String, Integer>();
|
||||
|
||||
static {
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all exemptions.
|
||||
*/
|
||||
public static final void clear() {
|
||||
registeredPlayers.clear();
|
||||
// Use put with a new map to keep entries to stay thread safe.
|
||||
for (final CheckType type : CheckType.values())
|
||||
if (APIUtils.needsSynchronization(type))
|
||||
exempted.put(type, Collections.synchronizedSet(new HashSet<Integer>()));
|
||||
else
|
||||
exempted.put(type, new HashSet<Integer>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt an entity from all checks permanently.
|
||||
*
|
||||
* @param entityId
|
||||
* the entity id
|
||||
*/
|
||||
public static final void exemptPermanently(final int entityId) {
|
||||
exemptPermanently(entityId, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt an entity from the given check or check group permanently (only until restart).
|
||||
*
|
||||
* @param entityId
|
||||
* the entity id
|
||||
* @param checkType
|
||||
* the check type
|
||||
*/
|
||||
public static final void exemptPermanently(final int entityId, final CheckType checkType) {
|
||||
final Integer id = entityId;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt a player from a check or check group permanently.
|
||||
*
|
||||
* @param player
|
||||
* the player
|
||||
* @param type
|
||||
* the check type
|
||||
*/
|
||||
public static final void exemptPermanently(final Player player, final CheckType type) {
|
||||
exemptPermanently(player.getEntityId(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be registered before all other listeners of NoCheatPlus.
|
||||
*
|
||||
* NOTE: For internal use only, DO NOT CALL FROM OUTSIDE.
|
||||
*
|
||||
* @return the listener
|
||||
*/
|
||||
public static Listener getListener() {
|
||||
return new Listener() {
|
||||
@SuppressWarnings("unused")
|
||||
@EventHandler(
|
||||
ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
NCPExemptionManager.registerPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@EventHandler(
|
||||
ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(final PlayerQuitEvent event) {
|
||||
NCPExemptionManager.tryToRemove(event.getPlayer());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity is exempted from a check right now.
|
||||
* <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
|
||||
* the entity id to exempt from checks
|
||||
* @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 entity is exempted from checks right now
|
||||
*/
|
||||
public static final boolean isExempted(final int entityId, final CheckType type) {
|
||||
return exempted.get(type).contains(entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player is exempted from a check right now.
|
||||
*
|
||||
* @param player
|
||||
* the player to exempt from checks
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register current entity id for the player.
|
||||
*
|
||||
* @param player
|
||||
* the player
|
||||
*/
|
||||
public static final void registerPlayer(final Player player) {
|
||||
final int entityId = player.getEntityId();
|
||||
final String name = player.getName();
|
||||
|
||||
final Integer registeredId = registeredPlayers.get(name);
|
||||
if (registeredId == null)
|
||||
// Player wasn't registered.
|
||||
registeredPlayers.put(name, entityId);
|
||||
else if (entityId != registeredId.intValue()) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the registeredPlayers mapping can be removed for a player, i.e. no exemptions are present.
|
||||
*
|
||||
* @param player
|
||||
* the player
|
||||
*/
|
||||
protected static final void tryToRemove(final Player player) {
|
||||
if (!registeredPlayers.containsKey(player.getName()))
|
||||
return;
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting an entity from all checks.
|
||||
*
|
||||
* @param entityId
|
||||
* the entity id
|
||||
*/
|
||||
public static final void unexempt(final int entityId) {
|
||||
unexempt(entityId, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting an entity from a certain check, or check group, as given.
|
||||
*
|
||||
* @param entityId
|
||||
* the entity id
|
||||
* @param type
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting a player from all checks.
|
||||
*
|
||||
* @param player
|
||||
* the player
|
||||
*/
|
||||
public static final void unexempt(final Player player) {
|
||||
unexempt(player, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting a player form a certain check, or check group, as given.
|
||||
*
|
||||
* @param player
|
||||
* the player
|
||||
* @param checkType
|
||||
* the check type
|
||||
*/
|
||||
public static final void unexempt(final Player player, final CheckType checkType) {
|
||||
unexempt(player.getEntityId(), checkType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register current entity id for the player.
|
||||
* @param player
|
||||
*/
|
||||
public static final void registerPlayer(final Player player) {
|
||||
final int newId = player.getEntityId();
|
||||
final String name = player.getName();
|
||||
|
||||
final Integer registeredId = registeredPlayers.get(name);
|
||||
if (registeredId == null){
|
||||
// Was not registered.
|
||||
registeredPlayers.put(name, newId);
|
||||
}
|
||||
else if (newId == registeredId.intValue()){
|
||||
// No change.
|
||||
}
|
||||
else {
|
||||
// Player was registered under another id (needs exchange).
|
||||
for (final Set<Integer> set : exempted.values()){
|
||||
if (set.remove(registeredId)){
|
||||
// replace.
|
||||
set.add(newId);
|
||||
}
|
||||
}
|
||||
registeredPlayers.put(name, newId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all exempting a player.
|
||||
* @param player
|
||||
*/
|
||||
public static final void unExempt(final Player player){
|
||||
unExempt(player, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting a player form a certain check, or check group, as given.
|
||||
* @param player
|
||||
* @param checkType
|
||||
*/
|
||||
public static final void unExempt(final Player player, final CheckType checkType){
|
||||
unExempt(player.getEntityId(), checkType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting an entity by entity id from all checks.
|
||||
* @param entityId
|
||||
*/
|
||||
public static final void unExempt(final int entityId){
|
||||
unExempt(entityId, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo exempting an entity by entity id from a certain check type, also check groups, etc.
|
||||
* @param entityId
|
||||
* @param checkType
|
||||
*/
|
||||
public static final void unExempt(final int entityId, final CheckType checkType){
|
||||
final Integer id = entityId;
|
||||
exempted.get(checkType).remove(id);
|
||||
for (final CheckType child : APIUtil.getChildren(checkType)){
|
||||
exempted.get(child).remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt a player form all checks.
|
||||
* @param player
|
||||
*/
|
||||
public static final void exemptPermanently(final Player player){
|
||||
exemptPermanently(player, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt a player from a check or check group permanently.
|
||||
* @param player
|
||||
* @param checkType
|
||||
*/
|
||||
public static final void exemptPermanently(final Player player, final CheckType checkType){
|
||||
exemptPermanently(player.getEntityId(), checkType);
|
||||
}
|
||||
|
||||
/**
|
||||
* exempt an entity from all checks, by entity id.
|
||||
* @param entityId
|
||||
*/
|
||||
public static final void exemptPermanently(final int entityId){
|
||||
exemptPermanently(entityId, CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt an entity by entity id from the given check or check group permanently (only until restart).
|
||||
* @param entityId
|
||||
* @param checkType
|
||||
*/
|
||||
public static final void exemptPermanently(final int entityId, final CheckType checkType){
|
||||
final Integer id = entityId;
|
||||
exempted.get(checkType).add(id);
|
||||
for (final CheckType child : APIUtil.getChildren(checkType)){
|
||||
exempted.get(child).add(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,112 +36,45 @@ import org.bukkit.entity.Player;
|
||||
*/
|
||||
public class PlayerLocation {
|
||||
|
||||
/**
|
||||
* 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,
|
||||
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.SPRUCE_WOOD_STAIRS, Material.BIRCH_WOOD_STAIRS, Material.JUNGLE_WOOD_STAIRS};
|
||||
|
||||
/** The original location. */
|
||||
private final Location location;
|
||||
private Location location;
|
||||
|
||||
/** Is the player above stairs? */
|
||||
private final CustomBoolean aboveStairs = new CustomBoolean();
|
||||
private Boolean aboveStairs;
|
||||
|
||||
/** Is the player in lava? */
|
||||
private final CustomBoolean inLava = new CustomBoolean();
|
||||
private Boolean inLava;
|
||||
|
||||
/** Is the player in water? */
|
||||
private final CustomBoolean inWater = new CustomBoolean();
|
||||
private Boolean inWater;
|
||||
|
||||
/** Is the player is web? */
|
||||
private final CustomBoolean inWeb = new CustomBoolean();
|
||||
private Boolean inWeb;
|
||||
|
||||
/** Is the player on the ground? */
|
||||
private final CustomBoolean onGround = new CustomBoolean();
|
||||
private Boolean onGround;
|
||||
|
||||
/** Is the player on ice? */
|
||||
private final CustomBoolean onIce = new CustomBoolean();
|
||||
private Boolean onIce;
|
||||
|
||||
/** Is the player on ladder? */
|
||||
private final CustomBoolean onLadder = new CustomBoolean();
|
||||
|
||||
/** 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();
|
||||
private Boolean onLadder;
|
||||
|
||||
/** The bounding box of the player. */
|
||||
private final AxisAlignedBB boundingBox;
|
||||
private AxisAlignedBB boundingBox;
|
||||
|
||||
/** The entity player. */
|
||||
private final EntityPlayer entity;
|
||||
private EntityPlayer entity;
|
||||
|
||||
/** The x, y and z coordinates. */
|
||||
private final int x, y, z;
|
||||
private int x, y, z;
|
||||
|
||||
/** The world. */
|
||||
private final 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();
|
||||
}
|
||||
private WorldServer world;
|
||||
|
||||
/**
|
||||
* Gets the location.
|
||||
@ -203,9 +136,9 @@ public class PlayerLocation {
|
||||
* @return true, if the player above on stairs
|
||||
*/
|
||||
public boolean isAboveStairs() {
|
||||
if (!aboveStairs.isSet())
|
||||
aboveStairs.set(Arrays.asList(STAIRS).contains(Material.getMaterial(world.getTypeId(x, y - 1, z))));
|
||||
return aboveStairs.get();
|
||||
if (aboveStairs == null)
|
||||
aboveStairs = Arrays.asList(STAIRS).contains(Material.getMaterial(world.getTypeId(x, y - 1, z)));
|
||||
return aboveStairs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,12 +147,12 @@ public class PlayerLocation {
|
||||
* @return true, if the player is in lava
|
||||
*/
|
||||
public boolean isInLava() {
|
||||
if (!inLava.isSet()) {
|
||||
if (inLava == null) {
|
||||
AxisAlignedBB boundingBoxLava = boundingBox.clone();
|
||||
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
|
||||
*/
|
||||
public boolean isInWater() {
|
||||
if (!inWater.isSet()) {
|
||||
if (inWater == null) {
|
||||
AxisAlignedBB boundingBoxWater = boundingBox.clone();
|
||||
boundingBoxWater = boundingBoxWater.grow(0.0D, -0.40000000596046448D, 0.0D);
|
||||
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
|
||||
*/
|
||||
public boolean isInWeb() {
|
||||
if (!inWeb.isSet()) {
|
||||
if (inWeb == null) {
|
||||
for (int blockX = (int) Math.floor(boundingBox.a + 0.001D); blockX <= (int) Math
|
||||
.floor(boundingBox.d - 0.001D); blockX++)
|
||||
for (int blockY = (int) Math.floor(boundingBox.b + 0.001D); blockY <= (int) Math
|
||||
.floor(boundingBox.e - 0.001D); blockY++)
|
||||
for (int blockZ = (int) Math.floor(boundingBox.c + 0.001D); blockZ <= (int) Math
|
||||
.floor(boundingBox.f - 0.001D); blockZ++)
|
||||
if (!inWeb.get() && world.getTypeId(blockX, blockY, blockZ) == Block.WEB.id)
|
||||
inWeb.set(true);
|
||||
if (!inWeb.isSet())
|
||||
inWeb.set(false);
|
||||
if (world.getTypeId(blockX, blockY, blockZ) == Block.WEB.id)
|
||||
inWeb = true;
|
||||
if (inWeb == null)
|
||||
inWeb = false;
|
||||
}
|
||||
return inWeb.get();
|
||||
return inWeb;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,12 +206,12 @@ public class PlayerLocation {
|
||||
* @return true, if the player is on ground
|
||||
*/
|
||||
public boolean isOnGround() {
|
||||
if (!onGround.isSet()) {
|
||||
if (onGround == null) {
|
||||
AxisAlignedBB boundingBoxGround = boundingBox.clone();
|
||||
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
|
||||
*/
|
||||
public boolean isOnIce() {
|
||||
if (!onIce.isSet())
|
||||
if (onIce == null)
|
||||
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
|
||||
onIce.set(world.getTypeId(x, y - 1, z) == Block.ICE.id);
|
||||
return onIce.get();
|
||||
onIce = world.getTypeId(x, y - 1, z) == Block.ICE.id;
|
||||
return onIce;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,71 +234,30 @@ public class PlayerLocation {
|
||||
* @return true, if the player is on a ladder
|
||||
*/
|
||||
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
|
||||
* ignore unclimbable vines or not?
|
||||
* @return true, if the player is on a ladder
|
||||
* @param location
|
||||
* the location
|
||||
* @param player
|
||||
* the player
|
||||
*/
|
||||
public boolean isOnLadder(final boolean ignoreUnclimbableVines) {
|
||||
if (ignoreUnclimbableVines) {
|
||||
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();
|
||||
}
|
||||
public void set(final Location location, final Player player) {
|
||||
this.location = location;
|
||||
|
||||
/**
|
||||
* Checks if the player is on soul sand.
|
||||
*
|
||||
* @return true, if the player is on soul sand
|
||||
*/
|
||||
public boolean isOnSoulSand() {
|
||||
if (!onSoulSand.isSet()) {
|
||||
AxisAlignedBB boundingBoxGround = boundingBox.clone();
|
||||
boundingBoxGround = boundingBoxGround.d(0D, -0.001D, 0D);
|
||||
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();
|
||||
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();
|
||||
|
||||
aboveStairs = inLava = inWater = inWeb = onGround = onIce = onLadder = null;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user