Forge1.8文档——核心概念(注册)

2 核心概念

2.1注册
2.1.1 注册介绍

注册是把Mod中的对象(物品、方块、音乐等)推送到游戏中,并让游戏知道这个对象。如果没有注册,那么游戏根本不知道这些物体,这将会导致游戏出现错误和崩溃。
大部分需要在游戏中注册的事情都是由Forge注册中心处理的。注册表是一个类似于键值对的映射对象。Forge使用带有ResourceLocation密匙的注册表来注册对象。这允许ResourceLocation充当对象的"注册名称"。注册表名称可由"#geRegistryName /#setRegistryName"对象访问。setter方法只能调用一次;调用两次会导致异常。

每种类型的可注册对象都有自己的注册表。要查看Forge支持的所有注册表,请参阅ForgeRegistries类。注册表中的所有注册表名称都必须是唯一的。但是,不同注册表中的名称不一致是没有影响的。比如一个是Block注册表和一个Item注册表。Block和Item中可以用相同的名称进行注册。如果使用相同的注册表名称在同一个注册表进行注册,会导致后面一个会覆盖前面一个。

2.1.2 注册方法

目前有两种方式来注册对象:DefferredRegister类和RegistryEvent.register的生命周期事件。

2.1.2.1 DefferredRegister方式

DeferredRegister是一种新的、有记录的方式去注册对象。它允许使用静态初始化,同时避免相关的问题。它仅仅需要维护注册对象的列表,并且在合适的RegistryEvent$Register事件期间(Mod初始化时)注册这些对象。
Mod注册自定义方块的示例:

package com.zw.yaotu.common;

public interface Common {

    //ModId
    public static final String MODID = "yaotu";
}
package com.zw.yaotu.register;

import com.zw.yaotu.common.Common;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Material;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;

public class RegisterBlock {

    //获取所有的自定义方块

    //定义BLOCKS 注册表
    public DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Common.MODID);

    //将方块注册到定义BLOCKS注册表中
    private RegistryObject<Block> ROCK_BLOCK = BLOCKS.register("rock", () -> new Block(BlockBehaviour.Properties.of(Material.STONE)));
}
package com.zw.yaotu;

import com.zw.yaotu.block.BaseYaoBlock;
import com.zw.yaotu.register.RegisterBlock;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;


import java.util.stream.Collectors;

//这个值应当与META-INF/mods.toml文件中的modId值相同
@Mod("yaotu")
public class YaoTuMod
{

    RegisterBlock registerBlock = new RegisterBlock();
    
    public YaoTuMod()
    {

        //注册自定义方块
        registerBlock.BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
    }
}
2.1.2.2 Register events 方法

RegistryEvents是注册对象的第二种方式,这种方式更加的灵活。这些事件是在Mod初始化(构造函数)之后和加载配置文件(configs)之前触发。
用于注册对象的事件是RegistryEvent.register.类型参数T应设置为要注册的对象类型。调用getRegistry()方法将返回注册表,在该注册表上使用register
()或registerAll()注册对象。
例子:

@SubscribeEvent
public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent)
{
    //定义自定义方块
    Block blockYao1 = new Block(BlockBehaviour.Properties.of(Material.STONE));
    Block blockYao2 = new Block(BlockBehaviour.Properties.of(Material.STONE));

    //方式一  一次性全部注册
    blockRegistryEvent.getRegistry().registerAll(blockYao1,blockYao2);
    
    //方式二  一个一个注册
    //blockRegistryEvent.getRegistry().register(blockYao1);
    //blockRegistryEvent.getRegistry().register(blockYao2);
    
    LOGGER.info("注册Mod方块完成");
}
2.1.2.3 非Forge注册表的注册表

