Mod Configurations
Overview
Balm provides a flexible configuration system that supports both declarative and annotation-based approaches.
Declarative vs Annotations
Balm offers two ways to define configurations:
- Annotation-based Configuration - Using annotated Java classes (simple)
- Declarative Configuration - Using a builder pattern (flexible)
Both approaches are backed by the same system and offer the same functionality. It is up to personal preference which one to choose.
Getting Started
To use Balm's configuration system, you need to:
- Create your configuration class (annotation-based) or configuration schema (declarative)
- Register it with Balm in your mod's initializer or
registerConfig()
method
Annotation-based Configuration
The annotation-based approach uses annotations on regular Java fields to define configuration properties.
@Config(value = "yourmodid", type = "common")
public class MyAnnotationConfig {
@Comment("Enables the special feature")
public boolean enableFeature = true;
@Comment("Maximum number of items")
public int maxItems = 100;
@Comment("Message shown to players")
public String welcomeMessage = "Welcome to my mod!";
@Comment("Advanced configuration options")
public AdvancedCategory advanced = new AdvancedCategory();
public static class AdvancedCategory {
@Comment("Multiplier for movement speed")
@Synced // This will sync to clients
public float speedMultiplier = 1.0f;
@NestedType(String.class) // Important to specify on Lists and Sets
@Comment("List of allowed items")
public List<String> allowedItems = Arrays.asList("minecraft:diamond", "minecraft:gold_ingot");
}
}
Registering a configuration class
Register your configuration class in your mod's initialization code:
// in your initializer:
Balm.getConfig().registerConfig(MyAnnotationConfig.class);
// or as part of a BalmModule:
@Override
public void registerConfig(BalmConfig config) {
config.registerConfig(MyAnnotationConfig.class);
}
Under the hood, Balm will automatically create a declarative schema for your configuration class.
Accessing properties
You can retrieve the current configuration with all fields set to their configured values. This config may differ from the local config, as it includes values that were synced from the server.
// Get the active config object
MyReflectionConfig config = Balm.getConfig().getActiveConfig(MyReflectionConfig.class);
// Access properties
boolean featureEnabled = config.enableFeature;
int max = config.maxItems;
float speed = config.advanced.speedMultiplier;
Updating properties
To update a property, you should use the updateLocalConfig()
method and update the field values of the given config object.
Balm.getConfig().updateLocalConfig(MyReflectionConfig.class, config -> {
config.maxItems = 200;
config.advanced.speedMultiplier = 1.5f;
});
Declarative Configuration
The declarative approach uses a builder pattern to define configuration properties.
Creating a configuration schema
public class MyDeclarativeConfig {
// Create a schema with your mod's namespace and config type
public static final ConfigSchemaBuilder schema = BalmConfigSchema.create(
ResourceLocation.fromNamespaceAndPath("yourmodid", "common")
);
// Define a boolean property
public static final ConfiguredBoolean enableFeature = schema
.property("enableFeature")
.comment("Enables the special feature")
.boolOf(true); // default value
// Define an integer property
public static final ConfiguredInt maxItems = schema
.property("maxItems")
.comment("Maximum number of items")
.intOf(100); // default value
// Define a string property
public static final ConfiguredString welcomeMessage = schema
.property("welcomeMessage")
.comment("Message shown to players")
.stringOf("Welcome to my mod!");
// Create a category for related settings
public static final MyCategory advancedSettings = schema
.category("advanced")
.comment("Advanced configuration options")
.via(MyCategory::new);
// Define a nested category
public static class MyCategory extends BalmConfigCategoryInitializer {
public final ConfiguredFloat speedMultiplier = category
.property("speedMultiplier")
.comment("Multiplier for movement speed")
.synced() // This will sync to clients
.floatOf(1.0f);
public MyCategory(ConfigCategoryBuilder category) {
super(category);
}
}
}
Registering a configuration schema
Register your configuration in your mod's initialization code:
// in your initializer:
Balm.getConfig().registerConfig(MyDeclarativeConfig.schema);
// or as part of a BalmModule:
@Override
public void registerConfig(BalmConfig config) {
config.registerConfig(MyDeclarativeConfig.schema);
}
Accessing properties
When accessing properties, Balm will automatically use the current active config. This config may differ from the local config, as it includes values that were synced from the server.
// Access a boolean value
boolean featureEnabled = MyDeclarativeConfig.enableFeature.get();
// Access an integer value
int max = MyDeclarativeConfig.maxItems.get();
// Access a category property
float speed = MyDeclarativeConfig.advancedSettings.speedMultiplier.get();
Updating properties
To update a property, you can use the set()
method - but you must remember to call saveLocalConfig()
afterwards to apply the changes.
// Update a boolean value
MyDeclarativeConfig.enableFeature.set(true);
// Update an integer value
MyDeclarativeConfig.maxItems.set(200);
// Update a category property
MyDeclarativeConfig.advancedSettings.speedMultiplier.set(1.5f);
// Save the updated config
Balm.getConfig().saveLocalConfig(MyDeclarativeConfig.schema);
Network Synchronization of Properties
Properties marked as .synced()
or @Synced
will be synced from server to clients when they join.
These changes are temporary and only apply to the active config, not the local config.
Loader-specific Differences
On Forge and NeoForge, local configs are managed by the mod loader and Balm acts as an access and network synchronization layer.
On Fabric, Balm manages local configs itself using a subset of the TOML format.
The resulting TOML files are identical in both format and naming. However, Fabric uses the StringRepresentable
of an enum for values while Neo/Forge use the enum's name (ignore-case).
Note that only the config types common
and client
are supported across all three mod loaders.
While the Fabric runtime supports custom types as well, using them is not recommended unless you are building a mod exclusively for Fabric (they will crash on Neo/Forge).
On Forge and NeoForge, configs are not loaded instantly. That means you will not have access to your common or client config within your mod initializer.
You can listen to ConfigLoadedEvent
to defer code until your config has been loaded (make sure to check if the schema is yours!).
On NeoForge and Fabric, you can use a config of type startup
which will be loaded immediately, but this is not supported on Forge.
Supported Config Screen
Balm automatically integrates with Mod Menu on Fabric to provide support for the following configuration screen mods:
- Cloth Config
- Configured
Since configs are managed through the default config system in NeoForge and Forge, any configuration screen mod that supports Neo/Forge's config system should work out of the box.
Supported Property Types
Balm supports the following property types:
boolean
int
long
float
double
String
ResourceLocation
(maps tonamespace:path
strings)any enum implementing StringRepresentable
(maps to enum'sgetSerializedName()
)List<any of the above>
Set<any of the above>
If you need property types beyond these, there's a good chance your use case would be better solved through a data pack.