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.
If you have designed your mod properly, this will be your "Base Mod" class.
For example- your mod contains a file called TestFrameworkMod.java:
@Mod(modid="testframeworkmod", name="Forge Test Framework Mod", version="0.0.1")
public class TestFrameworkMod {
//.. etc ...
}
Forge finds the @Mod, reads a couple of pieces of information about the mod (a unique "ID" name, the human-readable name, and the version), and now knows that the base class for your mod is TestFrameworkMod.
(NB all the code in this tutorial can be found here).
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.
Now that Forge has found your Base Mod Class definition, it also needs to know where the instance of your class is. To do this, it searches a bit further until it finds @Mod.Instance.
// The instance of your mod that Forge uses.
@Mod.Instance("testframeworkmod")
public static TestFrameworkMod instance;
It now knows that instance is the instance of your Base Mod Class, which it uses later on to communicate with your class.
For more technical details, see @Mod in the Forge code-
net/minecraftforge/fml/common/@Mod.
Proxies
In order to understand the next part, I first need to explain a couple of concepts about Server and Client.
Minecraft is designed from the ground up to be a multiplayer game. For this reason, it is split into two parts - "server" and "client".
- the "client" interacts with each user - reading their keyboard and mouse, and drawing things on your computer screen. Every player will have their own "client".
- the "server" maintains the world and everything in it. It talks to all of the clients to find out what each user is doing, updates the world accordingly, then sends the updated information back to all the clients. There is only one server.
- Dedicated Client - contained all the "client-side" code
- Dedicated Server - contained all the "server-side" code
So the two different Minecraft programs are now:
- CombinedClient (which has both client-side code and server-side code in it)
- Dedicated Server (which has only server-side code).
The Forge authors wanted to make it easy to use the same Mod code in both the "Combined" Client and the Dedicated Server. (This is not trivial because the Dedicated Server is missing the client-side code, and if your Mod attempts to call any of the client-side code when it's installed in the DedicatedServer, it will crash.)
They use the "SidedProxy" in order to do this. Forge hunts through your Base Mod until it finds @SidedProxy. It then chooses the appropriate Class from the options you have provided, creates an instance of it, and puts it into the field immediately after the @SidedProxy annotation.
So in the example below:
- If the mod is installed in a normal Minecraft client, the proxy variable will be set to your class CombinedClientProxy (which inherits from CommonProxy).
- If the mod is installed in a dedicated server, the proxy variable will be set to your class DedicatedServerProxy (which also inherits from CommonProxy).
// DedicatedServerProxy depending on whether this mod is running in a
// normal Minecraft client or a dedicated server.
@SidedProxy(clientSide="testframework.clientonly.CombinedClientProxy",
serverSide="testframework.serveronly.DedicatedServerProxy")
public static CommonProxy proxy;
Why is this useful? Because you can now easily split your initialisation code into three parts:
- Code in the CombinedClientProxy, which only gets called on the normal Minecraft client.
- Code in the DedicatedServerProxy, which only gets called on a dedicated server.
- Code in the CommonProxy base class, which is called regardless of where the mod is installed.
An example of what the Proxy code might look like:
public class CommonProxy {
public void doSomething() {
// do something on both
}
}
public class CombinedClientProxy extends CommonProxy {
@Override
public void doSomething() {
super.doSomething(); // do something on both
// do something on normal Minecraft only
}
}
public class DedicatedServerProxy extends CommonProxy {
@Override
public void doSomething() {
super.doSomething(); // do something on both
// do something on dedicated server only
}
}
A comment about Server vs Client objects - because of the way that Java loads classes, your server-side classes should not have any mention of Client-side-only vanilla objects whatsoever. It doesn't matter whether the code is ever executed- simply having a Client-side-only definition somewhere in your class is often enough to cause a crash. Use a dedicated client-side-only class instead, or alternatively a method in your proxy classes.
EventHandlers
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 by registering your Classes in the appropriate spots.Before Forge can call your Mod, it needs to know which methods to use. This is where @EventHandler comes in. For example - during startup Forge will go through a number of phases for all of the mods which are loaded:
- PreInitialization - "Run before anything else. Read your config, create blocks, items, etc, and register them with the GameRegistry."
- Initialization - "Do your mod setup. Build whatever data structures you care about. Register recipes."
- PostInitialization - "Handle interaction with other mods, complete your setup based on this.
When Forge wants to tell your mod that it's time to run your PreInitialization code, it reads through your mod's code until it finds @EventHandler in front of a method, then checks the parameter definition to see if it matches the FMLPreInitializationEvent Class. If so, it calls the method.
The PreInitialization, Initialization, and PostInitialization events will often need to do different things depending on whether your mod is in a CombinedClient or a DedicatedServer. For this reason I suggest that your event handlers should just immediately call a method in the CommonProxy, see below.
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
proxy.preInit();
}
@EventHandler
public void load(FMLInitializationEvent event) {
proxy.load();
}
@EventHandler
public void postInit(FMLPostInitializationEvent event) {
proxy.postInit();
}
There are many other events in addition to these three (not that you're likely to need them unless you're doing some hardcore trickery). See
cpw/mods/fml/common/event
~Overview of Forge (and what it can do for you)
Forge concepts
Some general notes
How Forge starts up your code (@Mod, @SidedProxy, etc)
Registering your classes
Extra methods for vanilla base classes
Events
Forge summary - grouped by task
Blocks
Items
TileEntities
Entities
World generation, loading, saving
Miscellaneous - player input, rendering, GUI, chat, sounds
Thank you for doing all of this!
ReplyDeleteYou're welcome :-) Actually it's good fun for me to figure out how it all works, and I find that trying to explain it helps me understand it better myself too.
DeleteIt's great to see some informative Forge documentation! Cleared a lot up for me.
ReplyDeleteThis is great information about server and client. Well explained about dedicated proxies and all. I am using Microleves Dedicated Proxies. It works fine for me and will understand what you want to explain in this post.
ReplyDeleteThank you for writing this! This helped me to understand proxies and event handlers
ReplyDeletethank you so much! :)
ReplyDelete