Minecraft Modding: FML and Mod Loading

Detecting If Your Mod Is Latest Version


Version-checking generally works by checking for information from a web site (maintained by you) and comparing the information there with the currently running version.

Tip: An easy way to create a public github repository to contain the file, but you could also use any other personal webpage site including blog sites.

Forge now provides a light-weight version checking implementation. You simply add a parameter to your @Mod annotation that points to a URL of a JSON formatted with some version and download information.

Before this built-in implementation was availabe you had to do this yourself. You can still do your own implementation to have more control. Here is my detailed tutorial for making a custom version checker: Jabelar's Version Checking Tutorial

Detecting if Another Mod Is Loaded


You can use the Loader class methods to list or check the loaded mods.  You should run such checks in the "post init" method in your proxy or main class, as you want to ensure all mods are loaded before you check for them.  

Anyway, the Loader.isModLoaded() method will test whether a mod is loaded as long as you pass the mod id as the string argument.  

There are also several non-static methods in Loader that can be accessed through its instance (which you can get from the instance() method).  If you're unsure about the right string for a particular mod, you can check by printing out the list of the installed mods that you can get like this:

List<ModContainer> myList = Loader.instance().getActiveModList();

Detecting The Version Of Another Mod


Thanks to RANKSHANK for this tip.

It may happen that you depend on a specific version of another mod. To check the version of another mod, you can use: FMLCommonHandler.instance().findContainerFor(Object).getVersion()

Put the modid string in for the object and you'll get the version string.

How Annotations Actually Work


Throughout the Forge-distributed code you'll find "annotations" which precede some constructors, fields and methods using an @ symbol. Annotations are "syntactic metadata" for Java. For example, the @Override is a Java annotation.

FML has some custom annoations. What they do is aid FML loader to do conditional passes on the code -- for example the way your main mod class is found is by searching for the @Mod annotation, and the way it knows that some classes should only be loaded on the client side is with the @SideOnly annotation.

Thanks to diesieben07 for this information.

FML searches the entire classpath (including the mod folder) and analyzes each class using the ASM library. This library allows you to read the raw class files, without actually loading them. Then each class is inspected for annotations. Your code in a mod can actually get access to that data via the ASMDataTable you get from the "pre-init" (FMLPreInitializationEvent) event.

Following are sections listing and explaining the various annotations you might come across.

The @Mod Annotation


You can check these yourself in Mod class, but for convenience here is a list of the properties with the comments explaining their use:

modid;
  • String type. 
  • The unique mod identifier for this mod. Should be all lower-case.
name
  • String type. 
  • A user friendly name for the mod. 
version:
  • String type. 
  • A version string for this mod.
dependencies:
  • String type. 
  • A simple dependency string for this mod (see mod loaders "priorities" string specification).
  • You can set the dependencies with before:modid and after:modid to control the loading order.
useMetadata:
  • boolean type (default false). 
  • Whether to use the mcmod.info metadata by default for this mod. If true, settings in the mcmod.info file will override settings in these annotations.
clientSideOnly:
  • boolean type (default false). 
  • If true, this mod will not be loaded on the Dedicated Server environment. Will crash if both serverSideOnly and clientSideOnly are set to true.
serverSideOnly:
  • boolean type (default false). 
  • If true, this mod will not be loaded on the Client environment. Will crash if both serverSideOnly and clientSideOnly are set to true.
acceptedMinecraftVersions:
  • String type. 
  • The acceptable range of Minecraft versions that this mod will load and run in the default ("empty string") indicates that the currently RUNNING Minecraft version is acceptable. This means ANY version that the end user adds the mod to. 
  • Modders PLEASE set this. 
  • FML will refuse to run with an error if the Minecraft version is not in this range across all mods. 
  • @return A version range as specified by the maven version range specification or the empty string.
acceptableRemoteVersions:
  • String type. 
  • A replacement for the no-longer-existing versionRange of NetworkMod. It specifies what remote version range that this mod will accept as valid. Defaults to nothing, which is interpreted as "only this version". 
  • Another special value is '*' which means accept all versions. 
  • This is ignored if there is a NetworkCheckHandler annotation on a method in this class. 
  • @return A version range as specified by the Maven version range specification or the empty string.
