Commit 5d8597e66e356c74cb2f24a1d3c63718bb1a24f8

Authored by Mumfrey
1 parent f5f17094

LiteLoader 1.6.4_02 - experimental - allow mods to provide tweaks and transformers

java/com/mumfrey/liteloader/core/ClassPathMod.java
1 1 package com.mumfrey.liteloader.core;
2 2  
3 3 import java.io.File;
  4 +import java.io.IOException;
  5 +import java.net.MalformedURLException;
4 6 import java.util.logging.Logger;
  7 +import java.util.zip.ZipEntry;
  8 +import java.util.zip.ZipFile;
5 9  
  10 +import net.minecraft.launchwrapper.LaunchClassLoader;
  11 +
  12 +import com.google.common.base.Charsets;
  13 +import com.google.common.io.Files;
6 14 import com.mumfrey.liteloader.resources.ModResourcePack;
7 15 import com.mumfrey.liteloader.resources.ModResourcePackDir;
8 16  
... ... @@ -17,39 +25,86 @@ public class ClassPathMod extends ModFile
17 25  
18 26 private static final Logger logger = Logger.getLogger("liteloader");
19 27  
20   - ClassPathMod(File file, String name, String version)
  28 + ClassPathMod(File file, String fallbackName)
21 29 {
22   - super(file, "");
  30 + super(file, ClassPathMod.getVersionMetaDataString(file));
23 31  
24   - this.modName = name;
25   - this.version = version;
  32 + if (this.modName == null) this.modName = fallbackName;
  33 + if (this.targetVersion == null) this.targetVersion = LiteLoaderBootstrap.VERSION.getMinecraftVersion();
26 34 }
27   -
  35 +
28 36 @Override
29   - protected void parseVersionFile(String strVersionData)
  37 + protected String getDefaultName()
30 38 {
31   - // Nope
  39 + return null;
32 40 }
33 41  
34 42 @Override
35   - public boolean canRegisterAsResourcePack(String name)
  43 + public void initResourcePack(String name)
36 44 {
37 45 if (this.resourcePack == null)
38 46 {
39 47 if (this.isDirectory())
40 48 {
41   - ClassPathMod.logger.info(String.format("Registering \"%s/%s\" as mod resource pack with identifier \"%s\"", this.getParentFile().getName(), this.getName(), name));
  49 + ClassPathMod.logger.info(String.format("Setting up \"%s/%s\" as mod resource pack with identifier \"%s\"", this.getParentFile().getName(), this.getName(), name));
42 50 this.resourcePack = new ModResourcePackDir(name, this);
43 51 }
44 52 else
45 53 {
46   - ClassPathMod.logger.info(String.format("Registering \"%s\" as mod resource pack with identifier \"%s\"", this.getName(), name));
  54 + ClassPathMod.logger.info(String.format("Setting up \"%s\" as mod resource pack with identifier \"%s\"", this.getName(), name));
47 55 this.resourcePack = new ModResourcePack(name, this);
48 56 }
49   -
50   - return true;
51 57 }
52   -
  58 + }
  59 +
  60 + @Override
  61 + public boolean injectIntoClassPath(LaunchClassLoader classLoader, boolean injectIntoParent) throws MalformedURLException
  62 + {
  63 + // Can't inject a class path entry into the class path!
53 64 return false;
54 65 }
  66 +
  67 + @Override
  68 + public boolean isInjected()
  69 + {
  70 + return true;
  71 + }
  72 +
  73 + private static String getVersionMetaDataString(File file)
  74 + {
  75 + try
  76 + {
  77 + if (file.isDirectory())
  78 + {
  79 + File versionMetaFile = new File(file, "litemod.json");
  80 + if (versionMetaFile.exists())
  81 + {
  82 + return Files.toString(versionMetaFile, Charsets.UTF_8);
  83 + }
  84 + }
  85 + else
  86 + {
  87 + String strVersion = null;
  88 + ZipFile modZip = new ZipFile(file);
  89 + ZipEntry versionEntry = modZip.getEntry("litemod.json");
  90 + if (versionEntry != null)
  91 + {
  92 + try
  93 + {
  94 + strVersion = ModFile.zipEntryToString(modZip, versionEntry);
  95 + }
  96 + catch (IOException ex) {}
  97 + }
  98 +
  99 + modZip.close();
  100 + return strVersion;
  101 + }
  102 + }
  103 + catch (IOException ex)
  104 + {
  105 + ex.printStackTrace();
  106 + }
  107 +
  108 + return null;
  109 + }
