prepareLandForFarming
Automate tilling soil in Minecraft for farming using a scripted tool that integrates with the MCP Server, enabling efficient land preparation for crop planting.
Instructions
Prepare land for farming by tilling soil
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- Main handler function that executes the tool: prepares farmland by tilling soil near water sources (within 4-block distance), clears grass above, navigates to each spot, tills using tillLandToFarmland helper, then plants seeds using plantSeedsOnFarmland, and emits completion observation.export const prepareLandForFarming = async ( bot: Bot, params: ISkillParams, serviceParams: ISkillServiceParams, ): Promise<boolean> => { const skillName = 'prepareLandForFarming'; const requiredParams: string[] = []; const isParamsValid = validateSkillParams( params, requiredParams, skillName, ); if (!isParamsValid) { serviceParams.cancelExecution?.(); bot.emit( 'alteraBotEndObservation', `Mistake: You didn't provide all of the required parameters ${requiredParams.join(', ')} for the ${skillName} skill.`, ); return false; } const {signal, getStatsData, setStatsData} = serviceParams; const mcData = minecraftData(bot.version); // Avoid breaking blocks while building const movements = new Movements(bot); movements.canDig = false; // Prevent the bot from breaking blocks bot.pathfinder.setMovements(movements); // Remove expanding the farms for now // const placedCount = expandFarm(bot, 8); const waterBlocks = bot .findBlocks({ matching: mcData.blocksByName.water.id, maxDistance: 6, count: 100, }) .map((position) => bot.blockAt(position)); // console.log("Found water blocks: ", waterBlocks.length); const airId = mcData.blocksByName.air.id; // grassId is 1.19, shortGrassId is 1.20 const grassId = mcData.blocksByName.grass?.id; const shortGrassId = mcData.blocksByName.short_grass?.id; const tallGrassId = mcData.blocksByName.tall_grass?.id; const adjacentWaterBlocks = waterBlocks.filter((waterBlock) => hasAdjacentLand(bot, {position: waterBlock.position}), ); // console.log("Found adjacent water blocks: ", adjacentWaterBlocks.length); const queue = adjacentWaterBlocks.map((waterBlock) => ({ block: waterBlock, distance: 0, })); // const queue = waterBlocks.map(waterBlock => ({ block: waterBlock, distance: 0 })); // console.log("Found water blocks: ", queue.length) const visited = new Set(); const tillableBlocks = []; while (queue.length > 0) { const {block, distance} = queue.shift(); // Get the first element from the queue // console.log("Processing block: ", block.position.toString()); const blockKey = block.position.toString(); if (visited.has(blockKey)) continue; // Skip if already visited visited.add(blockKey); // If it's a dirt or grass block and within 4 blocks of water, mark it for tilling if ( distance > 0 && distance <= 4 && (block.type === mcData.blocksByName.dirt.id || block.type === mcData.blocksByName.grass_block.id) ) { const blockAbove = bot.blockAt(block.position.offset(0, 1, 0)); if ( blockAbove && (blockAbove.type === airId || blockAbove.type === grassId || blockAbove.type === shortGrassId || blockAbove.type === tallGrassId) ) { tillableBlocks.push(block); } } if (distance < 4) { const neighbors = getNeighborBlocks(bot, { position: block.position, yLevel: block.position.y, }); for (const neighbor of neighbors) { const neighborKey = neighbor.position.toString(); if (!visited.has(neighborKey)) { queue.push({block: neighbor, distance: distance + 1}); } } } } // Till the identified blocks for (const block of tillableBlocks) { // Navigate to the block await navigateToLocation(bot, { x: block.position.x, y: block.position.y, z: block.position.z, range: 1, }); // check for signal to cancel if (isSignalAborted(signal)) { return bot.emit( 'alteraBotEndObservation', `You decided to do something else and stop preparing land for farming.`, ); } await tillLandToFarmland(bot, { targetBlock: block, setStatsData, getStatsData, }); } await plantSeedsOnFarmland(bot, {getStatsData, setStatsData, radius: 8}); const defaultMovements = new Movements(bot); bot.pathfinder.setMovements(defaultMovements); bot.emit( 'alteraBotEndObservation', `You ` /* + `placed ${placedCount} new dirt blocks in the farm, `*/ + `tilled ${tillableBlocks.length} pieces of land, and have finished preparing land for farming.`, ); return true; };
- mcp-server/src/skillRegistry.ts:174-178 (registration)Registers the 'prepareLandForFarming' skill in SKILL_METADATA, defining its description and empty input schema (no parameters required). Used by loadSkills() to create SkillDefinition.prepareLandForFarming: { description: "Prepare land for farming by tilling soil", params: {}, required: [] },
- Dynamically generates the input schema for the skill from SKILL_METADATA during registration in loadSkills().type: "object", properties: metadata.params, required: metadata.required }, execute: createSkillExecutor(skillName)
- Helper to filter water blocks that have adjacent land (dirt or grass_block), used to start BFS for finding tillable areas.const hasAdjacentLand = ( bot: Bot, options: IHasAdjacentLandOptions, ): boolean => { const {position} = options; const mcData = minecraftData(bot.version); const offsets = [ {x: 1, y: 0, z: 0}, {x: -1, y: 0, z: 0}, {x: 0, y: 0, z: 1}, {x: 0, y: 0, z: -1}, ]; return offsets.some((offset) => { const newPos = position.plus(new Vec3(offset.x, offset.y, offset.z)); const block = bot.blockAt(newPos); return ( block && (block.type === mcData.blocksByName.dirt.id || block.type === mcData.blocksByName.grass_block.id) ); }); };
- Helper to get horizontal neighbor blocks for BFS expansion from water to find tillable land within distance 4.const getNeighborBlocks = ( bot: Bot, options: IGetNeighborBlocksOptions, ): Block[] => { const {position, yLevel} = options; const offsets = [ {x: 1, y: 0, z: 0}, {x: -1, y: 0, z: 0}, {x: 0, y: 0, z: 1}, {x: 0, y: 0, z: -1}, // { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, // Check above and below too ]; return offsets .map((offset) => { const newPos = position.plus(new Vec3(offset.x, offset.y, offset.z)); return bot.blockAt(newPos); }) .filter((block) => block && block.position.y === yLevel); // Remove undefined blocks (e.g., out of the world bounds) };