Skip to content

Commit 305718e

Browse files
committed
codec config and rewritten kotlin dsl
1 parent 65b4f7b commit 305718e

36 files changed

+1342
-601
lines changed

build.gradle.kts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
22

33
plugins {
44
`java-library`
5-
kotlin("jvm") version "1.9.23"
5+
kotlin("jvm")
66

7-
id("dev.architectury.loom") version "1.6.+"
7+
id("dev.architectury.loom")
88

9-
id("me.modmuss50.mod-publish-plugin") version "0.5.+"
9+
id("me.modmuss50.mod-publish-plugin")
1010
`maven-publish`
11-
id("org.ajoberstar.grgit") version "5.0.+"
11+
id("org.ajoberstar.grgit")
1212
}
1313

1414
val loader = loom.platform.get().name.lowercase()
@@ -20,7 +20,7 @@ val isForgeLike = isNeoforge || isForge
2020
val mcVersion = findProperty("mcVersion").toString()
2121

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

2626
val snapshotVer = "${grgit.branch.current().name.replace('/', '.')}-SNAPSHOT"
@@ -35,19 +35,6 @@ base {
3535
archivesName.set(property("modName").toString())
3636
}
3737

38-
stonecutter.expression {
39-
when (it) {
40-
"controlify" -> isPropDefined("deps.controlify")
41-
"mod-menu" -> isPropDefined("deps.modMenu")
42-
"fabric" -> isFabric
43-
"neoforge" -> isNeoforge
44-
"forge" -> isForge
45-
"!forge" -> !isForge
46-
"forge-like" -> isForgeLike
47-
else -> null
48-
}
49-
}
50-
5138
val testmod by sourceSets.creating {
5239
compileClasspath += sourceSets.main.get().compileClasspath
5340
runtimeClasspath += sourceSets.main.get().runtimeClasspath

settings.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import dev.kikugie.stonecutter.gradle.StonecutterSettings
1+
import dev.kikugie.stonecutter.StonecutterSettings
22

33
pluginManagement {
44
repositories {
@@ -9,11 +9,12 @@ pluginManagement {
99
maven("https://maven.neoforged.net/releases/")
1010
maven("https://maven.minecraftforge.net/")
1111
maven("https://maven.kikugie.dev/releases")
12+
maven("https://maven.kikugie.dev/snapshots")
1213
}
1314
}
1415

1516
plugins {
16-
id("dev.kikugie.stonecutter") version "0.3.5"
17+
id("dev.kikugie.stonecutter") version "0.4-beta.3"
1718
}
1819

1920
extensions.configure<StonecutterSettings> {
@@ -22,7 +23,7 @@ extensions.configure<StonecutterSettings> {
2223
shared {
2324
fun mc(mcVersion: String, name: String = mcVersion, loaders: Iterable<String>) {
2425
for (loader in loaders) {
25-
versions("$name-$loader")
26+
vers("$name-$loader", mcVersion)
2627
}
2728
}
2829

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.apache.commons.lang3.Validate;
77

88
import java.util.function.Consumer;
9+
import java.util.function.Function;
910
import java.util.function.Supplier;
1011

1112
/**
@@ -19,6 +20,10 @@ public interface Binding<T> {
1920

2021
T defaultValue();
2122

23+
default <U> Binding<U> xmap(Function<T, U> to, Function<U, T> from) {
24+
return Binding.generic(to.apply(this.defaultValue()), () -> to.apply(this.getValue()), v -> this.setValue(from.apply(v)));
25+
}
26+
2227
/**
2328
* Creates a generic binding.
2429
*

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,28 @@ interface Builder {
128128
* <p>
129129
* However, <strong>THIS IS NOT API SAFE!</strong> As part of the gui package, things
130130
* may change that could break compatibility with future versions of YACL.
131-
* A helpful utility (that is also not API safe) is {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)}
131+
* A helpful utility (that is also not API safe) is {@link dev.isxander.yacl3.gui.image.ImageRendererManager#registerOrGetImage(ResourceLocation, Supplier)}
132132
* which will cache the image renderer for the whole game lifecycle and construct it asynchronously to the render thread.
133133
* @param image the image renderer to display
134134
* @return this builder
135135
*/
136136
Builder customImage(CompletableFuture<Optional<ImageRenderer>> image);
137137

138+
/**
139+
* Sets a custom image renderer to display with the description.
140+
* This is useful for rendering other abstract things relevant to your mod.
141+
* <p>
142+
* However, <strong>THIS IS NOT API SAFE!</strong> As part of the gui package, things
143+
* may change that could break compatibility with future versions of YACL.
144+
* A helpful utility (that is also not API safe) is {@link dev.isxander.yacl3.gui.image.ImageRendererManager#registerOrGetImage(ResourceLocation, Supplier)}
145+
* which will cache the image renderer for the whole game lifecycle and construct it asynchronously to the render thread.
146+
* @param image the image renderer to display
147+
* @return this builder
148+
*/
149+
default Builder customImage(ImageRenderer image) {
150+
return this.customImage(CompletableFuture.completedFuture(Optional.of(image)));
151+
}
152+
138153
/**
139154
* Sets an animated GIF image to display with the description. This is backed by a regular minecraft resource
140155
* in your mod's /assets folder.

src/main/java/dev/isxander/yacl3/config/GsonConfigInstance.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ public GsonConfigInstance(Class<T> configClass, Path path, GsonBuilder builder)
6868
this.path = path;
6969
this.gson = builder
7070
.setExclusionStrategies(new ConfigExclusionStrategy())
71-
/*? if >1.20.4 { */
71+
/*? if >1.20.4 {*/
7272
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter(RegistryAccess.EMPTY))
73-
/*? } elif =1.20.4 {*//*
74-
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
75-
*//*? } else {*//*
76-
.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
73+
/*?} elif =1.20.4 {*/
74+
/*.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
75+
*//*?} else {*/
76+
/*.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
7777
*//*?}*/
7878
.registerTypeHierarchyAdapter(Style.class, /*? if >=1.20.4 {*/new GsonConfigSerializer.StyleTypeAdapter()/*?} else {*//*new Style.Serializer()*//*?}*/)
7979
.registerTypeHierarchyAdapter(Color.class, new ColorTypeAdapter())
@@ -169,12 +169,12 @@ public static class Builder<T> {
169169
private UnaryOperator<GsonBuilder> gsonBuilder = builder -> builder
170170
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
171171
.serializeNulls()
172-
/*? if >1.20.4 { */
172+
/*? if >1.20.4 {*/
173173
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter(RegistryAccess.EMPTY))
174-
/*? } elif =1.20.4 {*//*
175-
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
176-
*//*? } else {*//*
177-
.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
174+
/*?} elif =1.20.4 {*/
175+
/*.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
176+
*//*?} else {*/
177+
/*.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
178178
*//*?}*/
179179
.registerTypeHierarchyAdapter(Style.class, /*? if >=1.20.4 {*/new GsonConfigSerializer.StyleTypeAdapter()/*?} else {*//*new Style.Serializer()*//*?}*/)
180180
.registerTypeHierarchyAdapter(Color.class, new ColorTypeAdapter())

src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,12 @@ public static class Builder<T> implements GsonConfigSerializerBuilder<T> {
221221
private UnaryOperator<GsonBuilder> gsonBuilder = builder -> builder
222222
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
223223
.serializeNulls()
224-
/*? if >1.20.4 { */
224+
/*? if >1.20.4 {*/
225225
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter(RegistryAccess.EMPTY))
226-
/*? } elif =1.20.4 {*//*
227-
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
228-
*//*? } else {*//*
229-
.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
226+
/*?} elif =1.20.4 {*/
227+
/*.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
228+
*//*?} else {*/
229+
/*.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
230230
*//*?}*/
231231
.registerTypeHierarchyAdapter(Style.class, /*? if >=1.20.4 {*/new StyleTypeAdapter()/*?} else {*//*new Style.Serializer()*//*?}*/)
232232
.registerTypeHierarchyAdapter(Color.class, new ColorTypeAdapter())
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dev.isxander.yacl3.config.v3;
2+
3+
import org.jetbrains.annotations.ApiStatus;
4+
5+
import java.util.function.Function;
6+
import java.util.function.UnaryOperator;
7+
8+
@ApiStatus.Experimental
9+
public abstract class AbstractConfigEntry<T> extends AbstractReadonlyConfigEntry<T> implements ConfigEntry<T> {
10+
private T value;
11+
private final T defaultValue;
12+
13+
private Function<T, T> setModifier;
14+
15+
public AbstractConfigEntry(String fieldName, T defaultValue) {
16+
super(fieldName);
17+
this.value = defaultValue;
18+
this.defaultValue = defaultValue;
19+
this.setModifier = UnaryOperator.identity();
20+
}
21+
22+
@Override
23+
protected T innerGet() {
24+
return this.value;
25+
}
26+
27+
@Override
28+
public void set(T value) {
29+
this.value = this.setModifier.apply(value);
30+
}
31+
32+
@Override
33+
public T defaultValue() {
34+
return this.defaultValue;
35+
}
36+
37+
@Override
38+
public ConfigEntry<T> modifyGet(UnaryOperator<T> modifier) {
39+
super.modifyGet(modifier);
40+
return this;
41+
}
42+
43+
@Override
44+
public ConfigEntry<T> modifySet(UnaryOperator<T> modifier) {
45+
this.setModifier = this.setModifier.andThen(modifier);
46+
return this;
47+
}
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package dev.isxander.yacl3.config.v3;
2+
3+
import org.jetbrains.annotations.ApiStatus;
4+
5+
import java.util.function.Function;
6+
import java.util.function.UnaryOperator;
7+
8+
@ApiStatus.Experimental
9+
public abstract class AbstractReadonlyConfigEntry<T> implements ReadonlyConfigEntry<T> {
10+
private final String fieldName;
11+
12+
private Function<T, T> getModifier;
13+
14+
public AbstractReadonlyConfigEntry(String fieldName) {
15+
this.fieldName = fieldName;
16+
this.getModifier = UnaryOperator.identity();
17+
}
18+
19+
@Override
20+
public String fieldName() {
21+
return fieldName;
22+
}
23+
24+
@Override
25+
public T get() {
26+
return this.getModifier.apply(this.innerGet());
27+
}
28+
29+
protected abstract T innerGet();
30+
31+
@Override
32+
public ReadonlyConfigEntry<T> modifyGet(UnaryOperator<T> modifier) {
33+
this.getModifier = this.getModifier.andThen(modifier);
34+
return this;
35+
}
36+
37+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package dev.isxander.yacl3.config.v3;
2+
3+
import com.mojang.serialization.DataResult;
4+
import com.mojang.serialization.DynamicOps;
5+
import com.mojang.serialization.MapCodec;
6+
import com.mojang.serialization.RecordBuilder;
7+
import dev.isxander.yacl3.impl.utils.YACLConstants;
8+
import org.jetbrains.annotations.ApiStatus;
9+
10+
import java.util.Optional;
11+
12+
@ApiStatus.Experimental
13+
public class ChildConfigEntryImpl<T extends CodecConfig<T>> extends AbstractReadonlyConfigEntry<T> {
14+
private final T config;
15+
private final MapCodec<T> mapCodec;
16+
17+
public ChildConfigEntryImpl(String fieldName, T config) {
18+
super(fieldName);
19+
this.config = config;
20+
this.mapCodec = config.fieldOf(this.fieldName());
21+
}
22+
23+
@Override
24+
protected T innerGet() {
25+
return config;
26+
}
27+
28+
@Override
29+
public <R> RecordBuilder<R> encode(DynamicOps<R> ops, RecordBuilder<R> recordBuilder) {
30+
return mapCodec.encode(config, ops, recordBuilder);
31+
}
32+
33+
@Override
34+
public <R> boolean decode(R encoded, DynamicOps<R> ops) {
35+
DataResult<T> result = mapCodec.decoder().parse(ops, encoded);
36+
37+
//? if >1.20.4 {
38+
Optional<DataResult.Error<T>> error = result.error();
39+
//?} else {
40+
/*Optional<DataResult.PartialResult<T>> error = result.error();
41+
*///?}
42+
if (error.isPresent()) {
43+
YACLConstants.LOGGER.error("Failed to decode entry {}: {}", this.fieldName(), error.get().message());
44+
return false;
45+
}
46+
47+
return true;
48+
}
49+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package dev.isxander.yacl3.config.v3;
2+
3+
import com.mojang.datafixers.util.Pair;
4+
import com.mojang.serialization.*;
5+
import org.jetbrains.annotations.ApiStatus;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
@ApiStatus.Experimental
11+
public abstract class CodecConfig<S extends CodecConfig<S>> implements EntryAddable, Codec<S> {
12+
private final List<ReadonlyConfigEntry<?>> entries = new ArrayList<>();
13+
14+
public CodecConfig() {
15+
// cast here to throw immediately on construction
16+
var ignored = (S) this;
17+
}
18+
19+
@Override
20+
public <T> ConfigEntry<T> register(String fieldName, T defaultValue, Codec<T> codec) {
21+
ConfigEntry<T> entry = new CodecConfigEntryImpl<>(fieldName, defaultValue, codec);
22+
entries.add(entry);
23+
return entry;
24+
}
25+
26+
@Override
27+
public <T extends CodecConfig<T>> ReadonlyConfigEntry<T> register(String fieldName, T configInstance) {
28+
ReadonlyConfigEntry<T> entry = new ChildConfigEntryImpl<>(fieldName, configInstance);
29+
entries.add(entry);
30+
return entry;
31+
}
32+
33+
protected void onFinishedDecode(boolean successful) {
34+
}
35+
36+
@Override
37+
public <R> DataResult<R> encode(S input, DynamicOps<R> ops, R prefix) {
38+
if (input != null && input != this) {
39+
throw new IllegalArgumentException("`input` is ignored. It must be null or equal to `this`.");
40+
}
41+
42+
return this.encode(ops, prefix);
43+
}
44+
45+
@Override
46+
public <R> DataResult<Pair<S, R>> decode(DynamicOps<R> ops, R input) {
47+
this.decode(input, ops);
48+
return DataResult.success(Pair.of((S) this, input));
49+
}
50+
51+
public final <R> DataResult<R> encode(DynamicOps<R> ops, R prefix) {
52+
RecordBuilder<R> builder = ops.mapBuilder();
53+
for (ReadonlyConfigEntry<?> entry : entries) {
54+
builder = entry.encode(ops, builder);
55+
}
56+
return builder.build(prefix);
57+
}
58+
59+
public final <R> DataResult<R> encodeStart(DynamicOps<R> ops) {
60+
return this.encode(ops, ops.empty());
61+
}
62+
63+
/**
64+
* @return true if decoding of all entries was successful
65+
*/
66+
public final <R> boolean decode(R encoded, DynamicOps<R> ops) {
67+
boolean success = true;
68+
69+
for (ReadonlyConfigEntry<?> entry : entries) {
70+
success &= entry.decode(encoded, ops);
71+
}
72+
73+
onFinishedDecode(success);
74+
75+
return success;
76+
}
77+
}

0 commit comments

Comments
 (0)