From 59d25b70668602a1ab8e239dfdad6afe8fb946c6 Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Wed, 17 Jun 2026 16:14:27 -0300 Subject: [PATCH] fix(world): cap block-search radius/count to avoid event-loop stalls bot.findBlocks is synchronous and scans a volume that grows with the cube of maxDistance. With the default count (10000) a search for a rare or absent block never early-exits, so a large radius walks a huge volume on the main thread and blocks the Node event loop long enough for mineflayer to miss keep-alive packets -- the server then drops the bot mid-task. !searchForBlock exposes a radius up to 512 to the model, so this is reachable in normal play. Cap the search radius at 128 (a search beyond the loaded view distance returns nothing useful anyway) and the match count at 4000 in getNearestBlocksWhere, and lower !searchForBlock's max range to 128 to match. A single search can no longer stall the connection. --- src/agent/commands/actions.js | 2 +- src/agent/library/world.js | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/agent/commands/actions.js b/src/agent/commands/actions.js index 07a0c5d76..4eb72ba4f 100644 --- a/src/agent/commands/actions.js +++ b/src/agent/commands/actions.js @@ -128,7 +128,7 @@ export const actionsList = [ description: 'Find and go to the nearest block of a given type in a given range.', params: { 'type': { type: 'BlockName', description: 'The block type to go to.' }, - 'search_range': { type: 'float', description: 'The range to search for the block. Minimum 32.', domain: [10, 512] } + 'search_range': { type: 'float', description: 'The range to search for the block. Minimum 32, maximum 128.', domain: [10, 128, '[]'] } }, perform: runAsAction(async (agent, block_type, range) => { if (range < 32) { diff --git a/src/agent/library/world.js b/src/agent/library/world.js index 132ff5bfa..1f7d0c122 100644 --- a/src/agent/library/world.js +++ b/src/agent/library/world.js @@ -141,17 +141,28 @@ export function getNearestBlocks(bot, block_types=null, distance=8, count=10000) return getNearestBlocksWhere(bot, block_ids, distance, count); } +// bot.findBlocks is synchronous and scans a volume that grows with the cube of maxDistance. With the +// default count (10000) a search for a rare or absent block never early-exits, so a large radius walks +// a huge volume on the main thread and blocks the event loop -- long enough that mineflayer misses +// keep-alive packets and the server drops the bot mid-task. !searchForBlock lets the model pass a +// radius up to 512, so this is reachable in normal play. Cap the radius (a search beyond the loaded +// view distance returns nothing useful anyway) and the count so one search can't stall the connection. +const MAX_SEARCH_DISTANCE = 128; +const MAX_SEARCH_COUNT = 4000; + export function getNearestBlocksWhere(bot, predicate, distance=8, count=10000) { /** * Get a list of the nearest blocks that satisfy the given predicate. * @param {Bot} bot - The bot to get the nearest blocks for. * @param {function} predicate - The predicate to filter the blocks. - * @param {number} distance - The maximum distance to search, default 16. - * @param {number} count - The maximum number of blocks to find, default 10000. + * @param {number} distance - The maximum distance to search, default 16 (capped at 128). + * @param {number} count - The maximum number of blocks to find, default 10000 (capped at 4000). * @returns {Block[]} - The nearest blocks that satisfy the given predicate. * @example * let waterBlocks = world.getNearestBlocksWhere(bot, block => block.name === 'water', 16, 10); **/ + distance = Math.min(distance, MAX_SEARCH_DISTANCE); + count = Math.min(count, MAX_SEARCH_COUNT); let positions = bot.findBlocks({matching: predicate, maxDistance: distance, count: count}); let blocks = positions.map(position => bot.blockAt(position)); return blocks;