55 110 }
... ...
java/com/mumfrey/liteloader/core/LiteLoader.java
... ... @@ -756,9 +756,11 @@ public final class LiteLoader
756 756 this.disabledMods.remove(modFile);
757 757  
758 758 LiteLoader.logInfo("Adding \"%s\" to active resource pack set", modFile.getAbsolutePath());
759   - if (modName != null && modFile.canRegisterAsResourcePack(modName))
  759 + if (modName != null)
760 760 {
761   - if (this.registerModResourcePack((ResourcePack)modFile.getResourcePack()))
  761 + modFile.initResourcePack(modName);
  762 +
  763 + if (modFile.hasResourcePack() && this.registerModResourcePack((ResourcePack)modFile.getResourcePack()))
762 764 {
763 765 LiteLoader.logInfo("Successfully added \"%s\" to active resource pack set", modFile.getAbsolutePath());
764 766 }
... ... @@ -1060,9 +1062,16 @@ public final class LiteLoader
1060 1062 LiteLoader.logger.warning(String.format(string, args));
1061 1063 }
1062 1064  
1063   - public static void populateCrashReport(CrashReport crashReport)
  1065 + /**
  1066 + * @param objCrashReport This is an object so that we don't need to transform the obfuscated name in the transformer
  1067 + */
  1068 + public static void populateCrashReport(Object objCrashReport)
1064 1069 {
1065   - crashReport.getCategory().addCrashSectionCallable("Mod Pack", new CallableLiteLoaderBrand(crashReport));
1066   - crashReport.getCategory().addCrashSectionCallable("LiteLoader Mods", new CallableLiteLoaderMods(crashReport));
  1070 + if (objCrashReport instanceof CrashReport)
  1071 + {
  1072 + CrashReport crashReport = (CrashReport)objCrashReport;
  1073 + crashReport.getCategory().addCrashSectionCallable("Mod Pack", new CallableLiteLoaderBrand(crashReport));
  1074 + crashReport.getCategory().addCrashSectionCallable("LiteLoader Mods", new CallableLiteLoaderMods(crashReport));
  1075 + }
1067 1076 }
1068 1077 }
1069 1078 \ No newline at end of file
... ...
java/com/mumfrey/liteloader/core/LiteLoaderBootstrap.java
... ... @@ -148,7 +148,7 @@ class LiteLoaderBootstrap implements ILoaderBootstrap
148 148 LiteLoaderBootstrap.logInfo("Java reports OS=\"%s\"", System.getProperty("os.name").toLowerCase());
149 149  
150 150 this.enumerator = new LiteLoaderEnumerator(this, classLoader, loadTweaks);
151   - this.enumerator.discoverModFiles();
  151 + this.enumerator.discoverMods();
152 152  
153 153 LiteLoaderBootstrap.logInfo("LiteLoader PreInit completed");
154 154 }
... ...
java/com/mumfrey/liteloader/core/LiteLoaderEnumerator.java
1 1 package com.mumfrey.liteloader.core;
2 2  
3   -import java.io.BufferedReader;
4 3 import java.io.File;
5 4 import java.io.FileInputStream;
6 5 import java.io.FileNotFoundException;
7 6 import java.io.FilenameFilter;
8 7 import java.io.IOException;
9   -import java.io.InputStream;
10   -import java.io.InputStreamReader;
11 8 import java.io.UnsupportedEncodingException;
12 9 import java.net.MalformedURLException;
13 10 import java.net.URISyntaxException;
... ... @@ -70,6 +67,11 @@ class LiteLoaderEnumerator implements FilenameFilter
70 67 private final LaunchClassLoader classLoader;
71 68  
72 69 /**
  70 + * Array of class path entries specified to the JVM instance
  71 + */
  72 + private final String[] classPathEntries;
  73 +
  74 + /**
73 75 * Classes to load, mapped by class name
74 76 */
75 77 private final Map<String, Class<? extends LiteMod>> modsToLoad = new HashMap<String, Class<? extends LiteMod>>();
... ... @@ -102,7 +104,7 @@ class LiteLoaderEnumerator implements FilenameFilter
102 104 private boolean searchModsFolder = true;
103 105 private boolean searchProtectionDomain = true;
104 106 private boolean searchClassPath = true;
105   -
  107 +
106 108 /**
107 109 * @param properties
108 110 * @param gameFolder
... ... @@ -114,6 +116,9 @@ class LiteLoaderEnumerator implements FilenameFilter
114 116 this.bootstrap = bootstrap;
115 117 this.classLoader = classLoader;
116 118 this.loadTweaks = loadTweaks;
  119 +
  120 + // Read the JVM class path into the local array
  121 + this.classPathEntries = this.readClassPath();
117 122  
118 123 // Read the discovery settings from the properties
119 124 this.readSettings();
... ... @@ -218,8 +223,13 @@ class LiteLoaderEnumerator implements FilenameFilter
218 223 /**
219 224 * Enumerate the "mods" folder to find mod files
220 225 */
221   - protected void discoverModFiles()
  226 + protected void discoverMods()
