Fix / "refine" elytra handling. Set all maxheight to 128.

* Lift-off side conditions.
* Force stop gliding for some side conditions, to avoid freezing.
* Set maxheight to 128 for elytra and levitation too (mods/plugins/etc)
- better refine checks to catch stuff.
* Fix special flags not getting set with initializing ModelFlying from
config.
* Debug log exceeding the maxheight setting.
* Mostly harmless: Add interfaces and access methods, including
convenience methods.
* Don't store methods in Bridge1_9.

Issues left, not limited to:
* Boost not recognized on occasion.
* Gliding state kept when submerged in water and moving normally like
when not gliding, e.g. ascending (head in water / fully submerged).
* Elytra lift off not accepted: Gliding state set near the water
surface, but survivalfly check runs. Might be fixed already, though.
This commit is contained in:
asofold 2017-05-07 22:29:38 +02:00
parent 9505c0747d
commit 344b032d49
17 changed files with 323 additions and 88 deletions

View File

@ -301,16 +301,28 @@ public class BlockInteractListener extends CheckListener {
final BlockInteractData data = BlockInteractData.getData(player);
data.setPlayerInteractEventResolution(event);
/*
* TODO: BlockDamageEvent fires before MONITOR level, BlockBreak after
* (!). Thus resolution is set on LOWEST already, probably should be
* HIGHEST to account for other plugins.
* TODO: BlockDamageEvent fires before BlockInteract/MONITOR level,
* BlockBreak after (!). Thus resolution is set on LOWEST already,
* probably should be HIGHEST to account for other plugins.
*/
// Elytra boost.
final Block block = event.getClickedBlock();
if ((event.getAction() == Action.RIGHT_CLICK_AIR
|| event.getAction() == Action.RIGHT_CLICK_BLOCK
&& block != null && BlockProperties.isLiquid(block.getType())
)
/*
* TODO: Cross check with the next incoming move: has an item been used,
* is gliding, reset if necessary.
*/
//final Block block = event.getClickedBlock();
// if (data.debug) {
// debug(player, "BlockInteractResolution: cancelled=" + event.isCancelled()
// + " action=" + event.getAction() + " block=" + block + " item=" + Bridge1_9.getUsedItem(player, event));
// }
if (
(
event.getAction() == Action.RIGHT_CLICK_AIR
// Water doesn't happen, block typically is null.
// || event.getAction() == Action.RIGHT_CLICK_BLOCK
// && block != null && BlockProperties.isLiquid(block.getType())
// TODO: web ?
)
&& event.isCancelled() && event.useItemInHand() != Result.DENY) {
final ItemStack stack = Bridge1_9.getUsedItem(player, event);
if (stack != null && BridgeMisc.maybeElytraBoost(player, stack.getType())) {

View File

@ -250,8 +250,6 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
public int combinedMediumHCount = 0;
/** Sum of actual speed / base speed for horizontal moving within air + certain medium. */
public double combinedMediumHValue = 0.0;
/** Count combined medium successive moves, permanent. */
public int combinedMediumPermanentCount = 0; // TODO: Not 100% convinced of usefulness.
// Locations shared between all checks.
private Location setBack = null;

View File

@ -30,8 +30,10 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityToggleGlideEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerBedLeaveEvent;
@ -64,6 +66,7 @@ import fr.neatmonster.nocheatplus.checks.combined.Combined;
import fr.neatmonster.nocheatplus.checks.combined.CombinedConfig;
import fr.neatmonster.nocheatplus.checks.combined.CombinedData;
import fr.neatmonster.nocheatplus.checks.moving.magic.Magic;
import fr.neatmonster.nocheatplus.checks.moving.model.ModelFlying;
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData;
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveInfo;
import fr.neatmonster.nocheatplus.checks.moving.player.CreativeFly;
@ -208,6 +211,43 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// Register vehicleChecks.
NCPAPIProvider.getNoCheatPlusAPI().addComponent(vehicleChecks);
blockChangeTracker = NCPAPIProvider.getNoCheatPlusAPI().getBlockChangeTracker();
if (Bridge1_9.hasEntityToggleGlideEvent()) {
queuedComponents.add(new Listener() {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityToggleGlide(final EntityToggleGlideEvent event) {
if (handleEntityToggleGlideEvent(event.getEntity(), event.isGliding())) {
event.setCancelled(true);
}
}
});
}
}
/**
*
* @param entity
* @param isGliding
* @return True, if the event is to be cancelled.
*/
private boolean handleEntityToggleGlideEvent(final Entity entity, final boolean isGliding) {
// Ignore non players.
if (!(entity instanceof Player)) {
return false;
}
final Player player = (Player) entity;
if (isGliding && !Bridge1_9.isGlidingWithElytra(player)) { // Includes check for elytra item.
final PlayerMoveInfo info = aux.usePlayerMoveInfo();
info.set(player, player.getLocation(info.useLoc), null, 0.001); // Only restrict very near ground.
final MovingData data = MovingData.getData(player);
final boolean res = !MovingUtil.canLiftOffWithElytra(player, info.from, data);
info.cleanup();
aux.returnPlayerMoveInfo(info);
if (res && data.debug) {
debug(player, "Prevent toggle glide on.");
}
return res;
}
return false;
}
/**
@ -625,15 +665,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
&& !player.hasPermission(Permissions.MOVING_CREATIVEFLY)) {
checkCf = true;
checkSf = false;
data.adjustFlySpeed(player.getFlySpeed(), tick, cc.speedGrace);
data.adjustWalkSpeed(player.getWalkSpeed(), tick, cc.speedGrace);
thisMove.flyCheck = CheckType.MOVING_CREATIVEFLY;
// TODO: Adjust height of PlayerLocation more efficiently / fetch model early.
thisMove.modelFlying = cc.getModelFlying(player, pFrom, data, cc);
if (MovingConfig.ID_JETPACK_ELYTRA.equals(thisMove.modelFlying.getId())) {
pFrom.setByGivenHeight(from, player, 0.6, cc.yOnGround);
pTo.setByGivenHeight(to, player, 0.6, cc.yOnGround);
}
prepareCreativeFlyCheck(player, from, to, moveInfo, thisMove, multiMoveCount, tick, data, cc);
}
else {
checkCf = checkSf = false;
@ -896,10 +928,41 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
}
// Set back handling.
prepareSetBack(player, event, newTo, data, cc);
// Prevent freezing (e.g. ascending with gliding set in water, but moving normally).
if ((thisMove.flyCheck == CheckType.MOVING_SURVIVALFLY
|| thisMove.flyCheck == CheckType.MOVING_CREATIVEFLY
&& pFrom.isInLiquid()
) && Bridge1_9.isGlidingWithElytra(player)) {
stopGliding(player);
}
return true;
}
}
private static void stopGliding(final Player player) {
player.setGliding(false);
}
private void prepareCreativeFlyCheck(final Player player, final Location from, final Location to,
final PlayerMoveInfo moveInfo, final PlayerMoveData thisMove, final int multiMoveCount,
final int tick, final MovingData data, final MovingConfig cc) {
data.adjustFlySpeed(player.getFlySpeed(), tick, cc.speedGrace);
data.adjustWalkSpeed(player.getWalkSpeed(), tick, cc.speedGrace);
// TODO: Adjust height of PlayerLocation more efficiently / fetch model early.
final ModelFlying model = cc.getModelFlying(player, moveInfo.from, data, cc);
if (MovingConfig.ID_JETPACK_ELYTRA.equals(model.getId())) {
final MCAccess mcAccess = this.mcAccess.getHandle();
MovingUtil.setElytraProperties(player, moveInfo.from, from, cc.yOnGround, mcAccess);
MovingUtil.setElytraProperties(player, moveInfo.to, to, cc.yOnGround, mcAccess);
thisMove.set(moveInfo.from, moveInfo.to);
if (multiMoveCount > 0) {
thisMove.multiMoveCount = multiMoveCount;
}
}
thisMove.flyCheck = CheckType.MOVING_CREATIVEFLY;
thisMove.modelFlying = model;
}
/**
* Only for yDistance < 0 + some bounce envelope checked.
* @param player

View File

@ -98,12 +98,12 @@ public class ModelFlying {
horizontalModSpeed(config.getDouble(prefix + ConfPaths.SUB_HORIZONTAL_SPEED, defaults.getHorizontalModSpeed()));
horizontalModSprint(config.getDouble(prefix + ConfPaths.SUB_HORIZONTAL_MODSPRINT, defaults.getHorizontalModSprint()));
verticalAscendModSpeed(config.getDouble(prefix + ConfPaths.SUB_VERTICAL_ASCEND_SPEED, defaults.getVerticalAscendModSpeed()));
// TODO: verticalAscendGliding.
verticalAscendGliding(defaults.getVerticalAscendGliding()); // Config ?
maxHeight(config.getDouble(prefix + ConfPaths.SUB_VERTICAL_MAXHEIGHT, defaults.getMaxHeight()));
applyModifiers(config.getBoolean(prefix + ConfPaths.SUB_MODIFIERS, defaults.getApplyModifiers()));
gravity(config.getBoolean(prefix + ConfPaths.SUB_VERTICAL_GRAVITY, defaults.getGravity()));
ground(config.getBoolean(prefix + ConfPaths.SUB_GROUND, defaults.getGround()));
// TODO: scaleLevitationEffect.
scaleLevitationEffect(defaults.getScaleLevitationEffect()); // Config?
}
/**

View File

@ -228,6 +228,9 @@ public class CreativeFly extends Check {
// Maximum height check (silent set back).
if (to.getY() > maximumHeight) {
setBack = data.getSetBack(to);
if (data.debug) {
debug(player, "Maximum height exceeded, silent set-back.");
}
}
if (setBack == null) {
// Slowly reduce the violation level with each event.
@ -375,9 +378,9 @@ public class CreativeFly extends Check {
}
// Related to elytra.
if (limitV == 0.0 && model.getVerticalAscendGliding()) {
if (model.getVerticalAscendGliding()) {
// TODO: Better detection of an elytra model (extra flags?).
limitV = hackLytra(yDistance, limitV, thisMove, lastMove, data);
limitV = Math.max(limitV, limitV = hackLytra(yDistance, limitV, thisMove, lastMove, data));
}
if (model.getGravity()) {

View File

@ -571,12 +571,10 @@ public class SurvivalFly extends Check {
// TODO: Is above stairs ?
}
// Count how long one is moving inside of a medium.
data.combinedMediumPermanentCount ++;
if (oldLiftOffEnvelope != data.liftOffEnvelope) {
data.insideMediumCount = 0;
data.combinedMediumHCount = 0;
data.combinedMediumHValue = 0.0;
data.combinedMediumPermanentCount = 0;
}
else if (!resetFrom || !resetTo) {
data.insideMediumCount = 0;

View File

@ -30,7 +30,6 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
import fr.neatmonster.nocheatplus.checks.moving.MovingData;
import fr.neatmonster.nocheatplus.checks.moving.magic.Magic;
import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope;
import fr.neatmonster.nocheatplus.checks.moving.model.MoveData;
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData;
import fr.neatmonster.nocheatplus.checks.moving.player.PlayerSetBackMethod;
@ -89,10 +88,14 @@ public class MovingUtil {
&& (cc.ignoreCreative || gameMode != GameMode.CREATIVE) && !player.isFlying()
&& (cc.ignoreAllowFlight || !player.getAllowFlight())
&& !NCPExemptionManager.isExempted(player, CheckType.MOVING_SURVIVALFLY, true)
&& (!Bridge1_9.isGlidingWithElytra(player)
|| !isGlidingWithElytraValid(player, fromLocation, data, cc))
&& (Double.isInfinite(Bridge1_9.getLevitationAmplifier(player))
|| fromLocation.isInLiquid())
&& (
!Bridge1_9.isGlidingWithElytra(player)
|| !isGlidingWithElytraValid(player, fromLocation, data, cc)
)
&& (
Double.isInfinite(Bridge1_9.getLevitationAmplifier(player))
|| fromLocation.isInLiquid()
)
&& !player.hasPermission(Permissions.MOVING_SURVIVALFLY);
}
@ -109,37 +112,110 @@ public class MovingUtil {
public static boolean isGlidingWithElytraValid(final Player player, final PlayerLocation fromLocation,
final MovingData data, final MovingConfig cc) {
// Prevent forms of noclip - could be too coarse / false positives.
if (!fromLocation.isPassable()) {
return false;
}
// TODO: Confine/relax more, if needed (in-medium count, past moves).
// TODO: Configuration for which/if to check on either lift-off / unknown / gliding.
// TODO: Item durability?
// TODO: TEST LAVA (ordinary and boost, lift off and other).
/*
* TODO: Allow if last move not touched ground (+-) after all the
* onGround check isn't much needed, if we can test for the relevant stuff (web?).
*/
// Check start glide conditions.
final PlayerMoveData firstPastMove = data.playerMoves.getFirstPastMove();
if (!firstPastMove.toIsValid || firstPastMove.modelFlying == null
if (
// Skip lift-off conditions if the EntityToggleGlideEvent is present (checked there).
!Bridge1_9.hasEntityToggleGlideEvent()
// Otherwise only treat as lift-off, if not already gliding.
&& !firstPastMove.toIsValid || firstPastMove.modelFlying == null
|| !MovingConfig.ID_JETPACK_ELYTRA.equals(firstPastMove.modelFlying.getId())) {
// Treat as a lift off.
return canLiftOffWithElytra(fromLocation, data);
// TODO: Past map states might allow lift off (...).
return canLiftOffWithElytra(player, fromLocation, data);
}
// TODO: Past map states might allow lift off (...).
// TODO: Other abort conditions.
/*
* TODO: Test / verify it gets turned off if depleted during gliding,
* provided the client doesn't help knowing. (Only shortly tested with
* grep -r "ItemElytra.d" <- looks good.)
*/
// // Test late, as lift-off check also tests for this.
// if (InventoryUtil.isItemBroken(player.getInventory().getChestplate())) {
// return false;
// }
/*
* TODO: Rather focus on abort conditions (in-medium stay time for
* special blocks, sleeping / dead / ...)?
*/
// Only the web can stop a player who isn't propelled by a rocket.
// TODO: If this is not lift-off, check with a lowered bounding box!
return data.fireworksBoostDuration > 0 || !fromLocation.isInWeb();
return data.fireworksBoostDuration > 0 || !BlockProperties.collidesId(fromLocation.getBlockCache(),
fromLocation.getMinX(), fromLocation.getMinY(), fromLocation.getMinZ(),
fromLocation.getMaxX(), fromLocation.getMinY() + 0.6, fromLocation.getMaxZ(), Material.WEB);
}
private static boolean canLiftOffWithElytra(final PlayerLocation fromLocation, final MovingData data) {
return !fromLocation.isInLiquid() && !fromLocation.isInWeb()
&& (data.liftOffEnvelope == LiftOffEnvelope.LIMIT_NEAR_GROUND
|| data.liftOffEnvelope == LiftOffEnvelope.NORMAL);
// && !fromLocation.isOnGround() // TODO: Nice to have: been in air slightly before.
//|| data.combinedMediumPermanentCount > data.insideMediumCount
//&& data.combinedMediumPermanentCount > 15;
/**
* Check lift-off (CB: on ground is done wrongly, inWater probably is
* correct, web is not checked).
*
* @param fromLocation
* @param data
* @return
*/
public static boolean canLiftOffWithElytra(final Player player, final PlayerLocation loc,
final MovingData data) {
// TODO: Item durability here too?
// TODO: this/firstPast- Move not touching or not explicitly on ground would be enough?
return
loc.isPassableBox() // Full box as if standing for lift-off.
&& !loc.isInWeb()
// Durability is checked within PlayerConnection (toggling on).
// && !InventoryUtil.isItemBroken(player.getInventory().getChestplate())
/*
* TODO: Could be a problem with too high yOnGround. Actual
* cheating rather would have to be prevented by monitoring
* jumping more tightly (low jump = set back, needs
* false-positive-free checking (...)).
*/
&& !loc.isOnGround(0.001)
// Assume water is checked correctly.
// && (
// !fromLocation.isInLiquid() // (Needs to check for actual block bounds).
// /*
// * Observed with head free, but feet clearly in water:
// * lift off from water (not 100% easy to do).
// */
// || !BlockProperties.isLiquid(fromLocation.getTypeId())
// || !BlockProperties.isLiquid(fromLocation.getTypeIdAbove())
// )
;
}
/**
* Workaround for getEyeHeight not accounting for special conditions like
* gliding with elytra. (Sleeping is not checked.)
*
* @param player
* @return
*/
public static double getEyeHeight(final Player player) {
// TODO: Need a variant/method to test legitimate state transitions?
// TODO: EntityToggleGlideEvent
return Bridge1_9.isGlidingWithElytra(player) ? 0.4 : player.getEyeHeight();
}
/**
* Initialize pLoc with edge data specific to gliding with elytra.
*
* @param player
* @param pLoc
* @param loc
* @param yOnGround
* @param mcAccess
*/
public static void setElytraProperties(final Player player, final PlayerLocation pLoc, final Location loc,
final double yOnGround, final MCAccess mcAccess) {
pLoc.set(loc, player, mcAccess.getWidth(player), 0.4, 0.6, 0.6, yOnGround);
}
/**

View File

@ -14,8 +14,6 @@
*/
package fr.neatmonster.nocheatplus.compat;
import java.lang.reflect.Method;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEntityEvent;
@ -35,10 +33,11 @@ public class Bridge1_9 {
public static final Material ELYTRA = Material.getMaterial("ELYTRA");
public static final Material END_CRYSTAL_ITEM = Material.getMaterial("END_CRYSTAL");
private static Method getItemInOffHand = ReflectionUtil.getMethodNoArgs(PlayerInventory.class, "getItemInOffHand", ItemStack.class);
private static Method getItemInMainHand = ReflectionUtil.getMethodNoArgs(PlayerInventory.class, "getItemInMainHand", ItemStack.class);
private static final boolean hasGetItemInOffHand = ReflectionUtil.getMethodNoArgs(PlayerInventory.class, "getItemInOffHand", ItemStack.class) != null;
private static final boolean hasGetItemInMainHand = ReflectionUtil.getMethodNoArgs(PlayerInventory.class, "getItemInMainHand", ItemStack.class) != null;
private static Method isGliding = ReflectionUtil.getMethodNoArgs(Player.class, "isGliding", boolean.class);
private static final boolean hasIsGliding = ReflectionUtil.getMethodNoArgs(Player.class, "isGliding", boolean.class) != null;
private static final boolean hasEntityToggleGlideEvent = ReflectionUtil.getClass("org.bukkit.event.entity.EntityToggleGlideEvent") != null;
public static boolean hasLevitation() {
return LEVITATION != null;
@ -57,15 +56,19 @@ public class Bridge1_9 {
}
public static boolean hasGetItemInOffHand() {
return getItemInOffHand != null;
return hasGetItemInOffHand;
}
public static boolean hasGetItemInMainHand() {
return getItemInMainHand != null;
return hasGetItemInMainHand;
}
public static boolean hasIsGliding() {
return isGliding != null;
return hasIsGliding;
}
public static boolean hasEntityToggleGlideEvent() {
return hasEntityToggleGlideEvent;
}
/**
@ -128,12 +131,8 @@ public class Bridge1_9 {
@SuppressWarnings("deprecation")
public static ItemStack getItemInMainHand(final Player player) {
if (getItemInMainHand == null) {
return player.getItemInHand(); // As long as feasible (see: CraftInventoryPlayer).
}
else {
return player.getInventory().getItemInMainHand();
}
return hasGetItemInMainHand ? player.getInventory().getItemInMainHand()
: player.getItemInHand(); // As long as feasible (see: CraftInventoryPlayer).
}
/**
@ -143,21 +142,11 @@ public class Bridge1_9 {
* @return In case the method is not present, null will be returned.
*/
public static ItemStack getItemInOffHand(final Player player) {
if (getItemInOffHand == null) {
return null;
}
else {
return player.getInventory().getItemInOffHand();
}
return hasGetItemInOffHand ? player.getInventory().getItemInOffHand() : null;
}
public static boolean isGliding(final Player player) {
if (isGliding == null) {
return false;
}
else {
return player.isGliding();
}
return hasIsGliding ? player.isGliding() : false;
}
/**
@ -183,7 +172,7 @@ public class Bridge1_9 {
}
}
}
/**
* Get the item that was used with this event, assume right click (not feet etc.).
* @param player

View File

@ -0,0 +1,14 @@
package fr.neatmonster.nocheatplus.components.location;
/**
* 3D bounding box (doubles).
*
* @author asofold
*
*/
public interface IGetBox2D {
public double getMinX();
public double getMinZ();
public double getMaxX();
public double getMaxZ();
}

View File

@ -0,0 +1,12 @@
package fr.neatmonster.nocheatplus.components.location;
/**
* 3D bounding box (doubles).
*
* @author asofold
*
*/
public interface IGetBox3D extends IGetBox2D {
public double getMinY();
public double getMaxY();
}

View File

@ -385,7 +385,7 @@ public class DefaultConfig extends ConfigFile {
if (Bridge1_9.hasLevitation()) {
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_HORIZONTAL_SPEED, 50, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_VERTICAL_ASCEND_SPEED, 10, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_VERTICAL_MAXHEIGHT, 32, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_VERTICAL_MAXHEIGHT, 128, 1104);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_VERTICAL_GRAVITY, false, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_MODIFIERS, false, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation." + ConfPaths.SUB_GRAVITY, false, 785);
@ -395,7 +395,7 @@ public class DefaultConfig extends ConfigFile {
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra." + ConfPaths.SUB_HORIZONTAL_SPEED, 520, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra." + ConfPaths.SUB_HORIZONTAL_MODSPRINT, 1.0, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra." + ConfPaths.SUB_VERTICAL_ASCEND_SPEED, 0, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra." + ConfPaths.SUB_VERTICAL_MAXHEIGHT, 8, 785);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra." + ConfPaths.SUB_VERTICAL_MAXHEIGHT, 128, 1104);
set(ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra." + ConfPaths.SUB_MODIFIERS, false, 785);
}
set(ConfPaths.MOVING_CREATIVEFLY_ACTIONS,

View File

@ -187,4 +187,23 @@ public class InventoryUtil {
(type.isEdible() || type == Material.POTION || type == Material.MILK_BUCKET);
}
/**
* Test for max durability, only makes sense with items that can be in
* inventory once broken, such as elytra. This method does not (yet) provide
* legacy support. This tests for ItemStack.getDurability() >=
* Material.getMaxDurability, so it only is suited for a context where this
* is what you want to check for.
*
* @param stack
* May be null, would yield true.
* @return
*/
public static boolean isItemBroken(final ItemStack stack) {
if (stack == null) {
return true;
}
final Material mat = stack.getType();
return stack.getDurability() >= mat.getMaxDurability();
}
}

View File

@ -14,6 +14,8 @@
*/
package fr.neatmonster.nocheatplus.utilities.collision;
import fr.neatmonster.nocheatplus.components.location.IGetBox3D;
public class CollideRayVsAABB implements ICollideRayVsAABB {
private boolean findNearestPointIfNotCollide = false;
@ -42,6 +44,12 @@ public class CollideRayVsAABB implements ICollideRayVsAABB {
return this;
}
@Override
public ICollideRayVsAABB setAABB(final IGetBox3D box) {
setAABB(box.getMinX(), box.getMinY(), box.getMinZ(), box.getMaxX(), box.getMaxY(), box.getMaxZ());
return this;
}
@Override
public ICollideRayVsAABB setAABB(double targetX, double targetY, double targetZ,
double boxMarginHorizontal, double boxMarginVertical) {

View File

@ -14,6 +14,7 @@
*/
package fr.neatmonster.nocheatplus.utilities.collision;
import fr.neatmonster.nocheatplus.components.location.IGetBox3D;
import fr.neatmonster.nocheatplus.components.location.IGetPosition;
/**
@ -73,6 +74,15 @@ public interface ICollideRayVsAABB extends IGetPosition {
public ICollideRayVsAABB setAABB(double targetX, double targetY, double targetZ,
double boxMarginHorizontal, double boxMarginVertical);
/**
* Set the properties of the AABB, using a given boxIGetBox3D instance.
*
* @param box
* The 3D bounding box to set.
* @return The same instance for chaining.
*/
public ICollideRayVsAABB setAABB(IGetBox3D box);
/**
* Set the properties of the AABB, using block coordinates and a margin.
* This can be called independently of setRay and loop, in order to

View File

@ -116,18 +116,18 @@ public class PlayerLocation extends RichEntityLocation {
}
/**
* Like set, but for the height properties only the given height is used, no
* special cases.
*
* Set with specific height/length/eyeHeight properties.
* @param location
* @param player
* @param width
* @param eyeHeight
* @param height
* @param fullHeight
* @param yOnGround
*/
public void setByGivenHeight(final Location location, final Player player, final double height,
final double yOnGround) {
super.doSetExactHeight(location, player, true, getMCAccess().getWidth(player),
height, height, height, yOnGround);
public void set(final Location location, final Player player, final double width,
final double eyeHeight, final double height, final double fullHeight, final double yOnGround) {
super.doSetExactHeight(location, player, true, width, eyeHeight, height, fullHeight, yOnGround);
// Entity reference.
this.player = player;
}

View File

@ -26,6 +26,7 @@ import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeReferen
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction;
import fr.neatmonster.nocheatplus.components.location.IGetBox3D;
import fr.neatmonster.nocheatplus.components.location.IGetBlockPosition;
import fr.neatmonster.nocheatplus.components.location.IGetBukkitLocation;
import fr.neatmonster.nocheatplus.components.location.IGetPosition;
@ -43,7 +44,7 @@ import fr.neatmonster.nocheatplus.utilities.map.MapUtil;
* @author asofold
*
*/
public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition {
public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition, IGetBox3D {
// TODO: Store IBlockCacheNode (s) ?
// TODO: Consider switching back from default to private visibility (use getters for other places).
@ -250,6 +251,36 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition
return new double[] {minX, minY, minZ, maxX, maxY, maxZ};
}
@Override
public double getMinX() {
return minX;
}
@Override
public double getMinZ() {
return minZ;
}
@Override
public double getMaxX() {
return maxX;
}
@Override
public double getMaxZ() {
return maxZ;
}
@Override
public double getMinY() {
return minY;
}
@Override
public double getMaxY() {
return maxY;
}
/**
* Get the bounding box margin from the center (x ,z) to the edge of the
* box. This value may be adapted from entity width or other input, and it

View File

@ -34,7 +34,8 @@ public class RichEntityLocation extends RichBoundsLocation {
/*
* TODO: HumanEntity default with + height (1.11.2): elytra 0.6/0.6,
* sleeping 0.2/0.2, sneaking 0.6/1.65, normal 0.6/1.8
* sleeping 0.2/0.2, sneaking 0.6/1.65, normal 0.6/1.8 - head height is 0.4
* with elytra, 0.2 with sleeping, height - 0.08 otherwise.
*/
/** The mc access. */
@ -397,6 +398,7 @@ public class RichEntityLocation extends RichBoundsLocation {
this.isLiving = isLiving;
final MCAccess mcAccess = this.mcAccess.getHandle();
this.width = mcAccess.getWidth(entity);
this.eyeHeight = eyeHeight;
this.height = mcAccess.getHeight(entity);
standsOnEntity = false;
super.set(location, fullWidth, fullHeight, yOnGround);