From 93e56cf45a88822ff9d7331c582e46905e14133a Mon Sep 17 00:00:00 2001 From: Brian Baker Date: Sun, 20 Aug 2023 11:49:25 -0500 Subject: [PATCH] Fix block update resets for optimized paths On a block update, the current path gets reset if the block updated is near one of the path nodes. On optimized paths, the distance between path nodes may be significant. Checking only the path nodes themselves may mean that a block update along the line between two path nodes gets ignored when it could impact the path. Consider a bot that farms pumpkins - after breaking many blocks and picking them up, it could try to navigate to the next part of the farm to harvest - across flat open land, this could easily be a straight line that optimizes nicely. But, a pumpkin could suddenly grow and block the bot. Currently, if the pumpkin didn't grow right at the beginning or end of that path segment, it would be ignored, and the bot may get stuck on the newly grown pumpkin. Improve isPositionNearPath to consider the space between path nodes by comparing the given position to the closest point along the path segment instead of just the current node. Two optimizations keep this fast: 1) If the segment is short enough (x / y / z deltas are 2 or under) then we fall back to the previous logic where we just compare the block update position to the current node - this looks like an unoptimized path, so the old fast logic suffices. 2) If the segment looks optimized, we first check pos against the AABB enclosing the path segment. If it isn't near the AABB, then it isn't near the segment. --- index.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 9f21c05..b38bd30 100644 --- a/index.js +++ b/index.js @@ -235,15 +235,70 @@ function inject (bot) { } function isPositionNearPath (pos, path) { + let prevNode = null for (const node of path) { - const dx = Math.abs(node.x - pos.x - 0.5) - const dy = Math.abs(node.y - pos.y - 0.5) - const dz = Math.abs(node.z - pos.z - 0.5) + let comparisonPoint = null + if ( + prevNode === null || + ( + Math.abs(prevNode.x - node.x) <= 2 && + Math.abs(prevNode.y - node.y) <= 2 && + Math.abs(prevNode.z - node.z) <= 2 + ) + ) { + // Unoptimized path, or close enough to last point + // to just check against the current point + comparisonPoint = node + } else { + // Optimized path - the points are far enough apart + // that we need to check the space between them too + + // First, a quick check - if point it outside the path + // segment's AABB, then it isn't near. + const minBound = prevNode.min(node) + const maxBound = prevNode.max(node) + if ( + pos.x - 0.5 < minBound.x - 1 || + pos.x - 0.5 > maxBound.x + 1 || + pos.y - 0.5 < minBound.y - 2 || + pos.y - 0.5 > maxBound.y + 2 || + pos.z - 0.5 < minBound.z - 1 || + pos.z - 0.5 > maxBound.z + 1 + ) { + continue + } + + comparisonPoint = closestPointOnLineSegment(pos, prevNode, node) + } + + const dx = Math.abs(comparisonPoint.x - pos.x - 0.5) + const dy = Math.abs(comparisonPoint.y - pos.y - 0.5) + const dz = Math.abs(comparisonPoint.z - pos.z - 0.5) if (dx <= 1 && dy <= 2 && dz <= 1) return true + + prevNode = node } + return false } + function closestPointOnLineSegment (point, segmentStart, segmentEnd) { + const segmentLength = segmentEnd.minus(segmentStart).norm() + + if (segmentLength === 0) { + return segmentStart + } + + // t is like an interpolation from segmentStart to segmentEnd + // for the closest point on the line + let t = (point.minus(segmentStart)).dot(segmentEnd.minus(segmentStart)) / segmentLength + + // bound t to be on the segment + t = Math.max(0, Math.min(1, t)) + + return segmentStart.plus(segmentEnd.minus(segmentStart).scaled(t)) + } + // Return the average x/z position of the highest standing positions // in the block. function getPositionOnTopOf (block) {