Commit 09ab166d80d1cd803a1ff7454f3ac2975af0d25b

Authored by Mumfrey
2 parents 434901f7 21dc1eac

Merge branch '1.11.2-backport' into 1.11.2

Showing 28 changed files with 1433 additions and 440 deletions
build.gradle
... ... @@ -72,7 +72,7 @@ repositories {
72 72 }
73 73  
74 74 dependencies {
75   - compile('org.spongepowered:mixin:0.7.1-SNAPSHOT') {
  75 + compile('org.spongepowered:mixin:0.7.3-SNAPSHOT') {
76 76 exclude module: 'asm-commons'
77 77 exclude module: 'asm-tree'
78 78 exclude module: 'launchwrapper'
... ...
src/client/java/com/mumfrey/liteloader/client/gui/GuiPanelLiteLoaderLog.java
... ... @@ -290,6 +290,9 @@ class GuiPanelLiteLoaderLog extends GuiPanel implements ScrollPanelContent
290 290 if (logLine.startsWith("tweakClass '")) return 0x5555FF;
291 291 if (logLine.startsWith("baking listener list")) return 0x00AAAA;
292 292 if (logLine.startsWith("generating new event handler")) return 0xFFFF55;
  293 + if (logLine.startsWith("resolving mods")) return 0xFFAA00;
  294 + if (logLine.startsWith("resolved artefact")) return 0x00AA00;
  295 + if (logLine.startsWith("evicting")) return 0xFFAA00;
293 296  
294 297 return 0xCCCCCC;
295 298 }
... ...
src/client/java/com/mumfrey/liteloader/modconfig/AbstractConfigPanel.java
... ... @@ -105,7 +105,7 @@ public abstract class AbstractConfigPanel implements ConfigPanel
105 105 /**
106 106 * Base for config option handle structs
107 107 */
108   - static abstract class ConfigOption
  108 + abstract static class ConfigOption
109 109 {
110 110 void onTick()
111 111 {
... ... @@ -303,7 +303,7 @@ public abstract class AbstractConfigPanel implements ConfigPanel
303 303 @Override
304 304 public ConfigTextField setRegex(String regex, boolean force)
305 305 {
306   - this.textField.setRegex(Pattern.compile(regex), force);;
  306 + this.textField.setRegex(Pattern.compile(regex), force);
307 307 return this;
308 308 }
309 309  
... ... @@ -343,7 +343,8 @@ public abstract class AbstractConfigPanel implements ConfigPanel
343 343 }
344 344 else
345 345 {
346   - this.setValidator((text) -> {
  346 + this.setValidator((text) ->
  347 + {
347 348 this.validate(text);
348 349 return true;
349 350 });
... ...
src/main/java/com/mumfrey/liteloader/api/EnumeratorModule.java
... ... @@ -36,10 +36,7 @@ public interface EnumeratorModule
36 36 public abstract void writeSettings(LoaderEnvironment environment, LoaderProperties properties);
37 37  
38 38 /**
39   - * Find loadable mods in this enumerator's domain, the enumerator module
40   - * should call back against the enumerator itself to register containers it
41   - * discovers using the registerModContainer() and registerTweakContainer()
42   - * callbacks.
  39 + * Find loadable mods in this enumerator's domain.
43 40 *
44 41 * <p>This method is called during loader PREINIT phase so <b>do not use any
45 42 * game classes here</b>!</p>
... ... @@ -48,6 +45,20 @@ public interface EnumeratorModule
48 45 * @param profile
49 46 */
50 47 public abstract void enumerate(ModularEnumerator enumerator, String profile);
  48 +
  49 + /**
  50 + * Register loadable mods discovered in this enumerator's domain during the
  51 + * call to {@link #enumerate}, the enumerator module should call back
  52 + * against the enumerator itself to register containers it discovers using
  53 + * the registerModContainer() and registerTweakContainer() callbacks.
  54 + *
  55 + * <p>This method is called during loader PREINIT phase so <b>do not use any
  56 + * game classes here</b>!</p>
  57 + *
  58 + * @param enumerator
  59 + * @param profile
  60 + */
  61 + public abstract void register(ModularEnumerator enumerator, String profile);
51 62  
52 63 /**
53 64 * The enumerator module should inject (as required) any discovered
... ...
src/main/java/com/mumfrey/liteloader/core/EnabledModsList.java
... ... @@ -8,7 +8,6 @@ package com.mumfrey.liteloader.core;
8 8 import java.io.File;
9 9 import java.io.FileReader;
10 10 import java.io.FileWriter;
11   -import java.io.IOException;
12 11 import java.util.List;
13 12 import java.util.Map;
14 13 import java.util.TreeMap;
... ... @@ -186,12 +185,9 @@ public final class EnabledModsList
186 185 {
187 186 if (file.exists())
188 187 {
189   - FileReader reader = null;
190   -
191   - try
  188 + try (FileReader reader = new FileReader(file))
192 189 {
193   - reader = new FileReader(file);
194   - EnabledModsList instance = gson.fromJson(reader, EnabledModsList.class);
  190 + EnabledModsList instance = EnabledModsList.gson.fromJson(reader, EnabledModsList.class);
195 191 instance.setEnabledModsFile(file);
196 192 return instance;
197 193 }
... ... @@ -199,20 +195,6 @@ public final class EnabledModsList
199 195 {
200 196 ex.printStackTrace();
201 197 }
202   - finally
203   - {
204   - try
205   - {
206   - if (reader != null)
207   - {
208   - reader.close();
209   - }
210   - }
211   - catch (IOException ex)
212   - {
213   - ex.printStackTrace();
214   - }
215   - }
216 198 }
217 199  
218 200 EnabledModsList instance = new EnabledModsList();
... ... @@ -229,31 +211,14 @@ public final class EnabledModsList
229 211 {
230 212 if (!this.allowSave) return;
231 213  
232   - FileWriter writer = null;
233   -
234   - try
  214 + try (FileWriter writer = new FileWriter(file))
235 215 {
236   - writer = new FileWriter(file);
237   - gson.toJson(this, writer);
  216 + EnabledModsList.gson.toJson(this, writer);
238 217 }
239 218 catch (Exception ex)
240 219 {
241 220 ex.printStackTrace();
242 221 }
243   - finally
244   - {
245   - try
246   - {
247   - if (writer != null)
248   - {
249   - writer.close();
250   - }
251   - }
252   - catch (IOException ex)
253   - {
254   - ex.printStackTrace();
255   - }
256   - }
257 222 }
258 223  
259 224 /**
... ...
src/main/java/com/mumfrey/liteloader/core/LiteLoaderBootstrap.java
... ... @@ -29,6 +29,7 @@ import com.mumfrey.liteloader.api.manager.APIProvider;
29 29 import com.mumfrey.liteloader.api.manager.APIRegistry;
30 30 import com.mumfrey.liteloader.common.LoadingProgress;
31 31 import com.mumfrey.liteloader.core.api.LiteLoaderCoreAPI;
  32 +import com.mumfrey.liteloader.core.api.repository.Repository;
32 33 import com.mumfrey.liteloader.interfaces.LoaderEnumerator;
33 34 import com.mumfrey.liteloader.launch.ClassTransformerManager;
34 35 import com.mumfrey.liteloader.launch.LiteLoaderTweaker;
... ... @@ -102,6 +103,16 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
102 103 * Folder containing version-specific configuration
103 104 */
104 105 private final File versionConfigFolder;
  106 +
  107 + /**
  108 + * Mods repo file
  109 + */
  110 + private final String repositoryFile;
  111 +
  112 + /**
  113 + * Mod repository defined in JSON
  114 + */
  115 + private final Repository repository;
105 116  
106 117 /**
107 118 * File to write log entries to
... ... @@ -138,6 +149,8 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
138 149  
139 150 private LaunchClassLoader classLoader;
140 151  
  152 + private final StartupEnvironment env;
  153 +
141 154 private final ITweaker tweaker;
142 155  
143 156 private final APIRegistry apiRegistry;
... ... @@ -157,7 +170,7 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
157 170 * List of mods passed into the command line
158 171 */
159 172 private EnabledModsList enabledModsList;
160   -
  173 +
161 174 /**
162 175 * @param env
163 176 * @param tweaker
... ... @@ -165,6 +178,7 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
165 178 public LiteLoaderBootstrap(StartupEnvironment env, ITweaker tweaker)
166 179 {
167 180 this.environmentType = EnvironmentType.values()[env.getEnvironmentTypeId()];
  181 + this.env = env;
168 182 this.tweaker = tweaker;
169 183  
170 184 this.apiRegistry = new APIRegistry(this.getEnvironment(), this.getProperties());
... ... @@ -173,6 +187,7 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
173 187 this.assetsDirectory = env.getAssetsDirectory();
174 188 this.profile = env.getProfile();
175 189 this.modsFolder = env.getModsFolder();
  190 + this.repositoryFile = env.getModsRepoFile();
176 191  
177 192 this.versionedModsFolder = new File(this.modsFolder, LiteLoaderVersion.CURRENT.getMinecraftVersion());
178 193 this.configBaseFolder = new File(this.gameDirectory, "liteconfig");
... ... @@ -183,17 +198,27 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
183 198 this.commonConfigFolder = new File(this.configBaseFolder, "common");
184 199 this.versionConfigFolder = this.inflectVersionedConfigPath(LiteLoaderVersion.CURRENT);
185 200  
186   - if (!this.modsFolder.exists()) this.modsFolder.mkdirs();
187   - if (!this.versionedModsFolder.exists()) this.versionedModsFolder.mkdirs();
188   - if (!this.configBaseFolder.exists()) this.configBaseFolder.mkdirs();
189   - if (!this.commonConfigFolder.exists()) this.commonConfigFolder.mkdirs();
190   - if (!this.versionConfigFolder.exists()) this.versionConfigFolder.mkdirs();
  201 + this.repository = new Repository(this.gameDirectory, this.versionedModsFolder);
  202 +
  203 + this.mkdir(this.modsFolder);
  204 + this.mkdir(this.versionedModsFolder);
  205 + this.mkdir(this.configBaseFolder);
  206 + this.mkdir(this.commonConfigFolder);
  207 + this.mkdir(this.versionConfigFolder);
191 208  
192 209 this.initAPIs(env.getAPIsToLoad());
193 210 this.apiProvider = this.apiRegistry.getProvider();
194 211 this.apiAdapter = this.apiRegistry.getAdapter();
195 212 }
196 213  
  214 + private void mkdir(File dir)
  215 + {
  216 + if (!dir.isDirectory())
  217 + {
  218 + dir.mkdirs();
  219 + }
  220 + }
  221 +
197 222 /**
198 223 * @param version
199 224 */
... ... @@ -249,6 +274,12 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
249 274 {
250 275 return this.enabledModsList;
251 276 }
  277 +
  278 + @Override
  279 + public Repository getModRepository()
  280 + {
  281 + return this.repository;
  282 + }
252 283  
253 284 @Override
254 285 public LoaderEnumerator getEnumerator()
... ... @@ -301,15 +332,20 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
301 332 * #preInit(net.minecraft.launchwrapper.LaunchClassLoader, boolean)
302 333 */
303 334 @Override
304   - public void preInit(LaunchClassLoader classLoader, boolean loadTweaks, List<String> modsToLoad)
  335 + public void preInit(LaunchClassLoader classLoader, boolean loadTweaks)
305 336 {
  337 + List<String> modsToLoad = this.env.getModFilterList();
  338 +
306 339 this.classLoader = classLoader;
307 340 this.loadTweaks = loadTweaks;
308 341  
309 342 LiteLoaderLogger.info(Verbosity.REDUCED, "LiteLoader begin PREINIT...");
310 343  
311 344 // Set up the bootstrap
312   - if (!this.prepare()) return;
  345 + if (!this.prepare())
  346 + {
  347 + return;
  348 + }
313 349  
314 350 LiteLoaderLogger.info(Verbosity.REDUCED, "LiteLoader %s starting up...", LiteLoaderVersion.CURRENT.getLoaderVersion());
315 351  
... ... @@ -323,7 +359,7 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
323 359  
324 360 this.enabledModsList = EnabledModsList.createFrom(this.enabledModsFile);
325 361 this.enabledModsList.processModsList(this.profile, modsToLoad);
326   -
  362 +
327 363 this.enumerator = this.spawnEnumerator(classLoader);
328 364 this.enumerator.onPreInit();
329 365  
... ... @@ -599,6 +635,16 @@ class LiteLoaderBootstrap implements LoaderBootstrap, LoaderEnvironment, LoaderP
599 635 {
600 636 return this.versionConfigFolder;
601 637 }
  638 +
  639 + /**
  640 + * Get the path to a JSON file describing a mod repository layout, can be
  641 + * null if not defined
  642 + */
  643 + @Override
  644 + public String getModsRepoFile()
  645 + {
  646 + return this.repositoryFile;
  647 + }
602 648  
603 649 /**
604 650 * Get a boolean propery from the properties file and also write the new
... ...
src/main/java/com/mumfrey/liteloader/core/LiteLoaderEnumerator.java
... ... @@ -122,7 +122,7 @@ public class LiteLoaderEnumerator implements LoaderEnumerator
122 122 private final FastIterableDeque<EnumerationObserver> observers = new HandlerList<EnumerationObserver>(EnumerationObserver.class);
123 123  
124 124 protected EnumeratorState state = EnumeratorState.INIT;
125   -
  125 +
126 126 /**
127 127 * @param environment
128 128 * @param properties
... ... @@ -144,7 +144,7 @@ public class LiteLoaderEnumerator implements LoaderEnumerator
144 144 // Initialise the shared mod list if we haven't already
145 145 this.getSharedModList();
146 146 }
147   -
  147 +
148 148 /**
149 149 * @param environment
150 150 */
... ... @@ -397,11 +397,13 @@ public class LiteLoaderEnumerator implements LoaderEnumerator
397 397 {
398 398 this.gotoState(EnumeratorState.DISCOVER);
399 399  
  400 + String profile = this.environment.getProfile();
  401 +
400 402 for (EnumeratorModule module : this.modules)
401 403 {
402 404 try
403 405 {
404   - module.enumerate(this, this.environment.getProfile());
  406 + module.enumerate(this, profile);
405 407 }
406 408 catch (Throwable th)
407 409 {
... ... @@ -409,6 +411,18 @@ public class LiteLoaderEnumerator implements LoaderEnumerator
409 411 }
410 412 }
411 413  
  414 + for (EnumeratorModule module : this.modules)
  415 + {
  416 + try
  417 + {
  418 + module.register(this, profile);
  419 + }
  420 + catch (Throwable th)
  421 + {
  422 + LiteLoaderLogger.warning(th, "Enumerator Module %s encountered an error whilst enumerating", module.getClass().getName());
  423 + }
  424 + }
  425 +
412 426 this.checkDependencies();
413 427 }
414 428  
... ... @@ -651,7 +665,15 @@ public class LiteLoaderEnumerator implements LoaderEnumerator
651 665 if (config.endsWith(".json"))
652 666 {
653 667 LiteLoaderLogger.info(Verbosity.REDUCED, "Registering mixin config %s for %s", config, container.getName());
654   - Mixins.addConfiguration(config);
  668 + try
  669 + {
  670 + Mixins.addConfiguration(config);
  671 + }
  672 + catch (Throwable th)
  673 + {
  674 + LiteLoaderLogger.severe(th, "Error registering mixin config %s for %s", config, container);
  675 + container.registerMixinError(th);
  676 + }
655 677 }
656 678 else if (config.contains(".json@"))
657 679 {
... ...
src/main/java/com/mumfrey/liteloader/core/LiteLoaderMods.java
... ... @@ -24,6 +24,7 @@ import com.mumfrey.liteloader.interfaces.FastIterableDeque;
24 24 import com.mumfrey.liteloader.interfaces.Loadable;
25 25 import com.mumfrey.liteloader.interfaces.LoadableMod;
26 26 import com.mumfrey.liteloader.interfaces.LoaderEnumerator;
  27 +import com.mumfrey.liteloader.interfaces.MixinContainer;
27 28 import com.mumfrey.liteloader.interfaces.TweakContainer;
28 29 import com.mumfrey.liteloader.launch.ClassTransformerManager;
29 30 import com.mumfrey.liteloader.launch.LoaderEnvironment;
... ... @@ -444,6 +445,12 @@ public class LiteLoaderMods
444 445 String identifier = mod.getIdentifier();
445 446 if (identifier == null || this.environment.getEnabledModsList().isEnabled(this.environment.getProfile(), identifier))
446 447 {
  448 + if (!this.validateMixins(mod, container))
  449 + {
  450 + this.onModLoadFailed(container, identifier, "mixins for the specified mod encountered a startup error", null);
  451 + continue;
  452 + }
  453 +
447 454 if (!this.enumerator.checkDependencies(container))
448 455 {
449 456 this.onModLoadFailed(container, identifier, "the mod was missing a required dependency", null);
... ... @@ -474,6 +481,23 @@ public class LiteLoaderMods
474 481 this.observers.all().onPostModLoaded(mod);
475 482 }
476 483 }
  484 +
  485 + private boolean validateMixins(ModInfo<?> mod, LoadableMod<?> container)
  486 + {
  487 + if (container instanceof MixinContainer)
  488 + {
  489 + @SuppressWarnings("unchecked")
  490 + Collection<Throwable> errors = ((MixinContainer<File>)container).getMixinErrors();
  491 + for (Throwable error : errors)
  492 + {
  493 + this.registerModStartupError(mod, error, true);
  494 + }
  495 +
  496 + return errors.size() == 0;
  497 + }
  498 +
  499 + return true;
  500 + }
477 501  
478 502 /**
479 503 * @param identifier
... ... @@ -697,26 +721,28 @@ public class LiteLoaderMods
697 721  
698 722 for (Mod mod : this.loadedMods)
699 723 {
700   - if (mod.hasClassTransformers())
  724 + if (!mod.hasClassTransformers())
701 725 {
702   - List<String> modTransformers = ((TweakContainer<?>)mod.getContainer()).getClassTransformerClassNames();
703   - for (String modTransformer : modTransformers)
  726 + continue;
  727 + }
  728 +
  729 + List<String> modTransformers = ((TweakContainer<?>)mod.getContainer()).getClassTransformerClassNames();
  730 + for (String modTransformer : modTransformers)
  731 + {
  732 + if (!injectedTransformers.contains(modTransformer))
704 733 {
705   - if (!injectedTransformers.contains(modTransformer))
  734 + List<Throwable> throwables = transformerManager.getTransformerStartupErrors(modTransformer);
  735 + if (throwables != null)
706 736 {
707   - List<Throwable> throwables = transformerManager.getTransformerStartupErrors(modTransformer);
708   - if (throwables != null)
  737 + for (Throwable th : throwables)
709 738 {
710   - for (Throwable th : throwables)
711   - {
712   - this.registerModStartupError(mod, th, true);
713   - }
714   - }
715   - else
716   - {
717   - this.registerModStartupError(mod, new RuntimeException("Missing class transformer " + modTransformer), true);
  739 + this.registerModStartupError(mod, th, true);
718 740 }
719 741 }
  742 + else
  743 + {
  744 + this.registerModStartupError(mod, new RuntimeException("Missing class transformer " + modTransformer), true);
  745 + }
720 746 }
721 747 }
722 748 }
... ... @@ -765,7 +791,11 @@ public class LiteLoaderMods
765 791 private void registerModStartupError(ModInfo<?> mod, Throwable th, boolean critical)
766 792 {
767 793 this.startupErrorCount++;
768   - if (critical) this.criticalErrorCount++;
  794 + if (critical)
  795 + {
  796 + this.criticalErrorCount++;
  797 + }
  798 +
769 799 mod.registerStartupError(th);
770 800  
771 801 if (!this.loadedMods.contains(mod) && !this.disabledMods.contains(mod))
... ...
src/main/java/com/mumfrey/liteloader/core/api/EnumeratorModuleClassPath.java
... ... @@ -82,37 +82,43 @@ public class EnumeratorModuleClassPath implements EnumeratorModule
82 82 @Override
83 83 public void enumerate(ModularEnumerator enumerator, String profile)
84 84 {
85   - if (this.loadTweaks)
  85 + }
  86 +
  87 + @Override
  88 + public void register(ModularEnumerator enumerator, String profile)
  89 + {
  90 + if (!this.loadTweaks)
86 91 {
87   - LiteLoaderLogger.info("Discovering tweaks on class path...");
88   -
89   - for (String classPathPart : this.classPathEntries)
  92 + return;
  93 + }
  94 +
  95 + LiteLoaderLogger.info("Discovering tweaks on class path...");
  96 + for (String classPathPart : this.classPathEntries)
  97 + {
  98 + try
90 99 {
91   - try
  100 + File packagePath = new File(classPathPart);
  101 + if (packagePath.exists())
92 102 {
93   - File packagePath = new File(classPathPart);
94   - if (packagePath.exists())
  103 + LoadableModClassPath classPathMod = new LoadableModClassPath(packagePath);
  104 + if (enumerator.registerModContainer(classPathMod))
95 105 {
96   - LoadableModClassPath classPathMod = new LoadableModClassPath(packagePath);
97   - if (enumerator.registerModContainer(classPathMod))
  106 + this.loadableMods.add(classPathMod);
  107 + if (classPathMod.requiresPreInitInjection())
98 108 {
99   - this.loadableMods.add(classPathMod);
100   - if (classPathMod.requiresPreInitInjection())
101   - {
102   - enumerator.registerTweakContainer(classPathMod);
103   - }
104   - }
105   - else
106   - {
107   - LiteLoaderLogger.info(Verbosity.REDUCED, "Mod %s is disabled or missing a required dependency, not injecting tranformers",
108   - classPathMod.getIdentifier());
  109 + enumerator.registerTweakContainer(classPathMod);
109 110 }
110 111 }
  112 + else
  113 + {
  114 + LiteLoaderLogger.info(Verbosity.REDUCED, "Mod %s is disabled or missing a required dependency, not injecting tranformers",
  115 + classPathMod.getIdentifier());
  116 + }
111 117 }
112   - catch (Throwable th)
113   - {
114   - LiteLoaderLogger.warning(th, "Error encountered whilst inspecting %s", classPathPart);
115   - }
  118 + }
  119 + catch (Throwable th)
  120 + {
  121 + LiteLoaderLogger.warning(th, "Error encountered whilst inspecting %s", classPathPart);
116 122 }
117 123 }
118 124 }
... ...
src/main/java/com/mumfrey/liteloader/core/api/EnumeratorModuleFiles.java 0 โ†’ 100644
  1 +/*
  2 + * This file is part of LiteLoader.
  3 + * Copyright (C) 2012-16 Adam Mummery-Smith
  4 + * All Rights Reserved.
  5 + */
  6 +package com.mumfrey.liteloader.core.api;
  7 +
  8 +import java.io.File;
  9 +import java.io.FilenameFilter;
  10 +import java.net.MalformedURLException;
  11 +import java.util.ArrayList;
  12 +import java.util.Iterator;
  13 +import java.util.LinkedHashMap;
  14 +import java.util.List;
  15 +import java.util.Map;
  16 +import java.util.Set;
  17 +import java.util.TreeSet;
  18 +
  19 +import com.google.common.base.Charsets;
  20 +import com.mumfrey.liteloader.api.EnumeratorModule;
  21 +import com.mumfrey.liteloader.common.LoadingProgress;
  22 +import com.mumfrey.liteloader.core.LiteLoaderVersion;
  23 +import com.mumfrey.liteloader.core.api.EnumeratorModuleFiles.ContainerEnvironment.Candidate;
  24 +import com.mumfrey.liteloader.interfaces.LoadableFile;
  25 +import com.mumfrey.liteloader.interfaces.LoadableMod;
  26 +import com.mumfrey.liteloader.interfaces.ModularEnumerator;
  27 +import com.mumfrey.liteloader.interfaces.TweakContainer;
  28 +import com.mumfrey.liteloader.launch.LoaderEnvironment;
  29 +import com.mumfrey.liteloader.launch.LoaderProperties;
  30 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
  31 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger.Verbosity;
  32 +
  33 +import net.minecraft.launchwrapper.LaunchClassLoader;
  34 +
  35 +public abstract class EnumeratorModuleFiles implements FilenameFilter, EnumeratorModule
  36 +{
  37 + public static class ContainerEnvironment implements Iterable<ContainerEnvironment.Candidate>
  38 + {
  39 + static class Candidate
  40 + {
  41 + private final Set<LoadableMod<File>> availableFiles = new TreeSet<LoadableMod<File>>();
  42 +
  43 + private boolean isRegistered;
  44 +
  45 + public void add(LoadableMod<File> modFile)
  46 + {
  47 + if (!this.isRegistered)
  48 + {
  49 + this.availableFiles.add(modFile);
  50 + }
  51 + }
  52 +
  53 + public LoadableMod<File> getNewestVersion()
  54 + {
  55 + return this.availableFiles.iterator().next();
  56 + }
  57 +
  58 + public boolean isRegistered()
  59 + {
  60 + return this.isRegistered;
  61 + }
  62 +
  63 + public void register()
  64 + {
  65 + this.isRegistered = true;
  66 + }
  67 + }
  68 +
  69 + /**
  70 + * Ordered sets used to sort mods by version/revision
  71 + */
  72 + private final Map<String, Candidate> orderedCandidates = new LinkedHashMap<String, Candidate>();
  73 +
  74 + public void addCandidate(LoadableMod<File> modFile)
  75 + {
  76 + if (!this.orderedCandidates.containsKey(modFile.getModName()))
  77 + {
  78 + this.orderedCandidates.put(modFile.getModName(), new Candidate());
  79 + }
  80 +
  81 + LiteLoaderLogger.info("Considering valid mod file: %s", modFile);
  82 + this.orderedCandidates.get(modFile.getModName()).add(modFile);
  83 + }
  84 +
  85 + @Override
  86 + public Iterator<Candidate> iterator()
  87 + {
  88 + return this.orderedCandidates.values().iterator();
  89 + }
  90 + }
  91 +
  92 + /**
  93 + * Ordered sets used to sort mods by version/revision
  94 + */
  95 + private final ContainerEnvironment containers;
  96 +
  97 + /**
  98 + * Mods to add once init is completed
  99 + */
  100 + private final List<LoadableMod<File>> loadableMods = new ArrayList<LoadableMod<File>>();
  101 +
  102 + protected final LiteLoaderCoreAPI api;
  103 +
  104 + public EnumeratorModuleFiles(LiteLoaderCoreAPI api, ContainerEnvironment containers)
  105 + {
  106 + this.api = api;
  107 + this.containers = containers;
  108 + }
  109 +
  110 + protected abstract boolean readJarFiles();
  111 +
  112 + protected abstract boolean loadTweakJars();
  113 +
  114 + protected abstract boolean loadTweaks();
  115 +
  116 + protected abstract boolean forceInjection();
  117 +
  118 + protected abstract File[] getFiles();
  119 +
  120 + @Override
  121 + public void init(LoaderEnvironment environment, LoaderProperties properties)
  122 + {
  123 + }
  124 +
  125 + /**
  126 + * Write settings
  127 + */
  128 + @Override
  129 + public void writeSettings(LoaderEnvironment environment, LoaderProperties properties)
  130 + {
  131 + }
  132 +
  133 + /* (non-Javadoc)
  134 + * @see com.mumfrey.liteloader.core.Enumerator#getLoadableMods()
  135 + */
  136 + public List<LoadableMod<File>> getLoadableMods()
  137 + {
  138 + return this.loadableMods;
  139 + }
  140 +
  141 + /**
  142 + * For FilenameFilter interface
  143 + *
  144 + * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
  145 + */
  146 + @Override
  147 + public boolean accept(File dir, String fileName)
  148 + {
  149 + fileName = fileName.toLowerCase();
  150 +
  151 + if (fileName.endsWith(".litemod.zip"))
  152 + {
  153 + LiteLoaderLogger.warning("Found %s with unsupported extension .litemod.zip."
  154 + + " Please change file extension to .litemod to allow this file to be loaded!", fileName);
  155 + return true;
  156 + }
  157 +
  158 + return fileName.endsWith(".litemod") || fileName.endsWith(".jar");
  159 + }
  160 +
  161 + /**
  162 + * Search the folder for (potentially) valid files
  163 + */
  164 + protected void findValidFiles(ModularEnumerator enumerator)
  165 + {
  166 + for (File file : this.getFiles())
  167 + {
  168 + LoadableFile candidateFile = new LoadableFile(file);
  169 + candidateFile.setForceInjection(this.forceInjection());
  170 + try
  171 + {
  172 + this.inspectFile(enumerator, candidateFile);
  173 + }
  174 + catch (Exception ex)
  175 + {
  176 + LiteLoaderLogger.warning(ex, "An error occurred whilst inspecting %s", candidateFile);
  177 + }
  178 + }
  179 + }
  180 +
  181 + /**
  182 + * Check whether a particular file is valid, and add it to the candiates
  183 + * list if it appears to be acceptable.
  184 + *
  185 + * @param enumerator
  186 + * @param candidateFile
  187 + */
  188 + protected void inspectFile(ModularEnumerator enumerator, LoadableFile candidateFile)
  189 + {
  190 + if (this.isValidFile(enumerator, candidateFile))
  191 + {
  192 + String metaData = candidateFile.getFileContents(LoadableMod.METADATA_FILENAME, Charsets.UTF_8);
  193 + if (metaData != null)
  194 + {
  195 + LoadableMod<File> modFile = this.getModFile(candidateFile, metaData);
  196 + this.addModFile(enumerator, modFile);
  197 + return;
  198 + }
  199 + else if (this.isValidTweakContainer(candidateFile))
  200 + {
  201 + TweakContainer<File> container = this.getTweakFile(candidateFile);
  202 + this.addTweakFile(enumerator, container);
  203 + return;
  204 + }
  205 + else
  206 + {
  207 + LiteLoaderLogger.info("Ignoring %s", candidateFile);
  208 +// enumerator.registerBadContainer(candidateFile, "No metadata");
  209 + }
  210 + }
  211 +// else
  212 +// {
  213 +// enumerator.registerBadContainer(candidateFile, "Not a valid file");
  214 +// }
  215 + }
  216 +
  217 + /**
  218 + * Check whether the specified file is a valid mod container
  219 + *
  220 + * @param enumerator
  221 + * @param candidateFile
  222 + */
  223 + protected boolean isValidFile(ModularEnumerator enumerator, LoadableFile candidateFile)
  224 + {
  225 + String filename = candidateFile.getName().toLowerCase();
  226 + if (filename.endsWith(".litemod.zip"))
  227 + {
  228 + enumerator.registerBadContainer(candidateFile, "Invalid file extension .litemod.zip");
  229 + return false;
  230 + }
  231 + else if (filename.endsWith(".litemod"))
  232 + {
  233 + return true;
  234 + }
  235 + else if (filename.endsWith(".jar"))
  236 + {
  237 + Set<String> modSystems = candidateFile.getModSystems();
  238 + boolean hasLiteLoader = modSystems.contains("LiteLoader");
  239 + if (modSystems.size() > 0)
  240 + {
  241 + LiteLoaderLogger.info("%s supports mod systems %s", candidateFile, modSystems);
  242 + if (!hasLiteLoader) return false;
  243 + }
  244 +
  245 + return this.loadTweakJars() || this.readJarFiles() || hasLiteLoader;
  246 + }
  247 +
  248 + return false;
  249 + }
  250 +
  251 + /**
  252 + * Called only if the file is not a valid mod container (has no mod
  253 + * metadata) to check whether it could instead be a potential tweak
  254 + * container.
  255 + *
  256 + * @param candidateFile
  257 + */
  258 + protected boolean isValidTweakContainer(LoadableFile candidateFile)
  259 + {
  260 + return this.loadTweakJars() && this.loadTweaks() && candidateFile.getName().toLowerCase().endsWith(".jar");
  261 + }
  262 +
  263 + /**
  264 + * Get the {@link FilenameFilter} to use to filter candidate files
  265 + */
  266 + protected FilenameFilter getFilenameFilter()
  267 + {
  268 + return this;
  269 + }
  270 +
  271 + /**
  272 + * @param modFile
  273 + */
  274 + protected boolean isFileSupported(LoadableMod<File> modFile)
  275 + {
  276 + return LiteLoaderVersion.CURRENT.isVersionSupported(modFile.getTargetVersion());
  277 + }
  278 +
  279 + /**
  280 + * @param candidateFile
  281 + * @param metaData
  282 + */
  283 + protected LoadableMod<File> getModFile(LoadableFile candidateFile, String metaData)
  284 + {
  285 + return new LoadableModFile(candidateFile, metaData);
  286 + }
  287 +
  288 + /**
  289 + * @param candidateFile
  290 + */
  291 + protected TweakContainer<File> getTweakFile(LoadableFile candidateFile)
  292 + {
  293 + return candidateFile;
  294 + }
  295 +
  296 + /**
  297 + * @param enumerator
  298 + * @param modFile
  299 + */
  300 + protected void addModFile(ModularEnumerator enumerator, LoadableMod<File> modFile)
  301 + {
  302 + if (modFile.hasValidMetaData())
  303 + {
  304 + // Only add the mod if the version matches, we add candidates to the versionOrderingSets in
  305 + // order to determine the most recent version available.
  306 + if (this.isFileSupported(modFile))
  307 + {
  308 + this.containers.addCandidate(modFile);
  309 + }
  310 + else
  311 + {
  312 + LiteLoaderLogger.info(Verbosity.REDUCED, "Not adding invalid or version-mismatched mod file: %s", modFile);
  313 + enumerator.registerBadContainer(modFile, "Version not supported");
  314 + }
  315 + }
  316 + }
  317 +
  318 + /**
  319 + * @param enumerator
  320 + * @param container
  321 + */
  322 + protected void addTweakFile(ModularEnumerator enumerator, TweakContainer<File> container)
  323 + {
  324 + enumerator.registerTweakContainer(container);
  325 + }
  326 +
  327 + /**
  328 + * @param enumerator
  329 + */
  330 + protected void sortAndRegisterFiles(ModularEnumerator enumerator)
  331 + {
  332 + // Copy the first entry in every version set into the modfiles list
  333 + for (Candidate candidate : this.containers)
  334 + {
  335 + if (candidate.isRegistered())
  336 + {
  337 + continue;
  338 + }
  339 +
  340 + LoadableMod<File> newestVersion = candidate.getNewestVersion();
  341 + this.registerFile(enumerator, newestVersion);
  342 + candidate.register();
  343 + }
  344 + }
  345 +
  346 + /**
  347 + * @param enumerator
  348 + * @param modFile
  349 + */
  350 + @SuppressWarnings("unchecked")
  351 + protected void registerFile(ModularEnumerator enumerator, LoadableMod<File> modFile)
  352 + {
  353 + if (enumerator.registerModContainer(modFile))
  354 + {
  355 + LiteLoaderLogger.info(Verbosity.REDUCED, "Adding newest valid mod file '%s' at revision %.4f", modFile, modFile.getRevision());
  356 + this.loadableMods.add(modFile);
  357 + }
  358 + else
  359 + {
  360 + LiteLoaderLogger.info(Verbosity.REDUCED, "Not adding valid mod file '%s', the specified mod is disabled or missing a required dependency",
  361 + modFile);
  362 + }
  363 +
  364 + if (this.loadTweaks())
  365 + {
  366 + try
  367 + {
  368 + if (modFile instanceof TweakContainer)
  369 + {
  370 + this.addTweakFile(enumerator, (TweakContainer<File>)modFile);
  371 + }
  372 + }
  373 + catch (Throwable th)
  374 + {
  375 + LiteLoaderLogger.warning("Error adding tweaks from '%s'", modFile);
  376 + }
  377 + }
  378 + }
  379 +
  380 + @Override
  381 + public void injectIntoClassLoader(ModularEnumerator enumerator, LaunchClassLoader classLoader)
  382 + {
  383 + LiteLoaderLogger.info("Injecting external mods into class path...");
  384 +
  385 + for (LoadableMod<?> loadableMod : this.loadableMods)
  386 + {
  387 + try
  388 + {
  389 + if (loadableMod.injectIntoClassPath(classLoader, false))
  390 + {
  391 + LiteLoaderLogger.info("Successfully injected mod file '%s' into classpath", loadableMod);
  392 + }
  393 + }
  394 + catch (MalformedURLException ex)
  395 + {
  396 + LiteLoaderLogger.warning("Error injecting '%s' into classPath. The mod will not be loaded", loadableMod);
  397 + }
  398 + }
  399 + }
  400 +
  401 + @Override
  402 + public void registerMods(ModularEnumerator enumerator, LaunchClassLoader classLoader)
  403 + {
  404 + LiteLoaderLogger.info(Verbosity.REDUCED, "Discovering mods in valid mod files...");
  405 + LoadingProgress.incTotalLiteLoaderProgress(this.loadableMods.size());
  406 +
  407 + for (LoadableMod<?> modFile : this.loadableMods)
  408 + {
  409 + LoadingProgress.incLiteLoaderProgress("Searching for mods in " + modFile.getModName() + "...");
  410 + LiteLoaderLogger.info("Searching %s...", modFile);
  411 + try
  412 + {
  413 + enumerator.registerModsFrom(modFile, true);
  414 + }
  415 + catch (Exception ex)
  416 + {
  417 + LiteLoaderLogger.warning("Error encountered whilst searching in %s...", modFile);
  418 + }
  419 + }
  420 + }
  421 +}
... ...
src/main/java/com/mumfrey/liteloader/core/api/EnumeratorModuleFolder.java
... ... @@ -6,50 +6,19 @@
6 6 package com.mumfrey.liteloader.core.api;
7 7  
8 8 import java.io.File;
9   -import java.io.FilenameFilter;
10   -import java.net.MalformedURLException;
11   -import java.util.ArrayList;
12   -import java.util.HashMap;
13   -import java.util.List;
14   -import java.util.Map;
15   -import java.util.Map.Entry;
16   -import java.util.Set;
17   -import java.util.TreeSet;
18 9  
19   -import com.google.common.base.Charsets;
20   -import com.mumfrey.liteloader.api.EnumeratorModule;
21   -import com.mumfrey.liteloader.common.LoadingProgress;
22   -import com.mumfrey.liteloader.core.LiteLoaderVersion;
23   -import com.mumfrey.liteloader.interfaces.LoadableFile;
24   -import com.mumfrey.liteloader.interfaces.LoadableMod;
25 10 import com.mumfrey.liteloader.interfaces.ModularEnumerator;
26   -import com.mumfrey.liteloader.interfaces.TweakContainer;
27 11 import com.mumfrey.liteloader.launch.LoaderEnvironment;
28 12 import com.mumfrey.liteloader.launch.LoaderProperties;
29 13 import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
30   -import com.mumfrey.liteloader.util.log.LiteLoaderLogger.Verbosity;
31   -
32   -import net.minecraft.launchwrapper.LaunchClassLoader;
33 14  
34 15 /**
35 16 * Enumerator module which searches for mods and tweaks in a folder
36 17 *
37 18 * @author Adam Mummery-Smith
38 19 */
39   -public class EnumeratorModuleFolder implements FilenameFilter, EnumeratorModule
  20 +public class EnumeratorModuleFolder extends EnumeratorModuleFiles
40 21 {
41   - /**
42   - * Ordered sets used to sort mods by version/revision
43   - */
44   - protected final Map<String, TreeSet<LoadableMod<File>>> versionOrderingSets = new HashMap<String, TreeSet<LoadableMod<File>>>();
45   -
46   - /**
47   - * Mods to add once init is completed
48   - */
49   - protected final List<LoadableMod<File>> loadableMods = new ArrayList<LoadableMod<File>>();
50   -
51   - protected LiteLoaderCoreAPI coreAPI;
52   -
53 22 protected File directory;
54 23  
55 24 protected boolean readJarFiles;
... ... @@ -62,13 +31,13 @@ public class EnumeratorModuleFolder implements FilenameFilter, EnumeratorModule
62 31 */
63 32 protected final boolean loadTweakJars;
64 33  
65   - public EnumeratorModuleFolder(LiteLoaderCoreAPI coreAPI, File directory, boolean loadTweakJars)
  34 + public EnumeratorModuleFolder(LiteLoaderCoreAPI api, ContainerEnvironment containers, File directory, boolean loadTweakJars)
66 35 {
67   - this.coreAPI = coreAPI;
68   - this.directory = directory;
69   - this.loadTweakJars = loadTweakJars;
  36 + super(api, containers);
  37 + this.directory = directory;
  38 + this.loadTweakJars = loadTweakJars;
70 39 }
71   -
  40 +
72 41 @Override
73 42 public void init(LoaderEnvironment environment, LoaderProperties properties)
74 43 {
... ... @@ -76,7 +45,7 @@ public class EnumeratorModuleFolder implements FilenameFilter, EnumeratorModule
76 45 this.readJarFiles = properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_SEARCH_JARFILES, true);
77 46 this.forceInjection = properties.getAndStoreBooleanProperty(LoaderProperties.OPTION_FORCE_INJECTION, false);
78 47  
79   - this.coreAPI.writeDiscoverySettings();
  48 + this.api.writeDiscoverySettings();
80 49 }
81 50  
82 51 /**
... ... @@ -88,6 +57,36 @@ public class EnumeratorModuleFolder implements FilenameFilter, EnumeratorModule
88 57 properties.setBooleanProperty(LoaderProperties.OPTION_SEARCH_JARFILES, this.readJarFiles);
89 58 properties.setBooleanProperty(LoaderProperties.OPTION_FORCE_INJECTION, this.forceInjection);
90 59 }
  60 +
  61 + @Override
  62 + protected boolean forceInjection()
  63 + {
  64 + return this.forceInjection;
  65 + }
  66 +
  67 + @Override
  68 + protected boolean loadTweakJars()
  69 + {
  70 + return this.loadTweakJars;
  71 + }
  72 +
  73 + @Override
  74 + protected boolean loadTweaks()
  75 + {
  76 + return this.loadTweaks;
  77 + }
  78 +
  79 + @Override
  80 + protected boolean readJarFiles()
  81 + {
  82 + return this.readJarFiles;
  83 + }
  84 +
  85 + @Override
  86 + protected File[] getFiles()
  87 + {
  88 + return this.directory.listFiles(this.getFilenameFilter());
  89 + }
91 90  
92 91 /* (non-Javadoc)
93 92 * @see java.lang.Object#toString()
... ... @@ -107,34 +106,6 @@ public class EnumeratorModuleFolder implements FilenameFilter, EnumeratorModule
107 106 }
108 107  
109 108 /* (non-Javadoc)
110   - * @see com.mumfrey.liteloader.core.Enumerator#getLoadableMods()
111   - */
112   - public List<LoadableMod<File>> getLoadableMods()
113   - {
114   - return this.loadableMods;
115   - }
116   -
117   - /**
118   - * For FilenameFilter interface
119   - *
120   - * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
121   - */
122   - @Override
123   - public boolean accept(File dir, String fileName)
124   - {
125   - fileName = fileName.toLowerCase();
126   -
127   - if (fileName.endsWith(".litemod.zip"))
128   - {
129   - LiteLoaderLogger.warning("Found %s with unsupported extension .litemod.zip."
130   - + " Please change file extension to .litemod to allow this file to be loaded!", fileName);
131   - return true;
132   - }
133   -
134   - return fileName.endsWith(".litemod") || fileName.endsWith(".jar");
135   - }
136   -
137   - /* (non-Javadoc)
138 109 * @see com.mumfrey.liteloader.core.Enumerator
139 110 * #enumerate(com.mumfrey.liteloader.core.EnabledModsList,
140 111 * java.lang.String)
... ... @@ -145,272 +116,22 @@ public class EnumeratorModuleFolder implements FilenameFilter, EnumeratorModule
145 116 if (this.directory.exists() && this.directory.isDirectory())
146 117 {
147 118 LiteLoaderLogger.info("Discovering valid mod files in folder %s", this.directory.getPath());
148   -
149 119 this.findValidFiles(enumerator);
150   - this.sortAndRegisterFiles(enumerator);
151   - }
152   - }
153   -
154   - /**
155   - * Search the folder for (potentially) valid files
156   - */
157   - private void findValidFiles(ModularEnumerator enumerator)
158   - {
159   - for (File file : this.directory.listFiles(this.getFilenameFilter()))
160   - {
161   - LoadableFile candidateFile = new LoadableFile(file);
162   - candidateFile.setForceInjection(this.forceInjection);
163   - try
164   - {
165   - this.inspectFile(enumerator, candidateFile);
166   - }
167   - catch (Exception ex)
168   - {
169   - LiteLoaderLogger.warning(ex, "An error occurred whilst inspecting %s", candidateFile);
170   - }
171 120 }
172 121 }
173   -
174   - /**
175   - * Check whether a particular file is valid, and add it to the candiates
176   - * list if it appears to be acceptable.
177   - *
178   - * @param enumerator
179   - * @param candidateFile
180   - */
181   - protected void inspectFile(ModularEnumerator enumerator, LoadableFile candidateFile)
182   - {
183   - if (this.isValidFile(enumerator, candidateFile))
184   - {
185   - String metaData = candidateFile.getFileContents(LoadableMod.METADATA_FILENAME, Charsets.UTF_8);
186   - if (metaData != null)
187   - {
188   - LoadableMod<File> modFile = this.getModFile(candidateFile, metaData);
189   - this.addModFile(enumerator, modFile);
190   - return;
191   - }
192   - else if (this.isValidTweakContainer(candidateFile))
193   - {
194   - TweakContainer<File> container = this.getTweakFile(candidateFile);
195   - this.addTweakFile(enumerator, container);
196   - return;
197   - }
198   - else
199   - {
200   - LiteLoaderLogger.info("Ignoring %s", candidateFile);
201   -// enumerator.registerBadContainer(candidateFile, "No metadata");
202   - }
203   - }
204   -// else
205   -// {
206   -// enumerator.registerBadContainer(candidateFile, "Not a valid file");
207   -// }
208   - }
209   -
210   - /**
211   - * Check whether the specified file is a valid mod container
212   - *
213   - * @param enumerator
214   - * @param candidateFile
215   - */
216   - protected boolean isValidFile(ModularEnumerator enumerator, LoadableFile candidateFile)
217   - {
218   - String filename = candidateFile.getName().toLowerCase();
219   - if (filename.endsWith(".litemod.zip"))
220   - {
221   - enumerator.registerBadContainer(candidateFile, "Invalid file extension .litemod.zip");
222   - return false;
223   - }
224   - else if (filename.endsWith(".litemod"))
225   - {
226   - return true;
227   - }
228   - else if (filename.endsWith(".jar"))
229   - {
230   - Set<String> modSystems = candidateFile.getModSystems();
231   - boolean hasLiteLoader = modSystems.contains("LiteLoader");
232   - if (modSystems.size() > 0)
233   - {
234   - LiteLoaderLogger.info("%s supports mod systems %s", candidateFile, modSystems);
235   - if (!hasLiteLoader) return false;
236   - }
237   -
238   - return this.loadTweakJars || this.readJarFiles || hasLiteLoader;
239   - }
240   -
241   - return false;
242   - }
243   -
244   - /**
245   - * Called only if the file is not a valid mod container (has no mod
246   - * metadata) to check whether it could instead be a potential tweak
247   - * container.
248   - *
249   - * @param candidateFile
250   - */
251   - protected boolean isValidTweakContainer(LoadableFile candidateFile)
252   - {
253   - return this.loadTweakJars && this.loadTweaks && candidateFile.getName().toLowerCase().endsWith(".jar");
254   - }
255   -
256   - /**
257   - * Get the {@link FilenameFilter} to use to filter candidate files
258   - */
259   - protected FilenameFilter getFilenameFilter()
260   - {
261   - return this;
262   - }
263   -
264   - /**
265   - * @param modFile
266   - */
267   - protected boolean isFileSupported(LoadableMod<File> modFile)
268   - {
269   - return LiteLoaderVersion.CURRENT.isVersionSupported(modFile.getTargetVersion());
270   - }
271   -
272   - /**
273   - * @param candidateFile
274   - * @param metaData
275   - */
276   - protected LoadableMod<File> getModFile(LoadableFile candidateFile, String metaData)
277   - {
278   - return new LoadableModFile(candidateFile, metaData);
279   - }
280   -
281   - /**
282   - * @param candidateFile
283   - */
284   - protected TweakContainer<File> getTweakFile(LoadableFile candidateFile)
285   - {
286   - return candidateFile;
287   - }
288   -
289   - /**
290   - * @param enumerator
291   - * @param modFile
292   - */
293   - protected void addModFile(ModularEnumerator enumerator, LoadableMod<File> modFile)
294   - {
295   - if (modFile.hasValidMetaData())
296   - {
297   - // Only add the mod if the version matches, we add candidates to the versionOrderingSets in
298   - // order to determine the most recent version available.
299   - if (this.isFileSupported(modFile))
300   - {
301   - if (!this.versionOrderingSets.containsKey(modFile.getName()))
302   - {
303   - this.versionOrderingSets.put(modFile.getModName(), new TreeSet<LoadableMod<File>>());
304   - }
305   -
306   - LiteLoaderLogger.info("Considering valid mod file: %s", modFile);
307   - this.versionOrderingSets.get(modFile.getModName()).add(modFile);
308   - }
309   - else
310   - {
311   - LiteLoaderLogger.info(Verbosity.REDUCED, "Not adding invalid or version-mismatched mod file: %s", modFile);
312   - enumerator.registerBadContainer(modFile, "Version not supported");
313   - }
314   - }
315   - }
316   -
317   - /**
318   - * @param enumerator
319   - * @param container
320   - */
321   - protected void addTweakFile(ModularEnumerator enumerator, TweakContainer<File> container)
322   - {
323   - enumerator.registerTweakContainer(container);
324   - }
325   -
326   - /**
327   - * @param enumerator
328   - */
329   - protected void sortAndRegisterFiles(ModularEnumerator enumerator)
330   - {
331   - // Copy the first entry in every version set into the modfiles list
332   - for (Entry<String, TreeSet<LoadableMod<File>>> modFileEntry : this.versionOrderingSets.entrySet())
333   - {
334   - LoadableMod<File> newestVersion = modFileEntry.getValue().iterator().next();
335   - this.registerFile(enumerator, newestVersion);
336   - }
337   -
338   - this.versionOrderingSets.clear();
339   - }
340   -
341   - /**
342   - * @param enumerator
343   - * @param modFile
  122 +
  123 + /* (non-Javadoc)
  124 + * @see com.mumfrey.liteloader.api.EnumeratorModule#register(
  125 + * com.mumfrey.liteloader.interfaces.ModularEnumerator,
  126 + * java.lang.String)
344 127 */
345   - @SuppressWarnings("unchecked")
346   - protected void registerFile(ModularEnumerator enumerator, LoadableMod<File> modFile)
347   - {
348   - if (enumerator.registerModContainer(modFile))
349   - {
350   - LiteLoaderLogger.info(Verbosity.REDUCED, "Adding newest valid mod file '%s' at revision %.4f", modFile, modFile.getRevision());
351   - this.loadableMods.add(modFile);
352   - }
353   - else
354   - {
355   - LiteLoaderLogger.info(Verbosity.REDUCED, "Not adding valid mod file '%s', the specified mod is disabled or missing a required dependency",
356   - modFile);
357   - }
358   -
359   - if (this.loadTweaks)
360   - {
361   - try
362   - {
363   - if (modFile instanceof TweakContainer)
364   - {
365   - this.addTweakFile(enumerator, (TweakContainer<File>)modFile);
366   - }
367   - }
368   - catch (Throwable th)
369   - {
370   - LiteLoaderLogger.warning("Error adding tweaks from '%s'", modFile);
371   - }
372   - }
373   - }
374   -
375 128 @Override
376   - public void injectIntoClassLoader(ModularEnumerator enumerator, LaunchClassLoader classLoader)
  129 + public void register(ModularEnumerator enumerator, String profile)
377 130 {
378   - LiteLoaderLogger.info("Injecting external mods into class path...");
379   -
380   - for (LoadableMod<?> loadableMod : this.loadableMods)
381   - {
382   - try
383   - {
384   - if (loadableMod.injectIntoClassPath(classLoader, false))
385   - {
386   - LiteLoaderLogger.info("Successfully injected mod file '%s' into classpath", loadableMod);
387   - }
388   - }
389   - catch (MalformedURLException ex)
390   - {
391   - LiteLoaderLogger.warning("Error injecting '%s' into classPath. The mod will not be loaded", loadableMod);
392   - }
393   - }
394   - }
395   -
396   - @Override
397   - public void registerMods(ModularEnumerator enumerator, LaunchClassLoader classLoader)
398   - {
399   - LiteLoaderLogger.info(Verbosity.REDUCED, "Discovering mods in valid mod files...");
400   - LoadingProgress.incTotalLiteLoaderProgress(this.loadableMods.size());
401   -
402   - for (LoadableMod<?> modFile : this.loadableMods)
  131 + if (this.directory.exists() && this.directory.isDirectory())
403 132 {
404   - LoadingProgress.incLiteLoaderProgress("Searching for mods in " + modFile.getModName() + "...");
405   - LiteLoaderLogger.info("Searching %s...", modFile);
406   - try
407   - {
408   - enumerator.registerModsFrom(modFile, true);
409   - }
410   - catch (Exception ex)
411   - {
412   - LiteLoaderLogger.warning("Error encountered whilst searching in %s...", modFile);
413   - }
  133 + LiteLoaderLogger.info("Registering discovered mod files in folder %s", this.directory.getPath());
  134 + this.sortAndRegisterFiles(enumerator);
414 135 }
415 136 }
416 137 }
... ...
src/main/java/com/mumfrey/liteloader/core/api/EnumeratorModuleRepository.java 0 โ†’ 100644
  1 +/*
  2 + * This file is part of LiteLoader.
  3 + * Copyright (C) 2012-16 Adam Mummery-Smith
  4 + * All Rights Reserved.
  5 + */
  6 +package com.mumfrey.liteloader.core.api;
  7 +
  8 +import java.io.File;
  9 +import java.util.ArrayList;
  10 +import java.util.List;
  11 +
  12 +import com.mumfrey.liteloader.core.api.repository.Repository;
  13 +import com.mumfrey.liteloader.interfaces.ModularEnumerator;
  14 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
  15 +
  16 +/**
  17 + * Enumerator module which searches for mods and tweaks in a folder
  18 + *
  19 + * @author Adam Mummery-Smith
  20 + */
  21 +public class EnumeratorModuleRepository extends EnumeratorModuleFiles
  22 +{
  23 + private final File modList;
  24 +
  25 + private final Repository repo;
  26 +
  27 + private File[] files = new File[0];
  28 +
  29 + public EnumeratorModuleRepository(LiteLoaderCoreAPI api, ContainerEnvironment containers, Repository repo, File modList)
  30 + {
  31 + super(api, containers);
  32 + this.repo = repo;
  33 + this.modList = modList;
  34 + }
  35 +
  36 + /* (non-Javadoc)
  37 + * @see java.lang.Object#toString()
  38 + */
  39 + @Override
  40 + public String toString()
  41 + {
  42 + return this.modList.getAbsolutePath();
  43 + }
  44 +
  45 + public File getModList()
  46 + {
  47 + return this.modList;
  48 + }
  49 +
  50 + @Override
  51 + protected boolean readJarFiles()
  52 + {
  53 + return true;
  54 + }
  55 +
  56 + @Override
  57 + protected boolean loadTweakJars()
  58 + {
  59 + return true;
  60 + }
  61 +
  62 + @Override
  63 + protected boolean loadTweaks()
  64 + {
  65 + return true;
  66 + }
  67 +
  68 + @Override
  69 + protected boolean forceInjection()
  70 + {
  71 + return false;
  72 + }
  73 +
  74 + @Override
  75 + protected File[] getFiles()
  76 + {
  77 + return this.files;
  78 + }
  79 +
  80 + /* (non-Javadoc)
  81 + * @see com.mumfrey.liteloader.core.Enumerator
  82 + * #enumerate(com.mumfrey.liteloader.core.EnabledModsList,
  83 + * java.lang.String)
  84 + */
  85 + @Override
  86 + public void enumerate(ModularEnumerator enumerator, String profile)
  87 + {
  88 + if (this.modList.isFile())
  89 + {
  90 + LiteLoaderLogger.info("Discovering mod files defined in repository %s", this.modList.getPath());
  91 + this.resolve();
  92 +
  93 + this.findValidFiles(enumerator);
  94 + }
  95 + }
  96 +
  97 + @Override
  98 + public void register(ModularEnumerator enumerator, String profile)
  99 + {
  100 + if (this.modList.isFile())
  101 + {
  102 + LiteLoaderLogger.info("Discovering mod files defined in repository %s", this.modList.getPath());
  103 + this.resolve();
  104 +
  105 + this.findValidFiles(enumerator);
  106 + this.sortAndRegisterFiles(enumerator);
  107 + }
  108 + }
  109 +
  110 + private void resolve()
  111 + {
  112 + this.repo.resolve(this.modList);
  113 +
  114 + List<File> files = new ArrayList<File>();
  115 + files.addAll(this.repo.getFiles());
  116 + this.files = files.toArray(this.files);
  117 + }
  118 +}
... ...
src/main/java/com/mumfrey/liteloader/core/api/LiteLoaderCoreAPI.java
... ... @@ -14,6 +14,7 @@ import com.mumfrey.liteloader.api.EnumeratorModule;
14 14 import com.mumfrey.liteloader.api.LiteAPI;
15 15 import com.mumfrey.liteloader.api.MixinConfigProvider;
16 16 import com.mumfrey.liteloader.core.LiteLoaderVersion;
  17 +import com.mumfrey.liteloader.core.api.EnumeratorModuleFiles.ContainerEnvironment;
17 18 import com.mumfrey.liteloader.interfaces.ObjectFactory;
18 19 import com.mumfrey.liteloader.launch.LoaderEnvironment;
19 20 import com.mumfrey.liteloader.launch.LoaderProperties;
... ... @@ -133,17 +134,26 @@ public abstract class LiteLoaderCoreAPI implements LiteAPI, MixinConfigProvider
133 134 {
134 135 enumeratorModules.add(new EnumeratorModuleClassPath());
135 136 }
  137 +
  138 + ContainerEnvironment containers = new ContainerEnvironment();
136 139  
137 140 if (this.searchModsFolder)
138 141 {
139 142 File modsFolder = this.environment.getModsFolder();
140   - enumeratorModules.add(new EnumeratorModuleFolder(this, modsFolder, false));
  143 + enumeratorModules.add(new EnumeratorModuleFolder(this, containers, modsFolder, false));
141 144  
142 145 File versionedModsFolder = this.environment.getVersionedModsFolder();
143   - enumeratorModules.add(new EnumeratorModuleFolder(this, versionedModsFolder, true));
  146 + enumeratorModules.add(new EnumeratorModuleFolder(this, containers, versionedModsFolder, true));
  147 + }
  148 +
  149 + String modsRepoFile = this.environment.getModsRepoFile();
  150 + if (modsRepoFile != null)
  151 + {
  152 + File modList = new File(modsRepoFile);
  153 + enumeratorModules.add(new EnumeratorModuleRepository(this, containers, this.environment.getModRepository(), modList));
144 154 }
145 155  
146   - return Collections.unmodifiableList(enumeratorModules);
  156 + return Collections.<EnumeratorModule>unmodifiableList(enumeratorModules);
147 157 }
148 158  
149 159 /**
... ...
src/main/java/com/mumfrey/liteloader/core/api/repository/Artefact.java 0 โ†’ 100644
  1 +/*
  2 + * This file is part of LiteLoader.
  3 + * Copyright (C) 2012-16 Adam Mummery-Smith
  4 + * All Rights Reserved.
  5 + */
  6 +package com.mumfrey.liteloader.core.api.repository;
  7 +
  8 +import java.io.File;
  9 +import java.util.regex.Matcher;
  10 +import java.util.regex.Pattern;
  11 +
  12 +/**
  13 + * Specifier for a mod in the ivy repo shorthand notation
  14 + */
  15 +public final class Artefact
  16 +{
  17 + /**
  18 + * Regex for matching valid specifiers
  19 + */
  20 + private static final Pattern PATTERN = Pattern.compile("^([^:]+):([^:]+):([^:@]+?)(:([^:@]+?))?(@(zip|jar|litemod))?$");
  21 +
  22 + /**
  23 + * Artefact group id
  24 + */
  25 + private final String group;
  26 +
  27 + /**
  28 + * Artefact name
  29 + */
  30 + private final String name;
  31 +
  32 + /**
  33 + * Artefact version
  34 + */
  35 + private final String version;
  36 +
  37 + /**
  38 + * Artefact classifier (can be null)
  39 + */
  40 + private final String classifier;
  41 +
  42 + /**
  43 + * Artefact type (can be null)
  44 + */
  45 + private final String type;
  46 +
  47 + /**
  48 + * Resolved file
  49 + */
  50 + private File file;
  51 +
  52 + public Artefact(String specifier)
  53 + {
  54 + if (specifier == null)
  55 + {
  56 + throw new IllegalArgumentException("Invalid artefact specifier: null");
  57 + }
  58 +
  59 + Matcher matcher = Artefact.PATTERN.matcher(specifier);
  60 + if (!matcher.matches())
  61 + {
  62 + throw new IllegalArgumentException("Invalid artefact specifier: " + specifier);
  63 + }
  64 +
  65 + this.group = matcher.group(1);
  66 + this.name = matcher.group(2);
  67 + this.version = matcher.group(3);
  68 + this.classifier = matcher.group(5);
  69 + this.type = matcher.group(7);
  70 + }
  71 +
  72 + /**
  73 + * Get the artefact id consisting of the group and name
  74 + *
  75 + * @return
  76 + */
  77 + public String getArtefactId()
  78 + {
  79 + return String.format("%s:%s", this.getGroup(), this.getName());
  80 + }
  81 +
  82 + /**
  83 + * Get the path portion of this artefact's resolved location
  84 + *
  85 + * @return artefact path
  86 + */
  87 + public String getPath()
  88 + {
  89 + return String.format("%s/%s/%s", this.getGroup().replace('.', '/'), this.getName(), this.getVersion());
  90 + }
  91 +
  92 + /**
  93 + * Get the file name portion of this artefact's resolved location
  94 + *
  95 + * @return arefact file name
  96 + */
  97 + public String getFileName()
  98 + {
  99 + return String.format("%s-%s%s.%s", this.getName(), this.getVersion(), this.getClassifier("-"), this.getType());
  100 + }
  101 +
  102 + /**
  103 + * Get the artefact group
  104 + */
  105 + public String getGroup()
  106 + {
  107 + return this.group;
  108 + }
  109 +
  110 + /**
  111 + * Get the artefact name
  112 + */
  113 + public String getName()
  114 + {
  115 + return this.name;
  116 + }
  117 +
  118 + /**
  119 + * Get the artefact version
  120 + */
  121 + public String getVersion()
  122 + {
  123 + return this.version;
  124 + }
  125 +
  126 + /**
  127 + * Get the artefact classifier, returns an empty string if no classifier is
  128 + * set
  129 + */
  130 + public String getClassifier()
  131 + {
  132 + return this.getClassifier("");
  133 + }
  134 +
  135 + private String getClassifier(String prefix)
  136 + {
  137 + return this.classifier != null ? prefix + this.classifier : "";
  138 + }
  139 +
  140 + /**
  141 + * Get the artefact type, defaults to "jar" if no type was specified
  142 + */
  143 + public String getType()
  144 + {
  145 + return this.type != null ? this.type : "jar";
  146 + }
  147 +
  148 + /**
  149 + * Resolve this artefact beneath the specified repository root
  150 + *
  151 + * @param repositoryRoot repository root
  152 + * @return resolved file
  153 + */
  154 + void resolve(File repositoryRoot)
  155 + {
  156 + this.file = new File(new File(repositoryRoot, this.getPath()), this.getFileName());
  157 + }
  158 +
  159 + /**
  160 + * Get whether the resolved artefact actually exists
  161 + */
  162 + public boolean exists()
  163 + {
  164 + return this.file != null && this.file.isFile();
  165 + }
  166 +
  167 + /**
  168 + * After resolution, return the resolved file location
  169 + *
  170 + * @return resolved location
  171 + */
  172 + public File getFile()
  173 + {
  174 + return this.file;
  175 + }
  176 +
  177 + /* (non-Javadoc)
  178 + * @see java.lang.Object#toString()
  179 + */
  180 + @Override
  181 + public String toString()
  182 + {
  183 + String type = this.type != null && !"jar".equals(this.type) ? "@" + this.type : "";
  184 + return String.format("%s:%s:%s%s%s", this.getGroup(), this.getName(), this.getVersion(), this.getClassifier(":"), type);
  185 + }
  186 +}
0 187 \ No newline at end of file
... ...
src/main/java/com/mumfrey/liteloader/core/api/repository/JsonResolver.java 0 โ†’ 100644
  1 +/*
  2 + * This file is part of LiteLoader.
  3 + * Copyright (C) 2012-16 Adam Mummery-Smith
  4 + * All Rights Reserved.
  5 + */
  6 +package com.mumfrey.liteloader.core.api.repository;
  7 +
  8 +import java.io.File;
  9 +import java.io.FileReader;
  10 +import java.util.Collections;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +import java.util.TreeMap;
  14 +
  15 +import com.google.common.base.Strings;
  16 +import com.google.gson.Gson;
  17 +import com.google.gson.GsonBuilder;
  18 +import com.google.gson.annotations.SerializedName;
  19 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
  20 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger.Verbosity;
  21 +
  22 +/**
  23 + * Modlist JSON resolver
  24 + */
  25 +public final class JsonResolver
  26 +{
  27 + /**
  28 + * Gson object for deserialisation
  29 + */
  30 + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
  31 +
  32 + /**
  33 + * Root resolver, used as parent for resolvers with no parent
  34 + */
  35 + private static final JsonResolver rootResolver = new JsonResolver().setRoot();
  36 +
  37 + @SerializedName("repositoryRoot")
  38 + private String repositoryRoot;
  39 +
  40 + @SerializedName("modRef")
  41 + private List<String> modRefs;
  42 +
  43 + @SerializedName("parentList")
  44 + private String parentList;
  45 +
  46 + /**
  47 + * True when resolving, used to prevent accidental re-entrance if a config
  48 + * is defined as its own parent!
  49 + */
  50 + private transient boolean resolving = false;
  51 +
  52 + /**
  53 + * JSON file in the file system
  54 + */
  55 + private transient File file;
  56 +
  57 + /**
  58 + * Resolved repository root
  59 + */
  60 + private transient File root;
  61 +
  62 + /**
  63 + * Resolved parent
  64 + */
  65 + private transient JsonResolver parent;
  66 +
  67 + /**
  68 + * Resolved artefacts
  69 + */
  70 + private final transient Map<String, Artefact> artefacts = new TreeMap<String, Artefact>();
  71 +
  72 + private JsonResolver()
  73 + {
  74 + }
  75 +
  76 + /**
  77 + * Configure this resolver as the root resolver
  78 + */
  79 + private JsonResolver setRoot()
  80 + {
  81 + this.parent = this;
  82 + return this;
  83 + }
  84 +
  85 + /**
  86 + * Set the source file, called by the deserialisation routine
  87 + *
  88 + * @param file JSON file
  89 + * @return fluent
  90 + */
  91 + JsonResolver setFile(File file)
  92 + {
  93 + this.file = file;
  94 + return this;
  95 + }
  96 +
  97 + boolean isResolved()
  98 + {
  99 + return this.parent != null;
  100 + }
  101 +
  102 + boolean isResolving()
  103 + {
  104 + return this.resolving;
  105 + }
  106 +
  107 + /**
  108 + * Get resolved artefacts from this resolver
  109 + */
  110 + public Map<String, Artefact> getArtefacts()
  111 + {
  112 + return Collections.<String, Artefact>unmodifiableMap(this.artefacts);
  113 + }
  114 +
  115 + /**
  116 + * Resolve this modlist, resolves parents recursively as required
  117 + *
  118 + * @param repository owning repository
  119 + * @return fluent
  120 + */
  121 + JsonResolver resolve(Repository repository)
  122 + {
  123 + if (!this.isResolved() && !this.isResolving())
  124 + {
  125 + LiteLoaderLogger.info(Verbosity.REDUCED, "Resolving mods in repository %s", this);
  126 + this.doResolve(repository);
  127 + }
  128 +
  129 + return this;
  130 + }
  131 +
  132 + private void doResolve(Repository repository)
  133 + {
  134 + this.resolving = true;
  135 + this.root = this.resolveRoot(repository);
  136 +
  137 + JsonResolver parent = this.resolveParent(repository);
  138 + if (parent.isResolving())
  139 + {
  140 + throw new IllegalStateException("Unexpected circular dependency in mod lists: " + this + " <-> " + parent);
  141 + }
  142 +
  143 + this.artefacts.clear();
  144 + this.artefacts.putAll(parent.artefacts);
  145 +
  146 + this.resolveArtefacts(repository);
  147 + this.resolving = false;
  148 + this.parent = parent;
  149 + }
  150 +
  151 + private File resolveRoot(Repository repository)
  152 + {
  153 + if (this.repositoryRoot == null)
  154 + {
  155 + if (this.file != null)
  156 + {
  157 + return this.file.getParentFile();
  158 + }
  159 +
  160 + return repository.getDefaultRepositoryRoot();
  161 + }
  162 +
  163 + // Absolute path
  164 + if (Repository.isAbsolutePath(this.repositoryRoot))
  165 + {
  166 + return new File(this.repositoryRoot);
  167 + }
  168 +
  169 + // Relative path, relative to
  170 + return new File(repository.getRoot(), this.repositoryRoot);
  171 + }
  172 +
  173 + private JsonResolver resolveParent(Repository repository)
  174 + {
  175 + if (this.parentList == null)
  176 + {
  177 + return JsonResolver.rootResolver;
  178 + }
  179 +
  180 + if (Repository.isAbsolutePath(this.parentList))
  181 + {
  182 + File path = new File(this.parentList);
  183 + return repository.getResolver(path).resolve(repository);
  184 + }
  185 +
  186 + File path = new File(repository.getRoot(), this.parentList);
  187 + return repository.getResolver(path).resolve(repository);
  188 + }
  189 +
  190 + private void resolveArtefacts(Repository repository)
  191 + {
  192 + if (this.modRefs == null)
  193 + {
  194 + return;
  195 + }
  196 +
  197 + for (String ref : this.modRefs)
  198 + {
  199 + if (Strings.isNullOrEmpty(ref))
  200 + {
  201 + continue;
  202 + }
  203 +
  204 + try
  205 + {
  206 + Artefact artefact = new Artefact(ref);
  207 + artefact.resolve(this.root);
  208 +
  209 + LiteLoaderLogger.info(Verbosity.VERBOSE, "Resolved artefact '%s' at %s", artefact.getArtefactId(), artefact.getFile());
  210 +
  211 + String artefactId = artefact.getArtefactId();
  212 + Artefact existing = this.artefacts.get(artefactId);
  213 + if (existing != null)
  214 + {
  215 + LiteLoaderLogger.info(Verbosity.VERBOSE, "Evicting artefact '%s' -> '%s'", existing, artefact.getVersion());
  216 + }
  217 +
  218 + this.artefacts.put(artefactId, artefact);
  219 + }
  220 + catch (IllegalArgumentException ex)
  221 + {
  222 + ex.printStackTrace();
  223 + }
  224 + }
  225 + }
  226 +
  227 + /**
  228 + * Deserialise a resolver from a JSON source using Gson
  229 + *
  230 + * @param file file to read
  231 + * @return deserialised resolver or new resolver if could not be read
  232 + */
  233 + static JsonResolver createFrom(File file)
  234 + {
  235 + if (file.isFile())
  236 + {
  237 + try (FileReader reader = new FileReader(file))
  238 + {
  239 + return JsonResolver.gson.fromJson(reader, JsonResolver.class).setFile(file);
  240 + }
  241 + catch (Exception ex)
  242 + {
  243 + ex.printStackTrace();
  244 + }
  245 + }
  246 +
  247 + return new JsonResolver().setFile(file);
  248 + }
  249 +
  250 + @Override
  251 + public String toString()
  252 + {
  253 + return this.file.getPath();
  254 + }
  255 +}
... ...
src/main/java/com/mumfrey/liteloader/core/api/repository/Repository.java 0 โ†’ 100644
  1 +/*
  2 + * This file is part of LiteLoader.
  3 + * Copyright (C) 2012-16 Adam Mummery-Smith
  4 + * All Rights Reserved.
  5 + */
  6 +package com.mumfrey.liteloader.core.api.repository;
  7 +
  8 +import java.io.File;
  9 +import java.util.ArrayList;
  10 +import java.util.Collection;
  11 +import java.util.Collections;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +import java.util.TreeMap;
  15 +
  16 +import com.google.common.collect.ImmutableList;
  17 +import com.google.common.collect.ImmutableList.Builder;
  18 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger;
  19 +import com.mumfrey.liteloader.util.log.LiteLoaderLogger.Verbosity;
  20 +
  21 +/**
  22 + * A mod container repository defined by JSON modlists
  23 + */
  24 +public class Repository
  25 +{
  26 + /**
  27 + * Default root which relative paths are relative to, usually the game
  28 + * directory
  29 + */
  30 + private final File root;
  31 +
  32 + /**
  33 + * Fallback repository root in case things go wrong. Usually the versionsed
  34 + * mods directory
  35 + */
  36 + private final File defaultRepositoryRoot;
  37 +
  38 + /**
  39 + * Resolvers in this repository
  40 + */
  41 + private final Map<String, JsonResolver> resolvers = new TreeMap<String, JsonResolver>();
  42 +
  43 + /**
  44 + * Global list of resolved artefacts
  45 + */
  46 + private final List<Artefact> artefacts = new ArrayList<Artefact>();
  47 +
  48 + public Repository(File root, File defaultRepositoryRoot)
  49 + {
  50 + this.root = root;
  51 + this.defaultRepositoryRoot = defaultRepositoryRoot;
  52 + }
  53 +
  54 + /**
  55 + * Get the repository root, usually the game dir
  56 + */
  57 + public File getRoot()
  58 + {
  59 + return this.root;
  60 + }
  61 +
  62 + /**
  63 + * Get the default repository location, usually the versioned mods dir
  64 + */
  65 + public File getDefaultRepositoryRoot()
  66 + {
  67 + return this.defaultRepositoryRoot;
  68 + }
  69 +
  70 + Map<String, JsonResolver> getResolvers()
  71 + {
  72 + return this.resolvers;
  73 + }
  74 +
  75 + JsonResolver getResolver(File path)
  76 + {
  77 + String location = path.getAbsolutePath();
  78 + JsonResolver resolver = this.resolvers.get(location);
  79 + if (resolver == null)
  80 + {
  81 + resolver = JsonResolver.createFrom(path);
  82 + this.resolvers.put(location, resolver);
  83 + }
  84 + return resolver;
  85 + }
  86 +
  87 + /**
  88 + * Resolve the specified modlist
  89 + *
  90 + * @param path modlist path
  91 + */
  92 + public void resolve(File path)
  93 + {
  94 + JsonResolver resolver = this.getResolver(path);
  95 + resolver.resolve(this);
  96 + this.artefacts.addAll(resolver.getArtefacts().values());
  97 + }
  98 +
  99 + /**
  100 + * Get resolved artefacts from this repository
  101 + */
  102 + public Collection<Artefact> getArtefacts()
  103 + {
  104 + return Collections.<Artefact>unmodifiableList(this.artefacts);
  105 + }
  106 +
  107 + /**
  108 + * Get all resolved files in this repository
  109 + */
  110 + public Collection<File> getFiles()
  111 + {
  112 + Builder<File> files = ImmutableList.<File>builder();
  113 + for (Artefact artefact : this.artefacts)
  114 + {
  115 + if (artefact.exists())
  116 + {
  117 + files.add(artefact.getFile());
  118 + }
  119 + else
  120 + {
  121 + LiteLoaderLogger.info(Verbosity.REDUCED, "Rejecting non-existent artefact %s at %s", artefact.getArtefactId(), artefact.getFile());
  122 + }
  123 + }
  124 + return files.build();
  125 + }
  126 +
  127 + static boolean isAbsolutePath(String path)
  128 + {
  129 + return path.matches("^([a-zA-Z]:[/\\\\]|/).+$");
  130 + }
  131 +}
... ...
src/main/java/com/mumfrey/liteloader/interfaces/LoadableFile.java
... ... @@ -10,6 +10,8 @@ import java.io.IOException;
10 10 import java.net.MalformedURLException;
11 11 import java.net.URL;
12 12 import java.nio.charset.Charset;
  13 +import java.util.ArrayList;
  14 +import java.util.Collection;
13 15 import java.util.Collections;
14 16 import java.util.HashSet;
15 17 import java.util.List;
... ... @@ -97,6 +99,11 @@ public class LoadableFile extends File implements TweakContainer&lt;File&gt;
97 99 * Mixin config resource names
98 100 */
99 101 protected Set<String> mixinConfigs = new HashSet<String>();
  102 +
  103 + /**
  104 + * Mixin errors
  105 + */
  106 + protected List<Throwable> mixinErrors = new ArrayList<Throwable>();
100 107  
101 108 /**
102 109 * Create a new tweak container wrapping the specified file
... ... @@ -344,6 +351,18 @@ public class LoadableFile extends File implements TweakContainer&lt;File&gt;
344 351 {
345 352 return this.mixinConfigs;
346 353 }
  354 +
  355 + @Override
  356 + public void registerMixinError(Throwable th)
  357 + {
  358 + this.mixinErrors.add(th);
  359 + }
  360 +
  361 + @Override
  362 + public Collection<Throwable> getMixinErrors()
  363 + {
  364 + return this.mixinErrors;
  365 + }
347 366  
348 367 @Override
349 368 public boolean hasEventTransformers()
... ...
src/main/java/com/mumfrey/liteloader/interfaces/MixinContainer.java
... ... @@ -5,6 +5,7 @@
5 5 */
6 6 package com.mumfrey.liteloader.interfaces;
7 7  
  8 +import java.util.Collection;
8 9 import java.util.Set;
9 10  
10 11 public interface MixinContainer<L> extends Loadable<L>, Injectable
... ... @@ -19,5 +20,17 @@ public interface MixinContainer&lt;L&gt; extends Loadable&lt;L&gt;, Injectable
19 20 * Get this mod's list of mixin configs
20 21 */
21 22 public abstract Set<String> getMixinConfigs();
  23 +
  24 + /**
  25 + * Register a mixin error with the container
  26 + *
  27 + * @param th caught throwable
  28 + */
  29 + public abstract void registerMixinError(Throwable th);
  30 +
  31 + /**
  32 + * @return mixin errors
  33 + */
  34 + public abstract Collection<Throwable> getMixinErrors();
22 35  
23 36 }
... ...
src/main/java/com/mumfrey/liteloader/launch/GameEnvironment.java
... ... @@ -30,4 +30,10 @@ public interface GameEnvironment
30 30 * config for legacy mods.
31 31 */
32 32 public abstract File getModsFolder();
  33 +
  34 + /**
  35 + * Get the path to a JSON file describing a mod repository layout, can be
  36 + * null if not defined
  37 + */
  38 + public abstract String getModsRepoFile();
33 39 }
... ...
src/main/java/com/mumfrey/liteloader/launch/LiteLoaderTweaker.java
... ... @@ -41,7 +41,8 @@ public class LiteLoaderTweaker implements ITweaker
41 41 // TODO Version - 1.11.2
42 42 public static final String VERSION = "1.11.2";
43 43  
44   - protected static final String bootstrapClassName = "com.mumfrey.liteloader.core.LiteLoaderBootstrap";
  44 + protected static final String BOOTSTRAP_CLASS = "com.mumfrey.liteloader.core.LiteLoaderBootstrap";
  45 + private static final String CLIENT_API = "com.mumfrey.liteloader.client.api.LiteLoaderCoreAPIClient";
45 46  
46 47 /**
47 48 * Loader startup state
... ... @@ -323,7 +324,7 @@ public class LiteLoaderTweaker implements ITweaker
323 324 {
324 325 this.initEnvironment(args, gameDirectory, assetsDirectory, profile);
325 326  
326   - this.bootstrap = this.spawnBootstrap(LiteLoaderTweaker.bootstrapClassName, Launch.classLoader);
  327 + this.bootstrap = this.spawnBootstrap(LiteLoaderTweaker.BOOTSTRAP_CLASS, Launch.classLoader);
327 328  
328 329 this.transformerManager = new ClassTransformerManager(this.bootstrap.getRequiredTransformers());
329 330  
... ... @@ -347,7 +348,7 @@ public class LiteLoaderTweaker implements ITweaker
347 348 {
348 349 MixinBootstrap.init();
349 350  
350   - this.bootstrap.preInit(Launch.classLoader, true, this.env.getModFilterList());
  351 + this.bootstrap.preInit(Launch.classLoader, true);
351 352  
352 353 this.injectDiscoveredTweakClasses();
353 354 StartupState.PREINIT.completed();
... ... @@ -567,7 +568,7 @@ public class LiteLoaderTweaker implements ITweaker
567 568 @Override
568 569 public void registerCoreAPIs(List<String> apisToLoad)
569 570 {
570   - apisToLoad.add(0, "com.mumfrey.liteloader.client.api.LiteLoaderCoreAPIClient");
  571 + apisToLoad.add(0, LiteLoaderTweaker.CLIENT_API);
571 572 }
572 573  
573 574 @Override
... ...
src/main/java/com/mumfrey/liteloader/launch/LiteLoaderTweakerServer.java
... ... @@ -13,9 +13,11 @@ import com.mumfrey.liteloader.util.log.LiteLoaderLogger.Verbosity;
13 13  
14 14 public class LiteLoaderTweakerServer extends LiteLoaderTweaker
15 15 {
  16 + private static final String SERVER_API = "com.mumfrey.liteloader.server.api.LiteLoaderCoreAPIServer";
  17 +
16 18 public LiteLoaderTweakerServer()
17 19 {
18   - LiteLoaderLogger.verbosity = Verbosity.REDUCED;
  20 + LiteLoaderLogger.setVerbosity(Verbosity.REDUCED);
19 21 }
20 22  
21 23 @Override
... ... @@ -26,7 +28,7 @@ public class LiteLoaderTweakerServer extends LiteLoaderTweaker
26 28 @Override
27 29 public void registerCoreAPIs(List<String> apisToLoad)
28 30 {
29   - apisToLoad.add(0, "com.mumfrey.liteloader.server.api.LiteLoaderCoreAPIServer");
  31 + apisToLoad.add(0, LiteLoaderTweakerServer.SERVER_API);
30 32 }
31 33  
32 34 @Override
... ... @@ -41,7 +43,6 @@ public class LiteLoaderTweakerServer extends LiteLoaderTweaker
41 43 public String getLaunchTarget()
42 44 {
43 45 super.getLaunchTarget();
44   -
45 46 return "net.minecraft.server.MinecraftServer";
46 47 }
47 48 }
... ...
src/main/java/com/mumfrey/liteloader/launch/LoaderBootstrap.java
... ... @@ -24,9 +24,8 @@ public interface LoaderBootstrap
24 24 *
25 25 * @param classLoader
26 26 * @param loadTweaks
27   - * @param modsToLoad
28 27 */
29   - public abstract void preInit(LaunchClassLoader classLoader, boolean loadTweaks, List<String> modsToLoad);
  28 + public abstract void preInit(LaunchClassLoader classLoader, boolean loadTweaks);
30 29  
31 30 /**
32 31 *
... ...
src/main/java/com/mumfrey/liteloader/launch/LoaderEnvironment.java
... ... @@ -11,6 +11,7 @@ import com.mumfrey.liteloader.api.manager.APIAdapter;
11 11 import com.mumfrey.liteloader.api.manager.APIProvider;
12 12 import com.mumfrey.liteloader.core.EnabledModsList;
13 13 import com.mumfrey.liteloader.core.LiteLoaderVersion;
  14 +import com.mumfrey.liteloader.core.api.repository.Repository;
14 15 import com.mumfrey.liteloader.interfaces.LoaderEnumerator;
15 16  
16 17 /**
... ... @@ -49,6 +50,12 @@ public interface LoaderEnvironment extends GameEnvironment
49 50 * about which mods are enabled/disabled.
50 51 */
51 52 public abstract EnabledModsList getEnabledModsList();
  53 +
  54 + /**
  55 + * The mod repository defined by command-line option which specifies a JSON
  56 + * file defining a mod repository
  57 + */
  58 + public abstract Repository getModRepository();
52 59  
53 60 /**
54 61 * The enumerator manages mod container and class discovery
... ...
src/main/java/com/mumfrey/liteloader/launch/StartupEnvironment.java
... ... @@ -31,6 +31,7 @@ public abstract class StartupEnvironment implements GameEnvironment
31 31  
32 32 private ArgumentAcceptingOptionSpec<String> modsDirOption;
33 33 private ArgumentAcceptingOptionSpec<String> modsOption;
  34 + private ArgumentAcceptingOptionSpec<String> modsRepoOption;
34 35 private ArgumentAcceptingOptionSpec<String> apisOption;
35 36 private NonOptionArgumentSpec<String> unparsedOptions;
36 37 private OptionSet parsedOptions;
... ... @@ -84,6 +85,8 @@ public abstract class StartupEnvironment implements GameEnvironment
84 85  
85 86 this.modsOption = optionParser.accepts("mods", "Comma-separated list of mods to load")
86 87 .withRequiredArg().ofType(String.class).withValuesSeparatedBy(',');
  88 + this.modsRepoOption = optionParser.accepts("modRepo", "Path to a JSON file which defines a mod repository")
  89 + .withRequiredArg().ofType(String.class);
87 90 this.apisOption = optionParser.accepts("api", "Additional API classes to load")
88 91 .withRequiredArg().ofType(String.class);
89 92 this.modsDirOption = optionParser.accepts("modsDir", "Path to 'mods' folder to use instead of default")
... ... @@ -239,4 +242,13 @@ public abstract class StartupEnvironment implements GameEnvironment
239 242 {
240 243 return this.getOptionalDirectory(this.gameDirectory, this.modsDirOption, "mods");
241 244 }
  245 +
  246 + /**
  247 + * Get the mods repo json path
  248 + */
  249 + @Override
  250 + public String getModsRepoFile()
  251 + {
  252 + return (this.parsedOptions.has(this.modsRepoOption)) ? this.modsRepoOption.value(this.parsedOptions) : null;
  253 + }
242 254 }
... ...
src/main/java/com/mumfrey/liteloader/permissions/PermissionsManagerClient.java
... ... @@ -47,7 +47,7 @@ public final class PermissionsManagerClient implements PermissionsManager, Plugi
47 47 * Permissions permissible which is a proxy for permissions that are common
48 48 * to all mods.
49 49 */
50   - private final static Permissible allMods = new PermissibleAllMods();
  50 + private static final Permissible allMods = new PermissibleAllMods();
51 51  
52 52 /**
53 53 * List of registered client mods supporting permissions
... ...
src/main/java/com/mumfrey/liteloader/transformers/IsolatedClassWriter.java
... ... @@ -69,7 +69,9 @@ public class IsolatedClassWriter extends ClassWriter
69 69 do
70 70 {
71 71 c = c.getSuperclass();
72   - } while (!c.isAssignableFrom(d));
  72 + }
  73 + while (!c.isAssignableFrom(d));
  74 +
73 75 return c.getName().replace('.', '/');
74 76 }
75 77 }
... ...
src/main/java/com/mumfrey/liteloader/util/jinput/ComponentRegistry.java
... ... @@ -98,7 +98,8 @@ public class ComponentRegistry
98 98 components.add(component);
99 99 }
100 100  
101   - } while (component != null && components.size() < 32);
  101 + }
  102 + while (component != null && components.size() < 32);
102 103  
103 104 return components;
104 105 }
... ... @@ -150,7 +151,8 @@ public class ComponentRegistry
150 151 controllers.add(controller);
151 152 }
152 153  
153   - } while (controller != null && controllers.size() < 32);
  154 + }
  155 + while (controller != null && controllers.size() < 32);
154 156  
155 157 return controllers;
156 158 }
... ...
src/main/java/com/mumfrey/liteloader/util/log/LiteLoaderLogger.java
... ... @@ -65,7 +65,7 @@ public class LiteLoaderLogger extends AbstractAppender
65 65 }
66 66 }
67 67  
68   - public static Verbosity verbosity = LiteLoaderLogger.DEBUG ? Verbosity.VERBOSE : Verbosity.NORMAL;
  68 + private static Verbosity verbosity = LiteLoaderLogger.DEBUG ? Verbosity.VERBOSE : Verbosity.NORMAL;
69 69  
70 70 static
71 71 {
... ... @@ -77,6 +77,11 @@ public class LiteLoaderLogger extends AbstractAppender
77 77 super("Internal Log Appender", null, null);
78 78 this.start();
79 79 }
  80 +
  81 + public static void setVerbosity(Verbosity verbosity)
  82 + {
  83 + LiteLoaderLogger.verbosity = verbosity;
  84 + }
80 85  
81 86 @Override
82 87 public void append(LogEvent event)
... ...