feat(config-core): Configurable config watch service
This commit is contained in:
parent
a1a6d22510
commit
e2a891c2e2
|
@ -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<Type> configClasses) {
|
||||
JsonObject fmj = INPUT_GSON.<JsonElement>fromJson(reader, JsonElement.class).getAsJsonObject();
|
||||
if (!fmj.has(ENTRYPOINTS)) fmj.add(ENTRYPOINTS, new JsonObject());
|
||||
|
|
|
@ -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<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
|
||||
Map<String, ConfigScreenFactory<?>> factories = new HashMap<>();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<String, ConfigInstance> configs = new HashMap<>();
|
||||
private final Map<Path, ConfigInstance> configsByPath = new HashMap<>();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Object> 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<Object> 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 -> {
|
||||
|
|
|
@ -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 <TEx extends Throwable> void lock(Path p, ThrowingRunnable<TEx> task) throws TEx {
|
||||
JfConfigWatchServiceImpl.lock(p, task
|
||||
.compose(() -> LibJf.LOGGER.info("Locking"))
|
||||
.andThen(() -> LibJf.LOGGER.info("Ran task after lock"))
|
||||
);
|
||||
}
|
||||
|
||||
void executeIteration();
|
||||
}
|
|
@ -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<JfConfigWatchService> REGISTERED_INSTANCES = new HashSet<>();
|
||||
private static final Set<JfConfigWatchServiceImpl> REGISTERED_INSTANCES = new HashSet<>();
|
||||
private final WatchService service;
|
||||
private static final Map<Path, Integer> locked = new HashMap<>();
|
||||
public static <TEx extends Throwable> void lock(Path p, ThrowingRunnable<TEx> 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) {
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"]
|
||||
},
|
||||
|
|
|
@ -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<Object> config : FabricLoader.getInstance().getEntrypointContainers(ConfigHolderImpl.MODULE_ID, Object.class)) {
|
||||
for (EntrypointContainer<Object> 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));
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue