From 647c67df6a6daa30b83e80753c849ccda8e97e76 Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Thu, 5 Feb 2026 23:13:34 -0300 Subject: [PATCH 1/4] feat: add auto_sleep and fall_protection modes Add two new survival modes: - fall_protection: Uses water bucket (MLG water) when falling from height >10 blocks. Checks for ground proximity before activating. High priority mode that interrupts all actions. - auto_sleep: Automatically finds and sleeps in nearby beds when night falls (13000-23000 ticks). Checks every 30 seconds to avoid spamming. Uses the existing goToBed skill which handles all bed types. Handles occupied bed errors gracefully. Both modes use English narration and follow the existing mode patterns (say/execute helpers, proper active/interrupts flags). --- src/agent/modes.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/agent/modes.js b/src/agent/modes.js index fd7993e3c..9bb358f1e 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -87,6 +87,58 @@ const modes_list = [ } } }, + { + name: 'fall_protection', + description: 'Use water bucket to break a long fall (MLG water). Requires a water bucket in inventory.', + interrupts: ['all'], + on: true, + active: false, + fallStartY: null, + update: async function (agent) { + const bot = agent.bot; + const pos = bot.entity.position; + const vel = bot.entity.velocity; + + // Detect if falling (negative Y velocity) + if (vel.y < -0.5) { + if (this.fallStartY === null) { + this.fallStartY = pos.y; + } + + const fallDistance = this.fallStartY - pos.y; + + // If falling more than 10 blocks and accelerating, try MLG water + if (fallDistance > 10 && vel.y < -0.8) { + const waterBucket = bot.inventory.items().find(item => item.name === 'water_bucket'); + if (waterBucket && !this.active) { + // Check if ground is near (within 4 blocks below) + const groundBlock = bot.blockAt(pos.offset(0, -4, 0)); + if (groundBlock && groundBlock.name !== 'air' && groundBlock.name !== 'water') { + execute(this, agent, async () => { + say(agent, 'MLG water!'); + try { + await bot.equip(waterBucket, 'hand'); + await bot.lookAt(pos.offset(0, -3, 0)); + bot.activateItem(); + await new Promise(r => setTimeout(r, 500)); + // Pick up water after landing + const waterBlock = world.getNearestBlock(bot, 'water', 3); + if (waterBlock) { + await bot.lookAt(waterBlock.position); + bot.activateItem(); + } + } catch (e) { + console.log('[FALL_PROTECTION] Error:', e.message); + } + }); + } + } + } + } else { + this.fallStartY = null; + } + } + }, { name: 'unstuck', description: 'Attempt to get unstuck when in the same place for a while. Interrupts some actions.', @@ -169,6 +221,45 @@ const modes_list = [ } } }, + { + name: 'auto_sleep', + description: 'Automatically sleep in a nearby bed when night falls to skip the night and avoid monsters.', + interrupts: ['action:followPlayer'], + on: true, + active: false, + lastSleepCheck: 0, + update: async function (agent) { + const bot = agent.bot; + + // Only check every 30 seconds to avoid spamming + if (Date.now() - this.lastSleepCheck < 30000) return; + this.lastSleepCheck = Date.now(); + + // Check if it's nighttime (13000-23000 ticks) and we're not already sleeping + const time = bot.time.timeOfDay; + const isNight = time >= 13000 && time <= 23000; + if (!isNight || bot.isSleeping) return; + + // Look for a bed within 32 blocks using block name matching (beds are named like 'white_bed', 'red_bed', etc.) + const beds = bot.findBlocks({ + matching: (block) => block.name.includes('bed'), + maxDistance: 32, + count: 1 + }); + if (beds.length > 0) { + execute(this, agent, async () => { + say(agent, 'It\'s getting dark, I should sleep.'); + try { + await skills.goToBed(bot); + } catch (e) { + if (e.message && e.message.includes('occupied')) { + say(agent, 'The bed is occupied.'); + } + } + }); + } + } + }, { name: 'hunting', description: 'Hunt nearby animals when idle.', From ee8f69c561a92db55018546bea6771dab390e020 Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Thu, 5 Feb 2026 23:32:12 -0300 Subject: [PATCH 2/4] fix: address Copilot review feedback on fall_protection and auto_sleep - Unify velocity threshold to -0.5 for consistent fall detection - Capture current position inside execute callback to avoid stale coords - Poll bot.entity.onGround instead of fixed 500ms wait for landing - Fix night time range: use >= 13000 (wraps from 23999 to 0) - Add null guard on error object in auto_sleep catch - Log non-occupied sleep errors instead of swallowing them silently --- src/agent/modes.js | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/agent/modes.js b/src/agent/modes.js index 9bb358f1e..cad58cba7 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -107,20 +107,34 @@ const modes_list = [ const fallDistance = this.fallStartY - pos.y; - // If falling more than 10 blocks and accelerating, try MLG water - if (fallDistance > 10 && vel.y < -0.8) { + // If falling more than 10 blocks and still accelerating, try MLG water + if (fallDistance > 10 && vel.y < -0.5) { const waterBucket = bot.inventory.items().find(item => item.name === 'water_bucket'); if (waterBucket && !this.active) { // Check if ground is near (within 4 blocks below) const groundBlock = bot.blockAt(pos.offset(0, -4, 0)); if (groundBlock && groundBlock.name !== 'air' && groundBlock.name !== 'water') { execute(this, agent, async () => { + const currentPos = bot.entity.position.clone(); say(agent, 'MLG water!'); try { await bot.equip(waterBucket, 'hand'); - await bot.lookAt(pos.offset(0, -3, 0)); + await bot.lookAt(currentPos.offset(0, -3, 0)); bot.activateItem(); - await new Promise(r => setTimeout(r, 500)); + // Wait until the bot has actually landed + await new Promise(resolve => { + const start = Date.now(); + const checkLanding = () => { + if (bot.entity.onGround || bot.entity.velocity.y >= 0) { + return resolve(); + } + if (Date.now() - start > 2000) { + return resolve(); + } + setTimeout(checkLanding, 50); + }; + checkLanding(); + }); // Pick up water after landing const waterBlock = world.getNearestBlock(bot, 'water', 3); if (waterBlock) { @@ -235,9 +249,9 @@ const modes_list = [ if (Date.now() - this.lastSleepCheck < 30000) return; this.lastSleepCheck = Date.now(); - // Check if it's nighttime (13000-23000 ticks) and we're not already sleeping + // Check if it's nighttime (time >= 13000 ticks) and we're not already sleeping const time = bot.time.timeOfDay; - const isNight = time >= 13000 && time <= 23000; + const isNight = time >= 13000; if (!isNight || bot.isSleeping) return; // Look for a bed within 32 blocks using block name matching (beds are named like 'white_bed', 'red_bed', etc.) @@ -252,8 +266,12 @@ const modes_list = [ try { await skills.goToBed(bot); } catch (e) { - if (e.message && e.message.includes('occupied')) { + if (e && e.message && e.message.includes('occupied')) { say(agent, 'The bed is occupied.'); + } else { + const errorMessage = e && e.message ? e.message : 'unknown reason'; + console.log('[AUTO_SLEEP] Error:', errorMessage); + say(agent, `I couldn't sleep (${errorMessage}).`); } } }); From a79da40a8f3e91b509b52bd96892289028d62c9a Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Thu, 19 Mar 2026 17:07:44 -0300 Subject: [PATCH 3/4] Update src/agent/modes.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/agent/modes.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/agent/modes.js b/src/agent/modes.js index cad58cba7..3bea99243 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -258,24 +258,17 @@ const modes_list = [ const beds = bot.findBlocks({ matching: (block) => block.name.includes('bed'), maxDistance: 32, - count: 1 - }); - if (beds.length > 0) { - execute(this, agent, async () => { + // Let skills.goToBed handle finding a suitable bed + execute(this, agent, async () => { + try { + await skills.goToBed(bot); say(agent, 'It\'s getting dark, I should sleep.'); - try { - await skills.goToBed(bot); - } catch (e) { - if (e && e.message && e.message.includes('occupied')) { - say(agent, 'The bed is occupied.'); - } else { - const errorMessage = e && e.message ? e.message : 'unknown reason'; - console.log('[AUTO_SLEEP] Error:', errorMessage); - say(agent, `I couldn't sleep (${errorMessage}).`); - } + } catch (e) { + if (e && e.message && e.message.includes('occupied')) { + say(agent, 'The bed is occupied.'); } - }); - } + } + }); } }, { From 01739319ee8cb84f9774bda299430d7f4c479e1d Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Thu, 19 Mar 2026 22:49:33 -0300 Subject: [PATCH 4/4] fix: resolve syntax error, water pickup, and ground detection bugs - Remove orphan bot.findBlocks() in auto_sleep that caused a syntax error breaking the entire modes.js module (sleep never triggered) - Equip empty bucket before activateItem() to actually pick up placed water - Scan 1-6 blocks below for ground instead of checking only at fixed 4-block offset, widening the activation window for MLG water - Use goToBed() return value instead of try/catch (it returns false, not throws) --- src/agent/modes.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/agent/modes.js b/src/agent/modes.js index 3bea99243..76a8c308b 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -111,9 +111,16 @@ const modes_list = [ if (fallDistance > 10 && vel.y < -0.5) { const waterBucket = bot.inventory.items().find(item => item.name === 'water_bucket'); if (waterBucket && !this.active) { - // Check if ground is near (within 4 blocks below) - const groundBlock = bot.blockAt(pos.offset(0, -4, 0)); - if (groundBlock && groundBlock.name !== 'air' && groundBlock.name !== 'water') { + // Check if ground is near (scan up to 6 blocks below) + let groundNear = false; + for (let dy = 1; dy <= 6; dy++) { + const block = bot.blockAt(pos.offset(0, -dy, 0)); + if (block && block.name !== 'air' && block.name !== 'water') { + groundNear = true; + break; + } + } + if (groundNear) { execute(this, agent, async () => { const currentPos = bot.entity.position.clone(); say(agent, 'MLG water!'); @@ -138,8 +145,12 @@ const modes_list = [ // Pick up water after landing const waterBlock = world.getNearestBlock(bot, 'water', 3); if (waterBlock) { - await bot.lookAt(waterBlock.position); - bot.activateItem(); + const emptyBucket = bot.inventory.items().find(item => item.name === 'bucket'); + if (emptyBucket) { + await bot.equip(emptyBucket, 'hand'); + await bot.lookAt(waterBlock.position); + bot.activateItem(); + } } } catch (e) { console.log('[FALL_PROTECTION] Error:', e.message); @@ -254,19 +265,11 @@ const modes_list = [ const isNight = time >= 13000; if (!isNight || bot.isSleeping) return; - // Look for a bed within 32 blocks using block name matching (beds are named like 'white_bed', 'red_bed', etc.) - const beds = bot.findBlocks({ - matching: (block) => block.name.includes('bed'), - maxDistance: 32, - // Let skills.goToBed handle finding a suitable bed + // Let skills.goToBed handle finding and sleeping in a bed execute(this, agent, async () => { - try { - await skills.goToBed(bot); + const result = await skills.goToBed(bot); + if (result) { say(agent, 'It\'s getting dark, I should sleep.'); - } catch (e) { - if (e && e.message && e.message.includes('occupied')) { - say(agent, 'The bed is occupied.'); - } } }); }