注册表参数
Minecraft 中的注册表保存了各种信息 - 可能的物品或方块类型、附魔、药水效果等等!
有两种类型的注册表参数:resource 和 resourceKey。
这两个参数的主要区别在于返回值:resource 参数返回解析后的值,而 resourceKey 只返回一个 TypedKey,你可以用它
来自己检索值。
Resource 参数
就像任何其他参数一样,你可以使用 ArgumentTypes.resource(RegistryKey<T>) 获取对它的 ArgumentType<T> 引用。可以在下面找到可能的注册表键的选择。
它们通过使用 RegistryKey 接口在静态上下文中访问。
RegistryKey 中的每个条目都返回一个 RegistryKey<T>。这里的 <T> 泛型参数描述了返回类型。这意味着如果我们要检索
RegistryKey.ITEM,返回类型将是 ItemType,因为它的定义如下:
public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
// ...
RegistryKey<ItemType> ITEM = RegistryKeyImpl.create("item");
// ...
}
实际上,就是这么简单。正是因为这个原因,这里有一个这样参数的实现示例:
public static LiteralCommandNode<CommandSourceStack> enchantmentRegistry() {
return Commands.literal("enchants-registry")
.then(Commands.argument("enchantment", ArgumentTypes.resource(RegistryKey.ENCHANTMENT))
.executes(ctx -> {
final Enchantment enchantment = ctx.getArgument("enchantment", Enchantment.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
final ItemStack stack = player.getInventory().getItemInMainHand();
stack.addUnsafeEnchantment(enchantment, 10);
ctx.getSource().getSender().sendRichMessage("已为 <player> 的 <item> 添加 <enchantment>!",
Placeholder.component("player", player.name()),
Placeholder.component("item", Component.translatable(stack)),
Placeholder.component("enchantment", enchantment.displayName(10))
);
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendRichMessage("<red>此命令需要一个玩家!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
我们使用附魔注册表键资源定义一个 enchantment 参数,并使用 ctx.getArgument("enchantment", Enchantment.class) 检索该值。
最后,我们用发送者选择的任何附魔为玩家手中的物品附魔,等级为 10,并发送一条成功消息。
它在游戏中的效果如下:
在某些边缘情况下,由于客户端缺少注册表,此参数会导致网络协议错误。
基本上,目前唯一出现这种情况的参数是 STRUCTURE 注册表键。
// 注册此命令将导致客户端无法连接到服务器。
final LiteralCommandNode<CommandSourceStack> invalidRegistryArgument = Commands.literal("registry-structure")
.then(Commands.argument("value", ArgumentTypes.resource(RegistryKey.STRUCTURE)))
.build();
由于这个事实,建议只使用带有 resourceKey(...) 参数类型的 STRUCTURE 注册表键,并自己解析值。
Resource key 参数
对于客户端来说,使用 ArgumentTypes.resource 或 ArgumentTypes.resourceKey 几乎没有区别。唯一的区别是
使用 ArgumentTypes.resourceKey 不提供错误检查。我们可以使用 RegistryKey.ITEM 来可视化这一点。
这是使用 ArgumentTypes.resource(RegistryKey.ITEM) 时的 tab 补全:
这是使用 ArgumentTypes.resourceKey(RegistryKey.ITEM) 时的 tab 补全:
在上面给出的示例中,由于未处理的空指针异常,命令没有成功运行。该命令的代码直接尝试使用注册表访问检索的值
通过执行 ItemType item = RegistryAccess.registryAccess().getRegistry(itemKey.registryKey()).get(itemKey.key())。如果你尝试对结果进行任何
操作,它可能为空并出错。
你应该始终检查注册表检索操作的结果。下面的直接代码比较中给出了一个示例。
resource 参数提供了更好的用户体验,而 resourceKey 参数有一个非常重要的用例:你得到原始的
TypedKey<T> 作为参数结果返回。这个对象特别有用,因为它提供了从注册表中检索
值所需的所有信息。
除非你有特定的理由使用 resourceKey 参数而不是 resource 参数,否则由于客户端错误检查和简单的可用性,推荐使用 resource 参数。
直接代码比较
这是一个使用 RegistryKey.ITEM 注册表和 resource 参数类型的简单代码片段:
Commands.argument("item", ArgumentTypes.resource(RegistryKey.ITEM))
.executes(ctx -> {
final ItemType item = ctx.getArgument("item", ItemType.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getInventory().addItem(item.createItemStack());
}
return Command.SINGLE_SUCCESS;
});
这是相同的代码,使用 resourceKey 参数类型。我们不直接使用 ctx.getArgument("item", TypedKey.class) 检索参数,而是使用
RegistryArgumentExtractor 来检索我们的 TypedKey<ItemType>。
Commands.argument("item", ArgumentTypes.resourceKey(RegistryKey.ITEM))
.executes(ctx -> {
final TypedKey<ItemType> itemKey = RegistryArgumentExtractor.getTypedKey(ctx, RegistryKey.ITEM, "item");
ItemType item = RegistryAccess.registryAccess().getRegistry(itemKey.registryKey()).get(itemKey.key());
if (item == null) {
ctx.getSource().getSender().sendRichMessage("<red>请提供一个有效的物品!");
return Command.SINGLE_SUCCESS;
}
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getInventory().addItem(item.createItemStack());
}
return Command.SINGLE_SUCCESS;
})
使用 TypedKey
首先,为了获取正确的注册表,你可以运行 RegistryAccess#getRegistry(RegistryKey)。要获取 RegistryAccess,你可以直接使用静态的
RegistryAccess.registryAccess() 方法。RegistryKey 可以使用 TypedKey#registryKey() 检索。
现在,要获取最终的值 T,你可以运行 Registry#get(Key),其中的键可以使用 TypedKey#key() 检索。这将返回该资源键的后备实例
或 null(如果未找到值)。
相对于 resource 参数的用例
此参数类型的主要用例是能够存储键(由 TypedKey#key 返回给你的值)。如果你想能够存储确切的用户
输入并且能够在不费太多力气的情况下检索后备实例,那就是这样做的方法。
注册表键预览
在编写时,存在以下 RegistryKeys:
| RegistryKeys 字段 | 返回值 | 预览视频 |
|---|---|---|
| ATTRIBUTE | Attribute | Attribute |
| BANNER_PATTERN | PatternType | Banner Pattern |
| BIOME | Biome | Biome |
| BLOCK | BlockType | Block |
| CAT_VARIANT | Cat.Type | Cat Variant |
| DAMAGE_TYPE | DamageType | Damage Type |
| DATA_COMPONENT_TYPE | DataComponentType | Data Component Type |
| ENCHANTMENT | Enchantment | Enchantment |
| ENTITY_TYPE | EntityType | Entity Type |
| FLUID | Fluid | Fluid |
| FROG_VARIANT | Frog.Variant | Frog Variant |
| GAME_EVENT | GameEvent | Game Event |
| INSTRUMENT | MusicInstrument | Instrument |
| ITEM | ItemType | Item |
| JUKEBOX_SONG | JukeboxSong | Jukebox Song |
| MAP_DECORATION_TYPE | MapCursor.Type | Map Decoration Type |
| MEMORY_MODULE_TYPE | MemoryKey<?> | Memory Module Type |
| MENU | MenuType | Menu |
| MOB_EFFECT | PotionEffectType | Mob effect |
| PAINTING_VARIANT | Art | Painting variant |
| PARTICLE_TYPE | Particle | Particle |
| POTION | PotionType | Potion |
| SOUND_EVENT | Sound | Sound |
| STRUCTURE | Structure | Structure |
| STRUCTURE_TYPE | StructureType | Structure Type |
| TRIM_MATERIAL | TrimMaterial | Trim Material |
| TRIM_PATTERN | TrimPattern | Trim Pattern |
| VILLAGER_PROFESSION | Villager.Profession | Villager Profession |
| VILLAGER_TYPE | Villager.Type | Villager Type |
| WOLF_VARIANT | Wolf.Variant | Wolf Variant |
Attribute
Banner Pattern
Biome
Block
Cat variant
Damage type
Enchantment
Entity type
Data component type
Fluid
Frog variant
Game event
Instrument
Item
Jukebox Song
Map decoration type
Memory module type
Menu
Mob effect
Painting variant
Particle
Potion
Sound
Structure
这个参数会踢出客户端,所以没有预览 ¯\_(ツ)_/¯