From 9a6a370f1d3aa38e9c2c9bb2b21e48a875e7f7f5 Mon Sep 17 00:00:00 2001 From: asofold Date: Wed, 7 Dec 2016 21:42:21 +0100 Subject: [PATCH] [BLEEDING] Adjustments + more slime+piston support (incomplete). There are typical cases to cover: * Extra falling height. * Fall damage where a slime block had been. Thus adding a specialized method for bounce (just foot position) instead of using the full bounds would be better, preferably just check within the MovingListener (set bounce / adjust velocity there). --- .../checks/moving/player/SurvivalFly.java | 59 +++++++++++------- .../changetracker/BlockChangeReference.java | 36 ++++++----- .../changetracker/BlockChangeTracker.java | 32 +++++++--- .../nocheatplus/utilities/CheckUtils.java | 17 ++++++ .../location/RichBoundsLocation.java | 61 ++++++++++++++----- 5 files changed, 143 insertions(+), 62 deletions(-) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java index 88d76684..820d6611 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/SurvivalFly.java @@ -39,6 +39,7 @@ import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope; import fr.neatmonster.nocheatplus.checks.moving.model.LocationData; import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData; import fr.neatmonster.nocheatplus.checks.moving.util.AuxMoving; +import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry; import fr.neatmonster.nocheatplus.checks.workaround.WRPT; import fr.neatmonster.nocheatplus.compat.Bridge1_9; import fr.neatmonster.nocheatplus.compat.BridgeEnchant; @@ -431,10 +432,12 @@ public class SurvivalFly extends Check { } // Post-check recovery. - if (useBlockChangeTracker && vDistanceAboveLimit > 0.0 && Math.abs(yDistance) <= 1.015) { + if (useBlockChangeTracker && vDistanceAboveLimit > 0.0 + // Skip for now: && Math.abs(yDistance) <= 1.55 + ) { // TODO: Better place for checking for moved blocks [redesign for intermediate result objects?]. // Vertical push/pull. - double[] blockMoveResult = getVerticalBlockMoveResult(yDistance, from, to, data); + double[] blockMoveResult = getVerticalBlockMoveResult(yDistance, from, to, tick, data); if (blockMoveResult != null) { vAllowedDistance = blockMoveResult[0]; vDistanceAboveLimit = blockMoveResult[1]; @@ -659,31 +662,45 @@ public class SurvivalFly extends Check { * @param data * @return */ - private double[] getVerticalBlockMoveResult(final double yDistance, final PlayerLocation from, final PlayerLocation to, final MovingData data) { - /* - * TODO: Once horizontal push is allowed too, a maxIdEntry has to be - * passed as argument and data.updateBlockChangeReference has to be - * called after processing all pushing. Return the new maxEntry if - * updated, or the old one. - */ + private double[] getVerticalBlockMoveResult(final double yDistance, + final PlayerLocation from, final PlayerLocation to, + final int tick, final MovingData data) { // TODO: Allow push up to 1.0 (or 0.65 something) even beyond block borders, IF COVERED [adapt PlayerLocation]. - // TODO: Might have to allow pushing up to a distance of 1.0 if covered. - // TODO: Cleanup todo. + // TODO: Other conditions/filters ... ? // Push (/pull) up. - if (yDistance > 0.0 && (yDistance <= 1.0 - // Extra condition for full blocks: slightly more possible. - // Extreme case: 1.51 blocks up (details pending). - || yDistance <= 1.015 && to.getY() - to.getBlockY() < 0.015)) { - // TODO: Other conditions? [some will be in passable later]. - if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, Math.min(yDistance, 1.0))) { - tags.add("blkmv_y_pos"); - final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ? - return new double[]{maxDistYPos, 0.0}; + if (yDistance > 0.0) { + if ((yDistance <= 1.0 + // Extra condition for full blocks: slightly more possible. + // Extreme case: 1.51 blocks up (details pending). + || yDistance <= 1.015 && to.getY() - to.getBlockY() < 0.015)) { + if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, + Math.min(yDistance, 1.0))) { + tags.add("blkmv_y_pos"); + final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ? + return new double[]{maxDistYPos, 0.0}; + } + } + // (No else.) + if (yDistance <= 1.55) { + // TODO: Edges ca. 0.5 (or 2x 0.5). + // TODO: Center ca. 1.5. With falling height, values increase slightly. + // Simplified: Always allow 1.5 or less with being pushed up by slime. + // TODO: + if (from.matchBlockChangeMatchResultingFlags( + blockChangeTracker, data.blockChangeRef, Direction.Y_POS, + Math.min(yDistance, 0.42), // Special limit. + BlockProperties.F_BOUNCE25)) { + tags.add("blkmv_y_pos_bounce"); + final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ? + // TODO: Set bounce effect or something !? + // TODO: Bounce effect instead ? + data.addVerticalVelocity(new SimpleEntry(tick, Math.max(0.515, yDistance - 0.5), 2)); + return new double[]{maxDistYPos, 0.0}; + } } } // Push (/pull) down. else if (yDistance < 0.0 && yDistance >= -1.0) { - // TODO: Other conditions? [some will be in passable later]. if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_NEG, -yDistance)) { tags.add("blkmv_y_neg"); final double maxDistYNeg = yDistance; // from.getY() - from.getBlockY(); // TODO: Margin ? diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeReference.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeReference.java index 89f08107..df9d8c40 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeReference.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeReference.java @@ -5,14 +5,16 @@ import fr.neatmonster.nocheatplus.utilities.location.RichBoundsLocation; /** * Simple class for helping with query functionality. Reference a - * BlockChangeEntry and contain more information, such as validity for - * further use/effects. This is meant for storing the state of last-consumed - * block change entries for a context within some data. + * BlockChangeEntry and contain more information, such as validity for further + * use/effects. This is meant for storing the state of last-consumed block + * change entries for a context within some data. * * @author asofold * */ public class BlockChangeReference { + // TODO: IBlockChangeReference ? + /* * TODO: public BlockChangeEntry firstUsedEntry = null; // Would the * span suffice? Consider using span + timing or just the span during @@ -34,25 +36,28 @@ public class BlockChangeReference { /** * Indicate if the timing of the last entry is still regarded as valid. */ - /* - * TODO: Subject to change, switching to tick rather than id (ids can be - * inverted, thus lock out paths). - */ public boolean valid = false; /** - * Check if this reference can be updated with the given entry, - * considering set validity information. By default, the given tick - * either must be greater than the stored one, or the tick are the same - * and valid is set to true. The internal state is not changed by - * calling this. + * Check if this reference can be updated with the given entry, considering + * set validity information. By default, the given tick either must be + * greater than the stored one, or the tick are the same and valid is set to + * true. The internal state is not changed by calling this. * * @param entry * @return */ public boolean canUpdateWith(final BlockChangeEntry entry) { - // Love access methods: return this.lastUsedEntry == null || entry.id > this.lastUsedEntry.id || entry.id == this.lastUsedEntry.id && valid; - // TODO: There'll be a span perhaps. + // Formerly: return this.lastUsedEntry == null || entry.id > this.lastUsedEntry.id || entry.id == this.lastUsedEntry.id && valid; + // TODO: Consider: Allow the same tick if id is higher. (order of ids doesn't really help too much though) + // TODO: Consider: A tick-tolerance value [needs storing the tick of setting/altering]. + // TODO: Consider keeping a map/set of used entries + allow reuse depending on context. + /* + * TODO: Alternative context def.: Hard invalidation via lastUsedEntry + * and soft invalidation via TBA span. E.g. hard for on ground and + * passable, soft for push/pull. + */ + // TODO: There'll be a span of validity, perhaps. /* * Using ticks seems more appropriate, as ids are not necessarily * ordered in a relevant way, if they reference the same tick. Even @@ -98,7 +103,8 @@ public class BlockChangeReference { */ if (lastSpanEntry != null && (lastUsedEntry == null || lastSpanEntry.id > lastUsedEntry.id)) { lastUsedEntry = lastSpanEntry; - if (to != null && to.isBlockIntersecting(lastSpanEntry.x, lastSpanEntry.y, lastSpanEntry.z)) { + if (to != null && to.isBlockIntersecting( + lastSpanEntry.x, lastSpanEntry.y, lastSpanEntry.z, lastSpanEntry.direction.blockFace)) { valid = true; } else { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeTracker.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeTracker.java index 993528ef..8a9ee0e0 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeTracker.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/changetracker/BlockChangeTracker.java @@ -35,6 +35,7 @@ import fr.neatmonster.nocheatplus.NCPAPIProvider; import fr.neatmonster.nocheatplus.components.location.IGetPosition; import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; import fr.neatmonster.nocheatplus.logging.Streams; +import fr.neatmonster.nocheatplus.utilities.CheckUtils; import fr.neatmonster.nocheatplus.utilities.TickTask; import fr.neatmonster.nocheatplus.utilities.ds.map.CoordHashMap; import fr.neatmonster.nocheatplus.utilities.ds.map.CoordMap; @@ -63,13 +64,13 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockProperties; public class BlockChangeTracker { public static enum Direction { - NONE, - X_POS, - X_NEG, - Y_POS, - Y_NEG, - Z_POS, - Z_NEG; + NONE(BlockFace.SELF), + X_POS(CheckUtils.matchBlockFace(1, 0, 0)), + X_NEG(CheckUtils.matchBlockFace(-1, 0, 0)), + Y_POS(CheckUtils.matchBlockFace(0, 1, 0)), + Y_NEG(CheckUtils.matchBlockFace(0, -1, 0)), + Z_POS(CheckUtils.matchBlockFace(0, 0, 1)), + Z_NEG(CheckUtils.matchBlockFace(0, 0, -1)); public static Direction getDirection(final BlockFace blockFace) { final int x = blockFace.getModX(); @@ -96,6 +97,12 @@ public class BlockChangeTracker { return NONE; } + public final BlockFace blockFace; + + private Direction(BlockFace blockFace) { + this.blockFace = blockFace; + } + } /** @@ -332,6 +339,7 @@ public class BlockChangeTracker { // Add this block. addBlockChange(changeId, tick, worldNode, x, y, z, Direction.getDirection(blockFace), blockCache.getOrCreateBlockCacheNode(x, y, z, true)); + //DebugUtil.debug("Piston: " + Direction.getDirection(blockFace) + " " + x + "," + y +"," + z + " / " + blockCache.getTypeId(x, y, z)); // TODO: REMOVE } /** @@ -594,7 +602,7 @@ public class BlockChangeTracker { * @param matchFlags * Only blocks having previous states that have any flags in * common with matchFlags are considered for output. If - * matchFlags is smaller than zero, the parameter is ignored. + * matchFlags is zero, the parameter is ignored. * @return The matching entry, or null if there is no matching entry. */ public BlockChangeEntry getBlockChangeEntryMatchFlags(final BlockChangeReference ref, final int tick, @@ -608,8 +616,12 @@ public class BlockChangeTracker { final LinkedList entries = getValidBlockChangeEntries(tick, worldNode, x, y, z); if (entries != null) { for (final BlockChangeEntry entry : entries) { - if ((ref == null || ref.canUpdateWith(entry) && (direction == null || entry.direction == direction)) - && (matchFlags < 0 || (matchFlags & BlockProperties.getBlockFlags(entry.previousState.getId())) != 0)) { + if ((ref == null + || ref.canUpdateWith(entry) + && (direction == null || entry.direction == direction)) + // + && (matchFlags == 0 + || (matchFlags & BlockProperties.getBlockFlags(entry.previousState.getId())) != 0)) { return entry; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/CheckUtils.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/CheckUtils.java index f7736dfc..df19ca24 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/CheckUtils.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/CheckUtils.java @@ -21,6 +21,7 @@ import java.util.Random; import java.util.Set; import org.bukkit.Bukkit; +import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import fr.neatmonster.nocheatplus.NCPAPIProvider; @@ -268,4 +269,20 @@ public class CheckUtils { return NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Random.class); } + /** + * Find the appropriate BlockFace. + * @param x Exact increments. + * @param y + * @param z + * @return + */ + public static BlockFace matchBlockFace(int x, int y, int z) { + for (BlockFace blockFace : BlockFace.values()) { + if (blockFace.getModX() == x && blockFace.getModY() == y && blockFace.getModZ() == z) { + return blockFace; + } + } + return null; + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java index 56f45875..b7249ec9 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/location/RichBoundsLocation.java @@ -19,6 +19,7 @@ import java.util.UUID; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; import fr.neatmonster.nocheatplus.checks.moving.location.LocUtil; @@ -1118,11 +1119,14 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition /** * Check for tracked block changes, having moved a block into a certain - * direction, using the full bounding box (pistons), only regarding blocks - * having flags in common with matchFlags. BlockChangeReference.updateSpan - * is called with the earliest entry found (updateFinal has to be called - * extra). This is an opportunistic version without any consistency checking - * done, just updating the span by the earliest entry found. + * direction, confined to certain blocks hitting the player, using the full + * bounding box (pistons), only regarding blocks having flags in common with + * matchFlags. Thus not the replaced state at a position is regarded, but + * the state that should result from a block having been pushed there. + * BlockChangeReference.updateSpan is called with the earliest entry found + * (updateFinal has to be called extra). This is an opportunistic version + * without any consistency checking done, just updating the span by the + * earliest entry found. * * @param blockChangeTracker * the block change tracker @@ -1134,25 +1138,28 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition * The (always positive) distance to cover. * @param matchFlags * Only blocks with past states having any flags in common with - * matchFlags. If matchFlags is smaller than zero, the parameter - * is ignored. + * matchFlags. If matchFlags is zero, the parameter is ignored. * @return Returns true, iff an entry was found. */ - public boolean matchBlockChangeMatchFlags(final BlockChangeTracker blockChangeTracker, + public boolean matchBlockChangeMatchResultingFlags(final BlockChangeTracker blockChangeTracker, final BlockChangeReference ref, final Direction direction, final double coverDistance, final long matchFlags) { + // TODO: Remove this method (!). Use a specialized method (here or external) just for bounce. /* * TODO: Not sure with code duplication. Is it better to run * BlockChangeTracker.getBlockChangeMatchFlags for the other method too? */ + // TODO: Intended use is bouncing off slime, thus need confine to foot level ? final int tick = TickTask.getTick(); final UUID worldId = world.getUID(); - final int iMinX = Location.locToBlock(minX); - final int iMaxX = Location.locToBlock(maxX); - final int iMinY = Location.locToBlock(minY); - final int iMaxY = Location.locToBlock(maxY); - final int iMinZ = Location.locToBlock(minZ); - final int iMaxZ = Location.locToBlock(maxZ); + // Shift the entire search box to the opposite direction (if direction is given). + final BlockFace blockFace = direction == null ? BlockFace.SELF : direction.blockFace; + final int iMinX = Location.locToBlock(minX) - blockFace.getModX(); + final int iMaxX = Location.locToBlock(maxX) - blockFace.getModX(); + final int iMinY = Location.locToBlock(minY) - blockFace.getModY(); + final int iMaxY = Location.locToBlock(maxY) - blockFace.getModY(); + final int iMinZ = Location.locToBlock(minZ) - blockFace.getModZ(); + final int iMaxZ = Location.locToBlock(maxZ) - blockFace.getModZ(); BlockChangeEntry minEntry = null; for (int x = iMinX; x <= iMaxX; x++) { for (int z = iMinZ; z <= iMaxZ; z++) { @@ -1161,7 +1168,9 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition ref, tick, worldId, x, y, z, direction, matchFlags); if (entry != null && (minEntry == null || entry.id < minEntry.id)) { // Check vs. coverDistance, exclude cases where the piston can't push that far. - if (coverDistance > 0.0 && coversDistance(x, y, z, direction, coverDistance)) { + if (coverDistance > 0.0 && coversDistance( + x + blockFace.getModX(), y + blockFace.getModY(), z + blockFace.getModZ(), + direction, coverDistance)) { minEntry = entry; } } @@ -1186,7 +1195,7 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition * the y * @param z * the z - * @return true, if is block intersecting + * @return true, if the block is intersecting */ public boolean isBlockIntersecting(final int x, final int y, final int z) { return CollisionUtil.intersectsBlock(minX, maxX, x) @@ -1194,6 +1203,26 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition && CollisionUtil.intersectsBlock(minZ, maxZ, z); } + /** + * Test, if either of two blocks intersects the bounding box, if assuming + * full bounds. + * + * @param x + * the x + * @param y + * the y + * @param z + * the z + * @param blockFace + * An additional block to check from the coordinates into that + * direction. + * @return true, if either block is intersecting + */ + public boolean isBlockIntersecting(final int x, final int y, final int z, final BlockFace blockFace) { + return isBlockIntersecting(x, y, z) + || isBlockIntersecting(x + blockFace.getModX(), y + blockFace.getModY(), z + blockFace.getModZ()); + } + /** * Test if a block fully moved into that direction can move the player by * coverDistance.