diff --git a/common/src/main/java/com/seibel/lod/common/Config.java b/common/src/main/java/com/seibel/lod/common/Config.java index 533f7fd56..d8c16d9f9 100644 --- a/common/src/main/java/com/seibel/lod/common/Config.java +++ b/common/src/main/java/com/seibel/lod/common/Config.java @@ -94,7 +94,7 @@ public class Config extends ConfigGui public static HorizontalResolution drawResolution = IQuality.DRAW_RESOLUTION_DEFAULT; @Category("client.graphics.quality") - @Entry(min = 16, max = 1024) + @Entry(minValue = 16, maxValue = 1024) public static int lodChunkRenderDistance = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX.defaultValue; @Category("client.graphics.quality") @@ -102,7 +102,7 @@ public class Config extends ConfigGui public static VerticalQuality verticalQuality = IQuality.VERTICAL_QUALITY_DEFAULT; @Category("client.graphics.quality") - @Entry(min = 2, max = 32) + @Entry(minValue = 2, maxValue = 32) public static int horizontalScale = IQuality.HORIZONTAL_SCALE_MIN_DEFAULT_MAX.defaultValue; @Category("client.graphics.quality") @@ -191,11 +191,11 @@ public class Config extends ConfigGui public static class Threading { @Category("client.advanced.threading") - @Entry(min = 1, max = 50) + @Entry(minValue = 1, maxValue = 50) public static int numberOfWorldGenerationThreads = IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT.defaultValue; @Category("client.advanced.threading") - @Entry(min = 1, max = 50) + @Entry(minValue = 1, maxValue = 50) public static int numberOfBufferBuilderThreads = IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX.defaultValue; } @@ -223,7 +223,7 @@ public class Config extends ConfigGui public static GpuUploadMethod gpuUploadMethod = IBuffers.GPU_UPLOAD_METHOD_DEFAULT; @Category("client.advanced.buffers") - @Entry(min = 0, max = 5000) + @Entry(minValue = 0, maxValue = 5000) public static int gpuUploadTimeoutInMilleseconds = IBuffers.GPU_UPLOAD_TIMEOUT_IN_MILLISECONDS_DEFAULT.defaultValue; @Category("client.advanced.buffers") diff --git a/common/src/main/java/com/seibel/lod/common/LodCommonMain.java b/common/src/main/java/com/seibel/lod/common/LodCommonMain.java index 11b31b144..4b10fea4d 100644 --- a/common/src/main/java/com/seibel/lod/common/LodCommonMain.java +++ b/common/src/main/java/com/seibel/lod/common/LodCommonMain.java @@ -4,7 +4,6 @@ import com.seibel.lod.common.forge.LodForgeMethodCaller; import com.seibel.lod.common.networking.NetworkInterface; import com.seibel.lod.common.wrappers.DependencySetup; import com.seibel.lod.common.wrappers.config.ConfigGui; -import com.seibel.lod.core.ModInfo; /** * This is the common main class @@ -28,7 +27,7 @@ public class LodCommonMain { public static void initConfig() { - ConfigGui.init(ModInfo.ID, Config.class); + ConfigGui.init(Config.class); } public static void registerNetworking(NetworkInterface networkInterface) { 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 7e901ebb3..ebc4b8ad4 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 @@ -1,11 +1,36 @@ package com.seibel.lod.common.wrappers.config; +import java.io.File; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; + // Uses https://github.com/mwanji/toml4j for toml + import com.moandjiezana.toml.Toml; -// TomlWriter is threadsave while Writer is not import com.moandjiezana.toml.TomlWriter; import com.mojang.blaze3d.vertex.PoseStack; +import com.seibel.lod.common.LodCommonMain; import com.seibel.lod.core.ModInfo; + +import com.seibel.lod.core.api.ClientApi; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -17,350 +42,514 @@ 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.client.resources.language.I18n; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Pattern; /** * Based upon TinyConfig * https://github.com/Minenash/TinyConfig - * + * + * Everything required is packed into 1 class, so it is easier to copy + * This config should work for both Fabric and Forge as long as you use Mojang mappings + * * Credits to Motschen - * * @author coolGi2007 - * @version 12-09-2021 + * @version 12-23-2021 */ -// Everything required is packed into 1 class, so it is easier to copy -// This config should work for both Fabric and Forge as long as you use Mojang mappings @SuppressWarnings("unchecked") -public abstract class ConfigGui { - /* - List of hacky things that are done that should be done properly +public abstract class ConfigGui +{ + /* + TODO list - The buttons that dont show are still loded but just not rendered - The screen with is set to double so the scroll bar dosnt show + Make a wiki + Make it so you can enable and disable buttons from showing + Make min and max not final + Move the ConfigScreenConfigs class to the config class that extends this */ - private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)"); - private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)"); + /* + List of hacky things that are done that should be done properly - private static final List entries = new ArrayList<>(); - - private static TomlWriter tomlWriter = new TomlWriter(); - - private static class ConfigScreenConfigs { - // This contains all the configs for the configs - public static final int SpaceFromRightScreen = 10; - public static final int ButtonWidthSpacing = 5; - public static final int ResetButtonWidth = 40; - } - - protected static class EntryInfo { - Field field; - Object widget; - int width = 0; - int max; - Map.Entry error; - Object defaultValue; - Object value; - String tempValue; - boolean inLimits = true; - String id; // ModID - TranslatableComponent name; - int index; - boolean hideOption = false; // Hides the button - boolean button = false; // This asks if it is a button to goto a new screen - String gotoScreen = ""; // This is only called if button is true - String category; - } - - public static final Map> configClass = new HashMap<>(); -// public static List nestedClasses = new ArrayList<>(); - private static Path path; - - public static void init(String modid, Class config) { - path = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(modid + ".toml"); - - // Goes through all the nested classes and normal classes and inits them - initClass(modid, config, ""); - - // Save and read the file - try { - new Toml().read(Files.newBufferedReader(path)).to(config); - } catch (Exception e) { - write(modid); - } + 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<>(); + + private static final String MOD_NAME = ModInfo.NAME; + + private static TomlWriter tomlWriter = new TomlWriter(); + + + + + //==============// + // Initializers // + //==============// + + private static class ConfigScreenConfigs + { + // This contains all the configs for the configs + public static final int SpaceFromRightScreen = 10; + public static final int ButtonWidthSpacing = 5; + public static final int ResetButtonWidth = 40; + } + + protected static class EntryInfo + { + Field field; + Object widget; + int width = 0; + int max; + Map.Entry error; + Object defaultValue; + Object value; + String tempValue; + boolean inLimits = true; + TranslatableComponent name; + int index; + /** Hides the button */ + boolean hideOption = false; + /** This asks if it is a button to goto a new screen */ + boolean button = false; + /** This is only called if button is true */ + String gotoScreen = ""; + String category; + } + + public static final Map> configClass = new HashMap<>(); + 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, ""); + + loadFromFile(); + + // Save and read the file + try + { + new Toml().read(Files.newBufferedReader(configFilePath)).to(config); + } + catch (Exception e) + { + saveToFile(); + } + + for (EntryInfo info : entries) + { + if (info.field.isAnnotationPresent(Entry.class)) + { + try + { + info.value = info.field.get(null); + info.tempValue = info.value.toString(); + } + catch (IllegalAccessException ignored) + { + } + } + } + } + + private static void initNestedClass(Class config, String category) + { + String modCategory = MOD_NAME + (!category.isBlank() ? "." + category : ""); + configClass.put(modCategory, config); + for (Field field : config.getFields()) + { + EntryInfo info = new EntryInfo(); + if (field.isAnnotationPresent(Entry.class) || field.isAnnotationPresent(Comment.class) || field.isAnnotationPresent(ScreenEntry.class)) + { + // If putting in your own mod then put your own check for server sided + if (!LodCommonMain.serverSided) + initClient(field, info); + } + + if (field.isAnnotationPresent(Entry.class)) + { + try + { + info.defaultValue = field.get(null); + } + catch (IllegalAccessException ignored) + { + } + } + + if (field.isAnnotationPresent(ScreenEntry.class)) + { + String className = field.getAnnotation(Category.class) != null ? field.getAnnotation(Category.class).value() : ""; + initNestedClass(field.getType(), + (!className.isBlank() ? className + "." : "") + + field.getName()); + } + } + } + + /** This adds the buttons to the queue to be rendered */ + private static void initClient(Field field, EntryInfo info) + { + Class fieldClass = field.getType(); + Category category = field.getAnnotation(Category.class); + Entry entry = field.getAnnotation(Entry.class); + ScreenEntry screenEntry = field.getAnnotation(ScreenEntry.class); + + if (entry != null) + info.width = entry.width(); + else if (screenEntry != null) + info.width = screenEntry.width(); + + info.field = field; + 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 + textField(info, Integer::parseInt, INTEGER_ONLY_REGEX, entry.minValue(), entry.maxValue(), true); + } + else if (fieldClass == double.class) + { + // For double + 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 -> { + info.value = !(Boolean) info.value; + button.setMessage(func.apply(info.value)); + }, 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()); + info.widget = new AbstractMap.SimpleEntry>(button -> { + int index = values.indexOf(info.value) + 1; + info.value = values.get(index >= values.size() ? 0 : index); + button.setMessage(func.apply(info.value)); + }, func); + } + } + else if (screenEntry != null) + { + if (!screenEntry.name().equals("")) + info.name = new TranslatableComponent(screenEntry.name()); + + info.button = true; + info.gotoScreen = (!info.category.isBlank() ? 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 -> + { + stringValue = stringValue.trim(); + if (!(stringValue.isBlank() || !isNumber || pattern.matcher(stringValue).matches())) + return false; + + Number value = 0; + boolean inLimits = false; + info.error = null; + if (isNumber && !stringValue.isBlank() && !stringValue.equals("-") && !stringValue.equals(".")) + { + value = func.apply(stringValue); + inLimits = value.doubleValue() >= minValue && value.doubleValue() <= maxValue; + info.error = inLimits ? null : new AbstractMap.SimpleEntry<>(editBox, new TextComponent(value.doubleValue() < minValue ? + "§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; + } + else if (inLimits) + { + if (((List) info.value).size() == info.index) + ((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() + { + // First try to create a config file + File file; + try + { + if (!Files.exists(configFilePath)) + Files.createFile(configFilePath); + } + catch (Exception e) + { + ClientApi.LOGGER.info("Failed creating config file for " + ModInfo.READABLE_NAME + " at the path [" + configFilePath.toString() + "]."); + e.printStackTrace(); + } + // If this line fails then delete the modid.toml and start the mod again + Toml toml = new Toml().read(configFilePath.toFile()); + + ClientApi.LOGGER.info("TomlWriter stuff not made yet"); + } + + /** + * Grabs what is in modid.toml and puts it into the config + * If the file doesn't exist then it runs saveToFile + */ + public static void loadFromFile() + { + Toml toml; + try + { + toml = new Toml().read(configFilePath.toFile()); + } + catch (Exception e) + { + ClientApi.LOGGER.info("Config file not found for " + ModInfo.READABLE_NAME + ". Creating config..."); + saveToFile(); + } + /* for (EntryInfo info : entries) { - if (info.field.isAnnotationPresent(Entry.class)) - try { - info.value = info.field.get(null); - info.tempValue = info.value.toString(); - } catch (IllegalAccessException ignored) { - } - } - } - private static void initClass(String modid, Class config, String category) { - String e = modid + (category != "" ? "." + category : ""); - configClass.put(e, config); - for (Field field : config.getFields()) { - EntryInfo info = new EntryInfo(); - if (field.isAnnotationPresent(Entry.class) || field.isAnnotationPresent(Comment.class) || field.isAnnotationPresent(ScreenEntry.class)) - // TODO[CONFIG]: Fix the check for client/server -// if (Minecraft.getInstance().getEnvironmentType() == EnvType.CLIENT) - initClient(modid, field, info); - if (field.isAnnotationPresent(Entry.class)) - try { - info.defaultValue = field.get(null); - } catch (IllegalAccessException ignored) {} - if (field.isAnnotationPresent(ScreenEntry.class)) { - String c = field.getAnnotation(Category.class) != null ? field.getAnnotation(Category.class).value() : ""; - initClass(modid, field.getType(), - (c != "" ? c + "." : "") - + field.getName()); - } - } - } - private static void initClient(String modid, Field field, EntryInfo info) { - // This adds the buttons to the queue to be rendered - // DONT CALL ON SERVER AS SERVERS CANT RENDER STUFF - Class type = field.getType(); - Category c = field.getAnnotation(Category.class); - Entry e = field.getAnnotation(Entry.class); - ScreenEntry s = field.getAnnotation(ScreenEntry.class); - if (e!=null) - info.width = e.width(); - else if (s!=null) - info.width = s.width(); - info.field = field; - info.id = modid; - info.category = c != null ? c.value() : ""; - - if (e != null) { - if (!e.name().equals("")) - info.name = new TranslatableComponent(e.name()); - if (type == int.class) // For int - textField(info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true); - else if (type == double.class) // For double - textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(), false); - else if (type == String.class || type == List.class) { // For string or list - info.max = e.max() == Double.MAX_VALUE ? Integer.MAX_VALUE : (int) e.max(); - textField(info, String::length, null, Math.min(e.min(), 0), Math.max(e.max(), 1), true); - } else if (type == 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 -> { - info.value = !(Boolean) info.value; - button.setMessage(func.apply(info.value)); - }, func); - } else if (type.isEnum()) { // For enum - List values = Arrays.asList(field.getType().getEnumConstants()); - Function func = value -> new TranslatableComponent(modid + ".config." + "enum." + type.getSimpleName() + "." + info.value.toString()); - info.widget = new AbstractMap.SimpleEntry>(button -> { - int index = values.indexOf(info.value) + 1; - info.value = values.get(index >= values.size() ? 0 : index); - button.setMessage(func.apply(info.value)); - }, func); - } - } else if (s != null) { - if (!s.name().equals("")) - info.name = new TranslatableComponent(s.name()); - info.button = true; - info.gotoScreen = (info.category != "" ? info.category + "." : "") + field.getName(); - } - entries.add(info); - } - - private static void textField(EntryInfo info, Function f, Pattern pattern, double min, double max, boolean cast) { - boolean isNumber = pattern != null; - info.widget = (BiFunction>) (t, b) -> s -> { - s = s.trim(); - if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches())) return false; - - Number value = 0; - boolean inLimits = false; - info.error = null; - if (!(isNumber && s.isEmpty()) && !s.equals("-") && !s.equals(".")) { - value = f.apply(s); - inLimits = value.doubleValue() >= min && value.doubleValue() <= max; - info.error = inLimits? null : new AbstractMap.SimpleEntry<>(t, new TextComponent(value.doubleValue() < min ? - "§cMinimum " + (isNumber? "value" : "length") + (cast? " is " + (int)min : " is " + min) : - "§cMaximum " + (isNumber? "value" : "length") + (cast? " is " + (int)max : " is " + max))); - } - - info.tempValue = s; - t.setTextColor(inLimits? 0xFFFFFFFF : 0xFFFF7777); - info.inLimits = inLimits; - b.active = entries.stream().allMatch(e -> e.inLimits); - - if (inLimits && info.field.getType() != List.class) - info.value = isNumber? value : s; - else if (inLimits) { - if (((List) info.value).size() == info.index) ((List) info.value).add(""); - ((List) info.value).set(info.index, Arrays.stream(info.tempValue.replace("[", "").replace("]", "").split(", ")).toList().get(0)); - } - - return true; - }; - } - - // Creates the modid.toml - public static void write(String modid) { - path = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(modid + ".toml"); - try { - if (!Files.exists(path)) - Files.createFile(path); - tomlWriter.write(configClass.get(modid).getDeclaredConstructor().newInstance()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static Screen getScreen(Screen parent, String modid, String category) { - return new ConfigScreen(parent, modid, category); - } - private static class ConfigScreen extends Screen { - protected ConfigScreen(Screen parent, String modid, String category) { - super(new TranslatableComponent(modid + ".config.title")); - this.parent = parent; - this.modid = modid; - this.category = category; - this.translationPrefix = modid + ".config."; - } - private final String translationPrefix; - private final Screen parent; - private final String modid; - private String category; - private ConfigListWidget list; - private boolean reload = false; - - // Real Time config update // - @Override - public void tick() { - super.tick(); - for (EntryInfo info : entries) { - try {info.field.set(null, info.value);} catch (IllegalAccessException ignored) {} - } - } - private void loadValues() { - try { - new Toml().read(Files.newBufferedReader(path)).to(configClass.get(modid)); - } catch (Exception e) { - write(modid); - } - - for (EntryInfo info : entries) { - if (info.field.isAnnotationPresent(Entry.class)) - try { - info.value = info.field.get(null); - info.tempValue = info.value.toString(); - } catch (IllegalAccessException ignored) {} - } - } - @Override - protected void init() { - super.init(); - if (!reload) loadValues(); - - this.addRenderableWidget(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> { - loadValues(); - Objects.requireNonNull(minecraft).setScreen(parent); - })); - - Button done = this.addRenderableWidget(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> { - for (EntryInfo info : entries) - if (info.id.equals(modid)) { - try { - info.field.set(null, info.value); - } catch (IllegalAccessException ignored) {} - } - write(modid); - 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); - this.addWidget(this.list); - for (EntryInfo info : entries) { - if (info.id.equals(modid) && info.category.matches(category) && !info.hideOption) { -// if (info.id.equals(modid) && !info.hideOption) { - TranslatableComponent name = Objects.requireNonNullElseGet(info.name, () -> new TranslatableComponent(translationPrefix + (info.category != "" ? info.category + "." : "") + info.field.getName())); - 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(); - info.index = 0; - this.reload = true; - Objects.requireNonNull(minecraft).setScreen(this); - })); - - if (info.widget instanceof Map.Entry) { - Map.Entry> widget = (Map.Entry>) info.widget; - if (info.field.getType().isEnum()) - widget.setValue(value -> new TranslatableComponent(translationPrefix + "enum." + info.field.getType().getSimpleName() + "." + info.value.toString())); - this.list.addButton(new Button(this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen, 0, info.width, 20, widget.getValue().apply(info.value), widget.getKey()), resetButton, null, name); - } else if (info.field.getType() == List.class) { - if (!reload) info.index = 0; - EditBox widget = new EditBox(font, this.width- info.width - ConfigScreenConfigs.SpaceFromRightScreen, 0, info.width, 20, null); - widget.setMaxLength(info.width); - if (info.index < ((List) info.value).size()) - widget.insertText((String.valueOf(((List) info.value).get(info.index)))); - else widget.insertText(""); - Predicate processor = ((BiFunction>) info.widget).apply(widget, done); - widget.setFilter(processor); - resetButton.setWidth(20); - resetButton.setMessage(new TextComponent("R").withStyle(ChatFormatting.RED)); - Button cycleButton = new Button(this.width - 185, 0, 20, 20, new TextComponent(String.valueOf(info.index)).withStyle(ChatFormatting.GOLD), (button -> { - ((List) info.value).remove(""); - this.reload = true; - info.index = info.index + 1; - if (info.index > ((List) info.value).size()) info.index = 0; - Objects.requireNonNull(minecraft).setScreen(this); - })); - this.list.addButton(widget, resetButton, cycleButton, name); - } else if (info.widget != null) { - EditBox widget = new EditBox(font, this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen + 2, 0, info.width - 4, 20, null); - widget.setMaxLength(info.width); - widget.insertText(info.tempValue); - Predicate processor = ((BiFunction>) info.widget).apply(widget, done); - widget.setFilter(processor); - this.list.addButton(widget, resetButton, null, name); - } else if (info.button) { - Button widget = new Button(this.width / 2 - info.width, this.height - 28, info.width*2, 20, name, (button -> { - Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, modid, info.gotoScreen)); - })); - this.list.addButton(widget, null, null, null); - } else { - this.list.addButton(null, null, null, name); - } + if (info.id.equals(modid)) { + if (info.widget instanceof Map.Entry) { // For enum + info.value = toml.getList((info.category != "" ? info.category + "." : "") + info.field.getName()); + } else if (info.field.getType() == String.class) { + info.value = toml.getString((info.category != "" ? info.category + "." : "") + info.field.getName()); + } else if (info.field.getType() == Double.class) { + info.value = toml.getDouble((info.category != "" ? info.category + "." : "") + info.field.getName()); + } else if (info.field.getType() == Long.class) { + info.value = toml.getLong((info.category != "" ? info.category + "." : "") + info.field.getName()); + } else if (info.field.getType() == List.class) { + info.value = toml.getList((info.category != "" ? info.category + "." : "") + info.field.getName()); } } - - } - @Override - public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { - this.renderBackground(matrices); - this.list.render(matrices, mouseX, mouseY, delta); - // Render title - drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); - - - // TODO[CONFIG]: Fix the tooltip + }*/ + } + + + + + + public static Screen getScreen(Screen parent, String category) + { + return new ConfigScreen(parent, category); + } + + private static class ConfigScreen extends Screen + { + protected ConfigScreen(Screen parent, String category) + { + super(new TranslatableComponent(MOD_NAME + ".config.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(); + for (EntryInfo info : entries) + { + try + { + info.field.set(null, info.value); + } + catch (IllegalAccessException ignored) + { + } + } + } + + private void loadValues() + { + loadFromFile(); + + for (EntryInfo info : entries) + { + if (info.field.isAnnotationPresent(Entry.class)) + try + { + info.value = info.field.get(null); + info.tempValue = info.value.toString(); + } + catch (IllegalAccessException ignored) + { + } + } + } + + @Override + protected void init() + { + super.init(); + if (!reload) + loadValues(); + + this.addRenderableWidget(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> { + loadValues(); + Objects.requireNonNull(minecraft).setScreen(parent); + })); + + Button done = this.addRenderableWidget(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> { + for (EntryInfo info : entries) + { + try + { + info.field.set(null, info.value); + } + catch (IllegalAccessException ignored) + { + } + } + + 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); + this.addWidget(this.list); + for (EntryInfo info : entries) + { + if (info.category.matches(category) && !info.hideOption) + { + TranslatableComponent name = Objects.requireNonNullElseGet(info.name, () -> new TranslatableComponent(translationPrefix + (!info.category.isBlank() ? info.category + "." : "") + info.field.getName())); + 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(); + info.index = 0; + this.reload = true; + Objects.requireNonNull(minecraft).setScreen(this); + })); + + if (info.widget instanceof Map.Entry) + { + Map.Entry> widget = (Map.Entry>) info.widget; + if (info.field.getType().isEnum()) + widget.setValue(value -> new TranslatableComponent(translationPrefix + "enum." + info.field.getType().getSimpleName() + "." + info.value.toString())); + this.list.addButton(new Button(this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen, 0, info.width, 20, widget.getValue().apply(info.value), widget.getKey()), resetButton, null, name); + } + else if (info.field.getType() == List.class) + { + if (!reload) + info.index = 0; + EditBox widget = new EditBox(font, this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen, 0, info.width, 20, null); + widget.setMaxLength(info.width); + if (info.index < ((List) info.value).size()) + widget.insertText((String.valueOf(((List) info.value).get(info.index)))); + else + widget.insertText(""); + Predicate processor = ((BiFunction>) info.widget).apply(widget, done); + widget.setFilter(processor); + resetButton.setWidth(20); + resetButton.setMessage(new TextComponent("R").withStyle(ChatFormatting.RED)); + Button cycleButton = new Button(this.width - 185, 0, 20, 20, new TextComponent(String.valueOf(info.index)).withStyle(ChatFormatting.GOLD), (button -> { + ((List) info.value).remove(""); + this.reload = true; + info.index = info.index + 1; + if (info.index > ((List) info.value).size()) + info.index = 0; + Objects.requireNonNull(minecraft).setScreen(this); + })); + this.list.addButton(widget, resetButton, cycleButton, name); + } + else if (info.widget != null) + { + EditBox widget = new EditBox(font, this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen + 2, 0, info.width - 4, 20, null); + widget.setMaxLength(info.width); + widget.insertText(info.tempValue); + Predicate processor = ((BiFunction>) info.widget).apply(widget, done); + widget.setFilter(processor); + this.list.addButton(widget, resetButton, null, name); + } + else if (info.button) + { + Button widget = new Button(this.width / 2 - info.width, this.height - 28, info.width * 2, 20, name, (button -> { + Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, info.gotoScreen)); + })); + this.list.addButton(widget, null, null, null); + } + else + { + this.list.addButton(null, null, null, name); + } + } + } + + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) + { + this.renderBackground(matrices); + this.list.render(matrices, mouseX, mouseY, delta); + // Render title + drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); + + + // TODO[CONFIG]: Fix the tooltip /* for (EntryInfo info : entries) { if (info.id.equals(modid)) { @@ -381,103 +570,165 @@ public abstract class ConfigGui { } } */ - 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()) { - if (buttonEntry.button != null && buttonEntry.button.isMouseOver(mouseX, mouseY)) { - return Optional.of(buttonEntry.button); - } - } - return Optional.empty(); - } - } - public static class ButtonEntry extends ContainerObjectSelectionList.Entry { - private static final Font textRenderer = Minecraft.getInstance().font; - public final AbstractWidget button; - private final AbstractWidget resetButton; - private final AbstractWidget indexButton; - 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); - this.button = button; - this.resetButton = resetButton; - this.text = text; - this.indexButton = indexButton; - if (button != null) children.add(button); - if (resetButton != null) children.add(resetButton); - 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); - } - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - if (button != null) { - button.y = y; - button.render(matrices, mouseX, mouseY, tickDelta); - } - if (resetButton != null) { - resetButton.y = y; - resetButton.render(matrices, mouseX, mouseY, tickDelta); - } - if (indexButton != null) { - indexButton.y = y; - indexButton.render(matrices, mouseX, mouseY, tickDelta); - } - 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;} - } - - // Where the @Entry is defined - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Entry { - String name() default ""; - int width() default 150; - double min() default Double.MIN_NORMAL; - double max() default Double.MAX_VALUE; - } - - // Where the @ScreenEntry is defined - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface ScreenEntry { - String name() default ""; - int width() default 100; - } - - // Where the @Category is defined - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Category { - String value() default ""; - } - - // Where the @Comment is defined - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Comment {} + 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()) + { + if (buttonEntry.button != null && buttonEntry.button.isMouseOver(mouseX, mouseY)) + { + return Optional.of(buttonEntry.button); + } + } + return Optional.empty(); + } + } + + + + + + public static class ButtonEntry extends ContainerObjectSelectionList.Entry + { + private static final Font textRenderer = Minecraft.getInstance().font; + public final AbstractWidget button; + private final AbstractWidget resetButton; + private final AbstractWidget indexButton; + 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); + this.button = button; + this.resetButton = resetButton; + this.text = text; + this.indexButton = indexButton; + if (button != null) + children.add(button); + if (resetButton != null) + children.add(resetButton); + 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) + { + if (button != null) + { + button.y = y; + button.render(matrices, mouseX, mouseY, tickDelta); + } + if (resetButton != null) + { + resetButton.y = y; + resetButton.render(matrices, mouseX, mouseY, tickDelta); + } + if (indexButton != null) + { + indexButton.y = y; + indexButton.render(matrices, mouseX, mouseY, tickDelta); + } + 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; + } + } + + + + + //=============// + // annotations // + //=============// + + // These could probably be moved into core since they don't rely on any Minecraft code. - James + + /** a textField, button, etc. that can be interacted with */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface Entry + { + String name() default ""; + + int width() default 150; + + double minValue() default Double.MIN_NORMAL; + + double maxValue() default Double.MAX_VALUE; + } + + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface ScreenEntry + { + String name() default ""; + + int width() default 100; + } + + + /** Used when sorting the configs in the menu */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface Category + { + String value() default ""; + } + + + /** displayed in a tooltip when hoving over a config button */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface Comment + { + + } } diff --git a/common/src/main/resources/assets/lod/lang/en_us.json b/common/src/main/resources/assets/lod/lang/en_us.json index 3aad790a6..a5a88a6c4 100644 --- a/common/src/main/resources/assets/lod/lang/en_us.json +++ b/common/src/main/resources/assets/lod/lang/en_us.json @@ -1,121 +1,121 @@ { "lod.title": "Distant Horizons", - "lod.config.title": "Distant Horizons config", - "lod.config.ShowButton": "Show menu button", - "lod.config.client": "Client", - "lod.config.client.graphics": "Graphics", - "lod.config.client.graphics.quality": "Quality options", - "lod.config.client.graphics.quality.drawResolution": "Draw resolution", - "lod.config.client.graphics.quality.drawResolution.@tooltip": "What is the maximum detail fake chunks should be drawn at?", - "lod.config.client.graphics.quality.lodChunkRenderDistance": "Chunk render distance", - "lod.config.client.graphics.quality.lodChunkRenderDistance.@tooltip": "The mod's render distance, measured in chunks", - "lod.config.client.graphics.quality.verticalQuality": "Vertical quality", - "lod.config.client.graphics.quality.verticalQuality.@tooltip": "This indicates how detailed fake chunks will represent overhangs, caves, floating islands, ect. \nHigher options will use more memory and increase GPU usage", - "lod.config.client.graphics.quality.horizontalScale": "Horizontal scale", - "lod.config.client.graphics.quality.horizontalScale.@tooltip": "This indicates how quickly fake chunks drop off in quality", - "lod.config.client.graphics.quality.horizontalQuality": "Horizontal quality", - "lod.config.client.graphics.quality.horizontalQuality.@tooltip": "This indicates the exponential base of the quadratic drop-off", - "lod.config.client.graphics.fogQuality": "Fog options", - "lod.config.client.graphics.fogQuality.fogDistance": "Fog distance", - "lod.config.client.graphics.fogQuality.fogDistance.@tooltip": "At what distance should Fog be drawn on the fake chunks?", - "lod.config.client.graphics.fogQuality.fogDrawMode": "Fog draw mode", - "lod.config.client.graphics.fogQuality.fogDrawMode.@tooltip": "When should fog be drawn?", - "lod.config.client.graphics.fogQuality.fogColorMode": "Fog color mode", - "lod.config.client.graphics.fogQuality.fogColorMode.@tooltip": "What color should fog use?", - "lod.config.client.graphics.fogQuality.disableVanillaFog": "Disable vanilla fog", - "lod.config.client.graphics.fogQuality.disableVanillaFog.@tooltip": "If true disable Minecraft's fog. \nMay cause issues with other mods that edit fog. \nMay cause errors with other fog editing mods", - "lod.config.client.graphics.advancedGraphics": "Advanced quality option", - "lod.config.client.graphics.advancedGraphics.lodTemplate": "LOD template", - "lod.config.client.graphics.advancedGraphics.lodTemplate.@tooltip": "How should the LODs be drawn? \nNOTE: Currently only CUBIC is implemented!", - "lod.config.client.graphics.advancedGraphics.disableDirectionalCulling": "Disable directional culling", - "lod.config.client.graphics.advancedGraphics.disableDirectionalCulling.@tooltip": "If false fake chunks behind the player's camera aren't drawn, increasing performance. \nIf true all LODs are drawn, even those behind the player's camera, decreasing performance", - "lod.config.client.graphics.advancedGraphics.alwaysDrawAtMaxQuality": "Always draw at max quality", - "lod.config.client.graphics.advancedGraphics.alwaysDrawAtMaxQuality.@tooltip": "Disable quality falloff, all fake chunks will be drawn at the highest available detail level", - "lod.config.client.graphics.advancedGraphics.vanillaOverdraw": "Vanilla overdraw", - "lod.config.client.graphics.advancedGraphics.vanillaOverdraw.@tooltip": "How often should LODs be drawn on top of regular chunks? \nHALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves.", - "lod.config.client.graphics.advancedGraphics.useExtendedNearClipPlane": "Use extended near clip plane", - "lod.config.client.graphics.advancedGraphics.useExtendedNearClipPlane.@tooltip": "Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly especially when in/near an ocean", - "lod.config.client.worldGenerator": "World generator", - "lod.config.client.worldGenerator.generationPriority": "Generation priority", - "lod.config.client.worldGenerator.generationPriority.@tooltip": "What is the priority of the chunks being generated around the player", - "lod.config.client.worldGenerator.distanceGenerationMode": "Distance generation mode", - "lod.config.client.worldGenerator.distanceGenerationMode.@tooltip": "How much of the generation should be used when generating fake chunks", - "lod.config.client.worldGenerator.allowUnstableFeatureGeneration": "Allow unstable feature generation", - "lod.config.client.worldGenerator.allowUnstableFeatureGeneration.@tooltip": "Some features may not be thread safe. \nCould cause instability and crashes", - "lod.config.client.worldGenerator.blocksToAvoid": "Block to avoid", - "lod.config.client.worldGenerator.blocksToAvoid.@tooltip": "What block to avoid when generating fake chunks", - "lod.config.client.advanced": "Advance options", - "lod.config.client.advanced.threading": "Threading", - "lod.config.client.advanced.threading.numberOfWorldGenerationThreads": "NO. of world generation threads", - "lod.config.client.advanced.threading.numberOfWorldGenerationThreads.@tooltip": "This is how many threads are used when generating LODs outside the normal render distance. \nIf you experience stuttering when generating distant LODs, decrease this number. If you want to increase LOD generation speed, increase this number \nCan only be between 1 and your current number of threads", - "lod.config.client.advanced.threading.numberOfBufferBuilderThreads": "NO. of buffer builder threads", - "lod.config.client.advanced.threading.numberOfBufferBuilderThreads.@tooltip": "This is how many threads are used when building vertex buffers (The things sent to your GPU to draw the fake chunks) \nCan only be between 1 and your current number of threads", - "lod.config.client.advanced.buffers": "Buffers", - "lod.config.client.advanced.buffers.gpuUploadMethod": "GPU upload method", - "lod.config.client.advanced.buffers.gpuUploadMethod.@tooltip": "What method should be used to upload geometry to the GPU?", - "lod.config.client.advanced.buffers.gpuUploadTimeoutInMilleseconds": "GPU upload timeout (milliseconds)", - "lod.config.client.advanced.buffers.gpuUploadTimeoutInMilleseconds.@tooltip": "How long should we wait before uploading a buffer to the GPU? \nHelpful resource for frame times: https://fpstoms.com", - "lod.config.client.advanced.buffers.rebuildTimes": "Rebuild times", - "lod.config.client.advanced.buffers.rebuildTimes.@tooltip": "Rebuild times", - "lod.config.client.advanced.debugging": "Debug", - "lod.config.client.advanced.debugging.drawLods": "Draw LOD's", - "lod.config.client.advanced.debugging.drawLods.@tooltip": "If true, the mod is enabled and fake chunks will be drawn.", - "lod.config.client.advanced.debugging.debugMode": "Debug mode", - "lod.config.client.advanced.debugging.debugMode.@tooltip": "What type of debug mode do you want", - "lod.config.client.advanced.debugging.enableDebugKeybindings": "Enable debug keybindings", - "lod.config.client.advanced.debugging.enableDebugKeybindings.@tooltip": "Enable debug keybindings to change the Debug mode on the fly in game", - "lod.config.enum.HorizontalResolution.BLOCK": "Block", - "lod.config.enum.HorizontalResolution.TWO_BLOCKS": "2 blocks", - "lod.config.enum.HorizontalResolution.FOUR_BLOCKS": "4 blocks", - "lod.config.enum.HorizontalResolution.HALF_CHUNK": "Half a chunk", - "lod.config.enum.HorizontalResolution.CHUNK": "Chunk", - "lod.config.enum.VerticalQuality.LOW": "Low", - "lod.config.enum.VerticalQuality.MEDIUM": "Medium", - "lod.config.enum.VerticalQuality.HIGH": "High", - "lod.config.enum.HorizontalScale.LOW": "Low", - "lod.config.enum.HorizontalScale.MEDIUM": "Medium", - "lod.config.enum.HorizontalScale.HIGH": "High", - "lod.config.enum.HorizontalQuality.LOWEST": "Lowest", - "lod.config.enum.HorizontalQuality.LOW": "Low", - "lod.config.enum.HorizontalQuality.MEDIUM": "Medium", - "lod.config.enum.HorizontalQuality.HIGH": "High", - "lod.config.enum.FogDistance.NEAR": "Near", - "lod.config.enum.FogDistance.FAR": "Far", - "lod.config.enum.FogDistance.NEAR_AND_FAR": "Near and far", - "lod.config.enum.FogDrawMode.USE_OPTIFINE_SETTING": "Use modded settings", - "lod.config.enum.FogDrawMode.FOG_ENABLED": "Fog enabled", - "lod.config.enum.FogDrawMode.FOG_DISABLED": "Fog disabled", - "lod.config.enum.FogColorMode.USE_WORLD_FOG_COLOR": "Use world fog", - "lod.config.enum.FogColorMode.USE_SKY_COLOR": "Use sky color", - "lod.config.enum.LodTemplate.CUBIC": "Cubic", - "lod.config.enum.LodTemplate.TRIANGULAR": "Triangular", - "lod.config.enum.LodTemplate.DYNAMIC": "Dynamic", - "lod.config.enum.VanillaOverdraw.NEVER": "Never", - "lod.config.enum.VanillaOverdraw.DYNAMIC": "Dynamic", - "lod.config.enum.VanillaOverdraw.ALWAYS": "Always", - "lod.config.enum.VanillaOverdraw.BORDER": "Border", - "lod.config.enum.GenerationPriority.AUTO": "Auto", - "lod.config.enum.GenerationPriority.NEAR_FIRST": "Near first", - "lod.config.enum.GenerationPriority.FAR_FIRST": "Far first", - "lod.config.enum.DistanceGenerationMode.NONE": "None", - "lod.config.enum.DistanceGenerationMode.BIOME_ONLY": "Biome only", - "lod.config.enum.DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT": "Biome only simulate height", - "lod.config.enum.DistanceGenerationMode.SURFACE": "Surface", - "lod.config.enum.DistanceGenerationMode.FEATURES": "Features", - "lod.config.enum.DistanceGenerationMode.FULL": "Full", - "lod.config.enum.BlocksToAvoid.NONE": "None", - "lod.config.enum.BlocksToAvoid.NON_FULL": "Non full", - "lod.config.enum.BlocksToAvoid.NO_COLLISION": "No collision", - "lod.config.enum.BlocksToAvoid.BOTH": "Both", - "lod.config.enum.DebugMode.OFF": "Off", - "lod.config.enum.DebugMode.SHOW_DETAIL": "Show detail", - "lod.config.enum.DebugMode.SHOW_DETAIL_WIREFRAME": "Show detail with wireframe", - "lod.config.enum.GpuUploadMethod.AUTO": "Auto", - "lod.config.enum.GpuUploadMethod.BUFFER_STORAGE": "Buffer storage", - "lod.config.enum.GpuUploadMethod.SUB_DATA": "Sub data", - "lod.config.enum.GpuUploadMethod.BUFFER_MAPPING": "Buffer mapping", - "lod.config.enum.GpuUploadMethod.DATA": "Data", - "lod.config.enum.BufferRebuildTimes.FREQUENT": "Frequent", - "lod.config.enum.BufferRebuildTimes.NORMAL": "Normal", - "lod.config.enum.BufferRebuildTimes.RARE": "Rare" + "DistantHorizons.config.title": "Distant Horizons config", + "DistantHorizons.config.ShowButton": "Show menu button", + "DistantHorizons.config.client": "Client", + "DistantHorizons.config.client.graphics": "Graphics", + "DistantHorizons.config.client.graphics.quality": "Quality options", + "DistantHorizons.config.client.graphics.quality.drawResolution": "Draw resolution", + "DistantHorizons.config.client.graphics.quality.drawResolution.@tooltip": "What is the maximum detail fake chunks should be drawn at?", + "DistantHorizons.config.client.graphics.quality.lodChunkRenderDistance": "Chunk render distance", + "DistantHorizons.config.client.graphics.quality.lodChunkRenderDistance.@tooltip": "The mod's render distance, measured in chunks", + "DistantHorizons.config.client.graphics.quality.verticalQuality": "Vertical quality", + "DistantHorizons.config.client.graphics.quality.verticalQuality.@tooltip": "This indicates how detailed fake chunks will represent overhangs, caves, floating islands, ect. \nHigher options will use more memory and increase GPU usage", + "DistantHorizons.config.client.graphics.quality.horizontalScale": "Horizontal scale", + "DistantHorizons.config.client.graphics.quality.horizontalScale.@tooltip": "This indicates how quickly fake chunks drop off in quality", + "DistantHorizons.config.client.graphics.quality.horizontalQuality": "Horizontal quality", + "DistantHorizons.config.client.graphics.quality.horizontalQuality.@tooltip": "This indicates the exponential base of the quadratic drop-off", + "DistantHorizons.config.client.graphics.fogQuality": "Fog options", + "DistantHorizons.config.client.graphics.fogQuality.fogDistance": "Fog distance", + "DistantHorizons.config.client.graphics.fogQuality.fogDistance.@tooltip": "At what distance should Fog be drawn on the fake chunks?", + "DistantHorizons.config.client.graphics.fogQuality.fogDrawMode": "Fog draw mode", + "DistantHorizons.config.client.graphics.fogQuality.fogDrawMode.@tooltip": "When should fog be drawn?", + "DistantHorizons.config.client.graphics.fogQuality.fogColorMode": "Fog color mode", + "DistantHorizons.config.client.graphics.fogQuality.fogColorMode.@tooltip": "What color should fog use?", + "DistantHorizons.config.client.graphics.fogQuality.disableVanillaFog": "Disable vanilla fog", + "DistantHorizons.config.client.graphics.fogQuality.disableVanillaFog.@tooltip": "If true disable Minecraft's fog. \nMay cause issues with other mods that edit fog. \nMay cause errors with other fog editing mods", + "DistantHorizons.config.client.graphics.advancedGraphics": "Advanced quality option", + "DistantHorizons.config.client.graphics.advancedGraphics.lodTemplate": "LOD template", + "DistantHorizons.config.client.graphics.advancedGraphics.lodTemplate.@tooltip": "How should the LODs be drawn? \nNOTE: Currently only CUBIC is implemented!", + "DistantHorizons.config.client.graphics.advancedGraphics.disableDirectionalCulling": "Disable directional culling", + "DistantHorizons.config.client.graphics.advancedGraphics.disableDirectionalCulling.@tooltip": "If false fake chunks behind the player's camera aren't drawn, increasing performance. \nIf true all LODs are drawn, even those behind the player's camera, decreasing performance", + "DistantHorizons.config.client.graphics.advancedGraphics.alwaysDrawAtMaxQuality": "Always draw at max quality", + "DistantHorizons.config.client.graphics.advancedGraphics.alwaysDrawAtMaxQuality.@tooltip": "Disable quality falloff, all fake chunks will be drawn at the highest available detail level", + "DistantHorizons.config.client.graphics.advancedGraphics.vanillaOverdraw": "Vanilla overdraw", + "DistantHorizons.config.client.graphics.advancedGraphics.vanillaOverdraw.@tooltip": "How often should LODs be drawn on top of regular chunks? \nHALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves.", + "DistantHorizons.config.client.graphics.advancedGraphics.useExtendedNearClipPlane": "Use extended near clip plane", + "DistantHorizons.config.client.graphics.advancedGraphics.useExtendedNearClipPlane.@tooltip": "Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly especially when in/near an ocean", + "DistantHorizons.config.client.worldGenerator": "World generator", + "DistantHorizons.config.client.worldGenerator.generationPriority": "Generation priority", + "DistantHorizons.config.client.worldGenerator.generationPriority.@tooltip": "What is the priority of the chunks being generated around the player", + "DistantHorizons.config.client.worldGenerator.distanceGenerationMode": "Distance generation mode", + "DistantHorizons.config.client.worldGenerator.distanceGenerationMode.@tooltip": "How much of the generation should be used when generating fake chunks", + "DistantHorizons.config.client.worldGenerator.allowUnstableFeatureGeneration": "Allow unstable feature generation", + "DistantHorizons.config.client.worldGenerator.allowUnstableFeatureGeneration.@tooltip": "Some features may not be thread safe. \nCould cause instability and crashes", + "DistantHorizons.config.client.worldGenerator.blocksToAvoid": "Block to avoid", + "DistantHorizons.config.client.worldGenerator.blocksToAvoid.@tooltip": "What block to avoid when generating fake chunks", + "DistantHorizons.config.client.advanced": "Advance options", + "DistantHorizons.config.client.advanced.threading": "Threading", + "DistantHorizons.config.client.advanced.threading.numberOfWorldGenerationThreads": "NO. of world generation threads", + "DistantHorizons.config.client.advanced.threading.numberOfWorldGenerationThreads.@tooltip": "This is how many threads are used when generating LODs outside the normal render distance. \nIf you experience stuttering when generating distant LODs, decrease this number. If you want to increase LOD generation speed, increase this number \nCan only be between 1 and your current number of threads", + "DistantHorizons.config.client.advanced.threading.numberOfBufferBuilderThreads": "NO. of buffer builder threads", + "DistantHorizons.config.client.advanced.threading.numberOfBufferBuilderThreads.@tooltip": "This is how many threads are used when building vertex buffers (The things sent to your GPU to draw the fake chunks) \nCan only be between 1 and your current number of threads", + "DistantHorizons.config.client.advanced.buffers": "Buffers", + "DistantHorizons.config.client.advanced.buffers.gpuUploadMethod": "GPU upload method", + "DistantHorizons.config.client.advanced.buffers.gpuUploadMethod.@tooltip": "What method should be used to upload geometry to the GPU?", + "DistantHorizons.config.client.advanced.buffers.gpuUploadTimeoutInMilleseconds": "GPU upload timeout (milliseconds)", + "DistantHorizons.config.client.advanced.buffers.gpuUploadTimeoutInMilleseconds.@tooltip": "How long should we wait before uploading a buffer to the GPU? \nHelpful resource for frame times: https://fpstoms.com", + "DistantHorizons.config.client.advanced.buffers.rebuildTimes": "Rebuild times", + "DistantHorizons.config.client.advanced.buffers.rebuildTimes.@tooltip": "Rebuild times", + "DistantHorizons.config.client.advanced.debugging": "Debug", + "DistantHorizons.config.client.advanced.debugging.drawLods": "Draw LOD's", + "DistantHorizons.config.client.advanced.debugging.drawLods.@tooltip": "If true, the mod is enabled and fake chunks will be drawn.", + "DistantHorizons.config.client.advanced.debugging.debugMode": "Debug mode", + "DistantHorizons.config.client.advanced.debugging.debugMode.@tooltip": "What type of debug mode do you want", + "DistantHorizons.config.client.advanced.debugging.enableDebugKeybindings": "Enable debug keybindings", + "DistantHorizons.config.client.advanced.debugging.enableDebugKeybindings.@tooltip": "Enable debug keybindings to change the Debug mode on the fly in game", + "DistantHorizons.config.enum.HorizontalResolution.BLOCK": "Block", + "DistantHorizons.config.enum.HorizontalResolution.TWO_BLOCKS": "2 blocks", + "DistantHorizons.config.enum.HorizontalResolution.FOUR_BLOCKS": "4 blocks", + "DistantHorizons.config.enum.HorizontalResolution.HALF_CHUNK": "Half a chunk", + "DistantHorizons.config.enum.HorizontalResolution.CHUNK": "Chunk", + "DistantHorizons.config.enum.VerticalQuality.LOW": "Low", + "DistantHorizons.config.enum.VerticalQuality.MEDIUM": "Medium", + "DistantHorizons.config.enum.VerticalQuality.HIGH": "High", + "DistantHorizons.config.enum.HorizontalScale.LOW": "Low", + "DistantHorizons.config.enum.HorizontalScale.MEDIUM": "Medium", + "DistantHorizons.config.enum.HorizontalScale.HIGH": "High", + "DistantHorizons.config.enum.HorizontalQuality.LOWEST": "Lowest", + "DistantHorizons.config.enum.HorizontalQuality.LOW": "Low", + "DistantHorizons.config.enum.HorizontalQuality.MEDIUM": "Medium", + "DistantHorizons.config.enum.HorizontalQuality.HIGH": "High", + "DistantHorizons.config.enum.FogDistance.NEAR": "Near", + "DistantHorizons.config.enum.FogDistance.FAR": "Far", + "DistantHorizons.config.enum.FogDistance.NEAR_AND_FAR": "Near and far", + "DistantHorizons.config.enum.FogDrawMode.USE_OPTIFINE_SETTING": "Use modded settings", + "DistantHorizons.config.enum.FogDrawMode.FOG_ENABLED": "Fog enabled", + "DistantHorizons.config.enum.FogDrawMode.FOG_DISABLED": "Fog disabled", + "DistantHorizons.config.enum.FogColorMode.USE_WORLD_FOG_COLOR": "Use world fog", + "DistantHorizons.config.enum.FogColorMode.USE_SKY_COLOR": "Use sky color", + "DistantHorizons.config.enum.LodTemplate.CUBIC": "Cubic", + "DistantHorizons.config.enum.LodTemplate.TRIANGULAR": "Triangular", + "DistantHorizons.config.enum.LodTemplate.DYNAMIC": "Dynamic", + "DistantHorizons.config.enum.VanillaOverdraw.NEVER": "Never", + "DistantHorizons.config.enum.VanillaOverdraw.DYNAMIC": "Dynamic", + "DistantHorizons.config.enum.VanillaOverdraw.ALWAYS": "Always", + "DistantHorizons.config.enum.VanillaOverdraw.BORDER": "Border", + "DistantHorizons.config.enum.GenerationPriority.AUTO": "Auto", + "DistantHorizons.config.enum.GenerationPriority.NEAR_FIRST": "Near first", + "DistantHorizons.config.enum.GenerationPriority.FAR_FIRST": "Far first", + "DistantHorizons.config.enum.DistanceGenerationMode.NONE": "None", + "DistantHorizons.config.enum.DistanceGenerationMode.BIOME_ONLY": "Biome only", + "DistantHorizons.config.enum.DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT": "Biome only simulate height", + "DistantHorizons.config.enum.DistanceGenerationMode.SURFACE": "Surface", + "DistantHorizons.config.enum.DistanceGenerationMode.FEATURES": "Features", + "DistantHorizons.config.enum.DistanceGenerationMode.FULL": "Full", + "DistantHorizons.config.enum.BlocksToAvoid.NONE": "None", + "DistantHorizons.config.enum.BlocksToAvoid.NON_FULL": "Non full", + "DistantHorizons.config.enum.BlocksToAvoid.NO_COLLISION": "No collision", + "DistantHorizons.config.enum.BlocksToAvoid.BOTH": "Both", + "DistantHorizons.config.enum.DebugMode.OFF": "Off", + "DistantHorizons.config.enum.DebugMode.SHOW_DETAIL": "Show detail", + "DistantHorizons.config.enum.DebugMode.SHOW_DETAIL_WIREFRAME": "Show detail with wireframe", + "DistantHorizons.config.enum.GpuUploadMethod.AUTO": "Auto", + "DistantHorizons.config.enum.GpuUploadMethod.BUFFER_STORAGE": "Buffer storage", + "DistantHorizons.config.enum.GpuUploadMethod.SUB_DATA": "Sub data", + "DistantHorizons.config.enum.GpuUploadMethod.BUFFER_MAPPING": "Buffer mapping", + "DistantHorizons.config.enum.GpuUploadMethod.DATA": "Data", + "DistantHorizons.config.enum.BufferRebuildTimes.FREQUENT": "Frequent", + "DistantHorizons.config.enum.BufferRebuildTimes.NORMAL": "Normal", + "DistantHorizons.config.enum.BufferRebuildTimes.RARE": "Rare" } diff --git a/core b/core index 96be86cac..a19189c2a 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 96be86cacf250088141a853308e0da2f3045e081 +Subproject commit a19189c2a8afa945edc65f3425ba1ca3896e0870 diff --git a/fabric/src/main/java/com/seibel/lod/fabric/mixins/MixinOptionsScreen.java b/fabric/src/main/java/com/seibel/lod/fabric/mixins/MixinOptionsScreen.java index dc525343d..4f30b253c 100644 --- a/fabric/src/main/java/com/seibel/lod/fabric/mixins/MixinOptionsScreen.java +++ b/fabric/src/main/java/com/seibel/lod/fabric/mixins/MixinOptionsScreen.java @@ -44,7 +44,7 @@ public class MixinOptionsScreen extends Screen { 20, ICON_TEXTURE, 20, 40, // Create the button and tell it where to go // For now it goes to the client option by default - (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, ModInfo.ID, "client")), + (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, "client")), // Add a title to the screen new TranslatableComponent("text.autoconfig." + ModInfo.ID + ".title"))); } diff --git a/fabric/src/main/java/com/seibel/lod/fabric/wrappers/config/ModMenuIntegration.java b/fabric/src/main/java/com/seibel/lod/fabric/wrappers/config/ModMenuIntegration.java index 95fb32fdb..05924a4ef 100644 --- a/fabric/src/main/java/com/seibel/lod/fabric/wrappers/config/ModMenuIntegration.java +++ b/fabric/src/main/java/com/seibel/lod/fabric/wrappers/config/ModMenuIntegration.java @@ -19,6 +19,6 @@ public class ModMenuIntegration implements ModMenuApi { // For the custom config code @Override public ConfigScreenFactory getModConfigScreenFactory() { - return parent -> Config.getScreen(parent, ModInfo.ID, ""); + return parent -> Config.getScreen(parent, ""); } } diff --git a/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java b/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java index fa73c9ffd..2ebc98bbf 100644 --- a/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java +++ b/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java @@ -78,7 +78,7 @@ public class ForgeMain implements LodForgeMethodCaller private void onClientStart(final FMLClientSetupEvent event) { ModLoadingContext.get().registerExtensionPoint(ConfigGuiHandler.ConfigGuiFactory.class, - () -> new ConfigGuiHandler.ConfigGuiFactory((client, parent) -> Config.getScreen(parent, ModInfo.ID, ""))); + () -> new ConfigGuiHandler.ConfigGuiFactory((client, parent) -> Config.getScreen(parent, ""))); forgeClientProxy = new ForgeClientProxy(); MinecraftForge.EVENT_BUS.register(forgeClientProxy); } diff --git a/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java b/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java index 3241e6c3b..05a85ffed 100644 --- a/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java +++ b/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java @@ -43,7 +43,7 @@ public class MixinOptionsScreen extends Screen { // Some textuary stuff 20, ICON_TEXTURE, 20, 40, // Create the button and tell it where to go - (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, ModInfo.ID, "client")), + (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, "client")), // Add a title to the screen new TranslatableComponent("text.autoconfig." + ModInfo.ID + ".title"))); }