数据组件API
数据组件API目前是实验性的,并且可能在不同版本之间发生变化。
数据组件API提供了一个版本特定的接口,用于访问和操作ItemMeta API无法表示的物品数据。
通过这个API,你可以以稳定和面向对象的方式读取和修改物品的属性(即数据组件)。
介绍
什么是数据组件?
数据组件表示与物品相关的一段数据。原版物品可以具有诸如自定义模型数据、容器战利品内容、旗帜图案或药水效果等属性。
结构
有关实现细节,点击这里。
原型(默认值)
物品带有一组初始组件,我们称之为原型。
这些组件定义在ItemStack的ItemType上。它们控制物品的基本行为,
代表一个没有任何修改的全新物品。
原型赋予物品其初始属性,例如它们是食物、工具、武器等。
补丁
补丁表示对物品所做的修改。这可能包括给它一个自定义名称、 修改附魔、损坏它或添加说明文字。补丁应用在原型之上, 允许我们对物品进行修改。
补丁还允许移除之前在原型中的组件。这通过红色显示的
minecraft:tool示例说明。我们正在移除这个组件,所以这个剑物品将不再
更快地破坏蜘蛛网或其他剑类方块。
我们还可以添加新组件,如新的minecraft:enchantment_glint_override组件所示,它
允许我们使其看起来像是被附魔了。
与ItemMeta的区别
ItemMeta API以分层方式提供修改ItemStack的方法,例如CompassMeta,它允许你修改minecraft:compass的组件。
虽然ItemMeta仍然非常有用,但它不能正确表示Minecraft物品使用的原型/补丁关系。
主要区别
扩展的数据模型
数据组件API暴露了比ItemMeta更广泛和更详细的物品属性集。
数据组件允许以更好地表示Minecraft如何进行物品修改的方式修改整个物品。
版本特定
数据组件API设计为适应版本变化。当Minecraft对组件进行更改时,数据组件API可能在版本更新时发生破坏性变化。 不保证向后兼容性。
因为ItemMeta以不同的格式表示,Mojang对组件所做的破坏性更改可能不会导致对ItemMeta的破坏性更改。
构建器和不可变性
许多复杂的数据组件需要使用构建器方法进行构造和编辑。API返回的所有数据类型也都是不可变的,所以它们不会直接修改组件。
仅补丁
ItemMeta只表示ItemStack的补丁。这意味着你无法获取ItemStack的原始属性(原型),例如其默认
耐久度或默认属性。
无快照
目前,ItemMeta表示ItemStack补丁映射的快照。
这很昂贵,因为它需要读取整个补丁,即使你可能不使用某些值。
数据组件API直接与ItemStack集成。虽然概念上类似,但数据组件API专注于显式、强类型的数据检索和更新,而没有这种额外开销。
我应该什么时候使用DataComponents或ItemMeta?
如果你:
- 只对
ItemStack进行简单更改 - 想要保持插件的跨版本兼容性
那么你应该使用
ItemMeta。
如果你:
- 想要对
ItemStack进行更复杂的修改 - 不关心跨版本兼容性
- 想要访问默认(原型)值
- 想要从
ItemStack的原型中移除组件 那么你应该使用数据组件。
基本用法
数据组件API将根据游戏中看到的行为获取值。所以,如果补丁移除了minecraft:tool组件,
尝试获取该组件将返回null。
检索原型值
// 获取钻石剑的默认耐久度
int defaultDurability = Material.DIAMOND_SWORD.getDefaultData(DataComponentTypes.MAX_DAMAGE)
检查数据组件
// 检查这个物品是否有自定义名称数据组件
boolean hasCustomName = stack.hasData(DataComponentTypes.CUSTOM_NAME);
logger.info("有自定义名称吗? " + hasCustomName);
读取有值的数据组件
// 物品的损坏值可以为null,所以我们需要空值检查
Integer damageValue = stack.getData(DataComponentTypes.DAMAGE);
if (damageValue != null) {
logger.info("当前损坏值: " + damageValue);
} else {
logger.info("这个物品没有设置损坏组件。");
}
// 某些组件,如最大堆叠数,在物品上总是存在
Integer maxStackSize = stack.getData(DataComponentTypes.MAX_STACK_SIZE);
设置有值的数据组件
// 在这个物品上设置自定义模型数据值
stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
.addFloat(0.5f)
.addFlag(true)
.build()
);
移除或重置数据组件
// 移除现有组件(例如工具)
stack.unsetData(DataComponentTypes.TOOL);
// 将组件重置为其物品类型的默认(原型)值(例如最大堆叠数)
stack.resetData(DataComponentTypes.MAX_STACK_SIZE);
无值数据组件
某些组件只是标志,不携带任何类型的值:
// 使物品成为可以像鞘翅一样使用的滑翔器(与可装备组件结合)
stack.setData(DataComponentTypes.GLIDER);
// 移除滑翔器标志
stack.unsetData(DataComponentTypes.GLIDER);
使用构建器的高级用法
许多数据组件具有需要构建器的复杂结构。
修改原型组件值
ItemStack helmet = ItemStack.of(Material.DIAMOND_HELMET);
// 获取这个物品的可装备组件,并使其成为构建器。
// 注意:不是所有类型都有.toBuilder()方法
// 这是钻石头盔的原型值。
Equippable.Builder builder = helmet.getData(DataComponentTypes.EQUIPPABLE).toBuilder();
// 让头盔看起来像下界合金头盔
// 我们从NETHERITE_HELMET获取原型可装备值
builder.assetId(Material.NETHERITE_HELMET.getDefaultData(DataComponentTypes.EQUIPPABLE).assetId());
// 并在穿戴时给它一个恐怖的声音
builder.equipSound(SoundEventKeys.ENTITY_GHAST_HURT);
// 设置我们的新物品
helmet.setData(DataComponentTypes.EQUIPPABLE, builder);
这将创建一个看起来像下界合金头盔并在装备时播放恐怖恶魂声音的钻石头盔。
示例:成书
ItemStack book = ItemStack.of(Material.WRITTEN_BOOK);
WrittenBookContent.Builder builder = WrittenBookContent.writtenBookContent("我的书", "作者名");
// 添加一页
builder.addPage(Component.text("这是新的一页!"));
// 添加一页,对开启了脏话过滤的人显示不同内容
// 禁用过滤的玩家将看到"我讨厌Paper!",而开启过滤的人将看到"我爱Paper!"。
builder.addFilteredPage(
Filtered.of(Component.text("我讨厌Paper!"), Component.text("我爱Paper!"))
);
// 更改代数
builder.generation(1);
// 应用更改
book.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, builder.build());
示例:酷炫的剑
ItemStack sword = ItemStack.of(Material.DIAMOND_SWORD);
sword.setData(DataComponentTypes.LORE, ItemLore.lore().addLine(Component.text("酷炫的剑!")).build());
sword.setData(DataComponentTypes.ENCHANTMENTS, ItemEnchantments.itemEnchantments().add(Enchantment.SHARPNESS, 10).build());
sword.setData(DataComponentTypes.RARITY, ItemRarity.RARE);
sword.unsetData(DataComponentTypes.TOOL); // 移除工具组件
sword.setData(DataComponentTypes.MAX_DAMAGE, 10);
sword.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); // 让它发光!
匹配不带某些数据组件的物品
在比较物品时,你有时想要忽略某些值。为此,我们可以使用
ItemStack#matchesWithoutData
方法。
例如,这里我们比较两把钻石剑,同时忽略它们的耐久度:
ItemStack originalSword = ItemStack.of(Material.DIAMOND_SWORD);
ItemStack damagedSword = ItemStack.of(Material.DIAMOND_SWORD);
damagedSword.setData(DataComponentTypes.DAMAGE, 100);
boolean match = damagedSword.matchesWithoutData(originalSword, Set.of(DataComponentTypes.DAMAGE), false);
logger.info("这些剑匹配吗? " + match); // -> true