Configs

This page is under construction.

This page is incomplete, and needs more work. Feel free to edit and improve this page!

Creating configs in Forge is fairly simple as Forge provides a ton of tools for configs. In addition, Forge will automatically update the config values you read in code and will also cache the config values for you.

To begin, create a new class file and add this:

public static final ForgeConfigSpec GENERAL_SPEC;
    
static {
    ForgeConfigSpec.Builder configBuilder = new ForgeConfigSpec.Builder();
    setupConfig(configBuilder);
    GENERAL_SPEC = configBuilder.build();
}

private static void setupConfig(ForgeConfigSpec.Builder builder) { 
}

ForgeConfigSpec is what will ultimately hold all the data and info about how to make and read your config file. We will be registering this later. In the static block, this will create the builder for ForgeConfigSpec, pass it into setupConfig where we will add all the config entries we want, and then we build the final ForgeConfigSpec to store into GENERAL_SPEC.

Now for creating config entries, lets add an integer config entry by creating a public static field for it and use builder.defineInRange to define it.

public static ForgeConfigSpec.IntValue exampleIntConfigEntry;

private static void setupConfig(ForgeConfigSpec.Builder builder) {
    exampleIntConfigEntry = builder.defineInRange("example_int_config_entry", 5, 2, 50);
}

Here, setupConfig method will add an config entry to the builder. This defineInRange states that there should be a "example_int_config_entry" entry in the toml file, it has a default value of 5, and will only accept a user-entered value between 2 and 50. The it assigns that resultant ForgeConfigSpec.IntValue to the exampleIntConfigEntry field. Now the exampleIntConfigEntry field can be call with .get() anywhere in our code to get the current config value for that config entry at the time.

There are many kinds of config entries you can make. Some of the more commonly used ones are:

  • ForgeConfigSpec.IntValue - uses defineInRange
  • ForgeConfigSpec.DoubleValue - uses defineInRange
  • ForgeConfigSpec.LongValue - uses defineInRange
  • ForgeConfigSpec.BooleanValue - uses define
  • ForgeConfigSpec.ConfigValue<String> - uses define
  • ForgeConfigSpec.ConfigValue<List<? extends String>> - uses defineList (IMPORTANT, do not use .define for lists. Always .defineList)

As for the methods you can use for each of these config types:

  • .define - takes the default config value.
  • .defineInRange - takes the default, the minimum, and maximum config values in that order.
  • .defineList - takes the default list to use and a validator to run on each list entry when the user tries to change the config file to make sure it is correctly edited by user.

Furthermore, you may attach a comment or translation key to your builder that is creating the config entries. And using builder.push("...") and builder.pop(); before and after some config entries will put them into a category. (comments can be attached to categories as well) Here is a large example of all this:

public static ForgeConfigSpec.IntValue exampleIntConfigEntry;
public static ForgeConfigSpec.DoubleValue exampleDoubleConfigEntry;
public static ForgeConfigSpec.ConfigValue<Double> exampleUnboundedDoubleConfigEntry;
public static ForgeConfigSpec.LongValue exampleLongConfigEntry;
public static ForgeConfigSpec.BooleanValue exampleBooleanConfigEntry;
public static ForgeConfigSpec.ConfigValue<String> exampleStringConfigEntry;
public static ForgeConfigSpec.ConfigValue<List<? extends String>> exampleStringListConfigEntry;

private static void setupConfig(ForgeConfigSpec.Builder builder) {
    builder.comment(" This category holds configs that uses numbers.")
    builder.push("Numeric Config Options");

      exampleIntConfigEntry = builder.defineInRange("example_int_config_entry", 5, 2, 50);
      exampleDoubleConfigEntry = builder.defineInRange("example_double_config_entry", 10D, 0D, 100D);

      exampleUnboundedDoubleConfigEntry = builder
         .comment("This comment will be attached to example_unbounded_double_config_entry in the config file.")
         .define("example_unbounded_double_config_entry", 1000D);

      exampleLongConfigEntry = builder.defineInRange("example_long_config_entry", 4L, -900L, 900L);

    builder.pop();


    builder.comment(" This category holds configs that uses strings.")
    builder.push("String Config Options");

      exampleStringConfigEntry = builder
         .comment("This config holds a single string.")
         .define("example_string_config_entry", "player444");

      builder.comment(" This category will be nested inside the String Config Options category.")
      builder.push("Nested Category");

        exampleStringListConfigEntry = builder
          .comment("This config entry will hold a list of strings.")
          .defineList("example_string_list_config_entry", Arrays.asList("pie", "trains"), entry -> true);

      builder.pop();
    builder.pop();
}

Now it is time to register your config file so that Forge can create and read the config file. Doing so, you simply call ModLoadingContext.get().registerConfig and pass in, the ModConfig.Type, the GENERAL_SPEC from your config file, and the name of the config file. (make sure you include .toml as well) Here's what that would look like:

ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModConfig.GENERAL_SPEC, "modconfig.toml");
NOTE: Forge will only update the values of your config fields in code after the registry events are finished. Therefore, you should only attempt to grab the config values after the registry events are completed. (FMLCommonSetupEvent and later are safe)

The ModConfig.Type determines where your config files goes and its behavior. While COMMON is generally used the most, the other config types do have specific uses. A simple summary is:

  • ModConfig.Type.COMMON - Exists in the config folder above the mods folder and will apply to all worlds created. (Works for both client and servers)
  • ModConfig.Type.CLIENT - Same as COMMON except the config file is only ever loaded on the client. Never on server.
  • ModConfig.Type.SERVER - Exists only in a world's own config folder on a per-world basis. This is read on both server and on single player as well.

If you are going to have many config files for organization, you make want to put them into a specific folder for your mod within the config folder. Such as configs/modid/blocks.toml and configs/modid/entities.toml However, you would need to make that new folder first as Forge will not do it for you. Example:

FileUtils.getOrCreateDirectory(FMLPaths.CONFIGDIR.get().resolve("modid"), "modid");
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModIdConfigs.GENERAL_SPEC, "modid/blocks.toml");
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModIdConfigs.GENERAL_SPEC, "modid/entities.toml");

Now your configs are setup, you know a decent chunk of how you can utilize Forge configs, and you are ready to go!