block-tools.ts•5.48 kB
import { z } from "zod";
import mineflayer from 'mineflayer';
import pathfinderPkg from 'mineflayer-pathfinder';
const { goals } = pathfinderPkg;
import { Vec3 } from 'vec3';
import minecraftData from 'minecraft-data';
import { ToolFactory } from '../tool-factory.js';
import { log } from '../logger.js';
type FaceDirection = 'up' | 'down' | 'north' | 'south' | 'east' | 'west';
interface FaceOption {
direction: string;
vector: Vec3;
}
export function registerBlockTools(factory: ToolFactory, getBot: () => mineflayer.Bot): void {
factory.registerTool(
"place-block",
"Place a block at the specified position",
{
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
z: z.number().describe("Z coordinate"),
faceDirection: z.enum(['up', 'down', 'north', 'south', 'east', 'west']).optional().describe("Direction to place against (default: 'down')")
},
async ({ x, y, z, faceDirection = 'down' }: { x: number, y: number, z: number, faceDirection?: FaceDirection }) => {
const bot = getBot();
const placePos = new Vec3(x, y, z);
const blockAtPos = bot.blockAt(placePos);
if (blockAtPos && blockAtPos.name !== 'air') {
return factory.createResponse(`There's already a block (${blockAtPos.name}) at (${x}, ${y}, ${z})`);
}
const possibleFaces: FaceOption[] = [
{ direction: 'down', vector: new Vec3(0, -1, 0) },
{ direction: 'north', vector: new Vec3(0, 0, -1) },
{ direction: 'south', vector: new Vec3(0, 0, 1) },
{ direction: 'east', vector: new Vec3(1, 0, 0) },
{ direction: 'west', vector: new Vec3(-1, 0, 0) },
{ direction: 'up', vector: new Vec3(0, 1, 0) }
];
if (faceDirection !== 'down') {
const specificFace = possibleFaces.find(face => face.direction === faceDirection);
if (specificFace) {
possibleFaces.unshift(possibleFaces.splice(possibleFaces.indexOf(specificFace), 1)[0]);
}
}
for (const face of possibleFaces) {
const referencePos = placePos.plus(face.vector);
const referenceBlock = bot.blockAt(referencePos);
if (referenceBlock && referenceBlock.name !== 'air') {
if (!bot.canSeeBlock(referenceBlock)) {
const goal = new goals.GoalNear(referencePos.x, referencePos.y, referencePos.z, 2);
await bot.pathfinder.goto(goal);
}
await bot.lookAt(placePos, true);
try {
await bot.placeBlock(referenceBlock, face.vector.scaled(-1));
return factory.createResponse(`Placed block at (${x}, ${y}, ${z}) using ${face.direction} face`);
} catch (placeError) {
log('warn', `Failed to place using ${face.direction} face: ${placeError}`);
continue;
}
}
}
return factory.createResponse(`Failed to place block at (${x}, ${y}, ${z}): No suitable reference block found`);
}
);
factory.registerTool(
"dig-block",
"Dig a block at the specified position",
{
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
z: z.number().describe("Z coordinate"),
},
async ({ x, y, z }) => {
const bot = getBot();
const blockPos = new Vec3(x, y, z);
const block = bot.blockAt(blockPos);
if (!block || block.name === 'air') {
return factory.createResponse(`No block found at position (${x}, ${y}, ${z})`);
}
if (!bot.canDigBlock(block) || !bot.canSeeBlock(block)) {
const goal = new goals.GoalNear(x, y, z, 2);
await bot.pathfinder.goto(goal);
}
await bot.dig(block);
return factory.createResponse(`Dug ${block.name} at (${x}, ${y}, ${z})`);
}
);
factory.registerTool(
"get-block-info",
"Get information about a block at the specified position",
{
x: z.number().describe("X coordinate"),
y: z.number().describe("Y coordinate"),
z: z.number().describe("Z coordinate"),
},
async ({ x, y, z }) => {
const bot = getBot();
const blockPos = new Vec3(x, y, z);
const block = bot.blockAt(blockPos);
if (!block) {
return factory.createResponse(`No block information found at position (${x}, ${y}, ${z})`);
}
return factory.createResponse(`Found ${block.name} (type: ${block.type}) at position (${block.position.x}, ${block.position.y}, ${block.position.z})`);
}
);
factory.registerTool(
"find-block",
"Find the nearest block of a specific type",
{
blockType: z.string().describe("Type of block to find"),
maxDistance: z.number().optional().describe("Maximum search distance (default: 16)")
},
async ({ blockType, maxDistance = 16 }) => {
const bot = getBot();
const mcData = minecraftData(bot.version);
const blocksByName = mcData.blocksByName;
if (!blocksByName[blockType]) {
return factory.createResponse(`Unknown block type: ${blockType}`);
}
const blockId = blocksByName[blockType].id;
const block = bot.findBlock({
matching: blockId,
maxDistance: maxDistance
});
if (!block) {
return factory.createResponse(`No ${blockType} found within ${maxDistance} blocks`);
}
return factory.createResponse(`Found ${blockType} at position (${block.position.x}, ${block.position.y}, ${block.position.z})`);
}
);
}