diff --git a/src/lib/translations/en/wiki.json b/src/lib/translations/en/wiki.json index e34cdf2d..90f3fbf1 100644 --- a/src/lib/translations/en/wiki.json +++ b/src/lib/translations/en/wiki.json @@ -88,6 +88,21 @@ "title": "Adding Custom Tools" } }, + "configuration": { + "title": "Configuration", + "getting-started": { + "title": "Getting Started with Quilt Config" + }, + "advanced-configuring": { + "title": "Advanced Configuring" + }, + "config-screen": { + "title": "Setting up a Config Screen" + }, + "metadata": { + "title": "Annotations and Metadata Reference" + } + }, "misc": { "title": "Misc", "commands": { diff --git a/wiki/configuration/+category.yml b/wiki/configuration/+category.yml new file mode 100644 index 00000000..4c4fcea6 --- /dev/null +++ b/wiki/configuration/+category.yml @@ -0,0 +1,2 @@ +name: wiki.configuration.title +index: 6 diff --git a/wiki/configuration/advanced-configuring/+page.yml b/wiki/configuration/advanced-configuring/+page.yml new file mode 100644 index 00000000..42e2404f --- /dev/null +++ b/wiki/configuration/advanced-configuring/+page.yml @@ -0,0 +1,2 @@ +title: wiki.configuration.advanced-configuring.title +index: 2 diff --git a/wiki/configuration/advanced-configuring/en.md b/wiki/configuration/advanced-configuring/en.md new file mode 100644 index 00000000..672ed620 --- /dev/null +++ b/wiki/configuration/advanced-configuring/en.md @@ -0,0 +1,270 @@ +--- +title: Advanced Configuring +index: 2 +--- + +# Advanced configuring + +Simple values are nice and all, but if you have a lot of them it can begin to get unwieldy. In this tutorial, we'll discuss how to organize your config and use processors to get the most out of it. + +## Using sections + +A flat file of dozens of values can get hard to navigate fast, not to mention confusing. Luckily we can organize it into sections using Quilt Config! This is super simple to get up and running. + +Via sections, you can use indentation to visually differentiate parts of the config file for users reading. We're going to add an example section that looks like this in TOML: + +```toml +# ... + +# This isn't actually used by the mod, but I was completely out of ideas for things to add. +typesOfSoup = ["tomato", "borscht", "chicken noodle", "ramen", "STEW", "mushroom"] + +# Advanced settings for advanced users. +[advanced_settings] + # Whether to automatically append newlines to every message printed. + # default: true + printNewlines = true +``` + +To do that, we'll create a section inside our code: + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + @Comment("Advanced settings for advanced users.") + @SerializedName("advanced_settings") + public final AdvancedSettings advancedSettings = new AdvancedSettings(); + + public static class AdvancedSettings extends Section { + @Comment("Whether to automatically append newlines to every message printed.") + @SerializedName("print_newlines") + public final TrackedValue printNewlines = this.value(true); + } +} +``` + +We simply create a new class, inside our config class, that extends `ReflectiveConfig.Section`. Then we have to create another object inside the main config to add the section. Note that this instance of our section is **not** stored inside a `TrackedValue` like everything else, instead it stores `TrackedValue`s inside itself! Now that we have a section, we can add as many values as we want inside. But what if we want to store something more interesting than a basic type, map, or list? Let's serialize a custom object. + +## Serializing custom values + +In Java, you can print to different output streams in the console. These aren't basic integer or float objects, so we can't just save them in our config! This is where the `ConfigSerializableObject` interface comes in. By implementing its three methods, we can set up any class to be usable as a config object. + +The interface works via generics, just like `TrackedValue`. The `` in `ConfigSerializableObject` can be swapped out with any serializable class (remember, by default that's primitive types, `String`, `ValueList`, and `ValueMap`), and the value of your object will be translated into that type to be saved to disk, and then converted back into your custom object when read. To do that translating, we need to implement three methods: +- `T getRepresentation()`: here, your value is converted to the serializable class that you specified in the generics (represented by `T`) so that it can be saved. +- `YourSerializableClass convertFrom(T)`: this one is called when reading the config file, and converts the representation created by `getRepresentation` back to its original type. +- `YourSerializableClass copy()`: makes a copy of the value, which Quilt Config uses internally. + +Enough with the explanations: let's see an example! + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + public static class AdvancedSettings extends Section { + // ... + @Comment("What stream to print the message to.") + @SerializedName("print_stream") + public final TrackedValue printStream = this.value(PrintStreamOption.SYSTEM_OUT); + + @SuppressWarnings("unused") // IDEs won't understand that all options in this enum can be used via the config + public enum PrintStreamOption implements ConfigSerializableObject { + SYSTEM_OUT(System.out), + SYSTEM_ERROR(System.err); + + private final PrintStream printStream; + + PrintStreamOption(PrintStream stream) { + this.printStream = stream; + } + + public PrintStream getStream() { + return this.printStream; + } + + @Override + public PrintStreamOption convertFrom(String representation) { + return valueOf(representation); + } + + @Override + public String getRepresentation() { + return this.name(); + } + + @Override + public PrintStreamOption copy() { + // enum values cannot be duplicated + return this; + } + } + } +} +``` + +This may look like a lot of code, but we've already covered most of it! We're using an `Enum` here, which allows us to clearly define a set of options for our value. If you wanted, you could use a regular class and have an infinite number of possibilities for your config field! An additional benefit of using an `Enum` is that Quilt Config will automagically generate a comment with the possible values in the serialized config file. + +The implementation of `ConfigSerializableObject`'s methods is incredibly simple here: since the values in our enum already have names, we just use the methods `name` and `valueOf(String)` to serialize and deserialize respectively. Cute! Let's look at another example of a custom object that can't be represented in an enum. + +Let's say we want to store a point on a 3d grid in the config. We would need to store an `x` coordinate, a `y` coordinate, and a `z` coordinate for that value. Let's do that! This is simply an example, and won't be used in our mod. + +```java +public class Vec3i implements ConfigSerializableObject> { + public final int x; + public final int y; + public final int z; + + public Vec3i(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Vec3i copy() { + return this; + } + + @Override + public Vec3i convertFrom(ValueMap representation) { + return new Vec3i(representation.get("x"), representation.get("y"), representation.get("z")); + } + + @Override + public ValueMap getRepresentation() { + return ValueMap.builder(0) + .put("x", this.x) + .put("y", this.y) + .put("z", this.z) + .build(); + } +} +``` + +Here we leverage a `ValueMap` instead of a `String` as the serialized type. This allows us to easily distinguish between the `x`, `y`, and `z` fields, and is the data type you'll nearly always use when serializing complex objects like this. We're not going to go in depth here, as the code is fairly self-explanatory now that we understand the `ConfigSerializableObject` interface. + +## Using processors + +Now that we've learned all about values, let's learn how to do evil things: introducing `Processor`s. +This devious annotation allows you to configure your configs and their values, as well as add modification callbacks, which allow you to run code when the value is changed. +The annotation works via allowing you to point to code that will be called as the config is built. +First we'll set up a simple processor that prints to the console when the config starts to be loaded. + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +@Processor("processConfig") +public class ExampleModConfig extends ReflectiveConfig { + public void processConfig(Config.Builder builder) { + System.out.println("Loading config!"); + } + + // ... +} +``` + +With that, our config will print "Loading config!" before any of its values are deserialized. Note despite the method name passed to `@Processor` not coming with any parameter information, we still had to put a `Config.Builder` on our method: what's up with that? +Processors can be attached to three different types: tracked values, config sections, and config classes. For each, the parameter will be different, as documented in `Processor`'s Javadoc: +- When used on a tracked value, the processor method will take a `TrackedValue.Builder` as its parameter. +- When used on a section, the processor method will take a `SectionBuilder` as its parameter. +- When used on a config class, the processor method will take a `Config.Builder` as its parameter. + +But there's more that we can do with processors than just printing nonsense to the command line! Let's see what we can do with that `Builder` object we're being offered. +On both tracked values and config classes, we're able to leverage a method called `callback` to set up code that runs when the config is changed! + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +@Processor("processConfig") +public class ExampleModConfig extends ReflectiveConfig { + public void processConfig(Config.Builder builder) { + System.out.println("Loading config!"); + builder.callback(config -> System.out.println("Updated!")); + } + + // ... +} +``` + +With that line, we've expanded our logging to now tell us whenever a config value is updated! Neat, but what else can we do with callbacks? + +One example of a callback usage is syncing a value between your config field and another. This could be needed for many reasons: your config value is complicated, and you want to make it easier to access, or maybe you need to update the configuration of one of the libraries you depend on when the value is changed ([enigma]() does just that!). +We're going to set up a shortcut to accessing the print stream made available in `printStream`, that doesn't force you to go through two separate getters to use. To do that, we can use a processor applied to the field! + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +@Processor("processConfig") +public class ExampleModConfig extends ReflectiveConfig { + // ... + public static class AdvancedSettings extends Section { + // ... + @Processor("processPrintStream") + public final TrackedValue printStream = this.value(PrintStreamOption.SYSTEM_OUT); + public transient PrintStream activeStream = printStream.value().getStream(); + + public void processPrintStream(TrackedValue.Builder builder) { + builder.callback(value -> activeStream = printStream.value().getStream()); + } + + // ... + } +} +``` + +Using our callback, we update the `activeStream` variable each time that the print stream is changed. This keeps it perfectly in sync with the `printStream` field at all times! Note that we mark it as `transient`, a keyword which tells Java (and subsequently Quilt Config!) not to serialize the value. +Now instead of dealing with `ExampleModConfig.INSTANCE.advancedSettings.printStream.value().getStream()` we can simply do `ExampleModConfig.INSTANCE.advancedSettings.activeStream`, simplifying our lives a little when interacting with the config. The power of processors, in action. + +## Changing the config format + +Let's get into how you choose a file format to save to. Quilt Config currently only provides two serializers: `json5`, an extension of the JSON format to allow cleaner syntax and comments, and `toml`, with the default being `toml`. If you want to switch to `json5`, we can do that using a `Processor`! +We'll need to apply this processor globally to our config, since the way we'll be changing the format is via the `Config.Builder` object a config class processor will provide. + +This processor needs to run before the config is read, so we're going to place it directly on the class: + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +@Processor("processConfig") +public class ExampleModConfig extends ReflectiveConfig { + public void processConfig(Config.Builder builder) { + // ... + builder.format("json5"); + } + + // ... +} +``` + +With our knowledge of processors, this is simple! You can also use the config builder to add new fields, new sections, and update metadata (TODO LINK TO METADATA PAGE), on top of changing the format and using callbacks as we've already covered. + +## Adding multiple files + +For massive mods, a single config file can become unwieldy, even when organized into sections. +Luckily, Quilt Config is designed to easily support adding multiple config files: by default, your config files are all placed into their own directory. +To add a second config file inside that directory, we must make another config class: let's call this one `ExampleModConfig2`. +We'll also have to update the name of our original config file to be more specific: + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + public static final ExampleModConfig INSTANCE = QuiltConfig.create(ExampleMod.MOD_ID, "main", ExampleModConfig.class); +} +``` + +Instead of using the mod ID as the name of our class, we call our original config `main` instead. +Now let's create a second config: + +`src/main/com/example/example_mod/ExampleModConfig2`: + +```java +public class ExampleModConfig2 extends ReflectiveConfig { + public static final ExampleModConfig2 INSTANCE = QuiltConfig.create(ExampleMod.MOD_ID, "secondary", ExampleModConfig2.class); +} +``` + +Just the same thing as our original, but using the `ExampleModConfig2` class instead of `ExampleModConfig` everywhere. We also name it `secondary`, to go along with the `main` name of our original config. +Now you can add whatever fields you'd like! With Quilt Config, you can repeat this process as much as you like, as long as no configs have duplicate names. diff --git a/wiki/configuration/config-screen/+page.yml b/wiki/configuration/config-screen/+page.yml new file mode 100644 index 00000000..cc71d1dc --- /dev/null +++ b/wiki/configuration/config-screen/+page.yml @@ -0,0 +1,2 @@ +title: wiki.configuration.config-screen.title +draft: true diff --git a/wiki/configuration/config-screen/en.md b/wiki/configuration/config-screen/en.md new file mode 100644 index 00000000..a5ddc8be --- /dev/null +++ b/wiki/configuration/config-screen/en.md @@ -0,0 +1 @@ +this page will be filled out once QSL has automatic config screen generation in place! unless someone else wants to write it for spruceui or something. if so have fun! diff --git a/wiki/configuration/getting-started/+page.yml b/wiki/configuration/getting-started/+page.yml new file mode 100644 index 00000000..7d56dbc7 --- /dev/null +++ b/wiki/configuration/getting-started/+page.yml @@ -0,0 +1,2 @@ +title: wiki.configuration.getting-started.title +index: 1 diff --git a/wiki/configuration/getting-started/en.md b/wiki/configuration/getting-started/en.md new file mode 100644 index 00000000..1e47d0b4 --- /dev/null +++ b/wiki/configuration/getting-started/en.md @@ -0,0 +1,153 @@ +# Getting started with Quilt Config + +[Quilt Config](https://github.com/QuiltMC/quilt-config) is Quilt's powerful configuration API. It allows developers (that's you!) to easily set up values that can be changed by the user in order to change the behaviour of your mod. It's built into [Quilt Loader](https://github.com/QuiltMC/quilt-loader) to allow any Quilt mod to leverage it without having to pull in new dependencies or run any setup. + +## Setting up your config + +In this tutorial, we're going to be building a simple mod that prints a line of text to the console when it's initialized. Keep in mind that this is an example, and your mod can use config values anywhere you want! You can view of the final source code of this tutorial [on GitHub](https://github.com/ix0rai/quilt-config-example-mod). + +To begin with Quilt Config, you'll need a dedicated class for holding your config. For an extremely configurable mod, you may want to break up your config into multiple files inside a `config` package, but for this tutorial and most mods we're going to put everything in one file. + +Let's create a new file for our config, in the same directory as our mod initializer. We'll call it `ExampleModConfig`, but you can call it whatever you want. This new config class will extend the `ReflectiveConfig` API, which will provide everything we need to get the config working. + +We'll start with this code: + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + public static final ExampleModConfig INSTANCE = QuiltConfig.create(ExampleMod.MOD_ID, ExampleMod.MOD_ID, ExampleModConfig.class); +} +``` + +Now that big line in the middle may seem intimidating, but we're going to break it down. +- First, `public static final` means that the value never changes (though data *stored* in the config can), and `instance` is a fancy programmer word that just means it's an object of a class. We create the object in this way since it's the only instance of the config we'll ever be making. +- Second, we're calling the method `QuiltConfig.create(String family, String id, Class configCreatorClass)`. As you can see, it takes three parameters: + - The string `family`, which indicates the directory that your config file will be stored in, relative to the global config directory (usually `/config`). We're using our mod ID as the directory, and that's the best practice. + - A second string, `id`, which will be the name of the configuration file (before the file extension is added, which changes depending on the format you choose). Here we use our mod ID once again, but if you have a complicated config with multiple files you'll want to use a different name. + - Finally, something confusing: what is a `Class configCreatorClass`? We're not going to worry too much about the details: just pass in the name of your config class with `.class` appended, as we've done here with `ExampleModConfig.class`. + +## Adding values + +That's it! We now have a config that we can access anywhere in our project. Problem is, there's no reason to access it because there's nothing there. Let's fix that by adding a value! Our example mod prints a line of text to the console when started up: why don't we allow the user to decide what that line is. Let's add a second field to our config: + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + public final TrackedValue message = this.value("rai minecraft :thumbsup:"); +} +``` + +We're introducing a bunch of things with one line again. Lovely! Let's dig into it: +- This value is `public final`, which means that instead of being accessible by anything you need an instance of `ExampleModConfig` first. Note that you *must* use `public final` whenever creating a config value using Quilt Config. Since we defined our `INSTANCE` field already, we'll be able to access this from anywhere via `ExampleModConfig.INSTANCE.message`. +- The type is `TrackedValue`. The angle brackets (`<>`) allow us to use what's called *generics*, which in Java are a way to adapt a class to your specific use case. Here, the `TrackedValue` class allows us to adapt the type of object it holds, so we use it to store a `String`. Thanks to the generics, we could put another type inside those brackets later to store a different kind of value! This is foreshadowing. We're going to store some different values. Get ready. +- We call the `value` method, which comes from the `ReflectiveConfig` class we're extending. This method takes one parameter, which will be the default value of that config field. Here, the author of this tutorial is using it to self-advertise. + +Phew. We got through it! That'll be the final big information dump of this tutorial, and we can begin having some fun. Now that we have a field in our config, we can pop back over to our mod initializer and start using it! + +`src/main/com/example/example_mod/ExampleMod`: + +```java +public class ExampleMod implements ModInitializer { + // ... + + @Override + public void onInitialize(ModContainer mod) { + LOGGER.info(ExampleModConfig.message.value()); + } +} +``` + +Since the `message` field is a `TrackedValue` instead of a simple `String`, we need to call the `value` method on it to get its value to print. We'll also be able to use the `setValue` method to change the value from code. + +Now our mod prints a custom message to the console on startup! While this would already get us an easy 1 million downloads, Quilt Config allows us to do so much more. + +## Using annotations + +Let's add a second field. Here we're going to get fancy with it, using both a new data type and one of Quilt Config's many fancy annotations. + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + @IntegerRange(min = 0, max = 10) + public final TrackedValue timesToPrint = this.value(1); +} +``` + +We're not going to show the changes to the code to use each one of these values, but remember you can see how they're used in the [final code](https://github.com/ix0rai/quilt-config-example-mod). Let's get back on our bullet points to explain this snippet! +- `IntegerRange` is an annotation: it adds new functionality to our field, outside the normal declaration. `IntegerRange` allows us to limit the allowed values of our `Integer` field, to between `min` and `max` (inclusively). +- We've changed the type from `String` to `Integer`. Now, why `Integer` instead of `int` like we would use for a normal number field? `int` is a *primitive type*, which means that it isn't actually a class! Since generics can only take classes, Java provides class versions of each primitive. `boolean` becomes `Boolean`, `float` becomes `Float`, `double` becomes `Double`, `char` becomes `Character`, etc etc. + +Something incredibly important to remember is that you can't just send any class into `TrackedValue`'s generics: Quilt Config has to know how to serialize it. By default, all [primitive types](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) are serializable, as well as the `String` class. We'll cover how to explain to Quilt Config how to serialize a class later, but first: more fun with annotations! + +Anyway. New value! + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + @Comment("Whether to print the message at all.") + public final TrackedValue print = this.value(true); +} +``` + +Here we simply leverage the `Comment` annotation to add a comment explaining exactly what the config field does. This will be saved by Quilt Config in the config file, as long as the format you use supports comments. Neat! + +Finally, we're going to use one more annotation to tie it all together. By default, Quilt Config serializes to [TOML](https://toml.io/en/) which has a couple standards we're not following. In Java, `public final` fields are named using `lowerCamelCase`, like our `timesToPrint` field. But, in a TOML file, names should use `snake_case`, meaning we should have named the field `times_to_print`. To match both conventions, we can use the `SerializedName` annotation! + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + @IntegerRange(min = 0, max = 10) + @SerializedName("times_to_print") + public final TrackedValue timesToPrint = this.value(1); + // ... +} +``` + +Problem solved! Now it'll follow the Java conventions in the Java code, and the TOML conventions in the saved TOML file. We can apply `SerializedName` annotations to every multi-word config field to solve our problems everywhere. + +## Maps and lists + +Two of the most common data structures used in Java programming are `Map` and `List`. Quilt Config provides convenient serializable versions of both of these in the for of `ValueMap` and `ValueList`! + +Starting with maps, `ReflectiveConfig` provides us a handy `map` method to help us easily make a default value. + +`src/main/com/example/example_mod/ExampleModConfig`: + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + @Comment("When a key in this map shows up in the message, it'll be replaced with the corresponding value.") + public final TrackedValue> replacements = this.map("") + .put("oro armor", "rai minecraft") + .put("lambda aurora", "rai minecraft") + .put("tib s", "rai minecraft") + .build(); +} +``` + +There are a few things of note here: +- We have to pass an empty string (`""`) to the `map` method, in order for it to understand that the map stores values of the type `String`. This value is not used beyond checking the type, so you can put whatever you'd like there. +- `ValueMap` always uses `String` as the type for its keys, instead of having you pick like most Java `Map` implementations. +- We can put as many values in the default map as we'd like. If the author wanted to replace every single Quilt developer with herself instead of just the admins, that would be possible! + +Now that we know how to use maps, onward to lists! + +```java +public class ExampleModConfig extends ReflectiveConfig { + // ... + @Comment("This isn't actually used by the mod, but I was completely out of ideas for things to add.") + @SerializedName("types_of_soup") + public final TrackedValue> typesOfSoup = this.list("", "tomato", "borscht", "chicken noodle", "ramen", "STEW", "mushroom"); +} +``` + +This is pretty similar to building a list. Instead of chaining `put` calls on a builder, we simply add as many values as we want directly in the constructor. Again, the first argument is unused and is to help Quilt Config infer the type. With that, we've made an excellent little config for our mod! If you want to know more, let's move on to the [Advanced Configuring tutorial](TODO LINK). diff --git a/wiki/configuration/metadata/+page.yml b/wiki/configuration/metadata/+page.yml new file mode 100644 index 00000000..1b3d167c --- /dev/null +++ b/wiki/configuration/metadata/+page.yml @@ -0,0 +1,2 @@ +title: wiki.configuration.metadata.title +index: 3 diff --git a/wiki/configuration/metadata/en.md b/wiki/configuration/metadata/en.md new file mode 100644 index 00000000..1d3f2307 --- /dev/null +++ b/wiki/configuration/metadata/en.md @@ -0,0 +1,62 @@ +# Annotations and Metadata Reference + +Quilt Config is absolutely packed full of annotations, with each one allowing you to expand on the functionality of your config! This page serves as a reference, giving an overview of the functionality of each annotation. More in-depth information can be found in the annotations' Javadoc. + +## `@Comment` + +- Usable on config fields and sections +- Can be repeated infinitely on either member + +Allows you to attach comments to your fields, which are saved as metadata. By default, they will be serialized to config files in both the `toml` and `json5` formats. Since they're saved as metadata, they will be accessible anywhere you have the `TrackedValue` for your config field, meaning you can display them in config screens and otherwise. + +## `@FloatRange` and `@IntegerRange` + +- Usable on config fields of type `Float` and type `Integer` respectively +- Can only be applied once per field + +On startup and when the annotated field is changed, checks if the number value of the field is in between the provided `min` and `max`, inclusively. If not, errors with a `TrackedValueException`. + +## `@Matches` + +- Usable on config fields of type `String` +- Can only be applied once per field + +On startup and when the annotated field is changed, checks if the `String` value of the field matches the provided [regular expression](https://regexr.com/). If not, errors with a `TrackedValueException`. + +## `@Processor` + +- Usable on config classes, fields, and sections +- Can only be applied once per member + +Allows you to set up methods that will run before the config is initialized. Takes a `String` parameter that matches the method name of a method inside your class: this method will be run before config initialization. You must give the method one parameter corresponding to the type annotated with `@Processor`: +- When used on a tracked value, the processor method will take a `TrackedValue.Builder` as its parameter. +- When used on a section, the processor method will take a `SectionBuilder` as its parameter. +- When used on a config class, the processor method will take a `Config.Builder` as its parameter. + +Processors allow you to do a large variety of things, including: +- Running custom code when values are edited +- Adding new values and sections programmatically +- Dynamically updating metadata + +You can see example processors and a more in-depth look at their usage in the [advanced configuring tutorial](TODO LINK). + +## `@SerializedName` + +- Usable on config fields and sections +- Can only be applied once per member + +Adds a piece of `String` metadata that's used as the name that will be serialized in the config file. The default name remains a fallback: if the serial name is not present in the file during deserialization, the default name will be used. The serial name will always be used when writing to the file. + +## `@SerializedNameConvention` + +- Usable on config classes, sections and fields +- Can only be applied once per member + +Adds name convention metadata to each field inside the config class, which dictates a casing to be used when saving to the config. This will transform the default name of the field to apply the casing stored in the metadata. If the field has a serial name, that name will take priority over the transformed name from this annotation. Casing options are provided and documented in `NamingSchemes`. When used under a parent that also has this annotation, overwrites for that element only (for example, if a class is annotated with a certain scheme and a field inside the class is annotated with a different scheme, the field will take the scheme it's annotated with). + +## `@Alias` + +- Usable on config fields and sections +- Can be repeated infinitely on either member + +Adds a piece of metadata dictating past names of this field. These names will be considered during deserialization, but will never be saved to the config file. This is useful for migrating configs to new names after changing things!