Minecraft Modding: Configuration GUIs

Introduction



(I learned how to make a configuration GUI originally from a minalien tutorial, but it seems that his is no longer posted.  So here is my own tutorial.)

A config is a text file that is user-editable, and usually within the user directory (not the installation directory or JAR) of the computer.  The values in the config file can be mapped to fields in your code to configure how your mod behaves.  For example, maybe you want the health of a custom mob to be configurable.

This works fine and there are other tutorials on configurations (see How To Make An Advanced Configuration File).  However it is annoying that a person has to edit the config file separately (using a text editor) to make configuration changes.  Therefore, there is now some classes such as GuiConfig in Minecraft Forge that allow you to create a gui to allow the user to edit the configuration within the game.  (To access this you select the Mods button on the main game menu, then select a mod, then select the Config button.)

Warning: The GuiConfig functionality is not fully implemented yet.  For example, if you try to configure while in world the default GUI only shows dummy options (you can replace the GUI entirely though as I explain below), also the ability to show parent/child categories of configuration options doesn't seem to be called.  But overall it is still functional enough for some simple configuration purposes -- I find it useful anyway.

The steps to creating the config GUI is fairly simple.  You have to:
  1. Create and associate a "GUI factory" with your mod
  2. Create a GuiConfig extension class that associates the config file and process the update of the config when the Done button is pressed.

In-Game Config Option Changes


There are actually two places where the menu system indicates you can access mod options.  In the main menu if you select Mods then select a mod there is an Configure button.  Additionally, if you press ESC during the game to pause the game you'll notice that there is a Mod Options button.

If you try Mod Options button from the in-game pause menu, you'll normally just see a couple of dummy strings displayed and no real options settings.  This functionality was never quite implemented (I expect the issue is that if there are several mods loaded, it is supposed to list multiple mods then go into their options).  It is possible to replace this GUI with your own.  I explain how to do that below.

Create A Configuration


First I assume that you already have set up a Configuration for your mod.  If not, please follow a tutorial such as: How To Make An Advanced Configuration File.

Create And Associate The "GUI Factory" (Implement IModGuiFactory)


You need to create a class for your GUI factory that implements IModGuiFactory interface.  For example:

public class GuiFactoryMagicBeans implements IModGuiFactory 
{
    @Override
    public void initialize(Minecraft minecraftInstance) 
    {
 
    }
 
    @Override
    public Class<? extends GuiScreen> mainConfigGuiClass() 
    {
        return GuiConfigMagicBeans.class;
    }
 
    @Override
    public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() 
    {
        return null;
    }
 
    @Override
    public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) 
    {
        return null;
    }
}


I'm not really sure the possibilities for using the initialize() method.  The Javadoc simply says: Called when instantiated to initialize with the active Minecraft instance.  Leaving the method blank seems to work fine.

Tip: The initialize() method is called by a method called FMLClientHandler.finishMinecraftLoading().  This is interesting because it means that if you want to run any code right after all mods are finished loading you can put that code in this initialize() method (even if that code is not related to the configuration GUI).

The mainConfigGuiClass() method returns the name of a class extending GuiScreen, which should be your custom GuiConfig class (which I explain how to make below). This class will be instantiated when the "config" button is pressed in the mod list. It will have a single argument constructor - the "parent" screen, the same as all Minecraft GUIs. The expected behavior is that this screen will replace the "mod list" screen completely, and will return to the mod list screen through the parent link, once the appropriate action is taken from the config screen. A null from this method indicates that the mod does not provide a "config" button GUI screen, and the config button will be hidden/disabled.

The runtimeGuiCategories() doesn't seem to be in any call hierarchy so I am not sure this is implemented yet -- I plan to try this soon.  It seems to be intended to create a hierarchy of config options so you could have multiple sub-options.  The Javadoc says the method returns a Set of the "runtime" categories this mod wishes to populate with GUI elements. Runtime categories are created on demand and organized in a 'lite' tree format. The parent represents the parent node in the tree. There is one special parent 'Help' that will always list first, and is generally meant to provide Help type content for mods. The remaining parents will sort alphabetically.

