Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Forge config gui


Recommended Posts

I don't suppose anyone knows of any good tutorials on how to build a config gui in Forge?  You know, when you go into the main menu, click on mods, scroll down to yours and click on Config button.  One that's preferably in english?  It'd be nice if Forge put out a template for those.

  • Like 1
Link to comment
Share on other sites

If you use the annotation-based config system, a config GUI will automatically be created for your mod. All you need to do is subscribe to ConfigChangedEvent.OnConfigChangedEvent, check if the event's mod ID is your mod ID and then call ConfigManager.sync to synchronise the changes made to the Configuration object (by the GUI) back to the fields of your @Config class.


You can see an example of this here.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

14 minutes ago, fuzzybat23 said:

You don't know how to setup multiple pages in the Config screen, do you?


Each category will be given its own page.


Any field of the @Config class whose type is a class that directly extends Object will become a config category, with its fields becoming properties or categories following the same rules as the top-level class. Map fields are also mapped to categories with properties as key-value pairs.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

9 minutes ago, fuzzybat23 said:

Let me see if my sleep addled brain got that right.  Each time you declared a new public class in your example, that opened up a new category page?


The classes could be declared anywhere, it's the fields that use those classes that are mapped to categories.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

26 minutes ago, fuzzybat23 said:

I don't think I quite understand.  I'm still learning this stuff, so I'm not up to snuff on all the terms xD  What exactly do you mean by the fields the classes are mapped to?


Each field in the @Config class is mapped to either a configuration category or a configuration property depending on its type.


If it's a type that implements Map, a category is created and a property is created in that category for each key-value pair in the Map. The Map must have String keys and primitive, primitive array, String or enum values.


If it's a type that directly extends Object, a category is created and a sub-category or property is created in that category for each field of the class following the same rules as the @Config class. These classes can be declared anywhere, they don't have be nested in the @Config class.


If it's a primitive, primitive array, enum or String, a property is created.


If it's any other type, an error is thrown.

Edited by Choonster
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

Which file in your Git did you initially create and populate the configuration file that your ModConfig.java syncs to?  I'd like to compare it to mine.  I think I'm slowly getting a grasp on the usage of @Config  I'm also wondering how in your main class you initially access your ModConfig to open the Config screen.  None of the examples I've seen so far were using @Config, so the structure is quite a bit different.  Like..  This one for example, is some code I'm trying to convert to make use of @Config.



Edited by fuzzybat23
Link to comment
Share on other sites

package com.mmyzd.llor;

import java.io.File;
import java.util.ArrayList;

import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