222 227 {
  228 + if (this.searchClassPath)
  229 + {
  230 + this.findTweaksInClassPath();
  231 + }
  232 +
223 233 if (this.searchModsFolder)
224 234 {
225 235 // Find and enumerate the "mods" folder
... ... @@ -249,13 +259,10 @@ class LiteLoaderEnumerator implements FilenameFilter
249 259 try
250 260 {
251 261 // Inject mod files discovered earlier into the class loader
252   - this.injectIntoClassPath();
253   -
254   - // Split the environment classpath into entries
255   - String[] classPathEntries = this.readClassPath();
  262 + this.injectDiscoveredModFiles();
256 263  
257 264 // then search through all sources to find mod classes
258   - this.findModClasses(classPathEntries);
  265 + this.findModClasses();
259 266 }
260 267 catch (Throwable th)
261 268 {
... ... @@ -267,7 +274,7 @@ class LiteLoaderEnumerator implements FilenameFilter
267 274 /**
268 275 * Injects all external mod files into the launch classloader's class path
269 276 */
270   - private void injectIntoClassPath()
  277 + private void injectDiscoveredModFiles()
271 278 {
272 279 LiteLoaderEnumerator.logInfo("Injecting external mods into class path...");
273 280  
... ... @@ -275,10 +282,12 @@ class LiteLoaderEnumerator implements FilenameFilter
275 282 {
276 283 try
277 284 {
278   - LiteLoaderEnumerator.logInfo("Injecting mod file '%s' into classpath", file.getAbsolutePath());
279   - this.classLoader.addURL(file.toURI().toURL());
  285 + if (file.injectIntoClassPath(this.classLoader, false))
  286 + {
  287 + LiteLoaderEnumerator.logInfo("Successfully injected mod file '%s' into classpath", file.getAbsolutePath());
  288 + }
280 289 }
281   - catch (Exception ex)
  290 + catch (MalformedURLException ex)
282 291 {
283 292 LiteLoaderEnumerator.logWarning("Error injecting '%s' into classPath. The mod will not be loaded", file.getAbsolutePath());
284 293 }
... ... @@ -302,6 +311,25 @@ class LiteLoaderEnumerator implements FilenameFilter
302 311 }
303 312  
304 313 /**
  314 + * Search class path entries for mod class path entries which contain tweaks, mainly for dev environment purposes
  315 + */
  316 + private void findTweaksInClassPath()
  317 + {
  318 + LiteLoaderEnumerator.logInfo("Discovering tweaks on class path...");
  319 +
  320 + for (String classPathPart : this.classPathEntries)
  321 + {
  322 + File packagePath = new File(classPathPart);
  323 + ClassPathMod classPathMod = new ClassPathMod(packagePath, null);
  324 +
  325 + if (classPathMod.hasTweakClass() || classPathMod.hasClassTransformer())
  326 + {
  327 + this.addTweaksFromMod(classPathMod);
  328 + }
  329 + }
  330 + }
  331 +
  332 + /**
305 333 * For FilenameFilter interface
306 334 *
307 335 * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
... ... @@ -336,7 +364,15 @@ class LiteLoaderEnumerator implements FilenameFilter
336 364 // Check for a version file
337 365 if (versionEntry != null)
338 366 {
339   - String strVersion = this.readVersion(modZip, versionEntry);
  367 + String strVersion = null;
  368 + try
  369 + {
  370 + strVersion = ModFile.zipEntryToString(modZip, versionEntry);
  371 + }
  372 + catch (IOException ex)
  373 + {
  374 + LiteLoaderEnumerator.logWarning("Error reading version data from %s", modZip.getName());
  375 + }
340 376  
341 377 if (strVersion != null)
342 378 {
... ... @@ -369,14 +405,13 @@ class LiteLoaderEnumerator implements FilenameFilter
369 405 }
370 406 else if (isVersionedModFolder && this.loadTweaks && this.readJarFiles && modFile.getName().toLowerCase().endsWith(".jar"))
371 407 {
372   - this.addTweaksFrom(modFile);
  408 + this.addTweaksFromJar(modFile);
373 409 }
374 410  
375 411 modZip.close();
376 412 }
377 413 catch (Exception ex)
378 414 {
379   - ex.printStackTrace(System.err);
380 415 LiteLoaderEnumerator.logInfo("Error enumerating '%s': Invalid zip file or error reading file", modFile.getAbsolutePath());
381 416 }
382 417 }
... ... @@ -393,8 +428,7 @@ class LiteLoaderEnumerator implements FilenameFilter
393 428 {
394 429 try
395 430 {
396   - this.addTweaksFrom(newestVersion);
397   -
  431 + this.addTweaksFromMod(newestVersion);
398 432 }
399 433 catch (Throwable th)
400 434 {
... ... @@ -403,81 +437,51 @@ class LiteLoaderEnumerator implements FilenameFilter
403 437 }
404 438 }
405 439 }
406   -
407   - /**
408   - * @param strVersion
409   - * @param modZip
410   - * @param versionEntry
411   - * @return
412   - * @throws IOException
413   - */
414   - public String readVersion(ZipFile modZip, ZipEntry versionEntry) throws IOException
  440 +
  441 + private void addTweaksFromMod(ModFile modFile)
415 442 {
416   - String strVersion = null;
417   - BufferedReader versionReader = null;
418   - StringBuilder versionBuilder = new StringBuilder();
419   -
420   - try
  443 + if (modFile.hasTweakClass())
421 444 {
422   - // Read the version string
423   - InputStream versionStream = modZip.getInputStream(versionEntry);
424   - versionReader = new BufferedReader(new InputStreamReader(versionStream));
425   -
426   - String versionFileLine;
427   - while ((versionFileLine = versionReader.readLine()) != null)
428   - versionBuilder.append(versionFileLine);
429   -
430   - strVersion = versionBuilder.toString();
  445 + this.addTweakFrom(modFile, modFile.getTweakClassName(), null);
431 446 }
432   - catch (Exception ex)
  447 + else if (modFile.isFile())
433 448 {
434   - LiteLoaderEnumerator.logWarning("Error reading version data from %s", modZip.getName());
  449 + this.addTweaksFromJar(modFile);
435 450 }
436   - finally
  451 +
  452 + if (modFile.hasClassTransformer())
437 453 {
438   - if (versionReader != null) versionReader.close();
  454 + this.addClassTransformerFrom(modFile, modFile.getClassTransformerClassName());
439 455 }
440   - return strVersion;
441 456 }
442 457  
443 458 /**
444 459 * @param jarFile
445 460 * @throws IOException
446 461 */
447   - private void addTweaksFrom(File jarFile)
  462 + private void addTweaksFromJar(File jarFile)
448 463 {
449 464 JarFile jar = null;
450 465  
451   - LiteLoaderEnumerator.logInfo("Searching for tweaks in '%s'", jarFile.getName());
452 466 try
453 467 {
454 468 jar = new JarFile(jarFile);
455   - Attributes manifestAttributes = jar.getManifest().getMainAttributes();
456   -
457   - String tweakClass = manifestAttributes.getValue("TweakClass");
458   - if (tweakClass != null)
  469 + if (jar.getManifest() != null)
459 470 {
460   - LiteLoaderEnumerator.logInfo("Mod file '%s' provides tweakClass '%s', adding to Launch queue", jarFile.getName(), tweakClass);
  471 + LiteLoaderEnumerator.logInfo("Searching for tweaks in '%s'", jarFile.getName());
  472 + Attributes manifestAttributes = jar.getManifest().getMainAttributes();
461 473  
462   - if (LiteLoaderTweaker.addTweaker(jarFile.toURI().toURL(), tweakClass))
  474 + String tweakClass = manifestAttributes.getValue("TweakClass");
  475 + if (tweakClass != null)
463 476 {
464   - LiteLoaderEnumerator.logInfo("tweakClass '%s' was successfully added", tweakClass);
465   - this.classLoader.addURL(jarFile.toURI().toURL());
466   - }
467   - }
468   -
469   - String classPath = manifestAttributes.getValue("Class-Path");
470   - if (classPath != null)
471   - {
472   - String[] classPathEntries = classPath.split(" ");
473   - for (String classPathEntry : classPathEntries)
474   - {
475   - File classPathJar = new File(this.bootstrap.getGameDirectory(), classPathEntry);
476   - URL jarUrl = classPathJar.toURI().toURL();
  477 + String[] classPathEntries = null;
  478 + String classPath = manifestAttributes.getValue("Class-Path");
  479 + if (classPath != null)
  480 + {
  481 + classPathEntries = classPath.split(" ");
  482 + }
477 483  
478   - LiteLoaderEnumerator.logInfo("Adding Class-Path entry: %s", classPathEntry);
479   - LiteLoaderTweaker.addURLToParentClassLoader(jarUrl);
480   - this.classLoader.addURL(jarUrl);
  484 + this.addTweakFrom(jarFile, tweakClass, classPathEntries);
481 485 }
482 486 }
483 487 }
... ... @@ -494,12 +498,78 @@ class LiteLoaderEnumerator implements FilenameFilter
494 498 catch (IOException ex) {}
495 499 }
496 500 }
  501 +
  502 + private void addTweakFrom(File jarFile, String tweakClass, String[] classPathEntries)
  503 + {
  504 + try
  505 + {
  506 + LiteLoaderEnumerator.logInfo("Mod file '%s' provides tweakClass '%s', adding to Launch queue", jarFile.getName(), tweakClass);
  507 + if (LiteLoaderTweaker.addTweaker(tweakClass))
  508 + {
  509 + LiteLoaderEnumerator.logInfo("tweakClass '%s' was successfully added", tweakClass);
  510 + this.injectIntoClassLoader(jarFile);
  511 +
  512 + if (classPathEntries != null)
  513 + {
  514 + for (String classPathEntry : classPathEntries)
  515 + {
  516 + try
  517 + {
  518 + File classPathJar = new File(this.bootstrap.getGameDirectory(), classPathEntry);
  519 + URL classPathJarUrl = classPathJar.toURI().toURL();
  520 +
  521 + LiteLoaderEnumerator.logInfo("Adding Class-Path entry: %s", classPathEntry);
  522 + LiteLoaderTweaker.addURLToParentClassLoader(classPathJarUrl);
  523 + this.classLoader.addURL(classPathJarUrl);
  524 + }
  525 + catch (MalformedURLException ex) {}
  526 + }
  527 + }
  528 + }
  529 + }
  530 + catch (MalformedURLException ex)
  531 + {
  532 + }
  533 + }
497 534  
  535 + private void addClassTransformerFrom(File jarFile, String classTransformerClass)
  536 + {
  537 + try
  538 + {
  539 + LiteLoaderEnumerator.logInfo("Mod file '%s' provides classTransformer '%s', adding to class loader", jarFile.getName(), classTransformerClass);
  540 + if (LiteLoaderTweaker.addClassTransformer(classTransformerClass))
  541 + {
  542 + LiteLoaderEnumerator.logInfo("classTransformer '%s' was successfully added", classTransformerClass);
  543 + this.injectIntoClassLoader(jarFile);
  544 + }
  545 + }
  546 + catch (MalformedURLException ex)
  547 + {
  548 + }
  549 + }
  550 +
  551 + /**
  552 + * @param jarFile
  553 + * @throws MalformedURLException
  554 + */
  555 + private void injectIntoClassLoader(File jarFile) throws MalformedURLException
  556 + {
  557 + if (jarFile instanceof ModFile)
  558 + {
  559 + ((ModFile)jarFile).injectIntoClassPath(this.classLoader, true);
  560 + }
  561 + else
  562 + {
  563 + LiteLoaderTweaker.addURLToParentClassLoader(jarFile.toURI().toURL());
  564 + this.classLoader.addURL(jarFile.toURI().toURL());
  565 + }
  566 + }
  567 +
498 568 /**
499 569 * Find mod classes in the class path and enumerated mod files list
500 570 * @param classPathEntries Java class path split into string entries
501 571 */
502   - private void findModClasses(String[] classPathEntries)
  572 + private void findModClasses()
503 573 {
504 574 if (this.searchProtectionDomain || this.searchClassPath)
505 575 LiteLoaderEnumerator.logInfo("Discovering mods on class path...");
... ... @@ -519,7 +589,7 @@ class LiteLoaderEnumerator implements FilenameFilter
519 589 if (this.searchClassPath)
520 590 {
521 591 // Search through the class path and find mod classes
522   - this.findModsInClassPath(classPathEntries);
  592 + this.findModsInClassPath();
523 593 }
524 594  
525 595 // Search through mod files and find mod classes
... ... @@ -582,14 +652,10 @@ class LiteLoaderEnumerator implements FilenameFilter
582 652 }
583 653 }
584 654  
585   - /**
586   - * @param classPathEntries
587   - * @param modsToLoad
588   - */
589 655 @SuppressWarnings("unchecked")
590   - private void findModsInClassPath(String[] classPathEntries)
  656 + private void findModsInClassPath()
591 657 {
592   - for (String classPathPart : classPathEntries)
  658 + for (String classPathPart : this.classPathEntries)
593 659 {
594 660 LiteLoaderEnumerator.logInfo("Searching %s...", classPathPart);
595 661  
... ... @@ -604,7 +670,7 @@ class LiteLoaderEnumerator implements FilenameFilter
604 670 }
605 671  
606 672 this.modsToLoad.put(mod.getSimpleName(), (Class<? extends LiteMod>)mod);
607   - this.modFiles.put(mod.getSimpleName(), new ClassPathMod(packagePath, mod.getSimpleName().substring(7), LiteLoaderBootstrap.VERSION.getLoaderVersion()));
  673 + this.modFiles.put(mod.getSimpleName(), new ClassPathMod(packagePath, mod.getSimpleName().substring(7).toLowerCase()));
608 674 }
609 675  
610 676 if (modClasses.size() > 0)
... ...
java/com/mumfrey/liteloader/core/ModFile.java
1 1 package com.mumfrey.liteloader.core;
2 2  
  3 +import java.io.BufferedReader;
3 4 import java.io.File;
  5 +import java.io.IOException;
  6 +import java.io.InputStream;
  7 +import java.io.InputStreamReader;
  8 +import java.net.MalformedURLException;
4 9 import java.util.HashMap;
5 10 import java.util.Map;
6 11 import java.util.logging.Logger;
  12 +import java.util.zip.ZipEntry;
  13 +import java.util.zip.ZipFile;
  14 +
  15 +import net.minecraft.launchwrapper.LaunchClassLoader;
  16 +import joptsimple.internal.Strings;
7 17  
8 18 import com.google.gson.Gson;
9 19 import com.google.gson.JsonSyntaxException;
  20 +import com.mumfrey.liteloader.launch.LiteLoaderTweaker;
10 21 import com.mumfrey.liteloader.resources.ModResourcePack;
11 22  
12 23 /**
... ... @@ -33,11 +44,6 @@ public class ModFile extends File
33 44 protected boolean valid = false;
34 45  
35 46 /**
36   - * True if parsed from JSON, false if fallback mode using legacy version.txt
37   - */
38   -// protected boolean json = false;
39   -
40   - /**
41 47 * Name of the mod specified in the JSON file, this can be any string but should be the same between mod versions
42 48 */
43 49 protected String modName;
... ... @@ -45,7 +51,17 @@ public class ModFile extends File
45 51 /**
46 52 * Loader version
47 53 */
48   - protected String version;
  54 + protected String targetVersion;
  55 +
  56 + /**
  57 + * Name of the tweak class
  58 + */
  59 + protected String tweakClassName;
  60 +
  61 + /**
  62 + * Name of the class transof
  63 + */
  64 + protected String classTransformerClassName;
49 65  
50 66 /**
51 67 * File time stamp, used as sorting criteria when no revision information is found
... ... @@ -70,7 +86,12 @@ public class ModFile extends File
70 86 /**
71 87 * ALL of the parsed metadata from the file, associated with the mod later on for retrieval via the loader
72 88 */
73   - protected HashMap<String, String> metaData = new HashMap<String, String>();
  89 + protected Map<String, String> metaData = new HashMap<String, String>();
  90 +
  91 + /**
  92 + * True once this file has been injected into the class path
  93 + */
  94 + private boolean injected;
74 95  
75 96 /**
76 97 * @param file
... ... @@ -81,29 +102,29 @@ public class ModFile extends File
81 102 super(file.getAbsolutePath());
82 103  
83 104 this.timeStamp = this.lastModified();
84   -
85 105 this.parseVersionFile(strVersion);
86 106 }
87 107  
88 108 @SuppressWarnings("unchecked")
89 109 protected void parseVersionFile(String strVersionData)
90 110 {
  111 + if (Strings.isNullOrEmpty(strVersionData)) return;
  112 +
91 113 try
92 114 {
93 115 this.metaData = ModFile.gson.fromJson(strVersionData, HashMap.class);
94 116 }
95 117 catch (JsonSyntaxException jsx)
96 118 {
97   - ModFile.logger.warning("Error reading litemod.json in " + this.getName() + ", JSON syntax exception: " + jsx.getMessage());
  119 + ModFile.logger.warning("Error reading litemod.json in " + this.getAbsolutePath() + ", JSON syntax exception: " + jsx.getMessage());
98 120 return;
99 121 }
100 122  
101 123 this.modName = this.metaData.get("name");
102   -
103   - this.version = this.metaData.get("mcversion");
104   - if (this.version == null)
  124 + this.targetVersion = this.metaData.get("mcversion");
  125 + if (this.targetVersion == null)
105 126 {
106   - ModFile.logger.warning("Mod in " + this.getName() + " has no loader version number reading litemod.json");
  127 + ModFile.logger.warning("Mod in " + this.getAbsolutePath() + " has no loader version number reading litemod.json");
107 128 return;
108 129 }
109 130  
... ... @@ -112,17 +133,26 @@ public class ModFile extends File
112 133 this.revision = Float.parseFloat(this.metaData.get("revision"));
113 134 this.hasRevision = true;
114 135 }
  136 + catch (NullPointerException ex) {}
115 137 catch (Exception ex)
116 138 {
117   - ModFile.logger.warning("Mod in " + this.getName() + " has an invalid revision number reading litemod.json");
  139 + ModFile.logger.warning("Mod in " + this.getAbsolutePath() + " has an invalid revision number reading litemod.json");
118 140 }
119 141  
120 142 this.valid = true;
121 143  
122 144 if (this.modName == null)
123 145 {
124   - this.modName = this.getName().replaceAll("[^a-zA-Z]", "");
  146 + this.modName = this.getDefaultName();
125 147 }
  148 +
  149 + this.tweakClassName = this.metaData.get("tweakClass");
  150 + this.classTransformerClassName = this.metaData.get("classTransformerClass");
  151 + }
  152 +
  153 + protected String getDefaultName()
  154 + {
  155 + return this.getName().replaceAll("[^a-zA-Z]", "");
126 156 }
127 157  
128 158 public String getModName()
... ... @@ -137,7 +167,7 @@ public class ModFile extends File
137 167  
138 168 public String getVersion()
139 169 {
140   - return this.version;
  170 + return this.targetVersion;
141 171 }
142 172  
143 173 public float getRevision()
... ... @@ -155,6 +185,48 @@ public class ModFile extends File
155 185 return this.metaData;
156 186 }
157 187  
  188 + public boolean hasTweakClass()
  189 + {
  190 + return this.tweakClassName != null;
  191 + }
  192 +
  193 + public String getTweakClassName()
  194 + {
  195 + return this.tweakClassName;
  196 + }
  197 +
  198 + public boolean hasClassTransformer()
  199 + {
  200 + return this.classTransformerClassName != null;
  201 + }
  202 +
  203 + public String getClassTransformerClassName()
  204 + {
  205 + return this.classTransformerClassName;
  206 + }
  207 +
  208 + public boolean isInjected()
  209 + {
  210 + return this.injected;
  211 + }
  212 +
  213 + public boolean injectIntoClassPath(LaunchClassLoader classLoader, boolean injectIntoParent) throws MalformedURLException
  214 + {
  215 + if (!this.injected)
  216 + {
  217 + if (injectIntoParent)
  218 + {
  219 + LiteLoaderTweaker.addURLToParentClassLoader(this.toURI().toURL());
  220 + }
  221 +
  222 + classLoader.addURL(this.toURI().toURL());
  223 + this.injected = true;
  224 + return true;
  225 + }
  226 +
  227 + return false;
  228 + }
  229 +
158 230 @SuppressWarnings("unchecked")
159 231 public <T> T getResourcePack()
160 232 {
... ... @@ -162,21 +234,28 @@ public class ModFile extends File
162 234 }
163 235  
164 236 /**
165   - * Registers this file as a minecraft resource pack
  237 + * Initialise the mod resource pack
166 238 *
167 239 * @param name
168   - * @return true if the pack was added
169 240 */
170   - public boolean canRegisterAsResourcePack(String name)
  241 + public void initResourcePack(String name)
171 242 {
172 243 if (this.resourcePack == null)
173 244 {
174   - ModFile.logger.info(String.format("Registering \"%s\" as mod resource pack with identifier \"%s\"", this.getName(), name));
  245 + ModFile.logger.info(String.format("Setting up \"%s\" as mod resource pack with identifier \"%s\"", this.getName(), name));
175 246 this.resourcePack = new ModResourcePack(name, this);
176   - return true;
177 247 }
178   -
179   - return false;
  248 + }
  249 +
  250 + /**
  251 + * Registers this file as a minecraft resource pack
  252 + *
  253 + * @param name
  254 + * @return true if the pack was added
  255 + */
  256 + public boolean hasResourcePack()
  257 + {
  258 + return (this.resourcePack != null);
180 259 }
181 260  
182 261 @Override
... ... @@ -201,4 +280,33 @@ public class ModFile extends File
201 280 // Give up and use timestamp
202 281 return (int)(otherMod.timeStamp - this.timeStamp);
203 282 }
  283 +
  284 +
  285 + /**
  286 + * @param zip
  287 + * @param entry
  288 + * @return
  289 + * @throws IOException
  290 + */
  291 + public static String zipEntryToString(ZipFile zip, ZipEntry entry) throws IOException
  292 + {
  293 + BufferedReader reader = null;
  294 + StringBuilder sb = new StringBuilder();
  295 +
  296 + try
  297 + {
  298 + InputStream stream = zip.getInputStream(entry);
  299 + reader = new BufferedReader(new InputStreamReader(stream));
  300 +
  301 + String versionFileLine;
  302 + while ((versionFileLine = reader.readLine()) != null)
  303 + sb.append(versionFileLine);
  304 + }
  305 + finally
  306 + {
  307 + if (reader != null) reader.close();
  308 + }
  309 +
  310 + return sb.toString();
  311 + }
204 312 }
... ...
java/com/mumfrey/liteloader/launch/LiteLoaderTweaker.java
... ... @@ -8,9 +8,11 @@ import java.net.URL;
8 8 import java.net.URLClassLoader;
9 9 import java.util.ArrayList;
10 10 import java.util.HashMap;
  11 +import java.util.HashSet;
