mcp2mqtt

  • src
import * as mineflayer from 'mineflayer'; import { EventEmitter } from 'events'; import { Vec3 } from 'vec3'; import pathfinderPkg from 'mineflayer-pathfinder'; const { pathfinder, Movements, goals } = pathfinderPkg; export interface BotConfig { host: string; port: number; username: string; version: string; } export class ProtocolHandler extends EventEmitter { private bot: mineflayer.Bot | null = null; private config: BotConfig; constructor(config: BotConfig) { super(); this.config = config; } public async connect(): Promise<void> { if (this.bot) { throw new Error('Bot is already connected'); } return new Promise((resolve, reject) => { try { this.bot = mineflayer.createBot({ host: this.config.host, port: this.config.port, username: this.config.username, version: this.config.version }); this.bot.once('spawn', () => { this.bot?.loadPlugin(pathfinder); if (this.bot?.pathfinder) { this.bot.pathfinder.setMovements(new Movements(this.bot)); } this.setupEventHandlers(); this.emit('connected'); resolve(); }); this.bot.on('error', (error) => { this.emit('error', error); reject(error); }); } catch (error) { reject(error); } }); } private setupEventHandlers(): void { if (!this.bot) return; this.bot.on('chat', (username, message) => { this.emit('chat', { username, message }); }); this.bot.on('kicked', (reason) => { this.emit('kicked', reason); }); this.bot.on('error', (error) => { this.emit('error', error); }); } public async sendChat(message: string): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); await this.bot.chat(message); } public async jump(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); this.bot.setControlState('jump', true); setTimeout(() => { if (this.bot) this.bot.setControlState('jump', false); }, 500); } public getPosition(): Vec3 | null { if (!this.bot || !this.bot.entity) return null; return this.bot.entity.position; } public async disconnect(): Promise<void> { if (!this.bot) return; return new Promise((resolve) => { const bot = this.bot; if (!bot) { resolve(); return; } bot.removeAllListeners(); bot.once('end', () => { this.bot = null; setTimeout(() => { process.exit(0); }, 1000); resolve(); }); bot.end(); }); } public isConnected(): boolean { return this.bot !== null; } public async moveForward(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); this.bot.setControlState('forward', true); await new Promise(resolve => setTimeout(resolve, 1000)); this.bot.setControlState('forward', false); } public async moveBack(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); this.bot.setControlState('back', true); await new Promise(resolve => setTimeout(resolve, 1000)); this.bot.setControlState('back', false); } public async turnLeft(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); this.bot.setControlState('left', true); await new Promise(resolve => setTimeout(resolve, 500)); this.bot.setControlState('left', false); } public async turnRight(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); this.bot.setControlState('right', true); await new Promise(resolve => setTimeout(resolve, 500)); this.bot.setControlState('right', false); } public async placeBlock(x: number, y: number, z: number): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { const targetPos = new Vec3(x, y, z); const faceVector = new Vec3(0, 1, 0); const referenceBlock = await this.bot.blockAt(targetPos); if (!referenceBlock) throw new Error('No reference block found'); await this.bot.placeBlock(referenceBlock, faceVector); } catch (error) { throw new Error(`Failed to place block: ${error}`); } } public async digBlock(x: number, y: number, z: number): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { const targetPos = new Vec3(x, y, z); const block = await this.bot.blockAt(targetPos); if (!block) throw new Error('No block at target position'); if (block.name === 'air') throw new Error('Cannot dig air'); await this.bot.dig(block); } catch (error) { throw new Error(`Failed to dig block: ${error}`); } } public async getBlockInfo(x: number, y: number, z: number): Promise<any> { if (!this.bot) throw new Error('Bot not connected'); try { const targetPos = new Vec3(x, y, z); const block = await this.bot.blockAt(targetPos); if (!block) throw new Error('No block at target position'); return { name: block.name, type: block.type, position: { x: block.position.x, y: block.position.y, z: block.position.z }, hardness: block.hardness }; } catch (error) { throw new Error(`Failed to get block info: ${error}`); } } public async selectSlot(slot: number): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); if (slot < 0 || slot > 8) throw new Error('Slot must be between 0 and 8'); try { await this.bot.setQuickBarSlot(slot); } catch (error) { throw new Error(`Failed to select slot: ${error}`); } } public async getInventory(): Promise<any> { if (!this.bot) throw new Error('Bot not connected'); const items = this.bot.inventory.items(); return items.map(item => ({ name: item.name, count: item.count, slot: item.slot, displayName: item.displayName })); } public async equipItem(itemName: string, destination?: string): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { const item = this.bot.inventory.items().find(item => item.name.includes(itemName)); if (!item) throw new Error(`Item ${itemName} not found in inventory`); const equipDestination: mineflayer.EquipmentDestination | null = destination as mineflayer.EquipmentDestination || null; await this.bot.equip(item, equipDestination); } catch (error) { throw new Error(`Failed to equip item: ${error}`); } } public async getStatus(): Promise<any> { if (!this.bot) throw new Error('Bot not connected'); return { health: this.bot.health, food: this.bot.food, gameMode: this.bot.game?.gameMode ?? 'unknown', position: this.getPosition(), isRaining: this.bot.isRaining, time: { timeOfDay: this.bot.time?.timeOfDay ?? 0, day: this.bot.time?.day ?? 0 } }; } public async getNearbyEntities(range: number = 10): Promise<any[]> { if (!this.bot) throw new Error('Bot not connected'); return Object.values(this.bot.entities) .filter(entity => { if (!entity.position || !this.bot?.entity?.position) return false; return entity.position.distanceTo(this.bot.entity.position) <= range; }) .map(entity => ({ name: entity.name, type: entity.type, position: entity.position, distance: entity.position && this.bot?.entity?.position ? entity.position.distanceTo(this.bot.entity.position) : null // Handle the case where position might be null })); } public async attack(entityName: string): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { const entity = Object.values(this.bot.entities) .find(e => e.name === entityName && e.position.distanceTo(this.bot!.entity.position) <= 4); if (!entity) throw new Error('Entity not found or too far'); await this.bot.attack(entity); } catch (error) { throw new Error(`Failed to attack: ${error}`); } } public async useItem(hand: 'right' | 'left' = 'right'): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { await this.bot.activateItem(hand === 'right'); } catch (error) { throw new Error(`Failed to use item: ${error}`); } } public async stopUsingItem(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { await this.bot.deactivateItem(); } catch (error) { throw new Error(`Failed to stop using item: ${error}`); } } public async lookAt(x: number, y: number, z: number): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { await this.bot.lookAt(new Vec3(x, y, z)); } catch (error) { throw new Error(`Failed to look at position: ${error}`); } } public async followPlayer(playerName: string): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { const player = this.bot.players[playerName]?.entity; if (!player) throw new Error('Player not found'); // Follow at 2 blocks distance await this.bot.pathfinder.goto( new goals.GoalFollow(player, 2) ); } catch (error) { throw new Error(`Failed to follow player: ${error}`); } } public async stopFollowing(): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); this.bot.pathfinder.stop(); } public async goToPosition(x: number, y: number, z: number): Promise<void> { if (!this.bot) throw new Error('Bot not connected'); try { await this.bot.pathfinder.goto( new goals.GoalBlock(x, y, z) ); } catch (error) { throw new Error(`Failed to go to position: ${error}`); } } }