public class ConfigManager {
	public Configuration file;
	public Property useSkyLight;
	public Property displayMode;
	public Property chunkRadius;
	public Property pollingInterval;
	public ArrayList<String> displayModeName = new ArrayList<String>();
	public ArrayList<String> displayModeDesc = new ArrayList<String>();
	public ConfigManager(File configDir) {
		file = new Configuration(new File(configDir, "LLOverlayReloaded.cfg"));
		displayModeDesc.add("Show green (safe) and red (spawnable) areas.");
		displayModeDesc.add("Show green (safe), red (always spawnable) and yellow (currently safe, but will be spawnable at night) areas.");
		displayModeDesc.add("Do not show green area. For other areas, use standard mode when not counting sky light, or advanced mode otherwise.");
	public void onConfigurationChanged(ConfigChangedEvent.OnConfigChangedEvent event) {
		if (event.getModID().equals(LightLevelOverlayReloaded.MODID)) update();
	void reload() {
		String comment = "How to display numbers? (default: 0)";
		for (int i = 0; i < displayModeName.size(); i++) {
			comment += "\n" + String.valueOf(i) + " - ";
			comment += displayModeName.get(i) + ": ";
			comment += displayModeDesc.get(i);
		useSkyLight = file.get("general", "useSkyLight", false, "If set to true, the sunlight/moonlight will be counted in light level. (default: false)"); 
		displayMode = file.get("general", "displayMode", 0, comment);
		chunkRadius = file.get("general", "chunkRadius", 3, "The distance (in chunks) of rendering radius. (default: 3)");
		pollingInterval = file.get("general", "pollingInterval", 200, "The update interval (in milliseconds) of light level. Farther chunks update less frequently. (default: 200)");
	public void update() {
		displayMode.set(Math.min(Math.max(displayMode.getInt(0), 0), displayModeName.size() - 1));
		chunkRadius.set(Math.min(Math.max(chunkRadius.getInt(3), 1), 9));
		pollingInterval.set(Math.min(Math.max(pollingInterval.getInt(200), 10), 60000));


Link to comment
Share on other sites

9 minutes ago, Jay Avery said:

Is this the case in 1.11, or is it only in 1.12+?

It is true for 1.11 aswell.


4 minutes ago, fuzzybat23 said:

I'm working with 1.12.  I guess 1.12 doesn't utilize GuiFActory at all anymore, just the @Config?

You can still use both. The annotation based config system is just less messy and easier to use in general.

Link to comment
Share on other sites

14 minutes ago, V0idWa1k3r said:

It is true for 1.11 aswell.

Okay, in that case I'm confused. I've made a simple @Config class, it correctly reads the config file, but the "config" button in the mods list is still greyed out. Does the @Config class need to be registered or anything? 

Link to comment
Share on other sites

12 minutes ago, fuzzybat23 said:

So this created the gui for you.  How did you create your config file or did this automatically do that, too.

Yes it did. I have no idea why it isn't working for Jay and I know that forge indeed automatically creates both the gui and the file for annotation based configs without you explicitly telling it to do so from multiple tests.


14 minutes ago, fuzzybat23 said:

And if so, where does it get the default values for the file?

It takes the values from the fields at the moment of file creation.

Link to comment
Share on other sites

So when you did:

        @Config.Comment("Minimum Y coordinate for this ore")
		public int minHeight;

It created an entry in the config file with minHeight but with no value set, where if you did:

        @Config.Comment("Minimum Y coordinate for this ore")
public int minHeight = 20;

then it would have created the config file with minHeight = 20?

Link to comment
Share on other sites

No, those fields are not static and they can't be assigned to a property as there is no object that hosts them.

When I did this:

public final GenSettings oreAether = new GenSettings(1, 80, 128, 4,-1, 1);

It created a oreaether entry in the config file, and inside that entry it created multiple other entries with their values set according to the arguments i've passed. And this entry was created under a worldgen entry as this specified it:

public static final Generation worldGen = new Generation();

So in the end for that code the config file looks like this:



# Configuration file

general {

    worldgen {
        oreaether {
            # The list of dimension IDs this ore is NOT allowed to generate in
            I:blacklistDimensions <

            # Maximum Y coordinate for this ore

            # Minimum Y coordinate for this ore

            # The amount of times the ore will try to generate in each chunk. Set to less than 1 to turn this into a chance to generate type of value

            # The maximum size of the vein





Link to comment
Share on other sites

Ahhhh, I think I see.  Int... blacklistDimensions ended up with -1, and 1 because they were the only two left and blacklistDimensions is an array, right?  I  wonder, though, why those values were placed in the order they were in your config file then xD  I'd think that triesPerChunk would be at the top of the list, instead blacklistDimensions is.  I'd say that it entered them in reverse order from how they're declared in your code, except that veinSize ends up being the last one entered into the config file.

Edited by fuzzybat23
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • Looked very nice 😀 Effect#removeAttributeModifiers gets called upon effect removal, you may find it useful in your case.
    • @poopoodice Thanks! So, assuming that I got your comment correctly, would this be a proper way to apply an effect:   @Override public void doPostAttack(LivingEntity pAttacker, Entity pTarget, int pLevel) { // IF: Code is executing on the client. if (pAttacker.level.isClientSide()) { return; } // IF: Entity is not living entity. if (!(pTarget instanceof LivingEntity)) { return; } // Cast entity to living entity. LivingEntity mob = (LivingEntity)pTarget; // IF: Player already has effect. if (mob.hasEffect(PlayerMobEffects.STUNNED.get())){ return; } // Calculate hit chance. int hitChance = pLevel * 10; // Create random generator. Random random = new Random(); // Get number between 0 and 99. int roll = random.nextInt(100); // IF: Apply on hit chance was missed. if (roll >= hitChance) { return; } // Calculate duration of the effect int duration = 20 * pLevel; // Create instance of effect. MobEffectInstance effect = new MobEffectInstance(PlayerMobEffects.STUNNED.get(), duration, pLevel); // Apply effect to mob. mob.addEffect(effect); }   I have tested the code and it works, although I am not sure if there is smarter way to apply effect. Still need to figure out how to make mob die after it gets stunned.  
    • Hey,   Is it already possible to login and authenticate when your account has been migrated? Currently I just get the error Migrated.
    • Amplifier is like the strength of the effect, for example movement speed boost with amplifier 0 will be Speed I, and 1 will be Speed II, 2 will be Speed III. Effect is just an...effect type, EffectInstance contains all information for the actual effect being applied to the entity (duration, amplifier...etc). For example there's only an effect type "Speed", but for an EffectInstance it could be: "Level 2 Speed for 10 seconds" "Level 4 Speed for 2 seconds"...   The first method applyEffectTick is called when a effect is impacting the entity, the first arg is the entity that has the effect/is affected by the effect, and amplifier is the strength of the effect. The second method isDurationEFfectTick is to check whether the effect should impact/affect the entity this tick (the effects usually don't trigger every tick!), if true, then the first method is called.  
    • Hi, I have enchantment named "Stun" that can be applied to weapon and that has 3 levels: Level 1 - 10% chance to stun mob for 1 sec Level 2 - 20% chance to stun mob for 2 secs Level 3 - 30% chance to stun mob for 3 secs Plan is to apply MobEffect named StunnedMobEffect on doPostAttack in enchantment. StunnedMobEffect should have variable duration based on the level of enchantment that was applied on weapon. I am trying to find an example on how to do the following: How can I set duration of effect dynamically? How can I track whether duration has expired? I have found these two methods that can be overriden:   public void applyEffectTick(LivingEntity pLivingEntity, int pAmplifier) { public boolean isDurationEffectTick(int pDuration, int pAmplifier) { But looking at the source code of MobEffect class, I can't figure out what pAmplifier is? Any hints / samples on this would be appreciated.          
  • Topics

  • Who's Online (See full list)

  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.