Skip to content

Commit e73a08e

Browse files
committed
3.6.0
1 parent 519ac2f commit e73a08e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1089
-410
lines changed

README.md

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
<center><div align="center">
2-
3-
![](https://raw.githubusercontent.com/isXander/YetAnotherConfigLib/1.19/src/main/resources/yacl-128x.png)
4-
51
# YetAnotherConfigLib
62

73
![Enviroment](https://img.shields.io/badge/Enviroment-Client-purple)
@@ -13,42 +9,76 @@
139

1410
[![Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/isxander)
1511

16-
Yet Another Config Lib, like, what were you expecting?
17-
18-
[![](https://www.bisecthosting.com/partners/custom-banners/08bbd3ff-5c0d-4480-8738-de0f070a04dd.png)](https://bisecthosting.com/xander)
12+
A mod designed to fit a modder's needs for client-side configuration.
1913

20-
</div></center>
14+
[![](https://www.bisecthosting.com/partners/custom-banners/08bbd3ff-5c0d-4480-8738-de0f070a04dd.png)](https://bisecthosting.com/xander)
2115

2216
## Why does this mod even exist?
2317

2418
This mod was made to fill a hole in this area of Fabric modding. The existing main config libraries don't achieve what I want from them:
2519

26-
- **[Cloth Config API](https://modrinth.com/mod/cloth-config)**:<br/>**It's 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))
27-
- **[SpruceUI](https://github.com/LambdAurora/SpruceUI)**:<br/>**It isn't designed for configuration.** In this essence the design feels cluttered. Further details available in [this issue](https://github.com/isXander/Zoomify/issues/85).
28-
- **[MidnightLib](https://modrinth.com/mod/midnightlib)**:<br/>**It has cosmetics among other utilities.** It may not be large but some players (including me) wouldn't want cosmetics out of nowhere.
29-
- **[OwoLib](https://modrinth.com/mod/owo-lib)**:<br/>**It's content focused.** It does a lot of other things as well as config, adding to the size.
20+
- **[Cloth Config API](https://modrinth.com/mod/cloth-config)**: **It's 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))
21+
- **[SpruceUI](https://github.com/LambdAurora/SpruceUI)**: **It isn't designed for configuration.** In this essence the design feels cluttered. Further details available in [this issue](https://github.com/isXander/Zoomify/issues/85).
22+
- **[MidnightLib](https://modrinth.com/mod/midnightlib)**: **It has cosmetics among other utilities.** It may not be large but some players (including me) wouldn't want cosmetics out of nowhere.
23+
- **[OwoLib](https://modrinth.com/mod/owo-lib)**: **It's content focused.** It does a lot of other things as well as config, adding to the size.
3024

3125
As you can see, there's sadly a drawback with all of them and this is where YetAnotherConfigLib comes in.
3226

33-
## How is YACL better?
27+
## Why use YACL?
3428

35-
YACL has the favour of hindsight. Whilst developing this fresh library, I can make sure that it does everything right:
29+
### Features
3630

37-
- **Client sided library.** YACL is built for client mods only, making it a smaller size.
38-
- **Easy API.** YACL takes inspiration from [Sodium's](https://modrinth.com/mod/sodium) internal configuration library.
39-
- **It's styled to fit in Minecraft.** YACL's GUI is designed to fit right in.
31+
YACL has a ton of configuration features:
4032

41-
## Usage
33+
- Custom control widgets
34+
- Create your own unique "controller" if the default set does not suit your needs
35+
- Rich descriptions
36+
- Clickable & hoverable text, powered by vanilla's Text component system
37+
- WebP (including animated) image previews
38+
- Custom rich-renderable section to replace image
39+
- Multiple controllers for the same type:
40+
- Sliders or fields for numbers
41+
- Dropdowns, cyclers, or raw text fields for strings
42+
- Tickboxes or ON/OFF text display for booleans
43+
- ...and more!
44+
- Fully-featured color picker
45+
- Accessible with full compatibility for keyboard control (optimised for Controlify usage)
46+
- High organisation with tabs (categories) and collapsable groups
47+
- Built-in serialization/deserialization techniques so you can skip the error-prone config code
48+
- Full alternative Kotlin DSL
4249

43-
[The wiki](https://github.com/isXander/YetAnotherConfigLib/wiki/Usage) contains a full documentation on how to use YACL.
50+
### Version support
51+
52+
YACL supports a huge amount of versions, all kept up to date and released simultaneously, thanks to the amazing
53+
[Stonecutter](https://stonecutter.kikugie.dev/) build tool.
4454

45-
## Screenshots
55+
| Version | Fabric | Forge | NeoForge |
56+
|-------------------------|--------|-------|----------|
57+
| **1.20.1** ||||
58+
| **1.20.4** ||||
59+
| **1.20.5 - 1.20.6** ||||
60+
| **1.21.0 - 1.21.1** ||||
61+
| **1.21.2** (RC version) ||||
4662

47-
<center><div align="center">
63+
That's **9** different targets, supporting versions that are 500+ days old!
4864

49-
![java_A3zdbksGkC](https://user-images.githubusercontent.com/43245524/206924832-293b0780-2a8c-4b09-8765-155318d09ed9.png)
65+
_**Note**: Forge (LexForge) is not and will not be supported past 1.20.1.
66+
If you're a developer, please port to NeoForge.
67+
If you're a user, you may find that all your favourite mods have already done so._
5068

51-
</div></center>
69+
Each is a separate build, so make sure your users get the correct YACL version for their target of choice.
70+
71+
### Design
72+
73+
YACL is designed to fit right in with the vanilla GUI aesthetic, and will evolve with Minecraft itself. Take a look at
74+
the gallery to see how even in all the currently supported versions, YACL's design looks different to fit in with
75+
vanilla GUI updates.
76+
77+
![image preview](https://cdn.modrinth.com/data/1eAoo2KR/images/5862570281f5109119c11f21a1bba52b6a2ab17f.png)
78+
79+
## Usage for Developers
80+
81+
[The wiki](https://github.com/isXander/YetAnotherConfigLib/wiki/Usage) contains a full documentation on how to use YACL.
5282

5383
## License
5484

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ val isForgeLike = isNeoforge || isForge
2020
val mcVersion = findProperty("mcVersion").toString()
2121

2222
group = "dev.isxander"
23-
val versionWithoutMC = "3.5.0"
23+
val versionWithoutMC = "3.6.0"
2424
version = "$versionWithoutMC+${stonecutter.current.project}"
2525

2626
val snapshotVer = "${grgit.branch.current().name.replace('/', '.')}-SNAPSHOT"

changelog.md

Lines changed: 74 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# YetAnotherConfigLib 3.5.0
1+
# YetAnotherConfigLib 3.6.0
22

33
This build supports the following versions:
4+
- Fabric 1.21.2
45
- Fabric 1.20.1
56
- Fabric 1.20.4
67
- Fabric 1.20.6 (also supports 1.20.5)
@@ -10,80 +11,83 @@ This build supports the following versions:
1011
- NeoForge 1.20.4
1112
- MinecraftForge 1.20.1
1213

13-
## *Experimental* Codec Config
14+
## State Managers
1415

15-
This update brings a new experimental config API that utilises Mojang's Codec for (de)serialization.
16+
Options now no longer hold their state themselves. This job is now delegated to the `StateManager`.
17+
18+
This change serves to allow multiple options to share the same state, but with different controllers, descriptions,
19+
names, etc.
20+
21+
### Example
22+
23+
Take an example of how this might be useful:
24+
25+
You have a long range of gamepad bindings, utilising a custom YACL _controller_. They are sorted alphabetically in YACL.
26+
You want a specific gamepad binding, let's call it 'Jump', to be featured at the top of the list, but also still
27+
appear alphabetically in the list.
28+
29+
You add a second 'Jump' YACL _option_ and place it at the beginning of the _category_, you assign it an identical
30+
_binding_.
31+
32+
This appears to work, but you discover a problem: you can modify the featured _option_ just fine, but when you hit
33+
'Save Changes', you see an error in your log 'Option value mismatch' and your option defaults to the previous, now
34+
unchanged value.
35+
36+
Why is this? Each instance of _`Option`_ holds its own _pending value_. Which is then applied to the _binding_ when you
37+
click save. When you click save, YACL iterates over each option in-order and applies the pending value to the _binding._
38+
Which leads to the following series of events:
39+
40+
1. YACL successfully applies the featured 'Jump' option to the binding, as it's first in the list.
41+
2. YACL then finds the non-featured 'Jump' option, which retained the original default value because the
42+
_pending value_ has not changed. From step 1, the binding's value has now changed to something none-default, so now
43+
it sets the binding again to the pending value of THIS option, now the binding is back to its default state.
44+
3. YACL finishes saving _options_, and checks over each one again. It checks that the _pending value_ matches the
45+
_binding_. Because the featured 'Jump' option now doesn't match, it creates an error in your log:
46+
'Option value mismatch' and assigns the _pending value_ to the _binding_ value.
47+
48+
### Solution
49+
50+
State managers essentially solve this by moving the _pending value_ into a separate object that can then be given to
51+
multiple _options_. They ensure that each option's controller is kept up to date by emitting events upon the state's
52+
change that controllers then listen to, to keep themselves up to date.
53+
54+
### Code Examples
1655

1756
```java
18-
public class CodecConfig extends JsonFileCodecConfig/*or*/CodecConfig {
19-
public static final CodecConfig INSTANCE = new CodecConfig();
20-
21-
public final ConfigEntry<Integer> myInt =
22-
register("my_int", 0, Codec.INT);
23-
24-
public final ReadonlyConfigEntry<InnerCodecConfig> myInnerConfig =
25-
register("my_inner_config", InnerCodecConfig.INSTANCE);
26-
27-
public CodecConfig() {
28-
super(path);
29-
}
30-
31-
void test() {
32-
loadFromFile(); // load like this
33-
saveToFile(); // save like this
34-
35-
// or if you just extend CodecConfig instead of JsonFileConfig:
36-
JsonElement element = null;
37-
this.decode(element, JsonOps.INSTANCE); // load
38-
DataResult<JsonElement> encoded = this.encodeStart(JsonOps.INSTANCE); // save
39-
}
40-
}
41-
```
42-
or in Kotlin...
43-
```kotlin
44-
object CodecConfig : JsonFileCodecConfig(path) {
45-
val myInt by register<Int>(0, Codec.INT)
46-
47-
val myInnerConfig by register(InnerCodecConfig)
48-
49-
fun test() {
50-
loadFromFile()
51-
saveToFile()
52-
53-
// blah blah blah
54-
}
55-
}
57+
StateManager<Boolean> stateManager = StateManager.createSimple(getter, setter, def);
58+
59+
category.option(Option.<Boolean>createOption()
60+
.name(Component.literal("Sharing Tick Box"))
61+
.stateManager(stateManager)
62+
.controller(TickBoxControllerBuilder::create)
63+
.build());
64+
category.option(Option.<Boolean>createOption()
65+
.name(Component.literal("Sharing Boolean"))
66+
.stateManager(stateManager)
67+
.controller(BooleanControllerBuilder::create)
68+
.build());
5669
```
5770

58-
## Rewritten Kotlin DSL
59-
60-
Completely rewrote the Kotlin DSL!
61-
62-
```kotlin
63-
YetAnotherConfigLib("namespace") {
64-
val category by categories.registering {
65-
val option by rootOptions.registering<Int> {
66-
controller = slider(range = 5..10)
67-
binding(::thisProp, default)
68-
69-
val otherOption by categories["category"]["group"].futureRef<Boolean>()
70-
otherOption.onReady { it.setAvailable(false) }
71-
}
72-
73-
// translation key is generated automagically
74-
val label by rootOptions.registeringLabel
75-
76-
val group by groups.registering {
77-
val otherOption = options.register<Boolean>("otherOption") {
78-
controller = tickBox()
79-
}
80-
}
81-
}
82-
}
71+
Here, instead of using a `.binding()`, you use a `.stateManager()`. Now both share and update the same state!
72+
73+
### Event Changes
74+
75+
Options that listen to events, `.listener((option, newValue) -> {})`, the syntax has slightly changed. The
76+
aforementioned methods have been deprecated, replaced with `.addListener((option, event) -> {})`, you can retrieve the
77+
new value with `option.pendingValue()`.
78+
79+
### Consolidation of .instant() behaviour
80+
81+
Because of this open-ness of how an option's pending value gets applied, users of YACL have a lot more power
82+
on what happens with how an option's state is applied.
83+
84+
```java
85+
.stateManager(StateManager.createInstant(getter, setter, def))
8386
```
8487

85-
## Changes
88+
Is the new way to get instantly applied options. You can also implement `StateManager` yourself.
89+
90+
## Other Changes
8691

87-
- Fix dropdown controllers erroneously showing their dropdown - Crendgrim
88-
- Make cancel/reset and undo buttons public for accessing
89-
- Add compatibility for 1.21
92+
- Added support for 1.21.2
93+
- Label Options now allow change of state, so labels can now be changed dynamically.

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
org.gradle.jvmargs=-Xmx4G
2+
org.gradle.parallel=true
23

34
modrinthId=1eAoo2KR
45
curseforgeId=667299
@@ -8,7 +9,7 @@ modId=yet_another_config_lib_v3
89
modName=YetAnotherConfigLib
910
modDescription=YetAnotherConfigLib (yacl) is just that. A builder-based configuration library for Minecraft.
1011

11-
deps.fabricLoader=0.15.11
12+
deps.fabricLoader=0.16.7
1213
deps.imageio=3.10.0
1314
deps.quiltParsers=0.2.1
1415
deps.mixinExtras=0.3.5
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Fri Apr 05 20:43:25 CEST 2024
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

settings.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pluginManagement {
1414
}
1515

1616
plugins {
17-
id("dev.kikugie.stonecutter") version "0.4-beta.3"
17+
id("dev.kikugie.stonecutter") version "0.4.5"
1818
}
1919

2020
extensions.configure<StonecutterSettings> {
@@ -27,9 +27,10 @@ extensions.configure<StonecutterSettings> {
2727
}
2828
}
2929

30+
mc("1.21.2", loaders = listOf("fabric"))
31+
mc("1.21", loaders = listOf("fabric", "neoforge"))
3032
mc("1.20.6", loaders = listOf("fabric", "neoforge"))
3133
mc("1.20.4", loaders = listOf("fabric", "neoforge"))
32-
mc("1.21", loaders = listOf("fabric", "neoforge"))
3334
mc("1.20.1", loaders = listOf("fabric", "forge"))
3435
}
3536
create(rootProject)

src/main/java/dev/isxander/yacl3/api/LabelOption.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ static Builder createBuilder() {
2626
}
2727

2828
interface Builder {
29+
Builder state(@NotNull StateManager<Component> stateManager);
30+
2931
/**
3032
* Appends a line to the label
3133
*/

src/main/java/dev/isxander/yacl3/api/ListOption.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ interface Builder<T> {
9393
*/
9494
Builder<T> customController(@NotNull Function<ListOptionEntry<T>, Controller<T>> control);
9595

96+
Builder<T> state(@NotNull StateManager<List<T>> stateManager);
97+
9698
/**
9799
* Sets the binding for the option.
98100
* Used for default, getter and setter.
@@ -159,6 +161,10 @@ interface Builder<T> {
159161
*/
160162
Builder<T> collapsed(boolean collapsible);
161163

164+
ListOption.Builder<T> addListener(@NotNull OptionEventListener<List<T>> listener);
165+
166+
ListOption.Builder<T> addListeners(@NotNull Collection<OptionEventListener<List<T>>> listeners);
167+
162168
/**
163169
* Adds a listener to the option. Invoked upon changing any of the list's entries.
164170
*

0 commit comments

Comments
 (0)