由于代码的一些特性,并不是所有的注册表都是由Forge封装的。这些注册可以是静态注册表,如RecipeType,这样使用起来很安全。还有些为动态注册表,如ConfiguredFeature、worldgen注册表,它们是以JSON表示。只有当有另一个注册对象需要时,这些对象就应该以动态注册的方式进行。DeferredRegister.create()有一个重载,它被允许使用注册表的Key来创建一个RegistObject。这个注册方法和附加到Mod事件总线的方法与其他的DeferredRegister相同。
例子

//由于RecipeType是一个接口,将创建一个匿名类进行注册
//出于调试目的,RecipeTypes重写了toString方法,因此在本例中省略了它
private static final DeferredRegister<RecipeType<?>> REGISTER = DeferredRegister.create(Registry.RECIPE_TYPE_REGISTRY, Common.MODID);


//这个注册表为Mod配方_丹药
public static final RegistryObject<RecipeType<RecipeDanYao>> RECIPE_TYPE = REGISTER.register("recipe_type", () -> new RecipeType<>() {
});

笔记:
有些类本身是无法注册的。相反,*Type类被注册,并在formers的构造函数中使用。例如,BlockEntity具有BlockEntityType,Entity具有EntityType。这些
Type类是工厂,他们只是根据需要的创建包含的类型。 这些工厂是通过使用它们的Type类中的Builder()来创建的。例如:REGISTER指的是DeferredRegister

//暂未实验出来
public static final RegistryObject<BlockEntityType<ExampleBlockEntity>> EXAMPLE_BLOCK_ENTITY = REGISTER.register(
  "example_block_entity", () -> BlockEntityType.Builder.of(ExampleBlockEntity::new, EXAMPLE_BLOCK.get()).build(null)
);
2.1.3 注册对象的引用

已注册的对象在创建和注册时不应被保存在字段中。而是在每次触发各自的Registry.Register()事件时被重新创建和注册。这样做是为了在未来版本的Forge中动态加载和卸载Mod。
已注册的对象必须通过RegistryObject或者带有@ObjectHolder的字段引用。

2.1.3.1 使用RegistryObjects

RegistryObjects能够被用来检查注册对象是否是可用的。这些被用于DeferredRegister来返回注册对象的引用。在触发对应注册表的RegistryEvent.Register事件以及@ObjectHolder注释后,将更新它们的引用。
要获取RegistryObject,请使用可注册对象的ResourceLocation和ForgeRegistry调用RegistryObect.create().自定义注册表也可以通过提供的对象类提供者来使用。保存RegistryObject在公共静态最终字段中,并且当你需要注册对象时调用get()方法。
例子

public static final RegistryObject<Item> BOW = RegistryObject.create(new ResourceLocation("minecraft:bow"),ForgeRegistries.ITEMS);

//假设“neomagicae:mana_type”是一个有效的注册表,“neomaagicae:coffeinum”是该注册表中的一个有效对象
public static final RegistryObject<ManaType> COFFEINUM = RegistryObject.create(new ResourceLocation("neomagicae", "coffeinum"), new ResourceLocation("neomagicae", "mana_type"), "modId");
2.1.3.2 使用@ObjectHolder

使用@ObjectHolder注解类或字段,并提供足够的信息来构建ResourceLocation以识别具体注册表中的具体对象,可以将注册表中已注册的对象注入到公共静态字段。
@ObjectHolder规则如下:

  • 如果Class(类)用@ObjectHolder来注解,如果没有明确的定义在,则它的值将所有字段的默认命名空间。
  • 如果Class(类)用@Mod来注解,如果没有明确定义,那么ModId为所有字段的默认命名空间。
  • 如果一个字段出现以下情况,则考虑注入:
  • 它至少有public static修饰符;
  • 以下条件成立之一
  • 封闭类有一个@ObjectHolder注解,字段是final;
  • namespace 值是字段的名称;
  • name值是封闭类的namespace;
  • 如果namespace 不能被找到和继承,则抛出异常;
  • 该字段使用@ObjectHolder进行注解;
  • name值是明确定义的;
  • namespace值是显式定义的,或者是封闭类的namespace;
  • 字段类型或者超类之一对应于有效注册表(例如:Item或ArrowItem的Item注册表)
  • 如果一个字段没有对应的注册表,则抛出异常;
  • 如果生成的ResourceLocation不完整或无效,则抛出异常
  • 如果没有发生其他错误或异常,则该字段将被注入;
  • 如果以上规则都不适用,则不会采取任何西操作(可能会记录消息)
    @ObjectHolder注解字段是在其相应注册表的RegistryEvent.Register事件以及RegistryObjects被触发后注入其值的。
    如果要注入对象时该对象不存在于注册表中,则会记录调试消息,并不会注入任何值
    由于这些规则相当复杂,以下是一些示例:
