Merge branch 'bspkrsgui'

This commit is contained in:
cpw 2014-06-25 20:16:20 -04:00
commit bcda92e941
9 changed files with 2977 additions and 246 deletions

2
fml

@ -1 +1 @@
Subproject commit 5d6dc5dce37e488188d6fc468c16e8a6183a3610
Subproject commit 7eb36a1481aea9f68fa46bc199195769b27d904b

View File

@ -0,0 +1,352 @@
/**
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.client.gui;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.ForgeModContainer;
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 cpw.mods.fml.client.IModGuiFactory;
import cpw.mods.fml.client.IModGuiFactory.RuntimeOptionCategoryElement;
import cpw.mods.fml.client.IModGuiFactory.RuntimeOptionGuiHandler;
import cpw.mods.fml.client.config.ConfigGuiType;
import cpw.mods.fml.client.config.DummyConfigElement;
import cpw.mods.fml.client.config.DummyConfigElement.DummyCategoryElement;
import cpw.mods.fml.client.config.GuiButtonExt;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.GuiConfigEntries.CategoryEntry;
import cpw.mods.fml.client.config.GuiConfigEntries.IConfigEntry;
import cpw.mods.fml.client.config.GuiConfigEntries.SelectValueEntry;
import cpw.mods.fml.client.config.GuiConfigEntries.BooleanEntry;
import cpw.mods.fml.client.config.HoverChecker;
import cpw.mods.fml.client.config.IConfigElement;
import cpw.mods.fml.client.config.GuiConfigEntries.ListEntryBase;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* 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:
* ForgeModContainer.syncConfig()
* ForgeModContainer.onConfigChanged()
* ForgeChunkManager.syncConfigDefaults()
* ForgeChunkManager.loadConfiguration()
*/
public class ForgeGuiFactory implements IModGuiFactory
{
@Override
public void initialize(Minecraft minecraftInstance) {}
@Override
public Class<? extends GuiScreen> mainConfigGuiClass() { return ForgeConfigGui.class; }
@Override
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() { return null; }
@Override
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) { return null; }
public static class ForgeConfigGui extends GuiConfig
{
public ForgeConfigGui(GuiScreen parentScreen)
{
super(parentScreen, getConfigElements(), "Forge", false, false, I18n.format("forge.configgui.forgeConfigTitle"));
}
private static List<IConfigElement> getConfigElements()
{
List<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new DummyCategoryElement("forgeCfg", "forge.configgui.ctgy.forgeGeneralConfig", GeneralEntry.class));
list.add(new DummyCategoryElement("forgeChunkLoadingCfg", "forge.configgui.ctgy.forgeChunkLoadingConfig", ChunkLoaderEntry.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(ForgeModContainer.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(ForgeModContainer.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<IConfigElement> list = new ArrayList<IConfigElement>();
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 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<IConfigElement> list = new ArrayList<IConfigElement>();
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<IConfigEntry>(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<IConfigElement> list = new ArrayList<IConfigElement>();
list.add(new DummyConfigElement("modID", "", ConfigGuiType.STRING, "forge.configgui.modID").setCustomListEntryClass(ModIDEntry.class));
list.add(new ConfigElement<Integer>(new Property("maximumTicketCount", "200", Property.Type.INTEGER, "forge.configgui.maximumTicketCount")));
list.add(new ConfigElement<Integer>(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<Object, String> getSelectableValues()
{
Map<Object, String> selectableValues = new TreeMap<Object, String>();
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 = Loader.instance().getModObjectList().get(Loader.instance().getIndexedModList().get(currentValue));
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();
}
}
}
}
}
}

View File

@ -1,7 +1,13 @@
/**
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.common;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -87,8 +93,17 @@ public class ForgeChunkManager
private static Configuration config;
private static int playerTicketLength;
private static int dormantChunkCacheSize;
public static final List<String> MOD_PROP_ORDER = new ArrayList<String>(2);
private static Set<String> warnedMods = Sets.newHashSet();
static
{
MOD_PROP_ORDER.add("maximumTicketCount");
MOD_PROP_ORDER.add("maximumChunksPerTicket");
}
/**
* All mods requiring chunkloading need to implement this to handle the
* re-registration of chunk tickets at world loading time
@ -773,6 +788,8 @@ public class ForgeChunkManager
static void loadConfiguration()
{
ticketConstraints.clear();
chunkConstraints.clear();
for (String mod : config.getCategoryNames())
{
if (mod.equals("Forge") || mod.equals("defaults"))
@ -924,51 +941,109 @@ public class ForgeChunkManager
cfgFile.renameTo(dest);
FMLLog.log(Level.ERROR, e, "A critical error occured reading the forgeChunkLoading.cfg file, defaults will be used - the invalid file is backed up at forgeChunkLoading.cfg.bak");
}
config.addCustomCategoryComment("defaults", "Default configuration for forge chunk loading control");
Property maxTicketCount = config.get("defaults", "maximumTicketCount", 200);
maxTicketCount.comment = "The default maximum ticket count for a mod which does not have an override\n" +
"in this file. This is the number of chunk loading requests a mod is allowed to make.";
defaultMaxCount = maxTicketCount.getInt(200);
syncConfigDefaults();
}
/**
* Synchronizes the local fields with the values in the Configuration object.
*/
public static void syncConfigDefaults()
{
// By adding a property order list we are defining the order that the properties will appear both in the config file and on the GUIs.
// Property order lists are defined per-ConfigCategory.
List<String> propOrder = new ArrayList<String>();
Property maxChunks = config.get("defaults", "maximumChunksPerTicket", 25);
maxChunks.comment = "The default maximum number of chunks a mod can force, per ticket, \n" +
config.setCategoryComment("defaults", "Default configuration for forge chunk loading control")
.setCategoryRequiresWorldRestart("defaults", true);
Property temp = config.get("defaults", "enabled", true);
temp.comment = "Are mod overrides enabled?";
temp.setLanguageKey("forge.configgui.enableModOverrides");
overridesEnabled = temp.getBoolean(true);
propOrder.add("enabled");
temp = config.get("defaults", "maximumChunksPerTicket", 25);
temp.comment = "The default maximum number of chunks a mod can force, per ticket, \n" +
"for a mod without an override. This is the maximum number of chunks a single ticket can force.";
defaultMaxChunks = maxChunks.getInt(25);
temp.setLanguageKey("forge.configgui.maximumChunksPerTicket");
temp.setMinValue(0);
defaultMaxChunks = temp.getInt(25);
propOrder.add("maximumChunksPerTicket");
temp = config.get("defaults", "maximumTicketCount", 200);
temp.comment = "The default maximum ticket count for a mod which does not have an override\n" +
"in this file. This is the number of chunk loading requests a mod is allowed to make.";
temp.setLanguageKey("forge.configgui.maximumTicketCount");
temp.setMinValue(0);
defaultMaxCount = temp.getInt(200);
propOrder.add("maximumTicketCount");
Property playerTicketCount = config.get("defaults", "playerTicketCount", 500);
playerTicketCount.comment = "The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it.";
playerTicketLength = playerTicketCount.getInt(500);
temp = config.get("defaults", "playerTicketCount", 500);
temp.comment = "The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it.";
temp.setLanguageKey("forge.configgui.playerTicketCount");
temp.setMinValue(0);
playerTicketLength = temp.getInt(500);
propOrder.add("playerTicketCount");
Property dormantChunkCacheSizeProperty = config.get("defaults", "dormantChunkCacheSize", 0);
dormantChunkCacheSizeProperty.comment = "Unloaded chunks can first be kept in a dormant cache for quicker\n" +
temp = config.get("defaults", "dormantChunkCacheSize", 0);
temp.comment = "Unloaded chunks can first be kept in a dormant cache for quicker\n" +
"loading times. Specify the size (in chunks) of that cache here";
dormantChunkCacheSize = dormantChunkCacheSizeProperty.getInt(0);
FMLLog.info("Configured a dormant chunk cache size of %d", dormantChunkCacheSizeProperty.getInt(0));
Property modOverridesEnabled = config.get("defaults", "enabled", true);
modOverridesEnabled.comment = "Are mod overrides enabled?";
overridesEnabled = modOverridesEnabled.getBoolean(true);
temp.setLanguageKey("forge.configgui.dormantChunkCacheSize");
temp.setMinValue(0);
dormantChunkCacheSize = temp.getInt(0);
propOrder.add("dormantChunkCacheSize");
FMLLog.info("Configured a dormant chunk cache size of %d", temp.getInt(0));
config.setCategoryPropertyOrder("defaults", propOrder);
config.addCustomCategoryComment("Forge", "Sample mod specific control section.\n" +
"Copy this section and rename the with the modid for the mod you wish to override.\n" +
"A value of zero in either entry effectively disables any chunkloading capabilities\n" +
"for that mod");
Property sampleTC = config.get("Forge", "maximumTicketCount", 200);
sampleTC.comment = "Maximum ticket count for the mod. Zero disables chunkloading capabilities.";
sampleTC = config.get("Forge", "maximumChunksPerTicket", 25);
sampleTC.comment = "Maximum chunks per ticket for the mod.";
temp = config.get("Forge", "maximumTicketCount", 200);
temp.comment = "Maximum ticket count for the mod. Zero disables chunkloading capabilities.";
temp = config.get("Forge", "maximumChunksPerTicket", 25);
temp.comment = "Maximum chunks per ticket for the mod.";
for (String mod : config.getCategoryNames())
{
if (mod.equals("Forge") || mod.equals("defaults"))
{
continue;
}
config.get(mod, "maximumTicketCount", 200);
config.get(mod, "maximumChunksPerTicket", 25);
config.get(mod, "maximumTicketCount", 200).setLanguageKey("forge.configgui.maximumTicketCount").setMinValue(0);
config.get(mod, "maximumChunksPerTicket", 25).setLanguageKey("forge.configgui.maximumChunksPerTicket").setMinValue(0);
}
if (config.hasChanged())
{
config.save();
}
}
public static Configuration getConfig()
{
return config;
}
public static ConfigCategory getDefaultsCategory()
{
return config.getCategory("defaults");
}
public static List<ConfigCategory> getModCategories()
{
List<ConfigCategory> list = new ArrayList<ConfigCategory>();
for (String mod : config.getCategoryNames())
{
if (mod.equals("Forge") || mod.equals("defaults"))
{
continue;
}
list.add(config.getCategory(mod));
}
return list;
}
public static ConfigCategory getConfigFor(Object mod)
{
@ -987,7 +1062,12 @@ public class ForgeChunkManager
if (container != null)
{
ConfigCategory cat = config.getCategory(container.getModId());
cat.put(propertyName, new Property(propertyName, value, type));
Property prop = new Property(propertyName, value, type).setLanguageKey("forge.configgui." + propertyName);
if (type == Property.Type.INTEGER)
{
prop.setMinValue(0);
}
cat.put(propertyName, prop);
}
}
}

View File

@ -1,3 +1,8 @@
/**
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.common;
import static net.minecraftforge.common.ForgeVersion.buildVersion;
@ -7,6 +12,7 @@ import static net.minecraftforge.common.ForgeVersion.revisionVersion;
import static net.minecraftforge.common.config.Configuration.CATEGORY_GENERAL;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -30,7 +36,9 @@ import com.google.common.eventbus.Subscribe;
import cpw.mods.fml.client.FMLFileResourcePack;
import cpw.mods.fml.client.FMLFolderResourcePack;
import cpw.mods.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.LoadController;
import cpw.mods.fml.common.Loader;
@ -42,6 +50,7 @@ import cpw.mods.fml.common.event.FMLModIdMappingEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
public class ForgeModContainer extends DummyModContainer implements WorldAccessContainer
@ -57,6 +66,8 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static float zombieBabyChance = 0.05f;
public static boolean shouldSortRecipies = true;
public static boolean disableVersionCheck = false;
private static Configuration config;
public ForgeModContainer()
{
@ -75,44 +86,91 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
meta.screenshots = new String[0];
meta.logoFile = "/forge_logo.png";
Configuration config = null;
config = null;
File cfgFile = new File(Loader.instance().getConfigDir(), "forge.cfg");
try
{
config = new Configuration(cfgFile);
}
catch (Exception e)
{
System.out.println("Error loading forge.cfg, deleting file and resetting: ");
e.printStackTrace();
config = new Configuration(cfgFile);
syncConfig(true);
}
if (cfgFile.exists())
cfgFile.delete();
@Override
public String getGuiClassName()
{
return "net.minecraftforge.client.gui.ForgeGuiFactory";
}
public static Configuration getConfig()
{
return config;
}
config = new Configuration(cfgFile);
}
/**
* Synchronizes the local fields with the values in the Configuration object.
*/
private static void syncConfig(boolean load)
{
// By adding a property order list we are defining the order that the properties will appear both in the config file and on the GUIs.
// Property order lists are defined per-ConfigCategory.
List<String> propOrder = new ArrayList<String>();
if (!config.isChild)
{
config.load();
Property enableGlobalCfg = config.get(Configuration.CATEGORY_GENERAL, "enableGlobalConfig", false);
if (load)
{
config.load();
}
Property enableGlobalCfg = config.get(Configuration.CATEGORY_GENERAL, "enableGlobalConfig", false).setShowInGui(false);
if (enableGlobalCfg.getBoolean(false))
{
Configuration.enableGlobalConfig();
}
}
Property prop;
prop = config.get(Configuration.CATEGORY_GENERAL, "clumpingThreshold", 64);
prop.comment = "Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024";
prop = config.get(CATEGORY_GENERAL, "disableVersionCheck", false);
prop.comment = "Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.";
// Language keys are a good idea to implement if you are using config GUIs. This allows you to use a .lang file that will hold the
// "pretty" version of the property name as well as allow others to provide their own localizations.
// This language key is also used to get the tooltip for a property. The tooltip language key is langKey + ".tooltip".
// If no tooltip language key is defined in your .lang file, the tooltip will default to the property comment field.
prop.setLanguageKey("forge.configgui.disableVersionCheck");
disableVersionCheck = prop.getBoolean(disableVersionCheck);
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "clumpingThreshold", 64,
"Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024", 64, 1024);
prop.setLanguageKey("forge.configgui.clumpingThreshold").setRequiresWorldRestart(true);
clumpingThreshold = prop.getInt(64);
if (clumpingThreshold > 1024 || clumpingThreshold < 64)
{
clumpingThreshold = 64;
prop.set(64);
}
propOrder.add(prop.getName());
prop = config.get(CATEGORY_GENERAL, "sortRecipies", true);
prop.comment = "Set to true to enable the post initialization sorting of crafting recipes using Forge's sorter. May cause desyncing on conflicting recipies. MUST RESTART MINECRAFT IF CHANGED FROM THE CONFIG GUI.";
prop.setLanguageKey("forge.configgui.sortRecipies").setRequiresMcRestart(true);
shouldSortRecipies = prop.getBoolean(shouldSortRecipies);
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "forceDuplicateFluidBlockCrash", true);
prop.comment = "Set this to true to force a crash if more than one block attempts to link back to the same Fluid. Enabled by default.";
prop.setLanguageKey("forge.configgui.forceDuplicateFluidBlockCrash").setRequiresMcRestart(true);
forceDuplicateFluidBlockCrash = prop.getBoolean(true);
propOrder.add(prop.getName());
if (!forceDuplicateFluidBlockCrash)
{
FMLLog.warning("Disabling forced crashes on duplicate Fluid Blocks - USE AT YOUR OWN RISK");
}
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringEntities", false);
prop.comment = "Set this to just remove any TileEntity that throws a error in there update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
prop.comment = "Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
prop.setLanguageKey("forge.configgui.removeErroringEntities").setRequiresWorldRestart(true);
removeErroringEntities = prop.getBoolean(false);
propOrder.add(prop.getName());
if (removeErroringEntities)
{
@ -120,8 +178,10 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
}
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringTileEntities", false);
prop.comment = "Set this to just remove any TileEntity that throws a error in there update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
prop.comment = "Set this to true to remove any TileEntity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
prop.setLanguageKey("forge.configgui.removeErroringTileEntities").setRequiresWorldRestart(true);
removeErroringTileEntities = prop.getBoolean(false);
propOrder.add(prop.getName());
if (removeErroringTileEntities)
{
@ -133,43 +193,57 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
//disableStitchedFileSaving = prop.getBoolean(true);
prop = config.get(Configuration.CATEGORY_GENERAL, "fullBoundingBoxLadders", false);
prop.comment = "Set this to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticable differences in mechanics so default is vanilla behavior. Default: false";
prop.comment = "Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticable differences in mechanics so default is vanilla behavior. Default: false";
prop.setLanguageKey("forge.configgui.fullBoundingBoxLadders").setRequiresWorldRestart(true);
fullBoundingBoxLadders = prop.getBoolean(false);
prop = config.get(Configuration.CATEGORY_GENERAL, "forceDuplicateFluidBlockCrash", true);
prop.comment = "Set this to force a crash if more than one block attempts to link back to the same Fluid. Enabled by default.";
forceDuplicateFluidBlockCrash = prop.getBoolean(true);
if (!forceDuplicateFluidBlockCrash)
{
FMLLog.warning("Disabling forced crashes on duplicate Fluid Blocks - USE AT YOUR OWN RISK");
}
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "biomeSkyBlendRange", new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 });
prop.comment = "Control the range of sky blending for colored skies in biomes.";
prop.setLanguageKey("forge.configgui.biomeSkyBlendRange");
blendRanges = prop.getIntList();
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBaseSummonChance", 0.1);
prop.comment = "Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.";
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBaseSummonChance", 0.1,
"Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.", 0.0D, 1.0D);
prop.setLanguageKey("forge.configgui.zombieBaseSummonChance").setRequiresWorldRestart(true);
zombieSummonBaseChance = prop.getDouble(0.1);
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBabyChance", 0.05);
prop.comment = "Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.";
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBabyChance", 0.05,
"Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.", 0.0D, 1.0D);
prop.setLanguageKey("forge.configgui.zombieBabyChance").setRequiresWorldRestart(true);
zombieBabyChance = (float) prop.getDouble(0.05);
prop = config.get(CATEGORY_GENERAL, "sortRecipies", shouldSortRecipies);
prop.comment = "Set to true to enable the post initlization sorting of crafting recipes using Froge's sorter. May cause desyncing on conflicting recipies. ToDo: Set to true by default in 1.7";
shouldSortRecipies = prop.getBoolean(shouldSortRecipies);
prop = config.get(CATEGORY_GENERAL, "disableVersionCheck", disableVersionCheck);
prop.comment = "Set to true to disable Forge's version check mechanics, Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.";
disableVersionCheck = prop.getBoolean(disableVersionCheck);
propOrder.add(prop.getName());
config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder);
if (config.hasChanged())
{
config.save();
}
}
/**
* By subscribing to the OnConfigChangedEvent we are able to execute code when our config screens are closed.
* This implementation uses the optional configID string to handle multiple Configurations using one event handler.
*/
@SubscribeEvent
public void onConfigChanged(OnConfigChangedEvent event)
{
if (getMetadata().modId.equals(event.modID) && !event.isWorldRunning)
{
if (Configuration.CATEGORY_GENERAL.equals(event.configID))
{
syncConfig(false);
}
else if ("chunkLoader".equals(event.configID))
{
ForgeChunkManager.syncConfigDefaults();
ForgeChunkManager.loadConfiguration();
}
}
}
@Override
public boolean registerBus(EventBus bus, LoadController controller)
@ -190,6 +264,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
{
MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER);
ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory());
FMLCommonHandler.instance().bus().register(this);
}
@Subscribe
@ -200,7 +275,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
}
@Subscribe
public void onAvalible(FMLLoadCompleteEvent evt)
public void onAvailable(FMLLoadCompleteEvent evt)
{
if (shouldSortRecipies)
{

View File

@ -1,5 +1,11 @@
/**
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.common.config;
import static net.minecraftforge.common.config.Configuration.COMMENT_SEPARATOR;
import static net.minecraftforge.common.config.Configuration.NEW_LINE;
import static net.minecraftforge.common.config.Configuration.allowedProperties;
@ -7,22 +13,34 @@ import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.base.Splitter;
import cpw.mods.fml.client.config.GuiConfigEntries.IConfigEntry;
public class ConfigCategory implements Map<String, Property>
{
private String name;
private String comment;
private String languagekey;
private ArrayList<ConfigCategory> children = new ArrayList<ConfigCategory>();
private Map<String, Property> properties = new TreeMap<String, Property>();
private int propNumber = 0;
public final ConfigCategory parent;
private boolean changed = false;
private boolean requiresWorldRestart = false;
private boolean showInGui = true;
private boolean requiresMcRestart = false;
private Class<? extends IConfigEntry> customEntryClass = null;
private List<String> propertyOrder = null;
public ConfigCategory(String name)
{
@ -51,6 +69,11 @@ public class ConfigCategory implements Map<String, Property>
return false;
}
public String getName()
{
return name;
}
public String getQualifiedName()
{
return getQualifiedName(name, parent);
@ -76,11 +99,134 @@ public class ConfigCategory implements Map<String, Property>
return ImmutableMap.copyOf(properties);
}
public void setComment(String comment)
public List<Property> getOrderedValues()
{
if (this.propertyOrder != null)
{
ArrayList<Property> set = new ArrayList<Property>();
for (String key : this.propertyOrder)
if (properties.containsKey(key))
set.add(properties.get(key));
return ImmutableList.copyOf(set);
}
else
return ImmutableList.copyOf(properties.values());
}
public ConfigCategory setConfigEntryClass(Class<? extends IConfigEntry> clazz)
{
this.customEntryClass = clazz;
return this;
}
public Class<? extends IConfigEntry> getConfigEntryClass()
{
return this.customEntryClass;
}
public ConfigCategory setLanguageKey(String languagekey)
{
this.languagekey = languagekey;
return this;
}
public String getLanguagekey()
{
if (this.languagekey != null)
return this.languagekey;
else
return getQualifiedName();
}
public ConfigCategory setComment(String comment)
{
this.comment = comment;
return this;
}
public String getComment()
{
return this.comment;
}
/**
* Sets the flag for whether or not this category can be edited while a world is running. Care should be taken to ensure
* that only properties that are truly dynamic can be changed from the in-game options menu. Only set this flag to
* true if all child properties/categories are unable to be modified while a world is running.
*/
public ConfigCategory setRequiresWorldRestart(boolean requiresWorldRestart)
{
this.requiresWorldRestart = requiresWorldRestart;
return this;
}
/**
* Returns whether or not this category is able to be edited while a world is running using the in-game Mod Options screen
* as well as the Mods list screen, or only from the Mods list screen.
*/
public boolean requiresWorldRestart()
{
return this.requiresWorldRestart;
}
/**
* Sets whether or not this ConfigCategory should be allowed to show on config GUIs.
* Defaults to true.
*/
public ConfigCategory setShowInGui(boolean showInGui)
{
this.showInGui = showInGui;
return this;
}
/**
* Gets whether or not this ConfigCategory should be allowed to show on config GUIs.
* Defaults to true unless set to false.
*/
public boolean showInGui()
{
return showInGui;
}
/**
* Sets whether or not this ConfigCategory requires Minecraft to be restarted when changed.
* Defaults to false. Only set this flag to true if ALL child properties/categories require
* Minecraft to be restarted when changed. Setting this flag will also prevent modification
* of the child properties/categories while a world is running.
*/
public ConfigCategory setRequiresMcRestart(boolean requiresMcRestart)
{
this.requiresMcRestart = this.requiresWorldRestart = requiresMcRestart;
return this;
}
/**
* Gets whether or not this ConfigCategory requires Minecraft to be restarted when changed.
* Defaults to false unless set to true.
*/
public boolean requiresMcRestart()
{
return this.requiresMcRestart;
}
public ConfigCategory setPropertyOrder(List<String> propertyOrder)
{
this.propertyOrder = propertyOrder;
for (String s : properties.keySet())
if (!propertyOrder.contains(s))
propertyOrder.add(s);
return this;
}
public List<String> getPropertyOrder()
{
if (this.propertyOrder != null)
return ImmutableList.copyOf(this.propertyOrder);
else
return ImmutableList.copyOf(properties.keySet());
}
public boolean containsKey(String key)
{
return properties.containsKey(key);
@ -111,22 +257,21 @@ public class ConfigCategory implements Map<String, Property>
String pad1 = getIndent(indent + 1);
String pad2 = getIndent(indent + 2);
write(out, pad0, "####################");
write(out, pad0, "# ", name);
if (comment != null)
if (comment != null && !comment.isEmpty())
{
write(out, pad0, "#===================");
write(out, pad0, COMMENT_SEPARATOR);
write(out, pad0, "# ", name);
write(out, pad0, "#--------------------------------------------------------------------------------------------------------#");
Splitter splitter = Splitter.onPattern("\r?\n");
for (String line : splitter.split(comment))
{
write(out, pad0, "# ", line);
}
write(out, pad0, COMMENT_SEPARATOR, NEW_LINE);
}
write(out, pad0, "####################", NEW_LINE);
if (!allowedProperties.matchesAllOf(name))
{
name = '"' + name + '"';
@ -134,13 +279,13 @@ public class ConfigCategory implements Map<String, Property>
write(out, pad0, name, " {");
Property[] props = properties.values().toArray(new Property[properties.size()]);
Property[] props = getOrderedValues().toArray(new Property[] {});
for (int x = 0; x < props.length; x++)
{
Property prop = props[x];
if (prop.comment != null)
if (prop.comment != null && !prop.comment.isEmpty())
{
if (x != 0)
{
@ -185,6 +330,9 @@ public class ConfigCategory implements Map<String, Property>
}
}
if (children.size() > 0)
out.newLine();
for (ConfigCategory child : children)
{
child.write(out, indent + 1);
@ -232,6 +380,8 @@ public class ConfigCategory implements Map<String, Property>
@Override public Property put(String key, Property value)
{
changed = true;
if (this.propertyOrder != null && !this.propertyOrder.contains(key))
this.propertyOrder.add(key);
return properties.put(key, value);
}
@Override public Property remove(Object key)
@ -242,6 +392,10 @@ public class ConfigCategory implements Map<String, Property>
@Override public void putAll(Map<? extends String, ? extends Property> m)
{
changed = true;
if (this.propertyOrder != null)
for (String key : m.keySet())
if (!this.propertyOrder.contains(key))
this.propertyOrder.add(key);
properties.putAll(m);
}
@Override public void clear()
@ -260,12 +414,13 @@ public class ConfigCategory implements Map<String, Property>
public Set<ConfigCategory> getChildren(){ return ImmutableSet.copyOf(children); }
public void removeChild(ConfigCategory child)
public ConfigCategory removeChild(ConfigCategory child)
{
if (children.contains(child))
{
children.remove(child);
changed = true;
}
return this;
}
}

View File

@ -0,0 +1,359 @@
/**
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.common.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import cpw.mods.fml.client.config.ConfigGuiType;
import cpw.mods.fml.client.config.GuiConfigEntries.IConfigEntry;
import cpw.mods.fml.client.config.GuiEditArrayEntries.IArrayEntry;
import cpw.mods.fml.client.config.IConfigElement;
/**
* This class bridges the gap between the FML config GUI classes and the Forge Configuration classes.
*/
public class ConfigElement<T> implements IConfigElement<T>
{
private Property prop;
private Property.Type type;
private boolean isProperty;
private ConfigCategory ctgy;
private boolean categoriesFirst = true;
public ConfigElement(ConfigCategory ctgy)
{
this.ctgy = ctgy;
isProperty = false;
}
public ConfigElement(Property prop)
{
this.prop = prop;
this.type = prop.getType();
this.isProperty = true;
}
public ConfigElement<T> listCategoriesFirst(boolean categoriesFirst)
{
this.categoriesFirst = categoriesFirst;
return this;
}
@Override
public List<IConfigElement> getChildElements()
{
if (!isProperty)
{
List<IConfigElement> elements = new ArrayList<IConfigElement>();
Iterator<ConfigCategory> ccI = ctgy.getChildren().iterator();
Iterator<Property> pI = ctgy.getOrderedValues().iterator();
int index = 0;
if (categoriesFirst)
while (ccI.hasNext())
{
ConfigElement temp = new ConfigElement(ccI.next());
if (temp.showInGui()) // don't bother adding elements that shouldn't show
elements.add(temp);
}
while (pI.hasNext())
{
ConfigElement<?> temp = getTypedElement(pI.next());
if (temp.showInGui())
elements.add(temp);
}
if (!categoriesFirst)
while (ccI.hasNext())
{
ConfigElement temp = new ConfigElement(ccI.next());
if (temp.showInGui())
elements.add(temp);
}
return elements;
}
return null;
}
public static ConfigElement<?> getTypedElement(Property prop)
{
switch (getType(prop))
{
case BOOLEAN:
return new ConfigElement<Boolean>(prop);
case DOUBLE:
return new ConfigElement<Double>(prop);
case INTEGER:
return new ConfigElement<Integer>(prop);
default:
return new ConfigElement<String>(prop);
}
}
@Override
public String getName()
{
return isProperty ? prop.getName() : ctgy.getName();
}
@Override
public boolean isProperty()
{
return isProperty;
}
@Override
public Class<? extends IConfigEntry> getConfigEntryClass()
{
return isProperty ? prop.getConfigEntryClass() : ctgy.getConfigEntryClass();
}
@Override
public Class<? extends IArrayEntry> getArrayEntryClass()
{
return isProperty ? prop.getArrayEntryClass() : null;
}
@Override
public String getQualifiedName()
{
return isProperty ? prop.getName() : ctgy.getQualifiedName();
}
@Override
public ConfigGuiType getType()
{
return isProperty ? getType(this.prop) : ConfigGuiType.CONFIG_CATEGORY;
}
public static ConfigGuiType getType(Property prop)
{
return prop.getType() == Property.Type.BOOLEAN ? ConfigGuiType.BOOLEAN : prop.getType() == Property.Type.DOUBLE ? ConfigGuiType.DOUBLE :
prop.getType() == Property.Type.INTEGER ? ConfigGuiType.INTEGER : prop.getType() == Property.Type.COLOR ? ConfigGuiType.COLOR :
prop.getType() == Property.Type.MOD_ID ? ConfigGuiType.MOD_ID : ConfigGuiType.STRING;
}
@Override
public boolean isList()
{
return isProperty && prop.isList();
}
@Override
public boolean isListLengthFixed()
{
return isProperty && prop.isListLengthFixed();
}
@Override
public int getMaxListLength()
{
return isProperty ? prop.getMaxListLength() : -1;
}
@Override
public String getComment()
{
return isProperty ? prop.comment : ctgy.getComment();
}
@Override
public boolean isDefault()
{
return !isProperty || prop.isDefault();
}
@Override
public void setToDefault()
{
if (isProperty)
prop.setToDefault();
}
@Override
public boolean requiresWorldRestart()
{
return isProperty ? prop.requiresWorldRestart() : ctgy.requiresWorldRestart();
}
@Override
public boolean showInGui()
{
return isProperty ? prop.showInGui() : ctgy.showInGui();
}
@Override
public boolean requiresMcRestart()
{
return isProperty ? prop.requiresMcRestart() : ctgy.requiresMcRestart();
}
@Override
public String[] getValidValues()
{
return isProperty ? prop.getValidValues() : null;
}
@Override
public String getLanguageKey()
{
return isProperty ? prop.getLanguageKey() : ctgy.getLanguagekey();
}
@Override
public Object getDefault()
{
return isProperty ? (T) prop.getDefault() : null;
}
@Override
public Object[] getDefaults()
{
if (isProperty)
{
String[] aVal = prop.getDefaults();
if (type == Property.Type.BOOLEAN)
{
Boolean[] ba = new Boolean[aVal.length];
for(int i = 0; i < aVal.length; i++)
ba[i] = Boolean.valueOf(aVal[i]);
return ba;
}
else if (type == Property.Type.DOUBLE)
{
Double[] da = new Double[aVal.length];
for(int i = 0; i < aVal.length; i++)
da[i] = Double.valueOf(aVal[i].toString());
return da;
}
else if (type == Property.Type.INTEGER)
{
Integer[] ia = new Integer[aVal.length];
for(int i = 0; i < aVal.length; i++)
ia[i] = Integer.valueOf(aVal[i].toString());
return ia;
}
else
return aVal;
}
return null;
}
@Override
public Pattern getValidationPattern()
{
return isProperty ? prop.getValidationPattern() : null;
}
@Override
public Object get()
{
return isProperty ? (T) prop.getString() : null;
}
@Override
public Object[] getList()
{
if (isProperty)
{
String[] aVal = prop.getStringList();
if (type == Property.Type.BOOLEAN)
{
Boolean[] ba = new Boolean[aVal.length];
for(int i = 0; i < aVal.length; i++)
ba[i] = Boolean.valueOf(aVal[i]);
return ba;
}
else if (type == Property.Type.DOUBLE)
{
Double[] da = new Double[aVal.length];
for(int i = 0; i < aVal.length; i++)
da[i] = Double.valueOf(aVal[i].toString());
return da;
}
else if (type == Property.Type.INTEGER)
{
Integer[] ia = new Integer[aVal.length];
for(int i = 0; i < aVal.length; i++)
ia[i] = Integer.valueOf(aVal[i].toString());
return ia;
}
else
return aVal;
}
return null;
}
@Override
public void set(T value)
{
if (isProperty)
{
if (type == Property.Type.BOOLEAN)
prop.set(Boolean.parseBoolean(value.toString()));
else if (type == Property.Type.DOUBLE)
prop.set(Double.parseDouble(value.toString()));
else if (type == Property.Type.INTEGER)
prop.set(Integer.parseInt(value.toString()));
else
prop.set(value.toString());
}
}
@Override
public void set(T[] aVal)
{
if (isProperty)
{
if (type == Property.Type.BOOLEAN)
{
boolean[] ba = new boolean[aVal.length];
for(int i = 0; i < aVal.length; i++)
ba[i] = Boolean.valueOf(aVal[i].toString());
prop.set(ba);
}
else if (type == Property.Type.DOUBLE)
{
double[] da = new double[aVal.length];
for(int i = 0; i < aVal.length; i++)
da[i] = Double.valueOf(aVal[i].toString());
prop.set(da);
}
else if (type == Property.Type.INTEGER)
{
int[] ia = new int[aVal.length];
for(int i = 0; i < aVal.length; i++)
ia[i] = Integer.valueOf(aVal[i].toString());
prop.set(ia);
}
else
{
String[] is = new String[aVal.length];
for(int i = 0; i < aVal.length; i++)
is[i] = aVal[i].toString();
prop.set(is);
}
}
}
@Override
public T getMinValue()
{
return isProperty ? (T) prop.getMinValue() : null;
}
@Override
public T getMaxValue()
{
return isProperty ? (T) prop.getMaxValue() : null;
}
}

View File

@ -6,6 +6,11 @@
package net.minecraftforge.common.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Pattern;
import cpw.mods.fml.client.config.GuiConfigEntries.IConfigEntry;
import cpw.mods.fml.client.config.GuiEditArrayEntries.IArrayEntry;
public class Property
{
@ -14,17 +19,17 @@ public class Property
STRING,
INTEGER,
BOOLEAN,
DOUBLE;
private static Type[] values = {STRING, INTEGER, BOOLEAN, DOUBLE};
DOUBLE,
COLOR,
MOD_ID;
public static Type tryParse(char id)
{
for (int x = 0; x < values.length; x++)
for (int x = 0; x < values().length; x++)
{
if (values[x].getID() == id)
if (values()[x].getID() == id)
{
return values[x];
return values()[x];
}
}
@ -39,33 +44,76 @@ public class Property
private String name;
private String value;
private String defaultValue;
public String comment;
private String[] values;
private String[] defaultValues;
private String[] validValues;
private String langKey;
private String minValue;
private String maxValue;
private Class<? extends IConfigEntry> configEntryClass = null;
private Class<? extends IArrayEntry> arrayEntryClass = null;
private boolean requiresWorldRestart = false;
private boolean showInGui = true;
private boolean requiresMcRestart = false;
private Pattern validationPattern;
private final boolean wasRead;
private final boolean isList;
private boolean isListLengthFixed = false;
private int maxListLength = -1;
private final Type type;
private boolean changed = false;
public Property()
{
wasRead = false;
type = null;
isList = false;
}
public Property(String name, String value, Type type)
{
this(name, value, type, false);
this(name, value, type, false, new String[0], name);
}
Property(String name, String value, Type type, boolean read)
public Property(String name, String value, Type type, boolean read)
{
this(name, value, type, read, new String[0], name);
}
public Property(String name, String value, Type type, String[] validValues)
{
this(name, value, type, false, validValues, name);
}
public Property(String name, String value, Type type, String langKey)
{
this(name, value, type, false, new String[0], langKey);
}
public Property(String name, String value, Type type, boolean read, String langKey)
{
this(name, value, type, read, new String[0], langKey);
}
public Property(String name, String value, Type type, String[] validValues, String langKey)
{
this(name, value, type, false, validValues, langKey);
}
Property(String name, String value, Type type, boolean read, String[] validValues, String langKey)
{
setName(name);
this.value = value;
this.values = new String[0];
this.type = type;
wasRead = read;
isList = false;
this.defaultValue = value;
this.defaultValues = new String[0];
this.validValues = validValues;
this.isListLengthFixed = false;
this.maxListLength = -1;
this.minValue = String.valueOf(Integer.MIN_VALUE);
this.maxValue = String.valueOf(Integer.MAX_VALUE);
this.langKey = langKey;
this.comment = "";
}
public Property(String name, String[] values, Type type)
@ -74,14 +122,514 @@ public class Property
}
Property(String name, String[] values, Type type, boolean read)
{
this(name, values, type, read, new String[0], name);
}
public Property(String name, String[] values, Type type, String langKey)
{
this(name, values, type, false, langKey);
}
Property(String name, String[] values, Type type, boolean read, String langKey)
{
this(name, values, type, read, new String[0], langKey);
}
Property(String name, String[] values, Type type, boolean read, String[] validValues, String langKey)
{
setName(name);
this.type = type;
this.values = values;
this.values = Arrays.copyOf(values, values.length);
wasRead = read;
isList = true;
this.value = "";
this.defaultValue = "";
for (String s : values)
this.defaultValue += ", [" + s + "]";
this.defaultValue = this.defaultValue.replaceFirst(", ", "");
this.defaultValues = Arrays.copyOf(values, values.length);
this.validValues = validValues;
this.isListLengthFixed = false;
this.maxListLength = -1;
this.minValue = String.valueOf(Integer.MIN_VALUE);
this.maxValue = String.valueOf(Integer.MAX_VALUE);
this.langKey = langKey;
this.comment = "";
}
/**
* Returns whether or not this Property is defaulted.
*
* @return true if the current value(s) is(are) deeply equal to the default value(s)
*/
public boolean isDefault()
{
if (this.isBooleanList())
{
if (values.length == defaultValues.length)
{
for (int i = 0; i < values.length; i++)
if (Boolean.parseBoolean(values[i]) != Boolean.parseBoolean(defaultValues[i]))
return false;
return true;
}
else
return false;
}
if (this.isIntList())
{
if (values.length == defaultValues.length)
{
for (int i = 0; i < values.length; i++)
if (Integer.parseInt(values[i]) != Integer.parseInt(defaultValues[i]))
return false;
return true;
}
else
return false;
}
if (this.isDoubleList())
{
if (values.length == defaultValues.length)
{
for (int i = 0; i < values.length; i++)
if (Double.parseDouble(values[i]) != Double.parseDouble(defaultValues[i]))
return false;
return true;
}
else
return false;
}
if (this.isList())
{
if (values.length == defaultValues.length)
{
for (int i = 0; i < values.length; i++)
if (!values[i].equals(defaultValues[i]))
return false;
return true;
}
else
return false;
}
if (this.type == Type.BOOLEAN && this.isBooleanValue())
return Boolean.parseBoolean(value) == Boolean.parseBoolean(defaultValue);
if (this.type == Type.INTEGER && this.isIntValue())
return Integer.parseInt(value) == Integer.parseInt(defaultValue);
if (this.type == Type.DOUBLE && this.isDoubleValue())
return Double.parseDouble(value) == Double.parseDouble(defaultValue);
return value.equals(defaultValue);
}
/**
* Sets the current value(s) of this Property to the default value(s).
*/
public Property setToDefault()
{
this.value = this.defaultValue;
this.values = Arrays.copyOf(this.defaultValues, this.defaultValues.length);
return this;
}
/**
* Gets the raw String default value of this Property. Check for isList() == false first.
*
* @return the default value String
*/
public String getDefault()
{
return defaultValue;
}
/**
* Gets the raw String[] default values of this Property. Check for isList() == true first.
*
* @return the default values String[]
*/
public String[] getDefaults()
{
return Arrays.copyOf(this.defaultValues, this.defaultValues.length);
}
/**
* Sets the flag for whether or not this Property can be edited while a world is running. Care should be taken to ensure
* that only properties that are truly dynamic can be changed from the in-game options menu. When set to false the Property will be
* editable from both the main menu Mods list config screen and the in-game Mod Options config screen. When set to true the Property
* will only be editable from the main menu Mods list config screen.
*/
public Property setRequiresWorldRestart(boolean requiresWorldRestart)
{
this.requiresWorldRestart = requiresWorldRestart;
return this;
}
/**
* Returns whether or not this Property is able to be edited while a world is running using the in-game Mod Options screen
* as well as the Mods list screen, or only from the Mods list screen. Setting this flag to true will disable editing of
* this property while a world is running.
*/
public boolean requiresWorldRestart()
{
return this.requiresWorldRestart;
}
/**
* Sets whether or not this Property should be allowed to show on config GUIs.
* Defaults to true.
*/
public Property setShowInGui(boolean showInGui)
{
this.showInGui = showInGui;
return this;
}
/**
* Gets whether or not this Property should be allowed to show on config GUIs.
* Defaults to true unless set to false.
*/
public boolean showInGui()
{
return showInGui;
}
/**
* Sets whether or not this Property requires Minecraft to be restarted when changed.
* Defaults to false. Setting this flag to true will also disable editing of
* this property while a world is running.
*/
public Property setRequiresMcRestart(boolean requiresMcRestart)
{
this.requiresMcRestart = this.requiresWorldRestart = requiresMcRestart;
return this;
}
/**
* Gets whether or not this Property requires Minecraft to be restarted when changed.
* Defaults to false unless set to true.
*/
public boolean requiresMcRestart()
{
return this.requiresMcRestart;
}
/**
* Sets the maximum length of this list/array Property. Only important if isList() == true. If the current values array or default
* values array is longer than the new maximum it will be resized. If calling both this method and setIsListLengthFixed(true), this
* method should be called afterwards (but is not required).
*/
public Property setMaxListLength(int max)
{
this.maxListLength = max;
if (this.maxListLength != -1)
{
if (values != null && values.length != maxListLength)
if (this.isListLengthFixed || values.length > maxListLength)
values = Arrays.copyOf(values, maxListLength);
if (defaultValues != null && defaultValues.length != maxListLength)
if (this.isListLengthFixed || defaultValues.length > maxListLength)
defaultValues = Arrays.copyOf(defaultValues, maxListLength);
}
return this;
}
/**
* Gets the maximum length of this list/array Property. Only important if isList() == true.
*/
public int getMaxListLength()
{
return this.maxListLength;
}
/**
* Sets the flag for whether this list/array Property has a fixed length. Only important if isList() == true. If calling both this
* method and setMaxListLength(), this method should be called first (but is not required).
*/
public Property setIsListLengthFixed(boolean isListLengthFixed)
{
this.isListLengthFixed = isListLengthFixed;
return this;
}
/**
* Returns whether or not this list/array has a fixed length. Only important if isList() == true.
*/
public boolean isListLengthFixed()
{
return this.isListLengthFixed;
}
/**
* Sets a custom IConfigEntry class that should be used in place of the standard entry class for this Property type. This class
* MUST provide a constructor with the following parameter types: {@code GuiConfig} (the owning GuiConfig screen will be provided),
* {@code GuiConfigEntries} (the owning GuiConfigEntries will be provided), {@code IConfigElement} (the IConfigElement for this Property
* will be provided).
*/
public Property setConfigEntryClass(Class<? extends IConfigEntry> clazz)
{
this.configEntryClass = clazz;
return this;
}
/**
* Gets the custom IConfigEntry class that should be used in place of the standard entry class for this Property type, or null if
* none has been set.
*
* @return a class that implements IConfigEntry
*/
public Class<? extends IConfigEntry> getConfigEntryClass()
{
return this.configEntryClass;
}
/**
* Sets a custom IGuiEditListEntry class that should be used in place of the standard entry class for this Property type. This class
* MUST provide a constructor with the following parameter types: {@code GuiEditList} (the owning GuiEditList screen will be provided),
* {@code GuiPropertyList} (the parent GuiPropertyList will be provided), {@code IConfigProperty} (the IConfigProperty for this Property
* will be provided).
*
* @param clazz a class that implements IConfigEntry
*/
public Property setArrayEntryClass(Class<? extends IArrayEntry> clazz)
{
this.arrayEntryClass = clazz;
return this;
}
/**
* Gets the custom IArrayEntry class that should be used in place of the standard entry class for this Property type, or null if
* none has been set.
*
* @return a class that implements IArrayEntry
*/
public Class<? extends IArrayEntry> getArrayEntryClass()
{
return this.arrayEntryClass;
}
/**
* Sets a regex Pattern object used to validate user input for formatted String or String[] properties.
*
* @param validationPattern
*/
public Property setValidationPattern(Pattern validationPattern)
{
this.validationPattern = validationPattern;
return this;
}
/**
* Gets the Pattern object used to validate user input for this Property.
*
* @return the user input validation Pattern object, or null if none is set
*/
public Pattern getValidationPattern()
{
return this.validationPattern;
}
/**
* Sets the localization language key for this Property so that the config GUI screens are nice and pretty <3. The string languageKey +
* ".tooltip" is used for tooltips when a user hovers the mouse over a GUI property label.
*
* @param langKey a string language key such as myawesomemod.config.myPropName
*/
public Property setLanguageKey(String langKey)
{
this.langKey = langKey;
return this;
}
/**
* Gets the language key string for this Property.
*
* @return the language key
*/
public String getLanguageKey()
{
return this.langKey;
}
/**
* Sets the default string value of this Property.
*
* @param defaultValue a String value
*/
public Property setDefaultValue(String defaultValue)
{
this.defaultValue = defaultValue;
return this;
}
/**
* Sets the default String[] values of this Property.
*
* @param defaultValues an array of String values
*/
public Property setDefaultValues(String[] defaultValues)
{
this.defaultValue = "";
for (String s : defaultValues)
this.defaultValue += ", [" + s + "]";
this.defaultValue = this.defaultValue.replaceFirst(", ", "");
this.defaultValues = Arrays.copyOf(defaultValues, defaultValues.length);
return this;
}
/**
* Sets the default int value of this Property.
*
* @param defaultValue an int value
*/
public Property setDefaultValue(int defaultValue)
{
setDefaultValue(Integer.toString(defaultValue));
return this;
}
/**
* Sets the default int[] values of this Property.
*
* @param defaultValues an array of int values
*/
public Property setDefaultValues(int[] defaultValues)
{
String[] temp = new String[defaultValues.length];
for (int i = 0; i < defaultValues.length; i++)
temp[i] = Integer.toString(defaultValues[i]);
setDefaultValues(temp);
return this;
}
/**
* Sets the default double value of this Property.
*
* @param defaultValue a double value
*/
public Property setDefaultValue(double defaultValue)
{
setDefaultValue(Double.toString(defaultValue));
return this;
}
/**
* Sets the default double[] values of this Property
*
* @param defaultValues an array of double values
*/
public Property setDefaultValues(double[] defaultValues)
{
String[] temp = new String[defaultValues.length];
for (int i = 0; i < defaultValues.length; i++)
temp[i] = Double.toString(defaultValues[i]);
setDefaultValues(temp);
return this;
}
/**
* Sets the default boolean value of this Property.
*
* @param defaultValue a boolean value
*/
public Property setDefaultValue(boolean defaultValue)
{
setDefaultValue(Boolean.toString(defaultValue));
return this;
}
/**
* Sets the default boolean[] values of this Property.
*
* @param defaultValues an array of boolean values
*/
public Property setDefaultValues(boolean[] defaultValues)
{
String[] temp = new String[defaultValues.length];
for (int i = 0; i < defaultValues.length; i++)
temp[i] = Boolean.toString(defaultValues[i]);
setDefaultValues(temp);
return this;
}
/**
* Sets the minimum int value of this Property.
*
* @param minValue an int value
*/
public Property setMinValue(int minValue)
{
this.minValue = Integer.toString(minValue);
return this;
}
/**
* Sets the maximum int value of this Property.
*
* @param maxValue an int value
*/
public Property setMaxValue(int maxValue)
{
this.maxValue = Integer.toString(maxValue);
return this;
}
/**
* Sets the minimum double value of this Property.
*
* @param minValue a double value
*/
public Property setMinValue(double minValue)
{
this.minValue = Double.toString(minValue);
return this;
}
/**
* Sets the maximum double value of this Property.
*
* @param maxValue a double value
*/
public Property setMaxValue(double maxValue)
{
this.maxValue = Double.toString(maxValue);
return this;
}
/**
* Gets the minimum value.
*
* @return the minimum value bound
*/
public String getMinValue()
{
return minValue;
}
/**
* Gets the maximum value.
*
* @return the maximum value bound
*/
public String getMaxValue()
{
return maxValue;
}
/**
* Returns the value in this property as it's raw string.
*
@ -92,15 +640,44 @@ public class Property
return value;
}
/**
* Sets the array of valid values that this String Property can be set to. When an array of valid values is defined for a Property the
* GUI control for that property will be a value cycle button.
*
* @param validValues a String array of valid values
*/
public Property setValidValues(String[] validValues)
{
this.validValues = validValues;
return this;
}
/**
* Gets the array of valid values that this String Property can be set to, or null if not defined.
*
* @return a String array of valid values
*/
public String[] getValidValues()
{
return this.validValues;
}
/**
* Returns the value in this property as an integer,
* if the value is not a valid integer, it will return -1.
* if the value is not a valid integer, it will return the initially provided default.
*
* @return The value
*/
public int getInt()
{
return getInt(-1);
try
{
return Integer.parseInt(value);
}
catch (NumberFormatException e)
{
return Integer.parseInt(defaultValue);
}
}
/**
@ -160,8 +737,26 @@ public class Property
}
}
/**
* Returns the value in this property as a boolean, if the value is not a valid boolean, it will return the provided default.
*
* @return The value as a boolean, or the default
*/
public boolean getBoolean()
{
if (isBooleanValue())
{
return Boolean.parseBoolean(value);
}
else
{
return Boolean.parseBoolean(defaultValue);
}
}
/**
* Checks if the current value held by this property is a valid boolean value.
*
* @return True if it is a boolean value
*/
public boolean isBooleanValue()
@ -206,6 +801,24 @@ public class Property
}
}
/**
* Returns the value in this property as a double, if the value is not a valid double, it will return the provided default.
*
* @param _default The default to provide if the current value is not a valid double
* @return The value
*/
public double getDouble()
{
try
{
return Double.parseDouble(value);
}
catch (NumberFormatException e)
{
return Double.parseDouble(defaultValue);
}
}
public String[] getStringList()
{
return values;
@ -246,18 +859,19 @@ public class Property
*/
public boolean isIntList()
{
for (String value : values)
{
try
if (isList && type == Type.INTEGER)
for (String value : values)
{
Integer.parseInt(value);
try
{
Integer.parseInt(value);
}
catch (NumberFormatException e)
{
return false;
}
}
catch (NumberFormatException e)
{
return false;
}
}
return true;
return isList && type == Type.INTEGER;
}
/**
@ -294,15 +908,16 @@ public class Property
*/
public boolean isBooleanList()
{
for (String value : values)
{
if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value))
if (isList && type == Type.BOOLEAN)
for (String value : values)
{
return false;
if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value))
{
return false;
}
}
}
return true;
return isList && type == Type.BOOLEAN;
}
/**
@ -339,34 +954,46 @@ public class Property
*/
public boolean isDoubleList()
{
for (String value : values)
{
try
if (isList && type == Type.DOUBLE)
for (String value : values)
{
Double.parseDouble(value);
try
{
Double.parseDouble(value);
}
catch (NumberFormatException e)
{
return false;
}
}
catch (NumberFormatException e)
{
return false;
}
}
return true;
return isList && type == Type.DOUBLE;
}
/**
* Gets the name/key for this Property.
*
* @return the Property name
*/
public String getName()
{
return name;
}
public void setName(String name)
/**
* Sets the name/key for this Property.
*
* @param name a name
*/
public Property setName(String name)
{
this.name = name;
return this;
}
/**
* Determines if this config value was just created, or if it was read from the config file.
* This is useful for mods who auto-assign there blocks to determine if the ID returned is
* This is useful for mods who auto-assign their blocks to determine if the ID returned is
* a configured one, or a automatically generated one.
*
* @return True if this property was loaded from the config file with a value
@ -376,31 +1003,141 @@ public class Property
return wasRead;
}
/**
* Gets the Property.Type enum value for this Property.
*
* @return the Property's type
*/
public Type getType()
{
return type;
}
/**
* Returns whether or not this Property is a list/array.
*
* @return true if this Property is a list/array, false otherwise
*/
public boolean isList()
{
return isList;
}
/**
* Gets the changed status of this Property.
*
* @return true if this Property has changed, false otherwise
*/
public boolean hasChanged(){ return changed; }
void resetChangedState(){ changed = false; }
public void set(String value)
/**
* Sets the value of this Property to the provided String value.
*/
public Property setValue(String value)
{
this.value = value;
changed = true;
return this;
}
public void set(String value)
{
this.setValue(value);
}
/**
* Sets the values of this Property to the provided String[] values.
*/
public Property setValues(String[] values)
{
this.values = Arrays.copyOf(values, values.length);
changed = true;
return this;
}
public void set(String[] values)
{
this.values = values;
this.setValues(values);
}
/**
* Sets the value of this Property to the provided int value.
*/
public Property setValue(int value)
{
setValue(Integer.toString(value));
return this;
}
/**
* Sets the value of this Property to the provided boolean value.
*/
public Property setValue(boolean value)
{
setValue(Boolean.toString(value));
return this;
}
/**
* Sets the value of this Property to the provided double value.
*/
public Property setValue(double value)
{
setValue(Double.toString(value));
return this;
}
/**
* Sets the values of this Property to the provided boolean[] values.
*/
public Property setValues(boolean[] values)
{
this.values = new String[values.length];
for (int i = 0; i < values.length; i++)
this.values[i] = String.valueOf(values[i]);
changed = true;
return this;
}
public void set(boolean[] values)
{
this.setValues(values);
}
/**
* Sets the values of this Property to the provided int[] values.
*/
public Property setValues(int[] values)
{
this.values = new String[values.length];
for (int i = 0; i < values.length; i++)
this.values[i] = String.valueOf(values[i]);
changed = true;
return this;
}
public void set(int[] values)
{
this.setValues(values);
}
/**
* Sets the values of this Property to the provided double[] values.
*/
public Property setValues(double[] values)
{
this.values = new String[values.length];
for (int i = 0; i < values.length; i++)
this.values[i] = String.valueOf(values[i]);
changed = true;
return this;
}
public void set(double[] values)
{
this.setValues(values);
}
public void set(int value){ set(Integer.toString(value)); }
public void set(boolean value){ set(Boolean.toString(value)); }
public void set(double value){ set(Double.toString(value)); }

View File

@ -7,4 +7,49 @@ forge.texture.preload.warning=Warning: Texture %s not preloaded, will cause rend
forge.client.shutdown.internal=Shutting down internal server...
forge.update.newversion=New Forge version available: %s
forge.update.beta.1=%sWARNING: %sForge Beta
forge.update.beta.2=Major issues may arise, verify before reporting.
forge.update.beta.2=Major issues may arise, verify before reporting.
forge.configgui.forgeConfigTitle=Minecraft Forge Configuration
forge.configgui.ctgy.forgeGeneralConfig.tooltip=This is where you can edit the settings contained in forge.cfg.
forge.configgui.ctgy.forgeGeneralConfig=General Settings
forge.configgui.ctgy.forgeChunkLoadingConfig.tooltip=This is where you can edit the settings contained in forgeChunkLoading.cfg.
forge.configgui.ctgy.forgeChunkLoadingConfig=Forge Chunk Loader Default Settings
forge.configgui.ctgy.forgeChunkLoadingModConfig.tooltip=This is where you can define mod-specific override settings that will be used instead of the defaults.
forge.configgui.ctgy.forgeChunkLoadingModConfig=Mod Overrides
forge.configgui.ctgy.forgeChunkLoadingAddModConfig.tooltip=Allows you to define mod-specific settings that will override the defaults. A value of zero in either entry effectively disables any chunkloading capabilities for that mod.
forge.configgui.ctgy.forgeChunkLoadingAddModConfig=+ Add New Mod Override
forge.configgui.biomeSkyBlendRange.tooltip=Control the range of sky blending for colored skies in biomes.
forge.configgui.biomeSkyBlendRange=Biome Sky Blend Range
forge.configgui.clumpingThreshold.tooltip=Controls the number threshold at which Packet51 is preferred over Packet52.
forge.configgui.clumpingThreshold=Packet Clumping Threshold
forge.configgui.disableVersionCheck.tooltip=Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.
forge.configgui.disableVersionCheck=Disable Forge Version Check
forge.configgui.enableGlobalConfig=Enable Global Config
forge.configgui.forceDuplicateFluidBlockCrash.tooltip=Set this to true to force a crash if more than one block attempts to link back to the same Fluid.
forge.configgui.forceDuplicateFluidBlockCrash=Force Dupe Fluid Block Crash
forge.configgui.fullBoundingBoxLadders.tooltip=Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticeable differences in mechanics so default is vanilla behavior.
forge.configgui.fullBoundingBoxLadders=Full Bounding Box Ladders
forge.configgui.removeErroringEntities.tooltip=Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.
forge.configgui.removeErroringEntities=Remove Erroring Entities
forge.configgui.removeErroringTileEntities.tooltip=Set this to true to remove any TileEntity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.
forge.configgui.removeErroringTileEntities=Remove Erroring Tile Entities
forge.configgui.sortRecipies.tooltip=Set to true to enable the post initialization sorting of crafting recipes using Forge's sorter. May cause desyncing on conflicting recipies. MUST RESTART MINECRAFT IF CHANGED FROM THE CONFIG GUI.
forge.configgui.sortRecipies=Sort Recipes
forge.configgui.zombieBabyChance.tooltip=Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.
forge.configgui.zombieBabyChance=Zombie Baby Chance
forge.configgui.zombieBaseSummonChance.tooltip=Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.
forge.configgui.zombieBaseSummonChance=Zombie Summon Chance
forge.configgui.modID.tooltip=The mod ID that you want to define override settings for.
forge.configgui.modID=Mod ID
forge.configgui.dormantChunkCacheSize.tooltip=Unloaded chunks can first be kept in a dormant cache for quicker loading times. Specify the size (in chunks) of that cache here.
forge.configgui.dormantChunkCacheSize=Dormant Chunk Cache Size
forge.configgui.enableModOverrides.tooltip=Enable this setting to allow custom per-mod settings to be defined.
forge.configgui.enableModOverrides=Enable Mod Overrides
forge.configgui.maximumChunksPerTicket.tooltip=This is the maximum number of chunks a single ticket can force.
forge.configgui.maximumChunksPerTicket=Chunks Per Ticket Limit
forge.configgui.maximumTicketCount.tooltip=This is the number of chunk loading requests a mod is allowed to make.
forge.configgui.maximumTicketCount=Mod Ticket Limit
forge.configgui.playerTicketCount.tooltip=The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it.
forge.configgui.playerTicketCount=Player Ticket Limit