11 12 import java.util.List;
12 13 import java.util.Map;
13 14 import java.util.Map.Entry;
  15 +import java.util.Set;
14 16 import java.util.logging.Level;
15 17 import java.util.logging.Logger;
16 18  
... ... @@ -48,6 +50,8 @@ public class LiteLoaderTweaker implements ITweaker
48 50  
49 51 private static ILoaderBootstrap bootstrap;
50 52  
  53 + private static Set<String> modTransformers = new HashSet<String>();
  54 +
51 55 private List<String> singularLaunchArgs = new ArrayList<String>();
52 56  
53 57 private Map<String, String> launchArgs;
... ... @@ -145,8 +149,16 @@ public class LiteLoaderTweaker implements ITweaker
145 149 @Override
146 150 public void injectIntoClassLoader(LaunchClassLoader classLoader)
147 151 {
148   - LiteLoaderTweaker.logger.info("Injecting LiteLoader Class Transformer");
  152 + LiteLoaderTweaker.logger.info("Injecting LiteLoader class transformer");
149 153 classLoader.registerTransformer(LiteLoaderTransformer.class.getName());
  154 +
  155 + for (String transformerClassName : LiteLoaderTweaker.modTransformers)
  156 + {
  157 + LiteLoaderTweaker.logger.info(String.format("Injecting additional class transformer class '%s'", transformerClassName));
  158 + classLoader.registerTransformer(transformerClassName);
  159 + }
  160 +
  161 + LiteLoaderTweaker.modTransformers.clear();
150 162 }
151 163  
152 164 @Override
... ... @@ -175,29 +187,37 @@ public class LiteLoaderTweaker implements ITweaker
175 187 return args.toArray(new String[args.size()]);
176 188 }
177 189  
178   - public static boolean addTweaker(URL tweakSource, String tweakClass)
  190 + @SuppressWarnings("unchecked")
  191 + public static boolean addTweaker(String tweakClass)