package com.zw.yaotu.holder;

import com.zw.yaotu.register.list.ManaType;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.ArrowItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;

@net.minecraftforge.registries.ObjectHolder("minecraft")  //继承 resource 命名空间:minecraft
public class AnnotatedHolder {

    //没有注解。需要[public static final]
    //Block具有相应的注册表:[Block]
    //Name path是字段的名称:”diamond_block“
    //Namespace未明确定义
    //因此,namespace继承自类注解:"minecraft"
    //从[Block]注册表注入"minecraft:diamond_block"
    public static final Block diamond_block = null;


    //存在注解,有[public static]
    //SoundEvent有相应的注册表:[SoundEvent]
    //Name path的值来自注解:"ambient.cave"
    //Namespace未明确定义
    //因此,该字段的namespace继承自雷注解:"minecraft"
    //从[SoundEvent]注册表注入 "minecraft:ambient.cave"
    @net.minecraftforge.registries.ObjectHolder("ambient.cave")
    public static SoundEvent ambient_sound = null;

    //对于下一个例子,假设[ManaType]是一个有效的注册表
    //存在注释,[public static]是必须的,[final]是可选的
    //ManaType有一个对应的注册表:[ManaType](自定义注册表)
    //Resource location是明确的:"neomagicae:coffeinum"
    //从[ManaType]注册表注入"neomagicae:coffeinum"
    @net.minecraftforge.registries.ObjectHolder("neomagicae:coffeinum")
    public static final ManaType coffeinum = null;


    //没有注释,[public static final]是必须
    //Item有相对应的注册表:[Item]
    //Name path是字段名称:"ENDER_PEARL"→"ender_pearl"  因为字段名称有效,被自动转换为小写
    //Namespace 未明确定义 因此namespace 继承类注释:"minecraft"
    //从[Item]注册表注入:"minecraft:ender_pearl"

    public static final Item ENDER_PEARL = null;

    //存在注释,[public static]是必须的,[final]可选
    //ArrowItem没有相应的注册表,但是其超类Item具有相对应的注册表:[Item]
    //Resource location明确定义:"minecraft:arrow"
    //从[Item]注册表注入:"minecraft:arrow"
    @net.minecraftforge.registries.ObjectHolder("minecraft:arrow")
    public static final ArrowItem arrow = null;


    //没有注释,所以[public static final]是必须的,因此该字段被忽略
    public static final Block bedrock = null;


    //没有注释。[public static final]是必须的
    //CreativeModeTab没有相应的注册表,并且CreativeModeTab的任何超类型都没有相应的注册表。
    //因此,这将产生异常
    public static final CreativeModeTab group = null;

}
package com.zw.yaotu.holder;

import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.level.biome.Biome;
import net.minecraftforge.registries.ObjectHolder;

//此类缺少@ObjectHolder
public class UnannotatedHolder {

    //存在注解。[public static]是必须的。[final]是可选的
    //Enchantment有相应的注册表:[Enchantment]
    //Resource location有明确定义:"minecraft:frame"
    //从[Enchantment]注册表中注入:"minecraft:frame"
    @ObjectHolder("minecraft:flame")
    public static final Enchantment flame = null;

    //封闭类上没有注解,因此该字段被忽略
    public static final Biome ice_flat = null;

