From f95a076fcb13bfd3c2bfe06ddc34cae61189ff88 Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Thu, 5 Feb 2026 23:13:34 -0300 Subject: [PATCH 1/2] 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 21b7b955e..638cbf849 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 3b2a3f33cb1549dcee490096acd75261b09211ab Mon Sep 17 00:00:00 2001 From: Adalberto Garcia Garces Date: Thu, 5 Feb 2026 23:32:12 -0300 Subject: [PATCH 2/2] 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 638cbf849..eb5921300 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}).`); } } });