Skip to main content
Glama
useItemOnBlockOrEntity.ts9.63 kB
import {Bot} from 'mineflayer'; import {ISkillServiceParams, ISkillParams} from '../../types/skillType.js'; import {closest, distance} from 'fastest-levenshtein'; import minecraftData from 'minecraft-data'; import {validateSkillParams} from '../index.js'; import {findClosestItemName} from '../library/findClosestItemName.js'; import {navigateToLocation} from '../library/navigateToLocation.js'; /** * Use an equipped item on an entity. * * @param {Object} bot - The Mineflayer bot instance. * @param {Object} params * @param {string} params.item - The name of the item to use, this should be null if you just want to use or attack the entity or block without a particular item * @param {string} params.target - The target that the item will be used on * @param {number} params.count - OPTIONAL: The number of times to use the item, defaults to a single time, or 1 * @param {object} serviceParams - additional parameters for the skill function. * **/ export const useItemOnBlockOrEntity = async ( bot: Bot, params: ISkillParams, serviceParams: ISkillServiceParams, ): Promise<boolean> => { const mcData = minecraftData(bot.version); const skillName = 'useItemOnBlockOrEntity'; const requiredParams = ['item', 'target']; 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 = { item: params.item, target: params.target, count: params.count ?? 1, }; let itemName; if (!unpackedParams.item || unpackedParams.item === '') { itemName = 'hands'; } else { // First check if the item is valid itemName = findClosestItemName(bot, {name: unpackedParams.item}); if (!itemName) { // if the item is not in the list, return an error message return bot.emit( 'alteraBotEndObservation', `Mistake: The item ${unpackedParams.item} does not exist in Minecraft, so you can't use it on ${unpackedParams.target}.`, ); } // Equip the item const inventoryItem = bot.inventory.findInventoryItem( itemName as any, null, false, ); if (!inventoryItem) { return bot.emit( 'alteraBotEndObservation', `Mistake: You don't have any ${itemName} in your inventory to use on ${unpackedParams.target}.`, ); } else { console.log( `Equipping ${inventoryItem.name} to use on ${unpackedParams.target}.`, ); await bot.equip(inventoryItem, 'hand'); } } // Then check if the target is an entity or a block let targetIsEntity = false; let targetIsBlock = false; // Special case for boat, if boat is a substring of the target, map the target to boat if (unpackedParams.target.includes('boat')) { unpackedParams.target = 'boat'; } // find a matching entity entry in mcData based on the name const entityName = closest( unpackedParams.target.toLowerCase(), Object.keys(mcData.entitiesByName), ); const distanceToEntity = distance( unpackedParams.target.toLowerCase(), entityName, ); if (distanceToEntity < 4) { targetIsEntity = true; console.log(`targetIsEntity is true, found ${entityName}`); } const blockName = closest( unpackedParams.target.toLowerCase(), Object.keys(mcData.blocksByName), ); const distanceToBlock = distance( unpackedParams.target.toLowerCase(), blockName, ); if (distanceToBlock < 4) { targetIsBlock = true; console.log(`targetIsBlock is true, found ${blockName}`); } if (!targetIsEntity && !targetIsBlock) { return bot.emit( 'alteraBotEndObservation', `Mistake: The target ${unpackedParams.target} does not exist in Minecraft. Do you mean ${entityName} or ${blockName}?`, ); } if (targetIsEntity && targetIsBlock) { if (distanceToBlock < distanceToEntity) { targetIsEntity = false; } else { targetIsBlock = false; } } if (targetIsEntity) { return useItemOnEntity(bot, { itemName, entityName, count: unpackedParams.count, }); } else { return useItemOnBlock(bot, { itemName, blockName, count: unpackedParams.count, }); } }; interface IUseItemOnEntityOptions { itemName: string; entityName: string; count?: number; } export const useItemOnEntity = async ( bot: Bot, options: IUseItemOnEntityOptions, ): Promise<boolean> => { const defaultOptions = { count: 1, }; const {itemName, entityName, count} = {...defaultOptions, ...options}; const nearbyEntityList = Object.values(bot.entities).filter( (entity) => entity.name === entityName && bot.entity.position.distanceTo(entity.position) <= bot.nearbyEntityRadius, ); let entityList = []; if (nearbyEntityList.length > 0) { // sort the list by distance nearbyEntityList.sort( (a, b) => a.position.distanceTo(bot.entity.position) - b.position.distanceTo(bot.entity.position), ); entityList = nearbyEntityList.slice(0, count); } else { return bot.emit( 'alteraBotEndObservation', `You couldn't find any ${entityName} nearby. `, ); } console.log( `Found ${entityList.length} ${entityName} nearby to use ${itemName} on.`, ); let successCount = 0; // use the item on the entity for (const activeEntity of entityList) { const heldItem = bot.inventory.slots[36]; if (itemName !== 'hands' && (heldItem === null || heldItem === undefined)) { return bot.emit( 'alteraBotEndObservation', `You lost or wore out the ${itemName}.`, ); } if (!bot.entities[activeEntity.id]) { console.log( `UseItemOnEntity: The entity ${activeEntity.name} is no longer valid, skipping.`, ); continue; // Skip if the entity is no longer valid } if (activeEntity.position.distanceTo(bot.entity.position) > 2) { console.log(`Navigating to ${activeEntity.name}.`); await navigateToLocation(bot, { x: activeEntity.position.x, y: activeEntity.position.y, z: activeEntity.position.z, range: 2, }); } console.log(`looking at ${activeEntity.name}.`); bot.lookAt(activeEntity.position); console.log(`Using ${itemName} on ${activeEntity.name}.`); try { bot.useOn(activeEntity); bot.waitForTicks(5); // We don't really know what's the best way to check if the item was used on the entity successCount++; // Wait for a short period before moving to the next entity } catch (err) { const error = err as Error; console.error( `Error using ${itemName} on ${activeEntity.name}: ${error.message}`, ); } } return bot.emit( 'alteraBotEndObservation', `You have successfuly finished using ${itemName} on ${entityName}.`, ); }; interface IUseItemOnBlockOptions { itemName: string; blockName: string; count?: number; } export const useItemOnBlock = async ( bot: Bot, options: IUseItemOnBlockOptions, ): Promise<boolean> => { const mcData = minecraftData(bot.version); const defaultOptions = { count: 1, }; const {itemName, blockName, count} = {...defaultOptions, ...options}; const blockPositions = bot.findBlocks({ matching: mcData.blocksByName[blockName].id, maxDistance: bot.nearbyBlockXZRange, count: count, // ignore count, find all blocks, filter later }); if (blockPositions.length === 0) { return bot.emit( 'alteraBotEndObservation', `You couldn't find any ${blockName} nearby.`, ); } console.log( `Found ${blockPositions.length} ${blockName} nearby to use ${itemName} on.`, ); let successCount = 0; // use the item on the block for (const blockPosition of blockPositions) { const heldItem = bot.inventory.slots[36]; if (itemName !== 'hands' && (heldItem === null || heldItem === undefined)) { return bot.emit( 'alteraBotEndObservation', `You lost or wore out the ${itemName}.`, ); } const block = bot.blockAt(blockPosition); if (blockPosition.distanceTo(bot.entity.position) > 2) { console.log(`Navigating to ${block.name}.`); await navigateToLocation(bot, { x: block.position.x, y: block.position.y, z: block.position.z, range: 2, }); } console.log(`looking at ${block.name} at ${block.position}.`); await bot.lookAt(block.position.offset(0.5, 0.5, 0.5), true); console.log(`Using ${itemName} on ${block.name}.`); try { bot._client.write('use_item', {hand: 0}); // 0 for main hand bot._client.write('block_place', { location: block.position, direction: 1, // 1 for the top face of the block hand: 0, cursorX: 0.5, cursorY: 0.5, cursorZ: 0.5, insideBlock: false, }); await bot.waitForTicks(5); // We don't really know what's the best way to check if the item was used on the block successCount++; // Wait for a short period before moving to the next block } catch (err) { const error = err as Error; console.error( `Error using ${itemName} on ${block.name}: ${error.message}`, ); } } return bot.emit( 'alteraBotEndObservation', `You have successfuly finished using ${itemName} on ${blockName}.`, ); };

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