Jump to content

Forge config gui


fuzzybat23

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"));
		displayModeName.add("Standard");
		displayModeName.add("Advanced");
		displayModeName.add("Minimal");
		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.");
		reload();
	}
	
	@SubscribeEvent
	public void onConfigurationChanged(ConfigChangedEvent.OnConfigChangedEvent event) {
		if (event.getModID().equals(LightLevelOverlayReloaded.MODID)) update();
	}
	
	void reload() {
		file.load();
		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)");
		update();
	}
	
	public void update() {
		useSkyLight.set(useSkyLight.getBoolean(false));
		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));
		file.save();
	}
	
}

 

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:

Spoiler

 

# Configuration file

general {

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

            # Maximum Y coordinate for this ore
            I:maxHeight=128

            # Minimum Y coordinate for this ore
            I:minHeight=80

            # 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
            D:triesPerChunk=1.0

            # The maximum size of the vein
            I:veinSize=4
        }

    }

}

 

 

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.

Announcements




  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • They were already updated, and just to double check I even did a cleanup and fresh update from that same page. I'm quite sure drivers are not the problem here. 
    • i tried downloading the drivers but it says no AMD graphics hardware has been detected    
    • Update your AMD/ATI drivers - get the drivers from their website - do not update via system  
    • As the title says i keep on crashing on forge 1.20.1 even without any mods downloaded, i have the latest drivers (nvidia) and vanilla minecraft works perfectly fine for me logs: https://pastebin.com/5UR01yG9
    • Hello everyone, I'm making this post to seek help for my modded block, It's a special block called FrozenBlock supposed to take the place of an old block, then after a set amount of ticks, it's supposed to revert its Block State, Entity, data... to the old block like this :  The problem I have is that the system breaks when handling multi blocks (I tried some fix but none of them worked) :  The bug I have identified is that the function "setOldBlockFields" in the item's "setFrozenBlock" function gets called once for the 1st block of multiblock getting frozen (as it should), but gets called a second time BEFORE creating the first FrozenBlock with the data of the 1st block, hence giving the same data to the two FrozenBlock :   Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=head] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@73681674 BlockEntityData : id:"minecraft:bed",x:3,y:-60,z:-6} Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=3, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=2, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} here is the code inside my custom "freeze" item :    @Override     public @NotNull InteractionResult useOn(@NotNull UseOnContext pContext) {         if (!pContext.getLevel().isClientSide() && pContext.getHand() == InteractionHand.MAIN_HAND) {             BlockPos blockPos = pContext.getClickedPos();             BlockPos secondBlockPos = getMultiblockPos(blockPos, pContext.getLevel().getBlockState(blockPos));             if (secondBlockPos != null) {                 createFrozenBlock(pContext, secondBlockPos);             }             createFrozenBlock(pContext, blockPos);             return InteractionResult.SUCCESS;         }         return super.useOn(pContext);     }     public static void createFrozenBlock(UseOnContext pContext, BlockPos blockPos) {         BlockState oldState = pContext.getLevel().getBlockState(blockPos);         BlockEntity oldBlockEntity = oldState.hasBlockEntity() ? pContext.getLevel().getBlockEntity(blockPos) : null;         CompoundTag oldBlockEntityData = oldState.hasBlockEntity() ? oldBlockEntity.serializeNBT() : null;         if (oldBlockEntity != null) {             pContext.getLevel().removeBlockEntity(blockPos);         }         BlockState FrozenBlock = setFrozenBlock(oldState, oldBlockEntity, oldBlockEntityData);         pContext.getLevel().setBlockAndUpdate(blockPos, FrozenBlock);     }     public static BlockState setFrozenBlock(BlockState blockState, @Nullable BlockEntity blockEntity, @Nullable CompoundTag blockEntityData) {         BlockState FrozenBlock = BlockRegister.FROZEN_BLOCK.get().defaultBlockState();         ((FrozenBlock) FrozenBlock.getBlock()).setOldBlockFields(blockState, blockEntity, blockEntityData);         return FrozenBlock;     }  
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.