# Table of Contents - [Getting started | YetAnotherConfigLib](#getting-started-yetanotherconfiglib) - [Overview | YetAnotherConfigLib](#overview-yetanotherconfiglib) - [Installing YACL | YetAnotherConfigLib](#installing-yacl-yetanotherconfiglib) - [Special Options | YetAnotherConfigLib](#special-options-yetanotherconfiglib) - [Controllers | YetAnotherConfigLib](#controllers-yetanotherconfiglib) - [Home | YetAnotherConfigLib](#home-yetanotherconfiglib) - [Basic usage of Config API | YetAnotherConfigLib](#basic-usage-of-config-api-yetanotherconfiglib) - [Config API | YetAnotherConfigLib](#config-api-yetanotherconfiglib) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Getting started | YetAnotherConfigLib](#getting-started-yetanotherconfiglib) - [Basic usage of Config API | YetAnotherConfigLib](#basic-usage-of-config-api-yetanotherconfiglib) - [Home | YetAnotherConfigLib](#home-yetanotherconfiglib) --- # Getting started | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started.md) . This wiki is currently a work-in-progress and is incomplete! Before we begin, it's important to note the wiki code examples will be using official Mojang mappings. You are also expected to have a basic knowledge of the Java programming language. If you don't, please learn Java first. There is a simple structure: categories contains groups, groups contain options. You can also skip the groups and just add options to the category directly. They will always appear above any groups. Before we start, lets go into detail about how to construct an `Option`, then we'll use that to make a GUI. Copy Option.createBuilder() // boolean is the type of option we'll be making .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This text will appear as a tooltip when you hover over the option."))) .binding( true, // the default value () -> this.myBooleanOption, // a getter to get the current value from newVal -> this.myBooleanOption = newVal ) .controller(TickBoxControllerBuilder::create) .build() An important concept in YACL options are the controllers. Each option type does not have a hardcoded way of being displayed. The logic of displaying the option in the GUI is held in the `Controller`. To learn more about controllers, click here. You will see in the above example, we're choosing to use a tick-box to display and control the boolean option. To start making a GUI with YACL, you will need to build an instance of `YetAnotherConfigLib`. We will plug in our `Option` code from above... Copy YetAnotherConfigLib.createBuilder() .title(Component.literal("Used for narration. Could be used to render a title in the future.")) .category(ConfigCategory.createBuilder() .name(Component.literal("Name of the category")) .tooltip(Component.literal("This text will appear as a tooltip when you hover or focus the button with Tab. There is no need to add \n to wrap as YACL will do it for you.")) .group(OptionGroup.createBuilder() .name(Component.literal("Name of the group")) .description(OptionDescription.of(Component.literal("This text will appear when you hover over the name or focus on the collapse button with Tab."))) .option(Option.createBuilder() .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This text will appear as a tooltip when you hover over the option."))) .binding(true, () -> this.myBooleanOption, newVal -> this.myBooleanOption = newVal) .controller(TickBoxControllerBuilder::create) .build()) .build()) .build()) .build() All you have to do then is tell YACL to generate a Screen instance from it. Copy YetAnotherConfigLib.createBuilder() [...] .build() .generateScreen(parentScreen) // the screen that opens up when you close YACL You must generate a new instance of `YetAnotherConfigLib` every time you want a GUI `Screen`. You cannot just call `generateScreen()` again! It's that simple! You have made your first GUI with YACL! ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252Fem9v9MPOFoEdIeuhFxTV%252FScreenshot%25202023-11-26%2520at%252014.23.12.png%3Falt%3Dmedia%26token%3Dff9c751b-b9ec-43ec-b0a2-4da45898c0a9&width=768&dpr=3&quality=100&sign=565ba8df&sv=2) Displaying the GUI[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started#displaying-the-gui) ---------------------------------------------------------------------------------------------------------------------- Now you've learned the basics of creating a config GUI, but how do you show it to the user? ### Mod Menu (Fabric)[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started#mod-menu-fabric) [Mod Menu](https://modrinth.com/mod/modmenu) is an extremely popular mod for Fabric that adds a menu that displays a list of currently installed mods, like Forge. You can use its API to add a config button to your mod's entry that opens up your newly created YACL config screen. #### Adding the dependency[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started#adding-the-dependency) First, add the repository to your build.gradle. Groovy DSL (build.gradle) Kotlin DSL (build.gradle.kts) Copy repositories { maven { name = "Terraformers" url = "https://maven.terraformersmc.com/" } } Copy repositories { maven("https://maven.terraformersmc.com/") { name = "Terraformers" } } Then, add the dependency Groovy DSL (build.gradle) Kotlin DSL (build.gradle.kts) Copy dependencies { modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") } Copy dependencies { modImplementation("com.terraformersmc:modmenu:${property("modmenu_version")}") } Then, define the version of Mod Menu you're using in your `gradle.properties`. You can get the latest version number [here](https://modrinth.com/mod/modmenu/version/latest) , but you may need a different version if you're not using the latest Minecraft version. See the [versions page](https://modrinth.com/mod/modmenu/versions) for a full list of versions. gradle.properties Copy modmenu_version=VERSION_NUMBER_HERE #### Using the API[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started#using-the-api) First, create the **modmenu entrypoint** by creating a new class in your mod. com/mymod/config/ModMenuIntegration.java Copy import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; public class ModMenuIntegration implements ModMenuApi { @Override public ConfigScreenFactory getModConfigScreenFactory() { return parentScreen -> YetAnotherConfigLib.createBuilder() ... .build() .generateScreen(parentScreen); } } If you want multiple methods to opening your configuration screen, extracting the config creation to a common method call is useful. #### Registering the entrypoint[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started#registering-the-entrypoint) Now that you've created the entrypoint, we need to tell mod menu to use it. fabric.mod.json Copy "entrypoints": { "modmenu": [\ "com.mymod.config.ModMenuIntegration"\ ] } And you're done! You can now test it out by going to the mod list, finding your mod, and pressing the configuration button to open your YACL GUI. ### NeoForge Mod List[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started#neoforge-mod-list) NeoForge has a mod list built-in, and allows you to register _extension points_ to extend the functionality of your mod's entry, notably, a config button. Add the following to your mod's constructor. after (and including) 1.20.6 before 1.20.6 Copy import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @Mod("my_mod_id") public class MyMod { public MyMod() { ModLoadingContext.get().registerExtensionPoint( IConfigScreenFactory.class, () -> (client, parent) -> YetAnotherConfigLib.createBuilder() [...] .build() .generateScreen(parent) ) } } Copy import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import nnet.neoforged.neoforge.client.ConfigScreenHandler; @Mod("my_mod_id") public class MyMod { public MyMod() { ModLoadingContext.get().registerExtensionPoint( ConfigScreenHandler.ConfigScreenFactory.class, () -> new ConfigScreenHandler.ConfigScreenFactory( (client, parent) -> YetAnotherConfigLib.createBuilder() [...] .build() .generateScreen(parent) ) ) } } If you want multiple methods to opening your configuration screen, extracting the config creation to a common method call is useful. That's it! You can now find your mod in the mod list and open your YACL configuration screen! [PreviousInstalling YACL](https://docs.isxander.dev/yet-another-config-lib/installing-yacl) [NextControllers](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers) Last updated 2 years ago --- # Overview | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/overview.md) . This wiki is currently a work-in-progress and is incomplete! Henceforth, YetAnotherConfigLib will be referred to as its acronym, YACL. ❓What is it?[](https://docs.isxander.dev/yet-another-config-lib#what-is-it) ---------------------------------------------------------------------------- Primarily, YACL is a config screen generator that helps developers create a user friendly GUI in Minecraft to allow users to configure their mods easily. YACL's functionality has also grew to also include an API to help developers save and load their config from a file. ![Screenshot of a YACL gui](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FuLtKjFddUiEfNhNWZAMB%252FScreenshot%25202023-11-26%2520at%252014.19.22.png%3Falt%3Dmedia%26token%3Df1375cd7-1cd3-497f-8842-cc41909e88ef&width=768&dpr=3&quality=100&sign=9c9b1be0&sv=2) The best way to tell you what YACL is all about is with an image. A picture speaks a thousand words! ### Why does it even exist?[](https://docs.isxander.dev/yet-another-config-lib#why-does-it-even-exist) This mod was made to fill a hole in this area of FabricMC modding. The already-existing config libraries don't achieve what I, as the developer, want from them. * [**Cloth Config**](https://github.com/shedaniel/cloth-config) **is stale.** The developer of cloth has clarified that they are likely not going to add any more features, they don't want to touch it. ([citation](https://user-images.githubusercontent.com/43245524/206530322-3ae46008-5356-468e-9a73-63b859364d4e.png) ) * [**Spruce UI**](https://github.com/LambdAurora/SpruceUI) **isn't designed for configuration.** In this essence, the design feels cluttered. * [**MidnightLib**](https://modrinth.com/mod/midnightlib) **has cosmetics built-in.** It may not be large in size, but players (including me) may not want bundled cosmetics. * [**OwO Lib**](https://modrinth.com/mod/owo-lib) **contains a lot of other utilities.** It isn't focused on config, however, is recommended if you are building a content mod. As you can see, there's sadly a drawback with every one of them. This is where YACL comes in! ### How is YACL better?[](https://docs.isxander.dev/yet-another-config-lib#how-is-yacl-better) YACL has the benefit of hindsight. It can see what everyone else has done, and combine the best parts to make this a great contender. Here are a few points that may convince you: * **Easy-to-use API:** YACL takes inspiration from [Sodium](https://modrinth.com/mod/sodium) 's internal configuration library. * **Minecraft styled:** YACL is designed to fit right in vanilla Minecraft so it doesn't look out of place. ✅ When should I use it?[](https://docs.isxander.dev/yet-another-config-lib#when-should-i-use-it) ------------------------------------------------------------------------------------------------- YACL is was designed with client mods in mind (mods that do not support loading on the server) so they can quickly produce a great UI for users, so these types of mods are recommended. Additionally, mods that are required on both environments to function are also supported with its config API available, so the server so administrators can configure with a file, whilst the client can configure with a GUI. However, no server-client syncing functionality is available for use which may limit functionality of your mod. If you need server-client syncing, use [OwO Lib](https://modrinth.com/mod/owo-lib) . ⛔ When shouldn't I use it?[](https://docs.isxander.dev/yet-another-config-lib#when-shouldnt-i-use-it) ------------------------------------------------------------------------------------------------------ On server only mods, depending on only YACL's config API is not recommended. This is because you are requiring the user to download YACL that will frequently update functionality never to be utilised by your mod, not to mention the waste of storage space. [NextInstalling YACL](https://docs.isxander.dev/yet-another-config-lib/installing-yacl) Last updated 2 years ago --- # Installing YACL | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/installing-yacl.md) . This wiki is currently a work-in-progress and is incomplete! Adding YACL as a dependency[](https://docs.isxander.dev/yet-another-config-lib/installing-yacl#adding-yacl-as-a-dependency) ---------------------------------------------------------------------------------------------------------------------------- First, you will need to add the maven repository that hosts YACL. Kotlin DSL (build.gradle.kts) Groovy DSL (build.gradle) Copy repositories { maven("https://maven.isxander.dev/releases") { name = "Xander Maven" } } Copy repositories { maven { name 'Xander Maven' url 'https://maven.isxander.dev/releases' } } Next, you need to place the YACL version you want to use in your `gradle.properties` file. Copy yacl_version=... Below is a handy chart to find the YACL version based on the Minecraft version and Mod loader you're using. Minecraft Version Mod Loader YACL version `1.21.2`, `1.21.3` Fabric `3.6.1+1.21.2-fabric` `1.21.2`, `1.21.3` NeoForge `3.6.1+1.21.2-neoforge` `1.21`, `1.21.1` Fabric `3.6.1+1.21-fabric` `1.21`, `1.21.1` NeoForge `3.6.1+1.21-neoforge` `1.20.6` Fabric `3.6.1+1.20.6-fabric` `1.20.6` NeoForge `3.6.1+1.20.6-neoforge` `1.20.4` Fabric `3.6.1+1.20.4-fabric` `1.20.4` NeoForge `3.6.1+1.20.4-neoforge` `1.20.1` Fabric `3.6.1+1.20.1-fabric` `1.20.1` MinecraftForge (LexForge) `3.6.1+1.20.1-forge` Next, you need to add the dependency to the classpath. It is highly discouraged to JiJ (jar in jar) the YACL dependency as it is likely that it's already in the user's mod folder and will significantly increase the size of your JAR. Loom ForgeGradle Copy dependencies { modImplementation "dev.isxander:yet-another-config-lib:${project.yacl_version}" } Replace `(latest)` with the latest version of YACL available for the target Minecraft version. You can find this on [Modrinth.](https://modrinth.com/mod/yacl/versions) Copy dependencies { compileOnly fg.deobf("dev.isxander:yet-another-config-lib:${project.yacl_version}") } Replace `(latest)` with the latest version of YACL available for the target Minecraft version. You can find this on [Modrinth.](https://modrinth.com/mod/yacl/versions) If you use Architectury for a multi-loader project, YACL provides no common artifact. Because Architectury is Loom-based, use the fabric artifact as the common one. Adding YACL as a dependency[](https://docs.isxander.dev/yet-another-config-lib/installing-yacl#adding-yacl-as-a-dependency-1) ------------------------------------------------------------------------------------------------------------------------------ Finally, you should also add the dependency in your mod manifest so the loader will crash elegantly if YACL is not present, giving the user a clear description of what they need to do. Fabric NeoForge MinecraftForge (LexForge) fabric.mod.json Copy "depends": { "yet_another_config_lib_v3": ">=YACL_VERSION_HERE" } neoforge.mods.toml Copy [[dependencies.your_mod_id]] modId = "yet_another_config_lib_v3" mandatory = true versionRange = "[YACL_VERSION_HERE,)"\ ordering = "NONE"\ side = "CLIENT"\ \ mods.toml\ \ Copy\ \ [[dependencies.your_mod_id]]\ modId = "yet_another_config_lib_v3"\ mandatory = true\ versionRange = "[YACL_VERSION_HERE,)"\ ordering = "NONE"\ side = "CLIENT"\ \ [PreviousOverview](https://docs.isxander.dev/yet-another-config-lib)\ [NextGetting started](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started)\ \ Last updated 1 year ago --- # Special Options | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options.md) . `ListOption`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options#listoption) -------------------------------------------------------------------------------------------------------- List options allow you to easily allow the user to easily append, sort and remove elements from a list, whilst also allowing the use of [regular controllers](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers) . These types of options are a hybrid between groups and options, behaving like both, and does not allow for it to be added to a group but must be added to a category directly. Under-the-hood, each option entry is like a regular option, but with no name. This option is a child of a list entry, which has extra buttons to reposition and remove elements. Copy ListOption.createBuilder() .name(Component.literal("List Option")) .binding(/* gets and sets a List, requires list field to be not final, does not manipulate the list */) .controller(StringControllerBuilder::create) // usual controllers, passed to every entry .initial("") // when adding a new entry to the list, this is the initial value it has .build() To re-iterate, _EVERY_ controller works with lists. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FJyReZy1fmvIBFn3PGWyi%252F206925713-115d5737-19c5-4469-894c-710f6fc271cd.png%3Falt%3Dmedia%26token%3D94f010b7-b837-4756-b33a-ba9375438628&width=768&dpr=3&quality=100&sign=5aa097f&sv=2) `LabelOption`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options#labeloption) ---------------------------------------------------------------------------------------------------------- Labels are simply options that display text. Create one like so: Copy LabelOption.create(Component.literal("Cool label!")) `ButtonOption`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options#buttonoption) ------------------------------------------------------------------------------------------------------------ Button options are options that do an action when pressed. Create one like so: Copy ButtonOption.createBuilder() .name(...) .description(...) .action((yaclScreen, thisOption) -> { /* do something here */ }) .build() [PreviousControllers](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers) [NextBasic usage of Config API](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api) Last updated 2 years ago --- # Controllers | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers.md) . Built-in controllers[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#built-in-controllers) ---------------------------------------------------------------------------------------------------------------------- This is an incomplete list of controllers! There are many different built-in controllers to get you set up quickly. `BooleanController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#booleancontroller) A toggleable controller that displays a different `Text` based on the state of the option. Copy .controller(BooleanControllerBuilder::create) * `valueFormatter` parameter is a function to return `Text` based on the state of the option. _(optional)_ * `coloured` parameter is a boolean that colours the returned text red or green based on the state. _(optional)_ To pass extra parameters, you need to construct a `BooleanController` like so. Copy .controller(opt -> BooleanControllerBuilder.create(opt) .valueFormatter(val -> val ? Component.literal("Amazing") : Component.literal("Not Amazing")) .coloured(true)) ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FXWcEa1kjdFXu0DuWdjMU%252Fjava_VROI8U8sCh.png%3Falt%3Dmedia%26token%3D87aa0297-db2b-4266-ac6d-bd79b16b98dc&width=300&dpr=3&quality=100&sign=18810fb7&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FwjxEpGaM1rS2UjBdO4F6%252Fjava_GQGtXD1SRJ.png%3Falt%3Dmedia%26token%3De5519ecb-a1d4-4ba1-936d-f16cd6871266&width=300&dpr=3&quality=100&sign=5a4c472c&sv=2) `TickBoxController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#tickboxcontroller) A toggleable controller that displays a tick box. Copy .controller(TickBoxControllerBuilder::create) There are no optional parameters for this controller. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FIcQqGBjwyKeLQVxDnTbL%252Fjava_4DLHxyCy5h.png%3Falt%3Dmedia%26token%3D7df04149-78da-4bcd-9aeb-a88743bfae1f&width=300&dpr=3&quality=100&sign=75741c94&sv=2) `SliderController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#less-than-number-greater-than-slidercontroller) Replace `` with either `Double`, `Float`, `Integer` and `Long`. Slider controllers take a minimum, a maximum and a step for the slider in their respected number types. Copy .controller(opt -> FloatSliderController.create(opt) .range(0, 10) .step(1) .valueFormatter(val -> Component.literal(val + " ticks"))) // sliders can also take a formatter ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FY46EjR6f6JrxOoeKRPO6%252Fjava_0tiJ9ys7yG.png%3Falt%3Dmedia%26token%3D51d8c7ea-4bbe-4bca-b224-c6f077b02706&width=300&dpr=3&quality=100&sign=cbd73fe6&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FcCNDiQjO167iQAkjbe2t%252Fjava_qku1qm9wDC.png%3Falt%3Dmedia%26token%3Dd3f09cad-65ea-421f-b228-b6471bc7d717&width=300&dpr=3&quality=100&sign=823d4fc5&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FWf6Tq0w5LgaAsoXT0fU9%252Fjava_ysYlg7hxGU.png%3Falt%3Dmedia%26token%3D63c80f11-a0b2-4360-b29d-65dbbeab7acb&width=300&dpr=3&quality=100&sign=44df1b3c&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252F3q4reY1ZbEi5j6OdVjDa%252Fjava_wgfnQitV2T.png%3Falt%3Dmedia%26token%3D6be506ff-11d6-4671-b6cc-a9669adb632a&width=300&dpr=3&quality=100&sign=dd092d75&sv=2) ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FnzuilrUfOQXv1TLccIkb%252Fjava_33thNIC5OK.png%3Falt%3Dmedia%26token%3D46f0aa94-c575-48e4-b3ab-951af673b16c&width=300&dpr=3&quality=100&sign=db67564a&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252Fs2CeO1EAzBgr4wS9gjaC%252Fjava_7bigpV0mxD.png%3Falt%3Dmedia%26token%3Dabbc9254-12ff-4f28-9d80-59d0900f2ba2&width=300&dpr=3&quality=100&sign=6e962b1&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252F5IkNNEcEEcfOoiEV5thu%252Fjava_cnGgxbcjdi.png%3Falt%3Dmedia%26token%3D266fa3bf-ce01-48d0-8097-0661e2d8e027&width=300&dpr=3&quality=100&sign=843697b7&sv=2)![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252Fl7t4I8ffIT6yUNlErJPs%252Fjava_NvztjD91sj.png%3Falt%3Dmedia%26token%3D7053df46-bac5-4ff2-90ab-c38be5dfa551&width=300&dpr=3&quality=100&sign=d8057607&sv=2) `EnumController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#enumcontroller) A controller that allows you to cycle through Enum constants. Copy .controller(opt -> EnumControllerBuilder.create(opt) .enumClass(Alphabet.class)) Enums can implement the `NameableEnum` interface to automatically name each constant without a value formatter function. Copy public enum Alphabet implements NameableEnum { A, B, C; @Override public Component getDisplayName() { return Component.translatable("mymod.alphabet." + name().toLowerCase()); } } Or alternatively, just pass a `valueFormatter` to the controller as usual; Copy .controller(opt -> EnumControllerBuilder.create(opt) .enumClass(Alphabet.class) .valueFormatter(v -> Component.translatable("mymod.alphabet." + v.name().toLowerCase()))) ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FuvgzaCopRaISHUo1RYpd%252Fjava_cFqi0yTGBc.png%3Falt%3Dmedia%26token%3D4d2bdcbc-28b4-4914-a313-a9e7716ef01b&width=300&dpr=3&quality=100&sign=3c37eb33&sv=2) `StringController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#stringcontroller) An input box that allows users to input text as a `String`. This allows for highlighting text with the keyboard and strings that are longer than the option itself, similarly to Minecraft's `EditBox` Copy .controller(StringControllerBuilder::create) This controller has no arguments, though its functionality can be extended by creating your own controller, implementing `IStringController`, more on this here. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252FCisNntGIdEoz8yDOpygZ%252Fjava_q3pKUNj5ph.png%3Falt%3Dmedia%26token%3D6e967591-71a7-456e-8365-f9b5c0e6bd20&width=300&dpr=3&quality=100&sign=f6d32835&sv=2) `ColorController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#colorcontroller) A controller that allows users to input colors as RGB hex format, with a preview. Copy .controller(ColorControllerBuilder::create) * `allowAlpha` parameter adds an alpha channel to the hex format as RGBA. Copy .controller(opt -> ColorControllerBuilder.create(opt) .alpha(true)) ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252F7qdvm0Zed9WpMy6f2P55%252Fjava_yG4eNdRHPO.png%3Falt%3Dmedia%26token%3D7c604874-6bcc-4c71-8b74-09320b7dff7c&width=300&dpr=3&quality=100&sign=54010d75&sv=2) `FieldController`[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#less-than-number-greater-than-fieldcontroller) Replace `` with either `Double`, `Float`, `Long` or `Integer`. Similar to [`StringController`](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#stringcontroller) , but forces a number format, doubles and floats allow decimals, but longs and integers do not. Copy .controller(SliderControllerBuilder::create) It also has a optional range where you can specify the upper and lower bound if necessary. And like usual has a value formatter. Copy .controller(opt -> SliderControllerBuilder.create(opt) .min(0).max(10) .valueFormatter(...)) Creating a custom Controller[](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers#creating-a-custom-controller) -------------------------------------------------------------------------------------------------------------------------------------- [PreviousGetting started](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started) [NextSpecial Options](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options) Last updated 1 year ago --- # Home | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/archive/home.md) . This is an archive of the old GitHub wiki! This will be re-written in the near future. Welcome to YACL's wiki! This page will guide you on how to use YACL: ### Notes[](https://docs.isxander.dev/yet-another-config-lib/archive/home#notes) A few things to clear up before we get started... * This page will be laid out in the chronological order a developer would use to implement YACL. * This mod is only available for Fabric and Quilt. * All code examples presume you use Yarn mappings. * You must have basic/intermediate Java knowledge. ### A quick overview[](https://docs.isxander.dev/yet-another-config-lib/archive/home#a-quick-overview) For people who just want to skip to the full example, [click here](https://docs.isxander.dev/yet-another-config-lib/archive/home#all-together) ### Backbones of the config[](https://docs.isxander.dev/yet-another-config-lib/archive/home#backbones-of-the-config) The main class that contains everything is `YetAnotherConfigLib` (how fitting!). This interface accepts a name, config categories, a save function, and an init function. YACL's API is builder based, not class based, so everything is defined inside of a method, let's pretend we have a config class, it has a save function, and a few properties, and a method called `MyConfig#createGui()` Copy import net.minecraft.client.screen.Screen; public class MyConfig { public boolean booleanToggle = true; public int intSlider = 5; public void save() { /* save your config! */ } public Screen createGui(Screen parent) { // time to use YOCL! } } But let's forget about the rest of this class for now and start constructing YetAnotherConfigLib... Copy YetAnotherConfigLib.createBuilder() .name(Text.of("Mod Name")) .save(MyConfig::save) .build() Here you can see the general syntax for the API beginning to shape, however, this isn't very useful, let's add a category... Copy YetAnotherConfigLib.createBuilder() .title(Text.of("Mod Name")) .category(ConfigCategory.createBuilder() .name(Text.of("My Category") .tooltip(Text.of("This displays when you hover over a category button")) // optional .build()) .save(MyConfig::save) .build() Still, not very useful, but here is where it gets good! Let's forget about what we have so far and just focus on `Option` ### Options[](https://docs.isxander.dev/yet-another-config-lib/archive/home#options) Options are broken down into two main parts, a binding and a controller. #### Bindings[](https://docs.isxander.dev/yet-another-config-lib/archive/home#bindings) Bindings are simple, you _bind_ a property to an interface. This provides essential functionality to query and set values in your config, there are two types of bindings you can use by default, a generic binding and a minecraft binding. **Generic** This is the Binding you will be using most often, it accepts a default value, a getter, and a setter, like this... Copy Binding.generic(0 /* default value for setting */, () -> this.booleanToggle, newValue -> this.booleanToggle = newValue) **Minecraft** You can also bind an option found in the `GameOptions` class like this... Copy Binding.minecraft(Minecraft.getInstance().gameOptions.getAutoJump()) **Immutable** There is also an option to make a binding immutable (cannot be changed). This may be useful for labels Copy Binding.immutable(value) #### Controllers[](https://docs.isxander.dev/yet-another-config-lib/archive/home#controllers) Controllers provide YACL a graphical widget to display and for users to interact with, this functionality has been completely separated from the Option itself so you can define multiple ways of displaying the same datatype. By default there are only a handful of controllers available, but it is super easy to add another controller (later in the wiki) and covers the basics, booleans, numbers and enums. **BooleanController** A simple controller that displays `Text` based on the state of the boolean. There are a few pre-defined value formatters: `ON_OFF` (default), `TRUE_FALSE` and `YES_NO`. These can be found in the class. Copy new BooleanController(option /* provided by builder */, BooleanController.YES_NO_FORMATTER /* default ON_OFF, optional */) **TickBoxController** A controller that displays a tickbox, indicating true or false. Copy new TickBoxController(option /* provided by builder */) **EnumController** A simple controller that displays `Text` based on the name of the enum constant (this can be customised). By default, this controller first looks to see if the enum implements `NameableEnum`, which provides a `Text`, falling back on `Enum#toString()` Copy new EnumController(option /* provided by builder */, enumConstant -> Text.of(enumConstant.toString()) /* optional */) **CyclingListController** Any `Iterable` can be passed to this controller. It behaves just like `EnumController` and can be cycled with a click. Copy new CyclingListController(option /* provided by builder */, List.of("A", "B", "C"), entry -> entry.toLowerCase() /* optional */) **SliderController** There is a slider controller for every common number datatype: * `IntegerSliderController` * `FloatSliderController` * `DoubleSliderController` * `LongSliderController` These sliders accept a minimum value, a maximum value and an interval (slider increment) and optionally a value formatter. By default, doubles format to two decimal places, floats one, and all four are separated with commas every three digits. (again, can be customized) In this example, I chose to use an integer but all are the same. Copy new IntegerSliderController(option /* provided by builder */, 0 /* min */, 10 /* max */, 1 /* interval */) **ActionController** Simply displays some text on the right and executes the ButtonOption action on press. By default, the text reads `EXECUTE` but this can be customized. Copy new ActionController(option /* provided by builder */, Text.of("Run") /* optional */) **StringController** A custom text field implementation. Copy new StringController(option /* provided by builder */) **ColorController** A hex color field with a color preview. Copy new ColorController(option /* provided by builder */, allowAlpha /* default false */) **LabelController** Renders some `Text`, should be used with an immutable binding. Allows user to click on styled text or hover like in the chat. Copy new LabelController(option /* provided by builder */) #### Building an option[](https://docs.isxander.dev/yet-another-config-lib/archive/home#building-an-option) Here you can see a basic example of an option. Please note that there is a shorthand function for `Binding.generic` being used (see here) Copy Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build() ### All together![](https://docs.isxander.dev/yet-another-config-lib/archive/home#all-together) That's all you need to know to build YetAnotherConfigLib, let's put everything together! Copy import net.minecraft.client.screen.Screen; public class MyConfig { public boolean booleanToggle = true; public int intSlider = 5; public void save() { /* save your config! */ } public Screen createGui(Screen parent) { YetAnotherConfigLib.createBuilder() .title(Text.of("Mod Name")) .category(ConfigCategory.createBuilder() .name(Text.of("My Category")) .tooltip(Text.of("This displays when you hover over a category button")) // optional .option(Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build()) .build()) .save(MyConfig::save) .build() .generateScreen(parent); } } Make sure you notice the last line where a `Screen` is generated. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F188279998-f099337e-d5ca-4142-ad29-7876b79d1d25.png&width=768&dpr=3&quality=100&sign=41e2766c&sv=2) basic example ### Advanced features[](https://docs.isxander.dev/yet-another-config-lib/archive/home#advanced-features) #### Option Groups[](https://docs.isxander.dev/yet-another-config-lib/archive/home#option-groups) Instead of just adding an option to a category, you can also add groups to a category. Groups act much like subcategories, they are displayed as a separator in the option list. They also don't need to be named and just be used as a spacer. You can also set them to be collapsed by default if there are too many. Copy ConfigCategory.createBuilder() .name(Text.of("My Category")) .tooltip(Text.of("This displays when you hover over a category button")) // optional .group(OptionGroup.createBuilder() .name(Text.of("Option Group")) .tooltip(Text.of("Like everything in YACL, you can have tooltips")) // optional .collapsed(true) // optional, default false .option(Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build()) .build()) .build() So, if we look at this in-game... ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F188280006-74aa192b-cdf5-496d-8f6e-d891e2f45b5d.png&width=768&dpr=3&quality=100&sign=2d159e36&sv=2) option group example #### Lists[](https://docs.isxander.dev/yet-another-config-lib/archive/home#lists) There is another type of option called a `ListOption`. It works with all controllers and all list types that has a controller type for it. Lists take a hybrid form of option groups and a regular option, and such you add lists with `.group()`, NOT `.option()`. You can minimize like a group and add flags like an option, it's both! Lists require an initial value for when users add another entry. Copy ListOption.createBuilder(String.class) .name(Text.of("List Option")) .binding(/* gets and sets a List, requires list field to be not final, does not manipulate the list */) .controller(StringController::new) // usual controllers, passed to every entry .initial("") // when adding a new entry to the list, this is the initial value it has .build() ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F206925713-115d5737-19c5-4469-894c-710f6fc271cd.png&width=768&dpr=3&quality=100&sign=2fa2b82e&sv=2) list example #### Available Flag[](https://docs.isxander.dev/yet-another-config-lib/archive/home#available-flag) You can mark options as unavailable on build to prevent changing the value. Copy Option.createBuilder(boolean.class) /* option things */ .available(false) .build() Additionally, you can modify this value after building the option with `option.setAvailable(false)`. You can do this while the user is in the UI and it will dynamically change. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fi.imgur.com%2FCzVtXpG.png&width=768&dpr=3&quality=100&sign=bef7e586&sv=2) #### Option Flags[](https://docs.isxander.dev/yet-another-config-lib/archive/home#option-flags) Options can be marked with flags that have an associated runnable that is ran when the option is saved. Can be used to reload chunks, require a restart etc. There are some builtin flags to use which are listed below: * `OptionFlag.GAME_RESTART` - Opens a `Screen` asking the user to restart Minecraft * `OptionFlag.RELOAD_CHUNKS` - Reloads the world renderer * `OptionFlag.WORLD_RENDER_UPDATE` - Rebuilds terrain * `OptionFlag.ASSET_RELOAD` - Reloads all assets Copy Option.createBuilder(boolean.class) /* option things */ .flag(OptionFlag.GAME_RESTART) .build() Please note if you are to use the same custom flag more than once it should be defined as a field or an interface, not in lambda form. This allows YACL to identify them as the same so they are only ran once. #### Dynamic Tooltips[](https://docs.isxander.dev/yet-another-config-lib/archive/home#dynamic-tooltips) You can also make tooltips change based on the value of the option. It is as simple as consuming the value in the builder... Copy Option.createBuilder(boolean.class) .tooltip(value -> Text.of("I now know that my option is set to " + value + "!")) .build() #### Button "Options"[](https://docs.isxander.dev/yet-another-config-lib/archive/home#button-options) A thing I noticed while using other config libraries is that it is way too hard to just make a custom button that a user can press next to all the other options, so I made it super easy! This time, you don't need a binding! See here about how to customize the action controller text. Copy ButtonOption.createBuilder() .name(Text.of("Pressable Button")) .tooltip(Text.of("This is so easy!")) // optional .action((yaclScreen, buttonOption) -> { System.out.println("Button has been pressed!"); }) .controller(ActionController::new) .build() ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F188097468-326d3a56-bed7-43bb-91f2-0fcc449313cf.png&width=768&dpr=3&quality=100&sign=c4fc65ee&sv=2) button option #### Placeholder Categories[](https://docs.isxander.dev/yet-another-config-lib/archive/home#placeholder-categories) Placeholder categories can be used in place of a `ConfigCategory`. Rather than changing the option list to the category's options/groups, it just opens up another `Screen`. Copy PlaceholderCategory.createBuilder() .name(Text.of("Category Name")) .tooltip(Text.of("Tooltip for this category!")) .screen((client, parent) -> new MyScreen(parent)) .build() #### Instant Option Application[](https://docs.isxander.dev/yet-another-config-lib/archive/home#instant-option-application) You can also specify options to instantly apply to their bindings as the user changes it, skipping the `Apply Changes` button altogether. Note that this does prevent the user from being able to undo their actions and you from using option flags Copy Option.createBuilder(int.class) /* option stuff! */ .instant(true) .build() [PreviousBasic usage of Config API](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api) [NextConfig API](https://docs.isxander.dev/yet-another-config-lib/archive/config-api) Last updated 3 years ago --- # Basic usage of Config API | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api.md) . What is it?[](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api#what-is-it) ---------------------------------------------------------------------------------------------------------------- Along with YACL's primary functionality, the GUI, this mod also provides a super easy way to manage your mod's config. It can be a tedious part of every mod you make, this is why the Config API was created! All you need to do is create a class, add some fields and tell YACL _how_ to save and load your config. An example...[](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api#an-example) ------------------------------------------------------------------------------------------------------------------ Copy public class MyConfig { public static ConfigClassHandler HANDLER = ConfigClassHandler.createBuilder(MyConfig.class) .id(new ResourceLocation("mymod", "my_config")) .serializer(config -> GsonConfigSerializerBuilder.create(config) .setPath(FabricLoader.getInstance().getConfigDir().resolve("my_mod.json5")) .appendGsonBuilder(GsonBuilder::setPrettyPrint) // not needed, pretty print by default .setJson5(true) .build()) .build(); @SerialEntry public boolean myCoolBoolean = true; @SerialEntry public int myCoolInteger = 5; @SerialEntry(comment = "This string is amazing") public String myCoolString = "How amazing!"; } That is it! That's all the setup you need for your config. In this example, you can see that we told the config handler to use GSON to serialize your fields, meaning not just primitive/basic types work, but any! You can even see me modifying the GSON builder. **Only fields annotated with** `**@SerialEntry**` **are considered.** Saving and loading[](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api#saving-and-loading) ------------------------------------------------------------------------------------------------------------------------------- In the above example, you can see we specified the serializer like so: Copy .serializer(config -> GsonConfigSerializerBuilder.create(config) .setPath(YACLPlatform.getConfigDir().resolve("my_mod.json5")) .appendGsonBuilder(GsonBuilder::setPrettyPrint) // not needed, pretty print by default .setJson5(true) .build()) You can see we specified the GSON serializer, set its path, configured GSON, and specified to use JSON5 spec. Currently, GSON is the only serializer made available to you. You can create your own, more on that later. To save and load use the respected methods: Copy MyConfig.HANDLER.save(); MyConfig.HANDLER.load(); [PreviousSpecial Options](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options) [NextHome](https://docs.isxander.dev/yet-another-config-lib/archive/home) Last updated 2 years ago --- # Config API | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/archive/config-api.md) . This is an archive of the old GitHub wiki! This will be re-written in the near future. What is this?[](https://docs.isxander.dev/yet-another-config-lib/archive/config-api#what-is-this) -------------------------------------------------------------------------------------------------- The Config API handles saving and loading fields in a class automatically. This pairs great with YetAnotherConfigLib because now this library is a full solution to config in your mods. This API doesn't handle automatically generating a GUI, that is up to you to build. It would be silly to have one full, comprehensive API along with a mediocre annotations-based API together. This API is purely saving and loading. How do I use it?[](https://docs.isxander.dev/yet-another-config-lib/archive/config-api#how-do-i-use-it) -------------------------------------------------------------------------------------------------------- Before we get to actual code, it's best to explain the structure of this API (it's very simple) It's split into to main components, a config instance and config data. Config data is a class you create, containing all the fields that you want to save and load. A config instance manages that class, actually implementing saving and loading. ### Tutorial[](https://docs.isxander.dev/yet-another-config-lib/archive/config-api#tutorial) First, let's make a config data class. Copy public class MyConfig { @ConfigEntry public boolean myOption = true; } Let's leave this for now, and come back to it later. There is one implementation of `ConfigInstance` available by default: `GsonConfigInstance`. This instance uses GSON to serialize and deserialize JSON to a file. Only fields annotated with `@ConfigEntry` are included in the JSON. `Text`, `Style` and `java.awt.Color` have default type adapters, so there is no need to provide them in your GSON instance. Also, GSON is automatically configured to format fields as `lower_camel_case`. Now, let's construct a `GsonConfigInstance`. Copy GsonConfigInstace configInstance = new GsonConfigInstance<>(MyConfig.class, Path.of("path/to/config.json")); It's as simple as that, you can optionally pass your own `Gson` instance or `GsonBuilder` as a third argument for fine control of GSON. You can load your config with `configInstance.load()` and save it with `configInstance.save()`, producing the following JSON file. Copy { "my_option": true } The fun doesn't stop there, there is also a utility method to help building `YetAnotherConfigLib` instances, (see the [main wiki page](https://github.com/isXander/YetAnotherConfigLib/wiki/home) for more details). Copy configInstance.buildConfig((configInstance, builder) -> /* return the builder here */) **NOTE: In 1.19.3 versions of YACL, the above has changed to the following:** Copy YetAnotherConfigLib.create(configInstance, (defaults, config, builder) -> /* return the builder here */) This method is useful because it does a few things: * Automatically adds the save runnable, so you don't need to do it. * Provides the config instance, allowing you to do some cool things: Config instances, also store a default version of your config data that you can access, along with the current one. This is really useful when creating bindings because you don't need to mirror your defaults, once in YACL builder, once in actual field initialisation. Copy .binding( config.getDefaults().myOption, () -> config.getConfig().myOption, val -> config.getConfig().myOption = val ) [PreviousHome](https://docs.isxander.dev/yet-another-config-lib/archive/home) Last updated 3 years ago --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started.md). # Getting started {% hint style="warning" %} This wiki is currently a work-in-progress and is incomplete! {% endhint %} Before we begin, it's important to note the wiki code examples will be using official Mojang mappings. You are also expected to have a basic knowledge of the Java programming language. If you don't, please learn Java first. There is a simple structure: categories contains groups, groups contain options. You can also skip the groups and just add options to the category directly. They will always appear above any groups. Before we start, lets go into detail about how to construct an \`Option\`, then we'll use that to make a GUI. \`\`\`java Option.createBuilder() // boolean is the type of option we'll be making .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This text will appear as a tooltip when you hover over the option."))) .binding( true, // the default value () -> this.myBooleanOption, // a getter to get the current value from newVal -> this.myBooleanOption = newVal ) .controller(TickBoxControllerBuilder::create) .build() \`\`\` An important concept in YACL options are the controllers. Each option type does not have a hardcoded way of being displayed. The logic of displaying the option in the GUI is held in the \`Controller\`. To learn more about controllers, click here. You will see in the above example, we're choosing to use a tick-box to display and control the boolean option. To start making a GUI with YACL, you will need to build an instance of \`YetAnotherConfigLib\`. We will plug in our \`Option\` code from above... \`\`\`java YetAnotherConfigLib.createBuilder() .title(Component.literal("Used for narration. Could be used to render a title in the future.")) .category(ConfigCategory.createBuilder() .name(Component.literal("Name of the category")) .tooltip(Component.literal("This text will appear as a tooltip when you hover or focus the button with Tab. There is no need to add \\n to wrap as YACL will do it for you.")) .group(OptionGroup.createBuilder() .name(Component.literal("Name of the group")) .description(OptionDescription.of(Component.literal("This text will appear when you hover over the name or focus on the collapse button with Tab."))) .option(Option.createBuilder() .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This text will appear as a tooltip when you hover over the option."))) .binding(true, () -> this.myBooleanOption, newVal -> this.myBooleanOption = newVal) .controller(TickBoxControllerBuilder::create) .build()) .build()) .build()) .build() \`\`\` All you have to do then is tell YACL to generate a Screen instance from it. \`\`\`java YetAnotherConfigLib.createBuilder() \[...\] .build() .generateScreen(parentScreen) // the screen that opens up when you close YACL \`\`\` {% hint style="warning" %} You must generate a new instance of \`YetAnotherConfigLib\` every time you want a GUI \`Screen\`. You cannot just call \`generateScreen()\` again! {% endhint %} It's that simple! You have made your first GUI with YACL!
## Displaying the GUI Now you've learned the basics of creating a config GUI, but how do you show it to the user? ### Mod Menu (Fabric) \[Mod Menu\](https://modrinth.com/mod/modmenu) is an extremely popular mod for Fabric that adds a menu that displays a list of currently installed mods, like Forge. You can use its API to add a config button to your mod's entry that opens up your newly created YACL config screen. #### Adding the dependency First, add the repository to your build.gradle. {% tabs %} {% tab title="Groovy DSL (build.gradle)" %} \`\`\`gradle repositories { maven { name = "Terraformers" url = "https://maven.terraformersmc.com/" } } \`\`\` {% endtab %} {% tab title="Kotlin DSL (build.gradle.kts)" %} \`\`\`kotlin repositories { maven("https://maven.terraformersmc.com/") { name = "Terraformers" } } \`\`\` {% endtab %} {% endtabs %} Then, add the dependency {% tabs %} {% tab title="Groovy DSL (build.gradle)" %} \`\`\`gradle dependencies { modImplementation("com.terraformersmc:modmenu:${project.modmenu\_version}") } \`\`\` {% endtab %} {% tab title="Kotlin DSL (build.gradle.kts)" %} \`\`\`kotlin dependencies { modImplementation("com.terraformersmc:modmenu:${property("modmenu\_version")}") } \`\`\` {% endtab %} {% endtabs %} Then, define the version of Mod Menu you're using in your \`gradle.properties\`. You can get the latest version number \[here\](https://modrinth.com/mod/modmenu/version/latest), but you may need a different version if you're not using the latest Minecraft version. See the \[versions page\](https://modrinth.com/mod/modmenu/versions) for a full list of versions. {% code title="gradle.properties" %} \`\`\`properties modmenu\_version=VERSION\_NUMBER\_HERE \`\`\` {% endcode %} #### Using the API First, create the \*\*modmenu entrypoint\*\* by creating a new class in your mod. {% code title="com/mymod/config/ModMenuIntegration.java" %} \`\`\`java import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; public class ModMenuIntegration implements ModMenuApi { @Override public ConfigScreenFactory getModConfigScreenFactory() { return parentScreen -> YetAnotherConfigLib.createBuilder() ... .build() .generateScreen(parentScreen); } } \`\`\` {% endcode %} If you want multiple methods to opening your configuration screen, extracting the config creation to a common method call is useful. #### Registering the entrypoint Now that you've created the entrypoint, we need to tell mod menu to use it. {% code title="fabric.mod.json" %} \`\`\`json "entrypoints": { "modmenu": \[\ "com.mymod.config.ModMenuIntegration"\ \] } \`\`\` {% endcode %} And you're done! You can now test it out by going to the mod list, finding your mod, and pressing the configuration button to open your YACL GUI. ### NeoForge Mod List NeoForge has a mod list built-in, and allows you to register \*extension points\* to extend the functionality of your mod's entry, notably, a config button. Add the following to your mod's constructor. {% tabs %} {% tab title="after (and including) 1.20.6" %} \`\`\`java import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @Mod("my\_mod\_id") public class MyMod { public MyMod() { ModLoadingContext.get().registerExtensionPoint( IConfigScreenFactory.class, () -> (client, parent) -> YetAnotherConfigLib.createBuilder() \[...\] .build() .generateScreen(parent) ) } } \`\`\` {% endtab %} {% tab title="before 1.20.6" %} \`\`\`java import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import nnet.neoforged.neoforge.client.ConfigScreenHandler; @Mod("my\_mod\_id") public class MyMod { public MyMod() { ModLoadingContext.get().registerExtensionPoint( ConfigScreenHandler.ConfigScreenFactory.class, () -> new ConfigScreenHandler.ConfigScreenFactory( (client, parent) -> YetAnotherConfigLib.createBuilder() \[...\] .build() .generateScreen(parent) ) ) } } \`\`\` {% endtab %} {% endtabs %} If you want multiple methods to opening your configuration screen, extracting the config creation to a common method call is useful. That's it! You can now find your mod in the mod list and open your YACL configuration screen! --- # Unknown \# YetAnotherConfigLib ## YetAnotherConfigLib - \[Overview\](https://docs.isxander.dev/yet-another-config-lib/overview.md): What YetAnotherConfigLib is, when you should use it, and when you shouldn't. - \[Installing YACL\](https://docs.isxander.dev/yet-another-config-lib/installing-yacl.md): Learn how to import YACL into your development environment. - \[Getting started\](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started.md): Learn the structure of YACL to understand how it works. - \[Controllers\](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers.md): In-depth review of Controllers. - \[Special Options\](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options.md): Learn about other Option implementations for more functionality. - \[Basic usage of Config API\](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api.md): Learn about what the Config API does. - \[Home\](https://docs.isxander.dev/yet-another-config-lib/archive/home.md) - \[Config API\](https://docs.isxander.dev/yet-another-config-lib/archive/config-api.md) --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/overview.md). # Overview {% hint style="warning" %} This wiki is currently a work-in-progress and is incomplete! {% endhint %} Henceforth, YetAnotherConfigLib will be referred to as its acronym, YACL. ## ❓What is it? Primarily, YACL is a config screen generator that helps developers create a user friendly GUI in Minecraft to allow users to configure their mods easily. YACL's functionality has also grew to also include an API to help developers save and load their config from a file.
Screenshot of a YACL gui

