====== 注册表介绍 ====== Minecraft 使用**注册表系统**处理游戏内的几乎一些,作为模组开发者,需要将添加到游戏的绝大多数内容注册进去,这有利于: * 让游戏知道你的内容存在 * 在客户端和服务器之间验证游戏内容 * 存储时处理无效内容 * 避免不同模组之间的冲突 * 利于客户端和服务器之间的沟通和数据储存 * 抽象化隐藏数字 ID 注册任何类型内容时,你传入一个 ''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 以下)。 如需详细了解所有可用的注册表,请看 ''Registries'' 或 ''Registry''类。[[registry types]] 也有各注册表类型的简要描述。 ===== 基本用法 ===== ==== 注册你的内容 ==== 使用 ''.'' 将你的内容注册到注册表。记住,每个内容不能注册超过 1 次。基本的语法是: Registry.register(registry, id, content); * **registry** - 你需要添加内容进的注册表的实例。 * **id** - 你的内容在注册表内的 ID。 * **content** - 你需要注册的内容的实例。 例如: Registry.register(Registries.ITEM, Identifier.of("tutorial", "example_item", EXAMPLE_ITEM)); > 记住:只能在模组初始化器中注册。否则,会因为注册表冻结而无法注册。 ==== 通过 ID 获取对象 ==== '''' 返回注册表内与 ID 关联的内容。如果内容不存在,'''' 会返回默认的注册表值,而 '''' 就返回 null。可以使用 ''containsId'' 或 ''getOrEmpty'' 方法检测 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 ==== '''' 返回注册表内与对象关联的 '''',如果这个项不存在, '''' 返回默认的注册表 ID,'''' 返回 null。例如: Registries.BLOCK.getId(Blocks.STONE); // 返回 Identifier.ofVanilla("stone")) ===== 相关概念 ===== ==== 注册表键 ===== 有些类型的注册表不是静态存储在 ''Registries'' 类中的。例如,你无法在 ''Registries'' 类中找到生物群系的注册表或战利品表的注册表。但是这些注册表在 Minecraft 中确实存在,这是因为这些注册表是//动态//的:在不同世界之间加载,因为是由数据包定义。相比之下,//静态//的注册表,例如 ''Registries.ITEM'' 和 ''Regitries.BLOCK'',还不是由数据包定义的,所以不管在哪个世界都保持不变。 但是,每个注册表都有个**注册表键**(''RegistryKey''),可以在 ''RegistrKeys'' 类中找到。可以通过 ''RegistryWrapper.WrapperLookup''(如果有 ''World'' 对象,通常是 ''world.getRegistryManager'',注册[[commands|命令]]时则通常是 ''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'')是属于有注册表的类型的内容。有两种类型: * ''RegistryEntry.Reference'':在注册表内注册的东西,因此有 ID。 * ''RegistryEntry.Direct'':不在注册表内注册的东西,可能是通过某种方式匿名指定的。 看看下面两个命令,以清晰地展示出了两者的区别: * ''/loot give @s loot minecraft:blocks/stone'' * ''%%/loot give @s loot {pools:[{rolls:1, entries:[{type:"item", name:"minecraft:stone"}]}]}%%'' 两个命令都会给你一个石头。前者会从世界的战利品表注册表中获取战利品表 ''minecraft:blocks/stone'',所以是 ''RegistryEntry.Reference''。后者会从 SNBT 中解码一个战利品表. 没有 ID,所以是 ''RegistryEntry.Direct''。两种类型的注册表项都可以通过调用 ''value()'' 方法来获取其实际的内容。 ==== 注册表项列表==== 和注册表项类似,**注册表项列表**(''RegistryEntryList'')用得很多,也有两种类型: * ''RegistryEntryList.Named'':通过由数据包定义的[[tags|标签]]定义的列表。 * ''RegistryEntryList.Direct'':直接通过列举出所有内容定义的列表。