Skip to main content
Glama
goToPerson.ts7.08 kB
import { Bot } from 'mineflayer'; import { Entity } from 'prismarine-entity'; import { Vec3 } from 'vec3'; import { isSignalAborted } from '../index.js'; import { findClosestPlayerByName } from './findClosestPlayerByName.js'; import minecraftData from 'minecraft-data'; import mineflayer_pathfinder from 'mineflayer-pathfinder'; const { GoalFollow } = mineflayer_pathfinder.goals; interface IGoToPersonParams { name: string; signal: AbortSignal; distance: number; keepFollowing: boolean; } /** * Goes to someone to get near them or follow them. * * @param {object} bot - The Mineflayer bot instance. Assume the bot is already spawned in the world. * @param {object} params * @param {string} params.name - The name of the person to go to or follow. * @param {number} params.distance - The desired distance to get within the person. Default is 3 blocks. * @param {boolean} params.keepFollowing - Whether to keep following the person after reaching them. Default is false. * @param {AbortSignal} params.signal - The signal to abort the skill. * * @return {Promise<boolean>} - Returns true if the bot successfully went to the person, false otherwise. */ export const goToPerson = async ( bot: Bot, params: IGoToPersonParams, ): Promise<boolean> => { const { distance: distanceParam, name, keepFollowing, signal } = params; const distance = Math.max(1, distanceParam); // Ensure distance is at least 1 const TELEPORT_TOLERANCE_THRESHOLD = 30; // in blocks, if the entity is this far away, try to teleport to them const EMIT_UPDATE_RATE = 2000; // in milliseconds - 2 seconds, how often to emit a message while following const MAX_FOLLOW_TIME = 300000; // in milliseconds - 5 minutes, how long to follow before stopping, this is an emergency time - out const PROCESS_UPDATE_FREQUENCY = 50; // how often to check if the bot is still following the entity in milliseconds, ~20 times per second const HASNT_MOVED_THRESHOLD = 10000; // in milliseconds - 3 seconds, how long to wait before assuming the entity has stopped moving const entity = findClosestPlayerByName(bot, { name }); if (!entity) { // bot.chat(`Entity ${name} not found.`); return bot.emit( 'alteraBotEndObservation', `Mistake: You couldn't find anyone with the name ${name} to go to.`, ); // return; } if (bot.pathfinder.isMoving()) { await bot.pathfinder.stop(); } await bot.waitForTicks(2); // give time for pathfinder to stop if (isSignalAborted(signal)) { return; } // define a dynamic goal to follow the entity const followGoal = new GoalFollow(entity, distance); bot.pathfinder.setGoal(followGoal, true); // The second argument true makes the goal dynamic await bot.waitForTicks(2); let curDistance = -9999; let messageCounter = 0; let followTargetPosition = new Vec3( entity.position.x, entity.position.y, entity.position.z, ); let hasntMovedCount = 0; // bot.emit('alteraBotTextObservation', `You started following ${ entity.username }`); for (let n = 0; n < MAX_FOLLOW_TIME; n += PROCESS_UPDATE_FREQUENCY) { if (followTargetPosition.equals(entity.position)) { hasntMovedCount += PROCESS_UPDATE_FREQUENCY; if (hasntMovedCount > HASNT_MOVED_THRESHOLD) { bot.emit('alteraBotTextObservation', `${name} has stopped moving.`); break; } } else { // reset hasn't moved count followTargetPosition = new Vec3( entity.position.x, entity.position.y, entity.position.z, ); // console.log(`new target position: ${ JSON.stringify(bot.followTargetPosition)}`); hasntMovedCount = 0; } if (!isBotFollowing(bot, { entity })) { break; } if (isSignalAborted(signal)) { break; } curDistance = bot.entity.position.distanceTo(entity.position); // check to see if we are too far away and cheats are enabled if (bot.cheatsAllowed === undefined) // if cheats have never been set, set them to true, assume they're enabled bot.cheatsAllowed = true; if (curDistance > TELEPORT_TOLERANCE_THRESHOLD && bot.cheatsAllowed) { console.log( ` attempting to telport to ${entity.username} because they are too far away.`, ); bot.chat(`/tp ${entity.username}`); // this assumes cheats are enabled // check to see if the we got a failure message when teleporting if cheats are not enabled await bot.waitForTicks(2); // give time for teleport to complete curDistance = bot.entity.position.distanceTo(entity.position); // check to see if we are still too far away (with a small buffer) if (curDistance > TELEPORT_TOLERANCE_THRESHOLD - 2) { console.log( ` failed to teleport to ${entity.username} because cheats are not enabled. Turning off cheats.`, ); bot.cheatsAllowed = false; } } if (curDistance <= distance && !keepFollowing) { return bot.emit( 'alteraBotEndObservation', `You finished going to ${name}.`, ); } messageCounter += PROCESS_UPDATE_FREQUENCY; if (keepFollowing && messageCounter >= EMIT_UPDATE_RATE) { bot.emit('alteraBotTextObservation', `You are still following ${name}.`); messageCounter = 0; } await new Promise((resolve) => setTimeout(resolve, PROCESS_UPDATE_FREQUENCY), ); // wait for a little bit } await bot.pathfinder.stop(); bot.pathfinder.setGoal(null); bot.waitForTicks(2); return bot.emit( 'alteraBotEndObservation', `You have stopped following ${name}.`, ); }; interface IFindEntityByNameParams { name: string; } /** * Finds a player entity by username. * * @param {object} bot - The Mineflayer bot instance. * @param {object} params * @param {string} params.name - The name of the entity to find. * * @return {Entity|null} - The entity with the given name, or null if not found. */ const findEntityByName = ( bot: Bot, params: IFindEntityByNameParams, ): Entity | null => { const { name } = params; return Object.values(bot.entities).find((entity) => entity.username === name); }; interface IIsBotFollowingParams { entity: Entity; } /** * Check to see if pathfinder is still following * * @param {object} bot - The Mineflayer bot instance. * @param {object} params * @param {Entity} params.entity - The entity to check if the bot is following. * * @return {boolean} - Returns true if the bot is still following the entity, false otherwise. */ const isBotFollowing = (bot: Bot, params: IIsBotFollowingParams): boolean => { const { entity } = params; const goal = bot.pathfinder.goal; // check to see that the goal is still valid and pointing to the same entity if ( goal === null || goal === undefined || !(goal as any).entity || (goal as any).entity.username !== entity.username || findEntityByName(bot, { name: entity.username }) === null ) { console.log(`Goal is null or entity is not found in following`); return false; } return true; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/leo4life2/minecraft-mcp-http'

If you have feedback or need assistance with the MCP directory API, please join our Discord server