Commit e38f7f248651a15b730929f96b622539e5930f97

Authored by Mumfrey
1 parent 813a72f5

LiteLoader 1.6.4_02 - experimental - add Exposable interface and support for mod config via Gson

java/com/mumfrey/liteloader/ExposeConfig.java 0 → 100644
  1 +package com.mumfrey.liteloader;
  2 +
  3 +import java.lang.annotation.ElementType;
  4 +import java.lang.annotation.Retention;
  5 +import java.lang.annotation.RetentionPolicy;
  6 +import java.lang.annotation.Target;
  7 +
  8 +import com.mumfrey.liteloader.modconfig.ConfigStrategy;
  9 +
  10 +/**
  11 + * Annotation which can be a applied to mod classes to indicate they should be serialised with Gson
  12 + *
  13 + * @author Adam Mummery-Smith
  14 + */
  15 +@Target(ElementType.TYPE)
  16 +@Retention(RetentionPolicy.RUNTIME)
  17 +public @interface ExposeConfig
  18 +{
  19 + /**
  20 + * Configuration strategy to use
  21 + */
  22 + ConfigStrategy strategy() default ConfigStrategy.Unversioned;
  23 +
  24 + /**
  25 + * Config file name, if not specified the mod class name is used
  26 + */
  27 + String filename() default "";
  28 +}
java/com/mumfrey/liteloader/LiteMod.java
@@ -2,12 +2,14 @@ package com.mumfrey.liteloader; @@ -2,12 +2,14 @@ package com.mumfrey.liteloader;
2 2
3 import java.io.File; 3 import java.io.File;
4 4
  5 +import com.mumfrey.liteloader.modconfig.Exposable;
  6 +
