aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/dev/isxander/yacl3
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/dev/isxander/yacl3')
-rw-r--r--src/main/java/dev/isxander/yacl3/api/Binding.java5
-rw-r--r--src/main/java/dev/isxander/yacl3/api/OptionDescription.java17
-rw-r--r--src/main/java/dev/isxander/yacl3/config/GsonConfigInstance.java20
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java10
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/AbstractConfigEntry.java48
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/AbstractReadonlyConfigEntry.java37
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/ChildConfigEntryImpl.java49
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/CodecConfig.java77
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/CodecConfigEntryImpl.java42
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/ConfigEntry.java38
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/EntryAddable.java11
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/JsonFileCodecConfig.java90
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/KotlinExts.kt53
-rw-r--r--src/main/java/dev/isxander/yacl3/config/v3/ReadonlyConfigEntry.java26
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java4
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java26
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/YACLScreen.java4
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/YACLTooltip.java7
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java22
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/DropdownWidget.java4
-rw-r--r--src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java12
-rw-r--r--src/main/java/dev/isxander/yacl3/mixin/TabNavigationBarAccessor.java4
-rw-r--r--src/main/java/dev/isxander/yacl3/platform/YACLPlatform.java8
23 files changed, 552 insertions, 62 deletions
diff --git a/src/main/java/dev/isxander/yacl3/api/Binding.java b/src/main/java/dev/isxander/yacl3/api/Binding.java
index f41b78b..61c59a2 100644
--- a/src/main/java/dev/isxander/yacl3/api/Binding.java
+++ b/src/main/java/dev/isxander/yacl3/api/Binding.java
@@ -6,6 +6,7 @@ import net.minecraft.client.OptionInstance;
import org.apache.commons.lang3.Validate;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
/**
@@ -19,6 +20,10 @@ public interface Binding<T> {
T defaultValue();
+ default <U> Binding<U> xmap(Function<T, U> to, Function<U, T> from) {
+ return Binding.generic(to.apply(this.defaultValue()), () -> to.apply(this.getValue()), v -> this.setValue(from.apply(v)));
+ }
+
/**
* Creates a generic binding.
*
diff --git a/src/main/java/dev/isxander/yacl3/api/OptionDescription.java b/src/main/java/dev/isxander/yacl3/api/OptionDescription.java
index 7336379..fce7e2f 100644
--- a/src/main/java/dev/isxander/yacl3/api/OptionDescription.java
+++ b/src/main/java/dev/isxander/yacl3/api/OptionDescription.java
@@ -128,7 +128,7 @@ public interface OptionDescription {
* <p>
* However, <strong>THIS IS NOT API SAFE!</strong> As part of the gui package, things
* may change that could break compatibility with future versions of YACL.
- * A helpful utility (that is also not API safe) is {@link ImageRenderer#getOrMakeAsync(ResourceLocation, Supplier)}
+ * A helpful utility (that is also not API safe) is {@link dev.isxander.yacl3.gui.image.ImageRendererManager#registerOrGetImage(ResourceLocation, Supplier)}
* which will cache the image renderer for the whole game lifecycle and construct it asynchronously to the render thread.
* @param image the image renderer to display
* @return this builder
@@ -136,6 +136,21 @@ public interface OptionDescription {
Builder customImage(CompletableFuture<Optional<ImageRenderer>> image);
/**
+ * Sets a custom image renderer to display with the description.
+ * This is useful for rendering other abstract things relevant to your mod.
+ * <p>
+ * However, <strong>THIS IS NOT API SAFE!</strong> As part of the gui package, things
+ * may change that could break compatibility with future versions of YACL.
+ * A helpful utility (that is also not API safe) is {@link dev.isxander.yacl3.gui.image.ImageRendererManager#registerOrGetImage(ResourceLocation, Supplier)}
+ * which will cache the image renderer for the whole game lifecycle and construct it asynchronously to the render thread.
+ * @param image the image renderer to display
+ * @return this builder
+ */
+ default Builder customImage(ImageRenderer image) {
+ return this.customImage(CompletableFuture.completedFuture(Optional.of(image)));
+ }
+
+ /**
* Sets an animated GIF image to display with the description. This is backed by a regular minecraft resource
* in your mod's /assets folder.
*
diff --git a/src/main/java/dev/isxander/yacl3/config/GsonConfigInstance.java b/src/main/java/dev/isxander/yacl3/config/GsonConfigInstance.java
index 83a0b1c..a0f7ee4 100644
--- a/src/main/java/dev/isxander/yacl3/config/GsonConfigInstance.java
+++ b/src/main/java/dev/isxander/yacl3/config/GsonConfigInstance.java
@@ -68,12 +68,12 @@ public class GsonConfigInstance<T> extends ConfigInstance<T> {
this.path = path;
this.gson = builder
.setExclusionStrategies(new ConfigExclusionStrategy())
- /*? if >1.20.4 { */
+ /*? if >1.20.4 {*/
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter(RegistryAccess.EMPTY))
- /*? } elif =1.20.4 {*//*
- .registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
- *//*? } else {*//*
- .registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
+ /*?} elif =1.20.4 {*/
+ /*.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
+ *//*?} else {*/
+ /*.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
*//*?}*/
.registerTypeHierarchyAdapter(Style.class, /*? if >=1.20.4 {*/new GsonConfigSerializer.StyleTypeAdapter()/*?} else {*//*new Style.Serializer()*//*?}*/)
.registerTypeHierarchyAdapter(Color.class, new ColorTypeAdapter())
@@ -169,12 +169,12 @@ public class GsonConfigInstance<T> extends ConfigInstance<T> {
private UnaryOperator<GsonBuilder> gsonBuilder = builder -> builder
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.serializeNulls()
- /*? if >1.20.4 { */
+ /*? if >1.20.4 {*/
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter(RegistryAccess.EMPTY))
- /*? } elif =1.20.4 {*//*
- .registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
- *//*? } else {*//*
- .registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
+ /*?} elif =1.20.4 {*/
+ /*.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
+ *//*?} else {*/
+ /*.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
*//*?}*/
.registerTypeHierarchyAdapter(Style.class, /*? if >=1.20.4 {*/new GsonConfigSerializer.StyleTypeAdapter()/*?} else {*//*new Style.Serializer()*//*?}*/)
.registerTypeHierarchyAdapter(Color.class, new ColorTypeAdapter())
diff --git a/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java b/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java
index 3492c55..70e0e0b 100644
--- a/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java
+++ b/src/main/java/dev/isxander/yacl3/config/v2/impl/serializer/GsonConfigSerializer.java
@@ -221,12 +221,12 @@ public class GsonConfigSerializer<T> extends ConfigSerializer<T> {
private UnaryOperator<GsonBuilder> gsonBuilder = builder -> builder
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.serializeNulls()
- /*? if >1.20.4 { */
+ /*? if >1.20.4 {*/
.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter(RegistryAccess.EMPTY))
- /*? } elif =1.20.4 {*//*
- .registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
- *//*? } else {*//*
- .registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
+ /*?} elif =1.20.4 {*/
+ /*.registerTypeHierarchyAdapter(Component.class, new Component.SerializerAdapter())
+ *//*?} else {*/
+ /*.registerTypeHierarchyAdapter(Component.class, new Component.Serializer())
*//*?}*/
.registerTypeHierarchyAdapter(Style.class, /*? if >=1.20.4 {*/new StyleTypeAdapter()/*?} else {*//*new Style.Serializer()*//*?}*/)
.registerTypeHierarchyAdapter(Color.class, new ColorTypeAdapter())
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/AbstractConfigEntry.java b/src/main/java/dev/isxander/yacl3/config/v3/AbstractConfigEntry.java
new file mode 100644
index 0000000..8092f23
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/AbstractConfigEntry.java
@@ -0,0 +1,48 @@
+package dev.isxander.yacl3.config.v3;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
+
+@ApiStatus.Experimental
+public abstract class AbstractConfigEntry<T> extends AbstractReadonlyConfigEntry<T> implements ConfigEntry<T> {
+ private T value;
+ private final T defaultValue;
+
+ private Function<T, T> setModifier;
+
+ public AbstractConfigEntry(String fieldName, T defaultValue) {
+ super(fieldName);
+ this.value = defaultValue;
+ this.defaultValue = defaultValue;
+ this.setModifier = UnaryOperator.identity();
+ }
+
+ @Override
+ protected T innerGet() {
+ return this.value;
+ }
+
+ @Override
+ public void set(T value) {
+ this.value = this.setModifier.apply(value);
+ }
+
+ @Override
+ public T defaultValue() {
+ return this.defaultValue;
+ }
+
+ @Override
+ public ConfigEntry<T> modifyGet(UnaryOperator<T> modifier) {
+ super.modifyGet(modifier);
+ return this;
+ }
+
+ @Override
+ public ConfigEntry<T> modifySet(UnaryOperator<T> modifier) {
+ this.setModifier = this.setModifier.andThen(modifier);
+ return this;
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/AbstractReadonlyConfigEntry.java b/src/main/java/dev/isxander/yacl3/config/v3/AbstractReadonlyConfigEntry.java
new file mode 100644
index 0000000..7f59853
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/AbstractReadonlyConfigEntry.java
@@ -0,0 +1,37 @@
+package dev.isxander.yacl3.config.v3;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
+
+@ApiStatus.Experimental
+public abstract class AbstractReadonlyConfigEntry<T> implements ReadonlyConfigEntry<T> {
+ private final String fieldName;
+
+ private Function<T, T> getModifier;
+
+ public AbstractReadonlyConfigEntry(String fieldName) {
+ this.fieldName = fieldName;
+ this.getModifier = UnaryOperator.identity();
+ }
+
+ @Override
+ public String fieldName() {
+ return fieldName;
+ }
+
+ @Override
+ public T get() {
+ return this.getModifier.apply(this.innerGet());
+ }
+
+ protected abstract T innerGet();
+
+ @Override
+ public ReadonlyConfigEntry<T> modifyGet(UnaryOperator<T> modifier) {
+ this.getModifier = this.getModifier.andThen(modifier);
+ return this;
+ }
+
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/ChildConfigEntryImpl.java b/src/main/java/dev/isxander/yacl3/config/v3/ChildConfigEntryImpl.java
new file mode 100644
index 0000000..5b608de
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/ChildConfigEntryImpl.java
@@ -0,0 +1,49 @@
+package dev.isxander.yacl3.config.v3;
+
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.MapCodec;
+import com.mojang.serialization.RecordBuilder;
+import dev.isxander.yacl3.impl.utils.YACLConstants;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.Optional;
+
+@ApiStatus.Experimental
+public class ChildConfigEntryImpl<T extends CodecConfig<T>> extends AbstractReadonlyConfigEntry<T> {
+ private final T config;
+ private final MapCodec<T> mapCodec;
+
+ public ChildConfigEntryImpl(String fieldName, T config) {
+ super(fieldName);
+ this.config = config;
+ this.mapCodec = config.fieldOf(this.fieldName());
+ }
+
+ @Override
+ protected T innerGet() {
+ return config;
+ }
+
+ @Override
+ public <R> RecordBuilder<R> encode(DynamicOps<R> ops, RecordBuilder<R> recordBuilder) {
+ return mapCodec.encode(config, ops, recordBuilder);
+ }
+
+ @Override
+ public <R> boolean decode(R encoded, DynamicOps<R> ops) {
+ DataResult<T> result = mapCodec.decoder().parse(ops, encoded);
+
+ //? if >1.20.4 {
+ Optional<DataResult.Error<T>> error = result.error();
+ //?} else {
+ /*Optional<DataResult.PartialResult<T>> error = result.error();
+ *///?}
+ if (error.isPresent()) {
+ YACLConstants.LOGGER.error("Failed to decode entry {}: {}", this.fieldName(), error.get().message());
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/CodecConfig.java b/src/main/java/dev/isxander/yacl3/config/v3/CodecConfig.java
new file mode 100644
index 0000000..833a7ef
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/CodecConfig.java
@@ -0,0 +1,77 @@
+package dev.isxander.yacl3.config.v3;
+
+import com.mojang.datafixers.util.Pair;
+import com.mojang.serialization.*;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@ApiStatus.Experimental
+public abstract class CodecConfig<S extends CodecConfig<S>> implements EntryAddable, Codec<S> {
+ private final List<ReadonlyConfigEntry<?>> entries = new ArrayList<>();
+
+ public CodecConfig() {
+ // cast here to throw immediately on construction
+ var ignored = (S) this;
+ }
+
+ @Override
+ public <T> ConfigEntry<T> register(String fieldName, T defaultValue, Codec<T> codec) {
+ ConfigEntry<T> entry = new CodecConfigEntryImpl<>(fieldName, defaultValue, codec);
+ entries.add(entry);
+ return entry;
+ }
+
+ @Override
+ public <T extends CodecConfig<T>> ReadonlyConfigEntry<T> register(String fieldName, T configInstance) {
+ ReadonlyConfigEntry<T> entry = new ChildConfigEntryImpl<>(fieldName, configInstance);
+ entries.add(entry);
+ return entry;
+ }
+
+ protected void onFinishedDecode(boolean successful) {
+ }
+
+ @Override
+ public <R> DataResult<R> encode(S input, DynamicOps<R> ops, R prefix) {
+ if (input != null && input != this) {
+ throw new IllegalArgumentException("`input` is ignored. It must be null or equal to `this`.");
+ }
+
+ return this.encode(ops, prefix);
+ }
+
+ @Override
+ public <R> DataResult<Pair<S, R>> decode(DynamicOps<R> ops, R input) {
+ this.decode(input, ops);
+ return DataResult.success(Pair.of((S) this, input));
+ }
+
+ public final <R> DataResult<R> encode(DynamicOps<R> ops, R prefix) {
+ RecordBuilder<R> builder = ops.mapBuilder();
+ for (ReadonlyConfigEntry<?> entry : entries) {
+ builder = entry.encode(ops, builder);
+ }
+ return builder.build(prefix);
+ }
+
+ public final <R> DataResult<R> encodeStart(DynamicOps<R> ops) {
+ return this.encode(ops, ops.empty());
+ }
+
+ /**
+ * @return true if decoding of all entries was successful
+ */
+ public final <R> boolean decode(R encoded, DynamicOps<R> ops) {
+ boolean success = true;
+
+ for (ReadonlyConfigEntry<?> entry : entries) {
+ success &= entry.decode(encoded, ops);
+ }
+
+ onFinishedDecode(success);
+
+ return success;
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/CodecConfigEntryImpl.java b/src/main/java/dev/isxander/yacl3/config/v3/CodecConfigEntryImpl.java
new file mode 100644
index 0000000..855fd22
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/CodecConfigEntryImpl.java
@@ -0,0 +1,42 @@
+package dev.isxander.yacl3.config.v3;
+
+import com.mojang.serialization.*;
+import dev.isxander.yacl3.impl.utils.YACLConstants;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.Optional;
+
+@ApiStatus.Experimental
+public class CodecConfigEntryImpl<T> extends AbstractConfigEntry<T> {
+ private final MapCodec<T> mapCodec;
+
+ public CodecConfigEntryImpl(String fieldName, T defaultValue, Codec<T> codec) {
+ super(fieldName, defaultValue);
+ this.mapCodec = codec.fieldOf(this.fieldName());
+ }
+
+ @Override
+ public <R> RecordBuilder<R> encode(DynamicOps<R> ops, RecordBuilder<R> recordBuilder) {
+ return mapCodec.encode(get(), ops, recordBuilder);
+ }
+
+ @Override
+ public <R> boolean decode(R encoded, DynamicOps<R> ops) {
+ DataResult<T> result = mapCodec.decoder().parse(ops, encoded);
+
+ //? if >1.20.4 {
+ Optional<DataResult.Error<T>> error = result.error();
+ //?} else {
+ /*Optional<DataResult.PartialResult<T>> error = result.error();
+ *///?}
+ if (error.isPresent()) {
+ YACLConstants.LOGGER.error("Failed to decode entry {}: {}", this.fieldName(), error.get().message());
+ return false;
+ }
+
+ T value = result.result().orElseThrow();
+ this.set(value);
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/ConfigEntry.java b/src/main/java/dev/isxander/yacl3/config/v3/ConfigEntry.java
new file mode 100644
index 0000000..c22d796
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/ConfigEntry.java
@@ -0,0 +1,38 @@
+package dev.isxander.yacl3.config.v3;
+
+import dev.isxander.yacl3.api.Binding;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+
+@ApiStatus.Experimental
+public interface ConfigEntry<T> extends ReadonlyConfigEntry<T> {
+
+ void set(T value);
+
+ T defaultValue();
+
+ @Override
+ ConfigEntry<T> modifyGet(UnaryOperator<T> modifier);
+
+ @Override
+ default ConfigEntry<T> onGet(Consumer<T> consumer) {
+ return this.modifyGet(v -> {
+ consumer.accept(v);
+ return v;
+ });
+ }
+
+ ConfigEntry<T> modifySet(UnaryOperator<T> modifier);
+ default ConfigEntry<T> onSet(Consumer<T> consumer) {
+ return this.modifySet(v -> {
+ consumer.accept(v);
+ return v;
+ });
+ }
+
+ default Binding<T> asBinding() {
+ return Binding.generic(this.defaultValue(), this::get, this::set);
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/EntryAddable.java b/src/main/java/dev/isxander/yacl3/config/v3/EntryAddable.java
new file mode 100644
index 0000000..9ebd12d
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/EntryAddable.java
@@ -0,0 +1,11 @@
+package dev.isxander.yacl3.config.v3;
+
+import com.mojang.serialization.Codec;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.Experimental
+public interface EntryAddable {
+ <T> ConfigEntry<T> register(String fieldName, T defaultValue, Codec<T> codec);
+
+ <T extends CodecConfig<T>> ReadonlyConfigEntry<T> register(String fieldName, T configInstance);
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/JsonFileCodecConfig.java b/src/main/java/dev/isxander/yacl3/config/v3/JsonFileCodecConfig.java
new file mode 100644
index 0000000..49a0dac
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/JsonFileCodecConfig.java
@@ -0,0 +1,90 @@
+package dev.isxander.yacl3.config.v3;
+
+import com.google.gson.*;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.JsonOps;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+@ApiStatus.Experimental
+public abstract class JsonFileCodecConfig extends CodecConfig {
+ private final Path configPath;
+ private final Gson gson;
+
+ public JsonFileCodecConfig(Path configPath) {
+ this.configPath = configPath;
+ this.gson = createGson();
+ }
+
+ public void saveToFile() {
+ DataResult<JsonElement> jsonTreeResult = this.encodeStart(JsonOps.INSTANCE);
+ if (jsonTreeResult.error().isPresent()) {
+ onSaveError(
+ SaveError.ENCODING,
+ new IllegalStateException("Failed to encode: " + jsonTreeResult.error().get().message())
+ );
+ return;
+ }
+
+ JsonElement jsonTree = jsonTreeResult.result().orElseThrow();
+ String json = gson.toJson(jsonTree);
+
+ try {
+ Files.writeString(configPath, json, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
+ } catch (IOException e) {
+ onSaveError(SaveError.WRITING, e);
+ }
+ }
+
+ public boolean loadFromFile() {
+ if (Files.notExists(configPath)) {
+ return false;
+ }
+
+ String json;
+ try {
+ json = Files.readString(configPath);
+ } catch (IOException e) {
+ onLoadError(LoadError.READING, e);
+ return false;
+ }
+
+ JsonElement jsonTree;
+ try {
+ jsonTree = JsonParser.parseString(json);
+ } catch (JsonParseException e) {
+ onLoadError(LoadError.JSON_PARSING, e);
+ return false;
+ }
+
+ return this.decode(jsonTree, JsonOps.INSTANCE);
+ }
+
+ protected Gson createGson() {
+ return new GsonBuilder().setPrettyPrinting().create();
+ }
+
+ protected void onSaveError(SaveError error, @Nullable Throwable e) {
+ throw new IllegalStateException("Error whilst " + error.name().toLowerCase(), e);
+ }
+
+ protected void onLoadError(LoadError error, @Nullable Throwable e) {
+ throw new IllegalStateException("Error whilst " + error.name().toLowerCase(), e);
+ }
+
+ protected enum SaveError {
+ WRITING,
+ ENCODING,
+ }
+
+ protected enum LoadError {
+ READING,
+ JSON_PARSING,
+ DECODING,
+ }
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/KotlinExts.kt b/src/main/java/dev/isxander/yacl3/config/v3/KotlinExts.kt
new file mode 100644
index 0000000..2c51656
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/KotlinExts.kt
@@ -0,0 +1,53 @@
+package dev.isxander.yacl3.config.v3
+
+import com.mojang.serialization.Codec
+import dev.isxander.yacl3.dsl.OptionDsl
+import dev.isxander.yacl3.dsl.OptionRegistrar
+import org.jetbrains.annotations.ApiStatus
+import kotlin.properties.PropertyDelegateProvider
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+@ApiStatus.Experimental
+fun <T> EntryAddable.register(
+ default: T,
+ codec: Codec<T>
+) = PropertyDelegateProvider<EntryAddable, ReadOnlyProperty<EntryAddable, ConfigEntry<T>>> { thisRef, property ->
+ val entry = thisRef.register(property.name, default, codec)
+ ReadOnlyProperty { _, _ -> entry }
+}
+
+fun <T : CodecConfig<T>> EntryAddable.register(
+ fieldName: String? = null,
+ configInstance: T
+) = PropertyDelegateProvider<EntryAddable, T> { thisRef, property ->
+ thisRef.register(fieldName ?: property.name, configInstance)
+ configInstance
+}
+
+operator fun <T : CodecConfig<T>> T.getValue(thisRef: CodecConfig<*>?, property: KProperty<*>): T {
+ return this
+}
+
+@get:ApiStatus.Experimental
+@set:ApiStatus.Experimental
+var <T> ConfigEntry<T>.value: T
+ get() = this.get()
+ set(value) = this.set(value)
+
+@get:ApiStatus.Experimental
+val <T> ConfigEntry<T>.default: T
+ get() = this.defaultValue()
+
+@get:ApiStatus.Experimental
+val ConfigEntry<*>.fieldName: String
+ get() = this.fieldName()
+
+@ApiStatus.Experimental
+fun <T : Any> OptionRegistrar.register(
+ configEntry: ConfigEntry<T>,
+ block: OptionDsl<T>.() -> Unit
+) = register<T>(configEntry.fieldName) {
+ binding(configEntry.asBinding())
+ block()
+}
diff --git a/src/main/java/dev/isxander/yacl3/config/v3/ReadonlyConfigEntry.java b/src/main/java/dev/isxander/yacl3/config/v3/ReadonlyConfigEntry.java
new file mode 100644
index 0000000..c38853b
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl3/config/v3/ReadonlyConfigEntry.java
@@ -0,0 +1,26 @@
+package dev.isxander.yacl3.config.v3;
+
+import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.RecordBuilder;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+
+@ApiStatus.Experimental
+public interface ReadonlyConfigEntry<T> {
+ String fieldName();
+
+ T get();
+
+ ReadonlyConfigEntry<T> modifyGet(UnaryOperator<T> modifier);
+ default ReadonlyConfigEntry<T> onGet(Consumer<T> consumer) {
+ return this.modifyGet(v -> {
+ consumer.accept(v);
+ return v;
+ });
+ }
+
+ <R> RecordBuilder<R> encode(DynamicOps<R> ops, RecordBuilder<R> recordBuilder);
+ <R> boolean decode(R encoded, DynamicOps<R> ops);
+}
diff --git a/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java b/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java
index e6ab3f1..8def3b3 100644
--- a/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java
+++ b/src/main/java/dev/isxander/yacl3/gui/AbstractWidget.java
@@ -100,12 +100,12 @@ public abstract class AbstractWidget implements GuiEventListener, Renderable, Na
vertex.addVertex(matrix4f, x1, y2, 0).setColor(startColor);
vertex.addVertex(matrix4f, x2, y2, 0).setColor(endColor);
vertex.addVertex(matrix4f, x2, y1, 0).setColor(endColor);
- *//*? } else { */
+ *//*?} else {*/
vertex.vertex(matrix4f, x1, y1, 0).color(startColor).endVertex();
vertex.vertex(matrix4f, x1, y2, 0).color(startColor).endVertex();
vertex.vertex(matrix4f, x2, y2, 0).color(endColor).endVertex();
vertex.vertex(matrix4f, x2, y1, 0).color(endColor).endVertex();
- /*? } */
+ /*?}*/
}
diff --git a/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java b/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java
index be95caa..8412cc8 100644
--- a/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java
+++ b/src/main/java/dev/isxander/yacl3/gui/ElementListWidgetExt.java
@@ -25,8 +25,8 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten
public ElementListWidgetExt(Minecraft client, int x, int y, int width, int height, boolean smoothScrolling) {
/*? if >1.20.2 {*/
super(client, width, x, y, height);
- /*? } else {*//*
- super(client, width, height, y, y + height, 22);
+ /*?} else {*/
+ /*super(client, width, height, y, y + height, 22);
this.x0 = x;
this.x1 = x + width;
*//*?}*/
@@ -53,10 +53,10 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten
}
@Override
- /*? if >1.20.2 { */
+ /*? if >1.20.2 {*/
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta)
- /*?} else { *//*
- public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta)
+ /*?} else {*/
+ /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta)
*//*?}*/
{
if (usingScrollbar) {
@@ -73,10 +73,10 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten
graphics.enableScissor(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight());
- /*? if >1.20.2 { */
+ /*? if >1.20.2 {*/
super.renderWidget(graphics, mouseX, mouseY, delta);
- /*?} else { *//*
- super.render(graphics, mouseX, mouseY, delta);
+ /*?} else {*/
+ /*super.render(graphics, mouseX, mouseY, delta);
*//*?}*/
graphics.disableScissor();
@@ -84,7 +84,7 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten
returnSmoothAmount = false;
}
- /*? if >1.20.1 { */
+ /*? if >1.20.1 {*/
@Override
/*?}*/
protected boolean isValidMouseClick(int button) {
@@ -197,8 +197,8 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten
@Override
/*? if >1.20.4 {*/
protected void renderListItems(GuiGraphics graphics, int mouseX, int mouseY, float delta)
- /*? } else {*//*
- protected void renderList(GuiGraphics graphics, int mouseX, int mouseY, float delta)
+ /*?} else {*/
+ /*protected void renderList(GuiGraphics graphics, int mouseX, int mouseY, float delta)
*//*?}*/
{
int left = this.getRowLeft();
@@ -252,8 +252,8 @@ public class ElementListWidgetExt<E extends ElementListWidgetExt.Entry<E>> exten
}
}
- /*? if <1.20.3 {*//*
- @Override
+ /*? if <1.20.3 {*/
+ /*@Override
public int getX() {
return x0;
}
diff --git a/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java
index 04f1b67..90bc75f 100644
--- a/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java
+++ b/src/main/java/dev/isxander/yacl3/gui/YACLScreen.java
@@ -123,8 +123,8 @@ public class YACLScreen extends Screen {
currentPopupController = null;
}
- /*? if <=1.20.4 {*//*
- @Override
+ /*? if <=1.20.4 {*/
+ /*@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
renderDirtBackground(graphics);
super.render(graphics, mouseX, mouseY, delta);
diff --git a/src/main/java/dev/isxander/yacl3/gui/YACLTooltip.java b/src/main/java/dev/isxander/yacl3/gui/YACLTooltip.java
index 33028d7..7c1e7e1 100644
--- a/src/main/java/dev/isxander/yacl3/gui/YACLTooltip.java
+++ b/src/main/java/dev/isxander/yacl3/gui/YACLTooltip.java
@@ -13,11 +13,10 @@ public class YACLTooltip extends Tooltip {
this.widget = widget;
}
- /*? if >1.20.4 {*/ // stonecutter cannot handle AND expressions
- /*? } elif >1.20.1 {*//*
- @Override
+ //? if >1.20.1 && <=1.20.4 {
+ /*@Override
protected ClientTooltipPositioner createTooltipPositioner(boolean bl, boolean bl2, ScreenRectangle screenRectangle) {
return new YACLTooltipPositioner(widget);
}
- *//*?}*/
+ *///?}
}
diff --git a/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java b/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java
index 8830f20..efa1aec 100644
--- a/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java
+++ b/src/main/java/dev/isxander/yacl3/gui/controllers/ColorPickerWidget.java
@@ -16,8 +16,8 @@ public class ColorPickerWidget extends ControllerPopupWidget<ColorController> {
/*? if >1.20.1 {*/
private static final ResourceLocation COLOR_PICKER_LOCATION = YACLPlatform.rl("controller/colorpicker");
private static final ResourceLocation TRANSPARENT_TEXTURE_LOCATION = YACLPlatform.rl("controller/transparent");
- /*? } else {*//*
- // nineslice and repeating only work on a 256x atlas
+ /*?} else {*/
+ /*// nineslice and repeating only work on a 256x atlas
private static final ResourceLocation COLOR_PICKER_ATLAS = YACLPlatform.rl("textures/gui/colorpicker-atlas.png");
*//*?}*/
@@ -88,10 +88,10 @@ public class ColorPickerWidget extends ControllerPopupWidget<ColorController> {
graphics.pose().translate(0, 0, 10); // render over text
//Background
- /*? if >1.20.3 { */
+ /*? if >1.20.3 {*/
graphics.blitSprite(COLOR_PICKER_LOCATION, colorPickerDim.x() - 5, colorPickerDim.y() - 5, colorPickerDim.width() + 10, colorPickerDim.height() + 10);
- /*? } else {*//*
- graphics.blitNineSliced(COLOR_PICKER_ATLAS, colorPickerDim.x() - 5, colorPickerDim.y() - 5, colorPickerDim.width() + 10, colorPickerDim.height() + 10, 3, 236, 34, 0, 0);
+ /*?} else {*/
+ /*graphics.blitNineSliced(COLOR_PICKER_ATLAS, colorPickerDim.x() - 5, colorPickerDim.y() - 5, colorPickerDim.width() + 10, colorPickerDim.height() + 10, 3, 236, 34, 0, 0);
*//*?}*/
//Main color preview
@@ -99,10 +99,10 @@ public class ColorPickerWidget extends ControllerPopupWidget<ColorController> {
graphics.fill(previewColorDim.x() - outline, previewColorDim.y() - outline, previewColorDim.xLimit() + outline, previewColorDim.yLimit() + outline, Color.black.getRGB());
//transparent texture - must be rendered BEFORE the main color preview
if(controller.allowAlpha()) {
- /*? if >1.20.3 { */
+ /*? if >1.20.3 {*/
graphics.blitSprite(TRANSPARENT_TEXTURE_LOCATION, previewColorDim.x(), previewColorDim.y(), previewColorDim.width(), previewColorDim.height());
- /*? } else {*//*
- graphics.blitRepeating(COLOR_PICKER_ATLAS, previewColorDim.x(), previewColorDim.y(), previewColorDim.width(), previewColorDim.height(), 236, 0, 8, 8);
+ /*?} else {*/
+ /*graphics.blitRepeating(COLOR_PICKER_ATLAS, previewColorDim.x(), previewColorDim.y(), previewColorDim.width(), previewColorDim.height(), 236, 0, 8, 8);
*//*?}*/
}
//main color preview
@@ -134,10 +134,10 @@ public class ColorPickerWidget extends ControllerPopupWidget<ColorController> {
//outline
graphics.fill(alphaGradientDim.x() - outline, alphaGradientDim.y() - outline, alphaGradientDim.xLimit() + outline, alphaGradientDim.yLimit() + outline, Color.black.getRGB());
//Transparent texture
- /*? if >1.20.3 { */
+ /*? if >1.20.3 {*/
graphics.blitSprite(TRANSPARENT_TEXTURE_LOCATION, alphaGradientDim.x(), alphaGradientDim.y(), alphaGradientDim.width(), sliderHeight);
- /*? } else {*//*
- graphics.blitRepeating(COLOR_PICKER_ATLAS, alphaGradientDim.x(), alphaGradientDim.y(), alphaGradientDim.width(), sliderHeight, 236, 0, 8, 8);
+ /*?} else {*/
+ /*graphics.blitRepeating(COLOR_PICKER_ATLAS, alphaGradientDim.x(), alphaGradientDim.y(), alphaGradientDim.width(), sliderHeight, 236, 0, 8, 8);
*//*?}*/
//Pending color to transparent
fillSidewaysGradient(graphics, alphaGradientDim.x(), alphaGradientDim.y(), alphaGradientDim.xLimit(), alphaGradientDim.yLimit(), getRgbWithoutAlpha(), 0x00000000);
diff --git a/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/DropdownWidget.java b/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/DropdownWidget.java
index 6252291..f799059 100644
--- a/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/DropdownWidget.java
+++ b/src/main/java/dev/isxander/yacl3/gui/controllers/dropdown/DropdownWidget.java
@@ -63,8 +63,8 @@ public class DropdownWidget<T> extends ControllerPopupWidget<AbstractDropdownCon
graphics.blit(
/*? if >1.20.4 {*/
Screen.MENU_BACKGROUND,
- /*?} else {*//*
- Screen.BACKGROUND_LOCATION,
+ /*?} else {*/
+ /*Screen.BACKGROUND_LOCATION,
*//*?}*/
dropdownDim.x(), dropdownDim.y(), 0,
0.0f, 0.0f,
diff --git a/src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java b/src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java
index b8293fb..aadc249 100644
--- a/src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java
+++ b/src/main/java/dev/isxander/yacl3/gui/utils/YACLRenderHelper.java
@@ -13,15 +13,15 @@ public class YACLRenderHelper {
YACLPlatform.mcRl("widget/button_highlighted"), // !disabled & focused
YACLPlatform.mcRl("widget/slider_highlighted") // disabled & focused
);
- /*?} else {*//*
- private static final ResourceLocation SLIDER_LOCATION = new ResourceLocation("textures/gui/slider.png");
+ /*?} else {*/
+ /*private static final ResourceLocation SLIDER_LOCATION = new ResourceLocation("textures/gui/slider.png");
*//*?}*/
public static void renderButtonTexture(GuiGraphics graphics, int x, int y, int width, int height, boolean enabled, boolean focused) {
/*? if >1.20.1 {*/
graphics.blitSprite(SPRITES.get(enabled, focused), x, y, width, height);
- /*?} else {*//*
- int textureV;
+ /*?} else {*/
+ /*int textureV;
if (enabled) {
textureV = focused ? 60 : 40;
} else {
@@ -36,8 +36,8 @@ public class YACLRenderHelper {
public static ResourceLocation getSpriteLocation(String path) {
/*? if >1.20.3 {*/
return YACLPlatform.rl(path);
- /*? } else {*//*
- return YACLPlatform.rl("textures/gui/sprites/" + path + ".png");
+ /*?} else {*/
+ /*return YACLPlatform.rl("textures/gui/sprites/" + path + ".png");
*//*?}*/
}
}
diff --git a/src/main/java/dev/isxander/yacl3/mixin/TabNavigationBarAccessor.java b/src/main/java/dev/isxander/yacl3/mixin/TabNavigationBarAccessor.java
index e0fe06c..3ad331f 100644
--- a/src/main/java/dev/isxander/yacl3/mixin/TabNavigationBarAccessor.java
+++ b/src/main/java/dev/isxander/yacl3/mixin/TabNavigationBarAccessor.java
@@ -13,8 +13,8 @@ public interface TabNavigationBarAccessor {
/*? if >1.20.4 {*/
@Accessor("layout")
net.minecraft.client.gui.layouts.LinearLayout yacl$getLayout();
- /*? } else {*//*
- @Accessor("layout")
+ /*?} else {*/
+ /*@Accessor("layout")
net.minecraft.client.gui.layouts.GridLayout yacl$getLayout();
*//*?}*/
diff --git a/src/main/java/dev/isxander/yacl3/platform/YACLPlatform.java b/src/main/java/dev/isxander/yacl3/platform/YACLPlatform.java
index e330324..514c964 100644
--- a/src/main/java/dev/isxander/yacl3/platform/YACLPlatform.java
+++ b/src/main/java/dev/isxander/yacl3/platform/YACLPlatform.java
@@ -1,6 +1,6 @@
package dev.isxander.yacl3.platform;
-/*?if fabric {*/
+/*? if fabric {*/
import net.fabricmc.loader.api.FabricLoader;
/*?} elif neoforge {*//*
import net.neoforged.fml.loading.FMLEnvironment;
@@ -39,7 +39,7 @@ public final class YACLPlatform {
}
public static Env getEnvironment() {
- /*?if fabric {*/
+ /*? if fabric {*/
return switch (FabricLoader.getInstance().getEnvironmentType()) {
case CLIENT -> Env.CLIENT;
case SERVER -> Env.SERVER;
@@ -53,7 +53,7 @@ public final class YACLPlatform {
}
public static Path getConfigDir() {
- /*?if fabric {*/
+ /*? if fabric {*/
return FabricLoader.getInstance().getConfigDir();
/*?} elif forge-like {*//*
return FMLPaths.CONFIGDIR.get();
@@ -61,7 +61,7 @@ public final class YACLPlatform {
}
public static boolean isDevelopmentEnv() {
- /*?if fabric {*/
+ /*? if fabric {*/
return FabricLoader.getInstance().isDevelopmentEnvironment();
/*?} elif forge-like {*//*
return !FMLEnvironment.production;