From e2a891c2e2691ed169decda4844c17c62bd220e8 Mon Sep 17 00:00:00 2001 From: JFronny Date: Sun, 9 Jul 2023 13:42:08 +0200 Subject: [PATCH] feat(config-core): Configurable config watch service --- .../plugin/fmj/FabricModJsonTransformer.java | 4 +- .../libjf/config/impl/ui/ModMenuAdapter.java | 6 +++ .../libjf/config/impl/AuxiliaryMetadata.java | 2 +- .../jfronny/libjf/config/impl/ConfigCore.java | 17 +++++++ .../libjf/config/impl/ConfigHolderImpl.java | 2 +- .../config/impl/dsl/ConfigBuilderImpl.java | 14 ++++-- .../config/impl/entrypoint/JfConfigSafe.java | 10 ++-- .../impl/watch/JfConfigWatchService.java | 18 ++++++++ .../JfConfigWatchServiceImpl.java} | 22 ++++----- .../watch/ToggleableConfigWatchService.java | 46 +++++++++++++++++++ .../libjf-config-core-v1/lang/en_us.json | 5 ++ .../src/main/resources/fabric.mod.json | 2 +- .../entrypoint/JfConfigReflectSafe.java | 7 ++- .../reflect/entrypoint/JfConfigUnsafe.java | 4 +- 14 files changed, 126 insertions(+), 33 deletions(-) create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigCore.java create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/JfConfigWatchService.java rename libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/{JfConfigWatchService.java => watch/JfConfigWatchServiceImpl.java} (82%) create mode 100644 libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/ToggleableConfigWatchService.java create mode 100644 libjf-config-core-v1/src/main/resources/assets/libjf-config-core-v1/lang/en_us.json 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 index 4cde1c1..f8ae541 100644 --- 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 @@ -3,7 +3,7 @@ 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.impl.ConfigCore; import io.gitlab.jfronny.libjf.config.plugin.asm.ConfigInjectClassTransformer; import org.objectweb.asm.Type; @@ -14,7 +14,7 @@ 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; + private static final String LIBJF_CONFIG = ConfigCore.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()); diff --git a/libjf-config-core-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/ModMenuAdapter.java b/libjf-config-core-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/ModMenuAdapter.java index 4739f92..6656c8f 100644 --- a/libjf-config-core-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/ModMenuAdapter.java +++ b/libjf-config-core-v1/src/client/java/io/gitlab/jfronny/libjf/config/impl/ui/ModMenuAdapter.java @@ -5,11 +5,17 @@ import com.terraformersmc.modmenu.api.ModMenuApi; import io.gitlab.jfronny.libjf.LibJf; 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.ConfigCore; import java.util.HashMap; import java.util.Map; public class ModMenuAdapter implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return buildFactory(ConfigCore.CONFIG_INSTANCE); + } + @Override public Map> getProvidedConfigScreenFactories() { Map> factories = new HashMap<>(); 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 index 854eebb..fb5d2ab 100644 --- 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 @@ -12,7 +12,7 @@ import org.jetbrains.annotations.Nullable; import java.util.LinkedList; import java.util.List; -import static io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl.MODULE_ID; +import static io.gitlab.jfronny.libjf.config.impl.ConfigCore.MODULE_ID; public class AuxiliaryMetadata { public static AuxiliaryMetadata of(Category category) { diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigCore.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigCore.java new file mode 100644 index 0000000..bff80d2 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigCore.java @@ -0,0 +1,17 @@ +package io.gitlab.jfronny.libjf.config.impl; + +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.dsl.DSL; + +public class ConfigCore { + public static final String MOD_ID = "libjf-config-core-v1"; + public static final String MODULE_ID = "libjf:config"; + public static boolean watchForChanges = true; + public static final ConfigInstance CONFIG_INSTANCE; + + static { + CONFIG_INSTANCE = DSL.create(MOD_ID).register(builder -> builder + .value("watchForChanges", watchForChanges, () -> watchForChanges, b -> watchForChanges = b) + ); + } +} diff --git a/libjf-config-core-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 index e06a08b..f5c7b04 100644 --- a/libjf-config-core-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 @@ -18,7 +18,7 @@ public class ConfigHolderImpl implements ConfigHolder { 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<>(); 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 index 36b48a0..ae51d1a 100644 --- 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 @@ -1,16 +1,22 @@ package io.gitlab.jfronny.libjf.config.impl.dsl; import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders; -import io.gitlab.jfronny.gson.*; +import io.gitlab.jfronny.gson.JsonElement; +import io.gitlab.jfronny.gson.JsonObject; +import io.gitlab.jfronny.gson.JsonParser; 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.ConfigCategory; +import io.gitlab.jfronny.libjf.config.api.v1.ConfigInstance; +import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo; 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 io.gitlab.jfronny.libjf.config.impl.watch.JfConfigWatchService; import net.fabricmc.loader.api.FabricLoader; -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; diff --git a/libjf-config-core-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 index 8bbcf39..636a934 100644 --- a/libjf-config-core-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 @@ -3,7 +3,7 @@ package io.gitlab.jfronny.libjf.config.impl.entrypoint; import io.gitlab.jfronny.libjf.LibJf; 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 io.gitlab.jfronny.libjf.config.impl.ConfigCore; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.entrypoint.EntrypointContainer; import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; @@ -19,10 +19,10 @@ public class JfConfigSafe implements PreLaunchEntrypoint { @Override public void onPreLaunch() { LibJf.setup(); - 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())); + for (EntrypointContainer custom : FabricLoader.getInstance().getEntrypointContainers(ConfigCore.MODULE_ID, Object.class)) { + String id = custom.getProvider().getMetadata().getId(); + if (custom.getEntrypoint() instanceof JfCustomConfig cfg && REGISTERED_MODS.add(id)) { + cfg.register(DSL.create(id)); } } TRANSLATION_SUPPLIER = s -> { diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/JfConfigWatchService.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/JfConfigWatchService.java new file mode 100644 index 0000000..0ca4750 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/JfConfigWatchService.java @@ -0,0 +1,18 @@ +package io.gitlab.jfronny.libjf.config.impl.watch; + +import io.gitlab.jfronny.commons.throwable.ThrowingRunnable; +import io.gitlab.jfronny.libjf.LibJf; + +import java.io.Closeable; +import java.nio.file.Path; + +public interface JfConfigWatchService extends Closeable { + static void lock(Path p, ThrowingRunnable task) throws TEx { + JfConfigWatchServiceImpl.lock(p, task + .compose(() -> LibJf.LOGGER.info("Locking")) + .andThen(() -> LibJf.LOGGER.info("Ran task after lock")) + ); + } + + void executeIteration(); +} diff --git a/libjf-config-core-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/watch/JfConfigWatchServiceImpl.java similarity index 82% rename from libjf-config-core-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/watch/JfConfigWatchServiceImpl.java index 44127aa..58e4ed0 100644 --- a/libjf-config-core-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/watch/JfConfigWatchServiceImpl.java @@ -1,34 +1,35 @@ -package io.gitlab.jfronny.libjf.config.impl; +package io.gitlab.jfronny.libjf.config.impl.watch; import io.gitlab.jfronny.commons.throwable.ThrowingRunnable; import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.config.api.v1.ConfigHolder; -import io.gitlab.jfronny.libjf.coprocess.ThreadCoProcess; import net.fabricmc.loader.api.FabricLoader; -import java.io.Closeable; import java.io.IOException; import java.nio.file.*; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import static java.nio.file.StandardWatchEventKinds.*; -public class JfConfigWatchService extends ThreadCoProcess implements Closeable { +public class JfConfigWatchServiceImpl implements JfConfigWatchService { private static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir(); - private static final Set REGISTERED_INSTANCES = new HashSet<>(); + private static final Set REGISTERED_INSTANCES = new HashSet<>(); private final WatchService service; private static final Map locked = new HashMap<>(); public static void lock(Path p, ThrowingRunnable task) throws TEx { synchronized (CONFIG_DIR) { locked.compute(p, (p1, val) -> val == null ? 1 : val + 1); task.run(); - for (JfConfigWatchService instance : REGISTERED_INSTANCES) { + for (JfConfigWatchServiceImpl instance : REGISTERED_INSTANCES) { instance.executeIteration(); } } } - public JfConfigWatchService() { + public JfConfigWatchServiceImpl() { WatchService ws = null; try { ws = FileSystems.getDefault().newWatchService(); @@ -49,11 +50,6 @@ public class JfConfigWatchService extends ThreadCoProcess implements Closeable { @Override public void executeIteration() { - try { - Thread.sleep(1000); - } catch (InterruptedException ignored) { - return; - } synchronized (CONFIG_DIR) { WatchKey key = service.poll(); if (key != null) { diff --git a/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/ToggleableConfigWatchService.java b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/ToggleableConfigWatchService.java new file mode 100644 index 0000000..0cc2ef8 --- /dev/null +++ b/libjf-config-core-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/watch/ToggleableConfigWatchService.java @@ -0,0 +1,46 @@ +package io.gitlab.jfronny.libjf.config.impl.watch; + +import io.gitlab.jfronny.libjf.LibJf; +import io.gitlab.jfronny.libjf.config.impl.ConfigCore; +import io.gitlab.jfronny.libjf.coprocess.ThreadCoProcess; + +import java.io.Closeable; +import java.io.IOException; + +public class ToggleableConfigWatchService extends ThreadCoProcess implements Closeable { + private JfConfigWatchService delegate = null; + + @Override + public void executeIteration() { + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) { + return; + } + if (ConfigCore.watchForChanges) { + if (delegate == null) { + delegate = new JfConfigWatchServiceImpl(); + LibJf.LOGGER.info("Created config watch service"); + } + delegate.executeIteration(); + } else { + if (delegate != null) { + try { + delegate.close(); + } catch (IOException ignored) { + } + delegate = null; + LibJf.LOGGER.info("Discarded config watch service implementation"); + } + } + } + + @Override + public void close() throws IOException { + if (delegate != null) { + delegate.close(); + delegate = null; + LibJf.LOGGER.info("Discarded config watch service"); + } + } +} diff --git a/libjf-config-core-v1/src/main/resources/assets/libjf-config-core-v1/lang/en_us.json b/libjf-config-core-v1/src/main/resources/assets/libjf-config-core-v1/lang/en_us.json new file mode 100644 index 0000000..91b2da2 --- /dev/null +++ b/libjf-config-core-v1/src/main/resources/assets/libjf-config-core-v1/lang/en_us.json @@ -0,0 +1,5 @@ +{ + "libjf-config-core-v1.jfconfig.title": "LibJF Config", + "libjf-config-core-v1.jfconfig.watchForChanges": "Watch for changes", + "libjf-config-core-v1.jfconfig.watchForChanges.tooltip": "Automatically reload configs when they are changed" +} \ No newline at end of file diff --git a/libjf-config-core-v1/src/main/resources/fabric.mod.json b/libjf-config-core-v1/src/main/resources/fabric.mod.json index d80d07e..7c378cb 100644 --- a/libjf-config-core-v1/src/main/resources/fabric.mod.json +++ b/libjf-config-core-v1/src/main/resources/fabric.mod.json @@ -14,7 +14,7 @@ "environment": "*", "entrypoints": { "preLaunch": ["io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe"], - "libjf:coprocess": ["io.gitlab.jfronny.libjf.config.impl.JfConfigWatchService"], + "libjf:coprocess": ["io.gitlab.jfronny.libjf.config.impl.watch.ToggleableConfigWatchService"], "libjf:config_screen": ["io.gitlab.jfronny.libjf.config.impl.ui.PlaceholderScreenFactory"], "modmenu": ["io.gitlab.jfronny.libjf.config.impl.ui.ModMenuAdapter"] }, 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 index 5539637..36dcb9c 100644 --- 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 @@ -4,7 +4,7 @@ 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.ConfigCore; import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.entrypoint.EntrypointContainer; @@ -14,14 +14,13 @@ public class JfConfigReflectSafe implements PreLaunchEntrypoint { @Override public void onPreLaunch() { LibJf.setup(); - for (EntrypointContainer config : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, Object.class)) { + for (EntrypointContainer config : FabricLoader.getInstance().getEntrypointContainers(ConfigCore.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); + if (JfConfigSafe.REGISTERED_MODS.add(modId)) { ConfigHolder.getInstance().migrateFiles(modId); if (config instanceof JfCustomConfig cfg) { cfg.register(DSL.create(modId)); diff --git a/libjf-config-reflect-v1/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 index 73c20df..dce4269 100644 --- a/libjf-config-reflect-v1/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,14 +1,14 @@ package io.gitlab.jfronny.libjf.config.impl.reflect.entrypoint; import io.gitlab.jfronny.libjf.LibJf; -import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl; +import io.gitlab.jfronny.libjf.config.impl.ConfigCore; 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, Object.class, + DynamicEntry.execute(ConfigCore.MODULE_ID, Object.class, s -> JfConfigReflectSafe.registerIfMissing(s.modId(), s.instance()) ); LibJf.LOGGER.info("Finished LibJF config entrypoint");