Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a section about quilt config #81

Merged
merged 18 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 133 additions & 6 deletions wiki/configuration/advanced-configuring/en.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,30 @@ title: Advanced Configuring
index: 2
---

# Advanced configuring - WORK IN PROGRESS
# 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 organise your config and use processors to get the most out of it.
ix0rai marked this conversation as resolved.
Show resolved Hide resolved

## Using sections

A flat file of dozens of values can get hard to navigate fast, and not to mention confusion. Luckily we can organise it into sections using Quilt Config! This is super simple to get up and running:
A flat file of dozens of values can get hard to navigate fast, and not to mention confusion. Luckily we can organise it into sections using Quilt Config! This is super simple to get up and running.
ix0rai marked this conversation as resolved.
Show resolved Hide resolved

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_ettings]
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
# 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`:

Expand Down Expand Up @@ -41,6 +58,8 @@ The interface works via generics, just like `TrackedValue`. The `<T>` in `Config

Enough with the explanations: let's see an example!

`src/main/com/example/example_mod/ExampleModConfig`:

```java
public class ExampleModConfig extends ReflectiveConfig {
// ...
Expand Down Expand Up @@ -129,15 +148,123 @@ Here we leverage a `ValueMap` instead of a `String` as the serialized type. This
## 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.

## Adding multiple files
`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](<https://github.com/QuiltMC/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<PrintStreamOption> printStream = this.value(PrintStreamOption.SYSTEM_OUT);
public transient PrintStream activeStream = printStream.value().getStream();

public void processPrintStream(TrackedValue.Builder<PrintStreamOption> builder) {
builder.callback(value -> activeStream = printStream.value().getStream());
}

// ...
}
}
```

g
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`, which is the default format it serializes to. If you want to switch to `json5`, we can do that using a `Processor`!
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:

https://github.com/hibiii/BlindMe/blob/main/src/main/java/hibi/blind_me/Config.java use processor to run builder.format
`src/main/com/example/example_mod/ExampleModConfig`:

```java
@Processor("processConfig")
public class ExampleModConfig extends ReflectiveConfig {
public void processConfig(Config.Builder builder) {
// ...
builder.format("json5");
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
}

// ...
}
```

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.
ix0rai marked this conversation as resolved.
Show resolved Hide resolved

## Adding multiple files

For massive mods, a single config file, even organised into sections, can become unwieldy.
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
Luckily, Quilt Config is designed to easily support adding multiple config files!
To add a second config file, 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.
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion wiki/configuration/config-screen/+page.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
title: wiki.configuration.config-screen.title
index: 3
draft: true
7 changes: 1 addition & 6 deletions wiki/configuration/config-screen/en.md
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
---
title: Setting up a Config Screen
index: 3
---

todo!
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!
2 changes: 1 addition & 1 deletion wiki/configuration/metadata/+page.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
title: wiki.configuration.metadata.title
index: 4
index: 3
Loading