    //存在注解。[public static]必须
    //Entity没有相应的注册表,实体的任何超类都没有相应的注册表。
    //因此,这个将产生异常
    @ObjectHolder("minecraft:creeper")
    public static Entity creeper = null;

    //存在注解。[public static]必须,[final]可选
    //Potion有相应的注册表:[Potion]
    //Name path是注解的值:"levitation"
    //Namespace未明确定义
    //封闭类没有注解,所以这将产生异常
    public static final Potion levitation = null;
}
2.1.4 创建自定义Forge注册表

自定义注册表是一个简单的键值对映射。这是笔记常见的风格。然而它强制对存在的注册表进行严格的依赖。它还要求任何数据需要同步到两端时,都必须手动完成。自定义Forge注册表提供了一个简单的替代方案,用于创建软依赖项,以及更好的管理和自动同步各端(Sides)(除非另有说明)。由于对象也使用Forge注册表,注册也应该以同样的方式标准化。
自定义Forge注册表是在RegistryBuilder的帮助下通过NewRegistryEvent或DeferredRegister创建的。RegistryBuild类接受各种参数(例如注册表的名称,class的值及注册表上不同事件的回调)。NewRegistry完成触发后,新的注册表将注册到RgeistryManager。
注册表Class的值必须是实现IForgeRegistryEntry。它定义可以对该类的对象调用#setRegistryName和#getRegistryName。建议继承ForgeRegistryEntry,而不是直接实现接口。一个字符串调用#setRegistryName(String)方法,并且该字符串没有显示的namespace,所以namespace将会被设置为ModId。
任何新创建的注册表都应该使其管理的注册方法来注册关联的对象。

package com.zw.yaotu.register.list;

import net.minecraftforge.registries.ForgeRegistryEntry;

/**
 * 描述:自定义注册表
 * 时间:2024-04-16
 * 作者:木木
 */
public class ManaType extends ForgeRegistryEntry {

    
}
2.1.4.1 使用NewRgeistryEvent

使用NewRegistryEvent时,使用RgeistryBuilder调用#create将返回一个supplier-wrapped(提供者的包装着)的注册表。NewRgeistryEvent完成发布到Mod事件总线后,可以访问提供的注册表。在NewRegistryEvent完成触发之前从supplier获取注册表将导致Null值。

2.1.4.2 使用DeferredRegister

DeferredRegister方法再次成为上述事件的另外一个包装器。一旦使用#create重载在常量字段中创建的DeferredRegister,就可以通过DeferredRegistry#makeRegistry构建注册表。这将接受注册表以及提供的包含任何其他配置的RegistryBuilder。默认情况下,改方法已添加#setName和#setType。由于此方法可以随时返回,因此会返回提供的IForgeRegistry变体。在触发NewRegistryEvent之前从supplier 获取自定义注册表将会导致空值。

在通过#register将DeferredRegister添加到Mod事件总线之前,必须调用DeferredRegister#makeRegistry#makeRegistry,也可以使用#register方法在NewRegistrEvent期间创建注册表。

2.1.5 处理条目丢失

在某些情况下,每当更新Mod或者删除Mod是,某个注册表对象就会不存在。我们可以通过第三个注册表事件指定操作来处理丢失的映射:RegistryEvent$missingMappings。在此事件中,丢失映射的列表可以通过给定ModId的#getMapping获得,也可以通过#getAllMappings获取所有映射。

对于每个映射,可以选择四种映射类型之一来处理丢失的列表:

功能描述
IGNORE忽略丢失的条目并放弃映射
WARN在日志中生成警告
FAIL阻止加载
REMAP将条目重新映射到已注册的非null对象

如果未指定任何操作,则默认将通过通知用户丢失的条目以及用户是否要加载世界来执行。除了重新映射之外的所有操作都将防止任何其他注册表对象取代现有Id,以防止相关条目被添加回游戏中

  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值