Mostly works
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
'use strict';
|
||||
|
||||
const Vec3 = require('vec3');
|
||||
const { goals: { GoalNear } } = require('mineflayer-pathfinder');
|
||||
const { sleep } = require('../../utils');
|
||||
|
||||
class Navigation {
|
||||
constructor(args) {
|
||||
this.bot = args.bot;
|
||||
|
||||
this.commands = [
|
||||
{
|
||||
name: 'goto',
|
||||
description: 'Move the bot to coordinates or a player position',
|
||||
parameters: [
|
||||
{ name: 'target', type: 'string', required: true, description: 'Coordinates "x y z" or player name' },
|
||||
{ name: 'range', type: 'number', required: false, description: 'Stop distance from target (default: 0)' }
|
||||
],
|
||||
category: 'movement'
|
||||
},
|
||||
{
|
||||
name: 'come',
|
||||
description: 'Come to the requesting player with recovery on failure',
|
||||
parameters: [
|
||||
{ name: 'player', type: 'string', required: false, description: 'Player to come to (defaults to requester)' }
|
||||
],
|
||||
category: 'movement'
|
||||
},
|
||||
{
|
||||
name: 'follow',
|
||||
description: 'Go to a player and maintain distance',
|
||||
parameters: [
|
||||
{ name: 'target', type: 'string', required: true, description: 'Player to follow' },
|
||||
{ name: 'range', type: 'number', required: false, description: 'Follow distance (default: 3)' }
|
||||
],
|
||||
category: 'movement'
|
||||
},
|
||||
{
|
||||
name: 'stop',
|
||||
description: 'Stop current pathfinding',
|
||||
parameters: [],
|
||||
category: 'movement'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async init() {}
|
||||
async unload() {
|
||||
try { this.bot.bot.pathfinder.stop(); } catch (e) { /* ignore */ }
|
||||
return true;
|
||||
}
|
||||
|
||||
async handleCommand(from, command, ...args) {
|
||||
switch (command) {
|
||||
case 'goto': {
|
||||
const [target, rangeStr] = args;
|
||||
const range = parseFloat(rangeStr) || 0;
|
||||
if (!target) return 'Specify coordinates "x y z" or a player name';
|
||||
|
||||
// Parse coordinates
|
||||
const parts = target.split(' ');
|
||||
if (parts.length >= 3) {
|
||||
const coords = parts.map(Number);
|
||||
if (coords.every(c => !isNaN(c))) {
|
||||
return this._goWithRecovery(new Vec3(coords[0], coords[1], coords[2]), range);
|
||||
}
|
||||
}
|
||||
|
||||
// Player target
|
||||
const player = this.bot.bot.players[target];
|
||||
if (player && player.entity) {
|
||||
return this._goWithRecovery(player.entity.position, range);
|
||||
}
|
||||
return `Target not found: ${target}`;
|
||||
}
|
||||
|
||||
case 'come': {
|
||||
const [playerName] = args;
|
||||
const target = playerName || from;
|
||||
const player = this.bot.bot.players[target];
|
||||
if (!player || !player.entity) return `Cannot find ${target}`;
|
||||
return this._goWithRecovery(player.entity.position, 3);
|
||||
}
|
||||
|
||||
case 'follow': {
|
||||
const [target, rangeStr] = args;
|
||||
const range = parseFloat(rangeStr) || 3;
|
||||
const player = this.bot.bot.players[target];
|
||||
if (!player || !player.entity) return `Cannot find ${target}`;
|
||||
return this._goWithRecovery(player.entity.position, range);
|
||||
}
|
||||
|
||||
case 'stop':
|
||||
try { this.bot.bot.pathfinder.stop(); } catch (e) { /* ignore */ }
|
||||
return 'Movement stopped';
|
||||
|
||||
default:
|
||||
return `Unknown navigation command: ${command}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Smart movement with recovery — retries with 360 scan and waypoint unstick when goTo fails
|
||||
async _goWithRecovery(targetPos, range) {
|
||||
let attempts = 0;
|
||||
|
||||
while (attempts < 3) {
|
||||
const ok = await this.bot.goTo({ where: targetPos, range });
|
||||
if (ok) return `Arrived at destination`;
|
||||
|
||||
attempts++;
|
||||
console.log(`[Navigation] goTo failed (attempt ${attempts}/3), applying recovery...`);
|
||||
|
||||
// 360 visual refresh
|
||||
const bot = this.bot.bot;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await bot.look(bot.entity.yaw + Math.PI / 2, 0, true);
|
||||
await sleep(150);
|
||||
}
|
||||
|
||||
// Perpendicular waypoint to unstick
|
||||
const pos = bot.entity.position;
|
||||
if (!isNaN(pos.x) && !isNaN(pos.z)) {
|
||||
const yaw = Math.atan2(targetPos.z - pos.z, targetPos.x - pos.x);
|
||||
const perps = [yaw + Math.PI / 2, yaw - Math.PI / 2, yaw + Math.PI];
|
||||
for (const p of perps) {
|
||||
const wx = pos.x + Math.cos(p) * 3;
|
||||
const wz = pos.z + Math.sin(p) * 3;
|
||||
try {
|
||||
await bot.pathfinder.goto(
|
||||
new GoalNear(wx, pos.y, wz, 1)
|
||||
);
|
||||
await sleep(1000);
|
||||
bot.clearControlStates();
|
||||
break;
|
||||
} catch (e) { /* try next */ }
|
||||
}
|
||||
|
||||
// Backward nudge
|
||||
bot.setControlState('back', true);
|
||||
await sleep(300);
|
||||
bot.clearControlStates();
|
||||
await sleep(200);
|
||||
}
|
||||
}
|
||||
|
||||
return 'Failed to reach destination after recovery attempts';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Navigation;
|
||||
Reference in New Issue
Block a user