From 699d27afd21bbec9596ef9a69c49cde131de335a Mon Sep 17 00:00:00 2001 From: coolGi2007 Date: Tue, 28 Dec 2021 10:50:32 +0000 Subject: [PATCH] More universal config (works with java 8 & 16) --- .../lod/common/wrappers/config/ConfigGui.java | 207 +++++++++--------- fabric/src/main/resources/fabric.mod.json | 6 +- 2 files changed, 108 insertions(+), 105 deletions(-) diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java b/common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java index e4ea374d1..dcd014d61 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java @@ -15,6 +15,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; +import java.util.stream.Collectors; // Logger (for debug stuff) import org.apache.logging.log4j.LogManager; @@ -41,7 +42,6 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; @@ -49,13 +49,14 @@ import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.client.resources.language.I18n; // translation import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.narration.NarratableEntry; // Remove in 1.16 /** * Based upon TinyConfig * https://github.com/Minenash/TinyConfig * * This config should work for both Fabric and Forge as long as you use Mojang mappings - * + * * Credits to Motschen * * @author coolGi2007 @@ -78,26 +79,26 @@ public abstract class ConfigGui The buttons that don't show are still loaded but just not rendered The screen with is set to double so the scroll bar doesn't show */ - - + + private static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)"); private static final Pattern DECIMAL_ONLY_REGEX = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)"); - + private static final List entries = new ArrayList<>(); // Change these to your own mod private static final String MOD_NAME = ModInfo.NAME; // For file saving and identifying private static final String MOD_NAME_READABLE = ModInfo.READABLE_NAME; // For logs -// private static final Logger LOGGER = ClientApi.LOGGER; // For logs + // private static final Logger LOGGER = ClientApi.LOGGER; // For logs private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME); // For logs (this inits before ClientAPI so this is a temp fix) - - - - + + + + //==============// // Initializers // //==============// - + private static class ConfigScreenConfigs { // This contains all the configs for the configs @@ -105,7 +106,7 @@ public abstract class ConfigGui public static final int ButtonWidthSpacing = 5; public static final int ResetButtonWidth = 40; } - + protected static class EntryInfo { Field field; @@ -130,14 +131,14 @@ public abstract class ConfigGui } private static Path configFilePath; - - - + + + public static void init(Class config) { Minecraft mc = Minecraft.getInstance(); configFilePath = mc.gameDirectory.toPath().resolve("config").resolve(MOD_NAME + ".toml"); - + initNestedClass(config); for (EntryInfo info : entries) { @@ -152,7 +153,7 @@ public abstract class ConfigGui loadFromFile(); } - + private static void initNestedClass(Class config) { for (Field field : config.getFields()) @@ -164,7 +165,7 @@ public abstract class ConfigGui if (!LodCommonMain.serverSided) initClient(field, info); } - + if (field.isAnnotationPresent(ConfigAnnotations.Entry.class)) { info.varClass = field.getType(); @@ -174,7 +175,7 @@ public abstract class ConfigGui } catch (IllegalAccessException ignored) {} } - + if (field.isAnnotationPresent(ConfigAnnotations.ScreenEntry.class)) initNestedClass(field.getType()); @@ -182,7 +183,7 @@ public abstract class ConfigGui info.field = field; } } - + /** This adds the buttons to the queue to be rendered */ private static void initClient(Field field, EntryInfo info) { @@ -190,21 +191,21 @@ public abstract class ConfigGui ConfigAnnotations.Category category = field.getAnnotation(ConfigAnnotations.Category.class); ConfigAnnotations.Entry entry = field.getAnnotation(ConfigAnnotations.Entry.class); ConfigAnnotations.ScreenEntry screenEntry = field.getAnnotation(ConfigAnnotations.ScreenEntry.class); - + if (entry != null) info.width = entry.width(); else if (screenEntry != null) info.width = screenEntry.width(); info.category = category != null ? category.value() : ""; - - + + if (entry != null) { if (!entry.name().equals("")) info.name = new TranslatableComponent(entry.name()); - + if (fieldClass == int.class) { // For int @@ -216,13 +217,13 @@ public abstract class ConfigGui textField(info, Double::parseDouble, DECIMAL_ONLY_REGEX, entry.minValue(), entry.maxValue(), false); } else if (fieldClass == String.class || fieldClass == List.class) - { + { // For string or list info.max = entry.maxValue() == Double.MAX_VALUE ? Integer.MAX_VALUE : (int) entry.maxValue(); textField(info, String::length, null, Math.min(entry.minValue(), 0), Math.max(entry.maxValue(), 1), true); } else if (fieldClass == boolean.class) - { + { // For boolean Function func = value -> new TextComponent((Boolean) value ? "True" : "False").withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED); info.widget = new AbstractMap.SimpleEntry>(button -> { @@ -231,7 +232,7 @@ public abstract class ConfigGui }, func); } else if (fieldClass.isEnum()) - { + { // For enum List values = Arrays.asList(field.getType().getEnumConstants()); Function func = value -> new TranslatableComponent(MOD_NAME + ".config." + "enum." + fieldClass.getSimpleName() + "." + info.value.toString()); @@ -246,30 +247,30 @@ public abstract class ConfigGui { if (!screenEntry.name().equals("")) info.name = new TranslatableComponent(screenEntry.name()); - + info.screenButton = true; - info.gotoScreen = (!info.category.isBlank() ? info.category + "." : "") + field.getName(); + info.gotoScreen = (!info.category.isEmpty() ? info.category + "." : "") + field.getName(); } entries.add(info); } - - - - + + + + /** creates a text field */ private static void textField(EntryInfo info, Function func, Pattern pattern, double minValue, double maxValue, boolean cast) { boolean isNumber = pattern != null; - info.widget = (BiFunction>) (editBox, button) -> stringValue -> + info.widget = (BiFunction>) (editBox, button) -> stringValue -> { stringValue = stringValue.trim(); - if (!(stringValue.isBlank() || !isNumber || pattern.matcher(stringValue).matches())) + if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches())) return false; - + Number value = 0; boolean inLimits = false; info.error = null; - if (isNumber && !stringValue.isBlank() && !stringValue.equals("-") && !stringValue.equals(".")) + if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals(".")) { value = func.apply(stringValue); inLimits = value.doubleValue() >= minValue && value.doubleValue() <= maxValue; @@ -277,13 +278,13 @@ public abstract class ConfigGui "§cMinimum " + "length" + (cast ? " is " + (int) minValue : " is " + minValue) : "§cMaximum " + "length" + (cast ? " is " + (int) maxValue : " is " + maxValue))); } - + info.tempValue = stringValue; editBox.setTextColor(inLimits ? 0xFFFFFFFF : 0xFFFF7777); info.inLimits = inLimits; button.active = entries.stream().allMatch(e -> e.inLimits); - - + + if (inLimits && info.field.getType() != List.class) { info.value = value; @@ -294,18 +295,18 @@ public abstract class ConfigGui ((List) info.value).add(""); ((List) info.value).set(info.index, Arrays.stream(info.tempValue.replace("[", "").replace("]", "").split(", ")).toList().get(0)); } - + return true; }; } - - - - + + + + //===============// // File Handling // //===============// - + /** Grabs what is in the config and puts it in modid.toml */ public static void saveToFile() { @@ -326,14 +327,14 @@ public abstract class ConfigGui for (EntryInfo info : entries) { if (info.field.isAnnotationPresent(ConfigAnnotations.Entry.class)) { - config.set((info.category.isBlank() ? "" : info.category + ".") + info.field.getName(), info.value); + config.set((info.category.isEmpty() ? "" : info.category + ".") + info.field.getName(), info.value); } } config.save(); config.close(); } - + /** * Grabs what is in modid.toml and puts it into the config * If the file doesn't exist then it runs saveToFile @@ -371,8 +372,8 @@ public abstract class ConfigGui config.close(); } - - + + public static Screen getScreen(Screen parent, String category) { return new ConfigScreen(parent, category); @@ -383,45 +384,45 @@ public abstract class ConfigGui protected ConfigScreen(Screen parent, String category) { super(new TranslatableComponent( - I18n.exists(MOD_NAME + ".config" + (category.isBlank()? "." + category : "") + ".title") ? + I18n.exists(MOD_NAME + ".config" + (category.isEmpty()? "." + category : "") + ".title") ? MOD_NAME + ".config.title" : - MOD_NAME + ".config" + (category.isBlank() ? "" : "." + category) + ".title") + MOD_NAME + ".config" + (category.isEmpty() ? "" : "." + category) + ".title") ); this.parent = parent; this.category = category; this.translationPrefix = MOD_NAME + ".config."; } - + private final String translationPrefix; private final Screen parent; private String category; private ConfigListWidget list; private boolean reload = false; - + // Real Time config update // @Override public void tick() { super.tick(); } - + @Override protected void init() { super.init(); if (!reload) loadFromFile(); - - this.addRenderableWidget(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> { + + this.addWidget(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> { loadFromFile(); Objects.requireNonNull(minecraft).setScreen(parent); })); - - Button done = this.addRenderableWidget(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> { + + Button done = this.addWidget(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> { saveToFile(); Objects.requireNonNull(minecraft).setScreen(parent); })); - + this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 25); if (this.minecraft != null && this.minecraft.level != null) this.list.setRenderBackground(false); @@ -430,7 +431,7 @@ public abstract class ConfigGui { if (info.category.matches(category) && !info.hideOption) { - TranslatableComponent name = Objects.requireNonNullElseGet(info.name, () -> new TranslatableComponent(translationPrefix + (!info.category.isBlank() ? info.category + "." : "") + info.field.getName())); + TranslatableComponent name = (info.name == null ? new TranslatableComponent(translationPrefix + (!info.category.isEmpty() ? info.category + "." : "") + info.field.getName()) : info.name); Button resetButton = new Button(this.width - ConfigScreenConfigs.SpaceFromRightScreen - info.width - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth, 0, ConfigScreenConfigs.ResetButtonWidth, 20, new TextComponent("Reset").withStyle(ChatFormatting.RED), (button -> { info.value = info.defaultValue; info.tempValue = info.defaultValue.toString(); @@ -438,7 +439,7 @@ public abstract class ConfigGui this.reload = true; Objects.requireNonNull(minecraft).setScreen(this); })); - + if (info.widget instanceof Map.Entry) { Map.Entry> widget = (Map.Entry>) info.widget; @@ -492,9 +493,9 @@ public abstract class ConfigGui } } } - + } - + @Override public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { @@ -503,53 +504,53 @@ public abstract class ConfigGui drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title // Render the tooltip only if it can find a tooltip in the language file - for (EntryInfo info : entries) { + for (EntryInfo info : entries) { if (info.category.matches(category) && !info.hideOption) { - if (list.getHoveredButton(mouseX,mouseY).isPresent()) { - AbstractWidget buttonWidget = list.getHoveredButton(mouseX,mouseY).get(); - Component text = ButtonEntry.buttonsWithText.get(buttonWidget); - TranslatableComponent name = new TranslatableComponent(this.translationPrefix + (info.category.isBlank() ? "" : info.category + ".") + info.field.getName()); - String key = translationPrefix + (info.category.isBlank() ? "" : info.category + ".") + info.field.getName() + ".@tooltip"; + if (list.getHoveredButton(mouseX,mouseY).isPresent()) { + AbstractWidget buttonWidget = list.getHoveredButton(mouseX,mouseY).get(); + Component text = ButtonEntry.buttonsWithText.get(buttonWidget); + TranslatableComponent name = new TranslatableComponent(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName()); + String key = translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName() + ".@tooltip"; - if (info.error != null && text.equals(name)) renderTooltip(matrices, (Component) info.error.getValue(), mouseX, mouseY); - else if (I18n.exists(key) && text.equals(name)) { - List list = new ArrayList<>(); - for (String str : I18n.get(key).split("\n")) - list.add(new TextComponent(str)); + if (info.error != null && text.equals(name)) renderTooltip(matrices, (Component) info.error.getValue(), mouseX, mouseY); + else if (I18n.exists(key) && text.equals(name)) { + List list = new ArrayList<>(); + for (String str : I18n.get(key).split("\n")) + list.add(new TextComponent(str)); renderComponentTooltip(matrices, list, mouseX, mouseY); - } - } - } - } + } + } + } + } super.render(matrices, mouseX, mouseY, delta); } } - - - - + + + + public static class ConfigListWidget extends ContainerObjectSelectionList { Font textRenderer; - + public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m) { super(minecraftClient, i, j, k, l, m); this.centerListVertically = false; textRenderer = minecraftClient.font; } - + public void addButton(AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text) { this.addEntry(ButtonEntry.create(button, text, resetButton, indexButton)); } - + @Override public int getRowWidth() { return 10000; } - + public Optional getHoveredButton(double mouseX, double mouseY) { for (ButtonEntry buttonEntry : this.children()) @@ -562,11 +563,11 @@ public abstract class ConfigGui return Optional.empty(); } } - - - - - + + + + + public static class ButtonEntry extends ContainerObjectSelectionList.Entry { private static final Font textRenderer = Minecraft.getInstance().font; @@ -576,7 +577,7 @@ public abstract class ConfigGui private final Component text; private final List children = new ArrayList<>(); public static final Map buttonsWithText = new HashMap<>(); - + private ButtonEntry(AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton) { buttonsWithText.put(button, text); @@ -591,12 +592,12 @@ public abstract class ConfigGui if (indexButton != null) children.add(indexButton); } - + public static ButtonEntry create(AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton) { return new ButtonEntry(button, text, resetButton, indexButton); } - + @Override public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { @@ -618,17 +619,19 @@ public abstract class ConfigGui if (text != null && (!text.getString().contains("spacer") || button != null)) GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); } - + @Override public List children() { return children; } - - @Override - public List narratables() - { - return children; - } + + // Only for 1.17 and over + // Remove in 1.16 and below + @Override + public List narratables() + { + return children; + } } } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index c9461a657..986471131 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -6,9 +6,9 @@ "name": "Distant Horizons Fabric", "description": "This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow.", "authors": [ - "James Seibel", - "Leonardo Amato", - "Cola", + "James Seibel", + "Leonardo Amato", + "Cola", "coolGi2007", "Ran" ],