UNPKG

mineflayer-crafting-util

Version:
355 lines (262 loc) 9.36 kB
# mineflayer-crafting-util Crafting planner and execution helpers for Mineflayer bots. The library can be used in two ways: - As a Mineflayer plugin, which adds planning and crafting methods to `bot`. - As a static planner, which builds plans from a `minecraft-data` registry without connecting a bot. ## Installation ```sh npm i mineflayer-crafting-util ``` ```sh yarn add mineflayer-crafting-util ``` ## Bot Usage ```js const mineflayer = require('mineflayer') const { plugin: craftingUtil } = require('mineflayer-crafting-util') const bot = mineflayer.createBot({ host: 'localhost', port: 25565, username: 'bot' }) bot.loadPlugin(craftingUtil()) bot.once('spawn', async () => { const stick = bot.registry.itemsByName.stick const craftingTable = bot.findBlock({ matching: bot.registry.blocksByName.crafting_table.id, maxDistance: 4 }) const plan = bot.planCraftInventory({ id: stick.id, count: 4 }) if (!plan.success) { console.log('Missing base items:', plan.itemsRequiredBase) return } await bot.craftPlan(plan, craftingTable) }) ``` For a one-call inventory-backed craft: ```js const pickaxe = bot.registry.itemsByName.iron_pickaxe const craftingTable = bot.findBlock({ matching: bot.registry.blocksByName.crafting_table.id, maxDistance: 4 }) await bot.craftItem(pickaxe.id, 1, craftingTable) ``` ## Static Usage ```js const mcData = require('minecraft-data')('1.21.4') const { buildStatic } = require('mineflayer-crafting-util') async function main () { const { Recipe } = require('prismarine-recipe')(mcData) const crafter = await buildStatic(Recipe) const pickaxe = mcData.itemsByName.stone_pickaxe const plan = crafter( { id: pickaxe.id, count: 1 }, { availableItems: [ { id: mcData.itemsByName.cobblestone.id, count: 3 }, { id: mcData.itemsByName.oak_log.id, count: 1 } ], multipleRecipes: true } ) console.log(plan.success) console.log(plan.recipesToDo) } main() ``` ## Exports ### `plugin` ```ts plugin(recipe?: Recipe): (bot: Bot, botOptions: BotOptions) => Promise<void> ``` Mineflayer plugin factory export. Call it to create a plugin, then use with `bot.loadPlugin(plugin())`. The default export is the same plugin factory: ```js const craftingUtil = require('mineflayer-crafting-util') bot.loadPlugin((craftingUtil.default ?? craftingUtil.plugin)()) ``` ### `buildStatic` ```ts buildStatic(registry: IndexedData): Promise<CraftingFunc> ``` Builds a version-specific planner from a `minecraft-data` registry. ```js const mcData = require('minecraft-data')('1.21.4') const { Recipe } = require('prismarine-recipe')(mcData) const crafter = await buildStatic(Recipe) const plan = crafter({ id: mcData.itemsByName.stick.id, count: 4 }) ``` ### `craftPlan` ```ts craftPlan( bot: Bot, plan: CraftingPlan, craftingTable: Block, options?: CraftPlanOptions ): Promise<CraftingPlan> ``` Executes every recipe step in an existing plan with `bot.craft`. Rules: - `craftingTable` is required. - Throws if `plan.success` is false. - Returns the same plan after crafting. - By default, each plan step is sent to Mineflayer as one `bot.craft(recipe, recipeApplications, craftingTable)` call. - With `{ strict: true }`, large recipe application counts are split by the crafted item stack size. ### `craftItem` ```ts craftItem( bot: Bot, itemId: number, count: number, craftingTable: Block, options?: CraftOptions, craftOptions?: CraftPlanOptions ): Promise<CraftingPlan> ``` Plans and executes a craft for `itemId`. Defaults used by `craftItem`: - `availableItems`: current bot inventory, unless provided. - `careAboutExisting`: `false`. - `includeRecursion`: `true`. - `multipleRecipes`: `true`. ### `setupActualCrafting` ```ts setupActualCrafting(bot: Bot): void ``` Adds `bot.craftPlan` and `bot.craftItem` to a bot. The plugin calls this automatically, so most users do not need to call it directly. ## Bot Methods Loading the plugin adds these methods to `bot`. ### `bot.planCraft` ```ts bot.planCraft(wantedItem: Item, options?: CraftOptions): CraftingPlan ``` Creates a crafting plan for `wantedItem`. Without `availableItems`, this answers "what would be required to craft this?" and does not validate against a specific inventory. With `availableItems`, this answers "can this be crafted from these items?" and returns a concrete ordered recipe plan when possible. ### `bot.planCraftInventory` ```ts bot.planCraftInventory(wantedItem: Item): CraftingPlan ``` Plans using the bot inventory as `availableItems`. Internally this uses: ```ts { availableItems: bot inventory items, careAboutExisting: false, includeRecursion: true, multipleRecipes: true } ``` ### `bot.craftPlan` ```ts bot.craftPlan( plan: CraftingPlan, craftingTable: Block, options?: CraftPlanOptions ): Promise<CraftingPlan> ``` Executes an existing plan. This is the bot-bound wrapper around the exported `craftPlan`. ### `bot.craftItem` ```ts bot.craftItem( itemId: number, count: number, craftingTable: Block, options?: CraftOptions, craftOptions?: CraftPlanOptions ): Promise<CraftingPlan> ``` Plans from inventory and executes the result. This is the bot-bound wrapper around the exported `craftItem`. ## Types ### `Item` ```ts interface Item { id: number count: number } ``` `id` is the numeric item id from the active Minecraft registry. `count` is the item count. ### `CraftOptions` ```ts interface CraftOptions { availableItems?: Item[] careAboutExisting?: boolean includeRecursion?: boolean multipleRecipes?: boolean } ``` | Option | Default | Description | | --- | --- | --- | | `availableItems` | `undefined` | Inventory constraints for the planner. When provided, the planner attempts to produce a craftable ordered plan from those items. | | `careAboutExisting` | `false` | When `false`, matching copies of the wanted item inside `availableItems` are ignored so the planner crafts the requested count fresh. When `true`, existing wanted items can satisfy the request. | | `includeRecursion` | `false` | Keeps recursive planning behavior enabled for inventory-backed plans. `bot.planCraftInventory` and `craftItem` enable this. | | `multipleRecipes` | `false` | Allows the planner to try alternate recipes and recipe families when building a plan from available items. Recommended with `availableItems`. | Note: `availableItems` may be normalized by the planner. Pass a cloned array if you need to preserve the original object identities and counts. ### `CraftingPlan` ```ts interface CraftingPlan { success: boolean itemsRequiredBase: Item[] itemsRequiredImmediate: Item[] itemsRemaining: Item[] itemsCreated: Item[] recipesToDo: RecipeInfo[] requiresCraftingTable: boolean } ``` | Property | Description | | --- | --- | | `success` | Whether the planner found a valid plan. | | `itemsRequiredBase` | Base items needed to complete the request, after accounting for `recipesToDo`. | | `itemsRequiredImmediate` | Direct recipe inputs needed at the point planning stopped. | | `itemsRemaining` | Requested output that is still not satisfied by the partial plan. | | `itemsCreated` | Net positive items produced by `recipesToDo`. On partial available-item plans, this shows what can be created even when `success` is `false`. | | `recipesToDo` | Ordered recipe applications to execute. | | `requiresCraftingTable` | Whether any step in `recipesToDo` requires a crafting table. | ### `RecipeInfo` ```ts interface RecipeInfo { recipe: Recipe recipeApplications: number } ``` `recipe` is a `prismarine-recipe` recipe object. `recipeApplications` is the number of times to apply that recipe. ### `CraftPlanOptions` ```ts interface CraftPlanOptions { strict?: boolean } ``` | Option | Default | Description | | --- | --- | --- | | `strict` | `false` | When `true`, execution splits each plan step into multiple `bot.craft` calls by the result item stack size. This is useful as a workaround for Mineflayer craft-count issues with non-stackable outputs. | ### `CraftingFunc` ```ts type CraftingFunc = (item: Item, options?: CraftOptions) => CraftingPlan ``` Returned by `buildStatic` and used internally for `bot.planCraft`. ## Working With Plans To print recipe steps, prefer `recipe.delta` because some Minecraft versions expose empty `recipe.ingredients` arrays for shaped recipes: ```js function recipeInputs (recipe) { return recipe.ingredients && recipe.ingredients.length > 0 ? recipe.ingredients : recipe.delta.filter(item => item.count < 0) } for (const step of plan.recipesToDo) { const inputs = recipeInputs(step.recipe) console.log(inputs, '=>', step.recipe.result, 'x', step.recipeApplications) } ``` ## Test And Smoke Commands ```sh npm run build npm test npm run smoke:matrix ``` Manual static smoke example: ```sh node scripts/staticPlanSmoke.js --version 1.21.4 --wanted-item stone_pickaxe --available cobblestone:3,oak_log:1 ```