Skip to main content
Glama
runAway.ts6.33 kB
import { Bot } from 'mineflayer'; import { ISkillServiceParams, ISkillParams } from '../../types/skillType.js'; import mineflayer_pathfinder from 'mineflayer-pathfinder'; const { goals } = mineflayer_pathfinder; import { Entity } from 'prismarine-entity'; import { Vec3 } from 'vec3'; import { isSignalAborted, validateSkillParams } from '../index.js'; import { findClosestPlayerByName } from '../library/findClosestPlayerByName.js'; import { cancelableMove } from '../library/navigateToLocation.js'; const { GoalXZ } = goals; /** * Runs away from hostile mobs or players * * @param {Bot} bot - The Mineflayer bot instance. * @param {IFunctionCall} params - The parameters for the skill function. * @param {string?} params.stringValue.targetType - OPTIONAL: The type of the target to run away from. Use 'mob' for any hostile mobs. Use 'player' for players. Defaults to 'mob'. * @param {string?} params.stringValue.targetName - OPTIONAL: The specific display name of the entity to attack (optional). For players, use the player's username. For mobs, use the mob type (e.g., 'Zombie'). Make sure the target name starts with a capital letter. * @param {number?} params.numberValue.runDistance - OPTIONAL: The distance to run away from hostile mobs. Defaults to 10 blocks. * @param {ISkillServiceParams} serviceParams - additional parameters for the skill function. * * @return {Promise<boolean>} - Returns true if the bot successfully ran away from the target, false otherwise. */ export const runAway = async ( bot: Bot, params: ISkillParams, serviceParams: ISkillServiceParams, ): Promise<boolean> => { const skillName = 'runAway'; 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 unpackedParams = { targetType: params.targetType ?? 'mob', targetName: params.targetName ?? '', runDistance: params.runDistance ?? 10, signal: serviceParams.signal, }; let { targetType, targetName, runDistance, signal } = unpackedParams; // Define the radius within which you want to detect mobs const NEARBY_ENTITY_RADIUS = bot.nearbyEntityRadius; const MIN_DISTANCE = 0.01; // Minimum distance to avoid extremely small values targetName = targetName ?? ''; const targetStr = targetName ? targetName : targetType; targetName = targetName.replace(/\s/g, '_'); // Replace spaces in name with _ if (targetType == 'mob') { targetType = 'hostile'; } // Check if entities are available if (!bot.entities) return; const allEntities = Object.values(bot.entities); // Filter function to identify mobs within the specified radius const isNearbyEntity = (entity: Entity) => { return ( entity.position.distanceTo(bot.entity.position) < NEARBY_ENTITY_RADIUS ); }; // Filter for nearby hostile mobs const hostileMobs = allEntities.filter((entity) => { const version = parseInt(bot.version.split('.')[1]); const NBT_HEALTH = version < 10 ? 6 : version < 14 ? 7 : version < 17 ? 8 : 9; if (targetName) { if (targetType === 'player') { const closestPlayer = findClosestPlayerByName(bot, { name: targetName }); return ( isNearbyEntity(entity) && entity.username !== bot.entity.username && entity.username == closestPlayer.username ); } else { return ( isNearbyEntity(entity) && entity.name.toLowerCase() === targetName.toLowerCase() ); } } return ( isNearbyEntity(entity) && entity.type === targetType && entity.metadata[NBT_HEALTH] && entity.username !== bot.entity.username ); }); // Check if there are no hostile mobs if (hostileMobs.length === 0) { return bot.emit( 'alteraBotEndObservation', `There are no ${targetType}s to run away from.`, ); } // Calculate the weighted vector direction to run away const runVector = { x: 0, y: 0, z: 0 }; hostileMobs.forEach((mob) => { const vector = { x: bot.entity.position.x - mob.position.x, y: bot.entity.position.y - mob.position.y, z: bot.entity.position.z - mob.position.z, }; const distance = Math.sqrt(vector.x ** 2 + vector.y ** 2 + vector.z ** 2); const weight = 1 / Math.max(distance, MIN_DISTANCE); // Avoid division by extremely small values runVector.x += vector.x * weight; runVector.y += vector.y * weight; runVector.z += vector.z * weight; }); // Normalize the resulting vector const vectorLength = Math.sqrt( runVector.x ** 2 + runVector.y ** 2 + runVector.z ** 2, ); if (vectorLength > 0) { runVector.x /= vectorLength; runVector.y /= vectorLength; runVector.z /= vectorLength; } // Determine the destination position const runDestination = new Vec3( bot.entity.position.x + runVector.x * runDistance, bot.entity.position.y + runVector.y * runDistance, bot.entity.position.z + runVector.z * runDistance, ); if (bot.pathfinder.isMoving()) await bot.pathfinder.stop(); await bot.waitForTicks(1); // give the pathfinder some time to stop // Make the bot move to the destination position const goal = new GoalXZ(runDestination.x, runDestination.z); // execute a cancelable move to the destination const result = await cancelableMove(bot, { goal, signal }); // check signal 1st if (isSignalAborted(signal)) { return bot.emit( 'alteraBotEndObservation', `You decided to do something else and stop running away.`, ); } if (result.error) { return bot.emit( 'alteraBotEndObservation', `You couldn't finish running away, but did your best.`, ); } // Print out the list of all nearby hostile mobs // const hostileMobNames = hostileMobs.map(entity => entity.name); // const uniqueHostileMobTypes = [...new Set(hostileMobNames)]; // console.log(`Nearby hostile mobs: ${hostileMobNames.join(', ')}`); return bot.emit('alteraBotEndObservation', `You have finished running away!`); };

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