179 192 {
180   - if (LiteLoaderTweaker.preInit)
  193 + if (!LiteLoaderTweaker.preInit)
181 194 {
182   - @SuppressWarnings("unchecked")
183   - List<String> tweakers = (List<String>)Launch.blackboard.get("TweakClasses");
184   - if (tweakers != null)
185   - {
186   - if (LiteLoaderTweaker.addURLToParentClassLoader(tweakSource))
187   - {
188   - tweakers.add(tweakClass);
189   - return true;
190   - }
191   - }
  195 + LiteLoaderTweaker.logger.warning(String.format("Failed to add tweak class %s because preInit is already complete", tweakClass));
  196 + return false;
192 197 }
193   - else
  198 +
  199 + List<String> tweakers = (List<String>)Launch.blackboard.get("TweakClasses");
  200 + if (tweakers != null)
194 201 {
195   - LiteLoaderTweaker.logger.warning(String.format("Failed to add tweak class %s from %s because preInit is already complete", tweakClass, tweakSource));
  202 + tweakers.add(tweakClass);
  203 + return true;
196 204 }
197 205  
198 206 return false;
199 207 }
200 208  
  209 + public static boolean addClassTransformer(String transfomerClass)
  210 + {
  211 + if (!LiteLoaderTweaker.preInit)
  212 + {
  213 + LiteLoaderTweaker.logger.warning(String.format("Failed to add transformer class %s because preInit is already complete", transfomerClass));
  214 + return false;
  215 + }
  216 +
  217 + LiteLoaderTweaker.modTransformers.add(transfomerClass);
  218 + return true;
  219 + }
  220 +
201 221 /**
202 222 * @param url URL to add
203 223 */
... ...