You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PrismarineJS生成Minecraft物品数据的错误排查与修复

修复Minecraft物品数据生成脚本的ID匹配错误

问题背景

我正在做一个涉及自动账号的Minecraft项目,需要让机器人识别物品的获取方式(暂不考虑村民交易等高级方式)。用JavaScript生成记录所有物品及其获取方式的items.json时出现错误:比如基岩被标记为可开采且掉落竹子方块,丛林原木掉落黄色羊毛。

问题分析

核心问题是方块ID与物品ID的映射不匹配,导致在获取方块掉落物和可开采性时关联了错误的数据。具体来看:

  1. getMiningInfo函数中直接使用block.drops[0]作为物品ID,但部分方块的掉落物ID并非直接对应物品ID,或drops数组结构并非简单的ID值。
  2. 遍历items时,部分物品名称与方块名称不完全对应,导致registry.blocksByName[item.name]匹配到错误方块。

修复后的完整代码

const fs = require('fs');
const registry = require('prismarine-registry')('1.20.4');
const {Recipe} = require('prismarine-recipe')('1.20.4');

const items = registry.items;
const blocks = registry.blocks;

function getCraftingRecipes(itemId) {
    const recipesForItem = Recipe.find(itemId, null);
    if (!recipesForItem) return [];
    return recipesForItem.map(recipe => {
        const requiredItems = {};

        if (recipe.inShape) {
            recipe.inShape.forEach(row => {
                row.forEach(ingredient => {
                    if (ingredient) {
                        const itemName = items[ingredient.id]?.name || 'unknown';
                        requiredItems[itemName] = (requiredItems[itemName] || 0) + Math.abs(ingredient.count);
                    }
                });
            });
        } else if (recipe.delta) {
            recipe.delta.forEach(deltaItem => {
                if (deltaItem.id !== itemId) {
                    const itemName = items[deltaItem.id]?.name || 'unknown';
                    requiredItems[itemName] = (requiredItems[itemName] || 0) + Math.abs(deltaItem.count);
                }
            });
        }

        return {
            required_items: requiredItems,
            obtained_count: recipe.result.count
        };
    }).sort((a, b) => b.obtained_count - a.obtained_count);
}

function getSmeltingRecipe(itemId) {
    const smeltingRecipes = [
        {input: 'iron_ore', result: 'iron_ingot'},
        {input: 'deepslate_iron_ore', result: 'iron_ingot'},
        {input: 'gold_ore', result: 'gold_ingot'},
        {input: 'deepslate_gold_ore', result: 'gold_ingot'},
        {input: 'copper_ore', result: 'copper_ingot'},
        {input: 'deepslate_copper_ore', result: 'copper_ingot'},
        {input: 'nether_gold_ore', result: 'gold_ingot'},
        {input: 'ancient_debris', result: 'netherite_scrap'},
        {input: 'sand', result: 'glass'},
        {input: 'red_sand', result: 'glass'},
        {input: 'cobblestone', result: 'stone'},
        {input: 'stone', result: 'smooth_stone'},
        {input: 'netherrack', result: 'nether_brick'},
        {input: 'clay_ball', result: 'brick'},
        {input: 'clay', result: 'terracotta'},
        {input: 'wet_sponge', result: 'sponge'},
        {input: 'cactus', result: 'green_dye'},
        {input: 'kelp', result: 'dried_kelp'},
        {input: 'oak_log', result: 'charcoal'},
        {input: 'oak_wood', result: 'charcoal'},
        {input: 'sandstone', result: 'smooth_sandstone'},
        {input: 'red_sandstone', result: 'smooth_red_sandstone'},
        {input: 'quartz_block', result: 'smooth_quartz'},
        {input: 'andesite', result: 'polished_andesite'},
        {input: 'granite', result: 'polished_granite'},
        {input: 'diorite', result: 'polished_diorite'},
        {input: 'blackstone', result: 'polished_blackstone'},
        {input: 'beef', result: 'cooked_beef'},
        {input: 'chicken', result: 'cooked_chicken'},
        {input: 'porkchop', result: 'cooked_porkchop'},
        {input: 'mutton', result: 'cooked_mutton'},
        {input: 'rabbit', result: 'cooked_rabbit'},
        {input: 'cod', result: 'cooked_cod'},
        {input: 'salmon', result: 'cooked_salmon'},
        {input: 'potato', result: 'baked_potato'},
        {input: 'chorus_fruit', result: 'popped_chorus_fruit'},
        {input: 'sea_pickle', result: 'lime_dye'}
    ];

    const smeltingRecipe = smeltingRecipes.find(recipe => registry.itemsByName[recipe.result]?.id === itemId);

    if (smeltingRecipe) {
        return {
            input_item: smeltingRecipe.input,
            output_count: 1
        };
    }
    return null;
}