The getHandlerFor() method returns an instance of a RuntimeOptionGuiHandler that handles painting the right hand side option screen for the specified RuntimeOptionCategoryElement. This seems to allow you to add custom buttons or "widgets", but I find the default button functionality is good enough for me so I just return null and it works.

Create The Custom GuiConfig Class


You need to create a class that extends GuiConfig.  For example:

public class GuiConfigMagicBeans extends GuiConfig 
{
    public GuiConfigMagicBeans(GuiScreen parent) 
    {
        super(parent,
                new ConfigElement(
                      MagicBeans.config.getCategory(Configuration.CATEGORY_GENERAL))
                            .getChildElements(),
                MagicBeans.MODID, 
                false, 
                false, 
                "Play Magic Beans Any Way You Want");
        titleLine2 = MagicBeans.configFile.getAbsolutePath();
    }
    
    @Override
    public void initGui()
    {
        // You can add buttons and initialize fields here
        super.initGui();
    }

    
    @Override
    public void drawScreen(int mouseX, int mouseY, float partialTicks)
    {
        // You can do things like create animations, draw additional elements, etc. here
        super.drawScreen(mouseX, mouseY, partialTicks);
    }

    @Override
    protected void actionPerformed(GuiButton button)
    {
        // You can process any additional buttons you may have added here
        super.actionPerformed(button);
    }

The constructor maps the all the configuration options you have that are in a specific category with the Mod ID and the configuration file itself.  As far as I can tell, you can only have one category per GuiConfig class, so if you want to create multiple levels of options one way would be to add buttons that take you to another GuiConfig class for the sub-category.

Warning: The category is simply a string, but it needs to match the category string used in your actual configuration file.

The last string parameter passed to the super constructor is the title that will be displayed on your custom GUI.  The Javadoc recommends "that you pass the path of the config file being edited".  I tried that and didn't like how it looked, but the GuiConfig class actually has both a title (set by the constructor) and a titleLine2 field.  The latter is displayed as a sub heading.  So my preference is to put the file path in the titleLine2 and pass some sort of nice heading for the main heading in the constructor..

Tip: In GuiConfig there is a public string field called titleLine2 that can be used for a subheading if you want.  Simply assign it a string value after you call the super constructor.

The drawScreen() is useful if you want to do anything visually fancy.  You can add pictures or a border, I like to create animated text by changing the title field each frame.  Anyway, you can have fun with this method.

The actionPerformed() method is the same as for any type of GuiScreen, it is called when a button is pressed.  Each button has an id and in this case there is a default button called "Done" which has id 2000.  You can just copy the code in this method as it is not specific to my mod.  Note that it handles the case where some configuration options require a restart of Minecraft (this is set in your Configuration class).

Associate The GUI Factory With Your @Mod Annotation


In your mod's main class, you should have an @Mod annotation.  In this you need to add a reference to the GUI factory class.  For example, my mod's annotation looks like:

@Mod(modid = 
      MagicBeans.MODID, 
      name = MagicBeans.MODNAME, 
      version = MagicBeans.MODVERSION,
      guiFactory = "com.blogspot.jabelarminecraft."+MagicBeans.MODID+".gui.GuiFactoryMagicBeans")


The key line is the guiFactory one and the string should give the fully qualified (i.e. your package and class name) for your custom GUI factory class.

Implement The In-Game Options Menu (If Desired)


As mentioned earlier, you can also access Mod Options GUI through the pause menu but by default it shows just some test strings.  If you wish, you can replace this with your configuration GUI.

Warning: This method could create compatibility problems with any other mod that tries to replace the same GUI.  I don't think many people do this, so probably okay to go ahead and do it, but ultimately it would be better to create a Java API that allows multiple mods to present options from the pause menu.

Anyway, to replace the GUI is actually pretty easy -- there is an event called GuiOpenEvent that you can handle to replace any GUI with your own.  If you need help implementing event handlers, you can check out my Event Handling Tutorial.

In your event handler class, you simply need something like this:

@SideOnly(Side.CLIENT)
@SubscribeEvent(priority=EventPriority.NORMAL, receiveCanceled=true)
public void onEvent(GuiOpenEvent event)
{
    if (event.gui instanceof GuiIngameModOptions)
    {
        System.out.println("GuiOpenEvent for GuiIngameModOptions");
        event.gui = new GuiConfigMagicBeans(null);        
    }
}

You should replace the GuiConfig class with your own custom class.

Note: Options that you change in-game will only take effect if your code reacts to the configuration change.  For example, if you only check the configuration values during initialization of your mod, then changing it mid-game won't do anything until the next time you load the world.

Confirming That It Works


If you did all the above, when you start Minecraft with Forge, if you click on the Mods button you should be able to select your mod and see a Config button.


If you click on the Config button you should get your custom configuration GUI.  For example, mine looks like:


You should be able to change the values, revert to default, etc.  Furthermore, if you save and quit Minecraft and start again it should remember your settings.

Similarly, if you chose to replace the in-game options menu you can try it out and confirm that changes persist.

Conclusion


I hope you found this useful.  I'm working on figuring out whether the categories work well and will update if I find anything interesting.  Please don't hesitate to comment if you need clarification or see something I need to correct.  Happy modding!

12 comments:

