From 1ce7ddaf38ba4de1740f4de7ccbf708df5a39983 Mon Sep 17 00:00:00 2001 From: JFronny Date: Sun, 21 Aug 2022 22:34:55 +0200 Subject: [PATCH 1/4] [config] Implement config DSL and rewrite reflection implementation --- build.gradle | 4 + docs/README.md | 2 +- docs/libjf-web-v0.md | 2 +- gradle.properties | 10 +- .../java/io/gitlab/jfronny/libjf/LibJf.java | 11 +- libjf-base/src/main/resources/fabric.mod.json | 5 +- libjf-config-reflect-v0/build.gradle | 7 + .../config/impl/reflect/ConfigProvider.java | 190 ++++++++++++++++++ .../entrypoint/JfConfigReflectSafe.java | 26 +++ .../reflect/entrypoint/JfConfigUnsafe.java | 22 ++ .../src/main/resources/fabric.mod.json | 36 ++++ .../jfronny/libjf/config/test/TestConfig.java | 0 .../libjf-config-v0-testmod/lang/en_us.json | 13 ++ .../src/testmod/resources/fabric.mod.json | 2 +- .../libjf/config/api/ConfigInstance.java | 31 --- .../libjf/config/impl/ConfigHolderImpl.java | 100 --------- .../config/impl/ConfigInstanceAbstract.java | 170 ---------------- .../config/impl/ConfigInstanceCategory.java | 22 -- .../libjf/config/impl/ConfigInstanceRoot.java | 77 ------- .../libjf/config/impl/EntryInfoImpl.java | 113 ----------- .../config/impl/entrypoint/JfConfigSafe.java | 31 --- .../impl/entrypoint/JfConfigUnsafe.java | 17 -- .../assets/libjf-config-v0/lang/en_us.json | 6 - .../libjf-config-v0-testmod/lang/en_us.json | 13 -- .../build.gradle | 4 +- .../libjf/config/api/WidgetFactory.java | 0 .../config/impl/client/JfConfigClient.java | 0 .../libjf/config/impl/client/ModMenu.java | 0 .../client/gui/EntryInfoWidgetBuilder.java | 41 ++-- .../client/gui/MidnightConfigListWidget.java | 0 .../impl/client/gui/TinyConfigScreen.java | 22 +- .../config/impl/client/gui/WidgetState.java | 4 +- .../client/gui/presets/PresetListWidget.java | 0 .../client/gui/presets/PresetsScreen.java | 7 +- .../jfronny/libjf/config/api/Category.java | 0 .../libjf/config/api/ConfigCategory.java | 33 +++ .../libjf/config/api/ConfigHolder.java | 4 +- .../libjf/config/api/ConfigInstance.java | 13 ++ .../jfronny/libjf/config/api/Entry.java | 0 .../jfronny/libjf/config/api/EntryInfo.java | 19 +- .../jfronny/libjf/config/api/JfConfig.java | 0 .../libjf/config/api/JfCustomConfig.java | 7 + .../jfronny/libjf/config/api/Preset.java | 0 .../jfronny/libjf/config/api/Verifier.java | 0 .../libjf/config/api/dsl/CategoryBuilder.java | 33 +++ .../libjf/config/api/dsl/ConfigBuilder.java | 19 ++ .../jfronny/libjf/config/api/dsl/DSL.java | 25 +++ .../jfronny/libjf/config/api/type/Type.java | 177 ++++++++++++++++ .../libjf/config/impl/AuxiliaryMetadata.java | 0 .../libjf/config/impl/ConfigHolderImpl.java | 56 ++++++ .../libjf/config/impl/JfConfigCommand.java | 59 +++--- .../config/impl/JfConfigWatchService.java | 0 .../config/impl/dsl/CategoryBuilderImpl.java | 166 +++++++++++++++ .../config/impl/dsl/ConfigBuilderImpl.java | 58 ++++++ .../libjf/config/impl/dsl/DSLImpl.java | 48 +++++ .../config/impl/dsl/DslConfigCategory.java | 89 ++++++++ .../config/impl/dsl/DslConfigInstance.java | 47 +++++ .../libjf/config/impl/dsl/DslEntryInfo.java | 174 ++++++++++++++++ .../config/impl/entrypoint/JfConfigSafe.java | 25 +++ .../assets/libjf-config-v0/lang/en_us.json | 6 + .../src/main/resources/fabric.mod.json | 3 +- .../libjf/config/test/TestConfigCustom.java | 31 +++ .../src/testmod/resources/fabric.mod.json | 18 ++ libjf-data-manipulation-v0/build.gradle | 3 +- libjf-data-v0/build.gradle | 4 +- libjf-devutil-v0/build.gradle | 2 +- libjf-translate-v1/build.gradle | 3 +- libjf-unsafe-v0/build.gradle | 2 +- .../jfronny/libjf/unsafe/DynamicEntry.java | 29 ++- .../libjf/unsafe/JfLanguageAdapter.java | 9 +- .../jfronny/libjf/unsafe/MixinPlugin.java | 5 +- .../gitlab/jfronny/libjf/unsafe/SafeLog.java | 34 ---- .../libjf/unsafe/asm/AsmTransformer.java | 14 +- .../modification/MethodModificationPatch.java | 6 +- .../targeting/InterfaceImplTargetPatch.java | 8 +- .../inject/FabricLauncherClassUnlocker.java | 5 +- .../src/main/resources/fabric.mod.json | 3 - .../libjf/unsafe/test/UnsafeEntryTest.java | 4 +- .../src/testmod/resources/fabric.mod.json | 2 +- libjf-web-v0/build.gradle | 3 +- .../src/main/resources/fabric.mod.json | 2 +- settings.gradle | 5 +- 82 files changed, 1494 insertions(+), 747 deletions(-) create mode 100644 libjf-config-reflect-v0/build.gradle create mode 100644 libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java create mode 100644 libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java create mode 100644 libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java create mode 100644 libjf-config-reflect-v0/src/main/resources/fabric.mod.json rename {libjf-config-v0 => libjf-config-reflect-v0}/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java (100%) create mode 100644 libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json rename {libjf-config-v0 => libjf-config-reflect-v0}/src/testmod/resources/fabric.mod.json (86%) delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceCategory.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceRoot.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java delete mode 100644 libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigUnsafe.java delete mode 100644 libjf-config-v0/src/main/resources/assets/libjf-config-v0/lang/en_us.json delete mode 100644 libjf-config-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json rename {libjf-config-v0 => libjf-config-v1}/build.gradle (71%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java (76%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java (87%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java (91%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetListWidget.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java (89%) rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java (100%) create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java (77%) create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java (90%) rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java (100%) create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java (100%) rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java (100%) create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java (100%) create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java (71%) rename {libjf-config-v0 => libjf-config-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java (100%) create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java create mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java create mode 100644 libjf-config-v1/src/main/resources/assets/libjf-config-v0/lang/en_us.json rename {libjf-config-v0 => libjf-config-v1}/src/main/resources/fabric.mod.json (89%) create mode 100644 libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java create mode 100644 libjf-config-v1/src/testmod/resources/fabric.mod.json delete mode 100644 libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/SafeLog.java diff --git a/build.gradle b/build.gradle index 8bedbde..f12e0ef 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,8 @@ apply from: "https://jfmods.gitlab.io/scripts/jfmod.gradle" allprojects { + if (project.name in rootProject.nonModSubprojects) return + loom { runs { testmodClient { @@ -21,5 +23,7 @@ allprojects { dependencies { testmodRuntimeOnly("com.terraformersmc:modmenu:4.0.5") testmodRuntimeOnly("net.fabricmc.fabric-api:fabric-api:$project.fabric_version") + compileOnly("io.gitlab.jfronny:commons:$rootProject.commons_version") + compileOnly("io.gitlab.jfronny:commons-gson:$rootProject.commons_version") } } diff --git a/docs/README.md b/docs/README.md index 1cf74a5..8919fb1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,7 +20,7 @@ repositories { and include LibJF modules like this: ```groovy dependencies { - include modImplementation("io.gitlab.jfronny.libjf:libjf-config-v0:${project.jfapi_version}") + include modImplementation("io.gitlab.jfronny.libjf:libjf-config-v1:${project.jfapi_version}") include("io.gitlab.jfronny.libjf:libjf-unsafe-v0:${project.jfapi_version}") include("io.gitlab.jfronny.libjf:libjf-base:${project.jfapi_version}") modRuntimeOnly("io.gitlab.jfronny.libjf:libjf-devutil-v0:${project.jfapi_version}") diff --git a/docs/libjf-web-v0.md b/docs/libjf-web-v0.md index 891b1a6..9cb2e99 100644 --- a/docs/libjf-web-v0.md +++ b/docs/libjf-web-v0.md @@ -1,7 +1,7 @@ # libjf-web-v0 libjf-web-v0 provides an HTTP web server you can use in your serverside (and technically also clientside) mods to serve web content through a unified port. -libjf-web-v0 depends on libjf-config-v0 to provide its config, libjf-base, fabric-lifecycle-events-v1 and fabric-command-api-v1 +libjf-web-v0 depends on libjf-config-v1 to provide its config, libjf-base, fabric-lifecycle-events-v1 and fabric-command-api-v1 ### Getting started Implement WebInit and register it as a libjf:web entrypoint. To enable the server, also add the following to your fabric.mod.json: diff --git a/gradle.properties b/gradle.properties index e97dc00..eb7f071 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,15 +2,17 @@ minecraft_version=1.19.2 yarn_mappings=build.8 loader_version=0.14.9 -fabric_version=0.60.0+1.19.2 maven_group=io.gitlab.jfronny.libjf archive_base_name=libjf -dev_only_module=libjf-devutil-v0 -modrinth_id=WKwQAwke -modrinth_optional_dependencies=P7dR8mSH +dev_only_module=libjf-devutil-v0 +non_mod_project=libjf-config-plugin + +modrinth_id=libjf +modrinth_optional_dependencies=fabric-api curseforge_id=482600 curseforge_optional_dependencies=fabric-api +fabric_version=0.60.0+1.19.2 commons_version=2022.7.4+11-13-3 diff --git a/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java b/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java index 1674464..7e5a40a 100644 --- a/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java +++ b/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java @@ -3,13 +3,13 @@ package io.gitlab.jfronny.libjf; import io.gitlab.jfronny.commons.log.Logger; import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; import io.gitlab.jfronny.gson.GsonBuilder; +import io.gitlab.jfronny.libjf.coprocess.CoProcessManager; import io.gitlab.jfronny.libjf.gson.GsonAdapter;; import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy; +import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; -public class LibJf { - private LibJf() { - } +public class LibJf implements ModInitializer { public static final String MOD_ID = "libjf"; public static final Logger LOGGER = Logger.forName(MOD_ID); @@ -23,4 +23,9 @@ public class LibJf { HiddenAnnotationExclusionStrategy.register(); GsonHolder.register(); } + + @Override + public void onInitialize() { + Logger.resetFactory(); + } } diff --git a/libjf-base/src/main/resources/fabric.mod.json b/libjf-base/src/main/resources/fabric.mod.json index 8f6f37a..174cb89 100644 --- a/libjf-base/src/main/resources/fabric.mod.json +++ b/libjf-base/src/main/resources/fabric.mod.json @@ -18,7 +18,10 @@ "fabric-lifecycle-events-v1": "*" }, "entrypoints": { - "main": ["io.gitlab.jfronny.libjf.coprocess.CoProcessManager"] + "main": [ + "io.gitlab.jfronny.libjf.LibJf", + "io.gitlab.jfronny.libjf.coprocess.CoProcessManager" + ] }, "custom": { "modmenu": { diff --git a/libjf-config-reflect-v0/build.gradle b/libjf-config-reflect-v0/build.gradle new file mode 100644 index 0000000..cd2c055 --- /dev/null +++ b/libjf-config-reflect-v0/build.gradle @@ -0,0 +1,7 @@ +archivesBaseName = "libjf-config-reflect-v0" + +dependencies { + api project(":libjf-base") + api project(":libjf-unsafe-v0") + api project(":libjf-config-v1") +} diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java new file mode 100644 index 0000000..46a592b --- /dev/null +++ b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java @@ -0,0 +1,190 @@ +package io.gitlab.jfronny.libjf.config.impl.reflect; + +import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; +import io.gitlab.jfronny.gson.*; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.dsl.*; +import io.gitlab.jfronny.libjf.config.api.type.Type; +import io.gitlab.jfronny.libjf.config.impl.*; +import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo; +import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; +import io.gitlab.jfronny.libjf.gson.FabricLoaderGsonGenerator; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.metadata.CustomValue; + +import java.io.*; +import java.lang.reflect.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import static io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl.MODULE_ID; + +public class ConfigProvider { + public static final String CONFIG_PRESET_DEFAULT = "libjf-config-v1.default"; + public static void register(String id, Class klazz) { + Optional container = FabricLoader.getInstance().getModContainer(id); + List previousNames = new LinkedList<>(); + var metaRef = new Object() { + AuxiliaryMetadata meta = new AuxiliaryMetadata(); + }; + if (container.isPresent()) { + CustomValue cv = container.get().getMetadata().getCustomValue(MODULE_ID); + if (cv == null) { + cv = container.get().getMetadata().getCustomValue("libjf"); + if (cv != null) { + cv = cv.getAsObject().get("config"); + } + } + if (cv != null) metaRef.meta = GsonHolder.getGson().fromJson(FabricLoaderGsonGenerator.toGson(cv), AuxiliaryMetadata.class); + previousNames.addAll(container.get().getMetadata().getProvides()); + } + else { + LibJf.LOGGER.warn("Attempted to register config for a mod that is not installed: " + id); + } + previousNames.add(id); + + Path cfg = FabricLoader.getInstance().getConfigDir(); + Path path = cfg.resolve(id + ".json5"); + if (!Files.exists(path)) { + try { + for (String s : previousNames) { + Path previousPath = cfg.resolve(s + ".json"); + if (Files.exists(previousPath)) { + Files.move(previousPath, path); + break; + } + previousPath = cfg.resolve(s + ".json5"); + if (Files.exists(previousPath)) { + Files.move(previousPath, path); + break; + } + } + } catch (IOException e) { + LibJf.LOGGER.error("Could not upgrade config directory", e); + } + } + + DSL.create(id).register(builder -> (ConfigBuilder) applyCategory(builder + .setLoadMethod(c -> { + if (Files.exists(path)) { + try (BufferedReader br = Files.newBufferedReader(path)) { + JsonElement element = JsonParser.parseReader(br); + if (element.isJsonObject()) loadFrom(element.getAsJsonObject(), c); + else LibJf.LOGGER.error("Invalid config: Not a JSON object for " + id); + } catch (Exception e) { + LibJf.LOGGER.error("Could not read config for " + id, e); + } + } + c.write(); + }) + .setWriteMethod(c -> JfConfigWatchService.lock(path, () -> { + try (BufferedWriter bw = Files.newBufferedWriter(path); + JsonWriter jw = GsonHolder.getGson().newJsonWriter(bw)) { + writeTo(jw, c); + } catch (Exception e) { + LibJf.LOGGER.error("Could not write config", e); + } + })) + .setPath(path), + klazz, id, metaRef.meta)); + } + + private static CategoryBuilder applyCategory(CategoryBuilder builder, Class configClass, String modId, AuxiliaryMetadata meta) { + if (meta.referencedConfigs != null) meta.referencedConfigs.forEach(builder::referenceConfig); + for (Field field : configClass.getFields()) { + if (field.isAnnotationPresent(Entry.class)) { + Entry entry = field.getAnnotation(Entry.class); + Object defaultValue = null; + try { + defaultValue = field.get(null); + } catch (IllegalAccessException ignored) { + } + //noinspection unchecked,rawtypes + builder.value(new DslEntryInfo( + field.getName(), + defaultValue, + () -> field.get(null), + v -> field.set(null, v), + (Type) Type.ofClass(field.getGenericType()), + entry == null ? 100 : entry.width(), + entry == null ? Double.NEGATIVE_INFINITY : entry.min(), + entry == null ? Double.POSITIVE_INFINITY : entry.max() + )); + } + } + builder.addPreset(CONFIG_PRESET_DEFAULT, ConfigCategory::reset); + for (Method method : configClass.getMethods()) { + if (method.isAnnotationPresent(Preset.class)) { + builder.addPreset(builder.getTranslationPrefix() + method.getName(), c -> { + try { + method.invoke(null); + } catch (InvocationTargetException | IllegalAccessException e) { + LibJf.LOGGER.error("Could not apply preset", e); + } + }); + } else if (method.isAnnotationPresent(Verifier.class)) { + builder.addVerifier(c -> { + try { + method.invoke(null); + } catch (InvocationTargetException | IllegalAccessException e) { + LibJf.LOGGER.error("Could not run verifier", e); + } + }); + } + } + + for (Class categoryClass : configClass.getClasses()) { + if (categoryClass.isAnnotationPresent(Category.class)) { + String name = categoryClass.getSimpleName(); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); // camelCase + //TODO allow custom auxiliary metadata + builder.category(name, builder1 -> applyCategory(builder1, categoryClass, modId, new AuxiliaryMetadata())); + } + } + + return builder; + } + + public static void loadFrom(JsonObject source, ConfigCategory category) { + for (EntryInfo entry : category.getEntries()) { + if (source.has(entry.getName())) { + try { + entry.loadFromJson(source.get(entry.getName())); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not set config entry value of " + entry.getName(), e); + } + } else LibJf.LOGGER.error("Config does not contain entry for " + entry.getName()); + } + for (Map.Entry entry : category.getCategories().entrySet()) { + if (source.has(entry.getKey())) { + JsonElement el = source.get(entry.getKey()); + if (el.isJsonObject()) loadFrom(el.getAsJsonObject(), entry.getValue()); + else LibJf.LOGGER.error("Config category is not a JSON object, skipping"); + } else LibJf.LOGGER.error("Config does not contain entry for subcategory " + entry.getKey()); + } + } + + public static void writeTo(JsonWriter writer, ConfigCategory category) throws IOException { + category.fix(); + writer.beginObject(); + String val; + for (EntryInfo entry : category.getEntries()) { + try { + entry.writeTo(writer, category.getTranslationPrefix()); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not write entry", e); + } + } + for (Map.Entry entry : category.getCategories().entrySet()) { + if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(category.getTranslationPrefix() + entry.getKey() + ".title")) != null) + writer.comment(val); + writer.name(entry.getKey()); + writeTo(writer, entry.getValue()); + } + writer.endObject(); + } +} diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java new file mode 100644 index 0000000..5007fb4 --- /dev/null +++ b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java @@ -0,0 +1,26 @@ +package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; + +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.JfConfig; +import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; +import io.gitlab.jfronny.libjf.config.impl.reflect.ConfigProvider; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.entrypoint.EntrypointContainer; +import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; + +public class JfConfigReflectSafe implements PreLaunchEntrypoint { + @Override + public void onPreLaunch() { + for (EntrypointContainer config : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, JfConfig.class)) { + registerIfMissing(config.getProvider().getMetadata().getId(), config.getEntrypoint().getClass()); + } + } + + public static void registerIfMissing(String modId, Class klazz) { + if (!ConfigHolder.getInstance().isRegistered(modId)) { + LibJf.LOGGER.info("Registering config for " + modId); + ConfigProvider.register(modId, klazz); + } + } +} diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java new file mode 100644 index 0000000..b6c2f95 --- /dev/null +++ b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java @@ -0,0 +1,22 @@ +package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; + +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.JfConfig; +import io.gitlab.jfronny.libjf.config.api.JfCustomConfig; +import io.gitlab.jfronny.libjf.config.api.dsl.DSL; +import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; +import io.gitlab.jfronny.libjf.unsafe.DynamicEntry; +import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit; + +public class JfConfigUnsafe implements UltraEarlyInit { + @Override + public void init() { + DynamicEntry.execute(ConfigHolderImpl.MODULE_ID, JfConfig.class, + s -> JfConfigReflectSafe.registerIfMissing(s.modId(), s.instance().getClass()) + ); + DynamicEntry.execute(ConfigHolderImpl.CUSTOM_ID, JfCustomConfig.class, + s -> s.instance().register(DSL.create(s.modId())) + ); + LibJf.LOGGER.info("Finished LibJF config entrypoint"); + } +} diff --git a/libjf-config-reflect-v0/src/main/resources/fabric.mod.json b/libjf-config-reflect-v0/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..965e3df --- /dev/null +++ b/libjf-config-reflect-v0/src/main/resources/fabric.mod.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": 1, + "id": "libjf-config-reflect-v0", + "name": "LibJF Config Reflect", + "version": "${version}", + "authors": ["JFronny"], + "contact": { + "website": "https://jfronny.gitlab.io", + "repo": "https://gitlab.com/jfmods/libjf" + }, + "license": "MIT", + "environment": "*", + "entrypoints": { + "libjf:preEarly": [ + "io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint.JfConfigUnsafe" + ], + "preLaunch": [ + "io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint.JfConfigReflectSafe" + ] + }, + "depends": { + "fabricloader": ">=0.12.0", + "minecraft": "*", + "fabric-resource-loader-v0": "*", + "fabric-command-api-v2": "*", + "libjf-base": ">=${version}", + "libjf-unsafe-v0": ">=${version}", + "libjf-config-v1": ">=${version}" + }, + "custom": { + "modmenu": { + "badges": ["library"], + "parent": "libjf" + } + } +} diff --git a/libjf-config-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java b/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java similarity index 100% rename from libjf-config-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java rename to libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java diff --git a/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json b/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json new file mode 100644 index 0000000..3ac4953 --- /dev/null +++ b/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json @@ -0,0 +1,13 @@ +{ + "libjf-config-v1-testmod.jfconfig.title": "JfConfig example", + "libjf-config-v1-testmod.jfconfig.disablePacks": "Disable resource packs", + "libjf-config-v1-testmod.jfconfig.intTest": "Int Test", + "libjf-config-v1-testmod.jfconfig.decimalTest": "Decimal Test", + "libjf-config-v1-testmod.jfconfig.dieStr": "String Test", + "libjf-config-v1-testmod.jfconfig.gsonOnlyStr.tooltip": "George", + "libjf-config-v1-testmod.jfconfig.enumTest": "Enum Test", + "libjf-config-v1-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip", + "libjf-config-v1-testmod.jfconfig.enum.Test.Test": "Test", + "libjf-config-v1-testmod.jfconfig.enum.Test.ER": "ER", + "libjf-config-v1-testmod.jfconfig.moskau": "Moskau" +} \ No newline at end of file diff --git a/libjf-config-v0/src/testmod/resources/fabric.mod.json b/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json similarity index 86% rename from libjf-config-v0/src/testmod/resources/fabric.mod.json rename to libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json index b925c7c..cc60942 100644 --- a/libjf-config-v0/src/testmod/resources/fabric.mod.json +++ b/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "libjf-config-v0-testmod", + "id": "libjf-config-reflect-v0-testmod", "version": "1.0", "environment": "*", "entrypoints": { diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java deleted file mode 100644 index 7813220..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.gitlab.jfronny.libjf.config.api; - -import java.util.List; -import java.util.Map; - -public interface ConfigInstance { - static ConfigInstance get(Class configClass) { - return ConfigHolder.getInstance().get(configClass); - } - static ConfigInstance get(String modId) { - return ConfigHolder.getInstance().get(modId); - } - void load(); - void write(); - String getId(); - String getCategoryPath(); - default String getTranslationPrefix() { - return getId() + ".jfconfig." + getCategoryPath(); - } - List> getEntries(); - Map getPresets(); - default List getReferencedConfigs() { - return List.of(); - } - Map getCategories(); - default void fix() { - for (EntryInfo entry : getEntries()) { - entry.fix(); - } - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java deleted file mode 100644 index 15bab30..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl; - -import com.google.common.collect.*; -import io.gitlab.jfronny.commons.serialize.gson.api.*; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.gson.*; -import io.gitlab.jfronny.libjf.unsafe.*; -import net.fabricmc.loader.api.*; -import net.fabricmc.loader.api.metadata.*; -import org.jetbrains.annotations.*; - -import java.nio.file.*; -import java.util.*; - -public class ConfigHolderImpl implements ConfigHolder { - @ApiStatus.Internal - public static final ConfigHolderImpl INSTANCE = new ConfigHolderImpl(); - private ConfigHolderImpl() { - } - public static final String MODULE_ID = "libjf:config"; - private final Map configs = new HashMap<>(); - private final Map configsByPath = new HashMap<>(); - - @Override - public void register(String modId, Class config) { - if (isRegistered(modId)) { - if (get(modId) instanceof ConfigInstanceAbstract instance && instance.matchesConfigClass(config)) { - SafeLog.warn("Attempted to set config of " + modId + " twice, skipping"); - return; - } - SafeLog.warn("Overriding config class of " + modId + " to " + config); - } - if (isRegistered(config)) { - SafeLog.warn("Attempted to reuse config class " + config + ", this is unsupported"); - } - Optional container = FabricLoader.getInstance().getModContainer(modId); - List previousNames = new LinkedList<>(); - AuxiliaryMetadata meta = new AuxiliaryMetadata(); - if (container.isPresent()) { - CustomValue cv = container.get().getMetadata().getCustomValue(MODULE_ID); - if (cv == null) { - cv = container.get().getMetadata().getCustomValue("libjf"); - if (cv != null) { - cv = cv.getAsObject().get("config"); - } - } - if (cv != null) meta = GsonHolder.getGson().fromJson(FabricLoaderGsonGenerator.toGson(cv), AuxiliaryMetadata.class); - previousNames.addAll(container.get().getMetadata().getProvides()); - } - else { - SafeLog.warn("Attempted to register config for a mod that is not installed: " + modId); - } - ConfigInstanceRoot instance = new ConfigInstanceRoot(modId, previousNames, config, meta.sanitize()); - configs.put(modId, instance); - configsByPath.put(instance.path, instance); - } - - @Override - public Map getRegistered() { - return ImmutableMap.copyOf(configs); - } - - @Override - public ConfigInstance get(Class configClass) { - for (ConfigInstance value : configs.values()) { - if (value instanceof ConfigInstanceAbstract instance && instance.matchesConfigClass(configClass)) - return value; - } - return null; - } - - @Override - public ConfigInstance get(String configClass) { - return configs.get(configClass); - } - - @Override - public ConfigInstance get(Path configPath) { - return configsByPath.get(configPath); - } - - @Override - public boolean isRegistered(Class config) { - for (ConfigInstance value : configs.values()) { - if (value instanceof ConfigInstanceAbstract instance && instance.matchesConfigClass(config)) - return true; - } - return false; - } - - @Override - public boolean isRegistered(String modId) { - return configs.containsKey(modId); - } - - @Override - public boolean isRegistered(Path configPath) { - return configsByPath.containsKey(configPath); - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java deleted file mode 100644 index 1c375f5..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl; - -import io.gitlab.jfronny.gson.JsonElement; -import io.gitlab.jfronny.gson.JsonObject; -import io.gitlab.jfronny.gson.stream.JsonWriter; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; - -public abstract class ConfigInstanceAbstract implements ConfigInstance { - public static final String CONFIG_PRESET_DEFAULT = "libjf-config-v0.default"; - public final String modId; - private final String categoryPath; - public final Class configClass; - public final List referencedConfigs; - public final List> entries = new ArrayList<>(); - public final Map presets = new LinkedHashMap<>(); - public final Set verifiers = new LinkedHashSet<>(); - public final Map subcategories = new LinkedHashMap<>(); - public ConfigInstanceAbstract(String modId, String categoryPath, Class configClass, AuxiliaryMetadata meta) { - this.modId = modId; - this.categoryPath = categoryPath; - this.configClass = configClass; - this.referencedConfigs = List.copyOf(meta.referencedConfigs); - for (Field field : configClass.getFields()) { - if (field.isAnnotationPresent(Entry.class)) - entries.add(new EntryInfoImpl<>(field)); - } - presets.put(CONFIG_PRESET_DEFAULT, () -> { - for (EntryInfo entry : entries) { - try { - reset(entry); - } catch (IllegalAccessException e) { - SafeLog.error("Could not reload default values", e); - } - } - }); - - for (Method method : configClass.getMethods()) { - if (method.isAnnotationPresent(Preset.class)) { - presets.put(modId + ".jfconfig." + method.getName(), () -> { - try { - method.invoke(null); - } catch (IllegalAccessException | InvocationTargetException e) { - SafeLog.error("Could not apply preset", e); - } - }); - } - else if (method.isAnnotationPresent(Verifier.class)) { - verifiers.add(() -> { - try { - method.invoke(null); - } catch (IllegalAccessException | InvocationTargetException e) { - SafeLog.error("Could not run verifier", e); - } - }); - } - } - - for (Class categoryClass : configClass.getClasses()) { - if (categoryClass.isAnnotationPresent(Category.class)) { - String name = camelCase(categoryClass.getSimpleName()); - //TODO allow custom auxiliary metadata - subcategories.put(name, new ConfigInstanceCategory(this, modId, categoryPath + name + ".", categoryClass, new AuxiliaryMetadata().sanitize())); - } - } - } - - // Here due to generics issues - private void reset(EntryInfo info) throws IllegalAccessException { - info.setValue(info.getDefault()); - } - - private String camelCase(String source) { - if (source == null) return null; - if (source.length() == 0) return source; - return Character.toLowerCase(source.charAt(0)) + source.substring(1); - } - - public void loadFrom(JsonObject source) { - for (EntryInfo entry : entries) { - if (source.has(entry.getName())) { - try { - entry.loadFromJson(source.get(entry.getName())); - } catch (IllegalAccessException e) { - SafeLog.error("Could not set config entry value of " + entry.getName(), e); - } - } else SafeLog.error("Config does not contain entry for " + entry.getName()); - } - for (Map.Entry entry : subcategories.entrySet()) { - if (entry.getValue() instanceof ConfigInstanceAbstract instance && source.has(entry.getKey())) { - JsonElement el = source.get(entry.getKey()); - if (el.isJsonObject()) instance.loadFrom(el.getAsJsonObject()); - else SafeLog.error("Config category is not a JSON object, skipping"); - } else SafeLog.error("Config does not contain entry for subcategory " + entry.getKey()); - } - } - - public void writeTo(JsonWriter writer) throws IOException { - fix(); - writer.beginObject(); - String val; - for (EntryInfo entry : entries) { - try { - entry.writeTo(writer, modId + ".jfconfig." + categoryPath); - } catch (IllegalAccessException e) { - SafeLog.error("Could not write entry", e); - } - } - for (Map.Entry entry : subcategories.entrySet()) { - if (!(entry.getValue() instanceof ConfigInstanceAbstract instance)) continue; - if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(modId + ".jfconfig." + categoryPath + entry.getKey() + ".title")) != null) - writer.comment(val); - writer.name(entry.getKey()); - instance.writeTo(writer); - } - writer.endObject(); - } - - @Override - public void fix() { - for (Runnable verifier : verifiers) verifier.run(); - for (EntryInfo entry : entries) entry.fix(); - } - - public boolean matchesConfigClass(Class candidate) { - return candidate != null && candidate.isAssignableFrom(configClass); - } - - @Override - public String getId() { - return modId; - } - - @Override - public String getCategoryPath() { - return categoryPath; - } - - @Override - public List> getEntries() { - return entries; - } - - @Override - public Map getPresets() { - return presets; - } - - @Override - public List getReferencedConfigs() { - List result = new LinkedList<>(); - for (String referencedConfig : referencedConfigs) { - ConfigInstance ci = ConfigHolder.getInstance().get(referencedConfig); - if (ci != null) result.add(ci); - } - return result; - } - - @Override - public Map getCategories() { - return subcategories; - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceCategory.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceCategory.java deleted file mode 100644 index b844946..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceCategory.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl; - -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; - -public class ConfigInstanceCategory extends ConfigInstanceAbstract { - private final ConfigInstance parent; - - public ConfigInstanceCategory(ConfigInstance parent, String modId, String categoryPath, Class configClass, AuxiliaryMetadata meta) { - super(modId, categoryPath, configClass, meta); - this.parent = parent; - } - - @Override - public void load() { - parent.load(); - } - - @Override - public void write() { - parent.write(); - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceRoot.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceRoot.java deleted file mode 100644 index 047f3bb..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceRoot.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl; - -import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; -import io.gitlab.jfronny.gson.JsonElement; -import io.gitlab.jfronny.gson.JsonParser; -import io.gitlab.jfronny.gson.stream.JsonWriter; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; -import net.fabricmc.loader.api.FabricLoader; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; - -/** Based on https://github.com/TeamMidnightDust/MidnightLib which is based on https://github.com/Minenash/TinyConfig - * Credits to TeamMidnightDust and Minenash */ -public class ConfigInstanceRoot extends ConfigInstanceAbstract { - public final Path path; - - public ConfigInstanceRoot(String modId, Collection previousNames, Class config, AuxiliaryMetadata meta) { - super(modId, "", config, meta); - Path cfg = FabricLoader.getInstance().getConfigDir(); - path = cfg.resolve(modId + ".json5"); - - previousNames = new ArrayList<>(previousNames); - previousNames.add(modId); - - if (!Files.exists(path)) { - try { - for (String s : previousNames) { - Path previousPath = cfg.resolve(s + ".json"); - if (Files.exists(previousPath)) { - Files.move(previousPath, path); - break; - } - previousPath = cfg.resolve(s + ".json5"); - if (Files.exists(previousPath)) { - Files.move(previousPath, path); - break; - } - } - } catch (IOException e) { - SafeLog.error("Could not upgrade config directory", e); - } - } - - load(); - } - - @Override - public void load() { - if (Files.exists(path)) { - try (BufferedReader br = Files.newBufferedReader(path)) { - JsonElement element = JsonParser.parseReader(br); - if (element.isJsonObject()) loadFrom(element.getAsJsonObject()); - else SafeLog.error("Invalid config: Not a JSON object for " + modId); - } - catch (Exception e) { - SafeLog.error("Could not read config for " + modId, e); - } - } - write(); - } - - @Override - public void write() { - JfConfigWatchService.lock(path, () -> { - try (BufferedWriter bw = Files.newBufferedWriter(path); - JsonWriter jw = GsonHolder.getGson().newJsonWriter(bw)) { - writeTo(jw); - } catch (Exception e) { - SafeLog.error("Could not write config", e); - } - }); - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java deleted file mode 100644 index 587c3ae..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/EntryInfoImpl.java +++ /dev/null @@ -1,113 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl; - -import io.gitlab.jfronny.commons.serialize.gson.api.*; -import io.gitlab.jfronny.gson.*; -import io.gitlab.jfronny.gson.stream.*; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.impl.entrypoint.*; -import io.gitlab.jfronny.libjf.unsafe.*; - -import java.io.*; -import java.lang.reflect.*; -import java.util.*; -import java.util.stream.*; - -public class EntryInfoImpl implements EntryInfo { - public Field field; - public T defaultValue; - public Entry entry; - - public EntryInfoImpl(Field field) { - this.field = field; - this.entry = field.getAnnotation(Entry.class); - try { - this.defaultValue = (T) field.get(null); - } catch (IllegalAccessException ignored) {} - } - - @Override - public T getDefault() { - return defaultValue; - } - - @Override - public T getValue() throws IllegalAccessException { - return (T) field.get(null); - } - - @Override - public void setValue(T value) throws IllegalAccessException { - field.set(null, value); - } - - @Override - public Class getValueType() { - return (Class) field.getType(); - } - - @Override - public void fix() { - Object value; - try { - value = field.get(null); - } catch (IllegalAccessException e) { - SafeLog.error("Could not read value", e); - return; - } - final Object valueOriginal = value; - if (value instanceof final Integer v) { - if (v < entry.min()) value = (int)entry.min(); - if (v > entry.max()) value = (int)entry.max(); - } else if (value instanceof final Float v) { - if (v < entry.min()) value = (float)entry.min(); - if (v > entry.max()) value = (float)entry.max(); - } else if (value instanceof final Double v) { - if (v < entry.min()) value = entry.min(); - if (v > entry.max()) value = entry.max(); - } - if (valueOriginal != value) { - try { - field.set(null, value); - } catch (IllegalAccessException e) { - SafeLog.error("Could not write value", e); - } - } - } - - @Override - public String getName() { - return field.getName(); - } - - @Override - public void loadFromJson(JsonElement element) throws IllegalAccessException { - setValue(GsonHolder.getGson().fromJson(element, field.getGenericType())); - } - - @Override - public void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException { - T value = getValue(); - String val; - if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(translationPrefix + getName() + ".tooltip")) != null) - writer.comment(val); - if (field.getType().isEnum()) - writer.comment("Valid: [" + Arrays.stream(field.getType().getEnumConstants()).map(Objects::toString).collect(Collectors.joining(", ")) + "]"); - writer.name(getName()); - GsonHolder.getGson().toJson(value, field.getGenericType(), writer); - } - - @Override - public int getWidth() { - return entry.width(); - } - - @Override - public double getMinValue() { - return entry.min(); - } - - @Override - public double getMaxValue() { - return entry.max(); - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java deleted file mode 100644 index 5b3519b..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl.entrypoint; - -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.impl.*; -import io.gitlab.jfronny.libjf.unsafe.*; -import net.fabricmc.loader.api.*; -import net.fabricmc.loader.api.entrypoint.*; -import net.minecraft.util.*; - -import java.util.function.*; - -public class JfConfigSafe implements PreLaunchEntrypoint { - public static Function TRANSLATION_SUPPLIER = s -> null; - @Override - public void onPreLaunch() { - for (EntrypointContainer config : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, JfConfig.class)) { - registerIfMissing(config.getProvider().getMetadata().getId(), config.getEntrypoint().getClass()); - } - TRANSLATION_SUPPLIER = s -> { - String translated = Language.getInstance().get(s); - return translated.equals(s) ? null : translated; - }; - } - - public static void registerIfMissing(String modId, Class klazz) { - if (!ConfigHolder.getInstance().isRegistered(modId)) { - SafeLog.info("Registering config for " + modId); - ConfigHolder.getInstance().register(modId, klazz); - } - } -} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigUnsafe.java b/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigUnsafe.java deleted file mode 100644 index 78795ca..0000000 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigUnsafe.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl.entrypoint; - -import io.gitlab.jfronny.libjf.config.api.JfConfig; -import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; -import io.gitlab.jfronny.libjf.unsafe.DynamicEntry; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; -import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit; - -public class JfConfigUnsafe implements UltraEarlyInit { - @Override - public void init() { - DynamicEntry.execute(ConfigHolderImpl.MODULE_ID, JfConfig.class, - s -> JfConfigSafe.registerIfMissing(s.modId(), s.instance().getClass()) - ); - SafeLog.info("Finished LibJF config entrypoint"); - } -} diff --git a/libjf-config-v0/src/main/resources/assets/libjf-config-v0/lang/en_us.json b/libjf-config-v0/src/main/resources/assets/libjf-config-v0/lang/en_us.json deleted file mode 100644 index 7330880..0000000 --- a/libjf-config-v0/src/main/resources/assets/libjf-config-v0/lang/en_us.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "libjf-config-v0.presets": "Presets", - "libjf-config-v0.default": "Default", - "libjf-config-v0.see-also": "See also: %s", - "libjf-config-v0.reset": "Reset" -} \ No newline at end of file diff --git a/libjf-config-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json b/libjf-config-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json deleted file mode 100644 index 36cb2cd..0000000 --- a/libjf-config-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "libjf-config-v0-testmod.jfconfig.title": "JfConfig example", - "libjf-config-v0-testmod.jfconfig.disablePacks": "Disable resource packs", - "libjf-config-v0-testmod.jfconfig.intTest": "Int Test", - "libjf-config-v0-testmod.jfconfig.decimalTest": "Decimal Test", - "libjf-config-v0-testmod.jfconfig.dieStr": "String Test", - "libjf-config-v0-testmod.jfconfig.gsonOnlyStr.tooltip": "George", - "libjf-config-v0-testmod.jfconfig.enumTest": "Enum Test", - "libjf-config-v0-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip", - "libjf-config-v0-testmod.jfconfig.enum.Test.Test": "Test", - "libjf-config-v0-testmod.jfconfig.enum.Test.ER": "ER", - "libjf-config-v0-testmod.jfconfig.moskau": "Moskau" -} \ No newline at end of file diff --git a/libjf-config-v0/build.gradle b/libjf-config-v1/build.gradle similarity index 71% rename from libjf-config-v0/build.gradle rename to libjf-config-v1/build.gradle index 0397ec9..4d08a55 100644 --- a/libjf-config-v0/build.gradle +++ b/libjf-config-v1/build.gradle @@ -1,7 +1,7 @@ -archivesBaseName = "libjf-config-v0" +archivesBaseName = "libjf-config-v1" dependencies { - moduleDependencies(project, ["libjf-base", "libjf-unsafe-v0"]) + api project(":libjf-base") include fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}") include modImplementation(fabricApi.module("fabric-command-api-v2", "${project.fabric_version}")) modCompileOnly("com.terraformersmc:modmenu:4.0.5") diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java similarity index 100% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java similarity index 100% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java similarity index 100% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java similarity index 76% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java index ccf9e81..d276c71 100644 --- a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java +++ b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java @@ -3,9 +3,8 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui; import io.gitlab.jfronny.commons.throwable.Try; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.EntryInfo; -import io.gitlab.jfronny.libjf.config.api.WidgetFactory; +import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.type.Type; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.widget.ButtonWidget; @@ -24,36 +23,33 @@ public class EntryInfoWidgetBuilder { private static final Pattern INTEGER_ONLY = Pattern.compile("(-?\\d*)"); private static final Pattern DECIMAL_ONLY = Pattern.compile("-?(\\d+\\.?\\d*|\\d*\\.?\\d+|\\.)"); - public static List> buildWidgets(ConfigInstance config) { - WidgetState state; + public static List> buildWidgets(ConfigCategory config) { List> knownStates = new ArrayList<>(); for (EntryInfo info : config.getEntries()) { - if ((state = initEntry(config, info, knownStates)) != null) { - knownStates.add(state); - } + knownStates.add(initEntry(config, info, knownStates)); } return knownStates; } - private static WidgetState initEntry(ConfigInstance config, EntryInfo info, List> knownStates) { - Class type = info.getValueType(); + private static WidgetState initEntry(ConfigCategory config, EntryInfo info, List> knownStates) { + Type type = info.getValueType(); WidgetState state = new WidgetState<>(); WidgetFactory factory; - if (type == int.class || type == Integer.class) factory = textField(info, state, INTEGER_ONLY, Integer::parseInt, true, info.getMinValue(), info.getMaxValue()); - else if (type == float.class || type == Float.class) factory = textField(info, state, DECIMAL_ONLY, Float::parseFloat, false, info.getMinValue(), info.getMaxValue()); - else if (type == double.class || type == Double.class) factory = textField(info, state, DECIMAL_ONLY, Double::parseDouble, false, info.getMinValue(), info.getMaxValue()); - else if (type == String.class) factory = textField(info, state, null, String::length, true, Math.min(info.getMinValue(),0), Math.max(info.getMaxValue(),1)); - else if (type == boolean.class || type == Boolean.class) { + if (type.isInt()) factory = textField(info, state, INTEGER_ONLY, Integer::parseInt, true, info.getMinValue(), info.getMaxValue()); + else if (type.isFloat()) factory = textField(info, state, DECIMAL_ONLY, Float::parseFloat, false, info.getMinValue(), info.getMaxValue()); + else if (type.isDouble()) factory = textField(info, state, DECIMAL_ONLY, Double::parseDouble, false, info.getMinValue(), info.getMaxValue()); + else if (type.isString()) factory = textField(info, state, null, String::length, true, Math.min(info.getMinValue(),0), Math.max(info.getMaxValue(),1)); + else if (type.isBool()) { factory = toggle(info, state, value -> !(Boolean) value, value -> Text.literal((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED)); } else if (type.isEnum()) { - List values = Arrays.asList(info.getValueType().getEnumConstants()); + T[] values = ((Type.TEnum)type).options(); factory = toggle(info, state, value -> { - int index = values.indexOf(value) + 1; - return values.get(index >= values.size() ? 0 : index); - }, value -> Text.translatable(config.getTranslationPrefix() + "enum." + type.getSimpleName() + "." + state.cachedValue)); + int index = indexOf(values, value) + 1; + return values[index >= values.length ? 0 : index]; + }, value -> Text.translatable(config.getTranslationPrefix() + "enum." + type.getName() + "." + state.cachedValue)); } else { LibJf.LOGGER.error("Invalid entry type in " + info.getName() + ": " + type.getName()); factory = ((screenWidth, textRenderer, done) -> new WidgetFactory.Widget(() -> {}, new ButtonWidget(-10, 0, 0, 0, Text.of(""), null))); @@ -63,6 +59,13 @@ public class EntryInfoWidgetBuilder { return state; } + private static int indexOf(Object[] array, Object value) { + for (int i = 0; i < array.length; i++) { + if (array[i] == value) return i; + } + return -1; + } + private static WidgetFactory toggle(EntryInfo info, WidgetState state, Function increment, Function valueTextifier) { return (screenWidth, textRenderer, done) -> { ButtonWidget button = new ButtonWidget(screenWidth - 110, 0, info.getWidth(), 20, valueTextifier.apply(state.cachedValue), btn -> { diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java similarity index 100% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java similarity index 87% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java index e8b75e5..9e9f875 100644 --- a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java +++ b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java @@ -1,10 +1,9 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui; import io.gitlab.jfronny.commons.throwable.Try; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.WidgetFactory; +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.*; import io.gitlab.jfronny.libjf.config.impl.client.gui.presets.PresetsScreen; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; @@ -19,7 +18,7 @@ import java.util.*; @Environment(EnvType.CLIENT) public class TinyConfigScreen extends Screen { - public TinyConfigScreen(ConfigInstance config, Screen parent) { + public TinyConfigScreen(ConfigCategory config, Screen parent) { super(Text.translatable(config.getTranslationPrefix() + "title")); this.parent = parent; this.config = config; @@ -28,7 +27,7 @@ public class TinyConfigScreen extends Screen { } private final String translationPrefix; private final Screen parent; - private final ConfigInstance config; + private final ConfigCategory config; private final List> widgets; private MidnightConfigListWidget list; @@ -38,26 +37,25 @@ public class TinyConfigScreen extends Screen { config.fix(); - this.addDrawableChild(new ButtonWidget(4, 6, 80, 20, Text.translatable("libjf-config-v0.presets"), button -> { + this.addDrawableChild(new ButtonWidget(4, 6, 80, 20, Text.translatable("libjf-config-v1.presets"), button -> { MinecraftClient.getInstance().setScreen(new PresetsScreen(this, config)); })); this.addDrawableChild(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> { - config.load(); Objects.requireNonNull(client).setScreen(parent); })); ButtonWidget done = this.addDrawableChild(new ButtonWidget(this.width / 2 + 4, this.height - 28, 150, 20, ScreenTexts.DONE, (button) -> { for (WidgetState state : widgets) { - Try.orElse(state::writeToEntry, e -> SafeLog.error("Could not write config data to class", e)); + Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e)); } - config.write(); + config.getRoot().write(); Objects.requireNonNull(client).setScreen(parent); })); this.list = new MidnightConfigListWidget(this.client, this.width, this.height, 32, this.height - 32, 25); this.addSelectableChild(this.list); - for (Map.Entry entry : config.getCategories().entrySet()) { + for (Map.Entry entry : config.getCategories().entrySet()) { this.list.addReference(width / 2, Text.translatable(entry.getValue().getTranslationPrefix() + "title"), () -> new TinyConfigScreen(entry.getValue(), this)); @@ -65,7 +63,7 @@ public class TinyConfigScreen extends Screen { for (WidgetState info : widgets) { MutableText name = Text.translatable(translationPrefix + info.entry.getName()); WidgetFactory.Widget control = info.factory.build(width, textRenderer, done); - ButtonWidget resetButton = new ButtonWidget(width - 155, 0, 40, 20, Text.translatable("libjf-config-v0.reset"), (button -> { + ButtonWidget resetButton = new ButtonWidget(width - 155, 0, 40, 20, Text.translatable("libjf-config-v1.reset"), (button -> { info.reset(); control.updateControls().run(); })); @@ -78,7 +76,7 @@ public class TinyConfigScreen extends Screen { for (ConfigInstance ci : config.getReferencedConfigs()) { if (ci != null) { this.list.addReference(width / 2, - Text.translatable("libjf-config-v0.see-also", Text.translatable(ci.getTranslationPrefix() + "title")), + Text.translatable("libjf-config-v1.see-also", Text.translatable(ci.getTranslationPrefix() + "title")), () -> new TinyConfigScreen(ci, this)); } } diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java similarity index 91% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java index 77fa5b5..bde2843 100644 --- a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java +++ b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java @@ -1,9 +1,9 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui; import io.gitlab.jfronny.commons.tuple.Tuple; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.EntryInfo; import io.gitlab.jfronny.libjf.config.api.WidgetFactory; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.text.Text; @@ -26,7 +26,7 @@ public class WidgetState { try { updateCache(entry.getValue()); } catch (IllegalAccessException e) { - SafeLog.error("Could not create initial widget state cache", e); + LibJf.LOGGER.error("Could not create initial widget state cache", e); } } diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetListWidget.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetListWidget.java similarity index 100% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetListWidget.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetListWidget.java diff --git a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java similarity index 89% rename from libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java rename to libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java index 908f5fd..95c1f86 100644 --- a/libjf-config-v0/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java +++ b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/presets/PresetsScreen.java @@ -1,6 +1,7 @@ package io.gitlab.jfronny.libjf.config.impl.client.gui.presets; import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.ConfigCategory; import io.gitlab.jfronny.libjf.config.api.ConfigInstance; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -15,11 +16,11 @@ import java.util.Map; @Environment(EnvType.CLIENT) public class PresetsScreen extends Screen { private final Screen parent; - private final ConfigInstance config; + private final ConfigCategory config; private PresetListWidget list; - public PresetsScreen(Screen parent, ConfigInstance config) { - super(Text.translatable("libjf-config-v0.presets")); + public PresetsScreen(Screen parent, ConfigCategory config) { + super(Text.translatable("libjf-config-v1.presets")); this.parent = parent; this.config = config; } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java new file mode 100644 index 0000000..13bec47 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java @@ -0,0 +1,33 @@ +package io.gitlab.jfronny.libjf.config.api; + +import io.gitlab.jfronny.libjf.LibJf; + +import java.util.List; +import java.util.Map; + +public interface ConfigCategory { + String getId(); + String getCategoryPath(); + default String getTranslationPrefix() { + return getId() + ".jfconfig." + getCategoryPath(); + } + List> getEntries(); + Map getPresets(); + List getReferencedConfigs(); + Map getCategories(); + ConfigInstance getRoot(); + default void fix() { + for (EntryInfo entry : getEntries()) { + entry.fix(); + } + } + default void reset() { + for (EntryInfo entry : getEntries()) { + try { + entry.reset(); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not reload default values", e); + } + } + } +} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java similarity index 77% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java index f0e763c..ca457f4 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java @@ -9,12 +9,10 @@ public interface ConfigHolder { static ConfigHolder getInstance() { return ConfigHolderImpl.INSTANCE; } - void register(String modId, Class config); + void register(String modId, ConfigInstance config); Map getRegistered(); - ConfigInstance get(Class configClass); ConfigInstance get(String modId); ConfigInstance get(Path configPath); - boolean isRegistered(Class configClass); boolean isRegistered(String modId); boolean isRegistered(Path configPath); } diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java new file mode 100644 index 0000000..875808d --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java @@ -0,0 +1,13 @@ +package io.gitlab.jfronny.libjf.config.api; + +import java.nio.file.Path; +import java.util.*; + +public interface ConfigInstance extends ConfigCategory { + static ConfigInstance get(String modId) { + return ConfigHolder.getInstance().get(modId); + } + void load(); + void write(); + Optional getFilePath(); +} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java similarity index 90% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java index 853cae3..6f3f117 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java @@ -2,10 +2,17 @@ package io.gitlab.jfronny.libjf.config.api; import io.gitlab.jfronny.gson.JsonElement; import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.config.api.type.Type; import java.io.IOException; public interface EntryInfo { + /** + * Get the name of this entry + * @return This entry's name + */ + String getName(); + /** * @return Get the default value of this entry */ @@ -27,19 +34,13 @@ public interface EntryInfo { * Get the value type of this entry. Will use the class definition, not the current value * @return The type of this entry */ - Class getValueType(); + Type getValueType(); /** * Ensure the current value is within expected bounds. */ void fix(); - /** - * Get the name of this entry - * @return This entry's name - */ - String getName(); - /** * Set this entry's value to that of the element * @param element The element to read from @@ -66,4 +67,8 @@ public interface EntryInfo { * @return Get the maximum value for this entry */ double getMaxValue(); + + default void reset() throws IllegalAccessException { + setValue(getDefault()); + } } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java new file mode 100644 index 0000000..a909fad --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java @@ -0,0 +1,7 @@ +package io.gitlab.jfronny.libjf.config.api; + +import io.gitlab.jfronny.libjf.config.api.dsl.DSL; + +public interface JfCustomConfig { + void register(DSL.Defaulted dsl); +} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java new file mode 100644 index 0000000..e9d3991 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java @@ -0,0 +1,33 @@ +package io.gitlab.jfronny.libjf.config.api.dsl; + +import io.gitlab.jfronny.libjf.config.api.*; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public interface CategoryBuilder> { + Builder setTranslationPrefix(String translationPrefix); + String getTranslationPrefix(); + Builder addPreset(String id, Consumer action); + Builder addVerifier(Consumer verifier); + Builder referenceConfig(String id); + Builder referenceConfig(ConfigInstance config); + Builder category(String id, CategoryBuilderFunction builder); + Builder value(String id, int def, double min, double max, Supplier get, Consumer set); + Builder value(String id, float def, double min, double max, Supplier get, Consumer set); + Builder value(String id, double def, double min, double max, Supplier get, Consumer set); + Builder value(String id, String def, Supplier get, Consumer set); + Builder value(String id, boolean def, Supplier get, Consumer set); + Builder value(String id, String def, String[] options, Supplier get, Consumer set); + > Builder value(String id, T def, Class klazz, Supplier get, Consumer set); + Builder value(EntryInfo entry); + + String getId(); + + ConfigCategory build(Supplier root); + + @FunctionalInterface + interface CategoryBuilderFunction { + CategoryBuilder apply(CategoryBuilder builder); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java new file mode 100644 index 0000000..4adddaf --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java @@ -0,0 +1,19 @@ +package io.gitlab.jfronny.libjf.config.api.dsl; + +import io.gitlab.jfronny.libjf.config.api.ConfigInstance; + +import java.nio.file.Path; +import java.util.function.Consumer; + +public interface ConfigBuilder> extends CategoryBuilder { + Builder setLoadMethod(Consumer load); + Builder setWriteMethod(Consumer write); + Builder setPath(Path path); + + ConfigInstance build(); + + @FunctionalInterface + interface ConfigBuilderFunction { + ConfigBuilder apply(ConfigBuilder builder); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java new file mode 100644 index 0000000..76faae0 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java @@ -0,0 +1,25 @@ +package io.gitlab.jfronny.libjf.config.api.dsl; + +import io.gitlab.jfronny.libjf.config.api.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.impl.dsl.DSLImpl; + +public interface DSL { + static DSL create() { + return new DSLImpl(); + } + + static DSL.Defaulted create(String defaultId) { + return new DSLImpl.Defaulted(defaultId); + } + + ConfigInstance config(String configId, ConfigBuilder.ConfigBuilderFunction builder); + ConfigInstance register(String configId, ConfigBuilder.ConfigBuilderFunction builder); + ConfigInstance register(ConfigHolder config, String configId, ConfigBuilder.ConfigBuilderFunction builder); + + interface Defaulted extends DSL { + ConfigInstance config(ConfigBuilder.ConfigBuilderFunction builder); + ConfigInstance register(ConfigBuilder.ConfigBuilderFunction builder); + ConfigInstance register(ConfigHolder config, ConfigBuilder.ConfigBuilderFunction builder); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java new file mode 100644 index 0000000..fa8c4be --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java @@ -0,0 +1,177 @@ +package io.gitlab.jfronny.libjf.config.api.type; + +import org.jetbrains.annotations.Nullable; + +public sealed interface Type { + static Type ofClass(java.lang.reflect.Type klazz) { + if (klazz == int.class || klazz == Integer.class) return TInt.INSTANCE; + else if (klazz == float.class || klazz == Float.class) return TFloat.INSTANCE; + else if (klazz == double.class || klazz == Double.class) return TDouble.INSTANCE; + else if (klazz == String.class) return TString.INSTANCE; + else if (klazz == boolean.class || klazz == Boolean.class) return TBool.INSTANCE; + else if (klazz instanceof Class k && k.isEnum()) return new TEnum<>(k); + else return new TUnknown<>(klazz); + } + + default boolean isInt() { + return false; + } + default boolean isFloat() { + return false; + } + default boolean isDouble() { + return false; + } + default boolean isString() { + return false; + } + default boolean isBool() { + return false; + } + default boolean isEnum() { + return false; + } + + @Nullable java.lang.reflect.Type asClass(); + + String getName(); + + final class TInt implements Type { + public static TInt INSTANCE = new TInt(); + private TInt() {} + @Override + public boolean isInt() { + return true; + } + + @Override + public Class asClass() { + return Integer.class; + } + + @Override + public String getName() { + return "Integer"; + } + } + + final class TFloat implements Type { + public static TFloat INSTANCE = new TFloat(); + private TFloat() {} + @Override + public boolean isFloat() { + return true; + } + + @Override + public Class asClass() { + return Float.class; + } + + @Override + public String getName() { + return "Float"; + } + } + + final class TDouble implements Type { + public static TDouble INSTANCE = new TDouble(); + private TDouble() {} + @Override + public boolean isDouble() { + return true; + } + + @Override + public Class asClass() { + return Double.class; + } + + @Override + public String getName() { + return "Double"; + } + } + + final class TString implements Type { + public static TString INSTANCE = new TString(); + private TString() {} + @Override + public boolean isString() { + return true; + } + + @Override + public Class asClass() { + return String.class; + } + + @Override + public String getName() { + return "String"; + } + } + + final class TBool implements Type { + public static TBool INSTANCE = new TBool(); + private TBool() {} + @Override + public boolean isBool() { + return true; + } + + @Override + public Class asClass() { + return Boolean.class; + } + + @Override + public String getName() { + return "Boolean"; + } + } + + final record TEnum(@Nullable Class klazz, String name, T[] options) implements Type { + public TEnum(Class klazz) { + this(klazz, klazz.getSimpleName(), klazz.getEnumConstants()); + } + + public static TEnum create(String name, String[] options) { + return new TEnum<>(null, name, options); + } + + @Override + public boolean isEnum() { + return true; + } + + @Override + public Class asClass() { + return klazz; + } + + @Override + public String getName() { + return name; + } + + public T optionForString(String string) { + for (T option : options) { + if (option.toString().equals(string)) return option; + } + return null; + } + } + + final record TUnknown(java.lang.reflect.Type klazz) implements Type { + @Override + public @Nullable java.lang.reflect.Type asClass() { + return klazz; + } + + @Override + public String getName() { + return klazz instanceof Class k ? k.getSimpleName() : klazz.getTypeName(); + } + } +} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java new file mode 100644 index 0000000..a2039fe --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java @@ -0,0 +1,56 @@ +package io.gitlab.jfronny.libjf.config.impl; + +import com.google.common.collect.ImmutableMap; +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import org.jetbrains.annotations.ApiStatus; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +public class ConfigHolderImpl implements ConfigHolder { + @ApiStatus.Internal + public static final ConfigHolderImpl INSTANCE = new ConfigHolderImpl(); + private ConfigHolderImpl() { + } + public static final String MODULE_ID = "libjf:config"; + public static final String CUSTOM_ID = MODULE_ID + "_custom"; + private final Map configs = new HashMap<>(); + private final Map configsByPath = new HashMap<>(); + + @Override + public void register(String modId, ConfigInstance config) { + if (isRegistered(modId)) { + LibJf.LOGGER.warn("Overriding config class of " + modId + " to " + config); + } + configs.put(modId, config); + config.getFilePath().ifPresent(path -> configsByPath.put(path, config)); + } + + @Override + public Map getRegistered() { + return ImmutableMap.copyOf(configs); + } + + @Override + public ConfigInstance get(String configClass) { + return configs.get(configClass); + } + + @Override + public ConfigInstance get(Path configPath) { + return configsByPath.get(configPath); + } + + @Override + public boolean isRegistered(String modId) { + return configs.containsKey(modId); + } + + @Override + public boolean isRegistered(Path configPath) { + return configsByPath.containsKey(configPath); + } +} diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java similarity index 71% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java index 7fc5083..c22545b 100644 --- a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java @@ -8,9 +8,8 @@ import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import io.gitlab.jfronny.commons.throwable.ThrowingRunnable; import io.gitlab.jfronny.commons.throwable.ThrowingSupplier; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.type.Type; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.server.command.ServerCommandSource; @@ -30,7 +29,7 @@ public class JfConfigCommand implements ModInitializer { LiteralArgumentBuilder c_config = literal("config") .requires((serverCommandSource) -> serverCommandSource.hasPermissionLevel(4)) .executes(context -> { - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Loaded configs for:"), false); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Loaded configs for:"), false); ConfigHolder.getInstance().getRegistered().forEach((s, config) -> { context.getSource().sendFeedback(Text.literal("- " + s), false); }); @@ -38,15 +37,20 @@ public class JfConfigCommand implements ModInitializer { }); LiteralArgumentBuilder c_reload = literal("reload").executes(context -> { ConfigHolder.getInstance().getRegistered().forEach((mod, config) -> config.load()); - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Reloaded configs"), true); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Reloaded configs"), true); return Command.SINGLE_SUCCESS; }); LiteralArgumentBuilder c_reset = literal("reset").executes(context -> { - context.getSource().sendError(Text.literal("[libjf-config-v0] Please specify a config to reset")); + context.getSource().sendError(Text.literal("[libjf-config-v1] Please specify a config to reset")); return Command.SINGLE_SUCCESS; }); ConfigHolder.getInstance().getRegistered().forEach((id, config) -> { - registerEntries(config, id, c_config, c_reload, c_reset, cns -> { + c_reload.then(literal(id).executes(context -> { + config.load(); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Reloaded config for " + id), true); + return Command.SINGLE_SUCCESS; + })); + registerEntries(config, id, c_config, c_reset, cns -> { LiteralArgumentBuilder c_instance = literal(id); cns.accept(c_instance); return c_instance; @@ -56,37 +60,32 @@ public class JfConfigCommand implements ModInitializer { }); } - private void registerEntries(ConfigInstance config, String subpath, LiteralArgumentBuilder c_config, LiteralArgumentBuilder c_reload, LiteralArgumentBuilder c_reset, Function>, LiteralArgumentBuilder> pathGen) { + private void registerEntries(ConfigCategory config, String subpath, LiteralArgumentBuilder c_config, LiteralArgumentBuilder c_reset, Function>, LiteralArgumentBuilder> pathGen) { c_config.then(pathGen.apply(cns -> { cns.executes(context -> { - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] " + subpath + " is a category"), false); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] " + subpath + " is a category"), false); return Command.SINGLE_SUCCESS; }); for (EntryInfo entry : config.getEntries()) { registerEntry(config, subpath, cns, entry); } })); - c_reload.then(pathGen.apply(cns -> cns.executes(context -> { - config.load(); - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Reloaded config for " + subpath), true); - return Command.SINGLE_SUCCESS; - }))); c_reset.then(pathGen.apply(cns -> { cns.executes(context -> { - config.getPresets().get(ConfigInstanceAbstract.CONFIG_PRESET_DEFAULT).run(); - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Reset config for " + subpath), true); + config.reset(); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Reset config for " + subpath), true); return Command.SINGLE_SUCCESS; }); config.getPresets().forEach((id2, preset) -> { cns.then(literal(id2).executes(context -> { preset.run(); - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Loaded preset " + id2 + " for " + subpath), true); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Loaded preset " + id2 + " for " + subpath), true); return Command.SINGLE_SUCCESS; })); }); })); config.getCategories().forEach((id2, cfg) -> { - registerEntries(cfg, cfg.getCategoryPath(), c_config, c_reload, c_reset, cns -> { + registerEntries(cfg, cfg.getCategoryPath(), c_config, c_reset, cns -> { return pathGen.apply(cns1 -> { LiteralArgumentBuilder c_instance2 = literal(id2); cns.accept(c_instance2); @@ -102,25 +101,25 @@ public class JfConfigCommand implements ModInitializer { } else return Text.literal("Could not execute command"); }); - private void registerEntry(ConfigInstance config, String subpath, LiteralArgumentBuilder cns, EntryInfo entry) { + private void registerEntry(ConfigCategory config, String subpath, LiteralArgumentBuilder cns, EntryInfo entry) { LiteralArgumentBuilder c_entry = literal(entry.getName()).executes(context -> { - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] The value of " + subpath + "." + entry.getName() + " is " + tryRun(entry::getValue)), false); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] The value of " + subpath + "." + entry.getName() + " is " + tryRun(entry::getValue)), false); return Command.SINGLE_SUCCESS; }); ArgumentType type = getType(entry); if (type != null) { c_entry.then(argument("value", type).executes(context -> { - T value = context.getArgument("value", entry.getValueType()); + @SuppressWarnings("unchecked") T value = context.getArgument("value", (Class) entry.getValueType().asClass()); tryRun(() -> entry.setValue(value)); - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Set " + subpath + "." + entry.getName() + " to " + value), true); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Set " + subpath + "." + entry.getName() + " to " + value), true); return Command.SINGLE_SUCCESS; })); } else if (entry.getValueType().isEnum()) { - for (T enumConstant : entry.getValueType().getEnumConstants()) { + for (T enumConstant : ((Type.TEnum)entry.getValueType()).options()) { c_entry.then(literal(enumConstant.toString()).executes(context -> { tryRun(() -> entry.setValue(enumConstant)); - context.getSource().sendFeedback(Text.literal("[libjf-config-v0] Set " + subpath + "." + entry.getName() + " to " + enumConstant), true); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Set " + subpath + "." + entry.getName() + " to " + enumConstant), true); return Command.SINGLE_SUCCESS; })); } @@ -129,12 +128,12 @@ public class JfConfigCommand implements ModInitializer { } private ArgumentType getType(EntryInfo info) { - Class type = info.getValueType(); - if (type == int.class || type == Integer.class) return IntegerArgumentType.integer((int) info.getMinValue(), (int) info.getMaxValue()); - else if (type == float.class || type == Float.class) return FloatArgumentType.floatArg((float) info.getMinValue(), (float) info.getMaxValue()); - else if (type == double.class || type == Double.class) return DoubleArgumentType.doubleArg(info.getMinValue(), info.getMaxValue()); - else if (type == String.class) return StringArgumentType.greedyString(); - else if (type == boolean.class || type == Boolean.class) return BoolArgumentType.bool(); + Type type = info.getValueType(); + if (type.isInt()) return IntegerArgumentType.integer((int) info.getMinValue(), (int) info.getMaxValue()); + else if (type.isFloat()) return FloatArgumentType.floatArg((float) info.getMinValue(), (float) info.getMaxValue()); + else if (type.isDouble()) return DoubleArgumentType.doubleArg(info.getMinValue(), info.getMaxValue()); + else if (type.isString()) return StringArgumentType.greedyString(); + else if (type.isBool()) return BoolArgumentType.bool(); else return null; } diff --git a/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java similarity index 100% rename from libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java rename to libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java new file mode 100644 index 0000000..3d5f929 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java @@ -0,0 +1,166 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.config.api.type.Type; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class CategoryBuilderImpl> implements CategoryBuilder { + public final List> categories = new LinkedList<>(); + public final String id; + public final String categoryPath; + public String translationPrefix; + public final List> entries = new LinkedList<>(); + public final Map> presets = new LinkedHashMap<>(); + public final List> referencedConfigs = new LinkedList<>(); + public final List> verifiers = new LinkedList<>(); + private boolean built = false; + + public CategoryBuilderImpl(String id, String categoryPath) { + this.id = id; + this.categoryPath = categoryPath; + this.translationPrefix = id + ".jfconfig." + categoryPath; + } + + @Override + public Builder setTranslationPrefix(String translationPrefix) { + checkBuilt(); + this.translationPrefix = translationPrefix; + return asBuilder(); + } + + @Override + public String getTranslationPrefix() { + checkBuilt(); + return translationPrefix; + } + + @Override + public Builder addPreset(String id, Consumer action) { + checkBuilt(); + presets.put(id, action); + return asBuilder(); + } + + @Override + public Builder addVerifier(Consumer verifier) { + checkBuilt(); + verifiers.add(verifier); + return asBuilder(); + } + + @Override + public Builder referenceConfig(String id) { + checkBuilt(); + referencedConfigs.add(() -> ConfigHolder.getInstance().get(id)); + return asBuilder(); + } + + @Override + public Builder referenceConfig(ConfigInstance config) { + checkBuilt(); + referencedConfigs.add(() -> config); + return asBuilder(); + } + + @Override + public Builder category(String id, CategoryBuilderFunction builder) { + checkBuilt(); + categories.add(builder.apply(new CategoryBuilderImpl(id, categoryPath + id + "."))); + return asBuilder(); + } + + @Override + public Builder value(String id, int def, double min, double max, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TInt.INSTANCE, 100, min, max)); + return asBuilder(); + } + + @Override + public Builder value(String id, float def, double min, double max, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TFloat.INSTANCE, 100, min, max)); + return asBuilder(); + } + + @Override + public Builder value(String id, double def, double min, double max, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TDouble.INSTANCE, 100, min, max)); + return asBuilder(); + } + + @Override + public Builder value(String id, String def, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TString.INSTANCE)); + return asBuilder(); + } + + @Override + public Builder value(String id, boolean def, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TBool.INSTANCE)); + return asBuilder(); + } + + @Override + public Builder value(String id, String def, String[] options, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TEnum.create(id, options))); + return asBuilder(); + } + + @Override + public > Builder value(String id, T def, Class klazz, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, new Type.TEnum<>(klazz))); + return asBuilder(); + } + + @Override + public Builder value(EntryInfo entry) { + checkBuilt(); + entries.add(Objects.requireNonNull(entry)); + return asBuilder(); + } + + @Override + public String getId() { + return id; + } + + protected Builder asBuilder() { + //noinspection unchecked + return (Builder) this; + } + + protected void checkBuilt() { + if (built) throw new IllegalStateException("This builder was already used to build a category!"); + } + + protected void markBuilt() { + checkBuilt(); + built = true; + } + + @Override + public DslConfigCategory build(Supplier root) { + markBuilt(); + return new DslConfigCategory(id, + categoryPath, + translationPrefix, + entries, + presets, + () -> referencedConfigs.stream().map(Supplier::get).toList(), + categories.stream().collect(Collectors.toMap(CategoryBuilder::getId, b -> b.build(root))), + root, + verifiers); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java new file mode 100644 index 0000000..eaae6dc --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java @@ -0,0 +1,58 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.config.api.dsl.ConfigBuilder; + +import java.nio.file.Path; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class ConfigBuilderImpl extends CategoryBuilderImpl implements ConfigBuilder { + public DslConfigInstance built; + public Consumer load = s -> {}; + public Consumer write = s -> {}; + public Path path; + + public ConfigBuilderImpl(String id) { + super(id, ""); + } + + @Override + public ConfigBuilderImpl setLoadMethod(Consumer load) { + checkBuilt(); + this.load = load; + return this; + } + + public ConfigBuilderImpl setWriteMethod(Consumer write) { + checkBuilt(); + this.write = write; + return this; + } + + public ConfigBuilderImpl setPath(Path path) { + checkBuilt(); + this.path = path; + return this; + } + + @Override + public DslConfigInstance build() { + markBuilt(); + built = new DslConfigInstance(id, + translationPrefix, + entries, + presets, + () -> referencedConfigs.stream().map(Supplier::get).toList(), + categories.stream().collect(Collectors.toMap(CategoryBuilder::getId, b -> b.build(() -> built))), + () -> built, + verifiers, + load, + write, + path); + built.load(); + return built; + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java new file mode 100644 index 0000000..9abec75 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java @@ -0,0 +1,48 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.libjf.config.api.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.dsl.ConfigBuilder; +import io.gitlab.jfronny.libjf.config.api.dsl.DSL; + +public class DSLImpl implements DSL { + @Override + public ConfigInstance config(String configId, ConfigBuilder.ConfigBuilderFunction builder) { + return builder.apply(new ConfigBuilderImpl(configId)).build(); + } + + @Override + public ConfigInstance register(String configId, ConfigBuilder.ConfigBuilderFunction builder) { + return register(ConfigHolder.getInstance(), configId, builder); + } + + @Override + public ConfigInstance register(ConfigHolder config, String configId, ConfigBuilder.ConfigBuilderFunction builder) { + ConfigInstance instance = config(configId, builder); + config.register(configId, instance); + return instance; + } + + public static class Defaulted extends DSLImpl implements DSL.Defaulted { + public final String defaultId; + + public Defaulted(String defaultId) { + this.defaultId = defaultId; + } + + @Override + public ConfigInstance config(ConfigBuilder.ConfigBuilderFunction builder) { + return config(defaultId, builder); + } + + @Override + public ConfigInstance register(ConfigBuilder.ConfigBuilderFunction builder) { + return register(defaultId, builder); + } + + @Override + public ConfigInstance register(ConfigHolder config, ConfigBuilder.ConfigBuilderFunction builder) { + return register(config, defaultId, builder); + } + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java new file mode 100644 index 0000000..47848d4 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java @@ -0,0 +1,89 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.libjf.config.api.*; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class DslConfigCategory implements ConfigCategory { + private final String id; + private final String categoryPath; + private final String translationPrefix; + private final List> entries; + private final Map> presets; + private final Supplier> referencedConfigs; + private final Map categories; + private final Supplier root; + private final List> verifiers; + + public DslConfigCategory(String id, + String categoryPath, + String translationPrefix, + List> entries, + Map> presets, + Supplier> referencedConfigs, + Map categories, + Supplier root, + List> verifiers) { + this.id = id; + this.categoryPath = categoryPath; + this.translationPrefix = translationPrefix; + this.entries = entries; + this.presets = presets; + this.referencedConfigs = referencedConfigs; + this.categories = categories; + this.root = root; + this.verifiers = verifiers; + } + + @Override + public String getId() { + return id; + } + + @Override + public String getCategoryPath() { + return categoryPath; + } + + @Override + public String getTranslationPrefix() { + return translationPrefix; + } + + @Override + public List> getEntries() { + return entries; + } + + @Override + public Map getPresets() { + return presets.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + t -> () -> t.getValue().accept(this) + )); + } + + @Override + public List getReferencedConfigs() { + return referencedConfigs.get(); + } + + @Override + public Map getCategories() { + return categories; + } + + @Override + public ConfigInstance getRoot() { + return root.get(); + } + + @Override + public void fix() { + ConfigCategory.super.fix(); + for (Consumer verifier : verifiers) verifier.accept(this); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java new file mode 100644 index 0000000..e93419a --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java @@ -0,0 +1,47 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.libjf.config.api.*; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class DslConfigInstance extends DslConfigCategory implements ConfigInstance { + private final Consumer load; + private final Consumer write; + private final Path path; + + public DslConfigInstance(String id, + String translationPrefix, + List> entries, + Map> presets, + Supplier> referencedConfigs, + Map categories, + Supplier root, + List> verifiers, + Consumer load, + Consumer write, + @Nullable Path path) { + super(id, "", translationPrefix, entries, presets, referencedConfigs, categories, root, verifiers); + this.load = load; + this.write = write; + this.path = path; + } + + @Override + public void load() { + load.accept(this); + } + + @Override + public void write() { + write.accept(this); + } + + @Override + public Optional getFilePath() { + return Optional.ofNullable(path); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java new file mode 100644 index 0000000..1f0d611 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java @@ -0,0 +1,174 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; +import io.gitlab.jfronny.commons.throwable.ThrowingConsumer; +import io.gitlab.jfronny.commons.throwable.ThrowingSupplier; +import io.gitlab.jfronny.gson.JsonElement; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.type.Type; +import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class DslEntryInfo implements EntryInfo { + private final String name; + private final T defaultValue; + private final ThrowingSupplier get; + private final ThrowingConsumer set; + private final Type type; + private final int width; + private final double minValue; + private final double maxValue; + + public DslEntryInfo(String name, + T defaultValue, + ThrowingSupplier get, + ThrowingConsumer set, + Type type, + int width, + double minValue, + double maxValue) { + this.name = name; + this.defaultValue = defaultValue; + this.get = get; + this.set = set; + this.type = type; + this.width = width; + this.minValue = minValue; + this.maxValue = maxValue; + } + + public DslEntryInfo(String name, + T def, + ThrowingSupplier get, + ThrowingConsumer set, + Type type) { + this(name, def, get, set, type, 100, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + } + + @Override + public String getName() { + return name; + } + + @Override + public T getDefault() { + return defaultValue; + } + + @Override + public T getValue() throws IllegalAccessException { + return get.get(); + } + + @Override + public void setValue(T value) throws IllegalAccessException { + set.accept(value); + } + + @Override + public Type getValueType() { + return type; + } + + @Override + public void fix() { + Object value; + try { + value = getValue(); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not read value", e); + return; + } + final Object valueOriginal = value; + if (value instanceof final Integer v) { + if (v < minValue) value = minValue; + if (v > maxValue) value = maxValue; + } else if (value instanceof final Float v) { + if (v < minValue) value = minValue; + if (v > maxValue) value = maxValue; + } else if (value instanceof final Double v) { + if (v < minValue) value = minValue; + if (v > maxValue) value = maxValue; + } + if (valueOriginal != value) { + try { + setUnchecked(value); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not write value", e); + } + } + } + + @Override + public void loadFromJson(JsonElement element) throws IllegalAccessException { + if (type.isBool()) { + if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isBoolean()) { + setUnchecked(element.getAsBoolean()); + } + } else if (type.isString()) { + if (element.isJsonPrimitive()) { + setUnchecked(element.getAsString()); + } + } else if (type.isInt()) { + if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) { + setUnchecked(element.getAsNumber().intValue()); + } + } else if (type.isDouble()) { + if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) { + setUnchecked(element.getAsNumber().doubleValue()); + } + } else if (type.isFloat()) { + if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) { + setUnchecked(element.getAsNumber().floatValue()); + } + } else if (type.isEnum()) { + Type.TEnum e = (Type.TEnum) type; + if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()) { + setUnchecked(e.optionForString(element.getAsString())); + } + } else { + setValue(GsonHolder.getGson().fromJson(element, type.asClass())); + } + } + + @SuppressWarnings("unchecked") + private void setUnchecked(Object object) throws IllegalAccessException { + if (object == null) return; + setValue((T) object); + } + + @Override + public void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException { + T value = getValue(); + String val; + if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(translationPrefix + getName() + ".tooltip")) != null) { + writer.comment(val); + } + if (type.isEnum()) { + writer.comment("Valid: [" + Arrays.stream(((Type.TEnum)type).options()).map(Objects::toString).collect(Collectors.joining(", ")) + "]"); + } + writer.name(name); + GsonHolder.getGson().toJson(value, Objects.requireNonNullElse(type.asClass(), String.class), writer); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public double getMinValue() { + return minValue; + } + + @Override + public double getMaxValue() { + return maxValue; + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java new file mode 100644 index 0000000..335f8f7 --- /dev/null +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java @@ -0,0 +1,25 @@ +package io.gitlab.jfronny.libjf.config.impl.entrypoint; + +import io.gitlab.jfronny.libjf.config.api.JfCustomConfig; +import io.gitlab.jfronny.libjf.config.api.dsl.DSL; +import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.entrypoint.EntrypointContainer; +import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; +import net.minecraft.util.Language; + +import java.util.function.Function; + +public class JfConfigSafe implements PreLaunchEntrypoint { + public static Function TRANSLATION_SUPPLIER = s -> null; + @Override + public void onPreLaunch() { + for (EntrypointContainer custom : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.CUSTOM_ID, JfCustomConfig.class)) { + custom.getEntrypoint().register(DSL.create(custom.getProvider().getMetadata().getId())); + } + TRANSLATION_SUPPLIER = s -> { + String translated = Language.getInstance().get(s); + return translated.equals(s) ? null : translated; + }; + } +} diff --git a/libjf-config-v1/src/main/resources/assets/libjf-config-v0/lang/en_us.json b/libjf-config-v1/src/main/resources/assets/libjf-config-v0/lang/en_us.json new file mode 100644 index 0000000..c4a89c7 --- /dev/null +++ b/libjf-config-v1/src/main/resources/assets/libjf-config-v0/lang/en_us.json @@ -0,0 +1,6 @@ +{ + "libjf-config-v1.presets": "Presets", + "libjf-config-v1.default": "Default", + "libjf-config-v1.see-also": "See also: %s", + "libjf-config-v1.reset": "Reset" +} \ No newline at end of file diff --git a/libjf-config-v0/src/main/resources/fabric.mod.json b/libjf-config-v1/src/main/resources/fabric.mod.json similarity index 89% rename from libjf-config-v0/src/main/resources/fabric.mod.json rename to libjf-config-v1/src/main/resources/fabric.mod.json index 9b26f7b..4a3d16d 100644 --- a/libjf-config-v0/src/main/resources/fabric.mod.json +++ b/libjf-config-v1/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "libjf-config-v0", + "id": "libjf-config-v1", "name": "LibJF Config", "version": "${version}", "authors": ["JFronny"], @@ -13,7 +13,6 @@ "entrypoints": { "modmenu": ["io.gitlab.jfronny.libjf.config.impl.client.ModMenu"], "client": ["io.gitlab.jfronny.libjf.config.impl.client.JfConfigClient"], - "libjf:preEarly": ["io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigUnsafe"], "preLaunch": ["io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe"], "main": ["io.gitlab.jfronny.libjf.config.impl.JfConfigCommand"], "libjf:coprocess": ["io.gitlab.jfronny.libjf.config.impl.JfConfigWatchService"] diff --git a/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java b/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java new file mode 100644 index 0000000..c3f8f25 --- /dev/null +++ b/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java @@ -0,0 +1,31 @@ +package io.gitlab.jfronny.libjf.config.test; + +import io.gitlab.jfronny.libjf.config.api.JfCustomConfig; +import io.gitlab.jfronny.libjf.config.api.dsl.DSL; + +public class TestConfigCustom { + public static Integer someValue = 10; + public static String someOther = "Yes"; + + public static class SomeCategory { + public static Boolean someBool = true; + } + + public static class LibJF_Companion implements JfCustomConfig { + @Override + public void register(DSL.Defaulted dsl) { + dsl.register(builder -> builder + .referenceConfig("libjf-config-reflect-v0-testmod") + .value("someValue", 10, -50, 50, () -> someValue, v -> someValue = v) + .value("someOther", "Yes", () -> someOther, v -> someOther = v) + .category("SomeCategory", builder1 -> builder1 + .value("someBool", true, () -> SomeCategory.someBool, v -> SomeCategory.someBool = v) + ) + ); + } + } + + static { + new LibJF_Companion().register(DSL.create("libjf-config-v1-testmod")); + } +} diff --git a/libjf-config-v1/src/testmod/resources/fabric.mod.json b/libjf-config-v1/src/testmod/resources/fabric.mod.json new file mode 100644 index 0000000..a99247b --- /dev/null +++ b/libjf-config-v1/src/testmod/resources/fabric.mod.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": 1, + "id": "libjf-config-v1-testmod", + "version": "1.0", + "environment": "*", + "entrypoints": { + "libjf:config_custom": [ + "io.gitlab.jfronny.libjf.config.test.TestConfigCustom$LibJF_Companion" + ] + }, + "custom": { + "libjf": { + "config": { + "referencedConfigs": ["libjf-web-v0"] + } + } + } +} \ No newline at end of file diff --git a/libjf-data-manipulation-v0/build.gradle b/libjf-data-manipulation-v0/build.gradle index db9a2e7..29c3bbf 100644 --- a/libjf-data-manipulation-v0/build.gradle +++ b/libjf-data-manipulation-v0/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "libjf-data-manipulation-v0" dependencies { - moduleDependencies(project, ["libjf-base", "libjf-unsafe-v0"]) + api project(":libjf-base") + api project(":libjf-unsafe-v0") modApi(fabricApi.module("fabric-api-base", "$rootProject.fabric_version")) } diff --git a/libjf-data-v0/build.gradle b/libjf-data-v0/build.gradle index d9d3d66..cb9eeb8 100644 --- a/libjf-data-v0/build.gradle +++ b/libjf-data-v0/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "libjf-data-v0" dependencies { - moduleDependencies(project, ["libjf-base"]) - include(fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}")) + api project(":libjf-base") + include fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}") } diff --git a/libjf-devutil-v0/build.gradle b/libjf-devutil-v0/build.gradle index 933e5ee..00e1ff7 100644 --- a/libjf-devutil-v0/build.gradle +++ b/libjf-devutil-v0/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "libjf-devutil-v0" dependencies { - moduleDependencies(project, ["libjf-base"]) + api project(":libjf-base") } diff --git a/libjf-translate-v1/build.gradle b/libjf-translate-v1/build.gradle index a8218db..6e297b5 100644 --- a/libjf-translate-v1/build.gradle +++ b/libjf-translate-v1/build.gradle @@ -1,5 +1,6 @@ archivesBaseName = "libjf-translate-v1" dependencies { - moduleDependencies(project, ["libjf-base", "libjf-config-v0"]) + api project(":libjf-base") + api project(":libjf-config-v1") } diff --git a/libjf-unsafe-v0/build.gradle b/libjf-unsafe-v0/build.gradle index a4e1a31..f797a2e 100644 --- a/libjf-unsafe-v0/build.gradle +++ b/libjf-unsafe-v0/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "libjf-unsafe-v0" dependencies { - moduleDependencies(project, ["libjf-base"]) + api project(":libjf-base") } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java index 59f166e..569ac0c 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java @@ -2,7 +2,7 @@ package io.gitlab.jfronny.libjf.unsafe; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.impl.ModContainerImpl; +import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.impl.metadata.EntrypointMetadata; import java.lang.reflect.InvocationTargetException; @@ -46,11 +46,11 @@ public class DynamicEntry { private static Collection getEntrypointTargets(final String entrypoint) { final List entrypoints = new LinkedList<>(); for (final ModContainer mod : FabricLoader.getInstance().getAllMods()) { - final List modEntrypoints = ((ModContainerImpl)mod).getInfo().getEntrypoints(entrypoint); + final List modEntrypoints = getEntrypoints(mod.getMetadata(), entrypoint); if (modEntrypoints != null) { - for (final EntrypointMetadata metadata : modEntrypoints) { - entrypoints.add(new EntrypointContainer(metadata.getValue(), mod.getMetadata().getId())); + for (final Object metadata : modEntrypoints) { + entrypoints.add(new EntrypointContainer(getValue(metadata), mod.getMetadata().getId())); } } } @@ -58,6 +58,27 @@ public class DynamicEntry { return entrypoints; } + @SuppressWarnings("unchecked") + private static List getEntrypoints(Object metadata, String name) { + try { + var method = metadata.getClass().getMethod("getEntrypoints", String.class); + method.setAccessible(true); + return (List) method.invoke(metadata, name); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private static String getValue(Object object) { + try { + var method = object.getClass().getMethod("getValue"); + method.setAccessible(true); + return (String) method.invoke(object); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + record EntrypointContainer(String entrypoint, String mod) { } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java index e0623d3..6a14fc2 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java @@ -3,6 +3,7 @@ package io.gitlab.jfronny.libjf.unsafe; import io.gitlab.jfronny.commons.log.Logger; import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; import io.gitlab.jfronny.libjf.Flags; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy; import io.gitlab.jfronny.libjf.unsafe.inject.FabricLauncherClassUnlocker; import io.gitlab.jfronny.libjf.unsafe.inject.KnotClassLoaderInterfaceAccessor; @@ -17,17 +18,17 @@ public class JfLanguageAdapter implements LanguageAdapter { public native T create(net.fabricmc.loader.api.ModContainer mod, String value, Class type); static { - Logger.registerFactory(FLLogger::new); // Reset in SafeLog entrypoint + Logger.registerFactory(FLLogger::new); // Reset in LibJf entrypoint Set flags = Flags.getBoolFlags("unsafe.unlock"); if (flags.stream().map(Flags.BooleanFlag::value).reduce(false, (left, right) -> left || right)) { - SafeLog.warn("Unlocking classpath due to: " + flags.stream().map(Flags.BooleanFlag::source).collect(Collectors.joining(", "))); + LibJf.LOGGER.warn("Unlocking classpath due to: " + flags.stream().map(Flags.BooleanFlag::source).collect(Collectors.joining(", "))); FabricLoaderImpl.INSTANCE.getGameProvider().unlockClassPath(new FabricLauncherClassUnlocker(new KnotClassLoaderInterfaceAccessor(Thread.currentThread().getContextClassLoader()))); - SafeLog.warn("Completed classpath unlock"); + LibJf.LOGGER.warn("Completed classpath unlock"); } HiddenAnnotationExclusionStrategy.register(); GsonHolder.register(); DynamicEntry.execute("libjf:preEarly", UltraEarlyInit.class, s -> s.instance().init()); DynamicEntry.execute("libjf:early", UltraEarlyInit.class, s -> s.instance().init()); - SafeLog.info("LibJF unsafe init completed"); + LibJf.LOGGER.info("LibJF unsafe init completed"); } } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/MixinPlugin.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/MixinPlugin.java index a37a32d..173266e 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/MixinPlugin.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/MixinPlugin.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.libjf.unsafe; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.unsafe.asm.*; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; @@ -28,13 +29,13 @@ public class MixinPlugin implements IMixinConfigPlugin { AsmTransformer.INSTANCE.delegate = (IMixinTransformer) mixinTransformerField.get(delegate); AsmTransformer.INSTANCE.asmConfigs = new HashSet<>(); DynamicEntry.execute("libjf:asm", AsmConfig.class, s -> { - SafeLog.info("Discovered LibJF asm plugin in " + s.modId()); + LibJf.LOGGER.info("Discovered LibJF asm plugin in " + s.modId()); AsmTransformer.INSTANCE.asmConfigs.add(new BakedAsmConfig(s.instance(), s.modId())); }); AsmTransformer.INSTANCE.init(); mixinTransformerField.set(delegate, AsmTransformer.INSTANCE); } catch (Throwable e) { - SafeLog.error("Could not initialize LibJF ASM", e); + LibJf.LOGGER.error("Could not initialize LibJF ASM", e); } } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/SafeLog.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/SafeLog.java deleted file mode 100644 index 444b315..0000000 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/SafeLog.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.gitlab.jfronny.libjf.unsafe; - -import io.gitlab.jfronny.commons.log.Logger; -import net.fabricmc.api.ModInitializer; - -public class SafeLog implements ModInitializer { - private static Logger BACKEND = Logger.forName("LibJF"); - - public static void debug(String text) { - BACKEND.debug(text); - } - - public static void info(String text) { - BACKEND.info(text); - } - - public static void warn(String text) { - BACKEND.warn(text); - } - - public static void error(String text) { - BACKEND.error(text); - } - - public static void error(String text, Throwable e) { - BACKEND.error(text, e); - } - - @Override - public void onInitialize() { - Logger.resetFactory(); - BACKEND = Logger.forName("libjf"); - } -} diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/AsmTransformer.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/AsmTransformer.java index 49e05c4..faf39e2 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/AsmTransformer.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/AsmTransformer.java @@ -1,7 +1,7 @@ package io.gitlab.jfronny.libjf.unsafe.asm; import io.gitlab.jfronny.libjf.Flags; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.MappingResolver; @@ -39,7 +39,7 @@ public class AsmTransformer implements IMixinTransformer { for (Flags.BooleanFlag flag : flags) { flagNames.add(flag.source()); } - SafeLog.info("Exporting ASM due to: " + String.join(", ", flagNames)); + LibJf.LOGGER.info("Exporting ASM due to: " + String.join(", ", flagNames)); } flags = Flags.getBoolFlags("asm.log"); flags.removeIf(flag -> !flag.value()); @@ -49,7 +49,7 @@ public class AsmTransformer implements IMixinTransformer { for (Flags.BooleanFlag flag : flags) { flagNames.add(flag.source()); } - SafeLog.info("Logging ASM logs due to: " + String.join(", ", flagNames)); + LibJf.LOGGER.info("Logging ASM logs due to: " + String.join(", ", flagNames)); } } @@ -77,7 +77,7 @@ public class AsmTransformer implements IMixinTransformer { classBytes = delegate.transformClassBytes(name, transformedName, classBytes); if (classBytes == null || name == null) return classBytes; if (isClassUnmoddable(name, null)) { - if (debugLogsEnabled()) SafeLog.info("Skipping " + name); + if (debugLogsEnabled()) LibJf.LOGGER.info("Skipping " + name); return classBytes; } @@ -93,7 +93,7 @@ public class AsmTransformer implements IMixinTransformer { patch.apply(klazz); } catch (Throwable t) { - SafeLog.error("Could not apply patch: " + patch.getClass() + " on " + name, t); + LibJf.LOGGER.error("Could not apply patch: " + patch.getClass() + " on " + name, t); } } } @@ -105,7 +105,7 @@ public class AsmTransformer implements IMixinTransformer { klazz.accept(writer); } catch (NullPointerException t) { - SafeLog.error("Could not transform " + transformedName, t); + LibJf.LOGGER.error("Could not transform " + transformedName, t); return null; } classBytes = writer.toByteArray(); @@ -115,7 +115,7 @@ public class AsmTransformer implements IMixinTransformer { if (!Files.exists(path)) Files.createDirectories(path.getParent()); Files.write(path, classBytes); } catch (IOException e) { - SafeLog.error("Could not export modified bytecode", e); + LibJf.LOGGER.error("Could not export modified bytecode", e); } } return classBytes; diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/modification/MethodModificationPatch.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/modification/MethodModificationPatch.java index 54f03d3..a5e528b 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/modification/MethodModificationPatch.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/modification/MethodModificationPatch.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.libjf.unsafe.asm.patch.modification; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer; import io.gitlab.jfronny.libjf.unsafe.asm.patch.MethodPatch; import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch; @@ -22,7 +22,7 @@ public class MethodModificationPatch implements Patch { ); } for (String s : this.patches.keySet()) { - if (AsmTransformer.INSTANCE.debugLogsEnabled()) SafeLog.info("Registered patch for " + s); + if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Registered patch for " + s); } } @@ -30,7 +30,7 @@ public class MethodModificationPatch implements Patch { public void apply(ClassNode klazz) { for (MethodNode method : klazz.methods) { if (patches.containsKey(method.name)) { - if (AsmTransformer.INSTANCE.debugLogsEnabled()) SafeLog.info("Patching " + method.name); + if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Patching " + method.name); patches.get(method.name).apply(method, klazz); } } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/targeting/InterfaceImplTargetPatch.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/targeting/InterfaceImplTargetPatch.java index 278df70..d899535 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/targeting/InterfaceImplTargetPatch.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/targeting/InterfaceImplTargetPatch.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.libjf.unsafe.asm.patch.targeting; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer; import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch; import org.objectweb.asm.Type; @@ -22,7 +22,7 @@ public class InterfaceImplTargetPatch implements Patch { public void apply(ClassNode klazz) { scanInterfaces(klazz); if (getUpper(klazz.name).contains(targetInterface)) { - if (AsmTransformer.INSTANCE.debugLogsEnabled()) SafeLog.info("Found " + klazz.name + " implementing " + targetInterface); + if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Found " + klazz.name + " implementing " + targetInterface); methodPatch.apply(klazz); } } @@ -78,7 +78,7 @@ public class InterfaceImplTargetPatch implements Patch { && !className.startsWith("it/unimi/dsi/fastutil/") //&& !className.startsWith("com/google/") ) { - if (AsmTransformer.INSTANCE.debugLogsEnabled()) SafeLog.info("Non-default class not considered for interface scanning: " + className); + if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Non-default class not considered for interface scanning: " + className); INTERFACES.put(className, Set.of()); return Set.of(); } @@ -86,7 +86,7 @@ public class InterfaceImplTargetPatch implements Patch { scanInterfaces(Class.forName(className.replace('/', '.'))); s = INTERFACES.get(className); } catch (ClassNotFoundException e) { - SafeLog.error("Could not get base for " + className, e); + LibJf.LOGGER.error("Could not get base for " + className, e); return Set.of(); } } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java index a8dbe1b..c96607f 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.libjf.unsafe.inject; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.unsafe.*; import net.fabricmc.api.*; import net.fabricmc.loader.impl.launch.*; @@ -17,7 +18,7 @@ public record FabricLauncherClassUnlocker(KnotClassLoaderInterfaceAccessor class @Override public void addToClassPath(Path path, String... allowedPrefixes) { - SafeLog.debug("Adding " + path + " to classpath."); + LibJf.LOGGER.debug("Adding " + path + " to classpath."); classLoader.getDelegate().setAllowedPrefixes(path, allowedPrefixes); classLoader.getDelegate().addCodeSource(path); @@ -89,7 +90,7 @@ public record FabricLauncherClassUnlocker(KnotClassLoaderInterfaceAccessor class } private T invalidCall() { - SafeLog.warn("Attempted invalid call in class path unlocker"); + LibJf.LOGGER.warn("Attempted invalid call in class path unlocker"); throw new IllegalStateException("unlockClassPath attempted to call a method not implemented here"); } } diff --git a/libjf-unsafe-v0/src/main/resources/fabric.mod.json b/libjf-unsafe-v0/src/main/resources/fabric.mod.json index 26cc28a..f5e35ae 100644 --- a/libjf-unsafe-v0/src/main/resources/fabric.mod.json +++ b/libjf-unsafe-v0/src/main/resources/fabric.mod.json @@ -24,9 +24,6 @@ "breaks": { "quilt_loader": "*" }, - "entrypoints": { - "main": ["io.gitlab.jfronny.libjf.unsafe.SafeLog"] - }, "custom": { "modmenu": { "parent": "libjf", diff --git a/libjf-unsafe-v0/src/testmod/java/io/gitlab/jfronny/libjf/unsafe/test/UnsafeEntryTest.java b/libjf-unsafe-v0/src/testmod/java/io/gitlab/jfronny/libjf/unsafe/test/UnsafeEntryTest.java index b02a1e1..9e264ba 100644 --- a/libjf-unsafe-v0/src/testmod/java/io/gitlab/jfronny/libjf/unsafe/test/UnsafeEntryTest.java +++ b/libjf-unsafe-v0/src/testmod/java/io/gitlab/jfronny/libjf/unsafe/test/UnsafeEntryTest.java @@ -1,12 +1,12 @@ package io.gitlab.jfronny.libjf.unsafe.test; -import io.gitlab.jfronny.libjf.unsafe.SafeLog; +import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit; public class UnsafeEntryTest implements UltraEarlyInit { @Override public void init() { - SafeLog.info("Successfully executed code before that should be possible\n" + + LibJf.LOGGER.info("Successfully executed code before that should be possible\n" + "'||' '|' '|| . '||''''| '|| \n" + " || | || .||. ... .. .... || . .... ... .. || .... ... \n" + " || | || || ||' '' '' .|| ||''| '' .|| ||' '' || '|. | \n" + diff --git a/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json b/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json index 0d79f3b..78bb142 100644 --- a/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json +++ b/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json @@ -15,7 +15,7 @@ "libjf": { "asm.export": true, "asm.log": true, - "unsafe.unlock": true + "unsafe.unlock": false } } } \ No newline at end of file diff --git a/libjf-web-v0/build.gradle b/libjf-web-v0/build.gradle index 7b6c039..1d58c6f 100644 --- a/libjf-web-v0/build.gradle +++ b/libjf-web-v0/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "libjf-web-v0" dependencies { - moduleDependencies(project, ["libjf-base", "libjf-config-v0"]) + api project(":libjf-base") + api project(":libjf-config-v1") include modImplementation(fabricApi.module("fabric-command-api-v2", "${rootProject.fabric_version}")) } diff --git a/libjf-web-v0/src/main/resources/fabric.mod.json b/libjf-web-v0/src/main/resources/fabric.mod.json index 54a3d33..a4e3ad0 100644 --- a/libjf-web-v0/src/main/resources/fabric.mod.json +++ b/libjf-web-v0/src/main/resources/fabric.mod.json @@ -21,7 +21,7 @@ "fabricloader": ">=0.12.0", "minecraft": "*", "libjf-base": ">=${version}", - "libjf-config-v0": ">=${version}", + "libjf-config-v1": ">=${version}", "fabric-command-api-v2": "*" }, "custom": { diff --git a/settings.gradle b/settings.gradle index cb5bc41..fc979f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,10 +12,11 @@ rootProject.name = "libjf" include 'libjf-base' -include 'libjf-config-v0' +include 'libjf-config-v1' +include 'libjf-config-reflect-v0' include 'libjf-data-v0' include 'libjf-data-manipulation-v0' include 'libjf-devutil-v0' include 'libjf-translate-v1' include 'libjf-unsafe-v0' -include 'libjf-web-v0' \ No newline at end of file +include 'libjf-web-v0' From 6e359462d2c55829ade5a2c30da0aae782329122 Mon Sep 17 00:00:00 2001 From: JFronny Date: Mon, 22 Aug 2022 00:12:19 +0200 Subject: [PATCH 2/4] [unsafe] Fix and remove unsafe.unlock --- build.gradle | 4 +- libjf-config-reflect-v0/build.gradle | 6 +- .../entrypoint/JfConfigReflectSafe.java | 2 - .../config/test/{ => reflect}/TestConfig.java | 2 +- .../libjf-config-v0-testmod/lang/en_us.json | 22 ++--- .../src/testmod/resources/fabric.mod.json | 2 +- libjf-config-v1/build.gradle | 2 +- .../libjf/config/impl/ConfigHolderImpl.java | 1 + .../config/impl/dsl/CategoryBuilderImpl.java | 2 +- .../libjf/config/test/TestConfigCustom.java | 2 +- libjf-data-manipulation-v0/build.gradle | 4 +- libjf-data-v0/build.gradle | 2 +- libjf-devutil-v0/build.gradle | 2 +- .../libjf/devutil/mixin/ProfileKeysMixin.java | 22 ++++- libjf-translate-v1/build.gradle | 4 +- libjf-unsafe-v0/build.gradle | 2 +- .../jfronny/libjf/unsafe/DynamicEntry.java | 29 +----- .../libjf/unsafe/JfLanguageAdapter.java | 13 --- .../inject/FabricLauncherClassUnlocker.java | 96 ------------------- .../inject/KnotClassDelegateAccessor.java | 36 ------- .../KnotClassLoaderInterfaceAccessor.java | 25 ----- .../src/testmod/resources/fabric.mod.json | 3 +- libjf-web-v0/build.gradle | 4 +- 23 files changed, 53 insertions(+), 234 deletions(-) rename libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/{ => reflect}/TestConfig.java (96%) delete mode 100644 libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java delete mode 100644 libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassDelegateAccessor.java delete mode 100644 libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassLoaderInterfaceAccessor.java diff --git a/build.gradle b/build.gradle index f12e0ef..d1ead15 100644 --- a/build.gradle +++ b/build.gradle @@ -21,8 +21,8 @@ allprojects { } dependencies { - testmodRuntimeOnly("com.terraformersmc:modmenu:4.0.5") - testmodRuntimeOnly("net.fabricmc.fabric-api:fabric-api:$project.fabric_version") + modLocalRuntime("com.terraformersmc:modmenu:4.0.5") + modLocalRuntime(fabricApi.module("fabric-command-api-v2", "$project.fabric_version")) compileOnly("io.gitlab.jfronny:commons:$rootProject.commons_version") compileOnly("io.gitlab.jfronny:commons-gson:$rootProject.commons_version") } diff --git a/libjf-config-reflect-v0/build.gradle b/libjf-config-reflect-v0/build.gradle index cd2c055..1073736 100644 --- a/libjf-config-reflect-v0/build.gradle +++ b/libjf-config-reflect-v0/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = "libjf-config-reflect-v0" dependencies { - api project(":libjf-base") - api project(":libjf-unsafe-v0") - api project(":libjf-config-v1") + api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-unsafe-v0", configuration: "dev") + api project(path: ":libjf-config-v1", configuration: "dev") } diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java index 5007fb4..7e1aa7f 100644 --- a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java +++ b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java @@ -1,6 +1,5 @@ package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; -import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.ConfigHolder; import io.gitlab.jfronny.libjf.config.api.JfConfig; import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; @@ -19,7 +18,6 @@ public class JfConfigReflectSafe implements PreLaunchEntrypoint { public static void registerIfMissing(String modId, Class klazz) { if (!ConfigHolder.getInstance().isRegistered(modId)) { - LibJf.LOGGER.info("Registering config for " + modId); ConfigProvider.register(modId, klazz); } } diff --git a/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java b/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java similarity index 96% rename from libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java rename to libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java index b82decb..5dd3814 100644 --- a/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfig.java +++ b/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.test; +package io.gitlab.jfronny.libjf.config.test.reflect; import io.gitlab.jfronny.commons.serialize.gson.api.Ignore; import io.gitlab.jfronny.libjf.config.api.*; diff --git a/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json b/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json index 3ac4953..5214aff 100644 --- a/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json +++ b/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json @@ -1,13 +1,13 @@ { - "libjf-config-v1-testmod.jfconfig.title": "JfConfig example", - "libjf-config-v1-testmod.jfconfig.disablePacks": "Disable resource packs", - "libjf-config-v1-testmod.jfconfig.intTest": "Int Test", - "libjf-config-v1-testmod.jfconfig.decimalTest": "Decimal Test", - "libjf-config-v1-testmod.jfconfig.dieStr": "String Test", - "libjf-config-v1-testmod.jfconfig.gsonOnlyStr.tooltip": "George", - "libjf-config-v1-testmod.jfconfig.enumTest": "Enum Test", - "libjf-config-v1-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip", - "libjf-config-v1-testmod.jfconfig.enum.Test.Test": "Test", - "libjf-config-v1-testmod.jfconfig.enum.Test.ER": "ER", - "libjf-config-v1-testmod.jfconfig.moskau": "Moskau" + "libjf-config-reflect-v0-testmod.jfconfig.title": "JfConfig example", + "libjf-config-reflect-v0-testmod.jfconfig.disablePacks": "Disable resource packs", + "libjf-config-reflect-v0-testmod.jfconfig.intTest": "Int Test", + "libjf-config-reflect-v0-testmod.jfconfig.decimalTest": "Decimal Test", + "libjf-config-reflect-v0-testmod.jfconfig.dieStr": "String Test", + "libjf-config-reflect-v0-testmod.jfconfig.gsonOnlyStr.tooltip": "George", + "libjf-config-reflect-v0-testmod.jfconfig.enumTest": "Enum Test", + "libjf-config-reflect-v0-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip", + "libjf-config-reflect-v0-testmod.jfconfig.enum.Test.Test": "Test", + "libjf-config-reflect-v0-testmod.jfconfig.enum.Test.ER": "ER", + "libjf-config-reflect-v0-testmod.jfconfig.moskau": "Moskau" } \ No newline at end of file diff --git a/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json b/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json index cc60942..2affb18 100644 --- a/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json +++ b/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json @@ -5,7 +5,7 @@ "environment": "*", "entrypoints": { "libjf:config": [ - "io.gitlab.jfronny.libjf.config.test.TestConfig" + "io.gitlab.jfronny.libjf.config.test.reflect.TestConfig" ] }, "custom": { diff --git a/libjf-config-v1/build.gradle b/libjf-config-v1/build.gradle index 4d08a55..8e0de14 100644 --- a/libjf-config-v1/build.gradle +++ b/libjf-config-v1/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = "libjf-config-v1" dependencies { - api project(":libjf-base") + api project(path: ":libjf-base", configuration: "dev") include fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}") include modImplementation(fabricApi.module("fabric-command-api-v2", "${project.fabric_version}")) modCompileOnly("com.terraformersmc:modmenu:4.0.5") diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java index a2039fe..d40de7f 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java @@ -25,6 +25,7 @@ public class ConfigHolderImpl implements ConfigHolder { if (isRegistered(modId)) { LibJf.LOGGER.warn("Overriding config class of " + modId + " to " + config); } + LibJf.LOGGER.info("Registering config for " + modId); configs.put(modId, config); config.getFilePath().ifPresent(path -> configsByPath.put(path, config)); } diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java index 3d5f929..ad641db 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java +++ b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java @@ -71,7 +71,7 @@ public class CategoryBuilderImpl> i @Override public Builder category(String id, CategoryBuilderFunction builder) { checkBuilt(); - categories.add(builder.apply(new CategoryBuilderImpl(id, categoryPath + id + "."))); + categories.add(builder.apply(new CategoryBuilderImpl(id, categoryPath + id + ".").setTranslationPrefix(translationPrefix + id + "."))); return asBuilder(); } diff --git a/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java b/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java index c3f8f25..a1e6880 100644 --- a/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java +++ b/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java @@ -18,7 +18,7 @@ public class TestConfigCustom { .referenceConfig("libjf-config-reflect-v0-testmod") .value("someValue", 10, -50, 50, () -> someValue, v -> someValue = v) .value("someOther", "Yes", () -> someOther, v -> someOther = v) - .category("SomeCategory", builder1 -> builder1 + .category("someCategory", builder1 -> builder1 .value("someBool", true, () -> SomeCategory.someBool, v -> SomeCategory.someBool = v) ) ); diff --git a/libjf-data-manipulation-v0/build.gradle b/libjf-data-manipulation-v0/build.gradle index 29c3bbf..9b53f8c 100644 --- a/libjf-data-manipulation-v0/build.gradle +++ b/libjf-data-manipulation-v0/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = "libjf-data-manipulation-v0" dependencies { - api project(":libjf-base") - api project(":libjf-unsafe-v0") + api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-unsafe-v0", configuration: "dev") modApi(fabricApi.module("fabric-api-base", "$rootProject.fabric_version")) } diff --git a/libjf-data-v0/build.gradle b/libjf-data-v0/build.gradle index cb9eeb8..03312a7 100644 --- a/libjf-data-v0/build.gradle +++ b/libjf-data-v0/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "libjf-data-v0" dependencies { - api project(":libjf-base") + api project(path: ":libjf-base", configuration: "dev") include fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}") } diff --git a/libjf-devutil-v0/build.gradle b/libjf-devutil-v0/build.gradle index 00e1ff7..5e600ba 100644 --- a/libjf-devutil-v0/build.gradle +++ b/libjf-devutil-v0/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "libjf-devutil-v0" dependencies { - api project(":libjf-base") + api project(path: ":libjf-base", configuration: "dev") } diff --git a/libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java b/libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java index 13443fb..cc29c4b 100644 --- a/libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java +++ b/libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java @@ -1,19 +1,31 @@ package io.gitlab.jfronny.libjf.devutil.mixin; -import com.mojang.authlib.minecraft.UserApiService; import net.minecraft.client.util.ProfileKeys; import net.minecraft.network.encryption.PlayerKeyPair; +import net.minecraft.network.encryption.PlayerPublicKey; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.Overwrite; import java.util.Optional; import java.util.concurrent.CompletableFuture; @Mixin(ProfileKeys.class) public class ProfileKeysMixin { - @Redirect(method = "(Lcom/mojang/authlib/minecraft/UserApiService;Ljava/util/UUID;Ljava/nio/file/Path;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/ProfileKeys;getKeyPair(Lcom/mojang/authlib/minecraft/UserApiService;)Ljava/util/concurrent/CompletableFuture;")) - private CompletableFuture> libjf$redirectGetKeyPair(ProfileKeys instance, UserApiService userApiService) { + /** + * @author JFronny + * @reason Do not load keys! + */ + @Overwrite + public CompletableFuture> refresh() { + return CompletableFuture.completedFuture(Optional.empty()); + } + + /** + * @author JFronny + * @reason Do not load keys! + */ + @Overwrite + private CompletableFuture> getKeyPair(Optional currentKey) { return CompletableFuture.completedFuture(Optional.empty()); } } diff --git a/libjf-translate-v1/build.gradle b/libjf-translate-v1/build.gradle index 6e297b5..41032df 100644 --- a/libjf-translate-v1/build.gradle +++ b/libjf-translate-v1/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "libjf-translate-v1" dependencies { - api project(":libjf-base") - api project(":libjf-config-v1") + api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-config-v1", configuration: "dev") } diff --git a/libjf-unsafe-v0/build.gradle b/libjf-unsafe-v0/build.gradle index f797a2e..632f5b5 100644 --- a/libjf-unsafe-v0/build.gradle +++ b/libjf-unsafe-v0/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "libjf-unsafe-v0" dependencies { - api project(":libjf-base") + api project(path: ":libjf-base", configuration: "dev") } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java index 569ac0c..59f166e 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/DynamicEntry.java @@ -2,7 +2,7 @@ package io.gitlab.jfronny.libjf.unsafe; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.api.metadata.ModMetadata; +import net.fabricmc.loader.impl.ModContainerImpl; import net.fabricmc.loader.impl.metadata.EntrypointMetadata; import java.lang.reflect.InvocationTargetException; @@ -46,11 +46,11 @@ public class DynamicEntry { private static Collection getEntrypointTargets(final String entrypoint) { final List entrypoints = new LinkedList<>(); for (final ModContainer mod : FabricLoader.getInstance().getAllMods()) { - final List modEntrypoints = getEntrypoints(mod.getMetadata(), entrypoint); + final List modEntrypoints = ((ModContainerImpl)mod).getInfo().getEntrypoints(entrypoint); if (modEntrypoints != null) { - for (final Object metadata : modEntrypoints) { - entrypoints.add(new EntrypointContainer(getValue(metadata), mod.getMetadata().getId())); + for (final EntrypointMetadata metadata : modEntrypoints) { + entrypoints.add(new EntrypointContainer(metadata.getValue(), mod.getMetadata().getId())); } } } @@ -58,27 +58,6 @@ public class DynamicEntry { return entrypoints; } - @SuppressWarnings("unchecked") - private static List getEntrypoints(Object metadata, String name) { - try { - var method = metadata.getClass().getMethod("getEntrypoints", String.class); - method.setAccessible(true); - return (List) method.invoke(metadata, name); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - private static String getValue(Object object) { - try { - var method = object.getClass().getMethod("getValue"); - method.setAccessible(true); - return (String) method.invoke(object); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - record EntrypointContainer(String entrypoint, String mod) { } diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java index 6a14fc2..572c727 100644 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java +++ b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/JfLanguageAdapter.java @@ -2,16 +2,9 @@ package io.gitlab.jfronny.libjf.unsafe; import io.gitlab.jfronny.commons.log.Logger; import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; -import io.gitlab.jfronny.libjf.Flags; import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy; -import io.gitlab.jfronny.libjf.unsafe.inject.FabricLauncherClassUnlocker; -import io.gitlab.jfronny.libjf.unsafe.inject.KnotClassLoaderInterfaceAccessor; import net.fabricmc.loader.api.LanguageAdapter; -import net.fabricmc.loader.impl.FabricLoaderImpl; - -import java.util.Set; -import java.util.stream.Collectors; public class JfLanguageAdapter implements LanguageAdapter { @Override @@ -19,12 +12,6 @@ public class JfLanguageAdapter implements LanguageAdapter { static { Logger.registerFactory(FLLogger::new); // Reset in LibJf entrypoint - Set flags = Flags.getBoolFlags("unsafe.unlock"); - if (flags.stream().map(Flags.BooleanFlag::value).reduce(false, (left, right) -> left || right)) { - LibJf.LOGGER.warn("Unlocking classpath due to: " + flags.stream().map(Flags.BooleanFlag::source).collect(Collectors.joining(", "))); - FabricLoaderImpl.INSTANCE.getGameProvider().unlockClassPath(new FabricLauncherClassUnlocker(new KnotClassLoaderInterfaceAccessor(Thread.currentThread().getContextClassLoader()))); - LibJf.LOGGER.warn("Completed classpath unlock"); - } HiddenAnnotationExclusionStrategy.register(); GsonHolder.register(); DynamicEntry.execute("libjf:preEarly", UltraEarlyInit.class, s -> s.instance().init()); diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java deleted file mode 100644 index c96607f..0000000 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/FabricLauncherClassUnlocker.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.gitlab.jfronny.libjf.unsafe.inject; - -import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.unsafe.*; -import net.fabricmc.api.*; -import net.fabricmc.loader.impl.launch.*; - -import java.io.*; -import java.nio.file.*; -import java.util.*; -import java.util.jar.*; - -public record FabricLauncherClassUnlocker(KnotClassLoaderInterfaceAccessor classLoader) implements FabricLauncher { - @Override - public MappingConfiguration getMappingConfiguration() { - return invalidCall(); - } - - @Override - public void addToClassPath(Path path, String... allowedPrefixes) { - LibJf.LOGGER.debug("Adding " + path + " to classpath."); - - classLoader.getDelegate().setAllowedPrefixes(path, allowedPrefixes); - classLoader.getDelegate().addCodeSource(path); - } - - @Override - public void setAllowedPrefixes(Path path, String... prefixes) { - classLoader.getDelegate().setAllowedPrefixes(path, prefixes); - } - - @Override - public void setValidParentClassPath(Collection paths) { - invalidCall(); - } - - @Override - public EnvType getEnvironmentType() { - return invalidCall(); - } - - @Override - public boolean isClassLoaded(String name) { - return invalidCall(); - } - - @Override - public Class loadIntoTarget(String name) throws ClassNotFoundException { - return invalidCall(); - } - - @Override - public InputStream getResourceAsStream(String name) { - return invalidCall(); - } - - @Override - public ClassLoader getTargetClassLoader() { - return invalidCall(); - } - - @Override - public byte[] getClassByteArray(String name, boolean runTransformers) throws IOException { - return invalidCall(); - } - - @Override - public Manifest getManifest(Path originPath) { - return invalidCall(); - } - - @Override - public boolean isDevelopment() { - return invalidCall(); - } - - @Override - public String getEntrypoint() { - return invalidCall(); - } - - @Override - public String getTargetNamespace() { - return invalidCall(); - } - - @Override - public List getClassPath() { - return invalidCall(); - } - - private T invalidCall() { - LibJf.LOGGER.warn("Attempted invalid call in class path unlocker"); - throw new IllegalStateException("unlockClassPath attempted to call a method not implemented here"); - } -} diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassDelegateAccessor.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassDelegateAccessor.java deleted file mode 100644 index 6a53885..0000000 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassDelegateAccessor.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.gitlab.jfronny.libjf.unsafe.inject; - -import java.lang.reflect.*; -import java.nio.file.*; - -public record KnotClassDelegateAccessor(Object delegate) { - private static final Method addCodeSourceMethod; - private static final Method setAllowedPrefixesMethod; - static { - try { - Class klazz = Class.forName("net.fabricmc.loader.impl.launch.knot.KnotClassDelegate"); - addCodeSourceMethod = klazz.getDeclaredMethod("addCodeSource", Path.class); - addCodeSourceMethod.setAccessible(true); - setAllowedPrefixesMethod = klazz.getDeclaredMethod("setAllowedPrefixes", Path.class, String[].class); - setAllowedPrefixesMethod.setAccessible(true); - } catch (ClassNotFoundException | NoSuchMethodException e) { - throw new IllegalStateException("Could not build accessor for class delegate. This version of fabric is likely not yet supported", e); - } - } - - public void addCodeSource(Path url) { - try { - addCodeSourceMethod.invoke(delegate, url); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException("Could not invoke addURL on class loader. This version of fabric is likely not yet supported", e); - } - } - - public void setAllowedPrefixes(Path url, String... prefixes) { - try { - setAllowedPrefixesMethod.invoke(delegate, url, prefixes); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException("Could not invoke setAllowedPrefixes on class delegate. This version of fabric is likely not yet supported", e); - } - } -} diff --git a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassLoaderInterfaceAccessor.java b/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassLoaderInterfaceAccessor.java deleted file mode 100644 index 2291438..0000000 --- a/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/inject/KnotClassLoaderInterfaceAccessor.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.gitlab.jfronny.libjf.unsafe.inject; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public record KnotClassLoaderInterfaceAccessor(ClassLoader loader) { - private static final Method getDelegateMethod; - static { - try { - Class klazz = Class.forName("net.fabricmc.loader.impl.launch.knot.KnotClassLoader"); - getDelegateMethod = klazz.getDeclaredMethod("getDelegate"); - getDelegateMethod.setAccessible(true); - } catch (ClassNotFoundException | NoSuchMethodException e) { - throw new IllegalStateException("Could not build accessor for class loader. This version of fabric is likely not yet supported", e); - } - } - - public KnotClassDelegateAccessor getDelegate() { - try { - return new KnotClassDelegateAccessor(getDelegateMethod.invoke(loader)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException("Could not invoke getDelegate on class loader. This version of fabric is likely not yet supported", e); - } - } -} diff --git a/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json b/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json index 78bb142..0d54739 100644 --- a/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json +++ b/libjf-unsafe-v0/src/testmod/resources/fabric.mod.json @@ -14,8 +14,7 @@ "custom": { "libjf": { "asm.export": true, - "asm.log": true, - "unsafe.unlock": false + "asm.log": true } } } \ No newline at end of file diff --git a/libjf-web-v0/build.gradle b/libjf-web-v0/build.gradle index 1d58c6f..2a12ac6 100644 --- a/libjf-web-v0/build.gradle +++ b/libjf-web-v0/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = "libjf-web-v0" dependencies { - api project(":libjf-base") - api project(":libjf-config-v1") + api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-config-v1", configuration: "dev") include modImplementation(fabricApi.module("fabric-command-api-v2", "${rootProject.fabric_version}")) } From 6a77d3221a7cafd5bf66b75b2bbc7b3d4a550fe2 Mon Sep 17 00:00:00 2001 From: JFronny Date: Fri, 26 Aug 2022 20:51:00 +0200 Subject: [PATCH 3/4] [config] Initial prototype for compile-time configs, module name changes and legacy compatibility --- extract.sh | 4 + gradle.properties | 3 +- libjf-config-commands-v1/build.gradle | 7 + .../impl/commands}/JfConfigCommand.java | 21 +- .../src/main/resources/fabric.mod.json | 29 +++ libjf-config-compiler-plugin/build.gradle | 60 ++++++ .../config/plugin/ConfigInjectCopyAction.java | 54 +++++ .../libjf/config/plugin/ConfigInjectTask.java | 40 ++++ .../libjf/config/plugin/ConfigPlugin.java | 11 + .../libjf/config/plugin/StreamAction.java | 189 ++++++++++++++++ .../asm/AnnotationMetaGatheringVisitor.java | 40 ++++ .../asm/ConfigInjectClassTransformer.java | 204 ++++++++++++++++++ .../plugin/asm/FieldMetaGatheringVisitor.java | 59 +++++ .../asm/MethodMetaGatheringVisitor.java | 32 +++ .../config/plugin/asm/TransformerMode.java | 5 + .../plugin/asm/value/DiscoveredValue.java | 89 ++++++++ .../config/plugin/asm/value/KnownType.java | 41 ++++ .../config/plugin/asm/value/KnownTypes.java | 38 ++++ .../plugin/fmj/FabricModJsonTransformer.java | 39 ++++ .../plugin/util/ArchiveFileTreeElement.java | 78 +++++++ .../plugin/util/ClInitInjectVisitor.java | 26 +++ .../plugin/util/DefaultZipCompressor.java | 30 +++ .../DelegatingUncloseableOutputStream.java | 36 ++++ .../plugin/util/EnumerationSpliterator.java | 35 +++ .../config/plugin/util/GeneratorAdapter2.java | 115 ++++++++++ .../config/plugin/util/GradleVersionUtil.java | 21 ++ .../plugin/util/RelativeArchivePath.java | 32 +++ .../config/plugin/util/ZipCompressor.java | 11 + libjf-config-core-v1/build.gradle | 5 + .../libjf/config/api/v1}/Category.java | 3 +- .../libjf/config/api/v1}/ConfigCategory.java | 2 +- .../libjf/config/api/v1}/ConfigHolder.java | 3 +- .../libjf/config/api/v1}/ConfigInstance.java | 2 +- .../jfronny/libjf/config/api/v1}/Entry.java | 2 +- .../libjf/config/api/v1/EntryInfo.java | 90 ++++++++ .../jfronny/libjf/config/api/v1/JfConfig.java | 9 + .../libjf/config/api/v1/JfCustomConfig.java | 7 + .../jfronny/libjf/config/api/v1}/Preset.java | 2 +- .../libjf/config/api/v1}/Verifier.java | 2 +- .../config/api/v1}/dsl/CategoryBuilder.java | 10 +- .../config/api/v1}/dsl/ConfigBuilder.java | 4 +- .../jfronny/libjf/config/api/v1}/dsl/DSL.java | 6 +- .../libjf/config/api/v1}/type/Type.java | 25 ++- .../libjf/config/impl/AuxiliaryMetadata.java | 72 +++++++ .../libjf/config/impl/ConfigHolderImpl.java | 36 +++- .../config/impl/JfConfigWatchService.java | 2 +- .../config/impl/dsl/CategoryBuilderImpl.java | 31 ++- .../config/impl/dsl/ConfigBuilderImpl.java | 133 ++++++++++++ .../libjf/config/impl/dsl/DSLImpl.java | 8 +- .../config/impl/dsl/DslConfigCategory.java | 2 +- .../config/impl/dsl/DslConfigInstance.java | 2 +- .../libjf/config/impl/dsl/DslEntryInfo.java | 30 ++- .../config/impl/entrypoint/JfConfigSafe.java | 14 +- .../src/main/resources/fabric.mod.json | 10 +- .../libjf/config/test/TestConfigCustom.java | 63 ++++++ .../src/testmod/resources/fabric.mod.json | 0 libjf-config-legacy-shim/build.gradle | 10 + .../libjf/config/api/WidgetFactory.java | 1 + .../jfronny/libjf/config/api/Category.java | 9 + .../libjf/config/api/ConfigHolder.java | 21 ++ .../libjf/config/api/ConfigInstance.java | 33 +++ .../jfronny/libjf/config/api/Entry.java | 12 ++ .../jfronny/libjf/config/api/EntryInfo.java | 20 +- .../jfronny/libjf/config/api/JfConfig.java | 1 + .../jfronny/libjf/config/api/Preset.java | 9 + .../jfronny/libjf/config/api/Verifier.java | 9 + .../config/impl/legacy/ConfigHolderImpl.java | 119 ++++++++++ .../impl/legacy/ConfigInstanceImpl.java | 72 +++++++ .../config/impl/legacy/EntryInfoImpl.java | 65 ++++++ .../impl/legacy/LegacyRegistrationHook.java | 20 ++ .../src/main/resources/fabric.mod.json | 32 +++ .../libjf/config/test/legacy}/TestConfig.java | 2 +- .../lang/en_us.json | 13 ++ .../src/testmod/resources/fabric.mod.json | 21 ++ .../config/impl/reflect/ConfigProvider.java | 190 ---------------- .../entrypoint/JfConfigReflectSafe.java | 24 --- .../build.gradle | 4 +- .../v1/reflect/ReflectiveConfigBuilder.java | 10 + .../reflect/ReflectiveConfigBuilderImpl.java | 69 ++++++ .../entrypoint/JfConfigReflectSafe.java | 38 ++++ .../reflect/entrypoint/JfConfigUnsafe.java | 10 +- .../src/main/resources/fabric.mod.json | 6 +- .../libjf/config/test/reflect/TestConfig.java | 58 +++++ .../libjf-config-v0-testmod/lang/en_us.json | 0 .../src/testmod/resources/fabric.mod.json | 7 - .../build.gradle | 4 +- .../config/api/v1/ui/tiny/ConfigScreen.java | 11 + .../config/api/v1/ui/tiny/WidgetFactory.java | 12 ++ .../config/impl/ui/tiny/ModMenuAdapter.java | 9 +- .../impl/ui/tiny}/TinyConfigScreen.java | 14 +- .../config/impl/ui/tiny}/WidgetState.java | 6 +- .../tiny/entry}/EntryInfoWidgetBuilder.java | 11 +- .../impl/ui/tiny/entry/EntryListWidget.java | 18 +- .../ui/tiny}/presets/PresetListWidget.java | 2 +- .../impl/ui/tiny}/presets/PresetsScreen.java | 5 +- .../libjf-config-ui-tiny-v1}/lang/en_us.json | 0 .../src/main/resources/fabric.mod.json | 29 +++ .../config/impl/client/JfConfigClient.java | 15 -- .../libjf/config/api/JfCustomConfig.java | 7 - .../libjf/config/impl/AuxiliaryMetadata.java | 13 -- .../config/impl/dsl/ConfigBuilderImpl.java | 58 ----- .../libjf/config/test/TestConfigCustom.java | 31 --- .../build.gradle | 0 .../devutil/mixin/MinecraftClientMixin.java | 0 .../libjf/devutil/mixin/ProfileKeysMixin.java | 0 .../libjf-devutil-v0.client.mixins.json | 0 .../jfronny/libjf/devutil/DevUtilMain.java | 0 .../jfronny/libjf/devutil/NoOpUserApi.java | 0 .../devutil/mixin/ArgumentTypesMixin.java | 0 .../devutil/mixin/CommandManagerMixin.java | 0 .../src/main/resources/fabric.mod.json | 0 .../resources/libjf-devutil-v0.mixins.json | 0 libjf-translate-v1/build.gradle | 2 +- .../libjf/translate/impl/TranslateConfig.java | 24 ++- libjf-web-v0/build.gradle | 2 +- .../jfronny/libjf/web/impl/JfWebConfig.java | 23 +- .../src/main/resources/fabric.mod.json | 2 +- settings.gradle | 11 +- 118 files changed, 2638 insertions(+), 480 deletions(-) create mode 100644 extract.sh create mode 100644 libjf-config-commands-v1/build.gradle rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl => libjf-config-commands-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/commands}/JfConfigCommand.java (91%) create mode 100644 libjf-config-commands-v1/src/main/resources/fabric.mod.json create mode 100644 libjf-config-compiler-plugin/build.gradle create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectCopyAction.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigPlugin.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/StreamAction.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/AnnotationMetaGatheringVisitor.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/FieldMetaGatheringVisitor.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/MethodMetaGatheringVisitor.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/TransformerMode.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownType.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownTypes.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/fmj/FabricModJsonTransformer.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ArchiveFileTreeElement.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ClInitInjectVisitor.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DefaultZipCompressor.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DelegatingUncloseableOutputStream.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/EnumerationSpliterator.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GradleVersionUtil.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/RelativeArchivePath.java create mode 100644 libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ZipCompressor.java create mode 100644 libjf-config-core-v1/build.gradle rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/Category.java (73%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/ConfigCategory.java (94%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/ConfigHolder.java (85%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/ConfigInstance.java (85%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/Entry.java (89%) create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/EntryInfo.java create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfConfig.java create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfCustomConfig.java rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/Preset.java (84%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/Verifier.java (84%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/dsl/CategoryBuilder.java (74%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/dsl/ConfigBuilder.java (81%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/dsl/DSL.java (83%) rename {libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api => libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1}/type/Type.java (87%) create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java (56%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java (98%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java (84%) create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java (86%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java (98%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java (97%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java (83%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java (51%) rename {libjf-config-v1 => libjf-config-core-v1}/src/main/resources/fabric.mod.json (61%) create mode 100644 libjf-config-core-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java rename {libjf-config-v1 => libjf-config-core-v1}/src/testmod/resources/fabric.mod.json (100%) create mode 100644 libjf-config-legacy-shim/build.gradle rename {libjf-config-v1 => libjf-config-legacy-shim}/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java (97%) create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java rename {libjf-config-v1 => libjf-config-legacy-shim}/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java (90%) rename {libjf-config-v1 => libjf-config-legacy-shim}/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java (86%) create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigHolderImpl.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigInstanceImpl.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/EntryInfoImpl.java create mode 100644 libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/LegacyRegistrationHook.java create mode 100644 libjf-config-legacy-shim/src/main/resources/fabric.mod.json rename {libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect => libjf-config-legacy-shim/src/testmod/java/io/gitlab/jfronny/libjf/config/test/legacy}/TestConfig.java (96%) create mode 100644 libjf-config-legacy-shim/src/testmod/resources/assets/libjf-config-legacy-shim-testmod/lang/en_us.json create mode 100644 libjf-config-legacy-shim/src/testmod/resources/fabric.mod.json delete mode 100644 libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java delete mode 100644 libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java rename {libjf-config-reflect-v0 => libjf-config-reflect-v1}/build.gradle (55%) create mode 100644 libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/reflect/ReflectiveConfigBuilder.java create mode 100644 libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ReflectiveConfigBuilderImpl.java create mode 100644 libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java rename {libjf-config-reflect-v0 => libjf-config-reflect-v1}/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java (54%) rename {libjf-config-reflect-v0 => libjf-config-reflect-v1}/src/main/resources/fabric.mod.json (83%) create mode 100644 libjf-config-reflect-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java rename {libjf-config-reflect-v0 => libjf-config-reflect-v1}/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json (100%) rename {libjf-config-reflect-v0 => libjf-config-reflect-v1}/src/testmod/resources/fabric.mod.json (66%) rename {libjf-config-v1 => libjf-config-ui-tiny-v1}/build.gradle (61%) create mode 100644 libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/ConfigScreen.java create mode 100644 libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java rename libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/ModMenuAdapter.java (73%) rename {libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny}/TinyConfigScreen.java (89%) rename {libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny}/WidgetState.java (87%) rename {libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry}/EntryInfoWidgetBuilder.java (92%) rename libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java (83%) rename {libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny}/presets/PresetListWidget.java (96%) rename {libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui => libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny}/presets/PresetsScreen.java (91%) rename {libjf-config-v1/src/main/resources/assets/libjf-config-v0 => libjf-config-ui-tiny-v1/src/client/resources/assets/libjf-config-ui-tiny-v1}/lang/en_us.json (100%) create mode 100644 libjf-config-ui-tiny-v1/src/main/resources/fabric.mod.json delete mode 100644 libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java delete mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java delete mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java delete mode 100644 libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java delete mode 100644 libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java rename {libjf-devutil-v0 => libjf-devutil}/build.gradle (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/MinecraftClientMixin.java (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/client/resources/libjf-devutil-v0.client.mixins.json (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/main/java/io/gitlab/jfronny/libjf/devutil/DevUtilMain.java (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/main/java/io/gitlab/jfronny/libjf/devutil/NoOpUserApi.java (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/ArgumentTypesMixin.java (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/CommandManagerMixin.java (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/main/resources/fabric.mod.json (100%) rename {libjf-devutil-v0 => libjf-devutil}/src/main/resources/libjf-devutil-v0.mixins.json (100%) diff --git a/extract.sh b/extract.sh new file mode 100644 index 0000000..0e2f01a --- /dev/null +++ b/extract.sh @@ -0,0 +1,4 @@ +#!/bin/zsh +cd ~/Downloads +java -cp asm-9.3.jar:asm-analysis-9.3.jar:asm-tree-9.3.jar:asm-util-9.3.jar org.objectweb.asm.util.ASMifier /mnt/Projects/Minecraft/LibJF/libjf-config-core-v1/build/classes/java/testmod/io/gitlab/jfronny/libjf/config/test/TestConfigCustom\$SomeCategory.class > /mnt/Projects/Minecraft/LibJF/libjf-config-compiler-plugin/src/main/java/asm/io/gitlab/jfronny/libjf/config/test/TestConfigCustom\$SomeCategoryDump.java +java -cp asm-9.3.jar:asm-analysis-9.3.jar:asm-tree-9.3.jar:asm-util-9.3.jar org.objectweb.asm.util.ASMifier /mnt/Projects/Minecraft/LibJF/libjf-config-core-v1/build/classes/java/testmod/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.class > /mnt/Projects/Minecraft/LibJF/libjf-config-compiler-plugin/src/main/java/asm/io/gitlab/jfronny/libjf/config/test/TestConfigCustomDump.java \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index eb7f071..ed64ea7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ maven_group=io.gitlab.jfronny.libjf archive_base_name=libjf dev_only_module=libjf-devutil-v0 -non_mod_project=libjf-config-plugin +non_mod_project=libjf-config-compiler-plugin modrinth_id=libjf modrinth_optional_dependencies=fabric-api @@ -16,3 +16,4 @@ curseforge_optional_dependencies=fabric-api fabric_version=0.60.0+1.19.2 commons_version=2022.7.4+11-13-3 +bytebuddy_version=1.12.13 \ No newline at end of file diff --git a/libjf-config-commands-v1/build.gradle b/libjf-config-commands-v1/build.gradle new file mode 100644 index 0000000..e2b4f7f --- /dev/null +++ b/libjf-config-commands-v1/build.gradle @@ -0,0 +1,7 @@ +archivesBaseName = "libjf-config-reflect-v0" + +dependencies { + api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-config-core-v1", configuration: "dev") + include modImplementation(fabricApi.module("fabric-command-api-v2", "${project.fabric_version}")) +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java b/libjf-config-commands-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/commands/JfConfigCommand.java similarity index 91% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java rename to libjf-config-commands-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/commands/JfConfigCommand.java index c22545b..c0b9c1f 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigCommand.java +++ b/libjf-config-commands-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/commands/JfConfigCommand.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.impl; +package io.gitlab.jfronny.libjf.config.impl.commands; import com.mojang.brigadier.Command; import com.mojang.brigadier.arguments.*; @@ -8,12 +8,12 @@ import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import io.gitlab.jfronny.commons.throwable.ThrowingRunnable; import io.gitlab.jfronny.commons.throwable.ThrowingSupplier; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.api.type.Type; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.*; +import net.minecraft.text.Text; import java.util.function.Consumer; import java.util.function.Function; @@ -29,12 +29,12 @@ public class JfConfigCommand implements ModInitializer { LiteralArgumentBuilder c_config = literal("config") .requires((serverCommandSource) -> serverCommandSource.hasPermissionLevel(4)) .executes(context -> { - context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Loaded configs for:"), false); - ConfigHolder.getInstance().getRegistered().forEach((s, config) -> { - context.getSource().sendFeedback(Text.literal("- " + s), false); - }); - return Command.SINGLE_SUCCESS; - }); + context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Loaded configs for:"), false); + ConfigHolder.getInstance().getRegistered().forEach((s, config) -> { + context.getSource().sendFeedback(Text.literal("- " + s), false); + }); + return Command.SINGLE_SUCCESS; + }); LiteralArgumentBuilder c_reload = literal("reload").executes(context -> { ConfigHolder.getInstance().getRegistered().forEach((mod, config) -> config.load()); context.getSource().sendFeedback(Text.literal("[libjf-config-v1] Reloaded configs"), true); @@ -130,6 +130,7 @@ public class JfConfigCommand implements ModInitializer { private ArgumentType getType(EntryInfo info) { Type type = info.getValueType(); if (type.isInt()) return IntegerArgumentType.integer((int) info.getMinValue(), (int) info.getMaxValue()); + else if (type.isLong()) return LongArgumentType.longArg((long) info.getMinValue(), (long) info.getMaxValue()); else if (type.isFloat()) return FloatArgumentType.floatArg((float) info.getMinValue(), (float) info.getMaxValue()); else if (type.isDouble()) return DoubleArgumentType.doubleArg(info.getMinValue(), info.getMaxValue()); else if (type.isString()) return StringArgumentType.greedyString(); diff --git a/libjf-config-commands-v1/src/main/resources/fabric.mod.json b/libjf-config-commands-v1/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..703f7d4 --- /dev/null +++ b/libjf-config-commands-v1/src/main/resources/fabric.mod.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "id": "libjf-config-commands-v1", + "name": "LibJF Config Commands", + "version": "${version}", + "authors": ["JFronny"], + "contact": { + "website": "https://jfronny.gitlab.io", + "repo": "https://gitlab.com/jfmods/libjf" + }, + "license": "MIT", + "environment": "server", + "entrypoints": { + "main": ["io.gitlab.jfronny.libjf.config.impl.commands.JfConfigCommand"] + }, + "depends": { + "fabricloader": ">=0.12.0", + "minecraft": "*", + "fabric-command-api-v2": "*", + "libjf-base": ">=${version}", + "libjf-config-core-v1": ">=${version}" + }, + "custom": { + "modmenu": { + "badges": ["library"], + "parent": "libjf" + } + } +} diff --git a/libjf-config-compiler-plugin/build.gradle b/libjf-config-compiler-plugin/build.gradle new file mode 100644 index 0000000..7de2f53 --- /dev/null +++ b/libjf-config-compiler-plugin/build.gradle @@ -0,0 +1,60 @@ +plugins { + id 'java-gradle-plugin' + id 'maven-publish' +} + +group project.maven_group +version rootProject.ext.currentVer + +repositories { + mavenCentral() + maven { + name = 'JF Commons' + url = 'https://gitlab.com/api/v4/projects/35745143/packages/maven' + } +} + +dependencies { + compileOnly(gradleApi()) + implementation("org.apache.ant:ant:1.10.12") + implementation("io.gitlab.jfronny:commons-gson:$rootProject.commons_version") + implementation("org.ow2.asm:asm:9.3") + implementation("org.ow2.asm:asm-commons:9.3") + implementation("org.ow2.asm:asm-util:9.3") + implementation(project(":libjf-config-core-v1")) { + transitive(false) + } +} + +gradlePlugin { + plugins { + simplePlugin { + id = "io.gitlab.jfronny.libjf.libjf-config-compiler-plugin" + implementationClass = "io.gitlab.jfronny.libjf.config.plugin.ConfigPlugin" + } + } +} + +publishing { + repositories { + mavenLocal() + + if (project.hasProperty("maven")) { + maven { + url = project.getProperty("maven") + name = "dynamic" + + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = System.getenv().CI_JOB_TOKEN + } + authentication { + header(HttpHeaderAuthentication) + } + } + } + } +} + +tasks.publish.dependsOn(tasks.build) +rootProject.tasks.deployDebug.dependsOn(tasks.publish) \ No newline at end of file diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectCopyAction.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectCopyAction.java new file mode 100644 index 0000000..495156e --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectCopyAction.java @@ -0,0 +1,54 @@ +package io.gitlab.jfronny.libjf.config.plugin; + +import io.gitlab.jfronny.libjf.config.plugin.util.ZipCompressor; +import org.apache.tools.zip.ZipOutputStream; +import org.gradle.api.GradleException; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.internal.file.copy.CopyActionProcessingStream; +import org.gradle.api.tasks.WorkResult; +import org.gradle.api.tasks.WorkResults; +import org.objectweb.asm.Type; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ConfigInjectCopyAction implements CopyAction { + private final File zipFile; + private final ZipCompressor compressor; + private final String encoding; + private final boolean preserveFileTimestamps; + private final String modId; + private final Set knownConfigClasses = new HashSet<>(); + + public ConfigInjectCopyAction(File zipFile, + ZipCompressor compressor, + String encoding, + boolean preserveFileTimestamps, + String modId) { + this.zipFile = zipFile; + this.compressor = compressor; + this.encoding = encoding; + this.preserveFileTimestamps = preserveFileTimestamps; + this.modId = modId; + } + + @Override + public WorkResult execute(CopyActionProcessingStream stream) { + try (ZipOutputStream zipOutStr = compressor.createArchiveOutputStream(zipFile)) { + if (encoding != null) { + zipOutStr.setEncoding(encoding); + } + AtomicBoolean fmjFound = new AtomicBoolean(false); + stream.process(details -> fmjFound.compareAndSet(false, + new StreamAction(zipOutStr, zipFile, preserveFileTimestamps, modId, knownConfigClasses) + .processFile(details) + )); + } catch (IOException e) { + throw new GradleException("Could not create ZIP " + zipFile, e); + } + return WorkResults.didWork(true); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java new file mode 100644 index 0000000..a9feadc --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java @@ -0,0 +1,40 @@ +package io.gitlab.jfronny.libjf.config.plugin; + +import io.gitlab.jfronny.libjf.config.plugin.util.GradleVersionUtil; +import io.gitlab.jfronny.libjf.config.plugin.util.ZipCompressor; +import org.gradle.api.file.DuplicatesStrategy; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.bundling.Jar; + +public abstract class ConfigInjectTask extends Jar { + @Input + public abstract Property getModId(); + private final GradleVersionUtil versionUtil; + + public ConfigInjectTask() { + super(); + setDuplicatesStrategy(DuplicatesStrategy.FAIL); + versionUtil = new GradleVersionUtil(getProject().getGradle().getGradleVersion()); + //TODO ensure this is unneeded +// setManifest(new DefaultInheritManifest(getServices().get(FileResolver.class))); + } + + @Override + protected CopyAction createCopyAction() { + return new ConfigInjectCopyAction( + getArchiveFile().get().getAsFile(), + getInternalCompressor(), + this.getMetadataCharset(), + isPreserveFileTimestamps(), + getModId().get() + ); + } + + @Internal + protected ZipCompressor getInternalCompressor() { + return versionUtil.getInternalCompressor(getEntryCompression(), this); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigPlugin.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigPlugin.java new file mode 100644 index 0000000..496bbf9 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigPlugin.java @@ -0,0 +1,11 @@ +package io.gitlab.jfronny.libjf.config.plugin; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class ConfigPlugin implements Plugin { + @Override + public void apply(Project project) { + + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/StreamAction.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/StreamAction.java new file mode 100644 index 0000000..3ce45bf --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/StreamAction.java @@ -0,0 +1,189 @@ +package io.gitlab.jfronny.libjf.config.plugin; + +import io.gitlab.jfronny.gson.stream.JsonReader; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer; +import io.gitlab.jfronny.libjf.config.plugin.fmj.FabricModJsonTransformer; +import io.gitlab.jfronny.libjf.config.plugin.util.*; +import org.apache.tools.zip.*; +import org.gradle.api.GradleException; +import org.gradle.api.file.FileCopyDetails; +import org.objectweb.asm.*; +import org.objectweb.asm.util.CheckClassAdapter; + +import java.io.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; + +public class StreamAction { + private static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = (new GregorianCalendar(1980, 1, 1, 0, 0, 0)).getTimeInMillis(); + private final ZipOutputStream zipOutStr; + private final File zipFile; + private final boolean preserveFileTimestamps; + private final String modId; + private final Set knownConfigClasses; + + public StreamAction(ZipOutputStream zipOutStr, File zipFile, boolean preserveFileTimestamps, String modId, Set knownConfigClasses) { + this.zipOutStr = zipOutStr; + this.zipFile = zipFile; + this.preserveFileTimestamps = preserveFileTimestamps; + this.modId = modId; + this.knownConfigClasses = knownConfigClasses; + } + + public boolean processFile(FileCopyDetails details) { + try { + if (details.isDirectory()) { + visitDirectory(details); + return false; + } else { + return visitFile(details); + } + } catch (Exception e) { + throw new GradleException("Could not add " + details + " to ZIP " + zipFile, e); + } + } + + private void visitDirectory(FileCopyDetails details) throws IOException { + String path = details.getRelativePath().getPathString() + "/"; + ZipEntry archiveEntry = new ZipEntry(path); + archiveEntry.setTime(getArchiveTimeFor(details.getLastModified())); + archiveEntry.setUnixMode(UnixStat.DIR_FLAG | details.getMode()); + zipOutStr.putNextEntry(archiveEntry); + zipOutStr.closeEntry(); + } + + private boolean visitFile(FileCopyDetails details) throws IOException { + if (details.getPath().endsWith(".jar")) { + return processArchive(details); + } else if (details.getPath().endsWith(".class")) { + processClass(details); + } else if (details.getPath().equals("fabric.mod.json")) { + processFMJ(details); + return true; + } else { + ZipEntry archiveEntry = new ZipEntry(details.getRelativePath().getPathString()); + archiveEntry.setTime(getArchiveTimeFor(details.getLastModified())); + archiveEntry.setUnixMode(UnixStat.FILE_FLAG | details.getMode()); + zipOutStr.putNextEntry(archiveEntry); + details.copyTo(zipOutStr); + zipOutStr.closeEntry(); + } + return false; + } + + private boolean processArchive(FileCopyDetails details) throws IOException { + try (ZipFile archive = new ZipFile(details.getFile())) { + AtomicBoolean fmjFound = new AtomicBoolean(false); + EnumerationSpliterator.stream(archive.getEntries()) + .map(RelativeArchivePath::new) + .map(ArchiveFileTreeElement::new) + .filter(it -> it.getRelativePath().isFile()) + .forEach(archiveElement -> { + fmjFound.compareAndSet(false, visitArchiveFile(archiveElement, archive)); + }); + return fmjFound.get(); + } + } + + private void visitArchiveDirectory(RelativeArchivePath archiveDir) throws IOException { + zipOutStr.putNextEntry(archiveDir.entry); + zipOutStr.closeEntry(); + } + + private boolean visitArchiveFile(ArchiveFileTreeElement archiveFile, ZipFile archive) { + RelativeArchivePath archiveFilePath = archiveFile.getRelativePath(); + try { + if (archiveFile.isClassFile()) { + processClass(archiveFilePath, archive); + } else if (archiveFilePath.getPathString().equals("fabric.mod.json")) { + processFMJ(archiveFilePath, archive); + return true; + } else { + copyArchiveEntry(archiveFilePath, archive); + } + return false; + } catch (IOException e) { + throw new GradleException("Could not read archive entry " + archiveFilePath.getPathString(), e); + } + } + + private void copyArchiveEntry(RelativeArchivePath archiveFile, ZipFile archive) throws IOException { + ZipEntry entry = new ZipEntry(archiveFile.entry.getName()); + entry.setTime(getArchiveTimeFor(archiveFile.entry.getTime())); + RelativeArchivePath path = new RelativeArchivePath(entry); + addParentDirectories(path); + zipOutStr.putNextEntry(path.entry); + try (InputStream is = archive.getInputStream(archiveFile.entry)) { + byte[] buffer = new byte[1024]; + int n; + while (-1 != (n = is.read(buffer))) { + zipOutStr.write(buffer, 0, n); + } + } + zipOutStr.closeEntry(); + } + + private void addParentDirectories(RelativeArchivePath file) throws IOException { + if (file != null) { + addParentDirectories(file.getParent()); + if (!file.isFile()) { + visitArchiveDirectory(file); + } + } + } + + private void processClass(RelativeArchivePath file, ZipFile archive) throws IOException { + ZipEntry zipEntry = new ZipEntry(file.getPathString()); + addParentDirectories(new RelativeArchivePath(zipEntry)); + processClass(archive.getInputStream(file.entry), file.getPathString(), file.entry.getTime()); + } + + private void processClass(FileCopyDetails details) throws IOException { + try (InputStream is = new BufferedInputStream(new FileInputStream(details.getFile()))) { + processClass(is, details.getPath(), details.getLastModified()); + } + } + + private void processClass(InputStream classInputStream, String path, long lastModified) throws IOException { + final ClassReader reader = new ClassReader(classInputStream); + final ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); + reader.accept(new CheckClassAdapter( + new ConfigInjectClassTransformer(modId, writer, knownConfigClasses) + ), ClassReader.EXPAND_FRAMES); + ZipEntry archiveEntry = new ZipEntry(path); + archiveEntry.setTime(getArchiveTimeFor(lastModified)); + zipOutStr.putNextEntry(archiveEntry); + zipOutStr.write(writer.toByteArray()); + zipOutStr.closeEntry(); + } + + private void processFMJ(RelativeArchivePath file, ZipFile archive) throws IOException { + ZipEntry zipEntry = new ZipEntry(file.getPathString()); + addParentDirectories(new RelativeArchivePath(zipEntry)); + processFMJ(archive.getInputStream(file.entry), file.getPathString(), file.entry.getTime()); + } + + private void processFMJ(FileCopyDetails details) throws IOException { + try (InputStream is = new BufferedInputStream(new FileInputStream(details.getFile()))) { + processFMJ(is, details.getPath(), details.getLastModified()); + } + } + + private void processFMJ(InputStream fmj, String path, long lastModified) throws IOException { + ZipEntry archiveEntry = new ZipEntry(path); + archiveEntry.setTime(getArchiveTimeFor(lastModified)); + zipOutStr.putNextEntry(archiveEntry); + // Leave this closeable open, as everything else will break the writer + + try (JsonReader reader = new JsonReader(new InputStreamReader(fmj)); + JsonWriter writer = new JsonWriter(new OutputStreamWriter(new DelegatingUncloseableOutputStream(zipOutStr)))) { + FabricModJsonTransformer.transform(reader, writer, knownConfigClasses); + } + zipOutStr.closeEntry(); + } + + private long getArchiveTimeFor(long timestamp) { + return preserveFileTimestamps ? timestamp : CONSTANT_TIME_FOR_ZIP_ENTRIES; + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/AnnotationMetaGatheringVisitor.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/AnnotationMetaGatheringVisitor.java new file mode 100644 index 0000000..af2b1b2 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/AnnotationMetaGatheringVisitor.java @@ -0,0 +1,40 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm; + +import org.gradle.api.GradleException; +import org.objectweb.asm.AnnotationVisitor; + +import java.util.List; + +import static org.objectweb.asm.Opcodes.*; + +public class AnnotationMetaGatheringVisitor extends AnnotationVisitor { + private final List referencedConfigs; + + public AnnotationMetaGatheringVisitor(AnnotationVisitor annotationVisitor, List referencedConfigs) { + super(ASM9, annotationVisitor); + this.referencedConfigs = referencedConfigs; + } + + @Override + public AnnotationVisitor visitArray(String name) { + return switch (name) { + case "referencedConfigs" -> new ArrayVisitor<>(super.visitArray(name), referencedConfigs); + default -> throw new GradleException("Unknown field in category or JfConfig annotation: " + name); + }; + } + + private static class ArrayVisitor extends AnnotationVisitor { + private final List target; + + protected ArrayVisitor(AnnotationVisitor annotationVisitor, List target) { + super(ASM9, annotationVisitor); + this.target = target; + } + + @Override + public void visit(String name, Object value) { + super.visit(name, value); + target.add((T) value); + } + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java new file mode 100644 index 0000000..9160f02 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java @@ -0,0 +1,204 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm; + +import io.gitlab.jfronny.libjf.config.api.v1.Category; +import io.gitlab.jfronny.libjf.config.api.v1.JfConfig; +import io.gitlab.jfronny.libjf.config.plugin.util.ClInitInjectVisitor; +import io.gitlab.jfronny.libjf.config.plugin.util.GeneratorAdapter2; +import io.gitlab.jfronny.libjf.config.plugin.asm.value.DiscoveredValue; +import org.gradle.api.GradleException; +import org.objectweb.asm.*; +import org.objectweb.asm.commons.GeneratorAdapter; +import org.objectweb.asm.commons.Method; + +import java.util.*; + +import static io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes.*; +import static org.objectweb.asm.Opcodes.*; +import static org.objectweb.asm.Type.*; + +public class ConfigInjectClassTransformer extends ClassVisitor { + // Field names + public static final String PREFIX = "libjf$config$"; + public static final String INIT_METHOD = PREFIX + "clinit"; + public static final String REGISTER_METHOD = PREFIX + "register"; + public static final String BUILDER_ROOT = PREFIX + "root"; + public static final String CLINIT = ""; + // Transformation metadata + public final String modId; + public Type current; + private final Set knownConfigClasses; + // Transformer state + private final List categories = new LinkedList<>(); + private final List referencedConfigs = new LinkedList<>(); + private final List presets = new LinkedList<>(); + private final List verifiers = new LinkedList<>(); + private final List values = new LinkedList<>(); + private boolean initFound = false; + private TransformerMode mode = TransformerMode.OTHER; + + public ConfigInjectClassTransformer(String modId, ClassVisitor cw, Set knownConfigClasses) { + super(ASM9, cw); + this.modId = modId; + this.knownConfigClasses = knownConfigClasses; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + this.current = Type.getType('L' + name + ';'); + mode = TransformerMode.OTHER; + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (descriptor.equals(Type.getDescriptor(Category.class)) && mode == TransformerMode.OTHER) { + mode = TransformerMode.CONFIG_CATEGORY; + return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs); + } + if (descriptor.equals(Type.getDescriptor(JfConfig.class)) && mode == TransformerMode.OTHER) { + mode = TransformerMode.CONFIG_ROOT; + knownConfigClasses.add(current); + return new AnnotationMetaGatheringVisitor(super.visitAnnotation(descriptor, visible), referencedConfigs); + } + return super.visitAnnotation(descriptor, visible); + } + + @Override + public void visitNestMember(String nestMember) { + if (mode != TransformerMode.OTHER) { + categories.add(nestMember); + } + super.visitNestMember(nestMember); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (mode == TransformerMode.CONFIG_ROOT) { + if ("".equals(name)) { + initFound = true; + return new ClInitInjectVisitor( + super.visitMethod(access, name, descriptor, signature, exceptions), + current.getInternalName(), + INIT_METHOD, + "()V" + ); + } + } + if (mode != TransformerMode.OTHER) { + if (name.startsWith(PREFIX)) { + throw new GradleException("This class declares methods generated by this plugin manually. Do not transform classes twice!"); + } + } + if ((access & ACC_STATIC) == ACC_STATIC) { + // Possibly add verifier or preset + return new MethodMetaGatheringVisitor( + super.visitMethod(access, name, descriptor, signature, exceptions), + name, presets, verifiers); + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + if ((access & ACC_STATIC) == ACC_STATIC) { + return new FieldMetaGatheringVisitor(super.visitField(access, name, descriptor, signature, value), name, values, Type.getType(descriptor)); + } + return super.visitField(access, name, descriptor, signature, value); + } + + @Override + public void visitEnd() { + if (mode == TransformerMode.CONFIG_ROOT) { + if (!initFound) { + // Generate if missing + GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, CLINIT, "()V", null, null); + m.invokeStatic(current, new Method(INIT_METHOD, "()V")); + m.returnValue(); + m.endMethod(); + } + // Generate main method + { + GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, INIT_METHOD, "()V", null, null); + // Migrate paths + m.invokeIStatic(CONFIG_HOLDER_TYPE, new Method("getInstance", CONFIG_HOLDER_TYPE, new Type[0])); + m.push(modId); + m.invokeInterface(CONFIG_HOLDER_TYPE, new Method("migrateFiles", "(Ljava/lang/String;)V")); + // Invoke DSL + m.push(modId); + m.invokeIStatic(DSL_TYPE, new Method("create", DSL_DEFAULTED_TYPE, new Type[]{STRING_TYPE})); + m.lambda("apply", + getMethodDescriptor(CONFIG_BUILDER_FUNCTION_TYPE), + getMethodType(CONFIG_BUILDER_TYPE, CONFIG_BUILDER_TYPE), + current.getInternalName(), + BUILDER_ROOT); + + m.invokeInterface(DSL_DEFAULTED_TYPE, new Method("config", CONFIG_INSTANCE_TYPE, new Type[]{CONFIG_BUILDER_FUNCTION_TYPE})); + m.pop(); + m.returnValue(); + m.endMethod(); + } + // Generate register method for impl + // This method does nothing but is needed to properly implement the interface + // This also ensures the static initializer is called + { + GeneratorAdapter2 m = method(ACC_PUBLIC | ACC_STATIC, REGISTER_METHOD, getMethodDescriptor(VOID_TYPE, DSL_DEFAULTED_TYPE), null, null); + m.returnValue(); + m.endMethod(); + } + } + if (mode != TransformerMode.OTHER) { + boolean root = mode == TransformerMode.CONFIG_ROOT; + Type builderType = root ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE; + GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, BUILDER_ROOT, getMethodDescriptor(builderType, builderType), null, null); + m.loadArg(0); + for (String name : referencedConfigs) { + m.push(name); + dslInvoke(m, "referenceConfig", CATEGORY_BUILDER_TYPE, STRING_TYPE); + } + for (String name : categories) { + m.push(camelCase(name.substring(current.getInternalName().length()))); + m.lambda("apply", + getMethodDescriptor(CATEGORY_BUILDER_FUNCTION_TYPE), + getMethodType(CATEGORY_BUILDER_TYPE, CATEGORY_BUILDER_TYPE), + name, + BUILDER_ROOT); + dslInvoke(m, "category", CATEGORY_BUILDER_TYPE, STRING_TYPE, CATEGORY_BUILDER_FUNCTION_TYPE); + } + for (String verifier : verifiers) { + m.runnable(current.getInternalName(), verifier); + dslInvoke(m, "addVerifier", CATEGORY_BUILDER_TYPE, RUNNABLE_TYPE); + } + for (String preset : presets) { + m.push(preset); + m.runnable(current.getInternalName(), preset); + dslInvoke(m, "addPreset", CATEGORY_BUILDER_TYPE, STRING_TYPE, RUNNABLE_TYPE); + } + for (DiscoveredValue value : values) { + value.generateRegistration(m, this); + } + m.returnValue(); + m.endMethod(); + for (DiscoveredValue value : values) { + value.generateLamdaMethods(this); + } + } + super.visitEnd(); + } + + public void dslInvoke(GeneratorAdapter m, String name, Type returnType, Type... arguments) { + boolean root = mode == TransformerMode.CONFIG_ROOT; + Type builderType = root ? CONFIG_BUILDER_TYPE : CATEGORY_BUILDER_TYPE; + m.invokeInterface(builderType, new Method(name, returnType, arguments)); + if (root) m.checkCast(CONFIG_BUILDER_TYPE); + } + + private String camelCase(String s) { + return Character.toLowerCase(s.charAt(0)) + s.substring(1); + } + + public GeneratorAdapter2 method(int access, String name, String descriptor, String signature, String[] exceptions) { + //TODO re-enable to hide generated code + //access |= ACC_SYNTHETIC; + return new GeneratorAdapter2(super.visitMethod(access, name, descriptor, signature, exceptions), access, name, descriptor); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/FieldMetaGatheringVisitor.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/FieldMetaGatheringVisitor.java new file mode 100644 index 0000000..9183093 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/FieldMetaGatheringVisitor.java @@ -0,0 +1,59 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm; + +import io.gitlab.jfronny.libjf.config.api.v1.Entry; +import io.gitlab.jfronny.libjf.config.plugin.asm.value.DiscoveredValue; +import org.objectweb.asm.*; + +import java.util.List; + +import static org.objectweb.asm.Opcodes.*; + +public class FieldMetaGatheringVisitor extends FieldVisitor { + private final String name; + private final List values; + private final Type type; + + protected FieldMetaGatheringVisitor(FieldVisitor fieldVisitor, String name, List values, Type type) { + super(ASM9, fieldVisitor); + this.name = name; + this.values = values; + this.type = type; + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (descriptor.equals(Type.getDescriptor(Entry.class))) { + return new MetaGatheringAnnotationVisitor(super.visitAnnotation(descriptor, visible)); + } + return super.visitAnnotation(descriptor, visible); + } + + public class MetaGatheringAnnotationVisitor extends AnnotationVisitor { + private double min = Double.NEGATIVE_INFINITY; + private double max = Double.POSITIVE_INFINITY; + protected MetaGatheringAnnotationVisitor(AnnotationVisitor annotationVisitor) { + super(ASM9, annotationVisitor); + } + + @Override + public void visit(String name, Object value) { + super.visit(name, value); + switch (name) { + case "min" -> { + min = (Double)value; + } + case "max" -> { + max = (Double)value; + } + case "width" -> {} + default -> throw new IllegalArgumentException("Unexpected name in @Entry annotation: " + name); + } + } + + @Override + public void visitEnd() { + super.visitEnd(); + values.add(new DiscoveredValue(name, min, max, type)); + } + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/MethodMetaGatheringVisitor.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/MethodMetaGatheringVisitor.java new file mode 100644 index 0000000..7945f3c --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/MethodMetaGatheringVisitor.java @@ -0,0 +1,32 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm; + +import io.gitlab.jfronny.libjf.config.api.v1.Preset; +import io.gitlab.jfronny.libjf.config.api.v1.Verifier; +import org.objectweb.asm.*; + +import java.util.List; + +import static org.objectweb.asm.Opcodes.*; + +public class MethodMetaGatheringVisitor extends MethodVisitor { + private final String name; + private final List presets; + private final List verifiers; + protected MethodMetaGatheringVisitor(MethodVisitor methodVisitor, String name, List presets, List verifiers) { + super(ASM9, methodVisitor); + this.name = name; + this.presets = presets; + this.verifiers = verifiers; + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (descriptor.equals(Type.getDescriptor(Preset.class))) { + presets.add(name); + } + if (descriptor.equals(Type.getDescriptor(Verifier.class))) { + verifiers.add(name); + } + return super.visitAnnotation(descriptor, visible); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/TransformerMode.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/TransformerMode.java new file mode 100644 index 0000000..b0de3fd --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/TransformerMode.java @@ -0,0 +1,5 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm; + +public enum TransformerMode { + CONFIG_ROOT, CONFIG_CATEGORY, OTHER +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java new file mode 100644 index 0000000..ed80eb1 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java @@ -0,0 +1,89 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm.value; + +import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer; +import io.gitlab.jfronny.libjf.config.plugin.util.GeneratorAdapter2; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.Method; + +import static io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes.*; +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Type.*; + +public class DiscoveredValue { + private static final String PREFIX = ConfigInjectClassTransformer.PREFIX; + + public final String name; + public final double min; + public final double max; + public final Type aType; + public final KnownType type; + + public DiscoveredValue(String name, double min, double max, Type type) { + this.name = name; + this.min = min; + this.max = max; + this.aType = type; + this.type = KnownType.of(type); + } + + public void generateRegistration(GeneratorAdapter2 m, ConfigInjectClassTransformer t) { + if (type != KnownType.OBJECT) { + m.push(name); + m.getStatic(t.current, name, aType); + m.unbox(type.unboxed); // Unbox (as parameter is unboxed) or leave as is (if target is unboxed) + } + switch (type) { + case INT, LONG, FLOAT, DOUBLE -> { + m.push(min); + m.push(max); + m.supplier(t.current.getInternalName(), gName(), gDesc()); + m.consumer(t.current.getInternalName(), sName(), sDesc()); + t.dslInvoke(m, "value", CATEGORY_BUILDER_TYPE, STRING_TYPE, type.unboxed, DOUBLE_TYPE, DOUBLE_TYPE, SUPPLIER_TYPE, CONSUMER_TYPE); + } + case BOOLEAN, STRING -> { + m.supplier(t.current.getInternalName(), gName(), gDesc()); + m.consumer(t.current.getInternalName(), sName(), sDesc()); + t.dslInvoke(m, "value", CATEGORY_BUILDER_TYPE, STRING_TYPE, type.unboxed, SUPPLIER_TYPE, CONSUMER_TYPE); + } + case OBJECT -> { + System.err.println("WARNING: Attempted to use unsupported type in config. The entry \"" + name + "\" will fall back to reflective runtime access!"); + m.push(t.current); + m.push(name); + m.invokeIStatic(ENTRY_INFO_TYPE, new Method("ofField", ENTRY_INFO_TYPE, new Type[]{CLASS_TYPE, STRING_TYPE})); + m.invokeInterface(CATEGORY_BUILDER_TYPE, new Method("value", CATEGORY_BUILDER_TYPE, new Type[]{ENTRY_INFO_TYPE})); + } + } + } + + public void generateLamdaMethods(ConfigInjectClassTransformer t) { + GeneratorAdapter2 m = t.method(ACC_PRIVATE | ACC_STATIC, gName(), gDesc().getInternalName(), null, null); + m.getStatic(t.current, name, aType); + m.box(aType); // Box target or leave as is (if target is boxed) + m.returnValue(); + m.endMethod(); + + m = t.method(ACC_PRIVATE | ACC_STATIC, sName(), sDesc().getInternalName(), null, null); + m.loadArg(0); + m.unbox(aType); // Unbox to target type or leave as is (if target is boxed) + m.putStatic(t.current, name, aType); + m.returnValue(); + m.endMethod(); + } + + private String gName() { + return PREFIX + "get$" + name; + } + + private Type gDesc() { + return getMethodType(type.boxed); + } + + private String sName() { + return PREFIX + "set$" + name; + } + + private Type sDesc() { + return getMethodType(VOID_TYPE, type.boxed); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownType.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownType.java new file mode 100644 index 0000000..9eab27b --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownType.java @@ -0,0 +1,41 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm.value; + +import org.objectweb.asm.Type; + +import java.util.HashMap; +import java.util.Map; + +import static io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes.*; +import static org.objectweb.asm.Type.*; + +public enum KnownType { + INT(INT_BOX_TYPE, INT_TYPE), + LONG(LONG_BOX_TYPE, LONG_TYPE), + FLOAT(FLOAT_BOX_TYPE, FLOAT_TYPE), + DOUBLE(DOUBLE_BOX_TYPE, DOUBLE_TYPE), + STRING(STRING_TYPE, STRING_TYPE), + BOOLEAN(BOOLEAN_BOX_TYPE, BOOLEAN_TYPE), + OBJECT(OBJECT_TYPE, OBJECT_TYPE); + + public final Type boxed; + public final Type unboxed; + private static final Map t2t = new HashMap<>(); + + KnownType(Type boxed, Type unboxed) { + this.boxed = boxed; + this.unboxed = unboxed; + } + + static { + for (KnownType value : values()) { + if (value != OBJECT) { + t2t.put(value.boxed, value); + t2t.put(value.unboxed, value); + } + } + } + + public static KnownType of(Type type) { + return t2t.getOrDefault(type, OBJECT); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownTypes.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownTypes.java new file mode 100644 index 0000000..5df393b --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/KnownTypes.java @@ -0,0 +1,38 @@ +package io.gitlab.jfronny.libjf.config.plugin.asm.value; + +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.*; +import org.objectweb.asm.Type; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class KnownTypes { + // DSL types + public static final Type CONFIG_BUILDER_TYPE = Type.getType(ConfigBuilder.class); + public static final Type CONFIG_BUILDER_FUNCTION_TYPE = Type.getType(ConfigBuilder.ConfigBuilderFunction.class); + public static final Type CATEGORY_BUILDER_TYPE = Type.getType(CategoryBuilder.class); + public static final Type CATEGORY_BUILDER_FUNCTION_TYPE = Type.getType(CategoryBuilder.CategoryBuilderFunction.class); + public static final Type DSL_TYPE = Type.getType(DSL.class); + public static final Type DSL_DEFAULTED_TYPE = Type.getType(DSL.Defaulted.class); + public static final Type CONFIG_HOLDER_TYPE = Type.getType(ConfigHolder.class); + public static final Type CONFIG_INSTANCE_TYPE = Type.getType(ConfigInstance.class); + public static final Type ENTRY_INFO_TYPE = Type.getType(EntryInfo.class); + // Boxes + public static final Type INT_BOX_TYPE = Type.getType(Integer.class); + public static final Type LONG_BOX_TYPE = Type.getType(Long.class); + public static final Type FLOAT_BOX_TYPE = Type.getType(Float.class); + public static final Type DOUBLE_BOX_TYPE = Type.getType(Double.class); + public static final Type BOOLEAN_BOX_TYPE = Type.getType(Boolean.class); + public static final Type CHARACTER_BOX_TYPE = Type.getType(Character.class); + public static final Type SHORT_BOX_TYPE = Type.getType(Short.class); + public static final Type BYTE_BOX_TYPE = Type.getType(Byte.class); + // Additional + public static final Type STRING_TYPE = Type.getType(String.class); + public static final Type OBJECT_TYPE = Type.getType(Object.class); + public static final Type RUNNABLE_TYPE = Type.getType(Runnable.class); + public static final Type SUPPLIER_TYPE = Type.getType(Supplier.class); + public static final Type CONSUMER_TYPE = Type.getType(Consumer.class); + public static final Type NUMBER_TYPE = Type.getType(Number.class); + public static final Type CLASS_TYPE = Type.getType(Class.class); +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/fmj/FabricModJsonTransformer.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/fmj/FabricModJsonTransformer.java new file mode 100644 index 0000000..4716d05 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/fmj/FabricModJsonTransformer.java @@ -0,0 +1,39 @@ +package io.gitlab.jfronny.libjf.config.plugin.fmj; + +import io.gitlab.jfronny.gson.*; +import io.gitlab.jfronny.gson.stream.JsonReader; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; +import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer; +import org.objectweb.asm.Type; + +import java.util.*; +import java.util.function.Predicate; + +public class FabricModJsonTransformer { + private static final Gson INPUT_GSON = new GsonBuilder().setLenient().create(); + private static final Gson OUTPUT_GSON = new GsonBuilder().create(); + private static final String ENTRYPOINTS = "entrypoints"; + private static final String LIBJF_CONFIG = ConfigHolderImpl.MODULE_ID; + public static void transform(JsonReader reader, JsonWriter writer, Set configClasses) { + JsonObject fmj = INPUT_GSON.fromJson(reader, JsonElement.class).getAsJsonObject(); + if (!fmj.has(ENTRYPOINTS)) fmj.add(ENTRYPOINTS, new JsonObject()); + JsonObject entrypoints = fmj.get(ENTRYPOINTS).getAsJsonObject(); + if (!entrypoints.has(LIBJF_CONFIG)) entrypoints.add(LIBJF_CONFIG, new JsonArray()); + JsonArray libjfConfig = entrypoints.getAsJsonArray(LIBJF_CONFIG).getAsJsonArray(); + for (Type klazz : configClasses) { + // Remove references to class + Iterator each = libjfConfig.iterator(); + while(each.hasNext()) { + JsonElement element = each.next(); + if (element.isJsonPrimitive() && element.getAsString().equals(klazz.getClassName())) { + each.remove(); + } + } + + // Add reference to init method + libjfConfig.add(klazz.getClassName() + "::" + ConfigInjectClassTransformer.REGISTER_METHOD); + } + OUTPUT_GSON.toJson(fmj, writer); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ArchiveFileTreeElement.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ArchiveFileTreeElement.java new file mode 100644 index 0000000..d3bf44c --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ArchiveFileTreeElement.java @@ -0,0 +1,78 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.file.RelativePath; +import org.gradle.api.internal.file.DefaultFileTreeElement; + +import java.io.*; + +public class ArchiveFileTreeElement implements FileTreeElement { + private final RelativeArchivePath archivePath; + + public ArchiveFileTreeElement(RelativeArchivePath archivePath) { + this.archivePath = archivePath; + } + + public boolean isClassFile() { + return archivePath.isClassFile(); + } + + @Override + public File getFile() { + return null; + } + + @Override + public boolean isDirectory() { + return archivePath.entry.isDirectory(); + } + + @Override + public long getLastModified() { + return archivePath.entry.getLastModifiedDate().getTime(); + } + + @Override + public long getSize() { + return archivePath.entry.getSize(); + } + + @Override + public InputStream open() { + return null; + } + + @Override + public void copyTo(OutputStream outputStream) { + + } + + @Override + public boolean copyTo(File file) { + return false; + } + + @Override + public String getName() { + return archivePath.getPathString(); + } + + @Override + public String getPath() { + return archivePath.getLastName(); + } + + @Override + public RelativeArchivePath getRelativePath() { + return archivePath; + } + + @Override + public int getMode() { + return archivePath.entry.getUnixMode(); + } + + public FileTreeElement asFileTreeElement() { + return new DefaultFileTreeElement(null, new RelativePath(!isDirectory(), archivePath.getSegments()), null, null); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ClInitInjectVisitor.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ClInitInjectVisitor.java new file mode 100644 index 0000000..b2d7444 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ClInitInjectVisitor.java @@ -0,0 +1,26 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import org.objectweb.asm.MethodVisitor; + +import static org.objectweb.asm.Opcodes.*; + +public class ClInitInjectVisitor extends MethodVisitor { + private final String owner; + private final String name; + private final String descriptor; + + public ClInitInjectVisitor(MethodVisitor mw, String owner, String name, String descriptor) { + super(ASM9, mw); + this.owner = owner; + this.name = name; + this.descriptor = descriptor; + } + + @Override + public void visitInsn(int opcode) { + if (opcode == RETURN) { + super.visitMethodInsn(INVOKESTATIC, owner, name, descriptor, false); + } + super.visitInsn(opcode); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DefaultZipCompressor.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DefaultZipCompressor.java new file mode 100644 index 0000000..a056884 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DefaultZipCompressor.java @@ -0,0 +1,30 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import org.apache.tools.zip.Zip64Mode; +import org.apache.tools.zip.ZipOutputStream; +import org.gradle.api.UncheckedIOException; + +import java.io.File; + +public class DefaultZipCompressor implements ZipCompressor { + private final int entryCompressionMethod; + private final Zip64Mode zip64Mode; + + public DefaultZipCompressor(boolean allowZip64Mode, int entryCompressionMethod) { + this.entryCompressionMethod = entryCompressionMethod; + this.zip64Mode = allowZip64Mode ? Zip64Mode.AsNeeded : Zip64Mode.Never; + } + + @Override + public ZipOutputStream createArchiveOutputStream(File destination) { + try { + ZipOutputStream zipOutputStream = new ZipOutputStream(destination); + zipOutputStream.setUseZip64(zip64Mode); + zipOutputStream.setMethod(entryCompressionMethod); + return zipOutputStream; + } catch (Exception e) { + String message = String.format("Unable to create ZIP output stream for file %s.", destination); + throw new UncheckedIOException(message, e); + } + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DelegatingUncloseableOutputStream.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DelegatingUncloseableOutputStream.java new file mode 100644 index 0000000..abee81c --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/DelegatingUncloseableOutputStream.java @@ -0,0 +1,36 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import java.io.IOException; +import java.io.OutputStream; + +public class DelegatingUncloseableOutputStream extends OutputStream { + private final OutputStream delegate; + + public DelegatingUncloseableOutputStream(OutputStream delegate) { + super(); + this.delegate = delegate; + } + + @Override + public void write(int i) throws IOException { + delegate.write(i); + } + + @Override + public void write(byte[] b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + delegate.write(b, off, len); + } + + @Override + public void flush() { + } + + @Override + public void close() { + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/EnumerationSpliterator.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/EnumerationSpliterator.java new file mode 100644 index 0000000..6edda7f --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/EnumerationSpliterator.java @@ -0,0 +1,35 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class EnumerationSpliterator extends Spliterators.AbstractSpliterator { + private final Enumeration enumeration; + + public EnumerationSpliterator(long est, int additionalCharacteristics, Enumeration enumeration) { + super(est, additionalCharacteristics); + this.enumeration = enumeration; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (enumeration.hasMoreElements()) { + action.accept(enumeration.nextElement()); + return true; + } + return false; + } + + @Override + public void forEachRemaining(Consumer action) { + while (enumeration.hasMoreElements()) + action.accept(enumeration.nextElement()); + } + + public static Stream stream(Enumeration enumeration) { + EnumerationSpliterator spliterator = new EnumerationSpliterator<>(Long.MAX_VALUE, Spliterator.ORDERED, enumeration); + return StreamSupport.stream(spliterator, false); + } +} \ No newline at end of file diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java new file mode 100644 index 0000000..9be9449 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java @@ -0,0 +1,115 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes; +import org.objectweb.asm.*; +import org.objectweb.asm.commons.GeneratorAdapter; +import org.objectweb.asm.commons.Method; + +import java.lang.invoke.LambdaMetafactory; + +import static org.objectweb.asm.Opcodes.ASM9; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; + +public class GeneratorAdapter2 extends GeneratorAdapter { + private static final Handle metafactory = new Handle( + H_INVOKESTATIC, + Type.getInternalName(LambdaMetafactory.class), + "metafactory", + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", + false + ); + + public GeneratorAdapter2(MethodVisitor methodVisitor, int access, String name, String descriptor) { + super(ASM9, methodVisitor, access, name, descriptor); + } + + public void lambda(String lambdaMethodName, String lambdaType, Type lambdaDescriptor, Type implDescriptor, String implOwner, String implName) { + invokeDynamic(lambdaMethodName, + lambdaType, + metafactory, + lambdaDescriptor, + new Handle(H_INVOKESTATIC, implOwner, implName, implDescriptor.getInternalName(), false), + implDescriptor); + } + + public void lambda(String lambdaMethodName, String lambdaType, Type descriptor, String implOwner, String implName) { + lambda(lambdaMethodName, lambdaType, descriptor, descriptor, implOwner, implName); + } + + public void runnable(String implOwner, String implName) { + lambda("run", + "()Ljava/lang/Runnable;", + Type.getType("()V"), + implOwner, + implName); + } + + public void supplier(String implOwner, String implName, Type implDescriptor) { + lambda("get", + "()Ljava/util/function/Supplier;", + Type.getType("()Ljava/lang/Object;"), + implDescriptor, + implOwner, + implName); + } + + public void consumer(String implOwner, String implName, Type implDescriptor) { + lambda("accept", + "()Ljava/util/function/Consumer;", + Type.getType("(Ljava/lang/Object;)V"), + implDescriptor, + implOwner, + implName); + } + + public void box(Type type) { + Type boxedType; + switch (type.getSort()) { + case Type.VOID: + return; + case Type.CHAR: + boxedType = KnownTypes.CHARACTER_BOX_TYPE; + break; + case Type.BOOLEAN: + boxedType = KnownTypes.BOOLEAN_BOX_TYPE; + break; + case Type.DOUBLE: + boxedType = KnownTypes.DOUBLE_BOX_TYPE; + break; + case Type.FLOAT: + boxedType = KnownTypes.FLOAT_BOX_TYPE; + break; + case Type.LONG: + boxedType = KnownTypes.LONG_BOX_TYPE; + break; + case Type.INT: + boxedType = KnownTypes.INT_BOX_TYPE; + break; + case Type.SHORT: + boxedType = KnownTypes.SHORT_BOX_TYPE; + break; + case Type.BYTE: + boxedType = KnownTypes.BYTE_BOX_TYPE; + break; + default: + boxedType = null; + break; + } + if (boxedType == null) { + checkCast(type); + } else { + checkCast(boxedType); + invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[]{type})); + } + } + + public void invokeIStatic(Type owner, Method method) { + invokeInsn(Opcodes.INVOKESTATIC, owner, method, true); + } + + // Taken from GeneratorAdapter + private void invokeInsn(final int opcode, final Type type, final Method method, final boolean isInterface) { + String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName(); + mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor(), isInterface); + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GradleVersionUtil.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GradleVersionUtil.java new file mode 100644 index 0000000..1a2be9c --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GradleVersionUtil.java @@ -0,0 +1,21 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import org.apache.tools.zip.ZipOutputStream; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.bundling.ZipEntryCompression; +import org.gradle.util.GradleVersion; + +public class GradleVersionUtil { + private final GradleVersion version; + + public GradleVersionUtil(String version) { + this.version = GradleVersion.version(version); + } + + public ZipCompressor getInternalCompressor(ZipEntryCompression entryCompression, Jar jar) { + return switch (entryCompression) { + case DEFLATED -> new DefaultZipCompressor(jar.isZip64(), ZipOutputStream.DEFLATED); + case STORED -> new DefaultZipCompressor(jar.isZip64(), ZipOutputStream.STORED); + }; + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/RelativeArchivePath.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/RelativeArchivePath.java new file mode 100644 index 0000000..06a25cc --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/RelativeArchivePath.java @@ -0,0 +1,32 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + +import org.apache.tools.zip.ZipEntry; +import org.gradle.api.file.RelativePath; + +import java.util.Arrays; +import java.util.List; + +public class RelativeArchivePath extends RelativePath { + public ZipEntry entry; + + public RelativeArchivePath(ZipEntry entry) { + super(!entry.isDirectory(), entry.getName().split("/")); + this.entry = entry; + } + + public boolean isClassFile() { + return getLastName().endsWith(".class"); + } + + @Override + public RelativeArchivePath getParent() { + List segments = Arrays.asList(getSegments()); + if (segments.size() == 1) { + return null; + } else { + //Parent is always a directory so add / to the end of the path + String path = String.join("/", segments.subList(0, segments.size() - 1)) + "/"; + return new RelativeArchivePath(new ZipEntry(path)); + } + } +} diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ZipCompressor.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ZipCompressor.java new file mode 100644 index 0000000..4a1b6f2 --- /dev/null +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/ZipCompressor.java @@ -0,0 +1,11 @@ +package io.gitlab.jfronny.libjf.config.plugin.util; + + +import org.apache.tools.zip.ZipOutputStream; +import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory; + +import java.io.File; + +public interface ZipCompressor extends ArchiveOutputStreamFactory { + ZipOutputStream createArchiveOutputStream(File destination); +} diff --git a/libjf-config-core-v1/build.gradle b/libjf-config-core-v1/build.gradle new file mode 100644 index 0000000..43ecaf9 --- /dev/null +++ b/libjf-config-core-v1/build.gradle @@ -0,0 +1,5 @@ +archivesBaseName = "libjf-config-core-v1" + +dependencies { + api project(path: ":libjf-base", configuration: "dev") +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Category.java similarity index 73% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Category.java index 975aae6..9b6d4a7 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Category.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -8,4 +8,5 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Category { + String[] referencedConfigs() default {}; } diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigCategory.java similarity index 94% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigCategory.java index 13bec47..f8adb85 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigCategory.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigCategory.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import io.gitlab.jfronny.libjf.LibJf; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigHolder.java similarity index 85% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigHolder.java index ca457f4..cb0bc01 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigHolder.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; @@ -15,4 +15,5 @@ public interface ConfigHolder { ConfigInstance get(Path configPath); boolean isRegistered(String modId); boolean isRegistered(Path configPath); + void migrateFiles(String modId); } diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigInstance.java similarity index 85% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigInstance.java index 875808d..2da7082 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/ConfigInstance.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import java.nio.file.Path; import java.util.*; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Entry.java similarity index 89% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Entry.java index 83f485b..32685da 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Entry.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/EntryInfo.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/EntryInfo.java new file mode 100644 index 0000000..a6d1521 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/EntryInfo.java @@ -0,0 +1,90 @@ +package io.gitlab.jfronny.libjf.config.api.v1; + +import io.gitlab.jfronny.gson.JsonElement; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; +import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo; +import org.jetbrains.annotations.ApiStatus; + +import java.io.IOException; +import java.lang.reflect.Field; + +public interface EntryInfo { + static EntryInfo ofField(Field field) { + return DslEntryInfo.ofField(field); + } + + @ApiStatus.Internal + static EntryInfo ofField(Class klazz, String name) { + try { + return ofField(klazz.getField(name)); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + /** + * Get the name of this entry + * @return This entry's name + */ + String getName(); + + /** + * @return Get the default value of this entry + */ + T getDefault(); + + /** + * Gets the current value + * @return The current value + */ + T getValue() throws IllegalAccessException; + + /** + * Set the current value to the parameter + * @param value The value to use + */ + void setValue(T value) throws IllegalAccessException; + + /** + * Get the value type of this entry. Will use the class definition, not the current value + * @return The type of this entry + */ + Type getValueType(); + + /** + * Ensure the current value is within expected bounds. + */ + void fix(); + + /** + * Set this entry's value to that of the element + * @param element The element to read from + */ + void loadFromJson(JsonElement element) throws IllegalAccessException; + + /** + * Write the currently cached value to the writer + * @param writer The writer to write to + */ + void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException; + + /** + * @return Get the width for this entry + */ + int getWidth(); + + /** + * @return Get the minimum value of this entry + */ + double getMinValue(); + + /** + * @return Get the maximum value for this entry + */ + double getMaxValue(); + + default void reset() throws IllegalAccessException { + setValue(getDefault()); + } +} diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfConfig.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfConfig.java new file mode 100644 index 0000000..afc8c72 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfConfig.java @@ -0,0 +1,9 @@ +package io.gitlab.jfronny.libjf.config.api.v1; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface JfConfig { + String[] referencedConfigs() default {}; +} diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfCustomConfig.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfCustomConfig.java new file mode 100644 index 0000000..fcd0bb2 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/JfCustomConfig.java @@ -0,0 +1,7 @@ +package io.gitlab.jfronny.libjf.config.api.v1; + +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; + +public interface JfCustomConfig { + void register(DSL.Defaulted dsl); +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Preset.java similarity index 84% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Preset.java index 0bc0083..45db602 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Preset.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Verifier.java similarity index 84% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Verifier.java index b60131c..94c25f2 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/Verifier.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.api; +package io.gitlab.jfronny.libjf.config.api.v1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/CategoryBuilder.java similarity index 74% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/CategoryBuilder.java index e9d3991..c47f505 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/CategoryBuilder.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/CategoryBuilder.java @@ -1,7 +1,9 @@ -package io.gitlab.jfronny.libjf.config.api.dsl; +package io.gitlab.jfronny.libjf.config.api.v1.dsl; -import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; +import java.lang.reflect.Field; import java.util.function.Consumer; import java.util.function.Supplier; @@ -9,17 +11,21 @@ public interface CategoryBuilder> { Builder setTranslationPrefix(String translationPrefix); String getTranslationPrefix(); Builder addPreset(String id, Consumer action); + Builder addPreset(String id, Runnable preset); Builder addVerifier(Consumer verifier); + Builder addVerifier(Runnable verifier); Builder referenceConfig(String id); Builder referenceConfig(ConfigInstance config); Builder category(String id, CategoryBuilderFunction builder); Builder value(String id, int def, double min, double max, Supplier get, Consumer set); + Builder value(String id, long def, double min, double max, Supplier get, Consumer set); Builder value(String id, float def, double min, double max, Supplier get, Consumer set); Builder value(String id, double def, double min, double max, Supplier get, Consumer set); Builder value(String id, String def, Supplier get, Consumer set); Builder value(String id, boolean def, Supplier get, Consumer set); Builder value(String id, String def, String[] options, Supplier get, Consumer set); > Builder value(String id, T def, Class klazz, Supplier get, Consumer set); + Builder value(String id, T def, double min, double max, Type type, int width, Supplier get, Consumer set); Builder value(EntryInfo entry); String getId(); diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/ConfigBuilder.java similarity index 81% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/ConfigBuilder.java index 4adddaf..3a1c04d 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/ConfigBuilder.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/ConfigBuilder.java @@ -1,6 +1,6 @@ -package io.gitlab.jfronny.libjf.config.api.dsl; +package io.gitlab.jfronny.libjf.config.api.v1.dsl; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; import java.nio.file.Path; import java.util.function.Consumer; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/DSL.java similarity index 83% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/DSL.java index 76faae0..b4ad674 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/dsl/DSL.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/dsl/DSL.java @@ -1,7 +1,7 @@ -package io.gitlab.jfronny.libjf.config.api.dsl; +package io.gitlab.jfronny.libjf.config.api.v1.dsl; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; import io.gitlab.jfronny.libjf.config.impl.dsl.DSLImpl; public interface DSL { diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/type/Type.java similarity index 87% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/type/Type.java index fa8c4be..3823352 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/type/Type.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/type/Type.java @@ -1,10 +1,11 @@ -package io.gitlab.jfronny.libjf.config.api.type; +package io.gitlab.jfronny.libjf.config.api.v1.type; import org.jetbrains.annotations.Nullable; public sealed interface Type { static Type ofClass(java.lang.reflect.Type klazz) { if (klazz == int.class || klazz == Integer.class) return TInt.INSTANCE; + else if (klazz == long.class || klazz == Long.class) return TLong.INSTANCE; else if (klazz == float.class || klazz == Float.class) return TFloat.INSTANCE; else if (klazz == double.class || klazz == Double.class) return TDouble.INSTANCE; else if (klazz == String.class) return TString.INSTANCE; @@ -16,6 +17,9 @@ public sealed interface Type { default boolean isInt() { return false; } + default boolean isLong() { + return false; + } default boolean isFloat() { return false; } @@ -55,6 +59,25 @@ public sealed interface Type { } } + final class TLong implements Type { + public static TLong INSTANCE = new TLong(); + private TLong() {} + @Override + public boolean isLong() { + return true; + } + + @Override + public Class asClass() { + return Long.class; + } + + @Override + public String getName() { + return "Long"; + } + } + final class TFloat implements Type { public static TFloat INSTANCE = new TFloat(); private TFloat() {} diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java new file mode 100644 index 0000000..06af86a --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java @@ -0,0 +1,72 @@ +package io.gitlab.jfronny.libjf.config.impl; + +import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.v1.Category; +import io.gitlab.jfronny.libjf.config.api.v1.JfConfig; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.gson.FabricLoaderGsonGenerator; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.metadata.CustomValue; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedList; +import java.util.List; + +import static io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl.MODULE_ID; + +public class AuxiliaryMetadata { + public static AuxiliaryMetadata of(Category category) { + AuxiliaryMetadata meta = new AuxiliaryMetadata(); + if (category != null) { + meta.referencedConfigs = List.of(category.referencedConfigs()); + } + return meta.sanitize(); + } + + public static AuxiliaryMetadata of(JfConfig config) { + AuxiliaryMetadata meta = new AuxiliaryMetadata(); + if (config != null) { + meta.referencedConfigs = List.of(config.referencedConfigs()); + } + return meta.sanitize(); + } + + public static @Nullable AuxiliaryMetadata forMod(String modId) { + var metaRef = new Object() { + AuxiliaryMetadata meta = null; + }; + FabricLoader.getInstance().getModContainer(modId).ifPresent(container -> { + CustomValue cv = container.getMetadata().getCustomValue(MODULE_ID); + if (cv == null) { + cv = container.getMetadata().getCustomValue("libjf"); + if (cv != null) { + cv = cv.getAsObject().get("config"); + } + } + if (cv != null) metaRef.meta = GsonHolder.getGson().fromJson(FabricLoaderGsonGenerator.toGson(cv), AuxiliaryMetadata.class); + }); + return metaRef.meta; + } + + public List referencedConfigs; + + public void applyTo(CategoryBuilder builder) { + if (referencedConfigs != null) referencedConfigs.forEach(builder::referenceConfig); + } + + public AuxiliaryMetadata sanitize() { + if (referencedConfigs == null) referencedConfigs = List.of(); + else referencedConfigs = List.copyOf(referencedConfigs); + return this; + } + + public AuxiliaryMetadata merge(AuxiliaryMetadata other) { + if (other == null) return this; + AuxiliaryMetadata meta = new AuxiliaryMetadata(); + meta.referencedConfigs = new LinkedList<>(); + meta.referencedConfigs.addAll(this.referencedConfigs); + meta.referencedConfigs.addAll(other.referencedConfigs); + return meta.sanitize(); + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java similarity index 56% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java index d40de7f..e06a08b 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigHolderImpl.java @@ -2,10 +2,13 @@ package io.gitlab.jfronny.libjf.config.impl; import com.google.common.collect.ImmutableMap; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import net.fabricmc.loader.api.FabricLoader; import org.jetbrains.annotations.ApiStatus; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; @@ -16,7 +19,6 @@ public class ConfigHolderImpl implements ConfigHolder { private ConfigHolderImpl() { } public static final String MODULE_ID = "libjf:config"; - public static final String CUSTOM_ID = MODULE_ID + "_custom"; private final Map configs = new HashMap<>(); private final Map configsByPath = new HashMap<>(); @@ -54,4 +56,32 @@ public class ConfigHolderImpl implements ConfigHolder { public boolean isRegistered(Path configPath) { return configsByPath.containsKey(configPath); } + + @Override + public void migrateFiles(String modId) { + Path cfg = FabricLoader.getInstance().getConfigDir(); + Path target = cfg.resolve(modId + ".json5"); + if (Files.exists(target)) return; + if (Files.exists(cfg.resolve(modId + ".json"))) { + try { + Files.move(cfg.resolve(modId + ".json"), target); + } catch (IOException ignored) { + } + } + FabricLoader.getInstance().getModContainer(modId).ifPresent(mod -> { + try { + for (String id : mod.getMetadata().getProvides()) { + if (Files.exists(cfg.resolve(id + ".json"))) { + Files.move(cfg.resolve(id + ".json"), target); + return; + } + if (Files.exists(cfg.resolve(id + ".json5"))) { + Files.move(cfg.resolve(id + ".json5"), target); + return; + } + } + } catch (IOException ignored) { + } + }); + } } diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java similarity index 98% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java index 6b2ee27..44cb171 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/JfConfigWatchService.java @@ -2,7 +2,7 @@ package io.gitlab.jfronny.libjf.config.impl; import io.gitlab.jfronny.commons.throwable.*; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder; import io.gitlab.jfronny.libjf.coprocess.ThreadCoProcess; import net.fabricmc.loader.api.FabricLoader; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java similarity index 84% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java index ad641db..570d916 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/CategoryBuilderImpl.java @@ -1,10 +1,9 @@ package io.gitlab.jfronny.libjf.config.impl.dsl; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.api.dsl.CategoryBuilder; -import io.gitlab.jfronny.libjf.config.api.type.Type; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; -import java.lang.reflect.Field; import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; @@ -47,6 +46,11 @@ public class CategoryBuilderImpl> i return asBuilder(); } + @Override + public Builder addPreset(String id, Runnable preset) { + return addPreset(id, cfg -> preset.run()); + } + @Override public Builder addVerifier(Consumer verifier) { checkBuilt(); @@ -54,6 +58,11 @@ public class CategoryBuilderImpl> i return asBuilder(); } + @Override + public Builder addVerifier(Runnable verifier) { + return addVerifier(cfg -> verifier.run()); + } + @Override public Builder referenceConfig(String id) { checkBuilt(); @@ -82,6 +91,13 @@ public class CategoryBuilderImpl> i return asBuilder(); } + @Override + public Builder value(String id, long def, double min, double max, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, Type.TLong.INSTANCE, 100, min, max)); + return asBuilder(); + } + @Override public Builder value(String id, float def, double min, double max, Supplier get, Consumer set) { checkBuilt(); @@ -124,6 +140,13 @@ public class CategoryBuilderImpl> i return asBuilder(); } + @Override + public Builder value(String id, T def, double min, double max, Type type, int width, Supplier get, Consumer set) { + checkBuilt(); + entries.add(new DslEntryInfo<>(id, def, get::get, set::accept, type, width, min, max)); + return asBuilder(); + } + @Override public Builder value(EntryInfo entry) { checkBuilt(); diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java new file mode 100644 index 0000000..9d90d59 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java @@ -0,0 +1,133 @@ +package io.gitlab.jfronny.libjf.config.impl.dsl; + +import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; +import io.gitlab.jfronny.gson.*; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.ConfigBuilder; +import io.gitlab.jfronny.libjf.config.impl.JfConfigWatchService; +import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class ConfigBuilderImpl extends CategoryBuilderImpl implements ConfigBuilder { + public DslConfigInstance built; + public Consumer load; + public Consumer write; + public Path path; + + public ConfigBuilderImpl(String id) { + super(id, ""); + load = c -> { + c.getFilePath().ifPresent(path -> { + if (Files.exists(path)) { + try (BufferedReader br = Files.newBufferedReader(path)) { + JsonElement element = JsonParser.parseReader(br); + if (element.isJsonObject()) loadFrom(element.getAsJsonObject(), c); + else LibJf.LOGGER.error("Invalid config: Not a JSON object for " + id); + } catch (Exception e) { + LibJf.LOGGER.error("Could not read config for " + id, e); + } + } + c.write(); + }); + }; + write = c -> { + c.getFilePath().ifPresent(path -> JfConfigWatchService.lock(path, () -> { + try (BufferedWriter bw = Files.newBufferedWriter(path); + JsonWriter jw = GsonHolder.getGson().newJsonWriter(bw)) { + writeTo(jw, c); + } catch (Exception e) { + LibJf.LOGGER.error("Could not write config", e); + } + })); + }; + path = FabricLoader.getInstance().getConfigDir().resolve(id + ".json5"); + } + + private static void loadFrom(JsonObject source, ConfigCategory category) { + for (EntryInfo entry : category.getEntries()) { + if (source.has(entry.getName())) { + try { + entry.loadFromJson(source.get(entry.getName())); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not set config entry value of " + entry.getName(), e); + } + } else LibJf.LOGGER.error("Config does not contain entry for " + entry.getName()); + } + for (Map.Entry entry : category.getCategories().entrySet()) { + if (source.has(entry.getKey())) { + JsonElement el = source.get(entry.getKey()); + if (el.isJsonObject()) loadFrom(el.getAsJsonObject(), entry.getValue()); + else LibJf.LOGGER.error("Config category is not a JSON object, skipping"); + } else LibJf.LOGGER.error("Config does not contain entry for subcategory " + entry.getKey()); + } + } + + private static void writeTo(JsonWriter writer, ConfigCategory category) throws IOException { + category.fix(); + writer.beginObject(); + String val; + for (EntryInfo entry : category.getEntries()) { + try { + entry.writeTo(writer, category.getTranslationPrefix()); + } catch (IllegalAccessException e) { + LibJf.LOGGER.error("Could not write entry", e); + } + } + for (Map.Entry entry : category.getCategories().entrySet()) { + if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(category.getTranslationPrefix() + entry.getKey() + ".title")) != null) + writer.comment(val); + writer.name(entry.getKey()); + writeTo(writer, entry.getValue()); + } + writer.endObject(); + } + + @Override + public ConfigBuilderImpl setLoadMethod(Consumer load) { + checkBuilt(); + this.load = load; + return this; + } + + public ConfigBuilderImpl setWriteMethod(Consumer write) { + checkBuilt(); + this.write = write; + return this; + } + + public ConfigBuilderImpl setPath(Path path) { + checkBuilt(); + this.path = path; + return this; + } + + @Override + public DslConfigInstance build() { + markBuilt(); + built = new DslConfigInstance(id, + translationPrefix, + entries, + presets, + () -> referencedConfigs.stream().map(Supplier::get).toList(), + categories.stream().collect(Collectors.toMap(CategoryBuilder::getId, b -> b.build(() -> built))), + () -> built, + verifiers, + load, + write, + path); + built.load(); + return built; + } +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java similarity index 86% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java index 9abec75..369f7a0 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DSLImpl.java @@ -1,9 +1,9 @@ package io.gitlab.jfronny.libjf.config.impl.dsl; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.dsl.ConfigBuilder; -import io.gitlab.jfronny.libjf.config.api.dsl.DSL; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.ConfigBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; public class DSLImpl implements DSL { @Override diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java similarity index 98% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java index 47848d4..a9cee74 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigCategory.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.libjf.config.impl.dsl; -import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.v1.*; import java.util.*; import java.util.function.Consumer; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java similarity index 97% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java index e93419a..1861520 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslConfigInstance.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.libjf.config.impl.dsl; -import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.v1.*; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java similarity index 83% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java index 1f0d611..cf91e97 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java @@ -6,11 +6,13 @@ import io.gitlab.jfronny.commons.throwable.ThrowingSupplier; import io.gitlab.jfronny.gson.JsonElement; import io.gitlab.jfronny.gson.stream.JsonWriter; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.EntryInfo; -import io.gitlab.jfronny.libjf.config.api.type.Type; +import io.gitlab.jfronny.libjf.config.api.v1.Entry; +import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; import java.io.IOException; +import java.lang.reflect.Field; import java.util.Arrays; import java.util.Objects; import java.util.stream.Collectors; @@ -51,6 +53,26 @@ public class DslEntryInfo implements EntryInfo { this(name, def, get, set, type, 100, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); } + public static DslEntryInfo ofField(Field field) { + Entry entry = field.getAnnotation(Entry.class); + Object defaultValue = null; + try { + defaultValue = field.get(null); + } catch (IllegalAccessException ignored) { + } + //noinspection unchecked,rawtypes + return new DslEntryInfo( + field.getName(), + defaultValue, + () -> field.get(null), + v -> field.set(null, v), + (Type) Type.ofClass(field.getGenericType()), + entry == null ? 100 : entry.width(), + entry == null ? Double.NEGATIVE_INFINITY : entry.min(), + entry == null ? Double.POSITIVE_INFINITY : entry.max() + ); + } + @Override public String getName() { return name; @@ -119,6 +141,10 @@ public class DslEntryInfo implements EntryInfo { if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) { setUnchecked(element.getAsNumber().intValue()); } + } else if (type.isLong()) { + if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) { + setUnchecked(element.getAsNumber().longValue()); + } } else if (type.isDouble()) { if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) { setUnchecked(element.getAsNumber().doubleValue()); diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java similarity index 51% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java rename to libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java index 335f8f7..c475b7f 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java @@ -1,21 +1,27 @@ package io.gitlab.jfronny.libjf.config.impl.entrypoint; -import io.gitlab.jfronny.libjf.config.api.JfCustomConfig; -import io.gitlab.jfronny.libjf.config.api.dsl.DSL; +import io.gitlab.jfronny.libjf.config.api.v1.JfCustomConfig; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.entrypoint.EntrypointContainer; import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; import net.minecraft.util.Language; +import java.util.HashSet; +import java.util.Set; import java.util.function.Function; public class JfConfigSafe implements PreLaunchEntrypoint { public static Function TRANSLATION_SUPPLIER = s -> null; + public static final Set REGISTERED_MODS = new HashSet<>(); @Override public void onPreLaunch() { - for (EntrypointContainer custom : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.CUSTOM_ID, JfCustomConfig.class)) { - custom.getEntrypoint().register(DSL.create(custom.getProvider().getMetadata().getId())); + for (EntrypointContainer custom : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, Object.class)) { + if (!REGISTERED_MODS.contains(custom.getProvider().getMetadata().getId()) && custom.getEntrypoint() instanceof JfCustomConfig cfg) { + REGISTERED_MODS.add(custom.getProvider().getMetadata().getId()); + cfg.register(DSL.create(custom.getProvider().getMetadata().getId())); + } } TRANSLATION_SUPPLIER = s -> { String translated = Language.getInstance().get(s); diff --git a/libjf-config-v1/src/main/resources/fabric.mod.json b/libjf-config-core-v1/src/main/resources/fabric.mod.json similarity index 61% rename from libjf-config-v1/src/main/resources/fabric.mod.json rename to libjf-config-core-v1/src/main/resources/fabric.mod.json index 4a3d16d..18dd399 100644 --- a/libjf-config-v1/src/main/resources/fabric.mod.json +++ b/libjf-config-core-v1/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "libjf-config-v1", + "id": "libjf-config-core-v1", "name": "LibJF Config", "version": "${version}", "authors": ["JFronny"], @@ -11,19 +11,13 @@ "license": "MIT", "environment": "*", "entrypoints": { - "modmenu": ["io.gitlab.jfronny.libjf.config.impl.client.ModMenu"], - "client": ["io.gitlab.jfronny.libjf.config.impl.client.JfConfigClient"], "preLaunch": ["io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe"], - "main": ["io.gitlab.jfronny.libjf.config.impl.JfConfigCommand"], "libjf:coprocess": ["io.gitlab.jfronny.libjf.config.impl.JfConfigWatchService"] }, "depends": { "fabricloader": ">=0.12.0", "minecraft": "*", - "fabric-resource-loader-v0": "*", - "fabric-command-api-v2": "*", - "libjf-base": ">=${version}", - "libjf-unsafe-v0": ">=${version}" + "libjf-base": ">=${version}" }, "custom": { "modmenu": { diff --git a/libjf-config-core-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java b/libjf-config-core-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java new file mode 100644 index 0000000..01a9c03 --- /dev/null +++ b/libjf-config-core-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java @@ -0,0 +1,63 @@ +package io.gitlab.jfronny.libjf.config.test; + +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.*; + +import java.util.LinkedList; +import java.util.List; + +@JfConfig(referencedConfigs = {"Yes", "No", "Yes"}) +public class TestConfigCustom implements JfCustomConfig { + // User-created + @Entry + public static Integer someValue = 10; + @Entry public static String someOther = "Yes"; + + @Category + public static class SomeCategory { + @Entry public static boolean someBool = true; + @Entry public static List someObject = new LinkedList<>(); + + @Verifier + public static void exampleVerifier() { + + } + + @Preset + public static void examplePreset() { + + } + + // Generated + private static CategoryBuilder libjf$config$root(CategoryBuilder builder) { + return builder + .value("someBool", someBool, () -> SomeCategory.someBool, v -> SomeCategory.someBool = v) + .value(EntryInfo.ofField(SomeCategory.class, "someObject")) + .addVerifier(SomeCategory::exampleVerifier) + .addPreset("examplePreset", SomeCategory::examplePreset); + } + } + + // Generated + @Override + public void register(DSL.Defaulted dsl) { + // Here to ensure the static initializer is called + } + + static { + libjf$config$clinit(); + } + + private static void libjf$config$clinit() { + ConfigHolder.getInstance().migrateFiles("libjf-config-v1-testmod"); + DSL.create("libjf-config-v1-testmod").config(TestConfigCustom::libjf$config$root); + } + + private static ConfigBuilder libjf$config$root(ConfigBuilder builder) { + return builder + .referenceConfig("libjf-config-reflect-v0-testmod") + .value("someValue", someValue, -50, 50, () -> someValue, v -> someValue = v) + .value("someOther", someOther, () -> someOther, v -> someOther = v) + .category("someCategory", SomeCategory::libjf$config$root); + } +} diff --git a/libjf-config-v1/src/testmod/resources/fabric.mod.json b/libjf-config-core-v1/src/testmod/resources/fabric.mod.json similarity index 100% rename from libjf-config-v1/src/testmod/resources/fabric.mod.json rename to libjf-config-core-v1/src/testmod/resources/fabric.mod.json diff --git a/libjf-config-legacy-shim/build.gradle b/libjf-config-legacy-shim/build.gradle new file mode 100644 index 0000000..cefa393 --- /dev/null +++ b/libjf-config-legacy-shim/build.gradle @@ -0,0 +1,10 @@ +archivesBaseName = "libjf-config-legacy-shim" + +dependencies { + api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-config-core-v1", configuration: "dev") + api project(path: ":libjf-config-commands-v1", configuration: "dev") + api project(path: ":libjf-config-reflect-v1", configuration: "dev") + api project(path: ":libjf-config-ui-tiny-v1", configuration: "dev") + api project(path: ":libjf-unsafe-v0", configuration: "dev") +} diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java b/libjf-config-legacy-shim/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java similarity index 97% rename from libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java rename to libjf-config-legacy-shim/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java index d147e89..c982e86 100644 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java +++ b/libjf-config-legacy-shim/src/client/java/io/gitlab/jfronny/libjf/config/api/WidgetFactory.java @@ -4,6 +4,7 @@ import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ClickableWidget; +@Deprecated public interface WidgetFactory { Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done); diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java new file mode 100644 index 0000000..7e1c52f --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Category.java @@ -0,0 +1,9 @@ +package io.gitlab.jfronny.libjf.config.api; + +import java.lang.annotation.*; + +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Category { +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java new file mode 100644 index 0000000..389089c --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigHolder.java @@ -0,0 +1,21 @@ +package io.gitlab.jfronny.libjf.config.api; + +import io.gitlab.jfronny.libjf.config.impl.legacy.ConfigHolderImpl; + +import java.nio.file.Path; +import java.util.Map; + +@Deprecated +public interface ConfigHolder { + static ConfigHolder getInstance() { + return new ConfigHolderImpl(io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder.getInstance()); + } + void register(String modId, Class config); + Map getRegistered(); + ConfigInstance get(Class configClass); + ConfigInstance get(String modId); + ConfigInstance get(Path configPath); + boolean isRegistered(Class configClass); + boolean isRegistered(String modId); + boolean isRegistered(Path configPath); +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java new file mode 100644 index 0000000..6509f20 --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/ConfigInstance.java @@ -0,0 +1,33 @@ +package io.gitlab.jfronny.libjf.config.api; + +import java.util.List; +import java.util.Map; + +@Deprecated +public interface ConfigInstance { + static ConfigInstance get(Class configClass) { + return ConfigHolder.getInstance().get(configClass); + } + static ConfigInstance get(String modId) { + return ConfigHolder.getInstance().get(modId); + } + void load(); + void write(); + String getId(); + String getCategoryPath(); + default String getTranslationPrefix() { + return getId() + ".jfconfig." + getCategoryPath(); + } + List> getEntries(); + Map getPresets(); + default List getReferencedConfigs() { + return List.of(); + } + Map getCategories(); + default void fix() { + for (EntryInfo entry : getEntries()) { + entry.fix(); + } + } + +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java new file mode 100644 index 0000000..c248bba --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Entry.java @@ -0,0 +1,12 @@ +package io.gitlab.jfronny.libjf.config.api; + +import java.lang.annotation.*; + +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Entry { + int width() default 100; + double min() default Double.NEGATIVE_INFINITY; + double max() default Double.POSITIVE_INFINITY; +} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java similarity index 90% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java rename to libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java index 6f3f117..6153287 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/EntryInfo.java @@ -2,17 +2,11 @@ package io.gitlab.jfronny.libjf.config.api; import io.gitlab.jfronny.gson.JsonElement; import io.gitlab.jfronny.gson.stream.JsonWriter; -import io.gitlab.jfronny.libjf.config.api.type.Type; import java.io.IOException; +@Deprecated public interface EntryInfo { - /** - * Get the name of this entry - * @return This entry's name - */ - String getName(); - /** * @return Get the default value of this entry */ @@ -34,13 +28,19 @@ public interface EntryInfo { * Get the value type of this entry. Will use the class definition, not the current value * @return The type of this entry */ - Type getValueType(); + Class getValueType(); /** * Ensure the current value is within expected bounds. */ void fix(); + /** + * Get the name of this entry + * @return This entry's name + */ + String getName(); + /** * Set this entry's value to that of the element * @param element The element to read from @@ -67,8 +67,4 @@ public interface EntryInfo { * @return Get the maximum value for this entry */ double getMaxValue(); - - default void reset() throws IllegalAccessException { - setValue(getDefault()); - } } diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java similarity index 86% rename from libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java rename to libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java index 861dc5c..fd95039 100644 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/JfConfig.java @@ -1,4 +1,5 @@ package io.gitlab.jfronny.libjf.config.api; +@Deprecated public interface JfConfig { } diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java new file mode 100644 index 0000000..79df120 --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Preset.java @@ -0,0 +1,9 @@ +package io.gitlab.jfronny.libjf.config.api; + +import java.lang.annotation.*; + +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Preset { +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java new file mode 100644 index 0000000..9c34822 --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/api/Verifier.java @@ -0,0 +1,9 @@ +package io.gitlab.jfronny.libjf.config.api; + +import java.lang.annotation.*; + +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Verifier { +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigHolderImpl.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigHolderImpl.java new file mode 100644 index 0000000..1e1ec5a --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigHolderImpl.java @@ -0,0 +1,119 @@ +package io.gitlab.jfronny.libjf.config.impl.legacy; + +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.*; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; +import io.gitlab.jfronny.libjf.config.impl.AuxiliaryMetadata; +import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo; +import io.gitlab.jfronny.libjf.config.impl.reflect.ReflectiveConfigBuilderImpl; +import net.fabricmc.loader.api.ModContainer; + +import java.lang.reflect.*; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +@Deprecated +public record ConfigHolderImpl(io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder base) implements ConfigHolder { + private static final Map, io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance> klazzToInstance = new HashMap<>(); + @Override + public void register(String modId, Class config) { + AtomicReference mc = new AtomicReference<>(); + base.migrateFiles(modId); + klazzToInstance.put(config, DSL.create(modId).register(builder -> { + Optional.ofNullable(AuxiliaryMetadata.forMod(modId)).ifPresent(meta -> meta.applyTo(builder)); + return applyCategory(builder, config); + })); + } + + private > T applyCategory(T builder, Class klazz) { + for (Field field : klazz.getFields()) { + if (field.isAnnotationPresent(Entry.class)) { + Entry entry = field.getAnnotation(Entry.class); + Object defaultValue = null; + try { + defaultValue = field.get(null); + } catch (IllegalAccessException ignored) { + } + //noinspection unchecked,rawtypes + builder.value(new DslEntryInfo( + field.getName(), + defaultValue, + () -> field.get(null), + v -> field.set(null, v), + (Type) Type.ofClass(field.getGenericType()), + entry.width(), + entry.min(), + entry.max() + )); + } + } + builder.addPreset(ReflectiveConfigBuilderImpl.CONFIG_PRESET_DEFAULT, ConfigCategory::reset); + for (Method method : klazz.getMethods()) { + if (method.isAnnotationPresent(Preset.class)) { + builder.addPreset(builder.getTranslationPrefix() + method.getName(), c -> { + try { + method.invoke(null); + } catch (InvocationTargetException | IllegalAccessException e) { + LibJf.LOGGER.error("Could not apply preset", e); + } + }); + } else if (method.isAnnotationPresent(Verifier.class)) { + builder.addVerifier(c -> { + try { + method.invoke(null); + } catch (IllegalAccessException | InvocationTargetException e) { + LibJf.LOGGER.error("Could not run verifier", e); + } + }); + } + } + for (Class category : klazz.getClasses()) { + if (category.isAnnotationPresent(Category.class)) { + String name = category.getSimpleName(); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); // camelCase + builder.category(name, categoryBuilder -> applyCategory(categoryBuilder, category)); + } + } + return builder; + } + + @Override + public Map getRegistered() { + return base.getRegistered().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, s -> ConfigInstanceImpl.of(s.getValue()))); + } + + @Override + public ConfigInstance get(Class configClass) { + return ConfigInstanceImpl.of(klazzToInstance.get(configClass)); + } + + @Override + public ConfigInstance get(String modId) { + return ConfigInstanceImpl.of(base.get(modId)); + } + + @Override + public ConfigInstance get(Path configPath) { + return ConfigInstanceImpl.of(base.get(configPath)); + } + + @Override + public boolean isRegistered(Class configClass) { + return klazzToInstance.containsKey(configClass); + } + + @Override + public boolean isRegistered(String modId) { + return base.isRegistered(modId); + } + + @Override + public boolean isRegistered(Path configPath) { + return base.isRegistered(configPath); + } +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigInstanceImpl.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigInstanceImpl.java new file mode 100644 index 0000000..2c7dc4f --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/ConfigInstanceImpl.java @@ -0,0 +1,72 @@ +package io.gitlab.jfronny.libjf.config.impl.legacy; + +import io.gitlab.jfronny.libjf.config.api.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; + +import java.util.*; +import java.util.stream.Collectors; + +@Deprecated +public record ConfigInstanceImpl(io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance root, ConfigCategory category) implements ConfigInstance { + public ConfigInstanceImpl(io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance instance) { + this(Objects.requireNonNull(instance), instance); + } + + public static ConfigInstanceImpl of(io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance instance) { + return instance == null ? null : new ConfigInstanceImpl(instance); + } + + @Override + public void load() { + root.load(); + } + + @Override + public void write() { + root.write(); + } + + @Override + public String getId() { + return category.getId(); + } + + @Override + public String getCategoryPath() { + return category.getCategoryPath(); + } + + @Override + public String getTranslationPrefix() { + return category.getTranslationPrefix(); + } + + @Override + public List> getEntries() { + return category.getEntries().stream().>map(EntryInfoImpl::new).toList(); + } + + @Override + public Map getPresets() { + return category.getPresets(); + } + + @Override + public List getReferencedConfigs() { + return category.getReferencedConfigs().stream().map(r -> new ConfigInstanceImpl(root, r)).toList(); + } + + @Override + public Map getCategories() { + return category.getCategories().entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + s -> new ConfigInstanceImpl(root, s.getValue()) + )); + } + + @Override + public void fix() { + category.fix(); + } +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/EntryInfoImpl.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/EntryInfoImpl.java new file mode 100644 index 0000000..108a8d7 --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/EntryInfoImpl.java @@ -0,0 +1,65 @@ +package io.gitlab.jfronny.libjf.config.impl.legacy; + +import io.gitlab.jfronny.gson.JsonElement; +import io.gitlab.jfronny.gson.stream.JsonWriter; +import io.gitlab.jfronny.libjf.config.api.EntryInfo; + +import java.io.IOException; + +@Deprecated +public record EntryInfoImpl(io.gitlab.jfronny.libjf.config.api.v1.EntryInfo base) implements EntryInfo { + @Override + public Object getDefault() { + return base.getDefault(); + } + + @Override + public Object getValue() throws IllegalAccessException { + return base.getValue(); + } + + @Override + public void setValue(Object value) throws IllegalAccessException { + base.setValue((T) value); + } + + @Override + public Class getValueType() { + return base.getValueType().asClass() instanceof Class k ? k : null; + } + + @Override + public void fix() { + base.fix(); + } + + @Override + public String getName() { + return base.getName(); + } + + @Override + public void loadFromJson(JsonElement element) throws IllegalAccessException { + base.loadFromJson(element); + } + + @Override + public void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException { + base.writeTo(writer, translationPrefix); + } + + @Override + public int getWidth() { + return base.getWidth(); + } + + @Override + public double getMinValue() { + return base.getMinValue(); + } + + @Override + public double getMaxValue() { + return base.getMaxValue(); + } +} diff --git a/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/LegacyRegistrationHook.java b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/LegacyRegistrationHook.java new file mode 100644 index 0000000..882725e --- /dev/null +++ b/libjf-config-legacy-shim/src/main/java/io/gitlab/jfronny/libjf/config/impl/legacy/LegacyRegistrationHook.java @@ -0,0 +1,20 @@ +package io.gitlab.jfronny.libjf.config.impl.legacy; + +import io.gitlab.jfronny.libjf.config.api.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.JfConfig; +import io.gitlab.jfronny.libjf.config.api.v1.JfCustomConfig; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; +import io.gitlab.jfronny.libjf.unsafe.DynamicEntry; + +@Deprecated +public class LegacyRegistrationHook implements JfCustomConfig { + @Override + public void register(DSL.Defaulted dsl) { + DynamicEntry.execute("libjf:config", Object.class, s -> { + if (s.instance().getClass().isAnnotationPresent(io.gitlab.jfronny.libjf.config.api.v1.JfConfig.class)) return; + if (s.instance() instanceof JfCustomConfig) return; + if (!(s.instance() instanceof JfConfig)) return; + ConfigHolder.getInstance().register(s.modId(), s.instance().getClass()); + }); + } +} diff --git a/libjf-config-legacy-shim/src/main/resources/fabric.mod.json b/libjf-config-legacy-shim/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..f9d9429 --- /dev/null +++ b/libjf-config-legacy-shim/src/main/resources/fabric.mod.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "id": "libjf-config-legacy-shim", + "name": "LibJF Config Legacy Shim", + "version": "${version}", + "provides": ["libjf-config-v0"], + "authors": ["JFronny"], + "contact": { + "website": "https://jfronny.gitlab.io", + "repo": "https://gitlab.com/jfmods/libjf" + }, + "license": "MIT", + "environment": "*", + "entrypoints": { + "libjf:config": [ + "io.gitlab.jfronny.libjf.config.impl.legacy.LegacyRegistrationHook" + ] + }, + "depends": { + "fabricloader": ">=0.12.0", + "minecraft": "*", + "libjf-config-core-v1": ">=${version}", + "libjf-config-reflect-v1": ">=${version}", + "libjf-unsafe-v0": ">=${version}" + }, + "custom": { + "modmenu": { + "badges": ["library"], + "parent": "libjf" + } + } +} diff --git a/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java b/libjf-config-legacy-shim/src/testmod/java/io/gitlab/jfronny/libjf/config/test/legacy/TestConfig.java similarity index 96% rename from libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java rename to libjf-config-legacy-shim/src/testmod/java/io/gitlab/jfronny/libjf/config/test/legacy/TestConfig.java index 5dd3814..73d4a81 100644 --- a/libjf-config-reflect-v0/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java +++ b/libjf-config-legacy-shim/src/testmod/java/io/gitlab/jfronny/libjf/config/test/legacy/TestConfig.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.test.reflect; +package io.gitlab.jfronny.libjf.config.test.legacy; import io.gitlab.jfronny.commons.serialize.gson.api.Ignore; import io.gitlab.jfronny.libjf.config.api.*; diff --git a/libjf-config-legacy-shim/src/testmod/resources/assets/libjf-config-legacy-shim-testmod/lang/en_us.json b/libjf-config-legacy-shim/src/testmod/resources/assets/libjf-config-legacy-shim-testmod/lang/en_us.json new file mode 100644 index 0000000..3002237 --- /dev/null +++ b/libjf-config-legacy-shim/src/testmod/resources/assets/libjf-config-legacy-shim-testmod/lang/en_us.json @@ -0,0 +1,13 @@ +{ + "libjf-config-legacy-shim-testmod.jfconfig.title": "JfConfig example", + "libjf-config-legacy-shim-testmod.jfconfig.disablePacks": "Disable resource packs", + "libjf-config-legacy-shim-testmod.jfconfig.intTest": "Int Test", + "libjf-config-legacy-shim-testmod.jfconfig.decimalTest": "Decimal Test", + "libjf-config-legacy-shim-testmod.jfconfig.dieStr": "String Test", + "libjf-config-legacy-shim-testmod.jfconfig.gsonOnlyStr.tooltip": "George", + "libjf-config-legacy-shim-testmod.jfconfig.enumTest": "Enum Test", + "libjf-config-legacy-shim-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip", + "libjf-config-legacy-shim-testmod.jfconfig.enum.Test.Test": "Test", + "libjf-config-legacy-shim-testmod.jfconfig.enum.Test.ER": "ER", + "libjf-config-legacy-shim-testmod.jfconfig.moskau": "Moskau" +} diff --git a/libjf-config-legacy-shim/src/testmod/resources/fabric.mod.json b/libjf-config-legacy-shim/src/testmod/resources/fabric.mod.json new file mode 100644 index 0000000..4938e1b --- /dev/null +++ b/libjf-config-legacy-shim/src/testmod/resources/fabric.mod.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 1, + "id": "libjf-config-legacy-shim-testmod", + "version": "1.0", + "environment": "*", + "entrypoints": { + "libjf:config": [ + "io.gitlab.jfronny.libjf.config.test.legacy.TestConfig" + ] + }, + "depends": { + "libjf-config-v0": "*" + }, + "custom": { + "libjf": { + "config": { + "referencedConfigs": ["libjf-web-v0"] + } + } + } +} diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java deleted file mode 100644 index 46a592b..0000000 --- a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ConfigProvider.java +++ /dev/null @@ -1,190 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl.reflect; - -import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder; -import io.gitlab.jfronny.gson.*; -import io.gitlab.jfronny.gson.stream.JsonWriter; -import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.api.dsl.*; -import io.gitlab.jfronny.libjf.config.api.type.Type; -import io.gitlab.jfronny.libjf.config.impl.*; -import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo; -import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; -import io.gitlab.jfronny.libjf.gson.FabricLoaderGsonGenerator; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.api.metadata.CustomValue; - -import java.io.*; -import java.lang.reflect.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; - -import static io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl.MODULE_ID; - -public class ConfigProvider { - public static final String CONFIG_PRESET_DEFAULT = "libjf-config-v1.default"; - public static void register(String id, Class klazz) { - Optional container = FabricLoader.getInstance().getModContainer(id); - List previousNames = new LinkedList<>(); - var metaRef = new Object() { - AuxiliaryMetadata meta = new AuxiliaryMetadata(); - }; - if (container.isPresent()) { - CustomValue cv = container.get().getMetadata().getCustomValue(MODULE_ID); - if (cv == null) { - cv = container.get().getMetadata().getCustomValue("libjf"); - if (cv != null) { - cv = cv.getAsObject().get("config"); - } - } - if (cv != null) metaRef.meta = GsonHolder.getGson().fromJson(FabricLoaderGsonGenerator.toGson(cv), AuxiliaryMetadata.class); - previousNames.addAll(container.get().getMetadata().getProvides()); - } - else { - LibJf.LOGGER.warn("Attempted to register config for a mod that is not installed: " + id); - } - previousNames.add(id); - - Path cfg = FabricLoader.getInstance().getConfigDir(); - Path path = cfg.resolve(id + ".json5"); - if (!Files.exists(path)) { - try { - for (String s : previousNames) { - Path previousPath = cfg.resolve(s + ".json"); - if (Files.exists(previousPath)) { - Files.move(previousPath, path); - break; - } - previousPath = cfg.resolve(s + ".json5"); - if (Files.exists(previousPath)) { - Files.move(previousPath, path); - break; - } - } - } catch (IOException e) { - LibJf.LOGGER.error("Could not upgrade config directory", e); - } - } - - DSL.create(id).register(builder -> (ConfigBuilder) applyCategory(builder - .setLoadMethod(c -> { - if (Files.exists(path)) { - try (BufferedReader br = Files.newBufferedReader(path)) { - JsonElement element = JsonParser.parseReader(br); - if (element.isJsonObject()) loadFrom(element.getAsJsonObject(), c); - else LibJf.LOGGER.error("Invalid config: Not a JSON object for " + id); - } catch (Exception e) { - LibJf.LOGGER.error("Could not read config for " + id, e); - } - } - c.write(); - }) - .setWriteMethod(c -> JfConfigWatchService.lock(path, () -> { - try (BufferedWriter bw = Files.newBufferedWriter(path); - JsonWriter jw = GsonHolder.getGson().newJsonWriter(bw)) { - writeTo(jw, c); - } catch (Exception e) { - LibJf.LOGGER.error("Could not write config", e); - } - })) - .setPath(path), - klazz, id, metaRef.meta)); - } - - private static CategoryBuilder applyCategory(CategoryBuilder builder, Class configClass, String modId, AuxiliaryMetadata meta) { - if (meta.referencedConfigs != null) meta.referencedConfigs.forEach(builder::referenceConfig); - for (Field field : configClass.getFields()) { - if (field.isAnnotationPresent(Entry.class)) { - Entry entry = field.getAnnotation(Entry.class); - Object defaultValue = null; - try { - defaultValue = field.get(null); - } catch (IllegalAccessException ignored) { - } - //noinspection unchecked,rawtypes - builder.value(new DslEntryInfo( - field.getName(), - defaultValue, - () -> field.get(null), - v -> field.set(null, v), - (Type) Type.ofClass(field.getGenericType()), - entry == null ? 100 : entry.width(), - entry == null ? Double.NEGATIVE_INFINITY : entry.min(), - entry == null ? Double.POSITIVE_INFINITY : entry.max() - )); - } - } - builder.addPreset(CONFIG_PRESET_DEFAULT, ConfigCategory::reset); - for (Method method : configClass.getMethods()) { - if (method.isAnnotationPresent(Preset.class)) { - builder.addPreset(builder.getTranslationPrefix() + method.getName(), c -> { - try { - method.invoke(null); - } catch (InvocationTargetException | IllegalAccessException e) { - LibJf.LOGGER.error("Could not apply preset", e); - } - }); - } else if (method.isAnnotationPresent(Verifier.class)) { - builder.addVerifier(c -> { - try { - method.invoke(null); - } catch (InvocationTargetException | IllegalAccessException e) { - LibJf.LOGGER.error("Could not run verifier", e); - } - }); - } - } - - for (Class categoryClass : configClass.getClasses()) { - if (categoryClass.isAnnotationPresent(Category.class)) { - String name = categoryClass.getSimpleName(); - name = Character.toLowerCase(name.charAt(0)) + name.substring(1); // camelCase - //TODO allow custom auxiliary metadata - builder.category(name, builder1 -> applyCategory(builder1, categoryClass, modId, new AuxiliaryMetadata())); - } - } - - return builder; - } - - public static void loadFrom(JsonObject source, ConfigCategory category) { - for (EntryInfo entry : category.getEntries()) { - if (source.has(entry.getName())) { - try { - entry.loadFromJson(source.get(entry.getName())); - } catch (IllegalAccessException e) { - LibJf.LOGGER.error("Could not set config entry value of " + entry.getName(), e); - } - } else LibJf.LOGGER.error("Config does not contain entry for " + entry.getName()); - } - for (Map.Entry entry : category.getCategories().entrySet()) { - if (source.has(entry.getKey())) { - JsonElement el = source.get(entry.getKey()); - if (el.isJsonObject()) loadFrom(el.getAsJsonObject(), entry.getValue()); - else LibJf.LOGGER.error("Config category is not a JSON object, skipping"); - } else LibJf.LOGGER.error("Config does not contain entry for subcategory " + entry.getKey()); - } - } - - public static void writeTo(JsonWriter writer, ConfigCategory category) throws IOException { - category.fix(); - writer.beginObject(); - String val; - for (EntryInfo entry : category.getEntries()) { - try { - entry.writeTo(writer, category.getTranslationPrefix()); - } catch (IllegalAccessException e) { - LibJf.LOGGER.error("Could not write entry", e); - } - } - for (Map.Entry entry : category.getCategories().entrySet()) { - if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(category.getTranslationPrefix() + entry.getKey() + ".title")) != null) - writer.comment(val); - writer.name(entry.getKey()); - writeTo(writer, entry.getValue()); - } - writer.endObject(); - } -} diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java b/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java deleted file mode 100644 index 7e1aa7f..0000000 --- a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; - -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.JfConfig; -import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; -import io.gitlab.jfronny.libjf.config.impl.reflect.ConfigProvider; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.entrypoint.EntrypointContainer; -import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; - -public class JfConfigReflectSafe implements PreLaunchEntrypoint { - @Override - public void onPreLaunch() { - for (EntrypointContainer config : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, JfConfig.class)) { - registerIfMissing(config.getProvider().getMetadata().getId(), config.getEntrypoint().getClass()); - } - } - - public static void registerIfMissing(String modId, Class klazz) { - if (!ConfigHolder.getInstance().isRegistered(modId)) { - ConfigProvider.register(modId, klazz); - } - } -} diff --git a/libjf-config-reflect-v0/build.gradle b/libjf-config-reflect-v1/build.gradle similarity index 55% rename from libjf-config-reflect-v0/build.gradle rename to libjf-config-reflect-v1/build.gradle index 1073736..f154005 100644 --- a/libjf-config-reflect-v0/build.gradle +++ b/libjf-config-reflect-v1/build.gradle @@ -1,7 +1,7 @@ -archivesBaseName = "libjf-config-reflect-v0" +archivesBaseName = "libjf-config-reflect-v1" dependencies { api project(path: ":libjf-base", configuration: "dev") api project(path: ":libjf-unsafe-v0", configuration: "dev") - api project(path: ":libjf-config-v1", configuration: "dev") + api project(path: ":libjf-config-core-v1", configuration: "dev") } diff --git a/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/reflect/ReflectiveConfigBuilder.java b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/reflect/ReflectiveConfigBuilder.java new file mode 100644 index 0000000..c0ac31a --- /dev/null +++ b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/v1/reflect/ReflectiveConfigBuilder.java @@ -0,0 +1,10 @@ +package io.gitlab.jfronny.libjf.config.api.v1.reflect; + +import io.gitlab.jfronny.libjf.config.api.v1.dsl.ConfigBuilder; +import io.gitlab.jfronny.libjf.config.impl.reflect.ReflectiveConfigBuilderImpl; + +public interface ReflectiveConfigBuilder extends ConfigBuilder.ConfigBuilderFunction { + static ReflectiveConfigBuilder of(String id, Class klazz) { + return new ReflectiveConfigBuilderImpl(id, klazz); + } +} diff --git a/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ReflectiveConfigBuilderImpl.java b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ReflectiveConfigBuilderImpl.java new file mode 100644 index 0000000..711eec9 --- /dev/null +++ b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ReflectiveConfigBuilderImpl.java @@ -0,0 +1,69 @@ +package io.gitlab.jfronny.libjf.config.impl.reflect; + +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.ConfigBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.reflect.ReflectiveConfigBuilder; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; +import io.gitlab.jfronny.libjf.config.impl.AuxiliaryMetadata; +import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo; + +import java.lang.reflect.*; +import java.util.Objects; + +public class ReflectiveConfigBuilderImpl implements ReflectiveConfigBuilder { + public static final String CONFIG_PRESET_DEFAULT = "libjf-config-v1.default"; + private final AuxiliaryMetadata rootMeta; + private final Class rootClass; + + public ReflectiveConfigBuilderImpl(String id, Class klazz) { + this.rootClass = Objects.requireNonNull(klazz); + this.rootMeta = AuxiliaryMetadata.of(klazz.getAnnotation(JfConfig.class)) + .merge(AuxiliaryMetadata.forMod(id)); + } + + @Override + public ConfigBuilder apply(ConfigBuilder builder) { + return applyCategory(builder, rootClass, rootMeta); + } + + private static > T applyCategory(T builder, Class configClass, AuxiliaryMetadata meta) { + meta.applyTo(builder); + for (Field field : configClass.getFields()) { + if (field.isAnnotationPresent(Entry.class)) { + builder.value(DslEntryInfo.ofField(field)); + } + } + builder.addPreset(CONFIG_PRESET_DEFAULT, ConfigCategory::reset); + for (Method method : configClass.getMethods()) { + if (method.isAnnotationPresent(Preset.class)) { + builder.addPreset(builder.getTranslationPrefix() + method.getName(), c -> { + try { + method.invoke(null); + } catch (InvocationTargetException | IllegalAccessException e) { + LibJf.LOGGER.error("Could not apply preset", e); + } + }); + } else if (method.isAnnotationPresent(Verifier.class)) { + builder.addVerifier(c -> { + try { + method.invoke(null); + } catch (InvocationTargetException | IllegalAccessException e) { + LibJf.LOGGER.error("Could not run verifier", e); + } + }); + } + } + + for (Class categoryClass : configClass.getClasses()) { + if (categoryClass.isAnnotationPresent(Category.class)) { + String name = categoryClass.getSimpleName(); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); // camelCase + builder.category(name, builder1 -> applyCategory(builder1, categoryClass, AuxiliaryMetadata.of(categoryClass.getAnnotation(Category.class)))); + } + } + + return builder; + } +} diff --git a/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java new file mode 100644 index 0000000..14d129a --- /dev/null +++ b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigReflectSafe.java @@ -0,0 +1,38 @@ +package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; + +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; +import io.gitlab.jfronny.libjf.config.api.v1.reflect.ReflectiveConfigBuilder; +import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; +import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; +import io.gitlab.jfronny.libjf.config.impl.reflect.ReflectiveConfigBuilderImpl; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.entrypoint.EntrypointContainer; +import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; + +public class JfConfigReflectSafe implements PreLaunchEntrypoint { + @Override + public void onPreLaunch() { + for (EntrypointContainer config : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, Object.class)) { + registerIfMissing(config.getProvider().getMetadata().getId(), config.getEntrypoint()); + } + } + + public static void registerIfMissing(String modId, Object config) { + if (!JfConfigSafe.REGISTERED_MODS.contains(modId)) { + JfConfigSafe.REGISTERED_MODS.add(modId); + ConfigHolder.getInstance().migrateFiles(modId); + if (config instanceof JfCustomConfig cfg) { + cfg.register(DSL.create(modId)); + } else { + Class klazz = config.getClass(); + if (klazz.isAnnotationPresent(JfConfig.class)) { + DSL.create(modId).register(ReflectiveConfigBuilder.of(modId, klazz)); + } else { + LibJf.LOGGER.error("Attempted to register improper config for mod " + modId + " (missing @JfConfig annotation or JfCustomConfig interface)"); + } + } + } + } +} diff --git a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java similarity index 54% rename from libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java rename to libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java index b6c2f95..73c20df 100644 --- a/libjf-config-reflect-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java +++ b/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/entrypoint/JfConfigUnsafe.java @@ -1,9 +1,6 @@ package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.JfConfig; -import io.gitlab.jfronny.libjf.config.api.JfCustomConfig; -import io.gitlab.jfronny.libjf.config.api.dsl.DSL; import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; import io.gitlab.jfronny.libjf.unsafe.DynamicEntry; import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit; @@ -11,11 +8,8 @@ import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit; public class JfConfigUnsafe implements UltraEarlyInit { @Override public void init() { - DynamicEntry.execute(ConfigHolderImpl.MODULE_ID, JfConfig.class, - s -> JfConfigReflectSafe.registerIfMissing(s.modId(), s.instance().getClass()) - ); - DynamicEntry.execute(ConfigHolderImpl.CUSTOM_ID, JfCustomConfig.class, - s -> s.instance().register(DSL.create(s.modId())) + DynamicEntry.execute(ConfigHolderImpl.MODULE_ID, Object.class, + s -> JfConfigReflectSafe.registerIfMissing(s.modId(), s.instance()) ); LibJf.LOGGER.info("Finished LibJF config entrypoint"); } diff --git a/libjf-config-reflect-v0/src/main/resources/fabric.mod.json b/libjf-config-reflect-v1/src/main/resources/fabric.mod.json similarity index 83% rename from libjf-config-reflect-v0/src/main/resources/fabric.mod.json rename to libjf-config-reflect-v1/src/main/resources/fabric.mod.json index 965e3df..1a9369b 100644 --- a/libjf-config-reflect-v0/src/main/resources/fabric.mod.json +++ b/libjf-config-reflect-v1/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "libjf-config-reflect-v0", + "id": "libjf-config-reflect-v1", "name": "LibJF Config Reflect", "version": "${version}", "authors": ["JFronny"], @@ -21,11 +21,9 @@ "depends": { "fabricloader": ">=0.12.0", "minecraft": "*", - "fabric-resource-loader-v0": "*", - "fabric-command-api-v2": "*", "libjf-base": ">=${version}", "libjf-unsafe-v0": ">=${version}", - "libjf-config-v1": ">=${version}" + "libjf-config-core-v1": ">=${version}" }, "custom": { "modmenu": { diff --git a/libjf-config-reflect-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java b/libjf-config-reflect-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java new file mode 100644 index 0000000..8e3955e --- /dev/null +++ b/libjf-config-reflect-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/reflect/TestConfig.java @@ -0,0 +1,58 @@ +package io.gitlab.jfronny.libjf.config.test.reflect; + +import io.gitlab.jfronny.commons.serialize.gson.api.Ignore; +import io.gitlab.jfronny.libjf.config.api.v1.*; + +import java.util.ArrayList; +import java.util.List; + +@JfConfig(referencedConfigs = {"libjf-web-v0"}) +public class TestConfig { + @Entry + public static boolean disablePacks = false; + @Entry public static Boolean disablePacks2 = false; + @Entry public static int intTest = 20; + @Entry(min = -6) public static float floatTest = -5; + @Entry(max = 21) public static double doubleTest = 20; + @Entry public static String dieStr = "lolz"; + @Entry @Ignore + public static String guiOnlyStr = "lolz"; + public static String gsonOnlyStr = "lolz"; + @Entry public static Test enumTest = Test.Test; + @Entry public static List stringList; + + @Preset + public static void moskau() { + disablePacks = true; + disablePacks2 = true; + intTest = -5; + floatTest = -6; + doubleTest = 4; + dieStr = "Moskau"; + } + + @Verifier + public static void setIntTestIfDisable() { + if (disablePacks) intTest = 0; + } + + @Verifier + public static void stringListVerifier() { + if (stringList == null) stringList = new ArrayList<>(List.of("Obama")); + } + + public enum Test { + Test, ER + } + + @Category + public static class Subcategory { + @Entry public static boolean boolInSub = false; + @Entry public static int intIbSub = 15; + + @Category + public static class Inception { + @Entry public static Test yesEnum = Test.ER; + } + } +} diff --git a/libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json b/libjf-config-reflect-v1/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json similarity index 100% rename from libjf-config-reflect-v0/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json rename to libjf-config-reflect-v1/src/testmod/resources/assets/libjf-config-v0-testmod/lang/en_us.json diff --git a/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json b/libjf-config-reflect-v1/src/testmod/resources/fabric.mod.json similarity index 66% rename from libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json rename to libjf-config-reflect-v1/src/testmod/resources/fabric.mod.json index 2affb18..78a8241 100644 --- a/libjf-config-reflect-v0/src/testmod/resources/fabric.mod.json +++ b/libjf-config-reflect-v1/src/testmod/resources/fabric.mod.json @@ -7,12 +7,5 @@ "libjf:config": [ "io.gitlab.jfronny.libjf.config.test.reflect.TestConfig" ] - }, - "custom": { - "libjf": { - "config": { - "referencedConfigs": ["libjf-web-v0"] - } - } } } \ No newline at end of file diff --git a/libjf-config-v1/build.gradle b/libjf-config-ui-tiny-v1/build.gradle similarity index 61% rename from libjf-config-v1/build.gradle rename to libjf-config-ui-tiny-v1/build.gradle index 8e0de14..e317553 100644 --- a/libjf-config-v1/build.gradle +++ b/libjf-config-ui-tiny-v1/build.gradle @@ -1,8 +1,8 @@ -archivesBaseName = "libjf-config-v1" +archivesBaseName = "libjf-config-ui-tiny-v1" dependencies { api project(path: ":libjf-base", configuration: "dev") + api project(path: ":libjf-config-core-v1", configuration: "dev") include fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}") - include modImplementation(fabricApi.module("fabric-command-api-v2", "${project.fabric_version}")) modCompileOnly("com.terraformersmc:modmenu:4.0.5") } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/ConfigScreen.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/ConfigScreen.java new file mode 100644 index 0000000..11c5e2e --- /dev/null +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/ConfigScreen.java @@ -0,0 +1,11 @@ +package io.gitlab.jfronny.libjf.config.api.v1.ui.tiny; + +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.TinyConfigScreen; +import net.minecraft.client.gui.screen.Screen; + +public interface ConfigScreen { + static Screen create(ConfigInstance config, Screen parent) { + return new TinyConfigScreen(config, parent); + } +} diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java new file mode 100644 index 0000000..8963f74 --- /dev/null +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/api/v1/ui/tiny/WidgetFactory.java @@ -0,0 +1,12 @@ +package io.gitlab.jfronny.libjf.config.api.v1.ui.tiny; + +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ClickableWidget; + +public interface WidgetFactory { + Widget build(int screenWidth, TextRenderer textRenderer, ButtonWidget done); + + record Widget(Runnable updateControls, ClickableWidget control) { + } +} diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/ModMenuAdapter.java similarity index 73% rename from libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java rename to libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/ModMenuAdapter.java index aa8d3dc..e1b49dd 100644 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/ModMenu.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/ModMenuAdapter.java @@ -1,16 +1,15 @@ -package io.gitlab.jfronny.libjf.config.impl.client; +package io.gitlab.jfronny.libjf.config.impl.ui.tiny; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.impl.client.gui.TinyConfigScreen; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; import java.util.HashMap; import java.util.Map; -public class ModMenu implements ModMenuApi { +public class ModMenuAdapter implements ModMenuApi { @Override public Map> getProvidedConfigScreenFactories() { Map> factories = new HashMap<>(); diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java similarity index 89% rename from libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java rename to libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java index 9e9f875..30bfa95 100644 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/TinyConfigScreen.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java @@ -1,9 +1,13 @@ -package io.gitlab.jfronny.libjf.config.impl.client.gui; +package io.gitlab.jfronny.libjf.config.impl.ui.tiny; import io.gitlab.jfronny.commons.throwable.Try; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.impl.client.gui.presets.PresetsScreen; +import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.EntryInfoWidgetBuilder; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.EntryListWidget; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.presets.PresetsScreen; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; @@ -29,7 +33,7 @@ public class TinyConfigScreen extends Screen { private final Screen parent; private final ConfigCategory config; private final List> widgets; - private MidnightConfigListWidget list; + private EntryListWidget list; @Override protected void init() { @@ -53,7 +57,7 @@ public class TinyConfigScreen extends Screen { Objects.requireNonNull(client).setScreen(parent); })); - this.list = new MidnightConfigListWidget(this.client, this.width, this.height, 32, this.height - 32, 25); + this.list = new EntryListWidget(this.client, this.width, this.height, 32, this.height - 32, 25); this.addSelectableChild(this.list); for (Map.Entry entry : config.getCategories().entrySet()) { this.list.addReference(width / 2, diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java similarity index 87% rename from libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java rename to libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java index bde2843..ada6053 100644 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/WidgetState.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java @@ -1,9 +1,9 @@ -package io.gitlab.jfronny.libjf.config.impl.client.gui; +package io.gitlab.jfronny.libjf.config.impl.ui.tiny; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.EntryInfo; -import io.gitlab.jfronny.libjf.config.api.WidgetFactory; +import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory; import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.text.Text; diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java similarity index 92% rename from libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java rename to libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java index d276c71..b7e7558 100644 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/EntryInfoWidgetBuilder.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java @@ -1,10 +1,13 @@ -package io.gitlab.jfronny.libjf.config.impl.client.gui; +package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry; import io.gitlab.jfronny.commons.throwable.Try; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.*; -import io.gitlab.jfronny.libjf.config.api.type.Type; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; +import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo; +import io.gitlab.jfronny.libjf.config.api.v1.type.Type; +import io.gitlab.jfronny.libjf.config.api.v1.ui.tiny.WidgetFactory; +import io.gitlab.jfronny.libjf.config.impl.ui.tiny.WidgetState; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.widget.ButtonWidget; @@ -13,7 +16,6 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.regex.Pattern; @@ -37,6 +39,7 @@ public class EntryInfoWidgetBuilder { WidgetFactory factory; if (type.isInt()) factory = textField(info, state, INTEGER_ONLY, Integer::parseInt, true, info.getMinValue(), info.getMaxValue()); + else if (type.isLong()) factory = textField(info, state, INTEGER_ONLY, Long::parseLong, true, info.getMinValue(), info.getMaxValue()); else if (type.isFloat()) factory = textField(info, state, DECIMAL_ONLY, Float::parseFloat, false, info.getMinValue(), info.getMaxValue()); else if (type.isDouble()) factory = textField(info, state, DECIMAL_ONLY, Double::parseDouble, false, info.getMinValue(), info.getMaxValue()); else if (type.isString()) factory = textField(info, state, null, String::length, true, Math.min(info.getMinValue(),0), Math.max(info.getMaxValue(),1)); diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java similarity index 83% rename from libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java rename to libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java index f1f8cec..45ac40d 100644 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/gui/MidnightConfigListWidget.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryListWidget.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.libjf.config.impl.client.gui; +package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -19,10 +19,10 @@ import java.util.function.BooleanSupplier; import java.util.function.Supplier; @Environment(EnvType.CLIENT) -public class MidnightConfigListWidget extends ElementListWidget { +public class EntryListWidget extends ElementListWidget { TextRenderer textRenderer; - public MidnightConfigListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l, int m) { + public EntryListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l, int m) { super(minecraftClient, i, j, k, l, m); this.centerListVertically = false; textRenderer = minecraftClient.textRenderer; @@ -38,7 +38,7 @@ public class MidnightConfigListWidget extends ElementListWidget targetScreen) { - this.addEntry(new ConfigListReferenceEntry(centerX, text, targetScreen)); + this.addEntry(new ConfigReferenceEntry(centerX, text, targetScreen)); } @Override @@ -47,7 +47,7 @@ public class MidnightConfigListWidget extends ElementListWidget getHoveredEntryTitle(double mouseY) { - for (ConfigListEntryAbstract abstractEntry : this.children()) { + for (ConfigEntry abstractEntry : this.children()) { if (abstractEntry instanceof ConfigScreenEntry entry && entry.button.visible && mouseY >= entry.button.y && mouseY < entry.button.y + itemHeight) { @@ -58,14 +58,14 @@ public class MidnightConfigListWidget extends ElementListWidget { + public static abstract class ConfigEntry extends Entry { } @Environment(EnvType.CLIENT) - public static class ConfigListReferenceEntry extends ConfigListEntryAbstract { + public static class ConfigReferenceEntry extends ConfigEntry { private final ClickableWidget button; - public ConfigListReferenceEntry(int centerX, Text text, Supplier targetScreen) { + public ConfigReferenceEntry(int centerX, Text text, Supplier targetScreen) { this.button = new ButtonWidget(centerX - 154, 0, 308, 20, text, btn -> MinecraftClient.getInstance().setScreen(targetScreen.get())); } @@ -87,7 +87,7 @@ public class MidnightConfigListWidget extends ElementListWidget=0.12.0", + "minecraft": "*", + "fabric-resource-loader-v0": "*", + "libjf-base": ">=${version}", + "libjf-config-core-v1": ">=${version}" + }, + "custom": { + "modmenu": { + "badges": ["library"], + "parent": "libjf" + } + } +} diff --git a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java b/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java deleted file mode 100644 index f8f18b9..0000000 --- a/libjf-config-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/client/JfConfigClient.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl.client; - -import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import net.fabricmc.api.ClientModInitializer; - -public class JfConfigClient implements ClientModInitializer { - @Override - public void onInitializeClient() { - for (ConfigInstance config : ConfigHolder.getInstance().getRegistered().values()) { - LibJf.LOGGER.info("Registering config UI for " + config.getId()); - } - } -} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java deleted file mode 100644 index a909fad..0000000 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/api/JfCustomConfig.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.gitlab.jfronny.libjf.config.api; - -import io.gitlab.jfronny.libjf.config.api.dsl.DSL; - -public interface JfCustomConfig { - void register(DSL.Defaulted dsl); -} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java deleted file mode 100644 index 7541c62..0000000 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/AuxiliaryMetadata.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl; - -import java.util.List; - -public class AuxiliaryMetadata { - public List referencedConfigs; - - public AuxiliaryMetadata sanitize() { - if (referencedConfigs == null) referencedConfigs = List.of(); - else referencedConfigs = List.copyOf(referencedConfigs); - return this; - } -} diff --git a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java b/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java deleted file mode 100644 index eaae6dc..0000000 --- a/libjf-config-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/ConfigBuilderImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.gitlab.jfronny.libjf.config.impl.dsl; - -import io.gitlab.jfronny.libjf.config.api.ConfigInstance; -import io.gitlab.jfronny.libjf.config.api.dsl.CategoryBuilder; -import io.gitlab.jfronny.libjf.config.api.dsl.ConfigBuilder; - -import java.nio.file.Path; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -public class ConfigBuilderImpl extends CategoryBuilderImpl implements ConfigBuilder { - public DslConfigInstance built; - public Consumer load = s -> {}; - public Consumer write = s -> {}; - public Path path; - - public ConfigBuilderImpl(String id) { - super(id, ""); - } - - @Override - public ConfigBuilderImpl setLoadMethod(Consumer load) { - checkBuilt(); - this.load = load; - return this; - } - - public ConfigBuilderImpl setWriteMethod(Consumer write) { - checkBuilt(); - this.write = write; - return this; - } - - public ConfigBuilderImpl setPath(Path path) { - checkBuilt(); - this.path = path; - return this; - } - - @Override - public DslConfigInstance build() { - markBuilt(); - built = new DslConfigInstance(id, - translationPrefix, - entries, - presets, - () -> referencedConfigs.stream().map(Supplier::get).toList(), - categories.stream().collect(Collectors.toMap(CategoryBuilder::getId, b -> b.build(() -> built))), - () -> built, - verifiers, - load, - write, - path); - built.load(); - return built; - } -} diff --git a/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java b/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java deleted file mode 100644 index a1e6880..0000000 --- a/libjf-config-v1/src/testmod/java/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.gitlab.jfronny.libjf.config.test; - -import io.gitlab.jfronny.libjf.config.api.JfCustomConfig; -import io.gitlab.jfronny.libjf.config.api.dsl.DSL; - -public class TestConfigCustom { - public static Integer someValue = 10; - public static String someOther = "Yes"; - - public static class SomeCategory { - public static Boolean someBool = true; - } - - public static class LibJF_Companion implements JfCustomConfig { - @Override - public void register(DSL.Defaulted dsl) { - dsl.register(builder -> builder - .referenceConfig("libjf-config-reflect-v0-testmod") - .value("someValue", 10, -50, 50, () -> someValue, v -> someValue = v) - .value("someOther", "Yes", () -> someOther, v -> someOther = v) - .category("someCategory", builder1 -> builder1 - .value("someBool", true, () -> SomeCategory.someBool, v -> SomeCategory.someBool = v) - ) - ); - } - } - - static { - new LibJF_Companion().register(DSL.create("libjf-config-v1-testmod")); - } -} diff --git a/libjf-devutil-v0/build.gradle b/libjf-devutil/build.gradle similarity index 100% rename from libjf-devutil-v0/build.gradle rename to libjf-devutil/build.gradle diff --git a/libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/MinecraftClientMixin.java b/libjf-devutil/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/MinecraftClientMixin.java similarity index 100% rename from libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/MinecraftClientMixin.java rename to libjf-devutil/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/MinecraftClientMixin.java diff --git a/libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java b/libjf-devutil/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java similarity index 100% rename from libjf-devutil-v0/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java rename to libjf-devutil/src/client/java/io/gitlab/jfronny/libjf/devutil/mixin/ProfileKeysMixin.java diff --git a/libjf-devutil-v0/src/client/resources/libjf-devutil-v0.client.mixins.json b/libjf-devutil/src/client/resources/libjf-devutil-v0.client.mixins.json similarity index 100% rename from libjf-devutil-v0/src/client/resources/libjf-devutil-v0.client.mixins.json rename to libjf-devutil/src/client/resources/libjf-devutil-v0.client.mixins.json diff --git a/libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/DevUtilMain.java b/libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/DevUtilMain.java similarity index 100% rename from libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/DevUtilMain.java rename to libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/DevUtilMain.java diff --git a/libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/NoOpUserApi.java b/libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/NoOpUserApi.java similarity index 100% rename from libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/NoOpUserApi.java rename to libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/NoOpUserApi.java diff --git a/libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/ArgumentTypesMixin.java b/libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/ArgumentTypesMixin.java similarity index 100% rename from libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/ArgumentTypesMixin.java rename to libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/ArgumentTypesMixin.java diff --git a/libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/CommandManagerMixin.java b/libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/CommandManagerMixin.java similarity index 100% rename from libjf-devutil-v0/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/CommandManagerMixin.java rename to libjf-devutil/src/main/java/io/gitlab/jfronny/libjf/devutil/mixin/CommandManagerMixin.java diff --git a/libjf-devutil-v0/src/main/resources/fabric.mod.json b/libjf-devutil/src/main/resources/fabric.mod.json similarity index 100% rename from libjf-devutil-v0/src/main/resources/fabric.mod.json rename to libjf-devutil/src/main/resources/fabric.mod.json diff --git a/libjf-devutil-v0/src/main/resources/libjf-devutil-v0.mixins.json b/libjf-devutil/src/main/resources/libjf-devutil-v0.mixins.json similarity index 100% rename from libjf-devutil-v0/src/main/resources/libjf-devutil-v0.mixins.json rename to libjf-devutil/src/main/resources/libjf-devutil-v0.mixins.json diff --git a/libjf-translate-v1/build.gradle b/libjf-translate-v1/build.gradle index 41032df..253300b 100644 --- a/libjf-translate-v1/build.gradle +++ b/libjf-translate-v1/build.gradle @@ -2,5 +2,5 @@ archivesBaseName = "libjf-translate-v1" dependencies { api project(path: ":libjf-base", configuration: "dev") - api project(path: ":libjf-config-v1", configuration: "dev") + api project(path: ":libjf-config-core-v1", configuration: "dev") } diff --git a/libjf-translate-v1/src/main/java/io/gitlab/jfronny/libjf/translate/impl/TranslateConfig.java b/libjf-translate-v1/src/main/java/io/gitlab/jfronny/libjf/translate/impl/TranslateConfig.java index a813ea0..adc1cf7 100644 --- a/libjf-translate-v1/src/main/java/io/gitlab/jfronny/libjf/translate/impl/TranslateConfig.java +++ b/libjf-translate-v1/src/main/java/io/gitlab/jfronny/libjf/translate/impl/TranslateConfig.java @@ -1,21 +1,31 @@ package io.gitlab.jfronny.libjf.translate.impl; -import io.gitlab.jfronny.libjf.config.api.Entry; -import io.gitlab.jfronny.libjf.config.api.JfConfig; -import io.gitlab.jfronny.libjf.config.api.Verifier; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; -public class TranslateConfig implements JfConfig { - @Entry public static Translator translationService = Translator.Google; - @Entry public static String libreTranslateHost = "https://translate.argosopentech.com"; +public class TranslateConfig implements JfCustomConfig { + public static Translator translationService = Translator.Google; + public static String libreTranslateHost = "https://translate.argosopentech.com"; - @Verifier public static void ensureValid() { if (translationService == null) translationService = Translator.Google; if (translationService == Translator.LibreTranslate && libreTranslateHost == null || libreTranslateHost.isBlank()) libreTranslateHost = "https://translate.argosopentech.com"; } + @Override + public void register(DSL.Defaulted dsl) { + } + public enum Translator { Google, LibreTranslate, Noop } + + static { + DSL.create("libjf-translate-v1").register(b -> b + .value("translationService", translationService, Translator.class, () -> translationService, v -> translationService = v) + .value("libreTranslateHost", libreTranslateHost, () -> libreTranslateHost, v -> libreTranslateHost = v) + .addVerifier(TranslateConfig::ensureValid) + ); + } } diff --git a/libjf-web-v0/build.gradle b/libjf-web-v0/build.gradle index 2a12ac6..258ebf7 100644 --- a/libjf-web-v0/build.gradle +++ b/libjf-web-v0/build.gradle @@ -2,6 +2,6 @@ archivesBaseName = "libjf-web-v0" dependencies { api project(path: ":libjf-base", configuration: "dev") - api project(path: ":libjf-config-v1", configuration: "dev") + api project(path: ":libjf-config-core-v1", configuration: "dev") include modImplementation(fabricApi.module("fabric-command-api-v2", "${rootProject.fabric_version}")) } diff --git a/libjf-web-v0/src/main/java/io/gitlab/jfronny/libjf/web/impl/JfWebConfig.java b/libjf-web-v0/src/main/java/io/gitlab/jfronny/libjf/web/impl/JfWebConfig.java index f4c4981..4ba56ea 100644 --- a/libjf-web-v0/src/main/java/io/gitlab/jfronny/libjf/web/impl/JfWebConfig.java +++ b/libjf-web-v0/src/main/java/io/gitlab/jfronny/libjf/web/impl/JfWebConfig.java @@ -1,23 +1,17 @@ package io.gitlab.jfronny.libjf.web.impl; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.api.ConfigHolder; -import io.gitlab.jfronny.libjf.config.api.Entry; -import io.gitlab.jfronny.libjf.config.api.JfConfig; +import io.gitlab.jfronny.libjf.config.api.v1.*; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; import java.io.IOException; import java.net.ServerSocket; -public class JfWebConfig implements JfConfig { - @Entry +public class JfWebConfig implements JfCustomConfig { public static String serverIp = "http://127.0.0.1"; - @Entry public static int port = 0; - @Entry public static int portOverride = -1; - @Entry public static int maxConnections = 20; - @Entry public static boolean enableFileHost = false; public static void ensureValidPort() { @@ -30,4 +24,15 @@ public class JfWebConfig implements JfConfig { ConfigHolder.getInstance().getRegistered().get("libjf-web-v0").write(); } } + + @Override + public void register(DSL.Defaulted dsl) { + dsl.register(b -> b + .value("serverIp", serverIp, () -> serverIp, v -> serverIp = v) + .value("port", port, 0, 35535, () -> port, v -> port = v) + .value("portOverride", portOverride, -1, 35535, () -> portOverride, v -> portOverride = v) + .value("maxConnections", maxConnections, 8, 64, () -> maxConnections, v -> maxConnections = v) + .value("enableFileHost", enableFileHost, () -> enableFileHost, v -> enableFileHost = v) + ); + } } diff --git a/libjf-web-v0/src/main/resources/fabric.mod.json b/libjf-web-v0/src/main/resources/fabric.mod.json index a4e3ad0..8f38558 100644 --- a/libjf-web-v0/src/main/resources/fabric.mod.json +++ b/libjf-web-v0/src/main/resources/fabric.mod.json @@ -21,7 +21,7 @@ "fabricloader": ">=0.12.0", "minecraft": "*", "libjf-base": ">=${version}", - "libjf-config-v1": ">=${version}", + "libjf-config-core-v1": ">=${version}", "fabric-command-api-v2": "*" }, "custom": { diff --git a/settings.gradle b/settings.gradle index fc979f3..563b6d9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,11 +12,16 @@ rootProject.name = "libjf" include 'libjf-base' -include 'libjf-config-v1' -include 'libjf-config-reflect-v0' +include 'libjf-config-core-v1' +include 'libjf-config-reflect-v1' +include 'libjf-config-commands-v1' +include 'libjf-config-ui-tiny-v1' +include 'libjf-config-legacy-shim' include 'libjf-data-v0' include 'libjf-data-manipulation-v0' -include 'libjf-devutil-v0' +include 'libjf-devutil' include 'libjf-translate-v1' include 'libjf-unsafe-v0' include 'libjf-web-v0' + +include 'libjf-config-compiler-plugin' From 52f7f5570155f8be44492802bb1d584ae0051a84 Mon Sep 17 00:00:00 2001 From: JFronny Date: Sat, 27 Aug 2022 17:41:56 +0200 Subject: [PATCH 4/4] [config] Various fixes --- extract.sh | 4 ---- libjf-config-compiler-plugin/.gitignore | 1 + libjf-config-compiler-plugin/build.gradle | 10 ++++++++- .../libjf/config/plugin/ConfigInjectTask.java | 2 -- .../asm/ConfigInjectClassTransformer.java | 22 +++++++++++++------ .../plugin/asm/value/DiscoveredValue.java | 15 +++++++++---- .../config/plugin/util/GeneratorAdapter2.java | 22 +++++++++++++------ .../libjf/config/impl/dsl/DslEntryInfo.java | 8 +++---- .../config/impl/ui/tiny/TinyConfigScreen.java | 13 +++++++---- .../config/impl/ui/tiny/WidgetState.java | 6 ++++- .../ui/tiny/entry/EntryInfoWidgetBuilder.java | 2 +- .../impl/ui/tiny/presets/PresetsScreen.java | 8 +++---- 12 files changed, 74 insertions(+), 39 deletions(-) delete mode 100644 extract.sh create mode 100644 libjf-config-compiler-plugin/.gitignore diff --git a/extract.sh b/extract.sh deleted file mode 100644 index 0e2f01a..0000000 --- a/extract.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/zsh -cd ~/Downloads -java -cp asm-9.3.jar:asm-analysis-9.3.jar:asm-tree-9.3.jar:asm-util-9.3.jar org.objectweb.asm.util.ASMifier /mnt/Projects/Minecraft/LibJF/libjf-config-core-v1/build/classes/java/testmod/io/gitlab/jfronny/libjf/config/test/TestConfigCustom\$SomeCategory.class > /mnt/Projects/Minecraft/LibJF/libjf-config-compiler-plugin/src/main/java/asm/io/gitlab/jfronny/libjf/config/test/TestConfigCustom\$SomeCategoryDump.java -java -cp asm-9.3.jar:asm-analysis-9.3.jar:asm-tree-9.3.jar:asm-util-9.3.jar org.objectweb.asm.util.ASMifier /mnt/Projects/Minecraft/LibJF/libjf-config-core-v1/build/classes/java/testmod/io/gitlab/jfronny/libjf/config/test/TestConfigCustom.class > /mnt/Projects/Minecraft/LibJF/libjf-config-compiler-plugin/src/main/java/asm/io/gitlab/jfronny/libjf/config/test/TestConfigCustomDump.java \ No newline at end of file diff --git a/libjf-config-compiler-plugin/.gitignore b/libjf-config-compiler-plugin/.gitignore new file mode 100644 index 0000000..e13dd92 --- /dev/null +++ b/libjf-config-compiler-plugin/.gitignore @@ -0,0 +1 @@ +src/main/java/io/gitlab/jfronny/libjf/config/plugin/BuildMetadata.java \ No newline at end of file diff --git a/libjf-config-compiler-plugin/build.gradle b/libjf-config-compiler-plugin/build.gradle index 7de2f53..a6cf83c 100644 --- a/libjf-config-compiler-plugin/build.gradle +++ b/libjf-config-compiler-plugin/build.gradle @@ -57,4 +57,12 @@ publishing { } tasks.publish.dependsOn(tasks.build) -rootProject.tasks.deployDebug.dependsOn(tasks.publish) \ No newline at end of file +rootProject.tasks.deployDebug.dependsOn(tasks.publish) + +java.nio.file.Files.writeString(java.nio.file.Path.of("$projectDir/src/main/java/io/gitlab/jfronny/libjf/config/plugin/BuildMetadata.java"), """ +package io.gitlab.jfronny.libjf.config.plugin; + +public class BuildMetadata { + public static final boolean isRelease = ${project.hasProperty("release")}; +} +""") \ No newline at end of file diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java index a9feadc..65e850f 100644 --- a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/ConfigInjectTask.java @@ -18,8 +18,6 @@ public abstract class ConfigInjectTask extends Jar { super(); setDuplicatesStrategy(DuplicatesStrategy.FAIL); versionUtil = new GradleVersionUtil(getProject().getGradle().getGradleVersion()); - //TODO ensure this is unneeded -// setManifest(new DefaultInheritManifest(getServices().get(FileResolver.class))); } @Override diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java index 9160f02..8116ff1 100644 --- a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/ConfigInjectClassTransformer.java @@ -2,6 +2,7 @@ package io.gitlab.jfronny.libjf.config.plugin.asm; import io.gitlab.jfronny.libjf.config.api.v1.Category; import io.gitlab.jfronny.libjf.config.api.v1.JfConfig; +import io.gitlab.jfronny.libjf.config.plugin.BuildMetadata; import io.gitlab.jfronny.libjf.config.plugin.util.ClInitInjectVisitor; import io.gitlab.jfronny.libjf.config.plugin.util.GeneratorAdapter2; import io.gitlab.jfronny.libjf.config.plugin.asm.value.DiscoveredValue; @@ -119,14 +120,16 @@ public class ConfigInjectClassTransformer extends ClassVisitor { // Generate main method { GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, INIT_METHOD, "()V", null, null); - // Migrate paths + + m.comment("Migrate files from old paths"); m.invokeIStatic(CONFIG_HOLDER_TYPE, new Method("getInstance", CONFIG_HOLDER_TYPE, new Type[0])); m.push(modId); m.invokeInterface(CONFIG_HOLDER_TYPE, new Method("migrateFiles", "(Ljava/lang/String;)V")); - // Invoke DSL + + m.comment("Invoke DSL, continue in " + BUILDER_ROOT); m.push(modId); m.invokeIStatic(DSL_TYPE, new Method("create", DSL_DEFAULTED_TYPE, new Type[]{STRING_TYPE})); - m.lambda("apply", + m.λ("apply", getMethodDescriptor(CONFIG_BUILDER_FUNCTION_TYPE), getMethodType(CONFIG_BUILDER_TYPE, CONFIG_BUILDER_TYPE), current.getInternalName(), @@ -142,6 +145,7 @@ public class ConfigInjectClassTransformer extends ClassVisitor { // This also ensures the static initializer is called { GeneratorAdapter2 m = method(ACC_PUBLIC | ACC_STATIC, REGISTER_METHOD, getMethodDescriptor(VOID_TYPE, DSL_DEFAULTED_TYPE), null, null); + m.comment("This placeholder method is referenced in the FMJ to ensure the static initializer is called"); m.returnValue(); m.endMethod(); } @@ -152,12 +156,14 @@ public class ConfigInjectClassTransformer extends ClassVisitor { GeneratorAdapter2 m = method(ACC_PRIVATE | ACC_STATIC, BUILDER_ROOT, getMethodDescriptor(builderType, builderType), null, null); m.loadArg(0); for (String name : referencedConfigs) { + m.comment("Reference the config of \"" + name + "\""); m.push(name); dslInvoke(m, "referenceConfig", CATEGORY_BUILDER_TYPE, STRING_TYPE); } for (String name : categories) { + m.comment("Register the category defined in " + name); m.push(camelCase(name.substring(current.getInternalName().length()))); - m.lambda("apply", + m.λ("apply", getMethodDescriptor(CATEGORY_BUILDER_FUNCTION_TYPE), getMethodType(CATEGORY_BUILDER_TYPE, CATEGORY_BUILDER_TYPE), name, @@ -165,21 +171,24 @@ public class ConfigInjectClassTransformer extends ClassVisitor { dslInvoke(m, "category", CATEGORY_BUILDER_TYPE, STRING_TYPE, CATEGORY_BUILDER_FUNCTION_TYPE); } for (String verifier : verifiers) { + m.comment("Register the verifier \"" + verifier + "\""); m.runnable(current.getInternalName(), verifier); dslInvoke(m, "addVerifier", CATEGORY_BUILDER_TYPE, RUNNABLE_TYPE); } for (String preset : presets) { + m.comment("Register the preset \"" + preset + "\""); m.push(preset); m.runnable(current.getInternalName(), preset); dslInvoke(m, "addPreset", CATEGORY_BUILDER_TYPE, STRING_TYPE, RUNNABLE_TYPE); } for (DiscoveredValue value : values) { + m.comment("Register the value \"" + value + "\""); value.generateRegistration(m, this); } m.returnValue(); m.endMethod(); for (DiscoveredValue value : values) { - value.generateLamdaMethods(this); + value.generateλ(this); } } super.visitEnd(); @@ -197,8 +206,7 @@ public class ConfigInjectClassTransformer extends ClassVisitor { } public GeneratorAdapter2 method(int access, String name, String descriptor, String signature, String[] exceptions) { - //TODO re-enable to hide generated code - //access |= ACC_SYNTHETIC; + if (BuildMetadata.isRelease && !name.equals(CLINIT)) access |= ACC_SYNTHETIC; return new GeneratorAdapter2(super.visitMethod(access, name, descriptor, signature, exceptions), access, name, descriptor); } } diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java index ed80eb1..16de2d7 100644 --- a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/asm/value/DiscoveredValue.java @@ -31,10 +31,14 @@ public class DiscoveredValue { if (type != KnownType.OBJECT) { m.push(name); m.getStatic(t.current, name, aType); - m.unbox(type.unboxed); // Unbox (as parameter is unboxed) or leave as is (if target is unboxed) + if (!aType.equals(type.unboxed)) { + m.comment("Unboxing value " + name + " from " + aType + " to " + type.unboxed); + m.unbox(type.unboxed); // Unbox (as parameter is unboxed) or leave as is (if target is unboxed) + } } switch (type) { case INT, LONG, FLOAT, DOUBLE -> { + m.comment("Numeric type"); m.push(min); m.push(max); m.supplier(t.current.getInternalName(), gName(), gDesc()); @@ -42,12 +46,14 @@ public class DiscoveredValue { t.dslInvoke(m, "value", CATEGORY_BUILDER_TYPE, STRING_TYPE, type.unboxed, DOUBLE_TYPE, DOUBLE_TYPE, SUPPLIER_TYPE, CONSUMER_TYPE); } case BOOLEAN, STRING -> { + m.comment("Simple one-value type"); m.supplier(t.current.getInternalName(), gName(), gDesc()); m.consumer(t.current.getInternalName(), sName(), sDesc()); t.dslInvoke(m, "value", CATEGORY_BUILDER_TYPE, STRING_TYPE, type.unboxed, SUPPLIER_TYPE, CONSUMER_TYPE); } case OBJECT -> { System.err.println("WARNING: Attempted to use unsupported type in config. The entry \"" + name + "\" will fall back to reflective runtime access!"); + m.comment("Reflective access due to missing compatibility for the type \"" + aType + "\""); m.push(t.current); m.push(name); m.invokeIStatic(ENTRY_INFO_TYPE, new Method("ofField", ENTRY_INFO_TYPE, new Type[]{CLASS_TYPE, STRING_TYPE})); @@ -56,16 +62,17 @@ public class DiscoveredValue { } } - public void generateLamdaMethods(ConfigInjectClassTransformer t) { + public void generateλ(ConfigInjectClassTransformer t) { GeneratorAdapter2 m = t.method(ACC_PRIVATE | ACC_STATIC, gName(), gDesc().getInternalName(), null, null); m.getStatic(t.current, name, aType); - m.box(aType); // Box target or leave as is (if target is boxed) + m.comment("Boxing from " + aType + " (" + aType.getSort() + ")"); + m.box(aType); // Box if target field uses unboxed value m.returnValue(); m.endMethod(); m = t.method(ACC_PRIVATE | ACC_STATIC, sName(), sDesc().getInternalName(), null, null); m.loadArg(0); - m.unbox(aType); // Unbox to target type or leave as is (if target is boxed) + m.unbox(aType); // Unbox to the target fields type m.putStatic(t.current, name, aType); m.returnValue(); m.endMethod(); diff --git a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java index 9be9449..a853b51 100644 --- a/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java +++ b/libjf-config-compiler-plugin/src/main/java/io/gitlab/jfronny/libjf/config/plugin/util/GeneratorAdapter2.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.libjf.config.plugin.util; +import io.gitlab.jfronny.libjf.config.plugin.BuildMetadata; import io.gitlab.jfronny.libjf.config.plugin.asm.value.KnownTypes; import org.objectweb.asm.*; import org.objectweb.asm.commons.GeneratorAdapter; @@ -23,7 +24,7 @@ public class GeneratorAdapter2 extends GeneratorAdapter { super(ASM9, methodVisitor, access, name, descriptor); } - public void lambda(String lambdaMethodName, String lambdaType, Type lambdaDescriptor, Type implDescriptor, String implOwner, String implName) { + public void λ(String lambdaMethodName, String lambdaType, Type lambdaDescriptor, Type implDescriptor, String implOwner, String implName) { invokeDynamic(lambdaMethodName, lambdaType, metafactory, @@ -32,12 +33,12 @@ public class GeneratorAdapter2 extends GeneratorAdapter { implDescriptor); } - public void lambda(String lambdaMethodName, String lambdaType, Type descriptor, String implOwner, String implName) { - lambda(lambdaMethodName, lambdaType, descriptor, descriptor, implOwner, implName); + public void λ(String lambdaMethodName, String lambdaType, Type descriptor, String implOwner, String implName) { + λ(lambdaMethodName, lambdaType, descriptor, descriptor, implOwner, implName); } public void runnable(String implOwner, String implName) { - lambda("run", + λ("run", "()Ljava/lang/Runnable;", Type.getType("()V"), implOwner, @@ -45,7 +46,7 @@ public class GeneratorAdapter2 extends GeneratorAdapter { } public void supplier(String implOwner, String implName, Type implDescriptor) { - lambda("get", + λ("get", "()Ljava/util/function/Supplier;", Type.getType("()Ljava/lang/Object;"), implDescriptor, @@ -54,7 +55,7 @@ public class GeneratorAdapter2 extends GeneratorAdapter { } public void consumer(String implOwner, String implName, Type implDescriptor) { - lambda("accept", + λ("accept", "()Ljava/util/function/Consumer;", Type.getType("(Ljava/lang/Object;)V"), implDescriptor, @@ -96,9 +97,10 @@ public class GeneratorAdapter2 extends GeneratorAdapter { break; } if (boxedType == null) { + comment("Unkown boxed type, simply casting"); checkCast(type); } else { - checkCast(boxedType); + comment("Boxing " + type + " to " + boxedType); invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[]{type})); } } @@ -107,6 +109,12 @@ public class GeneratorAdapter2 extends GeneratorAdapter { invokeInsn(Opcodes.INVOKESTATIC, owner, method, true); } + public void comment(String comment) { + if (BuildMetadata.isRelease) return; + push("[Comment/Generator] " + comment); + pop(); + } + // Taken from GeneratorAdapter private void invokeInsn(final int opcode, final Type type, final Method method, final boolean isInterface) { String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName(); diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java index cf91e97..eabf942 100644 --- a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java @@ -109,11 +109,11 @@ public class DslEntryInfo implements EntryInfo { } final Object valueOriginal = value; if (value instanceof final Integer v) { - if (v < minValue) value = minValue; - if (v > maxValue) value = maxValue; + if (v < minValue) value = (int)minValue; + if (v > maxValue) value = (int)maxValue; } else if (value instanceof final Float v) { - if (v < minValue) value = minValue; - if (v > maxValue) value = maxValue; + if (v < minValue) value = (float)minValue; + if (v > maxValue) value = (float)maxValue; } else if (value instanceof final Double v) { if (v < minValue) value = minValue; if (v > maxValue) value = maxValue; diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java index 30bfa95..e0adc74 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/TinyConfigScreen.java @@ -40,10 +40,15 @@ public class TinyConfigScreen extends Screen { super.init(); config.fix(); + for (WidgetState widget : widgets) { + widget.updateCache(); + } - this.addDrawableChild(new ButtonWidget(4, 6, 80, 20, Text.translatable("libjf-config-v1.presets"), button -> { - MinecraftClient.getInstance().setScreen(new PresetsScreen(this, config)); - })); + if (!config.getPresets().isEmpty()) { + this.addDrawableChild(new ButtonWidget(4, 6, 80, 20, Text.translatable("libjf-config-v1.presets"), button -> { + Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config)); + })); + } this.addDrawableChild(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> { Objects.requireNonNull(client).setScreen(parent); @@ -118,6 +123,6 @@ public class TinyConfigScreen extends Screen { @Override public void close() { - MinecraftClient.getInstance().setScreen(parent); + Objects.requireNonNull(client).setScreen(parent); } } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java index ada6053..97e883c 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/WidgetState.java @@ -23,10 +23,14 @@ public class WidgetState { this.entry = entry; this.knownStates = knownStates; this.factory = factory; + updateCache(); + } + + public void updateCache() { try { updateCache(entry.getValue()); } catch (IllegalAccessException e) { - LibJf.LOGGER.error("Could not create initial widget state cache", e); + LibJf.LOGGER.error("Could not set widget state cache to current value", e); } } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java index b7e7558..e141eeb 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/entry/EntryInfoWidgetBuilder.java @@ -54,7 +54,7 @@ public class EntryInfoWidgetBuilder { return values[index >= values.length ? 0 : index]; }, value -> Text.translatable(config.getTranslationPrefix() + "enum." + type.getName() + "." + state.cachedValue)); } else { - LibJf.LOGGER.error("Invalid entry type in " + info.getName() + ": " + type.getName()); + LibJf.LOGGER.error("Unsupported entry type in " + info.getName() + ": " + type.getName() + " - not displaying config control"); factory = ((screenWidth, textRenderer, done) -> new WidgetFactory.Widget(() -> {}, new ButtonWidget(-10, 0, 0, 0, Text.of(""), null))); } diff --git a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/presets/PresetsScreen.java b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/presets/PresetsScreen.java index 948960b..6b1f9bd 100644 --- a/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/presets/PresetsScreen.java +++ b/libjf-config-ui-tiny-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/tiny/presets/PresetsScreen.java @@ -4,13 +4,13 @@ import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.v1.ConfigCategory; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.text.*; +import net.minecraft.text.Text; import java.util.Map; +import java.util.Objects; @Environment(EnvType.CLIENT) public class PresetsScreen extends Screen { @@ -35,7 +35,7 @@ public class PresetsScreen extends Screen { LibJf.LOGGER.info("Preset selected: " + entry.getKey()); entry.getValue().run(); config.fix(); - MinecraftClient.getInstance().setScreen(parent); + Objects.requireNonNull(client).setScreen(parent); })); } this.addSelectableChild(this.list); @@ -43,7 +43,7 @@ public class PresetsScreen extends Screen { @Override public void close() { - MinecraftClient.getInstance().setScreen(parent); + Objects.requireNonNull(client).setScreen(parent); } @Override