acceptableSaveVersions:
  • String type. 
  • A version range specifying compatible save version information. If your mod follows good version numbering practice (like this http://semver.org/) then this should be sufficient. 
  • Advanced users can specify a SaveInspectionHandler instead. 
  • @return A version range as specified by the Maven version range specification or the empty string.
bukkitPlugin:
  • String type. 
  • An optional Bukkit plugin that will be injected into the Bukkit plugin framework if this mod is loaded into the FML framework and the Bukkit core-mod is present. Instances of the Bukkit plugin can be obtained via the BukkitPluginRef annotation on fields. This may be implemented by a Bukkit integration. It is not provided with vanilla FML or Minecraft Forge
  • @return The name of the plugin to load for this mod.
certificateFingerprint:
  • String value. 
  • Specifying this field allows for a mod to expect a signed jar with a fingerprint matching this value. 
  • The fingerprint should be SHA-1 encoded, lowercase with ':' removed. 
  • An empty value indicates that the mod is not expecting to be signed. 
  • Any incorrectness of the fingerprint, be it missing or wrong, will result in the FMLFingerprintViolationEvent event firing prior to any other event on the mod
  • @return A certificate fingerprint that is expected for this mod.
modLanguage:
  • This sets the programming language.
  • String type with default value "java"
  • The language the mod is authored in. This will be used to control certain compatibility behaviours for this mod. Valid values are currently "java""scala"@return The language the mod is authored in.
canBeDeactivated:
  • boolean type (default false).
  •  If your mod doesn't have a runtime persistent effect on the state of the game, and can be disabled without side effects (e.g. mini-map mods, graphical tweak mods) then you can set true here and receive the FMLDeactivationEvent to perform deactivation tasks. This does not affect administrative disabling through the system property fml.modStates or the config file fmlModState.properties. The mod will only be deactivated outside of a running game world - FML will never allow mod deactivation whilst a game server is running. @return if this mod can be deactivated whilst the game is open.
guiFactory:
  • String type. 
  • An optional GUI factory for this mod. This is the name of a class implementing IModGuiFactory that will be instantiated on the client side, and will have certain configuration/options GUIs requested from it. @return The name of a class implementing IModGuiFactory.

@SideOnly And Proxies


Minecraft is a client-server architecture meaning that two different parts of the game are running simultaneously, one on the client (the computer the user is playing on) and one on the server (even if it is the same computer as the client).

Since some code like visual rendering, producing sounds and processing input only need to be on one side (the client), FML can load the code just on the side necessary. So basically even though the same JAR file can be used on both client and server, certain classes, fields and methods might only be available on one "side" and these are marked with the @SideOnly annotation.

In the case your mod needs to use code that is sided, the mod's code will also need to be sided otherwise the mod will fail to run when it tries to interpret your code and doesn't have the Minecraft code you're referencing.

To organize your sided code, it is highly recommended to use a "proxy" system. I explain this more in Jabelar's Proxy Tutorial.

What @Deprecated Means


@Deprecated is standard Java annotation that usually marks the method as obsolete and causes a compile warning if the method is used . So seeing this in Minecraft code that you want to use in modding can be scary and also confusing (because the methods are actually still being used so how can they be "obsolete"). So the use of it in Minecraft is a bit different. As explained by LexManos: @Deprecated in the Minecraft code base generally means: modders shouldn't call this method, but it is still in use so modders may need to implement this. In other words, you can feel free to @Override it.

The @Optional Annotation


Thanks to diesieben07 for this tip.

If you use another mod's API and make calls to its methods in your code, your mod will fail to execute if the other mod isn't loaded. Although you can check if the mod is loaded with the mod loader class, that still cause failures if you've included code that calls the other mod.

It is therefore highly recommended, where possible, to use the @Optional annotation specifying the modid for the other mod. Any code marked with that annotation will only be considered for execution if a mod with that modid is loaded.

Furthermore, if the code is run once during mod loading you can organize the code by creating a secondary pre-init (or whichever FML phase you need) method in your main @Mod class and annotate it with @Optional specifying the modid of the mod you depend on. Then inside that method (and any classes that are only referenced by that method) you can freely interact with that mod and the method will only run if that mod is present.

The @Instance Annotation To Access The Instance Of Your Mod


There are certain functions such as setForcedChunkLoadingCallback() where a parameter is supposed to be your @Mod instance. When in your mod's main class (the one that has the @Mod annotation) already it is "this", otherwise you can get it using the @Instance annotation. To do this, you create a field in your main mod that contains the instance, like this:

// instantiate the mod
@Instance(put_your_modid_here)
public static YourModMainClass instance;

Of course you should replace the put_your_modid_here and YourModMainClass with the right values for your mod. Then, thereafter you can always reference the mod itself with YourModMainClass.instance.

The mcmod.info File


In FML each mod there is metadata that contains information on the mod, such as the version, the authors, etc. This information can be populated in three ways:

  1. Through the mcmod.info file
  2. Directly through the FML pre-init event.
  3. Using @Mod annotation (for some metadata items)

Key Point: The official information on the mcmod.info file format is here: https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file

As you can see in the documentation, the mcmod.info file is JSON-formatted and should go in the top of your resource directory. It is possible to cover multiple mods in one file as it can be parsed as a list. although personally I organize my mods as fully separate projects.

Here is an example of a multi-mod mcmod.info file from BuildCraft mod:

[
{
  "modid": "BuildCraft|Core",
  "name": "BuildCraft",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more!",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent":"",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
},
{
  "modid": "BuildCraft|Builders",
  "name": "BC Builders",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more! (Builders Component)",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent": "BuildCraft|Core",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
},
{
  "modid": "BuildCraft|Energy",
  "name": "BC Energy",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more! (Energy Component)",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent": "BuildCraft|Core",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
},
{
  "modid": "BuildCraft|Factory",
  "name": "BC Factory",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more! (Factory Component)",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent": "BuildCraft|Core",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
},
{
  "modid": "BuildCraft|Silicon",
  "name": "BC Silicon",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more! (Silicon Component)",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent": "BuildCraft|Core",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
},
{
  "modid": "BuildCraft|Transport",
  "name": "BC Transport",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more! (Transport Component)",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent": "BuildCraft|Core",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
},
{
  "modid": "BuildCraft|Robotics",
  "name": "BC Robotics",
  "version": "${version}",
  "mcversion": "${mcversion}",
  "description": "Extending Minecraft with pipes, auto-crafting, quarries, engines and much more! (Transport Component)",
  "credits": "Created by SpaceToad",
  "logoFile": "assets/buildcraft/logo.png",
  "url": "http://www.mod-buildcraft.com/",
  "updateUrl": "",
  "authorList": [ "SpaceToad", "BuildCraft Team" ],
  "parent": "BuildCraft|Core",
  "screenshots": [],
  "dependencies": [
     "mod_MinecraftForge"
  ]
}
]


Making A Logo For The Mod Info Screen


The mcmod.info file has the ability to specify a logo.  This will be displayed when you look at the mod info from the mods list in the launcher.

First you need a logo picture file in PNG format.  If you have JPG or other format there are lots of online free converters.

Then you need to import it into your Eclipse project in the same place as the mcmod.info file -- directly under the src/main/resources folder not in the assets subfolder lower down.

Finally in your mcmod.info file you just need to have:
  "logoFile": "your_image_file_here.png",
where of course you should use your own file name.

That's it!  You should see the graphic appear in the mod info screen.

Using Replaceable Fields In Your mcmod.info File


Since your build.gradle file probably has information related to version and such, it is nice to not have to worry about updating both build.gradle and mcmod.info. Therefore, build.gradle has a replace script that allows replacement during the build.

To make use of this, in your mcmod.info file you can use replaceable strings. In fact, in the mcmod.info file that comes in the example mod in the Forge downloads, it has stuff like this:

"version": "${version}",

"mcversion": "${mcversion}",

Where you can see that the ${} format indicates a replaceable string.

Your build.gradle then can replace these in the resource processing section like this:

processResources
{
    // this will ensure that this task is redone when the versions change.
    inputs.property "version", project.version
    inputs.property "mcversion", project.minecraft.version

    // replace stuff in mcmod.info, nothing else

    from(sourceSets.main.resources.srcDirs) {
        include 'mcmod.info'
                
        // replace version and mcversion
        expand 'version':project.version, 'mcversion':project.minecraft.version
    }
        
    // copy everything else, thats not the mcmod.info
    from(sourceSets.main.resources.srcDirs) {
        exclude 'mcmod.info'
    }
}

Note that the project.version and project.minecraft.version values are defined earlier in most build.gradle files.


3 comments:

  1. very nice blogs!!! i have to learning for lot of information for this sites...Sharing for wonderful information.Thanks for sharing this valuable information to our vision.
    dedicated server

    ReplyDelete
    Replies
    1. Thanks for the feedback. Nice to know that it is helping people.

      Delete
  2. This comment has been removed by a blog administrator.

    ReplyDelete