From b9640cd470dbc45498ad19e9d6f53e3f027a64d9 Mon Sep 17 00:00:00 2001 From: quat1024 Date: Mon, 23 Oct 2023 23:36:09 -0400 Subject: [PATCH] Rough cut of a 'change set' api for config GUIs --- .../client/config/IngameConfigHandler.java | 30 +---- .../base/client/config/QButtonHandler.java | 6 - .../base/client/config/obj/BooleanObject.java | 2 +- .../client/config/screen/AbstractQScreen.java | 20 ++- .../config/screen/QuarkConfigHomeScreen.java | 29 ++-- .../client/config/screen/SectionScreen.java | 71 ++++++++++ .../config/screen/widgets/CheckboxButton.java | 23 ++-- .../config/screen/widgets/SectionList.java | 126 ++++++++++++++++++ .../base/module/config/IConfigCallback.java | 13 -- .../vazkii/quark/base/proxy/ClientProxy.java | 6 - .../vazkii/quark/base/proxy/CommonProxy.java | 48 +++---- src/main/java/vazkii/zeta/Zeta.java | 16 ++- .../java/vazkii/zeta/config/ChangeSet.java | 102 ++++++++++++++ .../zeta/config/IZetaConfigInternals.java | 2 +- .../vazkii/zeta/config/SectionDefinition.java | 2 +- .../vazkii/zeta/config/ValueDefinition.java | 3 +- .../zeta/config/WeirdConfigSingleton.java | 99 ++++++++++++-- .../java/vazkii/zeta/module/ZetaModule.java | 6 +- .../vazkii/zeta/module/ZetaModuleManager.java | 18 --- .../registry/IZetaBlockColorProvider.java | 2 +- .../zeta/registry/IZetaItemColorProvider.java | 2 +- .../java/vazkii/zetaimplforge/ForgeZeta.java | 10 +- .../config/TerribleForgeConfigHackery.java | 10 +- 23 files changed, 495 insertions(+), 151 deletions(-) create mode 100644 src/main/java/vazkii/quark/base/client/config/screen/SectionScreen.java create mode 100644 src/main/java/vazkii/quark/base/client/config/screen/widgets/SectionList.java create mode 100644 src/main/java/vazkii/zeta/config/ChangeSet.java diff --git a/src/main/java/vazkii/quark/base/client/config/IngameConfigHandler.java b/src/main/java/vazkii/quark/base/client/config/IngameConfigHandler.java index 1827f61e14..32cd408ccc 100644 --- a/src/main/java/vazkii/quark/base/client/config/IngameConfigHandler.java +++ b/src/main/java/vazkii/quark/base/client/config/IngameConfigHandler.java @@ -6,42 +6,26 @@ import vazkii.quark.api.config.IConfigElement; import vazkii.quark.api.config.IConfigObject; import vazkii.quark.base.Quark; -import vazkii.quark.base.module.config.IConfigCallback; +import vazkii.zeta.config.ChangeSet; import vazkii.zeta.module.ZetaCategory; import java.util.LinkedHashMap; import java.util.Map; @OnlyIn(Dist.CLIENT) -public final class IngameConfigHandler implements IConfigCallback { +@Deprecated +public final class IngameConfigHandler { + @Deprecated public static final IngameConfigHandler INSTANCE = new IngameConfigHandler(); - public Map topLevelCategories = new LinkedHashMap<>(); + //TODO: make this non-static mayybe + public final ChangeSet changeSet = new ChangeSet(Quark.ZETA.configInternals); - private IConfigCategory currCategory = null; + public Map topLevelCategories = new LinkedHashMap<>(); private IngameConfigHandler() {} - @Override - public void push(String s, String comment, Object holderObject) { - IConfigCategory newCategory; - if(currCategory == null) { - newCategory = new TopLevelCategory(s, comment, null); - topLevelCategories.put(s, (TopLevelCategory) newCategory); - } else newCategory = currCategory.addCategory(s, comment, holderObject); - - currCategory = newCategory; - } - - @Override - public void pop() { - if(currCategory != null) { - currCategory.close(); - currCategory = currCategory.getParent(); - } - } - public IConfigObject getCategoryEnabledObject(ZetaCategory category) { return topLevelCategories.get("categories").getModuleOption(category); } diff --git a/src/main/java/vazkii/quark/base/client/config/QButtonHandler.java b/src/main/java/vazkii/quark/base/client/config/QButtonHandler.java index e7940940af..c6ee630bd1 100644 --- a/src/main/java/vazkii/quark/base/client/config/QButtonHandler.java +++ b/src/main/java/vazkii/quark/base/client/config/QButtonHandler.java @@ -1,7 +1,6 @@ package vazkii.quark.base.client.config; import com.google.common.collect.ImmutableSet; -import net.minecraft.Util; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.events.GuiEventListener; @@ -11,7 +10,6 @@ import net.minecraft.client.resources.language.I18n; import net.minecraftforge.client.event.ScreenEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.loading.FMLPaths; import vazkii.quark.base.client.config.screen.widgets.QButton; import vazkii.quark.base.handler.GeneralConfig; @@ -40,8 +38,4 @@ public static void onGuiInit(ScreenEvent.Init event) { } } - public static void openFile() { - Util.getPlatform().openFile(FMLPaths.CONFIGDIR.get().toFile()); - } - } diff --git a/src/main/java/vazkii/quark/base/client/config/obj/BooleanObject.java b/src/main/java/vazkii/quark/base/client/config/obj/BooleanObject.java index bfdcfa7ac9..0cdf25449f 100644 --- a/src/main/java/vazkii/quark/base/client/config/obj/BooleanObject.java +++ b/src/main/java/vazkii/quark/base/client/config/obj/BooleanObject.java @@ -20,7 +20,7 @@ public BooleanObject(ConfigValue value, String comment, Boolean default @Override public void addWidgets(CategoryScreen parent, IConfigElement element, List widgets) { - widgets.add(new WidgetWrapper(new CheckboxButton(230, 3, this))); + //widgets.add(new WidgetWrapper(new CheckboxButton(230, 3, this))); //TODO } diff --git a/src/main/java/vazkii/quark/base/client/config/screen/AbstractQScreen.java b/src/main/java/vazkii/quark/base/client/config/screen/AbstractQScreen.java index 2cb43bb01f..b85e0b7e36 100644 --- a/src/main/java/vazkii/quark/base/client/config/screen/AbstractQScreen.java +++ b/src/main/java/vazkii/quark/base/client/config/screen/AbstractQScreen.java @@ -10,8 +10,11 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import vazkii.quark.api.config.IConfigCategory; +import vazkii.quark.base.client.config.IngameConfigHandler; import vazkii.quark.base.client.config.obj.AbstractStringInputObject; import vazkii.quark.base.client.config.obj.ListObject; +import vazkii.zeta.config.ChangeSet; +import vazkii.zeta.config.SectionDefinition; public abstract class AbstractQScreen extends Screen { @@ -22,11 +25,6 @@ public AbstractQScreen(Screen parent) { this.parent = parent; } - @Override - public void render(@Nonnull PoseStack mstack, int mouseX, int mouseY, float partialTicks) { - super.render(mstack, mouseX, mouseY, partialTicks); - } - public void returnToParent(Button button) { minecraft.setScreen(parent); } @@ -39,6 +37,10 @@ public OnPress categoryLink(IConfigCategory category) { return b -> minecraft.setScreen(new CategoryScreen(this, category)); } + public OnPress sectionLink(SectionDefinition section) { + return b -> minecraft.setScreen(new SectionScreen(this, section)); + } + public OnPress stringInput(AbstractStringInputObject object) { return b -> minecraft.setScreen(new StringInputScreen<>(this, object)); } @@ -47,4 +49,12 @@ public OnPress listInput(ListObject object) { return b -> minecraft.setScreen(new ListInputScreen(this, object)); } + public OnPress nothing_TODO_STUB() { + return b -> {}; + } + + //it's recommended to access it through here for now, since I dunno if this should be a singleton + public ChangeSet getChangeSet() { + return IngameConfigHandler.INSTANCE.changeSet; + } } diff --git a/src/main/java/vazkii/quark/base/client/config/screen/QuarkConfigHomeScreen.java b/src/main/java/vazkii/quark/base/client/config/screen/QuarkConfigHomeScreen.java index af370850a2..a8684b357d 100644 --- a/src/main/java/vazkii/quark/base/client/config/screen/QuarkConfigHomeScreen.java +++ b/src/main/java/vazkii/quark/base/client/config/screen/QuarkConfigHomeScreen.java @@ -20,14 +20,14 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; -import vazkii.quark.api.config.IConfigCategory; import vazkii.quark.base.Quark; -import vazkii.quark.base.client.config.IngameConfigHandler; import vazkii.quark.base.client.config.screen.widgets.CheckboxButton; import vazkii.quark.base.client.config.screen.widgets.IconButton; import vazkii.quark.base.client.config.screen.widgets.SocialButton; import vazkii.quark.base.handler.ContributorRewardHandler; import vazkii.quark.base.handler.GeneralConfig; +import vazkii.zeta.config.SectionDefinition; +import vazkii.zeta.config.ValueDefinition; import vazkii.zeta.module.ZetaCategory; public class QuarkConfigHomeScreen extends AbstractQScreen { @@ -62,19 +62,20 @@ protected void init() { if(i < categories.size()) { //a category button ZetaCategory category = categories.get(i); - IConfigCategory ingameCategory = IngameConfigHandler.INSTANCE.getConfigCategory(category); + ValueDefinition categoryEnabled = Quark.ZETA.weirdConfigSingleton.getCategoryEnabledOption(category); + SectionDefinition categorySection = Quark.ZETA.weirdConfigSingleton.getCategorySection(category); bWidth -= 20; //room for the checkbox - Button mainButton = addRenderableWidget(new IconButton(x, y, bWidth, 20, componentFor(ingameCategory), category.icon.get(), categoryLink(ingameCategory))); - Button checkButton = addRenderableWidget(new CheckboxButton(x + bWidth, y, IngameConfigHandler.INSTANCE.getCategoryEnabledObject(category))); + Button mainButton = addRenderableWidget(new IconButton(x, y, bWidth, 20, componentFor(categorySection), category.icon.get(), sectionLink(categorySection))); + Button checkButton = addRenderableWidget(new CheckboxButton(x + bWidth, y, categoryEnabled, getChangeSet())); boolean active = category.modsLoaded(Quark.ZETA); mainButton.active = active; checkButton.active = active; } else { //"General Settings" - IConfigCategory generalSettings = IngameConfigHandler.INSTANCE.getConfigCategory(null); - addRenderableWidget(new Button(x, y, bWidth, 20, componentFor(generalSettings), categoryLink(generalSettings))); + SectionDefinition generalSection = Quark.ZETA.weirdConfigSingleton.getGeneralSection(); + addRenderableWidget(new Button(x, y, bWidth, 20, componentFor(generalSection), sectionLink(generalSection))); } } @@ -108,17 +109,18 @@ private static List centeredRow(int centerX, int buttonWidth, int hpad, return result; } - private static Component componentFor(IConfigCategory c) { - MutableComponent comp = Component.translatable("quark.category." + c.getName()); + private Component componentFor(SectionDefinition section) { + MutableComponent comp = Component.translatable("quark.category." + section.name); - if(c.isDirty()) + if(getChangeSet().isDirty(section)) comp.append(Component.literal("*").withStyle(ChatFormatting.GOLD)); return comp; } public void commit(Button button) { - IngameConfigHandler.INSTANCE.commit(); + //IngameConfigHandler.INSTANCE.commit(); + getChangeSet().applyAllChanges(); returnToParent(button); } @@ -149,6 +151,11 @@ public void render(@Nonnull PoseStack mstack, int mouseX, int mouseY, float part drawCenteredString(mstack, font, ChatFormatting.BOLD + I18n.get("quark.gui.config.header", WordUtils.capitalizeFully(Quark.MOD_ID)), width / 2, 15, 0x48ddbc); drawCenteredString(mstack, font, I18n.get("quark.gui.config.subheader1", ChatFormatting.LIGHT_PURPLE, ContributorRewardHandler.featuredPatron, ChatFormatting.RESET), width / 2, 28, 0x9EFFFE); drawCenteredString(mstack, font, I18n.get("quark.gui.config.subheader2"), width / 2, 38, 0x9EFFFE); + + //TODO TODO TODO flesh this out + int changeCount = getChangeSet().changeCount(); + if(changeCount != 0) + drawCenteredString(mstack, font, changeCount + " unsaved change" + (changeCount > 1 ? "s" : ""), width/2 - 150, height-30, 0xFF8800); } } diff --git a/src/main/java/vazkii/quark/base/client/config/screen/SectionScreen.java b/src/main/java/vazkii/quark/base/client/config/screen/SectionScreen.java new file mode 100644 index 0000000000..501f066534 --- /dev/null +++ b/src/main/java/vazkii/quark/base/client/config/screen/SectionScreen.java @@ -0,0 +1,71 @@ +package vazkii.quark.base.client.config.screen; + +import javax.annotation.Nonnull; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.resources.language.I18n; +import org.apache.commons.lang3.text.WordUtils; +import vazkii.quark.base.Quark; +import vazkii.quark.base.client.config.screen.widgets.ScrollableWidgetList; +import vazkii.quark.base.client.config.screen.widgets.SectionList; +import vazkii.zeta.config.SectionDefinition; + +public class SectionScreen extends AbstractScrollingWidgetScreen { + + public final SectionDefinition section; + private String breadcrumbs; + + public SectionScreen(Screen parent, SectionDefinition section) { + super(parent); + this.section = section; + } + + @Override + protected void init() { + super.init(); + +// breadcrumbs = section.getName(); +// IConfigCategory currCategory = section.getParent(); +// while(currCategory != null) { +// breadcrumbs = String.format("%s > %s", currCategory.getName(), breadcrumbs); +// currCategory = currCategory.getParent(); +// } +// breadcrumbs = String.format("> %s", breadcrumbs); + breadcrumbs = section.name; //TODO readd breadcrumbs + } + + @Override + public void render(@Nonnull PoseStack mstack, int mouseX, int mouseY, float partialTicks) { + super.render(mstack, mouseX, mouseY, partialTicks); + + int left = 20; + + String modName = WordUtils.capitalizeFully(Quark.MOD_ID); + font.draw(mstack, ChatFormatting.BOLD + I18n.get("quark.gui.config.header", modName), left, 10, 0x48ddbc); + font.draw(mstack, breadcrumbs, left, 20, 0xFFFFFF); + } + + @Override + protected ScrollableWidgetList createWidgetList() { + return new SectionList<>(this); + } + + @Override + protected void onClickDefault(Button b) { + getChangeSet().resetToDefault(section); + } + + @Override + protected void onClickDiscard(Button b) { + getChangeSet().removeChange(section); + } + + @Override + protected boolean isDirty() { + return getChangeSet().isDirty(section); + } + +} diff --git a/src/main/java/vazkii/quark/base/client/config/screen/widgets/CheckboxButton.java b/src/main/java/vazkii/quark/base/client/config/screen/widgets/CheckboxButton.java index a024707c9a..67caa176f0 100644 --- a/src/main/java/vazkii/quark/base/client/config/screen/widgets/CheckboxButton.java +++ b/src/main/java/vazkii/quark/base/client/config/screen/widgets/CheckboxButton.java @@ -1,7 +1,5 @@ package vazkii.quark.base.client.config.screen.widgets; -import java.util.function.Supplier; - import javax.annotation.Nonnull; import com.mojang.blaze3d.systems.RenderSystem; @@ -10,20 +8,25 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; -import vazkii.quark.api.config.IConfigObject; import vazkii.quark.base.handler.MiscUtil; +import vazkii.zeta.config.ChangeSet; +import vazkii.zeta.config.ValueDefinition; public class CheckboxButton extends Button { - private final Supplier checkedSupplier; + private final ValueDefinition value; + private final ChangeSet changes; - public CheckboxButton(int x, int y, Supplier checkedSupplier, OnPress onClick) { - super(x, y, 20, 20, Component.literal(""), onClick); - this.checkedSupplier = checkedSupplier; + public CheckboxButton(int x, int y, ValueDefinition value, ChangeSet changes) { + super(x, y, 20, 20, Component.literal(""), CheckboxButton::toggle); + this.value = value; + this.changes = changes; } - public CheckboxButton(int x, int y, IConfigObject configObj) { - this(x, y, configObj::getCurrentObj, (b) -> configObj.setCurrentObj(!configObj.getCurrentObj())); + private static void toggle(Button press) { + if(press instanceof CheckboxButton checkbox) { + checkbox.changes.toggle(checkbox.value); + } } @Override @@ -33,7 +36,7 @@ public void renderButton(@Nonnull PoseStack mstack, int mouseX, int mouseY, floa RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); RenderSystem.setShaderTexture(0, MiscUtil.GENERAL_ICONS); - boolean enabled = checkedSupplier.get() && active; + boolean enabled = changes.get(value) && active; int u = enabled ? 0 : 16; int v = 93; diff --git a/src/main/java/vazkii/quark/base/client/config/screen/widgets/SectionList.java b/src/main/java/vazkii/quark/base/client/config/screen/widgets/SectionList.java new file mode 100644 index 0000000000..5905bd7bb3 --- /dev/null +++ b/src/main/java/vazkii/quark/base/client/config/screen/widgets/SectionList.java @@ -0,0 +1,126 @@ +package vazkii.quark.base.client.config.screen.widgets; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import javax.annotation.Nonnull; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import vazkii.quark.base.client.config.screen.SectionScreen; +import vazkii.quark.base.client.handler.TopLayerTooltipHandler; +import vazkii.zeta.config.Definition; +import vazkii.zeta.config.SectionDefinition; +import vazkii.zeta.config.ValueDefinition; + +public class SectionList extends ScrollableWidgetList> { + + public SectionList(SectionScreen parent) { + super(parent); + } + + @Override + @SuppressWarnings("unchecked") + protected void findEntries() { + for(ValueDefinition value : parent.section.getValues()) { + addEntry(new Entry<>(this.parent, (T) value)); + } + + Collection subsections = parent.section.getSubsections(); + if(!subsections.isEmpty()) { + addEntry(new Entry<>(this.parent, null)); //divider + + for(SectionDefinition section : parent.section.getSubsections()) + addEntry(new Entry<>(this.parent, (T) section)); + } + } + + public static final class Entry extends ScrollableWidgetList.Entry> { + + private final SectionScreen parent; + private final T element; + + public Entry(SectionScreen parent, T element) { + this.parent = parent; + this.element = element; + + //TODO: IWidgetProvider but side safe + //if(element != null) + // element.addWidgets(parent, element, children); + } + + @Override + public void render(@Nonnull PoseStack mstack, int index, int rowTop, int rowLeft, int rowWidth, int rowHeight, int mouseX, int mouseY, boolean hovered, float partialTicks) { + super.render(mstack, index, rowTop, rowLeft, rowWidth, rowHeight, mouseX, mouseY, hovered, partialTicks); + + Minecraft mc = Minecraft.getInstance(); + + if(element != null) { + int left = rowLeft + 10; + int top = rowTop + 4; + + int effIndex = index + 1; + if(element instanceof SectionDefinition) + effIndex--; // compensate for the divider + drawBackground(mstack, effIndex, rowTop, rowLeft, rowWidth, rowHeight, mouseX, mouseY, hovered); + + String name = element.getGuiDisplayName(Collections.emptyList(), I18n::get); + if(parent.getChangeSet().isDirty(element)) + name += ChatFormatting.GOLD + "*"; + + int len = mc.font.width(name); + int maxLen = rowWidth - 85; + String originalName = null; + if(len > maxLen) { + originalName = name; + do { + name = name.substring(0, name.length() - 1); + len = mc.font.width(name); + } while(len > maxLen); + + name += "..."; + } + + List tooltip = new ArrayList<>(element.comment); + if(originalName != null) { + if(tooltip.isEmpty()) { + tooltip = new LinkedList<>(); + tooltip.add(originalName); + } else { + tooltip.add(0, ""); + tooltip.add(0, originalName); + } + } + + if(!tooltip.isEmpty()) { + int hoverLeft = left + mc.font.width(name + " "); + int hoverRight = hoverLeft + mc.font.width("(?)"); + + name += (ChatFormatting.AQUA + " (?)"); + if(mouseX >= hoverLeft && mouseX < hoverRight && mouseY >= top && mouseY < (top + 10)) + TopLayerTooltipHandler.setTooltip(tooltip, mouseX, mouseY); + } + + mc.font.drawShadow(mstack, name, left, top, 0xFFFFFF); + //mc.font.drawShadow(mstack, element.getSubtitle(), left, top + 10, 0x999999); //TODO: getSubtitle + } else { + String s = I18n.get("quark.gui.config.subcategories"); + mc.font.drawShadow(mstack, s, rowLeft + (float) (rowWidth / 2 - mc.font.width(s) / 2), rowTop + 7, 0x6666FF); + } + } + + @Nonnull + @Override + public Component getNarration() { + return Component.literal(element == null ? "" : element.getGuiDisplayName(Collections.emptyList(), I18n::get)); + } + + } + +} diff --git a/src/main/java/vazkii/quark/base/module/config/IConfigCallback.java b/src/main/java/vazkii/quark/base/module/config/IConfigCallback.java index ee68dc24d5..332bda08ab 100644 --- a/src/main/java/vazkii/quark/base/module/config/IConfigCallback.java +++ b/src/main/java/vazkii/quark/base/module/config/IConfigCallback.java @@ -2,21 +2,8 @@ public interface IConfigCallback { - void push(String s, String comment, Object holderObject); - void pop(); - final class Dummy implements IConfigCallback { - @Override - public void push(String s, String comment, Object holderObject) { - // NO-OP - } - - @Override - public void pop() { - // NO-OP - } - } } diff --git a/src/main/java/vazkii/quark/base/proxy/ClientProxy.java b/src/main/java/vazkii/quark/base/proxy/ClientProxy.java index 7f9991473c..c2e6b29d73 100644 --- a/src/main/java/vazkii/quark/base/proxy/ClientProxy.java +++ b/src/main/java/vazkii/quark/base/proxy/ClientProxy.java @@ -31,7 +31,6 @@ import vazkii.quark.base.handler.MiscUtil; import vazkii.quark.base.handler.RenderLayerHandler; import vazkii.quark.base.handler.WoodSetHandler; -import vazkii.quark.base.module.config.IConfigCallback; import vazkii.quark.base.network.QuarkNetwork; import vazkii.quark.base.network.message.structural.C2SUpdateFlag; import vazkii.quark.mixin.client.accessor.AccessorMultiPlayerGameMode; @@ -116,11 +115,6 @@ public InteractionResult clientUseItem(Player player, Level level, InteractionHa return InteractionResult.PASS; } - @Override - public IConfigCallback getConfigCallback() { - return IngameConfigHandler.INSTANCE; - } - @Override public boolean isClientPlayerHoldingShift() { return Screen.hasShiftDown(); diff --git a/src/main/java/vazkii/quark/base/proxy/CommonProxy.java b/src/main/java/vazkii/quark/base/proxy/CommonProxy.java index 823312a93c..8eb26759e5 100644 --- a/src/main/java/vazkii/quark/base/proxy/CommonProxy.java +++ b/src/main/java/vazkii/quark/base/proxy/CommonProxy.java @@ -5,7 +5,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; @@ -32,7 +31,6 @@ import vazkii.zeta.event.ZRegister; import vazkii.zeta.event.bus.LoadEvent; import vazkii.zeta.module.ZetaCategory; -import vazkii.zeta.module.ZetaModuleManager; import vazkii.zetaimplforge.module.ModFileScanDataModuleFinder; import java.time.LocalDateTime; @@ -45,9 +43,6 @@ public class CommonProxy { public static boolean jingleTheBells = false; private boolean configGuiSaving = false; - //TODO: better spot - private IZetaConfigInternals iZetaConfigInternals; - public void start() { ForgeRegistries.RECIPE_SERIALIZERS.register(Quark.MOD_ID + ":exclusion", ExclusionRecipe.SERIALIZER); @@ -75,25 +70,22 @@ public void start() { MinecraftForge.EVENT_BUS.register(RecipeCrawlHandler.class); MinecraftForge.EVENT_BUS.register(ToolInteractionHandler.class); - ZetaModuleManager modules = Quark.ZETA.modules; - modules.initCategories(List.of( - new ZetaCategory("automation", Items.REDSTONE), - new ZetaCategory("building", Items.BRICKS), - new ZetaCategory("management", Items.CHEST), - new ZetaCategory("tools", Items.IRON_PICKAXE), - new ZetaCategory("tweaks", Items.NAUTILUS_SHELL), - new ZetaCategory("world", Items.GRASS_BLOCK), - new ZetaCategory("mobs", Items.PIG_SPAWN_EGG), - new ZetaCategory("client", Items.ENDER_EYE), - new ZetaCategory("experimental", Items.TNT), - new ZetaCategory("oddities", Items.CHORUS_FRUIT, Quark.ODDITIES_ID) - )); - Quark.ZETA.modules.load(new ModFileScanDataModuleFinder(Quark.MOD_ID) - .and(new LegacyQuarkModuleFinder())); //TODO: no - - //TODO: find somewhere better to put this - SectionDefinition rootConfig = Quark.ZETA.weirdConfigSingleton.makeRootConfig(GeneralConfig.INSTANCE, Quark.ZETA.modules); - iZetaConfigInternals = Quark.ZETA.makeConfigInternals(rootConfig); + Quark.ZETA.loadModules( + List.of( + new ZetaCategory("automation", Items.REDSTONE), + new ZetaCategory("building", Items.BRICKS), + new ZetaCategory("management", Items.CHEST), + new ZetaCategory("tools", Items.IRON_PICKAXE), + new ZetaCategory("tweaks", Items.NAUTILUS_SHELL), + new ZetaCategory("world", Items.GRASS_BLOCK), + new ZetaCategory("mobs", Items.PIG_SPAWN_EGG), + new ZetaCategory("client", Items.ENDER_EYE), + new ZetaCategory("experimental", Items.TNT), + new ZetaCategory("oddities", Items.CHORUS_FRUIT, Quark.ODDITIES_ID) + ), + new ModFileScanDataModuleFinder(Quark.MOD_ID).and(new LegacyQuarkModuleFinder()), + GeneralConfig.INSTANCE + ); IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); bus.addListener(this::configChanged); @@ -130,10 +122,10 @@ public void setConfigGuiSaving(boolean saving) { lastConfigChange = Quark.ZETA.ticker_SHOULD_NOT_BE_HERE.ticksInGame; } - //TODO: mess + //TODO: mess, find a better spot for this public void handleQuarkConfigChange() { //ModuleLoader.INSTANCE.configChanged(); - Quark.ZETA.weirdConfigSingleton.onReload(iZetaConfigInternals); //refreshes all the @Config annotations with data from the config + Quark.ZETA.weirdConfigSingleton.onReload(Quark.ZETA.configInternals); //Quark.ZETA.weirdConfigSingleton.getConfigFlagManager().clear(); Quark.ZETA.loadBus.fire(new ZConfigChanged()); if(ModuleLoader.INSTANCE.onConfigReloadJEI != null) @@ -154,10 +146,6 @@ public InteractionResult clientUseItem(Player player, Level level, InteractionHa return InteractionResult.PASS; } - public IConfigCallback getConfigCallback() { - return new IConfigCallback.Dummy(); - } - public boolean isClientPlayerHoldingShift() { return false; } diff --git a/src/main/java/vazkii/zeta/Zeta.java b/src/main/java/vazkii/zeta/Zeta.java index 3190b03d4a..51ae329b61 100644 --- a/src/main/java/vazkii/zeta/Zeta.java +++ b/src/main/java/vazkii/zeta/Zeta.java @@ -1,10 +1,13 @@ package vazkii.zeta; +import java.util.Collection; + import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.BlockHitResult; import org.apache.logging.log4j.Logger; +import vazkii.quark.base.handler.GeneralConfig; import vazkii.zeta.client.ClientTicker; import vazkii.zeta.config.IZetaConfigInternals; import vazkii.zeta.config.SectionDefinition; @@ -14,6 +17,8 @@ import vazkii.zeta.event.bus.LoadEvent; import vazkii.zeta.event.bus.ZetaEventBus; import vazkii.zeta.event.bus.PlayEvent; +import vazkii.zeta.module.ModuleFinder; +import vazkii.zeta.module.ZetaCategory; import vazkii.zeta.module.ZetaModuleManager; import vazkii.zeta.network.ZetaNetworkHandler; import vazkii.zeta.registry.ZetaRegistry; @@ -43,11 +48,20 @@ public Zeta(String modid, Logger log) { public final ZetaModuleManager modules; public final ZetaRegistry registry; - public final WeirdConfigSingleton weirdConfigSingleton = new WeirdConfigSingleton(); + public WeirdConfigSingleton weirdConfigSingleton; //Should probably split this up into various parts + public IZetaConfigInternals configInternals; //TODO: move to ZetaClient. Some bits of the server *do* actually need this for some raisin (config code) @Deprecated public final ClientTicker ticker_SHOULD_NOT_BE_HERE; + public void loadModules(Iterable categories, ModuleFinder finder, Object rootPojo) { + modules.initCategories(categories); + modules.load(finder); + + this.weirdConfigSingleton = new WeirdConfigSingleton(this, rootPojo); + this.configInternals = makeConfigInternals(weirdConfigSingleton.getRootConfig()); + } + public abstract ZetaSide getSide(); public abstract boolean isModLoaded(String modid); diff --git a/src/main/java/vazkii/zeta/config/ChangeSet.java b/src/main/java/vazkii/zeta/config/ChangeSet.java new file mode 100644 index 0000000000..580ad49289 --- /dev/null +++ b/src/main/java/vazkii/zeta/config/ChangeSet.java @@ -0,0 +1,102 @@ +package vazkii.zeta.config; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class ChangeSet implements IZetaConfigInternals { + private final IZetaConfigInternals internals; + + private record Entry(ValueDefinition valueDef, T currentValue, T nextValue) { } + private final Map, Entry> changes = new HashMap<>(); + + public ChangeSet(IZetaConfigInternals internals) { + this.internals = internals; + } + + // Changing single values + + @Override + public void set(ValueDefinition valueDef, T nextValue) { + T currentValue = internals.get(valueDef); + if(Objects.equals(currentValue, nextValue)) + removeChange(valueDef); + else + changes.put(valueDef, new Entry<>(valueDef, currentValue, nextValue)); + } + + public void toggle(ValueDefinition boolDef) { + set(boolDef, !get(boolDef)); + } + + public void resetToDefault(ValueDefinition valueDef) { + set(valueDef, valueDef.defaultValue); + } + + public void removeChange(ValueDefinition valueDef) { + changes.remove(valueDef); + } + + // Changing whole sections + + public void resetToDefault(SectionDefinition sectionDef) { + sectionDef.getValues().forEach(this::resetToDefault); + sectionDef.getSubsections().forEach(this::resetToDefault); + } + + public void removeChange(SectionDefinition sectionDef) { + sectionDef.getValues().forEach(this::removeChange); + sectionDef.getSubsections().forEach(this::removeChange); + } + + // Dirtiness + + public boolean isDirty(ValueDefinition valueDef) { + return changes.containsKey(valueDef); + } + + public boolean isDirty(SectionDefinition sectionDefinition) { + return sectionDefinition.getValues().stream().anyMatch(this::isDirty) || + sectionDefinition.getSubsections().stream().anyMatch(this::isDirty); + } + + public boolean isDirty(Definition def) { + if(def instanceof ValueDefinition val) + return isDirty(val); + else + return isDirty((SectionDefinition) def); + } + + public int changeCount() { + return changes.size(); + } + + // Getting data as if the changes were applied + + @Override + public T get(ValueDefinition definition) { + @SuppressWarnings("unchecked") + Entry entry = (Entry) changes.get(definition); + + if(entry != null) + return entry.nextValue; + else + return internals.get(definition); + } + + @Override + public void refresh(Definition thing) { + // NO-OP + } + + // Application + + public void applyAllChanges() { + changes.values().forEach(this::applyOneChange); + changes.clear(); + } + + private void applyOneChange(Entry entry) { + internals.set(entry.valueDef, entry.nextValue); + } +} diff --git a/src/main/java/vazkii/zeta/config/IZetaConfigInternals.java b/src/main/java/vazkii/zeta/config/IZetaConfigInternals.java index 9aff1b1352..d2a4b3ae9a 100644 --- a/src/main/java/vazkii/zeta/config/IZetaConfigInternals.java +++ b/src/main/java/vazkii/zeta/config/IZetaConfigInternals.java @@ -4,5 +4,5 @@ public interface IZetaConfigInternals { T get(ValueDefinition definition); void set(ValueDefinition definition, T value); - void refresh(Definition thing); + void refresh(Definition thing); //TODO, probably won't need this? } diff --git a/src/main/java/vazkii/zeta/config/SectionDefinition.java b/src/main/java/vazkii/zeta/config/SectionDefinition.java index 8f3a1965d0..308e439d94 100644 --- a/src/main/java/vazkii/zeta/config/SectionDefinition.java +++ b/src/main/java/vazkii/zeta/config/SectionDefinition.java @@ -41,6 +41,6 @@ public Collection> getValues() { @Override public String toString() { - return "SectionDefinition{name='" + name + "'}"; + return "SectionDefinition{" + name + " (" + subsections.size() + " subsections, " + values.size() + " values)}"; } } diff --git a/src/main/java/vazkii/zeta/config/ValueDefinition.java b/src/main/java/vazkii/zeta/config/ValueDefinition.java index b1c78dcda7..8e3538984f 100644 --- a/src/main/java/vazkii/zeta/config/ValueDefinition.java +++ b/src/main/java/vazkii/zeta/config/ValueDefinition.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.function.Predicate; +//TODO: maybe we need "boolean equals(T thing1, T thing2)" public class ValueDefinition extends Definition { public final T defaultValue; @@ -20,6 +21,6 @@ public ValueDefinition(String name, List comment, T defaultValue) { @Override public String toString() { - return "ValueDefinition{name='" + name + "'}"; + return "ValueDefinition{" + name + "}"; } } diff --git a/src/main/java/vazkii/zeta/config/WeirdConfigSingleton.java b/src/main/java/vazkii/zeta/config/WeirdConfigSingleton.java index 2a7f9104e3..7867b9ece9 100644 --- a/src/main/java/vazkii/zeta/config/WeirdConfigSingleton.java +++ b/src/main/java/vazkii/zeta/config/WeirdConfigSingleton.java @@ -1,37 +1,72 @@ package vazkii.zeta.config; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import vazkii.quark.base.handler.GeneralConfig; import vazkii.quark.base.module.config.ConfigFlagManager; +import vazkii.zeta.Zeta; import vazkii.zeta.module.ZetaCategory; import vazkii.zeta.module.ZetaModule; import vazkii.zeta.module.ZetaModuleManager; //TODO: find somewhere better to put this, better data structure etc public class WeirdConfigSingleton { + private final Zeta z; private final ConfigFlagManager cfm = new ConfigFlagManager(); + private final SectionDefinition rootConfig; + + //for updating the values of @Config annotations to match the current state of the config private final List> fieldUpdaters = new ArrayList<>(); - public SectionDefinition makeRootConfig(Object rootPojo, ZetaModuleManager modules) { - SectionDefinition root = new SectionDefinition("root", List.of()); + //ummmmmmm i think my abstraction isn't very good + private final SectionDefinition generalSection; + private final Map categoriesToSections = new HashMap<>(); + private final Map> categoryEnabledOptions = new HashMap<>(); + private final Map> moduleEnabledOptions = new HashMap<>(); + + //state (TODO: unused) + private final Set enabledCategories = new HashSet<>(); + + public WeirdConfigSingleton(Zeta z, Object rootPojo) { + this.z = z; + + ZetaModuleManager modules = z.modules; - SectionDefinition general = root.getOrCreateSubsection("general", List.of()); - ConfigObjectMapper.readInto(general, rootPojo, fieldUpdaters::add); + //all modules are enabled by default + enabledCategories.addAll(modules.getCategories()); + //TODO: track module enablement too with a hashset too? + + this.rootConfig = new SectionDefinition("root", List.of()); + + if(rootPojo == null) + generalSection = null; + else { + generalSection = rootConfig.getOrCreateSubsection("general", List.of()); + ConfigObjectMapper.readInto(generalSection, rootPojo, fieldUpdaters::add); + } for(ZetaCategory category : modules.getInhabitedCategories()) { //category enablement option - ValueDefinition categoryEnabled = root.getOrCreateSubsection("categories", List.of()).addValue(category.name, List.of(), true); - fieldUpdaters.add(z -> modules.MOVE_TO_CONFIG_setCategoryEnabled(category, z.get(categoryEnabled))); + //TODO: why aren't these showing up? + ValueDefinition categoryEnabled = rootConfig.getOrCreateSubsection("categories", List.of("cateogyr enabled :O")).addValue(category.name, List.of("is it enabed"), true); + categoryEnabledOptions.put(category, categoryEnabled); + fieldUpdaters.add(i -> setCategoryEnabled(category, i.get(categoryEnabled))); //per-category options: - SectionDefinition categorySection = root.getOrCreateSubsection(category.name, List.of()); + SectionDefinition categorySection = rootConfig.getOrCreateSubsection(category.name, List.of()); + categoriesToSections.put(category, categorySection); + for(ZetaModule module : modules.modulesInCategory(category)) { //module enablement option ValueDefinition moduleEnabled = categorySection.addValue(module.displayName, List.of(module.description), module.enabledByDefault); - fieldUpdaters.add(z -> module.enabled = z.get(moduleEnabled)); + moduleEnabledOptions.put(module, moduleEnabled); + fieldUpdaters.add(i -> setModuleEnabled(module, i.get(moduleEnabled))); //module @Config options SectionDefinition moduleSection = categorySection.getOrCreateSubsection(module.lowercaseName, List.of(module.description)); @@ -45,14 +80,58 @@ public SectionDefinition makeRootConfig(Object rootPojo, ZetaModuleManager modul desc.append("This is done to prevent content overlap.\nYou can turn this on to force the feature to be loaded even if the above mods are also loaded."); ValueDefinition ignoreAntiOverlap = moduleSection.addValue("Ignore Anti Overlap", List.of(desc.toString()), false); - fieldUpdaters.add(z -> module.ignoreAntiOverlap = !GeneralConfig.useAntiOverlap || z.get(ignoreAntiOverlap)); + fieldUpdaters.add(i -> module.ignoreAntiOverlap = !GeneralConfig.useAntiOverlap || i.get(ignoreAntiOverlap)); } } } + } + + public SectionDefinition getRootConfig() { + return rootConfig; + } + + // bad bad bad + + public SectionDefinition getGeneralSection() { + return generalSection; + } + + public SectionDefinition getCategorySection(ZetaCategory cat) { + return categoriesToSections.get(cat); + } + + public ValueDefinition getCategoryEnabledOption(ZetaCategory cat) { + return categoryEnabledOptions.get(cat); + } + + public ValueDefinition getModuleEnabledOption(ZetaModule module) { + return moduleEnabledOptions.get(module); + } + + // support for the options added by this class + + private void setCategoryEnabled(ZetaCategory cat, boolean enabled) { + if(enabled) + enabledCategories.add(cat); + else + enabledCategories.remove(cat); + + //TODO TODO bad bad bad + for(ZetaModule mod : z.modules.modulesInCategory(cat)) { + mod.setEnabled(z, mod.enabled); + } + } - return root; + private void setModuleEnabled(ZetaModule module, boolean enabled) { + module.setEnabled(z, enabled); } + public boolean isCategoryEnabled(ZetaCategory cat) { + return enabledCategories.contains(cat); + } + + //ummm + public ConfigFlagManager getConfigFlagManager() { return cfm; } diff --git a/src/main/java/vazkii/zeta/module/ZetaModule.java b/src/main/java/vazkii/zeta/module/ZetaModule.java index e281bced98..36eb093f1e 100644 --- a/src/main/java/vazkii/zeta/module/ZetaModule.java +++ b/src/main/java/vazkii/zeta/module/ZetaModule.java @@ -44,6 +44,10 @@ public void postConstruct() { public final void setEnabled(Zeta z, boolean willEnable) { configEnabled = willEnable; + //TODO: i messed up the category enabled stuff, Its Weird now + if(z.weirdConfigSingleton != null && !z.weirdConfigSingleton.isCategoryEnabled(category)) + willEnable = false; + disabledByOverlap = false; if(missingDep) willEnable = false; @@ -56,7 +60,7 @@ else if(!ignoreAntiOverlap && antiOverlap != null && antiOverlap.stream().anyMat firstLoad = false; } - public final void setEnabledAndManageSubscriptions(Zeta z, boolean nowEnabled) { + private void setEnabledAndManageSubscriptions(Zeta z, boolean nowEnabled) { if(firstLoad || (this.enabled != nowEnabled)) { //TODO: Cheap hacks to keep non-Zeta Quark modules on life support. diff --git a/src/main/java/vazkii/zeta/module/ZetaModuleManager.java b/src/main/java/vazkii/zeta/module/ZetaModuleManager.java index 33e17cdf75..7296a01e77 100644 --- a/src/main/java/vazkii/zeta/module/ZetaModuleManager.java +++ b/src/main/java/vazkii/zeta/module/ZetaModuleManager.java @@ -30,11 +30,6 @@ public class ZetaModuleManager { private final Map categoriesById = new LinkedHashMap<>(); private final Map> modulesInCategory = new HashMap<>(); - //TODO ZETA (Very important): move this state to some sort of "config" area - // It's only here since it's stored *on* the category *enum* in current Quark - @Deprecated - private final Set MOVE_TO_CONFIG_enabledCategoires = new HashSet<>(); - public ZetaModuleManager(Zeta z) { this.z = z; } @@ -80,19 +75,6 @@ public List modulesInCategory(ZetaCategory cat) { return modulesInCategory.computeIfAbsent(cat, __ -> new ArrayList<>()); } - @Deprecated - public boolean MOVE_TO_CONFIG_categoryIsEnabled(ZetaCategory cat) { - return MOVE_TO_CONFIG_enabledCategoires.contains(cat); - } - - @Deprecated - public void MOVE_TO_CONFIG_setCategoryEnabled(ZetaCategory cat, boolean enabled) { - if(enabled) - MOVE_TO_CONFIG_enabledCategoires.add(cat); - else - MOVE_TO_CONFIG_enabledCategoires.remove(cat); - } - // Loading // //first call this diff --git a/src/main/java/vazkii/zeta/registry/IZetaBlockColorProvider.java b/src/main/java/vazkii/zeta/registry/IZetaBlockColorProvider.java index 32a735d845..bffb1abc03 100644 --- a/src/main/java/vazkii/zeta/registry/IZetaBlockColorProvider.java +++ b/src/main/java/vazkii/zeta/registry/IZetaBlockColorProvider.java @@ -4,7 +4,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -@Deprecated(forRemoval = true) //not side-safe +@Deprecated //not side-safe public interface IZetaBlockColorProvider extends IZetaItemColorProvider { @OnlyIn(Dist.CLIENT) diff --git a/src/main/java/vazkii/zeta/registry/IZetaItemColorProvider.java b/src/main/java/vazkii/zeta/registry/IZetaItemColorProvider.java index f6cd39f323..2a27223dc4 100644 --- a/src/main/java/vazkii/zeta/registry/IZetaItemColorProvider.java +++ b/src/main/java/vazkii/zeta/registry/IZetaItemColorProvider.java @@ -4,7 +4,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -@Deprecated(forRemoval = true) //not side-safe +@Deprecated //not side-safe public interface IZetaItemColorProvider { @OnlyIn(Dist.CLIENT) diff --git a/src/main/java/vazkii/zetaimplforge/ForgeZeta.java b/src/main/java/vazkii/zetaimplforge/ForgeZeta.java index bf66eefdee..0b04203fa5 100644 --- a/src/main/java/vazkii/zetaimplforge/ForgeZeta.java +++ b/src/main/java/vazkii/zetaimplforge/ForgeZeta.java @@ -13,10 +13,7 @@ import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -66,15 +63,10 @@ public boolean isModLoaded(String modid) { @Override public IZetaConfigInternals makeConfigInternals(SectionDefinition rootSection) { ForgeConfigSpec.Builder bob = new ForgeConfigSpec.Builder(); - ForgeBackedConfig forge = new ForgeBackedConfig(rootSection, bob); ForgeConfigSpec spec = bob.build(); - ModContainer container = ModLoadingContext.get().getActiveContainer(); - ModConfig modCfg = new ModConfig(ModConfig.Type.COMMON, spec, container); - container.addConfig(modCfg); - - TerribleForgeConfigHackery.loadConfigEarly(modCfg); + TerribleForgeConfigHackery.registerAndLoadConfigEarlierThanUsual(spec); return forge; } diff --git a/src/main/java/vazkii/zetaimplforge/config/TerribleForgeConfigHackery.java b/src/main/java/vazkii/zetaimplforge/config/TerribleForgeConfigHackery.java index 0e88477d73..e4ba4f573b 100644 --- a/src/main/java/vazkii/zetaimplforge/config/TerribleForgeConfigHackery.java +++ b/src/main/java/vazkii/zetaimplforge/config/TerribleForgeConfigHackery.java @@ -8,6 +8,9 @@ import com.electronwill.nightconfig.core.ConfigFormat; import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.io.WritingMode; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.config.ConfigFileTypeHandler; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.loading.FMLPaths; @@ -18,9 +21,12 @@ public class TerribleForgeConfigHackery { private static final Method SETUP_CONFIG_FILE = ObfuscationReflectionHelper.findMethod(ConfigFileTypeHandler.class, "setupConfigFile", ModConfig.class, Path.class, ConfigFormat.class); - public static void loadConfigEarly(ModConfig modConfig) { - //same stuff that forge config tracker does + public static void registerAndLoadConfigEarlierThanUsual(ForgeConfigSpec spec) { + ModContainer container = ModLoadingContext.get().getActiveContainer(); + ModConfig modConfig = new ModConfig(ModConfig.Type.COMMON, spec, container); + container.addConfig(modConfig); + //same stuff that forge config tracker does ConfigFileTypeHandler handler = modConfig.getHandler(); //read config without setting file watcher which could cause resets. forge will load it later CommentedFileConfig configData = readConfig(handler, FMLPaths.CONFIGDIR.get(), modConfig);