使用PrismarineJS生成Minecraft物品数据的错误排查与修复
修复Minecraft物品数据生成脚本的ID匹配错误
问题背景
我正在做一个涉及自动账号的Minecraft项目,需要让机器人识别物品的获取方式(暂不考虑村民交易等高级方式)。用JavaScript生成记录所有物品及其获取方式的items.json时出现错误:比如基岩被标记为可开采且掉落竹子方块,丛林原木掉落黄色羊毛。
问题分析
核心问题是方块ID与物品ID的映射不匹配,导致在获取方块掉落物和可开采性时关联了错误的数据。具体来看:
getMiningInfo函数中直接使用block.drops[0]作为物品ID,但部分方块的掉落物ID并非直接对应物品ID,或drops数组结构并非简单的ID值。- 遍历
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文件已生成。');
关键修复点
- 掉落物逻辑修正:
- 不再直接使用
block.drops[0],优先关联方块对应的物品ID,同时针对矿石等特殊方块做单独处理(比如铁矿石掉落铁锭、下界金矿石掉落金粒)。 - 明确标记基岩为不可挖掘。
- 不再直接使用
- 物品与方块匹配修正:
- 优化
block的查找逻辑,处理原木、木材等物品与方块名称的对应关系,避免匹配错误。
- 优化
- 熔炉配方修正:
- 将原脚本中模糊的
log和wood替换为具体的oak_log和oak_wood,符合Minecraft实际配方。
- 将原脚本中模糊的
- 空值安全处理:
- 多处添加
?.可选链操作符,避免因ID不存在导致的错误映射。
- 多处添加
验证结果
修复后生成的items.json将正确显示:
- 基岩:
mineable: false,无掉落物 - 丛林原木:
mineable: true,掉落自身(jungle_log) - 各类矿石的掉落物与实际游戏一致
内容的提问来源于stack exchange,提问作者Yanis GANGNANT
相关产品推荐
相关产品推荐