The best way to tell you what YACL is all about is with an image. A picture speaks a thousand words!

### Why does it even exist? This mod was made to fill a hole in this area of FabricMC modding. The already-existing config libraries don't achieve what I, as the developer, want from them. \* \[\*\*Cloth Config\*\*\](https://github.com/shedaniel/cloth-config) \*\*is stale.\*\* The developer of cloth has clarified that they are likely not going to add any more features, they don't want to touch it. (\[citation\](https://user-images.githubusercontent.com/43245524/206530322-3ae46008-5356-468e-9a73-63b859364d4e.png)) \* \[\*\*Spruce UI\*\*\](https://github.com/LambdAurora/SpruceUI) \*\*isn't designed for configuration.\*\* In this essence, the design feels cluttered. \* \[\*\*MidnightLib\*\*\](https://modrinth.com/mod/midnightlib) \*\*has cosmetics built-in.\*\* It may not be large in size, but players (including me) may not want bundled cosmetics. \* \[\*\*OwO Lib\*\*\](https://modrinth.com/mod/owo-lib) \*\*contains a lot of other utilities.\*\* It isn't focused on config, however, is recommended if you are building a content mod. As you can see, there's sadly a drawback with every one of them. This is where YACL comes in! ### How is YACL better? YACL has the benefit of hindsight. It can see what everyone else has done, and combine the best parts to make this a great contender. Here are a few points that may convince you: \* \*\*Easy-to-use API:\*\* YACL takes inspiration from \[Sodium\](https://modrinth.com/mod/sodium)'s internal configuration library. \* \*\*Minecraft styled:\*\* YACL is designed to fit right in vanilla Minecraft so it doesn't look out of place. ## ✅ When should I use it? YACL is was designed with client mods in mind (mods that do not support loading on the server) so they can quickly produce a great UI for users, so these types of mods are recommended. Additionally, mods that are required on both environments to function are also supported with its config API available, so the server so administrators can configure with a file, whilst the client can configure with a GUI. However, no server-client syncing functionality is available for use which may limit functionality of your mod. If you need server-client syncing, use \[OwO Lib\](https://modrinth.com/mod/owo-lib). ## ⛔ When shouldn't I use it? On server only mods, depending on only YACL's config API is not recommended. This is because you are requiring the user to download YACL that will frequently update functionality never to be utilised by your mod, not to mention the waste of storage space. --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options.md). # Special Options ## \`ListOption\` List options allow you to easily allow the user to easily append, sort and remove elements from a list, whilst also allowing the use of \[regular controllers\](/yet-another-config-lib/gui-builder/controllers.md). These types of options are a hybrid between groups and options, behaving like both, and does not allow for it to be added to a group but must be added to a category directly. Under-the-hood, each option entry is like a regular option, but with no name. This option is a child of a list entry, which has extra buttons to reposition and remove elements. \`\`\`java ListOption.createBuilder() .name(Component.literal("List Option")) .binding(/\* gets and sets a List, requires list field to be not final, does not manipulate the list \*/) .controller(StringControllerBuilder::create) // usual controllers, passed to every entry .initial("") // when adding a new entry to the list, this is the initial value it has .build() \`\`\` To re-iterate, \*EVERY\* controller works with lists.
## \`LabelOption\` Labels are simply options that display text. Create one like so: \`\`\`java LabelOption.create(Component.literal("Cool label!")) \`\`\` ## \`ButtonOption\` Button options are options that do an action when pressed. Create one like so: \`\`\`java ButtonOption.createBuilder() .name(...) .description(...) .action((yaclScreen, thisOption) -> { /\* do something here \*/ }) .build() \`\`\` --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/installing-yacl.md). # Installing YACL {% hint style="warning" %} This wiki is currently a work-in-progress and is incomplete! {% endhint %} ## Adding YACL as a dependency First, you will need to add the maven repository that hosts YACL. {% tabs %} {% tab title="Kotlin DSL (build.gradle.kts)" %} \`\`\`kts repositories { maven("https://maven.isxander.dev/releases") { name = "Xander Maven" } } \`\`\` {% endtab %} {% tab title="Groovy DSL (build.gradle)" %} \`\`\`groovy repositories { maven { name 'Xander Maven' url 'https://maven.isxander.dev/releases' } } \`\`\` {% endtab %} {% endtabs %} Next, you need to place the YACL version you want to use in your \`gradle.properties\` file. \`\`\`properties yacl\_version=... \`\`\` Below is a handy chart to find the YACL version based on the Minecraft version and Mod loader you're using.
Minecraft VersionMod LoaderYACL version
1.21.2, 1.21.3Fabric3.6.1+1.21.2-fabric
1.21.2, 1.21.3NeoForge3.6.1+1.21.2-neoforge
1.21, 1.21.1Fabric3.6.1+1.21-fabric
1.21, 1.21.1NeoForge3.6.1+1.21-neoforge
1.20.6Fabric3.6.1+1.20.6-fabric
1.20.6NeoForge3.6.1+1.20.6-neoforge
1.20.4Fabric3.6.1+1.20.4-fabric
1.20.4NeoForge3.6.1+1.20.4-neoforge
1.20.1Fabric3.6.1+1.20.1-fabric
1.20.1MinecraftForge (LexForge)3.6.1+1.20.1-forge
Next, you need to add the dependency to the classpath. {% hint style="warning" %} It is highly discouraged to JiJ (jar in jar) the YACL dependency as it is likely that it's already in the user's mod folder and will significantly increase the size of your JAR. {% endhint %} {% tabs %} {% tab title="Loom" %} \`\`\`gradle dependencies { modImplementation "dev.isxander:yet-another-config-lib:${project.yacl\_version}" } \`\`\` {% hint style="info" %} Replace \`(latest)\` with the latest version of YACL available for the target Minecraft version. You can find this on \[Modrinth.\](https://modrinth.com/mod/yacl/versions) {% endhint %} {% endtab %} {% tab title="ForgeGradle" %} \`\`\`gradle dependencies { compileOnly fg.deobf("dev.isxander:yet-another-config-lib:${project.yacl\_version}") } \`\`\` {% hint style="info" %} Replace \`(latest)\` with the latest version of YACL available for the target Minecraft version. You can find this on \[Modrinth.\](https://modrinth.com/mod/yacl/versions) {% endhint %} {% endtab %} {% endtabs %} {% hint style="info" %} If you use Architectury for a multi-loader project, YACL provides no common artifact. Because Architectury is Loom-based, use the fabric artifact as the common one. {% endhint %} ## Adding YACL as a dependency Finally, you should also add the dependency in your mod manifest so the loader will crash elegantly if YACL is not present, giving the user a clear description of what they need to do. {% tabs %} {% tab title="Fabric" %} {% code title="fabric.mod.json" %} \`\`\`json "depends": { "yet\_another\_config\_lib\_v3": ">=YACL\_VERSION\_HERE" } \`\`\` {% endcode %} {% endtab %} {% tab title="NeoForge" %} {% code title="neoforge.mods.toml" %} \`\`\`toml \[\[dependencies.your\_mod\_id\]\] modId = "yet\_another\_config\_lib\_v3" mandatory = true versionRange = "\[YACL\_VERSION\_HERE,)"\ ordering = "NONE"\ side = "CLIENT"\ \`\`\`\ \ {% endcode %}\ {% endtab %}\ \ {% tab title="MinecraftForge (LexForge)" %}\ {% code title="mods.toml" %}\ \ \`\`\`toml\ \[\[dependencies.your\_mod\_id\]\]\ modId = "yet\_another\_config\_lib\_v3"\ mandatory = true\ versionRange = "\[YACL\_VERSION\_HERE,)"\ ordering = "NONE"\ side = "CLIENT"\ \`\`\`\ \ {% endcode %}\ {% endtab %}\ {% endtabs %} --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers.md). # Controllers ## Built-in controllers {% hint style="warning" %} This is an incomplete list of controllers! {% endhint %} There are many different built-in controllers to get you set up quickly.
BooleanController A toggleable controller that displays a different \`Text\` based on the state of the option. \`\`\`java .controller(BooleanControllerBuilder::create) \`\`\` \* \`valueFormatter\` parameter is a function to return \`Text\` based on the state of the option. \*(optional)\* \* \`coloured\` parameter is a boolean that colours the returned text red or green based on the state. \*(optional)\* To pass extra parameters, you need to construct a \`BooleanController\` like so.
.controller(opt -> BooleanControllerBuilder.create(opt)
        .valueFormatter(val -> val ? Component.literal("Amazing") : Component.literal("Not Amazing"))
        .coloured(true))
!\[\](/files/1ritFqzzDnaiWCYD9LKK)!\[\](/files/vPfD5b7Wa6vfLsnuhXO1)
TickBoxController A toggleable controller that displays a tick box. \`\`\`java .controller(TickBoxControllerBuilder::create) \`\`\` There are no optional parameters for this controller. !\[\](/files/cUgcAlxADiLVpjyeMRVi)
<Number>SliderController Replace \`\` with either \`Double\`, \`Float\`, \`Integer\` and \`Long\`. Slider controllers take a minimum, a maximum and a step for the slider in their respected number types. \`\`\`java .controller(opt -> FloatSliderController.create(opt) .range(0, 10) .step(1) .valueFormatter(val -> Component.literal(val + " ticks"))) // sliders can also take a formatter \`\`\` !\[\](/files/Rl3d8vQUoJW5NpKm24ux)!\[\](/files/xtWH1gPQ1uhDF7tB4Jhf)!\[\](/files/Xe6SnUwewHMzZTxXZmdn)!\[\](/files/NWkzPfXJy0OMVHwW00Yx) !\[\](/files/V9zSaNnSD7jx2WcBrv4Q)!\[\](/files/Y8axWC4kudTS8trIOMhx)!\[\](/files/adNHl5Gge0BZJu43znQZ)!\[\](/files/do9BomEnanvh5wERDM3z)
EnumController A controller that allows you to cycle through Enum constants. \`\`\`java .controller(opt -> EnumControllerBuilder.create(opt) .enumClass(Alphabet.class)) \`\`\` Enums can implement the \`NameableEnum\` interface to automatically name each constant without a value formatter function. \`\`\`java public enum Alphabet implements NameableEnum { A, B, C; @Override public Component getDisplayName() { return Component.translatable("mymod.alphabet." + name().toLowerCase()); } } \`\`\` Or alternatively, just pass a \`valueFormatter\` to the controller as usual; \`\`\`java .controller(opt -> EnumControllerBuilder.create(opt) .enumClass(Alphabet.class) .valueFormatter(v -> Component.translatable("mymod.alphabet." + v.name().toLowerCase()))) \`\`\`
StringController An input box that allows users to input text as a \`String\`. This allows for highlighting text with the keyboard and strings that are longer than the option itself, similarly to Minecraft's \`EditBox\` \`\`\`java .controller(StringControllerBuilder::create) \`\`\` This controller has no arguments, though its functionality can be extended by creating your own controller, implementing \`IStringController\`, more on this here. !\[\](/files/9WaIosIOdKj8KW0Tzvq6)
ColorController A controller that allows users to input colors as RGB hex format, with a preview. \`\`\`java .controller(ColorControllerBuilder::create) \`\`\` \* \`allowAlpha\` parameter adds an alpha channel to the hex format as RGBA. \`\`\`java .controller(opt -> ColorControllerBuilder.create(opt) .alpha(true)) \`\`\` !\[\](/files/fEVk4Knflus7sGSVIygI)
<Number>FieldController Replace \`\` with either \`Double\`, \`Float\`, \`Long\` or \`Integer\`. Similar to \[\`StringController\`\](#stringcontroller), but forces a number format, doubles and floats allow decimals, but longs and integers do not. \`\`\`java .controller(SliderControllerBuilder::create) \`\`\` It also has a optional range where you can specify the upper and lower bound if necessary. And like usual has a value formatter. \`\`\`java .controller(opt -> SliderControllerBuilder.create(opt) .min(0).max(10) .valueFormatter(...)) \`\`\`
## Creating a custom Controller --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api.md). # Basic usage of Config API ## What is it? Along with YACL's primary functionality, the GUI, this mod also provides a super easy way to manage your mod's config. It can be a tedious part of every mod you make, this is why the Config API was created! All you need to do is create a class, add some fields and tell YACL \*how\* to save and load your config. ## An example...
public class MyConfig {
    public static ConfigClassHandler<MyConfig> HANDLER = ConfigClassHandler.createBuilder(MyConfig.class)
            .id(new ResourceLocation("mymod", "my\_config"))
            .serializer(config -> GsonConfigSerializerBuilder.create(config)
                    .setPath(FabricLoader.getInstance().getConfigDir().resolve("my\_mod.json5"))
                    .appendGsonBuilder(GsonBuilder::setPrettyPrint) // not needed, pretty print by default
                    .setJson5(true)
                    .build())
            .build();
            
    @SerialEntry
    public boolean myCoolBoolean = true;
    
    @SerialEntry
    public int myCoolInteger = 5;
    
    @SerialEntry(comment = "This string is amazing")
    public String myCoolString = "How amazing!";
    
}
That is it! That's all the setup you need for your config. In this example, you can see that we told the config handler to use GSON to serialize your fields, meaning not just primitive/basic types work, but any! You can even see me modifying the GSON builder. \*\*Only fields annotated with \`@SerialEntry\` are considered.\*\* ## Saving and loading In the above example, you can see we specified the serializer like so: \`\`\`java .serializer(config -> GsonConfigSerializerBuilder.create(config) .setPath(YACLPlatform.getConfigDir().resolve("my\_mod.json5")) .appendGsonBuilder(GsonBuilder::setPrettyPrint) // not needed, pretty print by default .setJson5(true) .build()) \`\`\` You can see we specified the GSON serializer, set its path, configured GSON, and specified to use JSON5 spec. Currently, GSON is the only serializer made available to you. You can create your own, more on that later. To save and load use the respected methods: \`\`\`java MyConfig.HANDLER.save(); MyConfig.HANDLER.load(); \`\`\` \[^1\]: This should be your mod ID \[^2\]: You can add comments! --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/archive/home.md). # Home {% hint style="danger" %} This is an archive of the old GitHub wiki! This will be re-written in the near future. {% endhint %} Welcome to YACL's wiki! This page will guide you on how to use YACL: ### Notes A few things to clear up before we get started... \* This page will be laid out in the chronological order a developer would use to implement YACL. \* This mod is only available for Fabric and Quilt. \* All code examples presume you use Yarn mappings. \* You must have basic/intermediate Java knowledge. ### A quick overview For people who just want to skip to the full example, \[click here\](#all-together) ### Backbones of the config The main class that contains everything is \`YetAnotherConfigLib\` (how fitting!). This interface accepts a name, config categories, a save function, and an init function. YACL's API is builder based, not class based, so everything is defined inside of a method, let's pretend we have a config class, it has a save function, and a few properties, and a method called \`MyConfig#createGui()\` \`\`\`java import net.minecraft.client.screen.Screen; public class MyConfig { public boolean booleanToggle = true; public int intSlider = 5; public void save() { /\* save your config! \*/ } public Screen createGui(Screen parent) { // time to use YOCL! } } \`\`\` But let's forget about the rest of this class for now and start constructing YetAnotherConfigLib... \`\`\`java YetAnotherConfigLib.createBuilder() .name(Text.of("Mod Name")) .save(MyConfig::save) .build() \`\`\` Here you can see the general syntax for the API beginning to shape, however, this isn't very useful, let's add a category... \`\`\`java YetAnotherConfigLib.createBuilder() .title(Text.of("Mod Name")) .category(ConfigCategory.createBuilder() .name(Text.of("My Category") .tooltip(Text.of("This displays when you hover over a category button")) // optional .build()) .save(MyConfig::save) .build() \`\`\` Still, not very useful, but here is where it gets good! Let's forget about what we have so far and just focus on \`Option\` ### Options Options are broken down into two main parts, a binding and a controller. #### Bindings Bindings are simple, you \*bind\* a property to an interface. This provides essential functionality to query and set values in your config, there are two types of bindings you can use by default, a generic binding and a minecraft binding. \*\*Generic\*\* This is the Binding you will be using most often, it accepts a default value, a getter, and a setter, like this... \`\`\`java Binding.generic(0 /\* default value for setting \*/, () -> this.booleanToggle, newValue -> this.booleanToggle = newValue) \`\`\` \*\*Minecraft\*\* You can also bind an option found in the \`GameOptions\` class like this... \`\`\`java Binding.minecraft(Minecraft.getInstance().gameOptions.getAutoJump()) \`\`\` \*\*Immutable\*\* There is also an option to make a binding immutable (cannot be changed). This may be useful for labels \`\`\`java Binding.immutable(value) \`\`\` #### Controllers Controllers provide YACL a graphical widget to display and for users to interact with, this functionality has been completely separated from the Option itself so you can define multiple ways of displaying the same datatype. By default there are only a handful of controllers available, but it is super easy to add another controller (later in the wiki) and covers the basics, booleans, numbers and enums. \*\*BooleanController\*\* A simple controller that displays \`Text\` based on the state of the boolean. There are a few pre-defined value formatters: \`ON\_OFF\` (default), \`TRUE\_FALSE\` and \`YES\_NO\`. These can be found in the class. \`\`\`java new BooleanController(option /\* provided by builder \*/, BooleanController.YES\_NO\_FORMATTER /\* default ON\_OFF, optional \*/) \`\`\` \*\*TickBoxController\*\* A controller that displays a tickbox, indicating true or false. \`\`\`java new TickBoxController(option /\* provided by builder \*/) \`\`\` \*\*EnumController\*\* A simple controller that displays \`Text\` based on the name of the enum constant (this can be customised). By default, this controller first looks to see if the enum implements \`NameableEnum\`, which provides a \`Text\`, falling back on \`Enum#toString()\` \`\`\`java new EnumController(option /\* provided by builder \*/, enumConstant -> Text.of(enumConstant.toString()) /\* optional \*/) \`\`\` \*\*CyclingListController\*\* Any \`Iterable\` can be passed to this controller. It behaves just like \`EnumController\` and can be cycled with a click. \`\`\`java new CyclingListController(option /\* provided by builder \*/, List.of("A", "B", "C"), entry -> entry.toLowerCase() /\* optional \*/) \`\`\` \*\*SliderController\*\* There is a slider controller for every common number datatype: \* \`IntegerSliderController\` \* \`FloatSliderController\` \* \`DoubleSliderController\` \* \`LongSliderController\` These sliders accept a minimum value, a maximum value and an interval (slider increment) and optionally a value formatter. By default, doubles format to two decimal places, floats one, and all four are separated with commas every three digits. (again, can be customized) In this example, I chose to use an integer but all are the same. \`\`\`java new IntegerSliderController(option /\* provided by builder \*/, 0 /\* min \*/, 10 /\* max \*/, 1 /\* interval \*/) \`\`\` \*\*ActionController\*\* Simply displays some text on the right and executes the ButtonOption action on press. By default, the text reads \`EXECUTE\` but this can be customized. \`\`\`java new ActionController(option /\* provided by builder \*/, Text.of("Run") /\* optional \*/) \`\`\` \*\*StringController\*\* A custom text field implementation. \`\`\`java new StringController(option /\* provided by builder \*/) \`\`\` \*\*ColorController\*\* A hex color field with a color preview. \`\`\`java new ColorController(option /\* provided by builder \*/, allowAlpha /\* default false \*/) \`\`\` \*\*LabelController\*\* Renders some \`Text\`, should be used with an immutable binding. Allows user to click on styled text or hover like in the chat. \`\`\`java new LabelController(option /\* provided by builder \*/) \`\`\` #### Building an option Here you can see a basic example of an option. Please note that there is a shorthand function for \`Binding.generic\` being used (see here) \`\`\`java Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build() \`\`\` ### All together! That's all you need to know to build YetAnotherConfigLib, let's put everything together! \`\`\`java import net.minecraft.client.screen.Screen; public class MyConfig { public boolean booleanToggle = true; public int intSlider = 5; public void save() { /\* save your config! \*/ } public Screen createGui(Screen parent) { YetAnotherConfigLib.createBuilder() .title(Text.of("Mod Name")) .category(ConfigCategory.createBuilder() .name(Text.of("My Category")) .tooltip(Text.of("This displays when you hover over a category button")) // optional .option(Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build()) .build()) .save(MyConfig::save) .build() .generateScreen(parent); } } \`\`\` Make sure you notice the last line where a \`Screen\` is generated. !\[basic example\](https://user-images.githubusercontent.com/43245524/188279998-f099337e-d5ca-4142-ad29-7876b79d1d25.png) ### Advanced features #### Option Groups Instead of just adding an option to a category, you can also add groups to a category. Groups act much like subcategories, they are displayed as a separator in the option list. They also don't need to be named and just be used as a spacer. You can also set them to be collapsed by default if there are too many. \`\`\`java ConfigCategory.createBuilder() .name(Text.of("My Category")) .tooltip(Text.of("This displays when you hover over a category button")) // optional .group(OptionGroup.createBuilder() .name(Text.of("Option Group")) .tooltip(Text.of("Like everything in YACL, you can have tooltips")) // optional .collapsed(true) // optional, default false .option(Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build()) .build()) .build() \`\`\` So, if we look at this in-game... !\[option group example\](https://user-images.githubusercontent.com/43245524/188280006-74aa192b-cdf5-496d-8f6e-d891e2f45b5d.png) #### Lists There is another type of option called a \`ListOption\`. It works with all controllers and all list types that has a controller type for it. Lists take a hybrid form of option groups and a regular option, and such you add lists with \`.group()\`, NOT \`.option()\`. You can minimize like a group and add flags like an option, it's both! Lists require an initial value for when users add another entry. \`\`\`java ListOption.createBuilder(String.class) .name(Text.of("List Option")) .binding(/\* gets and sets a List, requires list field to be not final, does not manipulate the list \*/) .controller(StringController::new) // usual controllers, passed to every entry .initial("") // when adding a new entry to the list, this is the initial value it has .build() \`\`\` !\[list example\](https://user-images.githubusercontent.com/43245524/206925713-115d5737-19c5-4469-894c-710f6fc271cd.png) #### Available Flag You can mark options as unavailable on build to prevent changing the value. \`\`\`java Option.createBuilder(boolean.class) /\* option things \*/ .available(false) .build() \`\`\` Additionally, you can modify this value after building the option with \`option.setAvailable(false)\`. You can do this while the user is in the UI and it will dynamically change. !\[\](https://i.imgur.com/CzVtXpG.png) #### Option Flags Options can be marked with flags that have an associated runnable that is ran when the option is saved. Can be used to reload chunks, require a restart etc. There are some builtin flags to use which are listed below: \* \`OptionFlag.GAME\_RESTART\` - Opens a \`Screen\` asking the user to restart Minecraft \* \`OptionFlag.RELOAD\_CHUNKS\` - Reloads the world renderer \* \`OptionFlag.WORLD\_RENDER\_UPDATE\` - Rebuilds terrain \* \`OptionFlag.ASSET\_RELOAD\` - Reloads all assets \`\`\`java Option.createBuilder(boolean.class) /\* option things \*/ .flag(OptionFlag.GAME\_RESTART) .build() \`\`\` Please note if you are to use the same custom flag more than once it should be defined as a field or an interface, not in lambda form. This allows YACL to identify them as the same so they are only ran once. #### Dynamic Tooltips You can also make tooltips change based on the value of the option. It is as simple as consuming the value in the builder... \`\`\`java Option.createBuilder(boolean.class) .tooltip(value -> Text.of("I now know that my option is set to " + value + "!")) .build() \`\`\` #### Button "Options" A thing I noticed while using other config libraries is that it is way too hard to just make a custom button that a user can press next to all the other options, so I made it super easy! This time, you don't need a binding! See here about how to customize the action controller text. \`\`\`java ButtonOption.createBuilder() .name(Text.of("Pressable Button")) .tooltip(Text.of("This is so easy!")) // optional .action((yaclScreen, buttonOption) -> { System.out.println("Button has been pressed!"); }) .controller(ActionController::new) .build() \`\`\` !\[button option\](https://user-images.githubusercontent.com/43245524/188097468-326d3a56-bed7-43bb-91f2-0fcc449313cf.png) #### Placeholder Categories Placeholder categories can be used in place of a \`ConfigCategory\`. Rather than changing the option list to the category's options/groups, it just opens up another \`Screen\`. \`\`\`java PlaceholderCategory.createBuilder() .name(Text.of("Category Name")) .tooltip(Text.of("Tooltip for this category!")) .screen((client, parent) -> new MyScreen(parent)) .build() \`\`\` #### Instant Option Application You can also specify options to instantly apply to their bindings as the user changes it, skipping the \`Apply Changes\` button altogether. Note that this does prevent the user from being able to undo their actions and you from using option flags \`\`\`java Option.createBuilder(int.class) /\* option stuff! \*/ .instant(true) .build() \`\`\` --- # Unknown \> For the complete documentation index, see \[llms.txt\](https://docs.isxander.dev/yet-another-config-lib/llms.txt). Markdown versions of documentation pages are available by appending \`.md\` to page URLs; this page is available as \[Markdown\](https://docs.isxander.dev/yet-another-config-lib/archive/config-api.md). # Config API {% hint style="danger" %} This is an archive of the old GitHub wiki! This will be re-written in the near future. {% endhint %} ## What is this? The Config API handles saving and loading fields in a class automatically. This pairs great with YetAnotherConfigLib because now this library is a full solution to config in your mods. This API doesn't handle automatically generating a GUI, that is up to you to build. It would be silly to have one full, comprehensive API along with a mediocre annotations-based API together. This API is purely saving and loading. ## How do I use it? Before we get to actual code, it's best to explain the structure of this API (it's very simple) It's split into to main components, a config instance and config data. Config data is a class you create, containing all the fields that you want to save and load. A config instance manages that class, actually implementing saving and loading. ### Tutorial First, let's make a config data class. \`\`\`java public class MyConfig { @ConfigEntry public boolean myOption = true; } \`\`\` Let's leave this for now, and come back to it later. There is one implementation of \`ConfigInstance\` available by default: \`GsonConfigInstance\`. This instance uses GSON to serialize and deserialize JSON to a file. Only fields annotated with \`@ConfigEntry\` are included in the JSON. \`Text\`, \`Style\` and \`java.awt.Color\` have default type adapters, so there is no need to provide them in your GSON instance. Also, GSON is automatically configured to format fields as \`lower\_camel\_case\`. Now, let's construct a \`GsonConfigInstance\`. \`\`\`java GsonConfigInstace configInstance = new GsonConfigInstance<>(MyConfig.class, Path.of("path/to/config.json")); \`\`\` It's as simple as that, you can optionally pass your own \`Gson\` instance or \`GsonBuilder\` as a third argument for fine control of GSON. You can load your config with \`configInstance.load()\` and save it with \`configInstance.save()\`, producing the following JSON file. \`\`\`json { "my\_option": true } \`\`\` The fun doesn't stop there, there is also a utility method to help building \`YetAnotherConfigLib\` instances, (see the \[main wiki page\](https://github.com/isXander/YetAnotherConfigLib/wiki/home) for more details). \`\`\`java configInstance.buildConfig((configInstance, builder) -> /\* return the builder here \*/) \`\`\` \*\*NOTE: In 1.19.3 versions of YACL, the above has changed to the following:\*\* \`\`\`java YetAnotherConfigLib.create(configInstance, (defaults, config, builder) -> /\* return the builder here \*/) \`\`\` This method is useful because it does a few things: \* Automatically adds the save runnable, so you don't need to do it. \* Provides the config instance, allowing you to do some cool things: Config instances, also store a default version of your config data that you can access, along with the current one. This is really useful when creating bindings because you don't need to mirror your defaults, once in YACL builder, once in actual field initialisation. \`\`\`java .binding( config.getDefaults().myOption, () -> config.getConfig().myOption, val -> config.getConfig().myOption = val ) \`\`\` --- # Getting started | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/gui-builder/getting-started.md) . This wiki is currently a work-in-progress and is incomplete! Before we begin, it's important to note the wiki code examples will be using official Mojang mappings. You are also expected to have a basic knowledge of the Java programming language. If you don't, please learn Java first. There is a simple structure: categories contains groups, groups contain options. You can also skip the groups and just add options to the category directly. They will always appear above any groups. Before we start, lets go into detail about how to construct an `Option`, then we'll use that to make a GUI. Copy Option.createBuilder() // boolean is the type of option we'll be making .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This text will appear as a tooltip when you hover over the option."))) .binding( true, // the default value () -> this.myBooleanOption, // a getter to get the current value from newVal -> this.myBooleanOption = newVal ) .controller(TickBoxControllerBuilder::create) .build() An important concept in YACL options are the controllers. Each option type does not have a hardcoded way of being displayed. The logic of displaying the option in the GUI is held in the `Controller`. To learn more about controllers, click here. You will see in the above example, we're choosing to use a tick-box to display and control the boolean option. To start making a GUI with YACL, you will need to build an instance of `YetAnotherConfigLib`. We will plug in our `Option` code from above... Copy YetAnotherConfigLib.createBuilder() .title(Component.literal("Used for narration. Could be used to render a title in the future.")) .category(ConfigCategory.createBuilder() .name(Component.literal("Name of the category")) .tooltip(Component.literal("This text will appear as a tooltip when you hover or focus the button with Tab. There is no need to add \n to wrap as YACL will do it for you.")) .group(OptionGroup.createBuilder() .name(Component.literal("Name of the group")) .description(OptionDescription.of(Component.literal("This text will appear when you hover over the name or focus on the collapse button with Tab."))) .option(Option.createBuilder() .name(Component.literal("Boolean Option")) .description(OptionDescription.of(Component.literal("This text will appear as a tooltip when you hover over the option."))) .binding(true, () -> this.myBooleanOption, newVal -> this.myBooleanOption = newVal) .controller(TickBoxControllerBuilder::create) .build()) .build()) .build()) .build() All you have to do then is tell YACL to generate a Screen instance from it. Copy YetAnotherConfigLib.createBuilder() [...] .build() .generateScreen(parentScreen) // the screen that opens up when you close YACL You must generate a new instance of `YetAnotherConfigLib` every time you want a GUI `Screen`. You cannot just call `generateScreen()` again! It's that simple! You have made your first GUI with YACL! ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2F8383976-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fs4yG60a7UCSjUx5DT1wQ%252Fuploads%252Fem9v9MPOFoEdIeuhFxTV%252FScreenshot%25202023-11-26%2520at%252014.23.12.png%3Falt%3Dmedia%26token%3Dff9c751b-b9ec-43ec-b0a2-4da45898c0a9&width=768&dpr=3&quality=100&sign=565ba8df&sv=2) Displaying the GUI[](https://docs.isxander.dev/yet-another-config-lib/gui-builder#displaying-the-gui) ------------------------------------------------------------------------------------------------------ Now you've learned the basics of creating a config GUI, but how do you show it to the user? ### Mod Menu (Fabric)[](https://docs.isxander.dev/yet-another-config-lib/gui-builder#mod-menu-fabric) [Mod Menu](https://modrinth.com/mod/modmenu) is an extremely popular mod for Fabric that adds a menu that displays a list of currently installed mods, like Forge. You can use its API to add a config button to your mod's entry that opens up your newly created YACL config screen. #### Adding the dependency[](https://docs.isxander.dev/yet-another-config-lib/gui-builder#adding-the-dependency) First, add the repository to your build.gradle. Groovy DSL (build.gradle) Kotlin DSL (build.gradle.kts) Copy repositories { maven { name = "Terraformers" url = "https://maven.terraformersmc.com/" } } Copy repositories { maven("https://maven.terraformersmc.com/") { name = "Terraformers" } } Then, add the dependency Groovy DSL (build.gradle) Kotlin DSL (build.gradle.kts) Copy dependencies { modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") } Copy dependencies { modImplementation("com.terraformersmc:modmenu:${property("modmenu_version")}") } Then, define the version of Mod Menu you're using in your `gradle.properties`. You can get the latest version number [here](https://modrinth.com/mod/modmenu/version/latest) , but you may need a different version if you're not using the latest Minecraft version. See the [versions page](https://modrinth.com/mod/modmenu/versions) for a full list of versions. gradle.properties Copy modmenu_version=VERSION_NUMBER_HERE #### Using the API[](https://docs.isxander.dev/yet-another-config-lib/gui-builder#using-the-api) First, create the **modmenu entrypoint** by creating a new class in your mod. com/mymod/config/ModMenuIntegration.java Copy import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; public class ModMenuIntegration implements ModMenuApi { @Override public ConfigScreenFactory getModConfigScreenFactory() { return parentScreen -> YetAnotherConfigLib.createBuilder() ... .build() .generateScreen(parentScreen); } } If you want multiple methods to opening your configuration screen, extracting the config creation to a common method call is useful. #### Registering the entrypoint[](https://docs.isxander.dev/yet-another-config-lib/gui-builder#registering-the-entrypoint) Now that you've created the entrypoint, we need to tell mod menu to use it. fabric.mod.json Copy "entrypoints": { "modmenu": [\ "com.mymod.config.ModMenuIntegration"\ ] } And you're done! You can now test it out by going to the mod list, finding your mod, and pressing the configuration button to open your YACL GUI. ### NeoForge Mod List[](https://docs.isxander.dev/yet-another-config-lib/gui-builder#neoforge-mod-list) NeoForge has a mod list built-in, and allows you to register _extension points_ to extend the functionality of your mod's entry, notably, a config button. Add the following to your mod's constructor. after (and including) 1.20.6 before 1.20.6 Copy import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @Mod("my_mod_id") public class MyMod { public MyMod() { ModLoadingContext.get().registerExtensionPoint( IConfigScreenFactory.class, () -> (client, parent) -> YetAnotherConfigLib.createBuilder() [...] .build() .generateScreen(parent) ) } } Copy import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import nnet.neoforged.neoforge.client.ConfigScreenHandler; @Mod("my_mod_id") public class MyMod { public MyMod() { ModLoadingContext.get().registerExtensionPoint( ConfigScreenHandler.ConfigScreenFactory.class, () -> new ConfigScreenHandler.ConfigScreenFactory( (client, parent) -> YetAnotherConfigLib.createBuilder() [...] .build() .generateScreen(parent) ) ) } } If you want multiple methods to opening your configuration screen, extracting the config creation to a common method call is useful. That's it! You can now find your mod in the mod list and open your YACL configuration screen! [PreviousInstalling YACL](https://docs.isxander.dev/yet-another-config-lib/installing-yacl) [NextControllers](https://docs.isxander.dev/yet-another-config-lib/gui-builder/controllers) Last updated 2 years ago --- # Basic usage of Config API | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api.md) . What is it?[](https://docs.isxander.dev/yet-another-config-lib/config-api#what-is-it) -------------------------------------------------------------------------------------- Along with YACL's primary functionality, the GUI, this mod also provides a super easy way to manage your mod's config. It can be a tedious part of every mod you make, this is why the Config API was created! All you need to do is create a class, add some fields and tell YACL _how_ to save and load your config. An example...[](https://docs.isxander.dev/yet-another-config-lib/config-api#an-example) ---------------------------------------------------------------------------------------- Copy public class MyConfig { public static ConfigClassHandler HANDLER = ConfigClassHandler.createBuilder(MyConfig.class) .id(new ResourceLocation("mymod", "my_config")) .serializer(config -> GsonConfigSerializerBuilder.create(config) .setPath(FabricLoader.getInstance().getConfigDir().resolve("my_mod.json5")) .appendGsonBuilder(GsonBuilder::setPrettyPrint) // not needed, pretty print by default .setJson5(true) .build()) .build(); @SerialEntry public boolean myCoolBoolean = true; @SerialEntry public int myCoolInteger = 5; @SerialEntry(comment = "This string is amazing") public String myCoolString = "How amazing!"; } That is it! That's all the setup you need for your config. In this example, you can see that we told the config handler to use GSON to serialize your fields, meaning not just primitive/basic types work, but any! You can even see me modifying the GSON builder. **Only fields annotated with** `**@SerialEntry**` **are considered.** Saving and loading[](https://docs.isxander.dev/yet-another-config-lib/config-api#saving-and-loading) ----------------------------------------------------------------------------------------------------- In the above example, you can see we specified the serializer like so: Copy .serializer(config -> GsonConfigSerializerBuilder.create(config) .setPath(YACLPlatform.getConfigDir().resolve("my_mod.json5")) .appendGsonBuilder(GsonBuilder::setPrettyPrint) // not needed, pretty print by default .setJson5(true) .build()) You can see we specified the GSON serializer, set its path, configured GSON, and specified to use JSON5 spec. Currently, GSON is the only serializer made available to you. You can create your own, more on that later. To save and load use the respected methods: Copy MyConfig.HANDLER.save(); MyConfig.HANDLER.load(); [PreviousSpecial Options](https://docs.isxander.dev/yet-another-config-lib/gui-builder/special-options) [NextHome](https://docs.isxander.dev/yet-another-config-lib/archive/home) Last updated 2 years ago --- # Home | YetAnotherConfigLib For the complete documentation index, see [llms.txt](https://docs.isxander.dev/yet-another-config-lib/llms.txt) . This page is also available as [Markdown](https://docs.isxander.dev/yet-another-config-lib/archive/home.md) . This is an archive of the old GitHub wiki! This will be re-written in the near future. Welcome to YACL's wiki! This page will guide you on how to use YACL: ### Notes[](https://docs.isxander.dev/yet-another-config-lib/archive#notes) A few things to clear up before we get started... * This page will be laid out in the chronological order a developer would use to implement YACL. * This mod is only available for Fabric and Quilt. * All code examples presume you use Yarn mappings. * You must have basic/intermediate Java knowledge. ### A quick overview[](https://docs.isxander.dev/yet-another-config-lib/archive#a-quick-overview) For people who just want to skip to the full example, [click here](https://docs.isxander.dev/yet-another-config-lib/archive/home#all-together) ### Backbones of the config[](https://docs.isxander.dev/yet-another-config-lib/archive#backbones-of-the-config) The main class that contains everything is `YetAnotherConfigLib` (how fitting!). This interface accepts a name, config categories, a save function, and an init function. YACL's API is builder based, not class based, so everything is defined inside of a method, let's pretend we have a config class, it has a save function, and a few properties, and a method called `MyConfig#createGui()` Copy import net.minecraft.client.screen.Screen; public class MyConfig { public boolean booleanToggle = true; public int intSlider = 5; public void save() { /* save your config! */ } public Screen createGui(Screen parent) { // time to use YOCL! } } But let's forget about the rest of this class for now and start constructing YetAnotherConfigLib... Copy YetAnotherConfigLib.createBuilder() .name(Text.of("Mod Name")) .save(MyConfig::save) .build() Here you can see the general syntax for the API beginning to shape, however, this isn't very useful, let's add a category... Copy YetAnotherConfigLib.createBuilder() .title(Text.of("Mod Name")) .category(ConfigCategory.createBuilder() .name(Text.of("My Category") .tooltip(Text.of("This displays when you hover over a category button")) // optional .build()) .save(MyConfig::save) .build() Still, not very useful, but here is where it gets good! Let's forget about what we have so far and just focus on `Option` ### Options[](https://docs.isxander.dev/yet-another-config-lib/archive#options) Options are broken down into two main parts, a binding and a controller. #### Bindings[](https://docs.isxander.dev/yet-another-config-lib/archive#bindings) Bindings are simple, you _bind_ a property to an interface. This provides essential functionality to query and set values in your config, there are two types of bindings you can use by default, a generic binding and a minecraft binding. **Generic** This is the Binding you will be using most often, it accepts a default value, a getter, and a setter, like this... Copy Binding.generic(0 /* default value for setting */, () -> this.booleanToggle, newValue -> this.booleanToggle = newValue) **Minecraft** You can also bind an option found in the `GameOptions` class like this... Copy Binding.minecraft(Minecraft.getInstance().gameOptions.getAutoJump()) **Immutable** There is also an option to make a binding immutable (cannot be changed). This may be useful for labels Copy Binding.immutable(value) #### Controllers[](https://docs.isxander.dev/yet-another-config-lib/archive#controllers) Controllers provide YACL a graphical widget to display and for users to interact with, this functionality has been completely separated from the Option itself so you can define multiple ways of displaying the same datatype. By default there are only a handful of controllers available, but it is super easy to add another controller (later in the wiki) and covers the basics, booleans, numbers and enums. **BooleanController** A simple controller that displays `Text` based on the state of the boolean. There are a few pre-defined value formatters: `ON_OFF` (default), `TRUE_FALSE` and `YES_NO`. These can be found in the class. Copy new BooleanController(option /* provided by builder */, BooleanController.YES_NO_FORMATTER /* default ON_OFF, optional */) **TickBoxController** A controller that displays a tickbox, indicating true or false. Copy new TickBoxController(option /* provided by builder */) **EnumController** A simple controller that displays `Text` based on the name of the enum constant (this can be customised). By default, this controller first looks to see if the enum implements `NameableEnum`, which provides a `Text`, falling back on `Enum#toString()` Copy new EnumController(option /* provided by builder */, enumConstant -> Text.of(enumConstant.toString()) /* optional */) **CyclingListController** Any `Iterable` can be passed to this controller. It behaves just like `EnumController` and can be cycled with a click. Copy new CyclingListController(option /* provided by builder */, List.of("A", "B", "C"), entry -> entry.toLowerCase() /* optional */) **SliderController** There is a slider controller for every common number datatype: * `IntegerSliderController` * `FloatSliderController` * `DoubleSliderController` * `LongSliderController` These sliders accept a minimum value, a maximum value and an interval (slider increment) and optionally a value formatter. By default, doubles format to two decimal places, floats one, and all four are separated with commas every three digits. (again, can be customized) In this example, I chose to use an integer but all are the same. Copy new IntegerSliderController(option /* provided by builder */, 0 /* min */, 10 /* max */, 1 /* interval */) **ActionController** Simply displays some text on the right and executes the ButtonOption action on press. By default, the text reads `EXECUTE` but this can be customized. Copy new ActionController(option /* provided by builder */, Text.of("Run") /* optional */) **StringController** A custom text field implementation. Copy new StringController(option /* provided by builder */) **ColorController** A hex color field with a color preview. Copy new ColorController(option /* provided by builder */, allowAlpha /* default false */) **LabelController** Renders some `Text`, should be used with an immutable binding. Allows user to click on styled text or hover like in the chat. Copy new LabelController(option /* provided by builder */) #### Building an option[](https://docs.isxander.dev/yet-another-config-lib/archive#building-an-option) Here you can see a basic example of an option. Please note that there is a shorthand function for `Binding.generic` being used (see here) Copy Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build() ### All together![](https://docs.isxander.dev/yet-another-config-lib/archive#all-together) That's all you need to know to build YetAnotherConfigLib, let's put everything together! Copy import net.minecraft.client.screen.Screen; public class MyConfig { public boolean booleanToggle = true; public int intSlider = 5; public void save() { /* save your config! */ } public Screen createGui(Screen parent) { YetAnotherConfigLib.createBuilder() .title(Text.of("Mod Name")) .category(ConfigCategory.createBuilder() .name(Text.of("My Category")) .tooltip(Text.of("This displays when you hover over a category button")) // optional .option(Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build()) .build()) .save(MyConfig::save) .build() .generateScreen(parent); } } Make sure you notice the last line where a `Screen` is generated. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F188279998-f099337e-d5ca-4142-ad29-7876b79d1d25.png&width=768&dpr=3&quality=100&sign=41e2766c&sv=2) basic example ### Advanced features[](https://docs.isxander.dev/yet-another-config-lib/archive#advanced-features) #### Option Groups[](https://docs.isxander.dev/yet-another-config-lib/archive#option-groups) Instead of just adding an option to a category, you can also add groups to a category. Groups act much like subcategories, they are displayed as a separator in the option list. They also don't need to be named and just be used as a spacer. You can also set them to be collapsed by default if there are too many. Copy ConfigCategory.createBuilder() .name(Text.of("My Category")) .tooltip(Text.of("This displays when you hover over a category button")) // optional .group(OptionGroup.createBuilder() .name(Text.of("Option Group")) .tooltip(Text.of("Like everything in YACL, you can have tooltips")) // optional .collapsed(true) // optional, default false .option(Option.createBuilder(boolean.class) .name(Text.of("My Boolean Option")) .tooltip(Text.of("This option displays the basic capabilities of YetAnotherConfigLib")) // optional .binding( true, // default () -> this.booleanToggle, // getter newValue -> this.booleanToggle = newValue // setter ) .controller(BooleanController::new) .build()) .build()) .build() So, if we look at this in-game... ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F188280006-74aa192b-cdf5-496d-8f6e-d891e2f45b5d.png&width=768&dpr=3&quality=100&sign=2d159e36&sv=2) option group example #### Lists[](https://docs.isxander.dev/yet-another-config-lib/archive#lists) There is another type of option called a `ListOption`. It works with all controllers and all list types that has a controller type for it. Lists take a hybrid form of option groups and a regular option, and such you add lists with `.group()`, NOT `.option()`. You can minimize like a group and add flags like an option, it's both! Lists require an initial value for when users add another entry. Copy ListOption.createBuilder(String.class) .name(Text.of("List Option")) .binding(/* gets and sets a List, requires list field to be not final, does not manipulate the list */) .controller(StringController::new) // usual controllers, passed to every entry .initial("") // when adding a new entry to the list, this is the initial value it has .build() ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F206925713-115d5737-19c5-4469-894c-710f6fc271cd.png&width=768&dpr=3&quality=100&sign=2fa2b82e&sv=2) list example #### Available Flag[](https://docs.isxander.dev/yet-another-config-lib/archive#available-flag) You can mark options as unavailable on build to prevent changing the value. Copy Option.createBuilder(boolean.class) /* option things */ .available(false) .build() Additionally, you can modify this value after building the option with `option.setAvailable(false)`. You can do this while the user is in the UI and it will dynamically change. ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fi.imgur.com%2FCzVtXpG.png&width=768&dpr=3&quality=100&sign=bef7e586&sv=2) #### Option Flags[](https://docs.isxander.dev/yet-another-config-lib/archive#option-flags) Options can be marked with flags that have an associated runnable that is ran when the option is saved. Can be used to reload chunks, require a restart etc. There are some builtin flags to use which are listed below: * `OptionFlag.GAME_RESTART` - Opens a `Screen` asking the user to restart Minecraft * `OptionFlag.RELOAD_CHUNKS` - Reloads the world renderer * `OptionFlag.WORLD_RENDER_UPDATE` - Rebuilds terrain * `OptionFlag.ASSET_RELOAD` - Reloads all assets Copy Option.createBuilder(boolean.class) /* option things */ .flag(OptionFlag.GAME_RESTART) .build() Please note if you are to use the same custom flag more than once it should be defined as a field or an interface, not in lambda form. This allows YACL to identify them as the same so they are only ran once. #### Dynamic Tooltips[](https://docs.isxander.dev/yet-another-config-lib/archive#dynamic-tooltips) You can also make tooltips change based on the value of the option. It is as simple as consuming the value in the builder... Copy Option.createBuilder(boolean.class) .tooltip(value -> Text.of("I now know that my option is set to " + value + "!")) .build() #### Button "Options"[](https://docs.isxander.dev/yet-another-config-lib/archive#button-options) A thing I noticed while using other config libraries is that it is way too hard to just make a custom button that a user can press next to all the other options, so I made it super easy! This time, you don't need a binding! See here about how to customize the action controller text. Copy ButtonOption.createBuilder() .name(Text.of("Pressable Button")) .tooltip(Text.of("This is so easy!")) // optional .action((yaclScreen, buttonOption) -> { System.out.println("Button has been pressed!"); }) .controller(ActionController::new) .build() ![](https://docs.isxander.dev/yet-another-config-lib/~gitbook/image?url=https%3A%2F%2Fuser-images.githubusercontent.com%2F43245524%2F188097468-326d3a56-bed7-43bb-91f2-0fcc449313cf.png&width=768&dpr=3&quality=100&sign=c4fc65ee&sv=2) button option #### Placeholder Categories[](https://docs.isxander.dev/yet-another-config-lib/archive#placeholder-categories) Placeholder categories can be used in place of a `ConfigCategory`. Rather than changing the option list to the category's options/groups, it just opens up another `Screen`. Copy PlaceholderCategory.createBuilder() .name(Text.of("Category Name")) .tooltip(Text.of("Tooltip for this category!")) .screen((client, parent) -> new MyScreen(parent)) .build() #### Instant Option Application[](https://docs.isxander.dev/yet-another-config-lib/archive#instant-option-application) You can also specify options to instantly apply to their bindings as the user changes it, skipping the `Apply Changes` button altogether. Note that this does prevent the user from being able to undo their actions and you from using option flags Copy Option.createBuilder(int.class) /* option stuff! */ .instant(true) .build() [PreviousBasic usage of Config API](https://docs.isxander.dev/yet-another-config-lib/config-api/basic-usage-of-config-api) [NextConfig API](https://docs.isxander.dev/yet-another-config-lib/archive/config-api) Last updated 3 years ago ---