  1. What if the config file has multiple categories? Are we only allowed one in the config file? Or is there a way to put one category in each page, or what do we do for that? Sorry if this posted twice but i think the first time did not work

    ReplyDelete
  2. You are allowed multiple categories in the config file, but I think the GUI approach above would only allow one category per GUI page. I'll have to check but I don't think it will automatically link the pages, so I think if you want multiple categories that you would have to make a separate config GUI class for each page, and additionally make a custom regular GUI with buttons for each category (hitting the button would bring up the related config GUI). You'd make the IModGuiFactory return the GUI with the buttons.

    So GUI factory brings up custom GUI with buttons, and then each of those bring up the appropriate config GUI.

    By the way, you can also make your own fully custom config GUI. I like the fact that the one in this tutorial is pretty easy to use (especially you don't have to change GUI if you change config file), but it does have limitations -- for example the sorting order isn't controlled.

    ReplyDelete
  3. How do you make the config GUI show that Minecraft needs to be restarted in order for the options to work? Error: cannot invoke requiresMcRestart() on the primitive type boolean
    Why is this the case, and how have other mods used the method when I cannot?

    ReplyDelete
  4. The "How to make an advanced configuration file" link now links to mcforge.readthedocs.io/en/latest/.
    Can you point me to another tutorial for starting? I did your tutorial but I'm getting errors because I have no config file.

    ReplyDelete
  5. Download Minecraft APK free Game on android and PC windows. For Knowing more about Minecraft PE gmaes please go that link and now the updates about Minecraft Pocket Edition gmaes.

    ReplyDelete
  6. Hey when I try this code, I get this error:
    [14:07:13] [Client thread/ERROR] [FML]: There was a critical issue trying to build the config GUI for fovmod
    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_91]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_91]
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_91]
    at java.lang.reflect.Constructor.newInstance(Unknown Source) ~[?:1.8.0_91]
    at net.minecraftforge.fml.client.GuiModList.actionPerformed(GuiModList.java:293) [GuiModList.class:?]
    at net.minecraft.client.gui.GuiScreen.mouseClicked(GuiScreen.java:509) [GuiScreen.class:?]
    at net.minecraftforge.fml.client.GuiModList.mouseClicked(GuiModList.java:197) [GuiModList.class:?]
    at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:621) [GuiScreen.class:?]
    at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:587) [GuiScreen.class:?]
    at net.minecraft.client.Minecraft.runTick(Minecraft.java:1761) [Minecraft.class:?]
    at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1080) [Minecraft.class:?]
    at net.minecraft.client.Minecraft.run(Minecraft.java:380) [Minecraft.class:?]
    at net.minecraft.client.main.Main.main(Main.java:116) [Main.class:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_91]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_91]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_91]
    at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.12.jar:?]
    at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.12.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_91]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_91]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_91]
    at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97) [start/:?]
    at GradleStart.main(GradleStart.java:26) [start/:?]
    Caused by: java.lang.NullPointerException
    at fovmod.gui.FOVGuiConfig.(FOVGuiConfig.java:25) ~[FOVGuiConfig.class:?]
    ... 25 more

    ReplyDelete
  7. what if you cant press the config button at all?

    ReplyDelete
  8. Thanks for sharing, you can also download naruto senki mod apk unlimited money from our site

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete