From dec6ef1412808e3011fa7618b21455d6a50a7979 Mon Sep 17 00:00:00 2001 From: asofold Date: Fri, 25 Nov 2016 23:06:06 +0100 Subject: [PATCH] Opportunistic passable checking. --- .../checks/moving/MovingListener.java | 4 +- .../checks/moving/player/Passable.java | 17 +++-- .../checks/moving/player/SurvivalFly.java | 18 +++--- .../compat/blocks/BlockChangeTracker.java | 63 +++++++++++++++---- .../utilities/collision/ICollidePassable.java | 25 +++++++- .../collision/PassableAxisTracing.java | 41 +++++++++--- .../collision/PassableRayTracing.java | 10 +++ .../location/RichBoundsLocation.java | 21 +++++-- 8 files changed, 152 insertions(+), 47 deletions(-) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java index 99fc6129..ac6433f7 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java @@ -674,7 +674,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo boolean mightSkipNoFall = false; // If to skip nofall check (mainly on violation of other checks). if (newTo == null && cc.passableCheck && player.getGameMode() != BridgeMisc.GAME_MODE_SPECTATOR && !NCPExemptionManager.isExempted(player, CheckType.MOVING_PASSABLE) && !player.hasPermission(Permissions.MOVING_PASSABLE)) { // Passable is checked first to get the original set-back locations from the other checks, if needed. - newTo = passable.check(player, pFrom, pTo, data, cc); + newTo = passable.check(player, pFrom, pTo, data, cc, tick); if (newTo != null) { // Check if to skip the nofall check. mightSkipNoFall = true; @@ -697,7 +697,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // Actual check. if (newTo == null) { // Only check if passable has not already set back. - newTo = survivalFly.check(player, pFrom, pTo, mightBeMultipleMoves, data, cc, time); + newTo = survivalFly.check(player, pFrom, pTo, mightBeMultipleMoves, data, cc, tick, time); } // Only check NoFall, if not already vetoed. if (checkNf) { diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java index 67a58a9d..de082942 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/player/Passable.java @@ -19,6 +19,7 @@ import java.util.Locale; import org.bukkit.Location; import org.bukkit.entity.Player; +import fr.neatmonster.nocheatplus.NCPAPIProvider; import fr.neatmonster.nocheatplus.actions.ParameterName; import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.CheckType; @@ -26,6 +27,7 @@ import fr.neatmonster.nocheatplus.checks.ViolationData; import fr.neatmonster.nocheatplus.checks.moving.MovingConfig; import fr.neatmonster.nocheatplus.checks.moving.MovingData; import fr.neatmonster.nocheatplus.checks.moving.location.LocUtil; +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker; import fr.neatmonster.nocheatplus.utilities.collision.ICollidePassable; import fr.neatmonster.nocheatplus.utilities.collision.PassableAxisTracing; import fr.neatmonster.nocheatplus.utilities.collision.PassableRayTracing; @@ -60,26 +62,28 @@ public class Passable extends Check { // TODO: Store both and select on check (with config then). private final ICollidePassable rayTracingLegacy = new PassableRayTracing(); private final ICollidePassable rayTracingActual = new PassableAxisTracing(); + private final BlockChangeTracker blockTracker; public Passable() { super(CheckType.MOVING_PASSABLE); // TODO: Configurable maxSteps? rayTracingLegacy.setMaxSteps(60); rayTracingActual.setMaxSteps(60); + blockTracker = NCPAPIProvider.getNoCheatPlusAPI().getBlockChangeTracker(); } public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, - final MovingData data, final MovingConfig cc) { + final MovingData data, final MovingConfig cc, final int tick) { if (rt_legacy) { return checkLegacy(player, from, to, data, cc); } else { - return checkActual(player, from, to, data, cc); + return checkActual(player, from, to, data, cc, tick); } } private Location checkActual(final Player player, final PlayerLocation from, final PlayerLocation to, - final MovingData data, final MovingConfig cc) { + final MovingData data, final MovingConfig cc, final int tick) { // TODO: Distinguish feet vs. box. // Block distances (sum, max) for from-to (not for loc!). @@ -87,7 +91,7 @@ public class Passable extends Check { // General condition check for using ray-tracing. if (cc.passableRayTracingCheck && (!cc.passableRayTracingBlockChangeOnly || manhattan > 0)) { - final String newTag = checkRayTracing(player, from, to, manhattan, data, cc); + final String newTag = checkRayTracing(player, from, to, manhattan, data, cc, tick); if (newTag != null) { // Direct return. return potentialViolation(player, from, to, manhattan, newTag, data, cc); @@ -107,11 +111,14 @@ public class Passable extends Check { } private String checkRayTracing(final Player player, final PlayerLocation from, final PlayerLocation to, - final int manhattan, final MovingData data, final MovingConfig cc) { + final int manhattan, final MovingData data, final MovingConfig cc, final int tick) { String tags = null; setNormalMargins(rayTracingActual, from); rayTracingActual.set(from, to); rayTracingActual.setIgnoreInitiallyColliding(true); + if (cc.trackBlockMove) { // TODO: Extra flag for 'any' block changes. + rayTracingActual.setBlockChangeTracker(blockTracker, data.blockChangeRef, tick, from.getWorld().getUID()); + } //rayTracing.setCutOppositeDirectionMargin(true); rayTracingActual.loop(); rayTracingActual.setIgnoreInitiallyColliding(false); 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 9c3d4124..f952ceb4 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 @@ -43,7 +43,6 @@ import fr.neatmonster.nocheatplus.checks.workaround.WRPT; import fr.neatmonster.nocheatplus.compat.Bridge1_9; import fr.neatmonster.nocheatplus.compat.BridgeEnchant; import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker; -import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeEntry; import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.Direction; import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess; import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; @@ -51,7 +50,6 @@ import fr.neatmonster.nocheatplus.logging.Streams; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.utilities.CheckUtils; import fr.neatmonster.nocheatplus.utilities.StringUtil; -import fr.neatmonster.nocheatplus.utilities.TickTask; import fr.neatmonster.nocheatplus.utilities.ds.count.ActionAccumulator; import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation; import fr.neatmonster.nocheatplus.utilities.location.TrigUtil; @@ -115,7 +113,9 @@ public class SurvivalFly extends Check { * @param isSamePos * @return the location */ - public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final boolean mightBeMultipleMoves, final MovingData data, final MovingConfig cc, final long now) { + public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, + final boolean mightBeMultipleMoves, final MovingData data, final MovingConfig cc, + final int tick, final long now) { tags.clear(); if (data.debug) { justUsedWorkarounds.clear(); @@ -610,7 +610,7 @@ public class SurvivalFly extends Check { // TODO: Pull down tick / timing data (perhaps add an API object for millis + source + tick + sequence count (+ source of sequence count). if (data.debug) { // TODO: Only update, if velocity is queued at all. - data.getVerticalVelocityTracker().updateBlockedState(TickTask.getTick(), + data.getVerticalVelocityTracker().updateBlockedState(tick, // Assume blocked with being in web/water, despite not entirely correct. thisMove.headObstructed || thisMove.from.resetCond, // (Similar here.) @@ -655,9 +655,8 @@ public class SurvivalFly extends Check { // 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]. - final BlockChangeEntry entryYPos = from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, Math.min(yDistance, 1.0)); - if (entryYPos != null) { - data.blockChangeRef.updateFinal(entryYPos, to); + if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_POS, Math.min(yDistance, 1.0))) { + data.blockChangeRef.updateFinal(to); // TODO: -> MovingListener (!). tags.add("blkmv_y_pos"); final double maxDistYPos = yDistance; //1.0 - (from.getY() - from.getBlockY()); // TODO: Margin ? return new double[]{maxDistYPos, 0.0}; @@ -666,9 +665,8 @@ public class SurvivalFly extends Check { // Push (/pull) down. else if (yDistance < 0.0 && yDistance >= -1.0) { // TODO: Other conditions? [some will be in passable later]. - final BlockChangeEntry entryYNeg = from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_NEG, -yDistance); - if (entryYNeg != null) { - data.blockChangeRef.updateFinal(entryYNeg, to); + if (from.matchBlockChange(blockChangeTracker, data.blockChangeRef, Direction.Y_NEG, -yDistance)) { + data.blockChangeRef.updateFinal(to); // TODO: -> MovingListener (!). tags.add("blkmv_y_neg"); final double maxDistYNeg = yDistance; // from.getY() - from.getBlockY(); // TODO: Margin ? return new double[]{maxDistYNeg, 0.0}; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/BlockChangeTracker.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/BlockChangeTracker.java index 4d8e4e82..2cd1ffde 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/BlockChangeTracker.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/compat/blocks/BlockChangeTracker.java @@ -212,7 +212,15 @@ public class BlockChangeTracker { * one check covering multiple blocks. */ - /** Last used block change entry, highest id.*/ + /** First used (oldest) entry during checking. */ + public BlockChangeEntry firstSpanEntry = null; + /** Last used (newest) entry during checking. */ + public BlockChangeEntry lastSpanEntry = null; + + /** + * Last used block change entry, set after a complete iteration of + * checking, update with updateFinal. + */ public BlockChangeEntry lastUsedEntry = null; /** @@ -246,26 +254,43 @@ public class BlockChangeTracker { } /** - * Update lastUsedEntry by the given entry, assuming to to be the + * Update the span during checking. Ensure to test canUpdateWith(entry) + * before calling this. + * + * @param entry + */ + public void updateSpan(final BlockChangeEntry entry) { + if (firstSpanEntry == null || entry.id < firstSpanEntry.id) { + firstSpanEntry = entry; + } + if (lastSpanEntry == null || entry.id > lastSpanEntry.id) { + lastSpanEntry = entry; + } + } + + /** + * Update lastUsedEntry by the set span, assuming to to be the * move end-point to continue from next time. This is meant to finalize * prepared changes/span for use with the next move. * - * @param entry * @param to * If not null, allows keeping the latest entry valid, if * intersecting with the bounding box of to. */ - public void updateFinal(final BlockChangeEntry entry, final RichBoundsLocation to) { - // TODO: updateBlockChangeReference ... Span(entry, to !?)|Final() - if (lastUsedEntry == null || lastUsedEntry.id < entry.id) { // Here use the id for fastest comparison. - lastUsedEntry = entry; // Unchecked. - if (to != null && to.isBlockIntersecting(entry.x, entry.y, entry.z)) { + public void updateFinal(final RichBoundsLocation to) { + if (firstSpanEntry == null) { + return; + } + if (lastSpanEntry != null && (lastUsedEntry == null || lastSpanEntry.id > lastUsedEntry.id)) { + lastUsedEntry = lastSpanEntry; + if (to != null && to.isBlockIntersecting(lastSpanEntry.x, lastSpanEntry.y, lastSpanEntry.z)) { valid = true; } else { valid = false; } } + firstSpanEntry = lastSpanEntry = null; } /** @@ -275,18 +300,32 @@ public class BlockChangeTracker { */ public BlockChangeReference copy() { final BlockChangeReference copy = new BlockChangeReference(); + copy.firstSpanEntry = this.firstSpanEntry; + copy.lastSpanEntry = this.lastSpanEntry; copy.lastUsedEntry = this.lastUsedEntry; copy.valid = this.valid; return copy; } + public void clear() { + firstSpanEntry = lastSpanEntry = lastUsedEntry = null; + valid = false; + } + @Override public boolean equals(final Object obj) { if (obj == null || !(obj instanceof BlockChangeReference)) { return false; } final BlockChangeReference other = (BlockChangeReference) obj; - return valid == other.valid && (lastUsedEntry != null && lastUsedEntry.equals(other.lastUsedEntry) || lastUsedEntry == null && other.lastUsedEntry == null); + return valid == other.valid && + (lastUsedEntry != null && lastUsedEntry.equals(other.lastUsedEntry) + || lastUsedEntry == null && other.lastUsedEntry == null) + && (firstSpanEntry != null && firstSpanEntry.equals(other.firstSpanEntry) + || firstSpanEntry == null && other.firstSpanEntry == null) + && (lastSpanEntry != null && lastSpanEntry.equals(other.lastSpanEntry) + || lastSpanEntry == null && other.lastSpanEntry == null) + ; } } @@ -576,7 +615,7 @@ public class BlockChangeTracker { * @param ref * Reference for checking the validity of BlockChangeEntry * instances. No changes are made to the passed instance, - * canUpdateWith is called. + * canUpdateWith is called. Pass null to skip further validation. * @param tick * The current tick. Used for lazy expiration. * @param worldId @@ -604,7 +643,7 @@ public class BlockChangeTracker { * @param ref * Reference for checking the validity of BlockChangeEntry * instances. No changes are made to the passed instance, - * canUpdateWith is called. + * canUpdateWith is called. Pass null to skip further validation. * @param tick * The current tick. Used for lazy expiration. * @param worldNode @@ -643,7 +682,7 @@ public class BlockChangeTracker { it.remove(); } else { - if (ref.canUpdateWith(entry) && (direction == null || entry.direction == direction)) { + if (ref == null || ref.canUpdateWith(entry) && (direction == null || entry.direction == direction)) { return entry; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/ICollidePassable.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/ICollidePassable.java index be35a85b..8c6c11b5 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/ICollidePassable.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/ICollidePassable.java @@ -14,6 +14,10 @@ */ package fr.neatmonster.nocheatplus.utilities.collision; +import java.util.UUID; + +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker; +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference; import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation; import fr.neatmonster.nocheatplus.utilities.map.BlockCache; @@ -24,11 +28,26 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockCache; * */ public interface ICollidePassable extends ICollideBlocks, ISetMargins { + // TODO: Add a super interface (+ asbtract impl.) for BlockCache based stuff including BlockChangeTracker. public void setBlockCache(BlockCache blockCache); public BlockCache getBlockCache(); - // TODO: public void (bool?) setBlockChangeReference(tracker, reference). + /** + * Allows testing for past states. Should be reset in cleanup. Will do + * nothing, if not supported. + *
+ * Method signature subject to change (tick). + * + * @param blockChangeTracker + * @param blockChangeReference + * @param tick + * The tick right now (timing info). + * @param worldId + * the UUID of the world this takes place in. + */ + public void setBlockChangeTracker(BlockChangeTracker blockChangeTracker, + BlockChangeReference blockChangeReference, int tick, UUID worldId); /** * Convenience: Call set and setBlockCache with the data from the @@ -52,8 +71,8 @@ public interface ICollidePassable extends ICollideBlocks, ISetMargins { public boolean mightNeedSplitAxisHandling(); /** - * Remove reference to objects passed from outside (BlockCache, but not - * calling their cleanup methods). + * Remove reference to objects passed from outside (BlockCache, + * BlockChangeTracker and similar, but not calling their cleanup methods). */ public void cleanup(); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableAxisTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableAxisTracing.java index 06df5ded..efc1ff1a 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableAxisTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableAxisTracing.java @@ -14,6 +14,11 @@ */ package fr.neatmonster.nocheatplus.utilities.collision; +import java.util.UUID; + +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker; +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeEntry; +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference; import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation; import fr.neatmonster.nocheatplus.utilities.map.BlockCache; import fr.neatmonster.nocheatplus.utilities.map.BlockProperties; @@ -21,6 +26,10 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockProperties; public class PassableAxisTracing extends AxisTracing implements ICollidePassable { private BlockCache blockCache; + private BlockChangeTracker blockChangeTracker = null; + private BlockChangeReference blockChangeRef = null; + private int tick; + private UUID worldId; // TODO: Might need another option for margins (option to skip margin for the axis-start point, or alter ignoreFirst behavior). // TODO: Consider an iteration margin as well (0.5 below for fences). @@ -33,6 +42,15 @@ public class PassableAxisTracing extends AxisTracing implements ICollidePassable this.blockCache = blockCache; } + @Override + public void setBlockChangeTracker(BlockChangeTracker blockChangeTracker, + BlockChangeReference blockChangeReference, int tick, UUID worldId) { + this.blockChangeTracker = blockChangeTracker; + this.blockChangeRef = blockChangeReference; + this.tick = tick; + this.worldId = worldId; + } + @Override protected void collectInitiallyCollidingBlocks(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, BlockPositionContainer results) { @@ -56,16 +74,18 @@ public class PassableAxisTracing extends AxisTracing implements ICollidePassable */ return true; } - // TODO: if (blockChangeTracker != null && -- check with BlockChangeTracker and BlockChangeReference -- - else { - collides = true; - return false; + // Recovery attempt via the BlockChangeTracker. + if (blockChangeTracker != null) { + // Opportunistic (FCFS, no consistency). + final BlockChangeEntry entry = blockChangeTracker.getBlockChangeEntry(blockChangeRef, tick, worldId, blockX, blockY, blockZ, null); + if (entry != null) { + blockChangeRef.updateSpan(entry); + return true; + } } - } - - @Override - public void set(double x0, double y0, double z0, double x1, double y1, double z1) { - super.set(x0, y0, z0, x1, y1, z1); + // No condition for passing through found. + collides = true; + return false; } @Override @@ -82,6 +102,9 @@ public class PassableAxisTracing extends AxisTracing implements ICollidePassable @Override public void cleanup() { blockCache = null; + blockChangeTracker = null; + blockChangeRef = null; + worldId = null; } } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableRayTracing.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableRayTracing.java index 434622ee..1775397e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableRayTracing.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/collision/PassableRayTracing.java @@ -14,6 +14,10 @@ */ package fr.neatmonster.nocheatplus.utilities.collision; +import java.util.UUID; + +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker; +import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference; import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation; import fr.neatmonster.nocheatplus.utilities.map.BlockCache; import fr.neatmonster.nocheatplus.utilities.map.BlockProperties; @@ -32,6 +36,12 @@ public class PassableRayTracing extends RayTracing implements ICollidePassable { this.blockCache = blockCache; } + @Override + public void setBlockChangeTracker(BlockChangeTracker blockChangeTracker, + BlockChangeReference blockChangeReference, int tick, UUID worldId) { + // (Not supported.) + } + @Override public void set(final PlayerLocation from, final PlayerLocation to){ set(from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); 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 4694ca88..ee7e7df3 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 @@ -1013,8 +1013,11 @@ 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). The given - * BlockChangeReference is not changed, it has to be updated externally. + * direction, using the full bounding box (pistons). + * 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 @@ -1024,10 +1027,10 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition * the direction * @param coverDistance * The (always positive) distance to cover. - * @return A matching BlockChangeEntry with the minimal id. If no entry was - * found, null is returned. + * @return Returns true, iff an entry was found. */ - public BlockChangeEntry matchBlockChange(final BlockChangeTracker blockChangeTracker, final BlockChangeReference ref, final Direction direction, final double coverDistance) { + public boolean matchBlockChange(final BlockChangeTracker blockChangeTracker, final BlockChangeReference ref, final Direction direction, final double coverDistance) { + // TODO: update span instead ! final int tick = TickTask.getTick(); final UUID worldId = world.getUID(); final int iMinX = Location.locToBlock(minX); @@ -1050,7 +1053,13 @@ public class RichBoundsLocation implements IGetBukkitLocation, IGetBlockPosition } } } - return minEntry; + if (minEntry == null) { + return false; + } + else { + ref.updateSpan(minEntry); + return true; + } } /**