Refactor Config into ConfigInstance and ConfigInstanceImpl

This commit is contained in:
Johannes Frohnmeyer 2022-02-12 10:13:19 +01:00
parent 46fcb539de
commit e0e5aaba99
Signed by: Johannes
GPG Key ID: E76429612C2929F4
10 changed files with 94 additions and 55 deletions

View File

@ -28,9 +28,12 @@ You MAY annotate fields as @GsonHidden to not serialize them (-> [libjf-base](li
Numeric values MAY have a min and max value specified in their @Entry.
To register a config, add a `libjf:config` entrypoint pointing to its class to your fabric.mod.json.
To manually register a config or save changes, use `io.gitlab.jfronny.libjf.config.api.ConfigHolder`
To manually register a config or save changes, use `io.gitlab.jfronny.libjf.config.api.ConfigInstance`
For example, to save a config for a mod titled `yourmod`:
```java
// Directly using ConfigInstance
ConfigInstance.get("yourmod").write();
// Using ConfigHolder
ConfigHolder.getInstance().get("yourmod").write();
```

View File

@ -1,6 +1,5 @@
package io.gitlab.jfronny.libjf.config.api;
import io.gitlab.jfronny.libjf.config.impl.Config;
import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl;
import java.util.Map;
@ -10,8 +9,8 @@ public interface ConfigHolder {
return ConfigHolderImpl.INSTANCE;
}
void register(String modId, Class<?> config);
Map<String, Config> getRegistered();
Config get(Class<?> configClass);
Config get(String configClass);
Map<String, ConfigInstance> getRegistered();
ConfigInstance get(Class<?> configClass);
ConfigInstance get(String modId);
boolean isRegistered(Class<?> config);
}

View File

@ -0,0 +1,23 @@
package io.gitlab.jfronny.libjf.config.api;
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
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 syncToClass();
void syncFromClass();
void write();
String getModId();
boolean matchesConfigClass(Class<?> candidate);
List<EntryInfo> getEntries();
Map<String, Runnable> getPresets();
}

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.libjf.config.impl;
import com.google.common.collect.ImmutableMap;
import io.gitlab.jfronny.libjf.config.api.ConfigHolder;
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
import org.jetbrains.annotations.ApiStatus;
import java.util.HashMap;
@ -11,36 +12,36 @@ public class ConfigHolderImpl implements ConfigHolder {
@ApiStatus.Internal
public static final ConfigHolderImpl INSTANCE = new ConfigHolderImpl();
private ConfigHolderImpl() {}
private final Map<String, Config> configs = new HashMap<>();
private final Map<String, ConfigInstance> configs = new HashMap<>();
@Override
public void register(String modId, Class<?> config) {
if (!isRegistered(config))
configs.put(modId, new Config(modId, config));
configs.put(modId, new ConfigInstanceImpl(modId, config));
}
@Override
public Map<String, Config> getRegistered() {
public Map<String, ConfigInstance> getRegistered() {
return ImmutableMap.copyOf(configs);
}
@Override
public Config get(Class<?> configClass) {
for (Config value : configs.values()) {
if (value.configClass.equals(configClass))
public ConfigInstance get(Class<?> configClass) {
for (ConfigInstance value : configs.values()) {
if (value.matchesConfigClass(configClass))
return value;
}
return null;
}
@Override
public Config get(String configClass) {
public ConfigInstance get(String configClass) {
return configs.get(configClass);
}
public boolean isRegistered(Class<?> config) {
for (Config value : configs.values()) {
if (value.configClass.equals(config))
for (ConfigInstance value : configs.values()) {
if (value.matchesConfigClass(config))
return true;
}
return false;

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.libjf.config.impl;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
import io.gitlab.jfronny.libjf.config.api.Entry;
import io.gitlab.jfronny.libjf.config.api.Preset;
import io.gitlab.jfronny.libjf.config.api.Verifier;
@ -16,7 +17,7 @@ import java.util.*;
/** Based on https://github.com/TeamMidnightDust/MidnightLib which is based on https://github.com/Minenash/TinyConfig
* Credits to TeamMidnightDust and Minenash */
public class Config {
public class ConfigInstanceImpl implements ConfigInstance {
public final List<EntryInfo> entries = new ArrayList<>();
public final Map<String, Runnable> presets = new LinkedHashMap<>();
public final Set<Runnable> verifiers = new LinkedHashSet<>();
@ -24,7 +25,7 @@ public class Config {
public final String modid;
public final Class<?> configClass;
public Config(String modid, Class<?> config) {
public ConfigInstanceImpl(String modid, Class<?> config) {
this.modid = modid;
configClass = config;
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
@ -101,6 +102,7 @@ public class Config {
load();
}
@Override
public void load() {
try {
LibJf.GSON.fromJson(Files.newBufferedReader(path), configClass);
@ -112,6 +114,7 @@ public class Config {
write();
}
@Override
public void syncToClass() {
for (EntryInfo info : entries) {
try {
@ -123,6 +126,7 @@ public class Config {
syncFromClass();
}
@Override
public void syncFromClass() {
for (Runnable verifier : verifiers) {
verifier.run();
@ -138,6 +142,7 @@ public class Config {
}
}
@Override
public void write() {
try {
if (!Files.exists(path)) Files.createFile(path);
@ -146,4 +151,24 @@ public class Config {
e.printStackTrace();
}
}
@Override
public String getModId() {
return modid;
}
@Override
public boolean matchesConfigClass(Class<?> candidate) {
return candidate != null && candidate.isAssignableFrom(configClass);
}
@Override
public List<EntryInfo> getEntries() {
return entries;
}
@Override
public Map<String, Runnable> getPresets() {
return presets;
}
}

View File

@ -3,6 +3,7 @@ package io.gitlab.jfronny.libjf.config.impl;
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.config.impl.gui.TinyConfigScreen;
import io.gitlab.jfronny.libjf.LibJf;
@ -13,14 +14,14 @@ public class ModMenu implements ModMenuApi {
@Override
public Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
Map<String, ConfigScreenFactory<?>> factories = new HashMap<>();
for (Map.Entry<String, Config> entry : ConfigHolder.getInstance().getRegistered().entrySet()) {
for (Map.Entry<String, ConfigInstance> entry : ConfigHolder.getInstance().getRegistered().entrySet()) {
if (!LibJf.MOD_ID.equals(entry.getKey()))
factories.put(entry.getKey(), buildFactory(entry.getValue()));
}
return factories;
}
private static ConfigScreenFactory<?> buildFactory(Config config) {
private static ConfigScreenFactory<?> buildFactory(ConfigInstance config) {
return s -> new TinyConfigScreen(s, config);
}
}

View File

@ -1,17 +1,16 @@
package io.gitlab.jfronny.libjf.config.impl.entry;
import io.gitlab.jfronny.libjf.config.api.ConfigHolder;
import io.gitlab.jfronny.libjf.config.impl.Config;
import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl;
import io.gitlab.jfronny.libjf.config.impl.gui.EntryInfoWidgetBuilder;
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.impl.gui.EntryInfoWidgetBuilder;
import net.fabricmc.api.ClientModInitializer;
public class JfConfigClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
for (Config config : ConfigHolder.getInstance().getRegistered().values()) {
LibJf.LOGGER.info("Registring config UI for " + config.modid);
for (ConfigInstance config : ConfigHolder.getInstance().getRegistered().values()) {
LibJf.LOGGER.info("Registring config UI for " + config.getModId());
EntryInfoWidgetBuilder.initConfig(config);
}
}

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.libjf.config.impl.gui;
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
import io.gitlab.jfronny.libjf.config.api.Entry;
import io.gitlab.jfronny.libjf.config.impl.Config;
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
import io.gitlab.jfronny.libjf.gson.GsonHidden;
import net.fabricmc.api.EnvType;
@ -26,8 +26,8 @@ public class EntryInfoWidgetBuilder {
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
public static void initConfig(Config config) {
for (EntryInfo info : config.entries) {
public static void initConfig(ConfigInstance config) {
for (EntryInfo info : config.getEntries()) {
if (info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))
try {
initEntry(config, info);
@ -36,7 +36,7 @@ public class EntryInfoWidgetBuilder {
}
}
private static void initEntry(Config config, EntryInfo info) {
private static void initEntry(ConfigInstance config, EntryInfo info) {
if (!(info.field.isAnnotationPresent(io.gitlab.jfronny.libjf.config.api.Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))) return;
Class<?> type = info.field.getType();
info.width = info.entry != null ? info.entry.width() : 0;
@ -55,7 +55,7 @@ public class EntryInfoWidgetBuilder {
}, func);
} else if (type.isEnum()) {
List<?> values = Arrays.asList(info.field.getType().getEnumConstants());
Function<Object,Text> func = value -> new TranslatableText(config.modid + ".jfconfig." + "enum." + type.getSimpleName() + "." + info.value.toString());
Function<Object,Text> func = value -> new TranslatableText(config.getModId() + ".jfconfig." + "enum." + type.getSimpleName() + "." + info.value.toString());
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object, Text>>(button -> {
int index = values.indexOf(info.value) + 1;
info.value = values.get(index >= values.size()? 0 : index);
@ -70,7 +70,7 @@ public class EntryInfoWidgetBuilder {
}
}
private static void textField(Config config, EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
private static void textField(ConfigInstance config, EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
boolean isNumber = pattern != null;
info.widget = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
s = s.trim();
@ -90,7 +90,7 @@ public class EntryInfoWidgetBuilder {
info.tempValue = s;
t.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
info.inLimits = inLimits;
b.active = config.entries.stream().allMatch(e -> e.inLimits);
b.active = config.getEntries().stream().allMatch(e -> e.inLimits);
if (inLimits)
info.value = isNumber? value : s;

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.libjf.config.impl.gui;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.impl.Config;
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
import io.gitlab.jfronny.libjf.config.api.Entry;
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
import io.gitlab.jfronny.libjf.config.impl.gui.presets.PresetsScreen;
@ -28,15 +28,15 @@ import java.util.function.Predicate;
@Environment(EnvType.CLIENT)
public class TinyConfigScreen extends Screen {
public TinyConfigScreen(Screen parent, Config config) {
super(new TranslatableText(config.modid + ".jfconfig." + "title"));
public TinyConfigScreen(Screen parent, ConfigInstance config) {
super(new TranslatableText(config.getModId() + ".jfconfig." + "title"));
this.parent = parent;
this.config = config;
this.translationPrefix = config.modid + ".jfconfig.";
this.translationPrefix = config.getModId() + ".jfconfig.";
}
private final String translationPrefix;
private final Screen parent;
private final Config config;
private final ConfigInstance config;
private MidnightConfigListWidget list;
// Real Time config update //
@ -56,19 +56,7 @@ public class TinyConfigScreen extends Screen {
}));
this.addDrawableChild(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> {
try {
LibJf.GSON.fromJson(Files.newBufferedReader(config.path), config.configClass); }
catch (Exception e) { config.write(); }
for (EntryInfo info : config.entries) {
if (info.field.isAnnotationPresent(Entry.class)) {
try {
info.value = info.field.get(null);
info.tempValue = info.value.toString();
} catch (IllegalAccessException ignored) {
}
}
}
config.load();
Objects.requireNonNull(client).setScreen(parent);
}));
@ -80,7 +68,7 @@ public class TinyConfigScreen extends Screen {
this.list = new MidnightConfigListWidget(this.client, this.width, this.height, 32, this.height - 32, 25);
this.addSelectableChild(this.list);
for (EntryInfo info : config.entries) {
for (EntryInfo info : config.getEntries()) {
TranslatableText name = new TranslatableText(translationPrefix + info.field.getName());
ButtonWidget resetButton = new ButtonWidget(width - 155, 0, 40, 20, new LiteralText("Reset").formatted(Formatting.RED), (button -> {
info.value = info.defaultValue;
@ -117,7 +105,7 @@ public class TinyConfigScreen extends Screen {
Optional<ClickableWidget> widget = list.getHoveredButton(mouseY);
if (widget.isPresent()) {
for (EntryInfo info : config.entries) {
for (EntryInfo info : config.getEntries()) {
ClickableWidget buttonWidget = widget.get();
Text text = ConfigScreenEntry.buttonsWithText.get(buttonWidget);
TranslatableText name = new TranslatableText(this.translationPrefix + info.field.getName());

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.libjf.config.impl.gui.presets;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.impl.Config;
import io.gitlab.jfronny.libjf.config.api.ConfigInstance;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
@ -15,10 +15,10 @@ import java.util.Map;
@Environment(EnvType.CLIENT)
public class PresetsScreen extends Screen {
private final Screen parent;
private final Config config;
private final ConfigInstance config;
private PresetListWidget list;
public PresetsScreen(Screen parent, Config config) {
public PresetsScreen(Screen parent, ConfigInstance config) {
super(new TranslatableText("libjf-config-v0.presets"));
this.parent = parent;
this.config = config;
@ -28,7 +28,7 @@ public class PresetsScreen extends Screen {
protected void init() {
super.init();
this.list = new PresetListWidget(this.client, this.width, this.height, 32, this.height - 32, 25);
for (Map.Entry<String, Runnable> entry : config.presets.entrySet()) {
for (Map.Entry<String, Runnable> entry : config.getPresets().entrySet()) {
this.list.addButton(new ButtonWidget(width / 2 - 100, 0, 200, 20,
new TranslatableText(entry.getKey()),
button -> {