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 2  
3 3 import java.io.File;
4 4  
  5 +import com.mumfrey.liteloader.modconfig.Exposable;
  6 +
5 7 /**
6 8 * Base interface for mods
7 9 *
8 10 * @author Adam Mummery-Smith
9 11 */
10   -public interface LiteMod
  12 +public interface LiteMod extends Exposable
11 13 {
12 14 /**
13 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 40 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderMods;
41 41 import com.mumfrey.liteloader.gui.GuiControlsPaginated;
42 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 45 import com.mumfrey.liteloader.permissions.PermissionsManagerClient;
45 46 import com.mumfrey.liteloader.util.PrivateFields;
46 47  
... ... @@ -170,9 +171,9 @@ public final class LiteLoader
170 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 179 * Flag which keeps track of whether late initialisation has been done
... ... @@ -279,7 +280,7 @@ public final class LiteLoader
279 280 this.enabledModsList = EnabledModsList.createFrom(this.enabledModsFile);
280 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 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 822 * Create mod instances from the enumerated classes
791 823 *
792 824 * @param modsToLoad List of mods to load
... ... @@ -898,6 +930,9 @@ public final class LiteLoader
898 930 {
899 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 936 try
902 937 {
903 938 this.handleModVersionUpgrade(mod);
... ... @@ -907,6 +942,9 @@ public final class LiteLoader
907 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 948 // initialise the mod
911 949 mod.init(this.commonConfigFolder);
912 950  
... ... @@ -916,9 +954,6 @@ public final class LiteLoader
916 954 // add mod to permissions manager if permissible
917 955 this.permissionsManager.registerMod(mod);
918 956  
919   - // register mod config panel if configurable
920   - this.configPanelManager.registerMod(mod);
921   -
922 957 this.loadedMods.add(mod);
923 958 this.loadedModsList += String.format("\n - %s version %s", mod.getName(), mod.getVersion());
924 959 }
... ... @@ -934,6 +969,11 @@ public final class LiteLoader
934 969 if (LiteLoaderBootstrap.VERSION.getLoaderRevision() > lastModVersion.getLoaderRevision())
935 970 {
936 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 977 mod.upgradeSettings(LiteLoaderBootstrap.VERSION.getMinecraftVersion(), this.versionConfigFolder, this.inflectVersionedConfigPath(lastModVersion));
938 978  
939 979 this.bootstrap.storeLastKnownModRevision(modKey);
... ... @@ -1038,7 +1078,7 @@ public final class LiteLoader
1038 1078 // If we're at the main menu, prepare the overlay
1039 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 1084 this.modInfoScreen.drawScreen(mouseX, mouseY, partialTicks);
... ... @@ -1051,7 +1091,7 @@ public final class LiteLoader
1051 1091 }
1052 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 1104 // Tick the permissions manager
1065 1105 this.permissionsManager.onTick(this.minecraft, partialTicks, inGame);
1066 1106  
  1107 + // Tick the config manager
  1108 + this.configManager.onTick();
  1109 +
1067 1110 this.checkAndStoreKeyBindings();
1068 1111  
1069 1112 if (this.modInfoScreen != null && this.minecraft.currentScreen != this.modInfoScreen)
1070 1113 {
1071 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 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 20 import com.mumfrey.liteloader.core.LiteLoader;
21 21 import com.mumfrey.liteloader.core.ModFile;
22 22 import com.mumfrey.liteloader.modconfig.ConfigPanel;
23   -import com.mumfrey.liteloader.modconfig.ConfigPanelManager;
  23 +import com.mumfrey.liteloader.modconfig.ConfigManager;
24 24  
25 25 import net.minecraft.src.DynamicTexture;
26 26 import net.minecraft.src.GuiButton;
... ... @@ -136,7 +136,7 @@ public class GuiScreenModInfo extends GuiScreen
136 136 */
137 137 private GuiCheckbox chkEnabled;
138 138  
139   - private ConfigPanelManager configPanelManager;
  139 + private ConfigManager configManager;
140 140  
141 141 /**
142 142 * Configuration panel
... ... @@ -149,11 +149,11 @@ public class GuiScreenModInfo extends GuiScreen
149 149 * @param loader
150 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 154 this.mc = minecraft;
155 155 this.mainMenu = mainMenu;
156   - this.configPanelManager = configPanelManager;
  156 + this.configManager = configManager;
157 157  
158 158 // Spawn the texture resource if we haven't already
159 159 if (aboutTexture == null)
... ... @@ -484,7 +484,7 @@ public class GuiScreenModInfo extends GuiScreen
484 484 this.btnToggle.drawButton = true;
485 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 679 {
680 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 683 if (panel != null)
684 684 {
685 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 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 144 public static final PrivateFields<Minecraft, Timer> minecraftTimer = new PrivateFields<Minecraft, Timer> (Minecraft.class, "timer", "S", "field_71428_T"); // Minecraft/timer
145 145 public static final PrivateFields<Minecraft, Profiler> minecraftProfiler = new PrivateFields<Minecraft, Profiler> (Minecraft.class, "mcProfiler", "C", "field_71424_I"); // Minecraft/mcProfiler
146 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 148 public static final PrivateFields<RenderManager, Map> entityRenderMap = new PrivateFields<RenderManager, Map> (RenderManager.class, "entityRenderMap", "q", "field_78729_o"); // RenderManager/entityRenderMap
148 149 public static final PrivateFields<GuiControls, GuiScreen> guiControlsParentScreen = new PrivateFields<GuiControls, GuiScreen> (GuiControls.class, "parentScreen", "b", "field_73909_b"); // GuiControls/parentScreen
149 150 public static final PrivateFields<PlayerUsageSnooper, IPlayerUsage> playerStatsCollector = new PrivateFields<PlayerUsageSnooper, IPlayerUsage>(PlayerUsageSnooper.class, "playerStatsCollector", "d", "field_76478_d"); // PlayerUsageSnooper/playerStatsCollector
... ...