Saturday 1 February 2020

How Forge starts up your code [1.14.4+]

Base Mod Class

When you start Minecraft, the Forge code prepares a couple of things and then starts searching for mods.  It looks through the java code in the mods folder until it finds a class which is preceded by the @Mod annotation.

It then reads the unique name of your mod, and constructs the class.  eg

@Mod("examplemod")
public class ExampleMod
{
   public ExampleMod() {
    // your code here gets executed.
  }

The general name for this technique is Reflection.
Reflection: the ability of a computer program to examine and modify the structure and behavior of an object at runtime.
Reflection allows inspection of classes, interfaces, fields and methods at runtime without knowing the names of the interfaces, fields, methods at compile time.
Forge uses this quite a bit to integrate with your mod code without knowing in advance what you've called your classes and how they're structured.

EventHandlers
Although Forge will call your mod class during startup, that's not enough to do everything your mod needs.  Nearly all of your mod code will be designed to respond to things that happen; for example
* A player places your new "Strawberry bush" block into the world; or
* Your "Laser Cannon" entity fires a beam at a nearby player; or
* It becomes night

There are basically two different ways that Forge can call your code:
The first way is to extend vanilla objects and override some of the methods for those objects.
For example: you might write a StrawberryBushBlock class that extends the Block class and overrides some of its functions (such as onBlockAdded()).  During startup, your mod then registers this StrawberryBushBlock with Forge so that it becomes a part of the game.
Later on, when the player plants a StrawberryBush, Forge calls your StrawberryBushBlock.onBlockAdded() and your code performs the necessary instructions to make the bush grow where it has been planted.

public class StrawberryBushBlock extends Block
{
  @Override 
  public onBlockAdded(BlockState state, World worldIn, BlockPos pos, 
                      BlockState oldState, boolean isMoving) {
    // your code to manipulate the world to grow a strawberry push at the right place
  }

This method is used for vanilla objects such as blocks, items, entities, and similar.

The second way is to register an EventHandler for an Event that your code is interested in.
Forge provides a large number of events, such as
* Server TickEvent, which is triggered 20 times per second
* LivingDamageEvent, which is called when a player or entity is damaged by something

This is done by "subscribing" to an event, typically during your mod construction:

@Mod("examplemod")
public class ExampleMod
{
   public ExampleMod() {
    MinecraftForge.EVENT_BUS.register(new ServerTickHandlerClass());
  }
public class ServerTickHandlerClass {
  @SubscribeEvent
  public void serverTick(TickEvent.ServerTickEvent event) {
     // this code gets called at every server tick (20 times per second)
  }
}
** Important Note ** - there are two event busses: the MinecraftForge.EVENT_BUS and your mod's own ModEventBus.

FMLJavaModLoadingContext.get().getModEventBus();

If you subscribe your event to the wrong bus, it will never get called. Based on my testing on 1.14.4:
ModEventBus is used for setup/initialisation events only, in the following order:
* RegistryEvent of all types
* ColorHandlerEvent (for blocks & items)
* ParticleFactoryRegisterEvent
* FMLCommonSetupEvent
* TextureStitchEvent
* FMLClientSetupEvent or FMLDedicatedServerSetupEvent

* ModelRegistryEvent
* Other ModLifecycleEvents such as InterModEnqueueEvent, InterModProcessEvent
Everything else: the MinecraftForge.EVENT_BUS




Mod Startup Order

A number of things need to happen during mod startup, before the game starts running, and the order that they occur is important.  During startup, Forge will call your Mod several times to let you add new blocks, items, read configuration files, and otherwise integrate itself into the game at the right times. The most important stages are:

  • Construction of your mod class
  • Registry events for blocks, items, recipes, entities, sound events, etc - these allow you to tell Forge about new blocks
  • SetupEvents (FMLCommonSetupEvent, FMLClientSetupEvent)
Minecraft Distributions
There is one final concept that is important during the game startup.  There are two different distributions of Minecraft that your mod might be installed in:

  • The "Client" distribution; or
  • The "DedicatedServer" distribution.

(For more detail see here).

Why is this important?    If your mod is installed in a DedicatedServer, some of your setup is not needed (and will cause the server to crash if you try to do it anyway).  Examples are - setting up EntityRenderers, loading graphics, initialising sounds, and referring to any Dist.CLIENT classes.

Your mod should handle this correctly by putting its initialisation code into the appropriate SetupEvent
  • The FMLCommonSetupEvent is called during startup for both the CombinedClient and DedicatedServer.  Set up your server-side objects in here.
  • The FMLClientSetupEvent is called during startup of the CombinedClient only.  Set up your client-side objects in here.
In many cases, you won't actually need to put any code into the SetupEvent handlers, because the Registry events will do all the work.  Forge ensures that the Registry events are only called for the appropriate distributions.


2 comments: