-
Notifications
You must be signed in to change notification settings - Fork 5
New Configs
Creating a brand new config system based on Fzzy Config? Lets get started! This guide will walk through the basic steps needed to get a powerful, expressive config up and running.
- Development of Config layout
- Config class(es) creation
- Config Registration
- Polish
- Integration into the Mod
1. Development of a Config Layout ⤴
Before you get into the nitty gritty of your Config implementation, determine a layout first. This layout will play a critical role in how the configs are laid out visually, the user experience of navigating your settings, and how you interact with the config internally.
- This step is so important to all future use of the config that I hesitate to say that there is a "quick and dirty" way to do it. Nevertheless, some mods only have need of a very simplistic config, so little thought needs to go into it.
2. Config Creation ⤴
With your layout in mind, create some config classes and fill them with the settings you need. Configs extend the base class config
.
- Extend
Config
, provide an Identifier, and any custom folder(s) as needed. - Fill in your empty class with settings. Settings have the following considerations:
- fields/properties need to be non-final, non-static, and public*
- check out Validation for more powerful setting configuration and choice-restriction options.
* If you use the IgnoreVisibility
annotation, you can set the visibility less than public (private, protected, etc).
// kotlin
internal class MyConfig: Config(Identifier.of(MOD_ID, "my_config")) {
var bareDouble = 5.0 // A double without validation. It will still have basic type-validation backing it internally.
var booleanThing = false // most primitive types and a wide array of other types are supported by background validation. See Validation for details.
var validatedDouble = ValidatedDouble(5.0, 10.0, 0.0) //this field has defined validation, error correction, and will restrict user inputs to doubles between 0 and 10.
var mySection = MySection() // a section of the config with its own validated fields and other sections as applicable. This will appear in-game as a separate screen "layer" with a breadcrumb leading back to the parent screen.
internal class MySection: ConfigSection() { // a Config Section. Self-serializable. Of course it doesn't have to be defined inside of it's parent class, but it may be convenient
var sectionBoolean = ValidatedBoolean(true) //booleans can have defined validation, but it's not really necessary
var sectionMap = ValidatedIdentifierMap( //validation exists for common collections too, maps, lists, sets, and so on
mapOf(), //empty default map
ValidatedIdentifier.ofTag(Registries.ITEM.getId(Items.IRON_AXE), ItemTags.AXES), // the keys in this map can only be from the AXES tag
ValidatedDouble(1.0, 1.0, 0.0) //map values are double restricted between 0.0 and 1.0
)
}
}
NOTE: For Kotlin users, configs cannot be object
, as they need to be instanced. You can have a separate Config object used internally throughout the mod that references a val
instance of the config class implementation
// java
public class MyConfig extends Config {
public MyConfig() {
super(Identifier.of(MOD_ID, "my_config"));
}
public double bareDouble = 5.0; // A double without validation. It will still have basic type-validation backing it internally.
public boolean booleanThing = false; // most primitive types and a wide array of other types are supported by background validation. See Validation for details.
public ValidatedDouble validatedDouble = new ValidatedDouble(5.0, 10.0, 0.0); //this field has defined validation, error correction, and will restrict user inputs to doubles between 0 and 10.
public MySection mySection = new MySection(); // a section of the config with its own validated fields and other sections as applicable. This will appear in-game as a separate screen "layer" with a breadcrumb leading back to the parent screen.
public static class MySection extends ConfigSection { // a Config Section. Self-serializable. Of course it doesn't have to be defined inside of it's parent class, but it may be convenient
public MySection() {
super();
}
public ValidatedBoolean sectionBoolean = ValidatedBoolean(true); //booleans can have defined validation, but it's not really necessary
public ValidatedIdentifierMap<Double> sectionMap = ValidatedIdentifierMap( //validation exists for common collections too, maps, lists, sets, and so on
new LinkedHashMap(), //empty default map
ValidatedIdentifier.ofTag(Registries.ITEM.getId(Items.IRON_AXE), ItemTags.AXES), // the keys in this map can only be from the AXES tag
new ValidatedDouble(1.0, 1.0, 0.0) //map values are double restricted between 0.0 and 1.0
);
}
}
3. Config Registration ⤴
Configs are accessed via an INSTANCE
pattern. Access in this manner, with the API calls for registration and loading described below, prevents "too-early" access to a config that hasn't been populated with live data.
-
registerAndLoadConfig
. The API call for registering and loading a config from file, as the name implies. It will also automatically create the file if none exists. Select yourRegisterType
based on the user needs for the config settings-
BOTH
- Default, will have a GUI client side, and will also auto-sync between servers and clients. -
CLIENT
- Will only have GUI functionality, no syncing to/from servers. -
SERVER
- No GUI, only auto-syncing between servers and clients (Not Recommended, the only way to edit these configs is directly the .toml).
-
// kotlin
object Configs {
//instance of your config loaded from file and automatically registered to the SyncedConfigRegistry and ClientConfigRegistry using the getId() method
var myConfig = ConfigApi.registerAndLoadConfig(::MyConfig)
//adding the registerType, you can register a config as client-only. No syncing will occur. Useful for client-only mods.
var myClientOnlyConfig = ConfigApi.registerAndLoadConfig(::MyConfig, RegisterType.CLIENT)
//adding the registerType, you can register a config as sync-only. Their won't be any client-side GUI functionality, so the config will only be editable from the file itself, but it will auto-sync with clients.
var mySyncedOnlyConfig = ConfigApi.registerAndLoadConfig(::MyConfig, RegisterType.SERVER)
//Init function would be called in ModInitializer or some other entrypoint. Not strictly necessary if loading on-reference is ok.
fun init() {}
}
// java
class Configs {
//instance of your config loaded from file and automatically registered to the SyncedConfigRegistry and ClientConfigRegistry using the getId() method
//ConfigApiJava can come in handy to avoid pernicious compiler errors depending on your IDE and gradle setup.
public static MyConfig myConfig = ConfigApiJava.registerAndLoadConfig(MyConfig::new);
//adding the registerType, you can register a config as client-only. No syncing will occur. Useful for client-only mods.
public static MyConfig myClientOnlyConfig = ConfigApiJava.registerAndLoadConfig(MyConfig::new, RegisterType.CLIENT);
//adding the registerType, you can register a config as sync-only. Their won't be any client-side GUI functionality, so the config will only be editable from the file itself, but it will auto-sync with clients.
public static MyConfig mySyncedOnlyConfig = ConfigApiJava.registerAndLoadConfig(MyConfig::new, RegisterType.SERVER);
//Init function would be called in ModInitializer or some other entrypoint. Not strictly necessary if loading on-reference is ok.
public static void init() {}
}
4. Polish ⤴
Your basic config is complete! But the features are only just beginning. Read on to take your config to the next level! Or don't. Fzzy Config will work for you either way :).
Already mentioned in step 2, Fzzy Config comes with a powerful suite of validation tools. Among many other features, these tools:
- Validate inputs (surprising, I know)
- Correct improper inputs
- Provide input restrictions
- Suggest inputs to users
Learn more about validation opportunities at Validation.
Fzzy Config has a variety of annotations that provide secondary functions a config author might have need of:
- Version Control
- Annotation-based validation for primitives
- Comments
- Server config permission control
- Various flags; client only, restart-triggering, and more.
Annotations are covered in detail at Annotations.
Most aspects of a config are automatically translatable. You don't have to translate anything, but it's strongly recommended. If you don't, Fzzy Config will do it's bast to generate human-readable names from the class and field names of the configs themselves.
Translations are covered in detail at Translation.
5. Integration into your Mod ⤴
You've got a shiny, polished up config! How do you use it? There are two basic answers, both very easy!
- Simply call the fields as you normally would.
-
ValidatedField
instances are suppliers. Simply add.get()
to the end.
// kotlin
val myConfigDouble = Configs.myConfig.bareDouble //plain fields are just plain fields. access them like normal!
val myConfigSectionValidated = Configs.myConfig.mySection.sectionBoolean.get() //defined validation wraps your values in a Supplier.
// java
double myConfigDouble = Configs.myConfig.bareDouble; //plain fields are just plain fields. access them like normal!
boolean myConfigSectionValidated = Configs.myConfig.mySection.sectionBoolean.get(); //defined validation wraps your values in a Supplier.