跳到主要内容

注册表

实验性

注册表 API 及其使用的任何内容目前都是实验性的,未来可能会发生变化。

什么是注册表?​

在 Minecraft 的上下文中,注册表持有一组相同类型的值,每个值都由一个键标识。 这样的注册表的一个例子是 物品类型注册表,它持有所有已知的物品类型。 注册表可以通过 RegistryAccess 类访问。

虽然大部分注册表是由服务器和客户端独立定义的,但越来越多的注册表是由服务器定义并在客户端加入服务器时发送给客户端的。 这使得服务器,以及插件,能够为自己和在其上玩的客户端定义自定义内容。 值得注意的例子包括附魔和生物群系。

从注册表中检索值​

要从注册表中检索元素,可以使用它们各自的键。 API 定义了两种类型的键。

  1. net.kyori.adventure.key.Key 表示一个命名空间和一个键。

  2. TypedKey 包装了一个 Adventure 键, 但也包含了 注册表的键, 这个 TypedKey 属于该注册表。

使用 TypedKeys 检索 锋利 附魔的示例如下:

// 从注册表访问中获取附魔注册表
final Registry<Enchantment> enchantmentRegistry = RegistryAccess
.registryAccess()
.getRegistry(RegistryKey.ENCHANTMENT);

// 使用其键获取锋利附魔。
// 如果注册表可能不包含该值,getOrThrow 可以替换为 get
final Enchantment enchantment = enchantmentRegistry.getOrThrow(TypedKey.create(
RegistryKey.ENCHANTMENT, Key.key("minecraft:sharpness"))
);

// 与上面相同,但使用实例的方法
final Enchantment enchantment = enchantmentRegistry.getOrThrow(
RegistryKey.ENCHANTMENT.typedKey(Key.key("minecraft:sharpness"))
);

// 与上面相同,但使用生成的 create 方法
// 适用于数据驱动的注册表或"可写"的注册表
// (那些在 RegistryEvents 中绑定到生命周期事件的注册表)。
final Enchantment enchantment = enchantmentRegistry.getOrThrow(
EnchantmentKeys.create(Key.key("minecraft:sharpness"))
);

// 也与上面相同,但使用生成的类型化键。
// 只有原版条目有生成的键,对于自定义条目,必须使用上面的方法。
final Enchantment enchantment = enchantmentRegistry.getOrThrow(EnchantmentKeys.SHARPNESS);

引用注册表值​

引用注册表中的条目说起来容易做起来难。 虽然在大多数情况下,值的普通 Collection 可能就足够了,但 Minecraft 更常用替代方法, 因此你会遇到这些方法。

RegistrySet 定义了一个 与注册表相关的元素集合。

它最常见的子类型是 RegistryKeySet, 它只持有 TypedKey 实例。 这种数据结构的一个优点是,即使注册表的值发生变化,它也能保持有效。

可以通过 RegistrySet 上的工厂方法创建 RegistryKeySet,像这样:

// 创建一个新的注册表键集,持有一个附魔集合
final RegistryKeySet<Enchantment> bestEnchantments = RegistrySet.keySet(
RegistryKey.ENCHANTMENT,
// 要存储在键集中的任意附魔键。
EnchantmentKeys.CHANNELING,
EnchantmentKeys.create(Key.key("papermc:softspoon"))
);

Tag 延续了 RegistryKeySet 的概念, 但它本身是命名的,因此可以被引用。 原版标签的列表可以在 Minecraft Wiki 上找到。

修改注册表​

除了对注册表的纯读取访问外,Paper 还为插件提供了修改注册表的方式。

注意

修改注册表需要在服务器的引导阶段完成。 因此,本节仅适用于 Paper 插件。

在此阶段抛出的异常将导致服务器在加载前关闭, 因为注册表中缺少值或修改可能会导致数据丢失。

备注

修改注册表是通过 LifecycleEventManager 完成的。 有关更多信息,请参见生命周期事件页面。

修改注册表的一般入口点是 RegistryEvents 类型, 它为每个可以修改的注册表提供了一个入口点。 修改注册表可以采取两种不同的形式。

创建新条目​

通过各自注册表上的 freeze 生命周期事件 来创建新条目。 冻结事件在注册表的内容被冻结之前调用,这意味着所有原版条目都已注册。 插件可以在这一点上注册它们自己的条目。 以下示例展示了如何创建一个新的附魔:

TestPluginBootstrap.java
public class TestPluginBootstrap implements PluginBootstrap {

@Override
public void bootstrap(BootstrapContext context) {
// 在附魔注册表上为冻结生命周期事件注册一个新的处理器
context.getLifecycleManager().registerEventHandler(RegistryEvents.ENCHANTMENT.freeze().newHandler(event -> {
event.registry().register(
// 注册表的键
// 插件应该使用它们自己的命名空间而不是 minecraft 或 papermc
EnchantmentKeys.create(Key.key("papermc:pointy")),
b -> b.description(Component.text("尖锐"))
.supportedItems(event.getOrCreateTag(ItemTypeTagKeys.SWORDS))
.anvilCost(1)
.maxLevel(25)
.weight(10)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(1, 1))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(3, 1))
.activeSlots(EquipmentSlotGroup.ANY)
);
}));
}
}

修改现有条目​

修改现有条目对于旨在改变原版条目行为的插件很有用。为此,使用 entryAdd 生命周期事件。 该事件在向注册表添加_*任何*_条目时调用,但 API 提供了一种简单的方式来针对特定条目进行修改。 以下示例展示了如何增加 锋利 附魔的最大等级。

@Override
public void bootstrap(BootstrapContext context) {
context.getLifecycleManager().registerEventHandler(RegistryEvents.ENCHANTMENT.entryAdd()
// 将最大等级增加到 20
.newHandler(event -> event.builder().maxLevel(20))
// 配置处理器仅在原版锋利附魔时调用。
.filter(EnchantmentKeys.SHARPNESS)
);
}