Table of Contents

注册表介绍

Minecraft 使用注册表系统处理游戏内的几乎一些,作为模组开发者,需要将添加到游戏的绝大多数内容注册进去,这有利于:

注册任何类型内容时,你传入一个 Identifier 对象,也就是你需要加入的内容的标识符。标识符,简称 ID,通常拥有一个命名空间(namespace)和路径(path)。大多数情况下,命名空间是你的模组的 ID(见 terms),路径则是你要注册的内容的(英文)名称。命名空间和路径不能包含大写字母或其他语言(包括中文)的字符。例如,原版的泥土方块的 ID 为 minecraft:dirt

不注册就使用自定义内容容易导致 bug,比如缺失纹理、世界保存问题和崩溃。游戏通常会让你知道你是否忘记注册了某个东西。

注册表类型

注册内容时,你需要指定将内容加入哪个注册表。基本的游戏提供所有原版内容的注册表,可以在 Registries(1.19.3 以上)或 Registry(1.19.2 以下)中找到。

例如,你很有可能会使用用于物品的 Registries.ITEM(1.19.3 以上)/Registry.ITEM(1.19.2 以下)和用于方块的 Registries.BLOCK(1.19.3 以上)/Registry.BLOCK(1.19.2 以下)。

如需详细了解所有可用的注册表,请看 RegistriesRegistry类。registry types 也有各注册表类型的简要描述。

基本用法

注册你的内容

使用 Registry.register 将你的内容注册到注册表。记住,每个内容不能注册超过 1 次。基本的语法是:

    Registry.register(registry, id, content);

例如:

    Registry.register(Registries.ITEM, Identifier.of("tutorial", "example_item", EXAMPLE_ITEM));
记住:只能在模组初始化器中注册。否则,会因为注册表冻结而无法注册。

通过 ID 获取对象

get 返回注册表内与 ID 关联的内容。如果内容不存在,SimpleDefaultedRegistry 会返回默认的注册表值,而 SimpleRegistry 就返回 null。可以使用 containsIdgetOrEmpty 方法检测 ID 是否存在,例如:

    Registries.ITEM.containsId(Identifier.ofVanilla("diamond")); // 返回 true
 
    Registries.ITEM.containsId(Identifier.ofVanilla("invalid_name")); // 返回 false
 
    Registries.ITEM.get(Identifier.ofVanilla("diamond")); // 返回 Items.DIAMOND
 
    Registries.ITEM.get(Identifier.ofVanilla("invalid_name")); // 返回 Items.AIR
 
    Registries.ITEM.getOrEmpty(Identifier.ofVanilla("diamond")); // 返回 Optional.of(Items.DIAMOND)
 
    Registries.ITEM.getOrEmpty(Identifier.ofVanilla("invalid_name")); // 返回 Optional.empty()

获取对象的 ID

getId 返回注册表内与对象关联的 Identifier,如果这个项不存在, SimpleDefaultedRegistry 返回默认的注册表 ID,SimpleRegistry 返回 null。例如:

    Registries.BLOCK.getId(Blocks.STONE);  // 返回 Identifier.ofVanilla("stone"))

相关概念

注册表键

有些类型的注册表不是静态存储在 Registries 类中的。例如,你无法在 Registries 类中找到生物群系的注册表或战利品表的注册表。但是这些注册表在 Minecraft 中确实存在,这是因为这些注册表是动态的:在不同世界之间加载,因为是由数据包定义。相比之下,静态的注册表,例如 Registries.ITEMRegitries.BLOCK,还不是由数据包定义的,所以不管在哪个世界都保持不变。

但是,每个注册表都有个注册表键RegistryKey),可以在 RegistrKeys 类中找到。可以通过 RegistryWrapper.WrapperLookup(如果有 World 对象,通常是 world.getRegistryManager,注册命令时则通常是 CommandRegistryAccess)对象根据注册表键找到注册表。参见下面的例子,假设你有 World 对象。

    final DynamicRegistryManager registryManager = world.getRegistryManager();
 
    // 下面两个语句都返回当前世界的沙漠的 ''Biome'' 对象
    registryManager.get(RegistryKeys.BIOME).get(Identifier.ofVanilla("desert"));
    registryManager.getWrapperOrThrow(RegistryKeys.BIOME).getOrThrow(RegistryKey.of(RegistryKeys.BIOME, Identifier.ofVanilla("desert"))).value();

不仅注册表有注册表键。所有的注册表内容都有注册表键(因为注册表本身也属于 Registries.REGISTRIES 的注册表内容)。注册表内容在不同世界之间可能改变,但是注册表键是不会改变的。

注册表项

注册表项RegistryEntry)是属于有注册表的类型的内容。有两种类型:

看看下面两个命令,以清晰地展示出了两者的区别:

两个命令都会给你一个石头。前者会从世界的战利品表注册表中获取战利品表 minecraft:blocks/stone,所以是 RegistryEntry.Reference。后者会从 SNBT 中解码一个战利品表. 没有 ID,所以是 RegistryEntry.Direct。两种类型的注册表项都可以通过调用 value() 方法来获取其实际的内容。

注册表项列表

和注册表项类似,注册表项列表RegistryEntryList)用得很多,也有两种类型: