/* * Minecraft Forge * Copyright (c) 2016-2019. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.client.gui; /* import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.resources.I18n; import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.ForgeMod; import net.minecraftforge.versions.forge.ForgeVersion; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.ConfigElement; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import net.minecraftforge.fml.client.IModGuiFactory; import net.minecraftforge.fml.client.config.ConfigGuiType; import net.minecraftforge.fml.client.config.DummyConfigElement; import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement; import net.minecraftforge.fml.client.config.GuiConfig; import net.minecraftforge.fml.client.config.GuiConfigEntries; import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry; import net.minecraftforge.fml.client.config.GuiConfigEntries.IConfigEntry; import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry; import net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry; import net.minecraftforge.fml.client.config.IConfigElement; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; import static net.minecraftforge.common.ForgeMod.VERSION_CHECK_CAT; /** * This is the base GuiConfig screen class that all the other Forge-specific config screens will be called from. * Since Forge has multiple config files I thought I would use that opportunity to show some of the ways * that the config GUI system can be extended to create custom config GUIs that have additional features * over the base functionality of just displaying Properties and ConfigCategories. * * The concepts implemented here are: * - using custom IConfigEntry objects to define child-screens that have specific Properties listed * - using custom IConfigEntry objects to define a dummy property that can be used to generate new ConfigCategory objects * - defining the configID string for a GuiConfig object so that the config changed events will be posted when that GuiConfig screen is closed * (the configID string is optional; if it is not defined the config changed events will be posted when the top-most GuiConfig screen * is closed, eg when the parent is null or is not an instance of GuiConfig) * - overriding the IConfigEntry.enabled() method to control the enabled state of one list entry based on the value of another entry * - overriding the IConfigEntry.onGuiClosed() method to perform custom actions when the screen that owns the entry is closed (in this * case a new ConfigCategory is added to the Configuration object) * * The config file structure looks like this: * forge.cfg (general settings all in one category) * forgeChunkLoading.cfg * - Forge (category) * - defaults (category) * - [optional mod override categories]... * * The GUI structure is this: * Base Screen * - General Settings (from forge.cfg) * - Chunk Loader Settings (from forgeChunkLoading.cfg) * - Defaults (these elements are listed directly on this screen) * - Mod Overrides * - Add New Mod Override * - Mod1 * - Mod2 * - etc. * * Other things to check out: * ForgeMod.syncConfig() * ForgeMod.onConfigChanged() * ForgeChunkManager.syncConfigDefaults() * ForgeChunkManager.loadConfiguration() */ /* TODO Config gui public class ForgeGuiFactory implements IModGuiFactory { @Override public void initialize(Minecraft minecraftInstance) {} @Override public boolean hasConfigGui() { return true; } @Override public GuiScreen createConfigGui(GuiScreen parent) { return new ForgeConfigGui(parent); } @Override public Set runtimeGuiCategories() { return null; } public static class ForgeConfigGui extends GuiConfig { public ForgeConfigGui(GuiScreen parentScreen) { super(parentScreen, getConfigElements(), ForgeVersion.MOD_ID, false, false, I18n.format("forge.configgui.forgeConfigTitle")); } private static List getConfigElements() { List list = new ArrayList(); list.add(new DummyCategoryElement("forgeCfg", "forge.configgui.ctgy.forgeGeneralConfig", GeneralEntry.class)); list.add(new DummyCategoryElement("forgeClientCfg", "forge.configgui.ctgy.forgeClientConfig", ClientEntry.class)); list.add(new DummyCategoryElement("forgeChunkLoadingCfg", "forge.configgui.ctgy.forgeChunkLoadingConfig", ChunkLoaderEntry.class)); list.add(new DummyCategoryElement("forgeVersionCheckCfg", "forge.configgui.ctgy.VersionCheckConfig", VersionCheckEntry.class)); return list; } *//** * This custom list entry provides the General Settings entry on the Minecraft Forge Configuration screen. * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. *//* public static class GeneralEntry extends CategoryEntry { public GeneralEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop); } @Override protected GuiScreen buildChildScreen() { // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent // GuiConfig object's entryList will also be refreshed to reflect the changes. return new GuiConfig(this.owningScreen, (new ConfigElement(ForgeMod.getConfig().getCategory(Configuration.CATEGORY_GENERAL))).getChildElements(), this.owningScreen.modID, Configuration.CATEGORY_GENERAL, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart, this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, GuiConfig.getAbridgedConfigPath(ForgeMod.getConfig().toString())); } } *//** * This custom list entry provides the Client only Settings entry on the Minecraft Forge Configuration screen. * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. *//* public static class ClientEntry extends CategoryEntry { public ClientEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop); } @Override protected GuiScreen buildChildScreen() { // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent // GuiConfig object's entryList will also be refreshed to reflect the changes. return new GuiConfig(this.owningScreen, (new ConfigElement(ForgeMod.getConfig().getCategory(Configuration.CATEGORY_CLIENT))).getChildElements(), this.owningScreen.modID, Configuration.CATEGORY_CLIENT, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart, this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, GuiConfig.getAbridgedConfigPath(ForgeMod.getConfig().toString())); } } *//** * This custom list entry provides the Forge Chunk Manager Config entry on the Minecraft Forge Configuration screen. * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. *//* public static class ChunkLoaderEntry extends CategoryEntry { public ChunkLoaderEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop); } @Override protected GuiScreen buildChildScreen() { List list = new ArrayList(); list.add(new DummyCategoryElement("forgeChunkLoadingModCfg", "forge.configgui.ctgy.forgeChunkLoadingModConfig", ModOverridesEntry.class)); list.addAll((new ConfigElement(ForgeChunkManager.getDefaultsCategory())).getChildElements()); // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent // GuiConfig object's propertyList will also be refreshed to reflect the changes. return new GuiConfig(this.owningScreen, list, this.owningScreen.modID, "chunkLoader", this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart, this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, GuiConfig.getAbridgedConfigPath(ForgeChunkManager.getConfig().toString()), I18n.format("forge.configgui.ctgy.forgeChunkLoadingConfig")); } } *//** * This custom list entry provides the Forge Version Checking Config entry on the Minecraft Forge Configuration screen. * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. *//* public static class VersionCheckEntry extends CategoryEntry { public VersionCheckEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop); } @Override protected GuiScreen buildChildScreen() { ConfigCategory cfg = ForgeMod.getConfig().getCategory(VERSION_CHECK_CAT); Map values = new HashMap(cfg.getValues()); values.remove("Global"); Property global = ForgeMod.getConfig().get(VERSION_CHECK_CAT, "Global", true); List props = new ArrayList(); // TODO for (ModContainer mod : ForgeVersion.gatherMods().keySet()) { values.remove(mod.getModId()); props.add(ForgeMod.getConfig().get(VERSION_CHECK_CAT, mod.getModId(), true)); //Get or make the value in the config } props.addAll(values.values()); // Add any left overs from the config props.sort(Comparator.comparing(Property::getName)); List list = new ArrayList(); list.add(new ConfigElement(global)); for (Property prop : props) { list.add(new ConfigElement(prop)); } // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent // GuiConfig object's propertyList will also be refreshed to reflect the changes. return new GuiConfig(this.owningScreen, list, this.owningScreen.modID, VERSION_CHECK_CAT, true, true, GuiConfig.getAbridgedConfigPath(ForgeMod.getConfig().toString())); } } *//** * This custom list entry provides the Mod Overrides entry on the Forge Chunk Loading config screen. * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. * In this case it adds the custom entry for adding a new mod override and lists the existing mod overrides. *//* public static class ModOverridesEntry extends CategoryEntry { public ModOverridesEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop); } *//** * This method is called in the constructor and is used to set the childScreen field. *//* @Override protected GuiScreen buildChildScreen() { List list = new ArrayList(); list.add(new DummyCategoryElement("addForgeChunkLoadingModCfg", "forge.configgui.ctgy.forgeChunkLoadingAddModConfig", AddModOverrideEntry.class)); for (ConfigCategory cc : ForgeChunkManager.getModCategories()) list.add(new ConfigElement(cc)); return new GuiConfig(this.owningScreen, list, this.owningScreen.modID, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart, this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, this.owningScreen.title, I18n.format("forge.configgui.ctgy.forgeChunkLoadingModConfig")); } *//** * By overriding the enabled() method and checking the value of the "enabled" entry this entry is enabled/disabled based on the value of * the other entry. *//* @Override public boolean enabled() { for (IConfigEntry entry : this.owningEntryList.listEntries) { if (entry.getName().equals("enabled") && entry instanceof BooleanEntry) { return Boolean.valueOf(entry.getCurrentValue().toString()); } } return true; } *//** * Check to see if the child screen's entry list has changed. *//* @Override public boolean isChanged() { if (childScreen instanceof GuiConfig) { GuiConfig child = (GuiConfig) childScreen; return child.entryList.listEntries.size() != child.initEntries.size() || child.entryList.hasChangedEntry(true); } return false; } *//** * Since adding a new entry to the child screen is what constitutes a change here, reset the child * screen listEntries to the saved list. *//* @Override public void undoChanges() { if (childScreen instanceof GuiConfig) { GuiConfig child = (GuiConfig) childScreen; for (IConfigEntry ice : child.entryList.listEntries) if (!child.initEntries.contains(ice) && ForgeChunkManager.getConfig().hasCategory(ice.getName())) ForgeChunkManager.getConfig().removeCategory(ForgeChunkManager.getConfig().getCategory(ice.getName())); child.entryList.listEntries = new ArrayList(child.initEntries); } } } *//** * This custom list entry provides a button that will open to a screen that will allow a user to define a new mod override. *//* public static class AddModOverrideEntry extends CategoryEntry { public AddModOverrideEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop); } @Override protected GuiScreen buildChildScreen() { List list = new ArrayList(); list.add(new DummyConfigElement("modID", "", ConfigGuiType.STRING, "forge.configgui.modID").setCustomListEntryClass(ModIDEntry.class)); list.add(new ConfigElement(new Property("maximumTicketCount", "200", Property.Type.INTEGER, "forge.configgui.maximumTicketCount"))); list.add(new ConfigElement(new Property("maximumChunksPerTicket", "25", Property.Type.INTEGER, "forge.configgui.maximumChunksPerTicket"))); return new GuiConfig(this.owningScreen, list, this.owningScreen.modID, this.configElement.requiresWorldRestart() || this.owningScreen.allRequireWorldRestart, this.configElement.requiresMcRestart() || this.owningScreen.allRequireMcRestart, this.owningScreen.title, I18n.format("forge.configgui.ctgy.forgeChunkLoadingAddModConfig")); } @Override public boolean isChanged() { return false; } } *//** * This custom list entry provides a Mod ID selector. The control is a button that opens a list of values to select from. * This entry also overrides onGuiClosed() to run code to save the data to a new ConfigCategory when the user is done. *//* public static class ModIDEntry extends SelectValueEntry { public ModIDEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { super(owningScreen, owningEntryList, prop, getSelectableValues()); if (this.selectableValues.size() == 0) this.btnValue.enabled = false; } private static Map getSelectableValues() { Map selectableValues = new TreeMap(); // TODO for (ModContainer mod : Loader.instance().getActiveModList()) // only add mods to the list that have a non-immutable ModContainer if (!mod.isImmutable() && mod.getMod() != null) selectableValues.put(mod.getModId(), mod.getName()); return selectableValues; } *//** * By overriding onGuiClosed() for this entry we can perform additional actions when the user is done such as saving * a new ConfigCategory object to the Configuration object. *//* @Override public void onGuiClosed() { Object modObject = ModList.get().getModContainerById(getCurrentValue()); int maxTickets = 200; int maxChunks = 25; if (modObject != null) { this.owningEntryList.saveConfigElements(); for(IConfigElement ice : this.owningScreen.configElements) if ("maximumTicketCount".equals(ice.getName())) maxTickets = Integer.valueOf(ice.get().toString()); else if ("maximumChunksPerTicket".equals(ice.getName())) maxChunks = Integer.valueOf(ice.get().toString()); ForgeChunkManager.addConfigProperty(modObject, "maximumTicketCount", String.valueOf(maxTickets), Property.Type.INTEGER); ForgeChunkManager.addConfigProperty(modObject, "maximumChunksPerTicket", String.valueOf(maxChunks), Property.Type.INTEGER); if (this.owningScreen.parentScreen instanceof GuiConfig) { GuiConfig superParent = (GuiConfig) this.owningScreen.parentScreen; ConfigCategory modCtgy = ForgeChunkManager.getConfigFor(modObject); modCtgy.setPropertyOrder(ForgeChunkManager.MOD_PROP_ORDER); ConfigElement modConfig = new ConfigElement(modCtgy); boolean found = false; for (IConfigElement ice : superParent.configElements) if (ice.getName().equals(currentValue)) found = true; if (!found) superParent.configElements.add(modConfig); superParent.needsRefresh = true; superParent.initGui(); } } } } } }*/