Add option categories

This commit is contained in:
embeddedt 2023-07-04 11:14:23 -04:00
parent 18f78b9624
commit ce6ce1d341
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
7 changed files with 174 additions and 30 deletions

View File

@ -1,8 +1,7 @@
package org.embeddedt.modernfix.core.config;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.*;
import com.google.gson.*;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
@ -25,6 +24,7 @@ public class ModernFixEarlyConfig {
private static final Logger LOGGER = LogManager.getLogger("ModernFixConfig");
private final Map<String, Option> options = new HashMap<>();
private final Multimap<String, Option> optionsByCategory = HashMultimap.create();
public static final boolean OPTIFINE_PRESENT;
@ -176,12 +176,14 @@ public class ModernFixEarlyConfig {
private ModernFixEarlyConfig(File file) {
this.configFile = file;
OptionCategories.load();
this.scanForAndBuildMixinOptions();
mixinOptions.addAll(DEFAULT_SETTING_OVERRIDES.keySet());
for(String optionName : mixinOptions) {
boolean defaultEnabled = DEFAULT_SETTING_OVERRIDES.getOrDefault(optionName, true);
this.options.putIfAbsent(optionName, new Option(optionName, defaultEnabled, false));
Option option = new Option(optionName, defaultEnabled, false);
this.options.putIfAbsent(optionName, option);
this.optionsByCategory.put(OptionCategories.getCategoryForOption(optionName), option);
}
// Defines the default rules which can be configured by the user or other mods.
// You must manually add a rule for any new mixins not covered by an existing package rule.
@ -382,4 +384,8 @@ public class ModernFixEarlyConfig {
public Map<String, Option> getOptionMap() {
return Collections.unmodifiableMap(this.options);
}
public Multimap<String, Option> getOptionCategoryMap() {
return Multimaps.unmodifiableMultimap(this.optionsByCategory);
}
}

View File

@ -0,0 +1,55 @@
package org.embeddedt.modernfix.core.config;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class OptionCategories {
private static String defaultCategory = "default";
private static final Map<String, String> categoryByName = new HashMap<>();
private static final List<String> categoryOrder = new ArrayList<>();
public static void load() {
try(InputStream stream = OptionCategories.class.getResourceAsStream("/modernfix/option_categories.json")) {
if(stream == null)
throw new FileNotFoundException("option_categories.json");
JsonObject object = new JsonParser().parse(new JsonReader(new InputStreamReader(stream, StandardCharsets.UTF_8))).getAsJsonObject();
defaultCategory = object.get("default").getAsString();
JsonObject obj = object.get("categories").getAsJsonObject();
for(Map.Entry<String, JsonElement> category : obj.entrySet()) {
categoryOrder.add(category.getKey());
for(JsonElement e : category.getValue().getAsJsonArray()) {
categoryByName.put(e.getAsString(), category.getKey());
}
}
} catch(IOException | RuntimeException e) {
e.printStackTrace();
categoryOrder.clear();
categoryByName.clear();
categoryOrder.add("default");
}
}
public static List<String> getCategoriesInOrder() {
return Collections.unmodifiableList(categoryOrder);
}
public static String getCategoryForOption(String optionName) {
String category = categoryByName.get(optionName);
if(category == null) {
int lastDotIdx = optionName.lastIndexOf('.');
if(lastDotIdx > 0) {
category = getCategoryForOption(optionName.substring(0, lastDotIdx - 1));
} else
category = defaultCategory;
}
return category;
}
}

View File

@ -4,7 +4,9 @@ import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TranslatableComponent;
import org.jetbrains.annotations.Nullable;
public class ModernFixConfigScreen extends Screen {
private OptionList optionList;
@ -22,11 +24,16 @@ public class ModernFixConfigScreen extends Screen {
this.optionList = new OptionList(this, this.minecraft);
this.children.add(this.optionList);
this.doneButton = new Button(this.width / 2 - 100, this.height - 29, 200, 20, CommonComponents.GUI_DONE, (arg) -> {
this.minecraft.setScreen(lastScreen);
this.onClose();
});
this.addButton(this.doneButton);
}
@Override
public void onClose() {
this.minecraft.setScreen(lastScreen);
}
@Override
public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(poseStack);
@ -35,4 +42,9 @@ public class ModernFixConfigScreen extends Screen {
this.doneButton.setMessage(madeChanges ? new TranslatableComponent("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
super.render(poseStack, mouseX, mouseY, partialTicks);
}
@Override
public void renderComponentHoverEffect(PoseStack matrixStack, @Nullable Style style, int mouseX, int mouseY) {
super.renderComponentHoverEffect(matrixStack, style, mouseX, mouseY);
}
}

View File

@ -25,10 +25,15 @@ public class ModernFixOptionInfoScreen extends Screen {
protected void init() {
super.init();
this.addButton(new Button(this.width / 2 - 100, this.height - 29, 200, 20, CommonComponents.GUI_DONE, (button) -> {
this.minecraft.setScreen(lastScreen);
this.onClose();
}));
}
@Override
public void onClose() {
this.minecraft.setScreen(lastScreen);
}
private void drawMultilineString(PoseStack mStack, Font fr, Component str, int x, int y) {
for(FormattedCharSequence s : fr.split(str, this.width - 50)) {
fr.drawShadow(mStack, s, (float)x, (float)y, 16777215);

View File

@ -1,27 +1,24 @@
package org.embeddedt.modernfix.screen;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.*;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.core.config.Option;
import org.embeddedt.modernfix.core.config.OptionCategories;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
@ -34,6 +31,15 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
private ModernFixConfigScreen mainScreen;
private static MutableComponent getOptionComponent(String optionName) {
String friendlyKey = "modernfix.option.name." + optionName;
TextComponent baseComponent = new TextComponent(optionName);
if(I18n.exists(friendlyKey))
return new TranslatableComponent(friendlyKey).withStyle(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, baseComponent)));
else
return baseComponent;
}
public OptionList(ModernFixConfigScreen arg, Minecraft arg2) {
super(arg2,arg.width + 45, arg.height, 43, arg.height - 32, 20);
@ -41,20 +47,26 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
this.mainScreen = arg;
int maxW = 0;
Map<String, Option> optionMap = ModernFixMixinPlugin.instance.config.getOptionMap();
List<String> sortedKeys = optionMap.keySet().stream().filter(key -> {
int dotCount = 0;
for(char c : key.toCharArray()) {
if(c == '.')
dotCount++;
Multimap<String, Option> optionsByCategory = ModernFixMixinPlugin.instance.config.getOptionCategoryMap();
List<String> theCategories = OptionCategories.getCategoriesInOrder();
for(String category : theCategories) {
String categoryTranslationKey = "modernfix.option.category." + category;
this.addEntry(new CategoryEntry(new TranslatableComponent(categoryTranslationKey)
.withStyle(Style.EMPTY.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TranslatableComponent(categoryTranslationKey + ".description"))))
));
List<Option> sortedKeys = optionsByCategory.get(category).stream().filter(key -> {
int dotCount = 0;
for(char c : key.getName().toCharArray()) {
if(c == '.')
dotCount++;
}
return dotCount >= 2;
}).sorted(Comparator.comparing(Option::getName)).collect(Collectors.toList());
for(Option option : sortedKeys) {
int w = this.minecraft.font.width(getOptionComponent(option.getName()));
maxW = Math.max(w, maxW);
this.addEntry(new OptionEntry(option.getName(), option));
}
return dotCount >= 2;
}).sorted().collect(Collectors.toList());
for(String key : sortedKeys) {
Option option = optionMap.get(key);
int w = this.minecraft.font.width(key);
maxW = Math.max(w, maxW);
this.addEntry(new OptionEntry(key, option));
}
this.maxNameWidth = maxW;
}
@ -67,6 +79,33 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
return super.getRowWidth() + 32;
}
class CategoryEntry extends Entry {
private final Component name;
private final int width;
public CategoryEntry(Component component) {
this.name = component;
this.width = OptionList.this.minecraft.font.width(this.name);
}
public void render(PoseStack matrixStack, int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean isMouseOver, float partialTicks) {
Font var10000 = OptionList.this.minecraft.font;
float x = (float)(OptionList.this.minecraft.screen.width / 2 - this.width / 2);
int y = top + height - 10;
var10000.draw(matrixStack, this.name, x, y, 16777215);
if(mouseX >= x && mouseY >= y && mouseX <= (x + this.width) && mouseY <= (y + OptionList.this.minecraft.font.lineHeight))
OptionList.this.mainScreen.renderComponentHoverEffect(matrixStack, this.name.getStyle(), mouseX, mouseY);
}
public boolean changeFocus(boolean focus) {
return false;
}
public List<? extends GuiEventListener> children() {
return Collections.emptyList();
}
}
class OptionEntry extends Entry {
private final String name;
@ -102,10 +141,12 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
@Override
public void render(PoseStack matrixStack, int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean isMouseOver, float partialTicks) {
MutableComponent nameComponent = new TextComponent(this.name);
MutableComponent nameComponent = getOptionComponent(this.name);
if(this.option.isUserDefined())
nameComponent = nameComponent.withStyle(ChatFormatting.ITALIC).append(new TranslatableComponent("modernfix.config.not_default"));
OptionList.this.minecraft.font.draw(matrixStack, nameComponent, (float)(left + 160 - OptionList.this.maxNameWidth), (float)(top + height / 2 - 4), 16777215);
nameComponent = nameComponent.withStyle(style -> style.withItalic(true)).append(new TranslatableComponent("modernfix.config.not_default"));
float textX = (float)(left + 160 - OptionList.this.maxNameWidth);
float textY = (float)(top + height / 2 - 4);
OptionList.this.minecraft.font.draw(matrixStack, nameComponent, textX, textY, 16777215);
this.toggleButton.x = left + 175;
this.toggleButton.y = top;
this.toggleButton.setMessage(getOptionMessage(this.option));
@ -113,6 +154,8 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
this.helpButton.x = left + 175 + 55;
this.helpButton.y = top;
this.helpButton.render(matrixStack, mouseX, mouseY, partialTicks);
if(mouseX >= textX && mouseY >= textY && mouseX <= (textX + OptionList.this.maxNameWidth) && mouseY <= (textY + OptionList.this.minecraft.font.lineHeight))
OptionList.this.mainScreen.renderComponentHoverEffect(matrixStack, nameComponent.getStyle(), mouseX, mouseY);
}
private Component getOptionMessage(Option option) {

View File

@ -10,6 +10,15 @@
"modernfix.config.not_default": " (modified)",
"asynclocator.map.locating": "Map (Locating...)",
"asynclocator.map.none": "Map (No nearby feature found)",
"modernfix.option.category.performance": "Performance",
"modernfix.option.category.performance.description": "Features that help improve game/launch performance",
"modernfix.option.category.bugfixes": "Bugfixes",
"modernfix.option.category.bugfixes.description": "Core bugfixes to improve game stability",
"modernfix.option.category.troubleshooting": "Troubleshooting/Utilities",
"modernfix.option.category.troubleshooting.description": "Features intended to assist in diagnosing problems",
"modernfix.option.category.expert_only": "Expert only",
"modernfix.option.category.expert_only.description": "Do not change unless you know what you are doing",
"modernfix.option.name.mixin.perf.async_jei": "Background JEI loading",
"modernfix.option.mixin.perf.async_jei": "1.16 only. **A key optimization.** Patches JEI to perform its reloading on a background thread, completely eliminating the long delay it adds to world loading.",
"modernfix.option.mixin.perf.async_locator": "1.16 only. Backports the Async Locator mod's patches to eliminate server freezes associated with `/locate`, loot table generation, etc.",
"modernfix.option.mixin.perf.biome_zoomer": "1.16 only. Minor optimization to improve the performance of the biome zoomer using logic from 1.18.",

View File

@ -0,0 +1,14 @@
{
"default": "expert_only",
"categories": {
"performance": [
"mixin.perf.async_jei",
"mixin.perf.dynamic_resources"
],
"troubleshooting": [
"mixin.feature.spam_thread_dump",
"mixin.feature.spark_profile_launch"
],
"expert_only": []
}
}