Bukkit中如何为ItemStack设置自定义ID以支持附魔物品告示牌商店功能
代码错误原因与修复方案
现有代码的核心错误点
- 恢复数据时错误调用
createSection:你在restoreData方法里用config.createSection("eco.sell.aFirstItem")会直接覆盖已存在的配置节点,导致之前保存的数据全部被清空,自然读不到有效数据,应该调用getConfigurationSection读取已有的节点。 - 附魔存储的key不匹配:保存时你把附魔列表存在
meta.enchant(单数),恢复时读取的是meta.enchants(复数),key完全不一致,会读取到空列表,附魔数据加载失败。 - 未把修改后的ItemMeta写回ItemStack:你获取ItemMeta修改了显示名称后,没有调用
item.setItemMeta(meta),修改的名称不会生效。 - 配置保存逻辑不完整:
save()方法仅在物品有ItemMeta时才会调用,无附魔、无自定义名称的普通物品数据不会被持久化到配置文件。 - 空指针风险:未校验附魔名称是否合法、字符串拆分结果是否符合预期、自定义名称是否为空,特殊情况下会触发空指针或数组越界。
- (可选兼容问题)
player.getItemInHand()是低版本API,高版本Bukkit/Spigot已废弃,建议替换为player.getInventory().getItemInMainHand()。
修复后的代码
保存物品代码
public static void saveItem(Player player){ ItemStack item = player.getInventory().getItemInMainHand(); // 先移除旧节点再创建新的,避免残留旧数据 config.set("eco.sell.aFirstItem", null); ConfigurationSection configToEdit = config.createSection("eco.sell.aFirstItem"); configToEdit.set("type", item.getType().name()); configToEdit.set("amount", item.getAmount()); if(item.hasItemMeta()){ ItemMeta meta = item.getItemMeta(); // 仅当有自定义名称时存储,避免存null if(meta.hasDisplayName()){ configToEdit.set("meta.name", meta.getDisplayName()); } List<String> enchants = new ArrayList<>(); meta.getEnchants().forEach((en, lvl) -> enchants.add(en.getName() + ":" + lvl)); configToEdit.set("meta.enchants", enchants); // 统一用复数key } // 不管有没有meta都保存配置 save(); }
恢复物品代码
public static void restoreData(Player player){ // 读取已有的配置节点,不要创建新节点 ConfigurationSection configToEdit = config.getConfigurationSection("eco.sell.aFirstItem"); if(configToEdit == null){ player.sendMessage("没有找到可恢复的物品数据"); return; } ItemStack item = new ItemStack(Material.valueOf(configToEdit.getString("type")), configToEdit.getInt("amount")); ItemMeta meta = item.getItemMeta(); if(configToEdit.contains("meta")) { player.sendMessage("Contains( Meta )"); // 仅当存储了名称时设置 if(configToEdit.contains("meta.name")){ meta.setDisplayName(configToEdit.getString("meta.name")); } List<String> enchants = configToEdit.getStringList("meta.enchants"); enchants.forEach(line -> { String[] split = line.split(":"); if(split.length < 2) return; Enchantment enchant = Enchantment.getByName(split[0]); if(enchant == null) return; int level; try { level = Integer.parseInt(split[1]); } catch (NumberFormatException e) { return; } meta.addEnchant(enchant, level, true); // 附魔加在meta上更规范,true表示绕过等级限制 }); } // 把修改后的meta写回物品 item.setItemMeta(meta); player.getInventory().addItem(item); player.updateInventory(); }
自定义物品ID的实现建议
不需要写入lore,更推荐用Spigot提供的PersistentDataContainer存储自定义ID,属于物品的隐藏属性,玩家看不到也无法普通修改,示例如下:
// 写入自定义ID NamespacedKey key = new NamespacedKey(你的插件实例, "custom_item_id"); meta.getPersistentDataContainer().set(key, PersistentDataType.STRING, "你的自定义ID"); // 读取自定义ID String customId = meta.getPersistentDataContainer().get(key, PersistentDataType.STRING);
如果是低于1.14的低版本服务端,才考虑用带特殊前缀的隐藏lore存储。
内容的提问来源于stack exchange,提问作者chaniloko
相关产品推荐
相关产品推荐

