Config.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. package com.gmail.nossr50.config;
  2. import com.gmail.nossr50.mcMMO;
  3. import com.google.common.io.Files;
  4. import com.google.common.reflect.TypeToken;
  5. import ninja.leaping.configurate.commented.CommentedConfigurationNode;
  6. import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
  7. import ninja.leaping.configurate.loader.ConfigurationLoader;
  8. import ninja.leaping.configurate.objectmapping.ObjectMappingException;
  9. import java.io.File;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.nio.file.Path;
  13. import java.nio.file.Paths;
  14. import java.util.List;
  15. /**
  16. * Handles loading and cacheing configuration settings from a configurable compatible config file
  17. */
  18. public abstract class Config implements VersionedConfig {
  19. public static final String HOCON_FILE_EXTENSION = ".conf";
  20. public final File DIRECTORY_DATA_FOLDER; //Directory that the file is in
  21. public final String FILE_RELATIVE_PATH; //Relative Path to the file
  22. protected final String DIRECTORY_DEFAULTS = "defaults";
  23. /* SETTINGS */
  24. //private static final String FILE_EXTENSION = ".conf"; //HOCON
  25. private boolean mergeNewKeys; //Whether or not to merge keys found in the default config
  26. private boolean removeOldKeys; //Whether or not to remove unused keys form the config
  27. /* PATH VARS */
  28. private boolean copyDefaults; //Whether or not to copy the default config when first creating the file
  29. private boolean generateDefaults; //Whether or not we use Configurate to generate a default file, if this is false we copy the file from the JAR
  30. private String fileName; //The file name of the config
  31. /* LOADERS */
  32. private HoconConfigurationLoader defaultCopyLoader;
  33. private HoconConfigurationLoader userCopyLoader;
  34. //private ConfigurationLoader<CommentedCommentedConfigurationNode> defaultCopyLoader;
  35. //private ConfigurationLoader<CommentedCommentedConfigurationNode> userCopyLoader;
  36. /* CONFIG FILES */
  37. private File resourceConfigCopy; //Copy of the default config from the JAR (file is copied so that admins can easily compare to defaults)
  38. private File resourceUserCopy; //File in the /$MCMMO_ROOT/mcMMO/ directory that may contain user edited settings
  39. /* ROOT NODES */
  40. private CommentedConfigurationNode userRootNode = null;
  41. private CommentedConfigurationNode defaultRootNode = null;
  42. /* CONFIG MANAGER */
  43. //private ConfigurationLoader<CommentedCommentedConfigurationNode> configManager;
  44. /*public Config(String pathToParentFolder, String relativePath, boolean mergeNewKeys, boolean copyDefaults, boolean removeOldKeys) {
  45. //TODO: Check if this works...
  46. this(new File(pathToParentFolder), relativePath, mergeNewKeys, copyDefaults, removeOldKeys);
  47. System.out.println("mcMMO Debug: Don't forget to check if loading config file by string instead of File works...");
  48. }*/
  49. public Config(String fileName, File pathToParentFolder, String relativePath, boolean generateDefaults, boolean mergeNewKeys, boolean copyDefaults, boolean removeOldKeys) {
  50. mkdirDefaults(); // Make our default config dir
  51. /*
  52. * These must be at the top
  53. */
  54. this.fileName = fileName;
  55. this.generateDefaults = generateDefaults;
  56. this.copyDefaults = copyDefaults;
  57. this.mergeNewKeys = mergeNewKeys; //Whether or not we add new keys when they are found
  58. this.removeOldKeys = removeOldKeys;
  59. DIRECTORY_DATA_FOLDER = pathToParentFolder; //Data Folder for our plugin
  60. FILE_RELATIVE_PATH = relativePath + fileName + HOCON_FILE_EXTENSION; //Relative path to config from a parent folder
  61. }
  62. public void initFullConfig() {
  63. //Attempt IO Operations
  64. try {
  65. //Makes sure we have valid Files corresponding to this config
  66. initConfigFiles();
  67. //Init MainConfig Loaders
  68. initConfigLoaders();
  69. //Load MainConfig Nodes
  70. loadConfig();
  71. //Attempt to update user file, and then load it into memory
  72. readConfig();
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. }
  76. //Cleanup and backup registers
  77. registerFileBackup();
  78. }
  79. /**
  80. * Registers with the config managers file list
  81. * Used for backing up configs with our zip library
  82. */
  83. private void registerFileBackup() {
  84. mcMMO.getConfigManager().registerUserFile(getUserConfigFile());
  85. }
  86. /**
  87. * Initializes the default copy File and the user config File
  88. *
  89. * @throws IOException
  90. */
  91. private void initConfigFiles() throws IOException {
  92. //Init our config copy
  93. resourceConfigCopy = initDefaultConfig();
  94. //Init the user file
  95. resourceUserCopy = initUserConfig();
  96. }
  97. /**
  98. * Loads the root node for the default config File and user config File
  99. */
  100. private void loadConfig() {
  101. try {
  102. final CommentedConfigurationNode defaultConfig = this.defaultCopyLoader.load();
  103. defaultRootNode = defaultConfig;
  104. final CommentedConfigurationNode userConfig = this.userCopyLoader.load();
  105. userRootNode = userConfig;
  106. } catch (IOException e) {
  107. e.printStackTrace();
  108. }
  109. }
  110. /**
  111. * Initializes the Configuration Loaders for this config
  112. */
  113. private void initConfigLoaders() {
  114. this.defaultCopyLoader = HoconConfigurationLoader.builder().setPath(resourceConfigCopy.toPath()).build();
  115. this.userCopyLoader = HoconConfigurationLoader.builder().setPath(resourceUserCopy.toPath()).build();
  116. }
  117. /**
  118. * Copies a new file from the JAR to the defaults directory and uses that new file to initialize our resourceConfigCopy
  119. *
  120. * @throws IOException
  121. * @see Config#resourceConfigCopy
  122. */
  123. private File initDefaultConfig() throws IOException {
  124. if (generateDefaults) {
  125. return generateDefaultFile();
  126. } else
  127. return copyDefaultFromJar(getDefaultConfigCopyRelativePath(), true);
  128. }
  129. /**
  130. * Generates a default config file using the Configurate library, makes use of @Setting and @ConfigSerializable annotations in the config file
  131. * Assigns the default root node to the newly loaded default config if successful
  132. *
  133. * @return the File for the newly created config
  134. */
  135. private File generateDefaultFile() {
  136. mcMMO.p.getLogger().info("Attempting to create a default config for " + fileName);
  137. //Not sure if this will work properly...
  138. Path potentialFile = Paths.get(getDefaultConfigCopyRelativePath());
  139. ConfigurationLoader<CommentedConfigurationNode> generation_loader
  140. = HoconConfigurationLoader.builder().setPath(potentialFile).build();
  141. try {
  142. mcMMO.p.getLogger().info("Config File Full Path: " + getDefaultConfigFile().getAbsolutePath());
  143. //Delete any existing default config
  144. if (getDefaultConfigFile().exists())
  145. getDefaultConfigFile().delete();
  146. //Make new file
  147. getDefaultConfigFile().createNewFile();
  148. //Load the config
  149. defaultRootNode = generation_loader.load();
  150. //Save to a new file
  151. generation_loader.save(defaultRootNode);
  152. mcMMO.p.getLogger().info("Generated a default file for " + fileName);
  153. } catch (IOException e) {
  154. mcMMO.p.getLogger().severe("Error when trying to generate a default configuration file for " + getDefaultConfigCopyRelativePath());
  155. e.printStackTrace();
  156. }
  157. //Return the default file
  158. return getDefaultConfigFile();
  159. }
  160. /**
  161. * Attemps to load the config file if it exists, if it doesn't it copies a new one from within the JAR
  162. *
  163. * @return user config File
  164. * @throws IOException
  165. * @see Config#resourceUserCopy
  166. */
  167. private File initUserConfig() throws IOException {
  168. File userCopy = new File(DIRECTORY_DATA_FOLDER, FILE_RELATIVE_PATH); //Load the user file;
  169. if (userCopy.exists()) {
  170. // Yay
  171. return userCopy;
  172. } else {
  173. //If it's gone we copy default files
  174. //Note that we don't copy the values from the default copy put in /defaults/ that file exists only as a reference to admins and is unreliable
  175. if (copyDefaults)
  176. return copyDefaultFromJar(FILE_RELATIVE_PATH, false);
  177. else {
  178. //Make a new empty file
  179. userCopy.createNewFile();
  180. return userCopy;
  181. }
  182. }
  183. }
  184. /**
  185. * Gets the File representation of the this users config
  186. *
  187. * @return the users config File
  188. */
  189. public File getUserConfigFile() {
  190. return new File(DIRECTORY_DATA_FOLDER, FILE_RELATIVE_PATH);
  191. }
  192. /**
  193. * Used to make a new config file at a specified relative output path inside the data directory by copying the matching file found in that same relative path within the JAR
  194. *
  195. * @param relativeOutputPath the path to the output file
  196. * @param deleteOld whether or not to delete the existing output file on disk
  197. * @return a copy of the default config within the JAR
  198. * @throws IOException
  199. */
  200. private File copyDefaultFromJar(String relativeOutputPath, boolean deleteOld) throws IOException {
  201. /*
  202. * Gen a Default config from inside the JAR
  203. */
  204. mcMMO.p.getLogger().info("Preparing to copy internal resource file (in JAR) - " + FILE_RELATIVE_PATH);
  205. //InputStream inputStream = McmmoCore.getResource(FILE_RELATIVE_PATH);
  206. InputStream inputStream = mcMMO.p.getResource(FILE_RELATIVE_PATH);
  207. byte[] buffer = new byte[inputStream.available()];
  208. inputStream.read(buffer);
  209. //This is a copy of the default file, which we will overwrite every time mcMMO loads
  210. File targetFile = new File(DIRECTORY_DATA_FOLDER, relativeOutputPath);
  211. //Wipe old default file on disk
  212. if (targetFile.exists() && deleteOld) {
  213. mcMMO.p.getLogger().info("Updating file " + relativeOutputPath);
  214. targetFile.delete(); //Necessary?
  215. }
  216. if (!targetFile.exists()) {
  217. targetFile.getParentFile().mkdirs();
  218. targetFile.createNewFile(); //New File Boys
  219. }
  220. Files.write(buffer, targetFile);
  221. mcMMO.p.getLogger().info("Created config file - " + relativeOutputPath);
  222. inputStream.close(); //Close the input stream
  223. return targetFile;
  224. }
  225. /**
  226. * The path to the defaults directory
  227. *
  228. * @return the path to the defaults directory
  229. */
  230. private String getDefaultConfigCopyRelativePath() {
  231. return getDefaultConfigFile().getPath();
  232. }
  233. /**
  234. * Grabs the File representation of the default config, which is stored on disk in a defaults folder
  235. * this file will be overwritten every time mcMMO starts to keep it up to date.
  236. *
  237. * @return the copy of the default config file, stored in the defaults directory
  238. */
  239. private File getDefaultConfigFile() {
  240. return new File(ConfigConstants.getDefaultsFolder(), FILE_RELATIVE_PATH);
  241. }
  242. /**
  243. * Creates the defaults directory
  244. */
  245. private void mkdirDefaults() {
  246. ConfigConstants.makeAllConfigDirectories();
  247. }
  248. /**
  249. * Configs are versioned based on when they had significant changes to keys
  250. *
  251. * @return current MainConfig Version String
  252. */
  253. public String getVersion() {
  254. return String.valueOf(getConfigVersion());
  255. }
  256. /**
  257. * Attempts to read the loaded config file
  258. * MainConfig will have any necessary updates applied
  259. * MainConfig will be compared to the default config to see if it is missing any nodes
  260. * MainConfig will have any missing nodes inserted with their default value
  261. */
  262. public void readConfig() {
  263. mcMMO.p.getLogger().info("Attempting to read " + FILE_RELATIVE_PATH + ".");
  264. int version = this.userRootNode.getNode("ConfigVersion").getInt();
  265. mcMMO.p.getLogger().info(FILE_RELATIVE_PATH + " version is " + version);
  266. //Update our config
  267. updateConfig();
  268. }
  269. /**
  270. * Compares the users config file to the default and adds any missing nodes and applies any necessary updates
  271. */
  272. private void updateConfig() {
  273. mcMMO.p.getLogger().info(defaultRootNode.getChildrenMap().size() + " items in default children map");
  274. mcMMO.p.getLogger().info(userRootNode.getChildrenMap().size() + " items in default root map");
  275. // Merge Values from default
  276. if (mergeNewKeys)
  277. userRootNode = userRootNode.mergeValuesFrom(defaultRootNode);
  278. removeOldKeys();
  279. // Update config version
  280. updateConfigVersion();
  281. //Attempt to save
  282. try {
  283. saveUserCopy();
  284. } catch (IOException e) {
  285. e.printStackTrace();
  286. }
  287. }
  288. /**
  289. * Finds any keys in the users config that are not present in the default config and removes them
  290. */
  291. //TODO: Finish this
  292. private void removeOldKeys() {
  293. if (!removeOldKeys)
  294. return;
  295. for (CommentedConfigurationNode CommentedConfigurationNode : defaultRootNode.getChildrenList()) {
  296. }
  297. }
  298. /**
  299. * Saves the current state information of the config to the users copy (which they may edit)
  300. *
  301. * @throws IOException
  302. */
  303. private void saveUserCopy() throws IOException {
  304. mcMMO.p.getLogger().info("Saving new node");
  305. userCopyLoader.save(userRootNode);
  306. }
  307. /**
  308. * Performs any necessary operations to update this config
  309. */
  310. private void updateConfigVersion() {
  311. // Set a version for our config
  312. this.userRootNode.getNode("ConfigVersion").setValue(getConfigVersion());
  313. mcMMO.p.getLogger().info("Updated config to [" + getConfigVersion() + "] - " + FILE_RELATIVE_PATH);
  314. }
  315. /**
  316. * Returns the root node of this config
  317. *
  318. * @return the root node of this config
  319. */
  320. protected CommentedConfigurationNode getUserRootNode() {
  321. return userRootNode;
  322. }
  323. /**
  324. * Gets an int from the config and casts it to short before returning
  325. *
  326. * @param path the path to the int
  327. * @return the value of the int after being cast to short at the node, null references will zero initialize
  328. */
  329. public short getShortValue(String... path) {
  330. return (short) userRootNode.getNode(path).getInt();
  331. }
  332. /**
  333. * Grabs an int from the specified node
  334. *
  335. * @param path
  336. * @return the int from the node, null references will zero initialize
  337. */
  338. public int getIntValue(String... path) {
  339. return userRootNode.getNode(path).getInt();
  340. }
  341. /**
  342. * Grabs a double from the specified node
  343. *
  344. * @param path
  345. * @return the double from the node, null references will zero initialize
  346. */
  347. public double getDoubleValue(String... path) {
  348. return userRootNode.getNode(path).getDouble();
  349. }
  350. /**
  351. * Grabs a long from the specified node
  352. *
  353. * @param path
  354. * @return the long from the node, null references will zero initialize
  355. */
  356. public long getLongValue(String... path) {
  357. return userRootNode.getNode(path).getLong();
  358. }
  359. /**
  360. * Grabs a boolean from the specified node
  361. *
  362. * @param path
  363. * @return the boolean from the node, null references will zero initialize
  364. */
  365. public boolean getBooleanValue(String... path) {
  366. return userRootNode.getNode(path).getBoolean();
  367. }
  368. /**
  369. * Grabs a string from the specified node
  370. *
  371. * @param path
  372. * @return the string from the node, null references will zero initialize
  373. */
  374. public String getStringValue(String... path) {
  375. return userRootNode.getNode(path).getString();
  376. }
  377. /**
  378. * Checks to see if a node exists in the user's config file
  379. *
  380. * @param path path to the node
  381. * @return true if the node exists
  382. */
  383. public boolean hasNode(String... path) {
  384. return (userRootNode.getNode(path) != null);
  385. }
  386. /**
  387. * Returns the children of a specific node
  388. *
  389. * @param path the path to the parent node
  390. * @return the list of children for the target parent node
  391. */
  392. public List<? extends CommentedConfigurationNode> getChildren(String... path) {
  393. return userRootNode.getNode(path).getChildrenList();
  394. }
  395. public List<String> getListFromNode(String... path) throws ObjectMappingException {
  396. return userRootNode.getNode(path).getList(TypeToken.of(String.class));
  397. }
  398. }