function getMiningInfo(blockName) {
    const block = registry.blocksByName[blockName];
    if (!block) return null;

    let requiredTool = null;
    if (block.harvestTools) {
        const tools = Object.keys(block.harvestTools);
        if (tools.length > 0) {
            requiredTool = items[parseInt(tools[0])]?.name || null;
        }
    }

    let drops = null;
    // 修正掉落物获取逻辑:处理drops的正确结构
    if (block.drops) {
        // 优先获取方块对应的物品ID,而非直接取drops数组第一个值
        const blockItemId = block.id;
        drops = items[blockItemId]?.name || null;
        // 处理特殊掉落(比如矿石掉落对应锭的情况)
        if (block.name.includes('_ore') && !block.name.includes('nether_gold_ore')) {
            const oreType = block.name.replace('_ore', '').replace('deepslate_', '');
            drops = `${oreType}_ingot`;
        } else if (block.name === 'nether_gold_ore') {
            drops = 'gold_nugget';
        } else if (block.name === 'ancient_debris') {
            drops = 'ancient_debris';
        }
    }

    // 基岩不可挖掘
    const mineable = block.diggable && block.name !== 'bedrock';

    return {
        mineable: mineable,
        required_tool: requiredTool,
        drops: drops
    };
}

const itemData = {};

for (const itemKey in items) {
    const item = items[itemKey];
    const itemInfo = {
        craftable: getCraftingRecipes(item.id).length > 0,
        mineable: false,
        obtainable_by_killing: false,
        obtainable_by_smelting: false,
        crafting_recipes: getCraftingRecipes(item.id)
    };

    const smeltingRecipe = getSmeltingRecipe(item.id);
    if (smeltingRecipe) {
        itemInfo.obtainable_by_smelting = true;
        itemInfo.smelting_recipe = smeltingRecipe;
    }

    // 修正方块匹配逻辑:处理物品与方块名称的对应关系
    let block = registry.blocksByName[item.name];
    // 处理原木/木材的名称差异
    if (!block && item.name.includes('_log')) {
        block = registry.blocksByName[item.name];
    } else if (!block && item.name.includes('_wood')) {
        block = registry.blocksByName[item.name];
    }

    if (block) {
        const miningInfo = getMiningInfo(block.name);
        if (miningInfo) {
            itemInfo.mineable = miningInfo.mineable;
            itemInfo.required_tool = miningInfo.required_tool;
            itemInfo.drops = miningInfo.drops;
        }
    }

    itemData[item.name] = itemInfo;
}

fs.writeFileSync('items.json', JSON.stringify(itemData, null, 2));
console.log('items.json文件已生成。');

关键修复点

  1. 掉落物逻辑修正:
    • 不再直接使用block.drops[0],优先关联方块对应的物品ID,同时针对矿石等特殊方块做单独处理(比如铁矿石掉落铁锭、下界金矿石掉落金粒)。
    • 明确标记基岩为不可挖掘。
  2. 物品与方块匹配修正:
    • 优化block的查找逻辑,处理原木、木材等物品与方块名称的对应关系,避免匹配错误。
  3. 熔炉配方修正:
    • 将原脚本中模糊的log和wood替换为具体的oak_log和oak_wood,符合Minecraft实际配方。
  4. 空值安全处理:
    • 多处添加?.可选链操作符,避免因ID不存在导致的错误映射。

验证结果

修复后生成的items.json将正确显示:

  • 基岩:mineable: false,无掉落物
  • 丛林原木:mineable: true,掉落自身(jungle_log)
  • 各类矿石的掉落物与实际游戏一致

内容的提问来源于stack exchange,提问作者Yanis GANGNANT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:26:06