5 /** 7 /**
6 * Base interface for mods 8 * Base interface for mods
7 * 9 *
8 * @author Adam Mummery-Smith 10 * @author Adam Mummery-Smith
9 */ 11 */
10 -public interface LiteMod 12 +public interface LiteMod extends Exposable
11 { 13 {
12 /** 14 /**
13 * Get the mod's display name 15 * Get the mod's display name
java/com/mumfrey/liteloader/core/LiteLoader.java
@@ -40,7 +40,8 @@ import com.mumfrey.liteloader.crashreport.CallableLiteLoaderBrand; @@ -40,7 +40,8 @@ import com.mumfrey.liteloader.crashreport.CallableLiteLoaderBrand;
40 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderMods; 40 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderMods;
41 import com.mumfrey.liteloader.gui.GuiControlsPaginated; 41 import com.mumfrey.liteloader.gui.GuiControlsPaginated;
42 import com.mumfrey.liteloader.gui.GuiScreenModInfo; 42 import com.mumfrey.liteloader.gui.GuiScreenModInfo;
43 -import com.mumfrey.liteloader.modconfig.ConfigPanelManager; 43 +import com.mumfrey.liteloader.modconfig.ConfigManager;
  44 +import com.mumfrey.liteloader.modconfig.Exposable;
44 import com.mumfrey.liteloader.permissions.PermissionsManagerClient; 45 import com.mumfrey.liteloader.permissions.PermissionsManagerClient;
45 import com.mumfrey.liteloader.util.PrivateFields; 46 import com.mumfrey.liteloader.util.PrivateFields;
46 47
@@ -170,9 +171,9 @@ public final class LiteLoader @@ -170,9 +171,9 @@ public final class LiteLoader
170 private final PermissionsManagerClient permissionsManager = PermissionsManagerClient.getInstance(); 171 private final PermissionsManagerClient permissionsManager = PermissionsManagerClient.getInstance();
171 172
172 /** 173 /**
173 - * Configuration panel manager/registry 174 + * Mod configuration manager
174 */ 175 */
175 - private final ConfigPanelManager configPanelManager; 176 + private final ConfigManager configManager;
176 177
177 /** 178 /**
178 * Flag which keeps track of whether late initialisation has been done 179 * Flag which keeps track of whether late initialisation has been done
@@ -279,7 +280,7 @@ public final class LiteLoader @@ -279,7 +280,7 @@ public final class LiteLoader
279 this.enabledModsList = EnabledModsList.createFrom(this.enabledModsFile); 280 this.enabledModsList = EnabledModsList.createFrom(this.enabledModsFile);
280 this.enabledModsList.processModsList(bootstrap.getProfile(), modNameFilter); 281 this.enabledModsList.processModsList(bootstrap.getProfile(), modNameFilter);
281 282
282 - this.configPanelManager = new ConfigPanelManager(); 283 + this.configManager = new ConfigManager();
283 } 284 }
284 285
285 /** 286 /**
@@ -787,6 +788,37 @@ public final class LiteLoader @@ -787,6 +788,37 @@ public final class LiteLoader
787 } 788 }
788 789
789 /** 790 /**
  791 + * @param modClass
  792 + */
  793 + public void writeConfig(LiteMod mod)
  794 + {
  795 + if (mod != null)
  796 + {
  797 + this.writeConfig(mod.getClass());
  798 + }
  799 + }
  800 +
  801 + /**
  802 + * @param exposableClass
  803 + */
  804 + public void writeConfig(Class<? extends Exposable> exposableClass)
  805 + {
  806 + this.configManager.invalidateConfig(exposableClass);
  807 + }
  808 +
  809 + /**
  810 + * Register an arbitrary Exposable
  811 + *
  812 + * @param exposable Exposable object to register
  813 + * @param fileName
  814 + */
  815 + public void registerExposable(Exposable exposable, String fileName)
  816 + {
  817 + this.configManager.registerExposable(exposable, fileName, true);
  818 + this.configManager.initConfig(exposable);
  819 + }
  820 +
  821 + /**
790 * Create mod instances from the enumerated classes 822 * Create mod instances from the enumerated classes
791 * 823 *
792 * @param modsToLoad List of mods to load 824 * @param modsToLoad List of mods to load
@@ -898,6 +930,9 @@ public final class LiteLoader @@ -898,6 +930,9 @@ public final class LiteLoader
898 { 930 {
899 LiteLoader.logInfo("Initialising mod %s version %s", mod.getName(), mod.getVersion()); 931 LiteLoader.logInfo("Initialising mod %s version %s", mod.getName(), mod.getVersion());
900 932
  933 + // register mod config panel if configurable
  934 + this.configManager.registerMod(mod);
  935 +
901 try 936 try
902 { 937 {
903 this.handleModVersionUpgrade(mod); 938 this.handleModVersionUpgrade(mod);
@@ -907,6 +942,9 @@ public final class LiteLoader @@ -907,6 +942,9 @@ public final class LiteLoader
907 LiteLoader.logWarning("Error performing settings upgrade for %s. Settings may not be properly migrated", mod.getName()); 942 LiteLoader.logWarning("Error performing settings upgrade for %s. Settings may not be properly migrated", mod.getName());
908 } 943 }
909 944
  945 + // Init mod config if there is any
  946 + this.configManager.initConfig(mod);
  947 +
910 // initialise the mod 948 // initialise the mod
911 mod.init(this.commonConfigFolder); 949 mod.init(this.commonConfigFolder);
912 950
@@ -916,9 +954,6 @@ public final class LiteLoader @@ -916,9 +954,6 @@ public final class LiteLoader
916 // add mod to permissions manager if permissible 954 // add mod to permissions manager if permissible
917 this.permissionsManager.registerMod(mod); 955 this.permissionsManager.registerMod(mod);
918 956
919 - // register mod config panel if configurable  
920 - this.configPanelManager.registerMod(mod);  
921 -  
922 this.loadedMods.add(mod); 957 this.loadedMods.add(mod);
923 this.loadedModsList += String.format("\n - %s version %s", mod.getName(), mod.getVersion()); 958 this.loadedModsList += String.format("\n - %s version %s", mod.getName(), mod.getVersion());
924 } 959 }
@@ -934,6 +969,11 @@ public final class LiteLoader @@ -934,6 +969,11 @@ public final class LiteLoader
934 if (LiteLoaderBootstrap.VERSION.getLoaderRevision() > lastModVersion.getLoaderRevision()) 969 if (LiteLoaderBootstrap.VERSION.getLoaderRevision() > lastModVersion.getLoaderRevision())
935 { 970 {
936 LiteLoader.logInfo("Performing config upgrade for mod %s. Upgrading %s to %s...", mod.getName(), lastModVersion, LiteLoaderBootstrap.VERSION); 971 LiteLoader.logInfo("Performing config upgrade for mod %s. Upgrading %s to %s...", mod.getName(), lastModVersion, LiteLoaderBootstrap.VERSION);
  972 +
  973 + // Migrate versioned config if any is present
  974 + this.configManager.migrateModConfig(mod, this.versionConfigFolder, this.inflectVersionedConfigPath(lastModVersion));
  975 +
  976 + // Let the mod upgrade
937 mod.upgradeSettings(LiteLoaderBootstrap.VERSION.getMinecraftVersion(), this.versionConfigFolder, this.inflectVersionedConfigPath(lastModVersion)); 977 mod.upgradeSettings(LiteLoaderBootstrap.VERSION.getMinecraftVersion(), this.versionConfigFolder, this.inflectVersionedConfigPath(lastModVersion));
938 978
939 this.bootstrap.storeLastKnownModRevision(modKey); 979 this.bootstrap.storeLastKnownModRevision(modKey);
@@ -1038,7 +1078,7 @@ public final class LiteLoader @@ -1038,7 +1078,7 @@ public final class LiteLoader
1038 // If we're at the main menu, prepare the overlay 1078 // If we're at the main menu, prepare the overlay
1039 if (this.modInfoScreen == null || this.modInfoScreen.getMenu() != this.minecraft.currentScreen) 1079 if (this.modInfoScreen == null || this.modInfoScreen.getMenu() != this.minecraft.currentScreen)
1040 { 1080 {
1041 - this.modInfoScreen = new GuiScreenModInfo(this.minecraft, (GuiMainMenu)this.minecraft.currentScreen, this, this.enabledModsList, this.configPanelManager); 1081 + this.modInfoScreen = new GuiScreenModInfo(this.minecraft, (GuiMainMenu)this.minecraft.currentScreen, this, this.enabledModsList, this.configManager);
1042 } 1082 }
1043 1083
1044 this.modInfoScreen.drawScreen(mouseX, mouseY, partialTicks); 1084 this.modInfoScreen.drawScreen(mouseX, mouseY, partialTicks);
@@ -1051,7 +1091,7 @@ public final class LiteLoader @@ -1051,7 +1091,7 @@ public final class LiteLoader
1051 } 1091 }
1052 else if (this.minecraft.currentScreen instanceof GuiMainMenu && Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) && Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) && Keyboard.isKeyDown(Keyboard.KEY_TAB)) 1092 else if (this.minecraft.currentScreen instanceof GuiMainMenu && Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) && Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) && Keyboard.isKeyDown(Keyboard.KEY_TAB))
1053 { 1093 {
1054 - this.minecraft.displayGuiScreen(new GuiScreenModInfo(this.minecraft, (GuiMainMenu)this.minecraft.currentScreen, this, this.enabledModsList, this.configPanelManager)); 1094 + this.minecraft.displayGuiScreen(new GuiScreenModInfo(this.minecraft, (GuiMainMenu)this.minecraft.currentScreen, this, this.enabledModsList, this.configManager));
1055 } 1095 }
1056 } 1096 }
1057 1097
@@ -1064,14 +1104,30 @@ public final class LiteLoader @@ -1064,14 +1104,30 @@ public final class LiteLoader
1064 // Tick the permissions manager 1104 // Tick the permissions manager
1065 this.permissionsManager.onTick(this.minecraft, partialTicks, inGame); 1105 this.permissionsManager.onTick(this.minecraft, partialTicks, inGame);
1066 1106
  1107 + // Tick the config manager
  1108 + this.configManager.onTick();
  1109 +
1067 this.checkAndStoreKeyBindings(); 1110 this.checkAndStoreKeyBindings();
1068 1111
1069 if (this.modInfoScreen != null && this.minecraft.currentScreen != this.modInfoScreen) 1112 if (this.modInfoScreen != null && this.minecraft.currentScreen != this.modInfoScreen)
1070 { 1113 {
1071 this.modInfoScreen.updateScreen(); 1114 this.modInfoScreen.updateScreen();
1072 } 1115 }
  1116 +
  1117 + if (!PrivateFields.gameIsRunning.get(this.minecraft))
  1118 + {
  1119 + this.onShutDown();
  1120 + }
1073 } 1121 }
1074 - 1122 +
  1123 + private void onShutDown()
  1124 + {
  1125 + LiteLoader.logInfo("LiteLoader is shutting down, syncing configuration");
  1126 +
  1127 + this.storeBindings();
  1128 + this.configManager.syncConfig();
  1129 + }
  1130 +
1075 /** 1131 /**
1076 * Register a key for a mod 1132 * Register a key for a mod
1077 * 1133 *
java/com/mumfrey/liteloader/gui/GuiScreenModInfo.java
@@ -20,7 +20,7 @@ import com.mumfrey.liteloader.core.EnabledModsList; @@ -20,7 +20,7 @@ import com.mumfrey.liteloader.core.EnabledModsList;
20 import com.mumfrey.liteloader.core.LiteLoader; 20 import com.mumfrey.liteloader.core.LiteLoader;
21 import com.mumfrey.liteloader.core.ModFile; 21 import com.mumfrey.liteloader.core.ModFile;
22 import com.mumfrey.liteloader.modconfig.ConfigPanel; 22 import com.mumfrey.liteloader.modconfig.ConfigPanel;
23 -import com.mumfrey.liteloader.modconfig.ConfigPanelManager; 23 +import com.mumfrey.liteloader.modconfig.ConfigManager;
24 24
25 import net.minecraft.src.DynamicTexture; 25 import net.minecraft.src.DynamicTexture;
26 import net.minecraft.src.GuiButton; 26 import net.minecraft.src.GuiButton;
@@ -136,7 +136,7 @@ public class GuiScreenModInfo extends GuiScreen @@ -136,7 +136,7 @@ public class GuiScreenModInfo extends GuiScreen
136 */ 136 */
137 private GuiCheckbox chkEnabled; 137 private GuiCheckbox chkEnabled;
138 138
139 - private ConfigPanelManager configPanelManager; 139 + private ConfigManager configManager;
140 140
141 /** 141 /**
142 * Configuration panel 142 * Configuration panel
@@ -149,11 +149,11 @@ public class GuiScreenModInfo extends GuiScreen @@ -149,11 +149,11 @@ public class GuiScreenModInfo extends GuiScreen
149 * @param loader 149 * @param loader
150 * @param enabledModsList 150 * @param enabledModsList
151 */ 151 */
152 - public GuiScreenModInfo(Minecraft minecraft, GuiMainMenu mainMenu, LiteLoader loader, EnabledModsList enabledModsList, ConfigPanelManager configPanelManager) 152 + public GuiScreenModInfo(Minecraft minecraft, GuiMainMenu mainMenu, LiteLoader loader, EnabledModsList enabledModsList, ConfigManager configManager)
153 { 153 {
154 this.mc = minecraft; 154 this.mc = minecraft;
155 this.mainMenu = mainMenu; 155 this.mainMenu = mainMenu;
156 - this.configPanelManager = configPanelManager; 156 + this.configManager = configManager;
157 157
158 // Spawn the texture resource if we haven't already 158 // Spawn the texture resource if we haven't already
159 if (aboutTexture == null) 159 if (aboutTexture == null)
@@ -484,7 +484,7 @@ public class GuiScreenModInfo extends GuiScreen @@ -484,7 +484,7 @@ public class GuiScreenModInfo extends GuiScreen
484 this.btnToggle.drawButton = true; 484 this.btnToggle.drawButton = true;
485 this.btnToggle.displayString = this.selectedMod.willBeEnabled() ? "Disable mod" : "Enable mod"; 485 this.btnToggle.displayString = this.selectedMod.willBeEnabled() ? "Disable mod" : "Enable mod";
486 486
487 - this.btnConfig.drawButton = this.configPanelManager.hasPanel(this.selectedMod.getModClass()); 487 + this.btnConfig.drawButton = this.configManager.hasPanel(this.selectedMod.getModClass());
488 } 488 }
489 } 489 }
490 490
@@ -679,7 +679,7 @@ public class GuiScreenModInfo extends GuiScreen @@ -679,7 +679,7 @@ public class GuiScreenModInfo extends GuiScreen
679 { 679 {
680 if (this.selectedMod != null && this.selectedMod.getModClass() != null) 680 if (this.selectedMod != null && this.selectedMod.getModClass() != null)
681 { 681 {
682 - ConfigPanel panel = this.configPanelManager.getPanel(this.selectedMod.getModClass()); 682 + ConfigPanel panel = this.configManager.getPanel(this.selectedMod.getModClass());
683 if (panel != null) 683 if (panel != null)
684 { 684 {
685 if (this.configPanel != null) 685 if (this.configPanel != null)
java/com/mumfrey/liteloader/modconfig/ConfigManager.java 0 → 100644
  1 +package com.mumfrey.liteloader.modconfig;
  2 +
  3 +import java.io.File;
  4 +import java.io.IOException;
  5 +import java.util.HashMap;
  6 +import java.util.LinkedList;
  7 +import java.util.List;
  8 +import java.util.Map;
  9 +
  10 +import com.google.common.base.Strings;
  11 +import com.google.common.io.Files;
  12 +import com.mumfrey.liteloader.Configurable;
  13 +import com.mumfrey.liteloader.LiteMod;
  14 +import com.mumfrey.liteloader.ExposeConfig;
  15 +
  16 +/**
  17 + * Registry where we keep the mod config panel classes
  18 + *
  19 + * @author Adam Mummery-Smith
  20 + */
  21 +public class ConfigManager
  22 +{
  23 + /**
  24 + * Mod config panel classes
  25 + */
  26 + private Map<Class<? extends LiteMod>, Class<? extends ConfigPanel>> configPanels = new HashMap<Class<? extends LiteMod>, Class<? extends ConfigPanel>>();
  27 +
  28 + /**
  29 + * Mod config writers
  30 + */
  31 + private Map<Class<? extends Exposable>, ExposableConfigWriter> configWriters = new HashMap<Class<? extends Exposable>, ExposableConfigWriter>();
  32 +
  33 + /**
  34 + * List of config writers, for faster iteration in onTick
  35 + */
  36 + private List<ExposableConfigWriter> configWriterList = new LinkedList<ExposableConfigWriter>();
  37 +
  38 + /**
  39 + * Register a mod, adds the config panel class to the map if the mod implements Configurable
  40 + */
  41 + public void registerMod(LiteMod mod)
  42 + {
  43 + if (mod instanceof Configurable)
  44 + {
  45 + Class<? extends ConfigPanel> panelClass = ((Configurable)mod).getConfigPanelClass();
  46 + if (panelClass != null) this.configPanels.put(mod.getClass(), panelClass);
  47 + }
  48 +
  49 + this.registerExposable(mod, null, false);
  50 + }
  51 +
  52 + /**
  53 + * @param exposable
  54 + */
  55 + public void registerExposable(Exposable exposable, String fileName, boolean force)
  56 + {
  57 + ExposeConfig exposeConfig = exposable.getClass().<ExposeConfig>getAnnotation(ExposeConfig.class);
  58 + if (exposeConfig != null)
  59 + {
  60 + if (fileName == null) exposeConfig.filename();
  61 + this.initConfigWriter(exposable, fileName, exposeConfig.strategy());
  62 + }
  63 + else if (force)
  64 + {
  65 + this.initConfigWriter(exposable, fileName, ConfigStrategy.Versioned);
  66 + }
  67 + }
  68 +
  69 + /**
  70 + * Create a config writer instance for the specified mod
  71 + *
  72 + * @param exposable
  73 + * @param fileName
  74 + * @param strategy
  75 + */
  76 + private void initConfigWriter(Exposable exposable, String fileName, ConfigStrategy strategy)
  77 + {
  78 + if (this.configWriters.containsKey(exposable.getClass()))
  79 + {
  80 + throw new IllegalArgumentException("Cannot register multiple Exposable instances of the same class or the Exposable already registered");
  81 + }
  82 +
  83 + if (Strings.isNullOrEmpty(fileName))
  84 + {
  85 + fileName = exposable.getClass().getSimpleName().toLowerCase();
  86 +
  87 + if (fileName.startsWith("litemod"))
  88 + fileName = fileName.substring(7);
  89 + }
  90 +
  91 + ExposableConfigWriter configWriter = ExposableConfigWriter.create(exposable, strategy, fileName);
  92 + if (configWriter != null)
  93 + {
  94 + this.configWriters.put(exposable.getClass(), configWriter);
  95 + this.configWriterList.add(configWriter);
  96 + }
  97 + }
  98 +
  99 + /**
  100 + * Initialise the config writer for the specified mod
  101 + *
  102 + * @param exposable
  103 + */
  104 + public void initConfig(Exposable exposable)
  105 + {
  106 + Class<? extends Exposable> exposableClass = exposable.getClass();
  107 + if (exposableClass != null && this.configWriters.containsKey(exposableClass))
  108 + {
  109 + this.configWriters.get(exposableClass).init();
  110 + }
  111 + }
  112 +
  113 + /**
  114 + * If the specified mod has a versioned config strategy, attempt to copy the config
  115 + *
  116 + * @param mod
  117 + * @param newConfigPath
  118 + * @param oldConfigPath
  119 + */
  120 + public void migrateModConfig(LiteMod mod, File newConfigPath, File oldConfigPath)
  121 + {
  122 + Class<? extends Exposable> exposableClass = mod.getClass();
  123 + if (exposableClass != null && this.configWriters.containsKey(exposableClass))
  124 + {
  125 + ExposableConfigWriter writer = this.configWriters.get(exposableClass);
  126 + if (writer.isVersioned())
  127 + {
  128 + File newConfigFile = writer.getConfigFile();
  129 + File legacyConfigFile = new File(oldConfigPath, newConfigFile.getName());
  130 +
  131 + if (legacyConfigFile.exists() && !newConfigFile.exists())
  132 + {
  133 + try
  134 + {
  135 + Files.copy(legacyConfigFile, newConfigFile);
  136 + }
  137 + catch (IOException ex)
  138 + {
  139 + ex.printStackTrace();
  140 + }
  141 + }
  142 + }
  143 + }
  144 + }
  145 +
  146 + /**
  147 + * Check whether a config panel is available for the specified class
  148 + *
  149 + * @param modClass
  150 + * @return
  151 + */
  152 + public boolean hasPanel(Class<? extends LiteMod> modClass)
  153 + {
  154 + return modClass != null && this.configPanels.containsKey(modClass);
  155 + }
  156 +
  157 + /**
  158 + * Instance a new config panel for the specified mod class if one is available
  159 + *
  160 + * @param modClass
  161 + * @return
  162 + */
  163 + public ConfigPanel getPanel(Class<? extends LiteMod> modClass)
  164 + {
  165 + if (modClass != null && this.configPanels.containsKey(modClass))
  166 + {
  167 + try
  168 + {
  169 + return this.configPanels.get(modClass).newInstance();
  170 + }
  171 + catch (InstantiationException ex) {}
  172 + catch (IllegalAccessException ex) {}
  173 +
  174 + // If instantiation fails, remove the panel
  175 + this.configPanels.remove(modClass);
  176 + }
  177 +
  178 + return null;
  179 + }
  180 +
  181 + /**
  182 + * Invalidate the specified mod config, cause it to be written to disk or scheduled for writing
  183 + * if it has been written recent
  184 + *
  185 + * @param exposableClass
  186 + */
  187 + public void invalidateConfig(Class<? extends Exposable> exposableClass)
  188 + {
  189 + if (exposableClass != null && this.configWriters.containsKey(exposableClass))
  190 + {
  191 + this.configWriters.get(exposableClass).invalidate();
  192 + }
  193 + }
  194 +
  195 + /**
  196 + * Tick all of the configuration writers, handles latent writes for anti-hammer strategy
  197 + */
  198 + public void onTick()
  199 + {
  200 + for (ExposableConfigWriter writer : this.configWriterList)
  201 + {
  202 + writer.onTick();
  203 + }
  204 + }
  205 +
  206 + /**
  207 + * Force all mod configs to be flushed to disk
  208 + */
  209 + public void syncConfig()
  210 + {
  211 + for (ExposableConfigWriter writer : this.configWriterList)
  212 + {
  213 + writer.sync();
  214 + }
  215 + }
  216 +}
java/com/mumfrey/liteloader/modconfig/ConfigPanelManager.java deleted 100644 → 0
1 -package com.mumfrey.liteloader.modconfig;  
2 -  
3 -import java.util.HashMap;  
4 -import java.util.Map;  
5 -  
6 -import com.mumfrey.liteloader.Configurable;  
7 -import com.mumfrey.liteloader.LiteMod;  
8 -  
9 -/**  
10 - * Registry where we keep the mod config panel classes  
11 - *  
12 - * @author Adam Mummery-Smith  
13 - */  
14 -public class ConfigPanelManager  
15 -{  
16 - /**  
17 - * Mod config panel classes  
18 - */  
19 - private Map<Class<? extends LiteMod>, Class<? extends ConfigPanel>> configPanels = new HashMap<Class<? extends LiteMod>, Class<? extends ConfigPanel>>();  
20 -  
21 - /**  
22 - * Register a mod, adds the config panel class to the map if the mod implements Configurable  
23 - */  
24 - public void registerMod(LiteMod mod)  
25 - {  
26 - if (mod instanceof Configurable)  
27 - {  
28 - Class<? extends ConfigPanel> panelClass = ((Configurable)mod).getConfigPanelClass();  
29 - if (panelClass != null) this.configPanels.put(mod.getClass(), panelClass);  
30 - }  
31 - }  
32 -  
33 - /**  
34 - * Check whether a config panel is available for the specified class  
35 - *  
36 - * @param modClass  
37 - * @return  
38 - */  
39 - public boolean hasPanel(Class<? extends LiteMod> modClass)  
40 - {  
41 - return modClass != null && this.configPanels.containsKey(modClass);  
42 - }  
43 -  
44 - /**  
45 - * Instance a new config panel for the specified mod class if one is available  
46 - *  
47 - * @param modClass  
48 - * @return  
49 - */  
50 - public ConfigPanel getPanel(Class<? extends LiteMod> modClass)  
51 - {  
52 - if (modClass != null && this.configPanels.containsKey(modClass))  
53 - {  
54 - try  
55 - {  
56 - return this.configPanels.get(modClass).newInstance();  
57 - }  
58 - catch (InstantiationException ex) {}  
59 - catch (IllegalAccessException ex) {}  
60 -  
61 - // If instantiation fails, remove the panel  
62 - this.configPanels.remove(modClass);  
63 - }  
64 -  
65 - return null;  
66 - }  
67 -}  
java/com/mumfrey/liteloader/modconfig/ConfigStrategy.java 0 → 100644
  1 +package com.mumfrey.liteloader.modconfig;
  2 +
  3 +import java.io.File;
  4 +
  5 +import com.mumfrey.liteloader.core.LiteLoader;
  6 +
  7 +/**
  8 + * Configuration management strategy
  9 + *
  10 + * @author Adam Mummery-Smith
  11 + */
  12 +public enum ConfigStrategy
  13 +{
  14 + /**
  15 + * Use the unversioned "common" config folder
  16 + */
  17 + Unversioned,
  18 +
  19 + /**
  20 + * Use the versioned config folder
  21 + */
  22 + Versioned;
  23 +
  24 + public File getFileForStrategy(String fileName)
  25 + {
  26 + if (this == ConfigStrategy.Versioned)
  27 + {
  28 + return new File(LiteLoader.getConfigFolder(), fileName);
  29 + }
  30 +
  31 + return new File(LiteLoader.getCommonConfigFolder(), fileName);
  32 + }
  33 +}
java/com/mumfrey/liteloader/modconfig/Exposable.java 0 → 100644
  1 +package com.mumfrey.liteloader.modconfig;
  2 +
  3 +/**
  4 + * Base interface for objects which can support the ExposeConfig annotations
  5 + *
  6 + * @author Adam Mummery-Smith
  7 + */
  8 +public interface Exposable
  9 +{
  10 +}
java/com/mumfrey/liteloader/modconfig/ExposableConfigWriter.java 0 → 100644
  1 +package com.mumfrey.liteloader.modconfig;
  2 +
  3 +import java.io.File;
  4 +import java.io.FileReader;
  5 +import java.io.FileWriter;
  6 +import java.io.IOException;
  7 +import java.lang.reflect.Type;
  8 +
  9 +import com.google.gson.Gson;
  10 +import com.google.gson.GsonBuilder;
  11 +import com.google.gson.InstanceCreator;
  12 +
  13 +/**
  14 + * Manages serialisation of exposable properties to a JSON config file via Gson
  15 + *
  16 + * @author Adam Mummery-Smith
  17 + */
  18 +public final class ExposableConfigWriter implements InstanceCreator<Exposable>
  19 +{
  20 + /**
  21 + * Minimum number of milliseconds that must elapse between writes
  22 + */
  23 + private static final long ANTI_HAMMER_DELAY = 1000L;
  24 +
  25 + /**
  26 + * Exposable instance which we will serialise exposed properties
  27 + */
  28 + private final Exposable exposable;
  29 +
  30 + /**
  31 + * JSON file to write to
  32 + */
  33 + private final File configFile;
  34 +
  35 + /**
  36 + * True if this is a versioned config strategy
  37 + */
  38 + private final boolean versioned;
  39 +
  40 + /**
  41 + * Gson instance
  42 + */
  43 + private final Gson gson;
  44 +
  45 + /**
  46 + * True if a config write has been requested but anti-hammer has prevented the write from occurring
  47 + */
  48 + private volatile boolean dirty = false;
  49 +
  50 + /**
  51 + * Last time the config was written, used for anti-hammer
  52 + */
  53 + private volatile long lastWrite = 0L;
  54 +
  55 + /**
  56 + * It's possible that writes may be requested from different threads, lock object to prevent cross-thread derp
  57 + */
  58 + private Object readWriteLock = new Object();
  59 +
  60 + /**
  61 + * @param exposable
  62 + * @param configFile
  63 + */
  64 + private ExposableConfigWriter(Exposable exposable, File configFile, boolean versioned)
  65 + {
  66 + this.exposable = exposable;
  67 + this.configFile = configFile;
  68 + this.versioned = versioned;
  69 +
  70 + GsonBuilder gsonBuilder = new GsonBuilder();
  71 + gsonBuilder.setPrettyPrinting();
  72 + gsonBuilder.serializeNulls();
  73 + gsonBuilder.excludeFieldsWithoutExposeAnnotation();
  74 + gsonBuilder.registerTypeAdapter(exposable.getClass(), this);
  75 +
  76 + this.gson = gsonBuilder.create();
  77 + }
  78 +
  79 + /**
  80 + * Get the config file underlying this writer
  81 + */
  82 + File getConfigFile()
  83 + {
  84 + return this.configFile;
  85 + }
  86 +
  87 + /**
  88 + * Returns true if this writer is using a versioned strategy
  89 + */
  90 + boolean isVersioned()
  91 + {
  92 + return this.versioned;
  93 + }
  94 +
  95 + /**
  96 + * Returns true if this writer has been invalidated but not yet been flushed to disk
  97 + */
  98 + boolean isDirty()
  99 + {
  100 + return this.dirty;
  101 + }
  102 +
  103 + /* (non-Javadoc)
  104 + * @see com.google.gson.InstanceCreator#createInstance(java.lang.reflect.Type)
  105 + */
  106 + @Override
  107 + public Exposable createInstance(Type type)
  108 + {
  109 + return this.exposable;
  110 + }
  111 +
  112 + /**
  113 + * Initialise the config, reads from file and writes the initial config file if not present
  114 + */
  115 + void init()
  116 + {
  117 + // Read the config
  118 + this.read();
  119 +
  120 + // If the config doesn't exist yet, seed the config
  121 + if (!this.configFile.exists())
  122 + {
  123 + this.write();
  124 + }
  125 + }
  126 +
  127 + /**
  128 + * Read the config from the file
  129 + */
  130 + void read()
  131 + {
  132 + synchronized (this.readWriteLock)
  133 + {
  134 + if (this.configFile.exists())
  135 + {
  136 + FileReader reader = null;
  137 +
  138 + try
  139 + {
  140 + reader = new FileReader(this.configFile);
  141 +
  142 + // Normally GSON would produce a new object by calling the default constructor, but we
  143 + // trick it into deserialising properties on the existing object instance by implementing
  144 + // an InstanceCreator which just returns the object instance which we already have
  145 + this.gson.fromJson(reader, this.exposable.getClass());
  146 + }
  147 + catch (Exception ex)
  148 + {
  149 + ex.printStackTrace();
  150 + }
  151 + finally
  152 + {
  153 + try
  154 + {
  155 + if (reader != null)
  156 + reader.close();
  157 + }
  158 + catch (IOException ex)
  159 + {
  160 + ex.printStackTrace();
  161 + }
  162 + }
  163 + }
  164 + }
  165 + }
  166 +
  167 + /**
  168 + * Write the config to the file
  169 + */
  170 + void write()
  171 + {
  172 + synchronized (this.readWriteLock)
  173 + {
  174 + FileWriter writer = null;
  175 + try
  176 + {
  177 + writer = new FileWriter(this.configFile);
  178 + this.gson.toJson(this.exposable, writer);
  179 +
  180 + this.dirty = false;
  181 + this.lastWrite = System.currentTimeMillis();
  182 + }
  183 + catch (Exception ex)
  184 + {
  185 + ex.printStackTrace();
  186 + }
  187 + finally
  188 + {
  189 + try
  190 + {
  191 + if (writer != null)
  192 + writer.close();
  193 + }
  194 + catch (IOException ex)
  195 + {
  196 + ex.printStackTrace();
  197 + }
  198 + }
  199 + }
  200 + }
  201 +
  202 + /**
  203 + * Write the config to file, respecting anti-hammer and queuing the write if not
  204 + * enough time has elapsed
  205 + */
  206 + void invalidate()
  207 + {
  208 + long sinceLastWrite = System.currentTimeMillis() - this.lastWrite;
  209 + if (sinceLastWrite < ANTI_HAMMER_DELAY)
  210 + {
  211 + this.dirty = true;
  212 + return;
  213 + }
  214 +
  215 + this.write();
  216 + }
  217 +
  218 + /**
  219 + * Handle latent writes if the config was previously invalidated
  220 + */
  221 + void onTick()
  222 + {
  223 + if (this.dirty)
  224 + {
  225 + long sinceLastWrite = System.currentTimeMillis() - this.lastWrite;
  226 + if (sinceLastWrite >= ANTI_HAMMER_DELAY)
  227 + {
  228 + this.write();
  229 + }
  230 + }
  231 + }
  232 +
  233 + /**
  234 + * Force a write if dirty
  235 + */
  236 + void sync()
  237 + {
  238 + if (this.dirty)
  239 + {
  240 + this.write();
  241 + }
  242 + }
  243 +
  244 + /**
  245 + * Factory method which creates and intialises a new ExposableConfigWriter for the specified exposable object and strategy
  246 + *
  247 + * @param exposable
  248 + * @param strategy
  249 + * @param fileName
  250 + * @return
  251 + */
  252 + static ExposableConfigWriter create(Exposable exposable, ConfigStrategy strategy, String fileName)
  253 + {
  254 + if (!fileName.toLowerCase().endsWith(".json"))
  255 + fileName = fileName + ".json";
  256 +
  257 + File configFile = strategy.getFileForStrategy(fileName);
  258 + ExposableConfigWriter writer = new ExposableConfigWriter(exposable, configFile, strategy == ConfigStrategy.Versioned);
  259 +
  260 + return writer;
  261 + }
  262 +}
0 \ No newline at end of file 263 \ No newline at end of file
java/com/mumfrey/liteloader/util/PrivateFields.java
@@ -144,6 +144,7 @@ public class PrivateFields&lt;P, T&gt; @@ -144,6 +144,7 @@ public class PrivateFields&lt;P, T&gt;
144 public static final PrivateFields<Minecraft, Timer> minecraftTimer = new PrivateFields<Minecraft, Timer> (Minecraft.class, "timer", "S", "field_71428_T"); // Minecraft/timer 144 public static final PrivateFields<Minecraft, Timer> minecraftTimer = new PrivateFields<Minecraft, Timer> (Minecraft.class, "timer", "S", "field_71428_T"); // Minecraft/timer
145 public static final PrivateFields<Minecraft, Profiler> minecraftProfiler = new PrivateFields<Minecraft, Profiler> (Minecraft.class, "mcProfiler", "C", "field_71424_I"); // Minecraft/mcProfiler 145 public static final PrivateFields<Minecraft, Profiler> minecraftProfiler = new PrivateFields<Minecraft, Profiler> (Minecraft.class, "mcProfiler", "C", "field_71424_I"); // Minecraft/mcProfiler
146 public static final PrivateFields<Minecraft, List<ResourcePack>> defaultResourcePacks = new PrivateFields<Minecraft, List<ResourcePack>> (Minecraft.class, "defaultResourcePacks", "aq", "field_110449_ao"); // Minecraft/defaultResourcePacks 146 public static final PrivateFields<Minecraft, List<ResourcePack>> defaultResourcePacks = new PrivateFields<Minecraft, List<ResourcePack>> (Minecraft.class, "defaultResourcePacks", "aq", "field_110449_ao"); // Minecraft/defaultResourcePacks
  147 + public static final PrivateFields<Minecraft, Boolean> gameIsRunning = new PrivateFields<Minecraft, Boolean> (Minecraft.class, "running", "D", "field_71425_J"); // Minecraft/running
147 public static final PrivateFields<RenderManager, Map> entityRenderMap = new PrivateFields<RenderManager, Map> (RenderManager.class, "entityRenderMap", "q", "field_78729_o"); // RenderManager/entityRenderMap 148 public static final PrivateFields<RenderManager, Map> entityRenderMap = new PrivateFields<RenderManager, Map> (RenderManager.class, "entityRenderMap", "q", "field_78729_o"); // RenderManager/entityRenderMap
148 public static final PrivateFields<GuiControls, GuiScreen> guiControlsParentScreen = new PrivateFields<GuiControls, GuiScreen> (GuiControls.class, "parentScreen", "b", "field_73909_b"); // GuiControls/parentScreen 149 public static final PrivateFields<GuiControls, GuiScreen> guiControlsParentScreen = new PrivateFields<GuiControls, GuiScreen> (GuiControls.class, "parentScreen", "b", "field_73909_b"); // GuiControls/parentScreen
149 public static final PrivateFields<PlayerUsageSnooper, IPlayerUsage> playerStatsCollector = new PrivateFields<PlayerUsageSnooper, IPlayerUsage>(PlayerUsageSnooper.class, "playerStatsCollector", "d", "field_76478_d"); // PlayerUsageSnooper/playerStatsCollector 150 public static final PrivateFields<PlayerUsageSnooper, IPlayerUsage> playerStatsCollector = new PrivateFields<PlayerUsageSnooper, IPlayerUsage>(PlayerUsageSnooper.class, "playerStatsCollector", "d", "field_76478_d"); // PlayerUsageSnooper/playerStatsCollector