[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).
This commit is contained in:
asofold 2016-12-07 21:42:21 +01:00
parent 06c2cadf7f
commit 9a6a370f1d
5 changed files with 143 additions and 62 deletions

View File

@ -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 ?

View File

@ -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 {

View File

@ -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<BlockChangeEntry> 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;
}
}

View File

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

View File

@ -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.