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 package com.mumfrey.liteloader.core; 1 package com.mumfrey.liteloader.core;
2 2
3 import java.io.File; 3 import java.io.File;
  4 +import java.io.IOException;
  5 +import java.net.MalformedURLException;
4 import java.util.logging.Logger; 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 import com.mumfrey.liteloader.resources.ModResourcePack; 14 import com.mumfrey.liteloader.resources.ModResourcePack;
7 import com.mumfrey.liteloader.resources.ModResourcePackDir; 15 import com.mumfrey.liteloader.resources.ModResourcePackDir;
8 16
@@ -17,39 +25,86 @@ public class ClassPathMod extends ModFile @@ -17,39 +25,86 @@ public class ClassPathMod extends ModFile
17 25
18 private static final Logger logger = Logger.getLogger("liteloader"); 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 @Override 36 @Override
29 - protected void parseVersionFile(String strVersionData) 37 + protected String getDefaultName()
30 { 38 {
31 - // Nope 39 + return null;
32 } 40 }
33 41
34 @Override 42 @Override
35 - public boolean canRegisterAsResourcePack(String name) 43 + public void initResourcePack(String name)
36 { 44 {
37 if (this.resourcePack == null) 45 if (this.resourcePack == null)
38 { 46 {
39 if (this.isDirectory()) 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 this.resourcePack = new ModResourcePackDir(name, this); 50 this.resourcePack = new ModResourcePackDir(name, this);
43 } 51 }
44 else 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 this.resourcePack = new ModResourcePack(name, this); 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 return false; 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,9 +756,11 @@ public final class LiteLoader
756 this.disabledMods.remove(modFile); 756 this.disabledMods.remove(modFile);
757 757
758 LiteLoader.logInfo("Adding \"%s\" to active resource pack set", modFile.getAbsolutePath()); 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 LiteLoader.logInfo("Successfully added \"%s\" to active resource pack set", modFile.getAbsolutePath()); 765 LiteLoader.logInfo("Successfully added \"%s\" to active resource pack set", modFile.getAbsolutePath());
764 } 766 }
@@ -1060,9 +1062,16 @@ public final class LiteLoader @@ -1060,9 +1062,16 @@ public final class LiteLoader
1060 LiteLoader.logger.warning(String.format(string, args)); 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 \ No newline at end of file 1078 \ No newline at end of file
java/com/mumfrey/liteloader/core/LiteLoaderBootstrap.java
@@ -148,7 +148,7 @@ class LiteLoaderBootstrap implements ILoaderBootstrap @@ -148,7 +148,7 @@ class LiteLoaderBootstrap implements ILoaderBootstrap
148 LiteLoaderBootstrap.logInfo("Java reports OS=\"%s\"", System.getProperty("os.name").toLowerCase()); 148 LiteLoaderBootstrap.logInfo("Java reports OS=\"%s\"", System.getProperty("os.name").toLowerCase());
149 149
150 this.enumerator = new LiteLoaderEnumerator(this, classLoader, loadTweaks); 150 this.enumerator = new LiteLoaderEnumerator(this, classLoader, loadTweaks);
151 - this.enumerator.discoverModFiles(); 151 + this.enumerator.discoverMods();
152 152
153 LiteLoaderBootstrap.logInfo("LiteLoader PreInit completed"); 153 LiteLoaderBootstrap.logInfo("LiteLoader PreInit completed");
154 } 154 }
java/com/mumfrey/liteloader/core/LiteLoaderEnumerator.java
1 package com.mumfrey.liteloader.core; 1 package com.mumfrey.liteloader.core;
2 2
3 -import java.io.BufferedReader;  
4 import java.io.File; 3 import java.io.File;
5 import java.io.FileInputStream; 4 import java.io.FileInputStream;
6 import java.io.FileNotFoundException; 5 import java.io.FileNotFoundException;
7 import java.io.FilenameFilter; 6 import java.io.FilenameFilter;
8 import java.io.IOException; 7 import java.io.IOException;
9 -import java.io.InputStream;  
10 -import java.io.InputStreamReader;  
11 import java.io.UnsupportedEncodingException; 8 import java.io.UnsupportedEncodingException;
12 import java.net.MalformedURLException; 9 import java.net.MalformedURLException;
13 import java.net.URISyntaxException; 10 import java.net.URISyntaxException;
@@ -70,6 +67,11 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -70,6 +67,11 @@ class LiteLoaderEnumerator implements FilenameFilter
70 private final LaunchClassLoader classLoader; 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 * Classes to load, mapped by class name 75 * Classes to load, mapped by class name
74 */ 76 */
75 private final Map<String, Class<? extends LiteMod>> modsToLoad = new HashMap<String, Class<? extends LiteMod>>(); 77 private final Map<String, Class<? extends LiteMod>> modsToLoad = new HashMap<String, Class<? extends LiteMod>>();
@@ -102,7 +104,7 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -102,7 +104,7 @@ class LiteLoaderEnumerator implements FilenameFilter
102 private boolean searchModsFolder = true; 104 private boolean searchModsFolder = true;
103 private boolean searchProtectionDomain = true; 105 private boolean searchProtectionDomain = true;
104 private boolean searchClassPath = true; 106 private boolean searchClassPath = true;
105 - 107 +
106 /** 108 /**
107 * @param properties 109 * @param properties
108 * @param gameFolder 110 * @param gameFolder
@@ -114,6 +116,9 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -114,6 +116,9 @@ class LiteLoaderEnumerator implements FilenameFilter
114 this.bootstrap = bootstrap; 116 this.bootstrap = bootstrap;
115 this.classLoader = classLoader; 117 this.classLoader = classLoader;
116 this.loadTweaks = loadTweaks; 118 this.loadTweaks = loadTweaks;
  119 +
  120 + // Read the JVM class path into the local array
  121 + this.classPathEntries = this.readClassPath();
117 122
118 // Read the discovery settings from the properties 123 // Read the discovery settings from the properties
119 this.readSettings(); 124 this.readSettings();
@@ -218,8 +223,13 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -218,8 +223,13 @@ class LiteLoaderEnumerator implements FilenameFilter
218 /** 223 /**
219 * Enumerate the "mods" folder to find mod files 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 if (this.searchModsFolder) 233 if (this.searchModsFolder)
224 { 234 {
225 // Find and enumerate the "mods" folder 235 // Find and enumerate the "mods" folder
@@ -249,13 +259,10 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -249,13 +259,10 @@ class LiteLoaderEnumerator implements FilenameFilter
249 try 259 try
250 { 260 {
251 // Inject mod files discovered earlier into the class loader 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 // then search through all sources to find mod classes 264 // then search through all sources to find mod classes
258 - this.findModClasses(classPathEntries); 265 + this.findModClasses();
259 } 266 }
260 catch (Throwable th) 267 catch (Throwable th)
261 { 268 {
@@ -267,7 +274,7 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -267,7 +274,7 @@ class LiteLoaderEnumerator implements FilenameFilter
267 /** 274 /**
268 * Injects all external mod files into the launch classloader's class path 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 LiteLoaderEnumerator.logInfo("Injecting external mods into class path..."); 279 LiteLoaderEnumerator.logInfo("Injecting external mods into class path...");
273 280
@@ -275,10 +282,12 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -275,10 +282,12 @@ class LiteLoaderEnumerator implements FilenameFilter
275 { 282 {
276 try 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 LiteLoaderEnumerator.logWarning("Error injecting '%s' into classPath. The mod will not be loaded", file.getAbsolutePath()); 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,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 * For FilenameFilter interface 333 * For FilenameFilter interface
306 * 334 *
307 * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String) 335 * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
@@ -336,7 +364,15 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -336,7 +364,15 @@ class LiteLoaderEnumerator implements FilenameFilter
336 // Check for a version file 364 // Check for a version file
337 if (versionEntry != null) 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 if (strVersion != null) 377 if (strVersion != null)
342 { 378 {
@@ -369,14 +405,13 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -369,14 +405,13 @@ class LiteLoaderEnumerator implements FilenameFilter
369 } 405 }
370 else if (isVersionedModFolder && this.loadTweaks && this.readJarFiles && modFile.getName().toLowerCase().endsWith(".jar")) 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 modZip.close(); 411 modZip.close();
376 } 412 }
377 catch (Exception ex) 413 catch (Exception ex)
378 { 414 {
379 - ex.printStackTrace(System.err);  
380 LiteLoaderEnumerator.logInfo("Error enumerating '%s': Invalid zip file or error reading file", modFile.getAbsolutePath()); 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,8 +428,7 @@ class LiteLoaderEnumerator implements FilenameFilter
393 { 428 {
394 try 429 try
395 { 430 {
396 - this.addTweaksFrom(newestVersion);  
397 - 431 + this.addTweaksFromMod(newestVersion);
398 } 432 }
399 catch (Throwable th) 433 catch (Throwable th)
400 { 434 {
@@ -403,81 +437,51 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -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 * @param jarFile 459 * @param jarFile
445 * @throws IOException 460 * @throws IOException
446 */ 461 */
447 - private void addTweaksFrom(File jarFile) 462 + private void addTweaksFromJar(File jarFile)
448 { 463 {
449 JarFile jar = null; 464 JarFile jar = null;
450 465
451 - LiteLoaderEnumerator.logInfo("Searching for tweaks in '%s'", jarFile.getName());  
452 try 466 try
453 { 467 {
454 jar = new JarFile(jarFile); 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,12 +498,78 @@ class LiteLoaderEnumerator implements FilenameFilter
494 catch (IOException ex) {} 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 * Find mod classes in the class path and enumerated mod files list 569 * Find mod classes in the class path and enumerated mod files list
500 * @param classPathEntries Java class path split into string entries 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 if (this.searchProtectionDomain || this.searchClassPath) 574 if (this.searchProtectionDomain || this.searchClassPath)
505 LiteLoaderEnumerator.logInfo("Discovering mods on class path..."); 575 LiteLoaderEnumerator.logInfo("Discovering mods on class path...");
@@ -519,7 +589,7 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -519,7 +589,7 @@ class LiteLoaderEnumerator implements FilenameFilter
519 if (this.searchClassPath) 589 if (this.searchClassPath)
520 { 590 {
521 // Search through the class path and find mod classes 591 // Search through the class path and find mod classes
522 - this.findModsInClassPath(classPathEntries); 592 + this.findModsInClassPath();
523 } 593 }
524 594
525 // Search through mod files and find mod classes 595 // Search through mod files and find mod classes
@@ -582,14 +652,10 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -582,14 +652,10 @@ class LiteLoaderEnumerator implements FilenameFilter
582 } 652 }
583 } 653 }
584 654
585 - /**  
586 - * @param classPathEntries  
587 - * @param modsToLoad  
588 - */  
589 @SuppressWarnings("unchecked") 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 LiteLoaderEnumerator.logInfo("Searching %s...", classPathPart); 660 LiteLoaderEnumerator.logInfo("Searching %s...", classPathPart);
595 661
@@ -604,7 +670,7 @@ class LiteLoaderEnumerator implements FilenameFilter @@ -604,7 +670,7 @@ class LiteLoaderEnumerator implements FilenameFilter
604 } 670 }
605 671
606 this.modsToLoad.put(mod.getSimpleName(), (Class<? extends LiteMod>)mod); 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 if (modClasses.size() > 0) 676 if (modClasses.size() > 0)
java/com/mumfrey/liteloader/core/ModFile.java
1 package com.mumfrey.liteloader.core; 1 package com.mumfrey.liteloader.core;
2 2
  3 +import java.io.BufferedReader;
3 import java.io.File; 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 import java.util.HashMap; 9 import java.util.HashMap;
5 import java.util.Map; 10 import java.util.Map;
6 import java.util.logging.Logger; 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 import com.google.gson.Gson; 18 import com.google.gson.Gson;
9 import com.google.gson.JsonSyntaxException; 19 import com.google.gson.JsonSyntaxException;
  20 +import com.mumfrey.liteloader.launch.LiteLoaderTweaker;
10 import com.mumfrey.liteloader.resources.ModResourcePack; 21 import com.mumfrey.liteloader.resources.ModResourcePack;
11 22
12 /** 23 /**
@@ -33,11 +44,6 @@ public class ModFile extends File @@ -33,11 +44,6 @@ public class ModFile extends File
33 protected boolean valid = false; 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 * Name of the mod specified in the JSON file, this can be any string but should be the same between mod versions 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 protected String modName; 49 protected String modName;
@@ -45,7 +51,17 @@ public class ModFile extends File @@ -45,7 +51,17 @@ public class ModFile extends File
45 /** 51 /**
46 * Loader version 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 * File time stamp, used as sorting criteria when no revision information is found 67 * File time stamp, used as sorting criteria when no revision information is found
@@ -70,7 +86,12 @@ public class ModFile extends File @@ -70,7 +86,12 @@ public class ModFile extends File
70 /** 86 /**
71 * ALL of the parsed metadata from the file, associated with the mod later on for retrieval via the loader 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 * @param file 97 * @param file
@@ -81,29 +102,29 @@ public class ModFile extends File @@ -81,29 +102,29 @@ public class ModFile extends File
81 super(file.getAbsolutePath()); 102 super(file.getAbsolutePath());
82 103
83 this.timeStamp = this.lastModified(); 104 this.timeStamp = this.lastModified();
84 -  
85 this.parseVersionFile(strVersion); 105 this.parseVersionFile(strVersion);
86 } 106 }
87 107
88 @SuppressWarnings("unchecked") 108 @SuppressWarnings("unchecked")
89 protected void parseVersionFile(String strVersionData) 109 protected void parseVersionFile(String strVersionData)
90 { 110 {
  111 + if (Strings.isNullOrEmpty(strVersionData)) return;
  112 +
91 try 113 try
92 { 114 {
93 this.metaData = ModFile.gson.fromJson(strVersionData, HashMap.class); 115 this.metaData = ModFile.gson.fromJson(strVersionData, HashMap.class);
94 } 116 }
95 catch (JsonSyntaxException jsx) 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 return; 120 return;
99 } 121 }
100 122
101 this.modName = this.metaData.get("name"); 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 return; 128 return;
108 } 129 }
109 130
@@ -112,17 +133,26 @@ public class ModFile extends File @@ -112,17 +133,26 @@ public class ModFile extends File
112 this.revision = Float.parseFloat(this.metaData.get("revision")); 133 this.revision = Float.parseFloat(this.metaData.get("revision"));
113 this.hasRevision = true; 134 this.hasRevision = true;
114 } 135 }
  136 + catch (NullPointerException ex) {}
115 catch (Exception ex) 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 this.valid = true; 142 this.valid = true;
121 143
122 if (this.modName == null) 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 public String getModName() 158 public String getModName()
@@ -137,7 +167,7 @@ public class ModFile extends File @@ -137,7 +167,7 @@ public class ModFile extends File
137 167
138 public String getVersion() 168 public String getVersion()
139 { 169 {
140 - return this.version; 170 + return this.targetVersion;
141 } 171 }
142 172
143 public float getRevision() 173 public float getRevision()
@@ -155,6 +185,48 @@ public class ModFile extends File @@ -155,6 +185,48 @@ public class ModFile extends File
155 return this.metaData; 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 @SuppressWarnings("unchecked") 230 @SuppressWarnings("unchecked")
159 public <T> T getResourcePack() 231 public <T> T getResourcePack()
160 { 232 {
@@ -162,21 +234,28 @@ public class ModFile extends File @@ -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 * @param name 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 if (this.resourcePack == null) 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 this.resourcePack = new ModResourcePack(name, this); 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 @Override 261 @Override
@@ -201,4 +280,33 @@ public class ModFile extends File @@ -201,4 +280,33 @@ public class ModFile extends File
201 // Give up and use timestamp 280 // Give up and use timestamp
202 return (int)(otherMod.timeStamp - this.timeStamp); 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,9 +8,11 @@ import java.net.URL;
8 import java.net.URLClassLoader; 8 import java.net.URLClassLoader;
9 import java.util.ArrayList; 9 import java.util.ArrayList;
10 import java.util.HashMap; 10 import java.util.HashMap;
  11 +import java.util.HashSet;
11 import java.util.List; 12 import java.util.List;
12 import java.util.Map; 13 import java.util.Map;
13 import java.util.Map.Entry; 14 import java.util.Map.Entry;
  15 +import java.util.Set;
14 import java.util.logging.Level; 16 import java.util.logging.Level;
15 import java.util.logging.Logger; 17 import java.util.logging.Logger;
16 18
@@ -48,6 +50,8 @@ public class LiteLoaderTweaker implements ITweaker @@ -48,6 +50,8 @@ public class LiteLoaderTweaker implements ITweaker
48 50
49 private static ILoaderBootstrap bootstrap; 51 private static ILoaderBootstrap bootstrap;
50 52
  53 + private static Set<String> modTransformers = new HashSet<String>();
  54 +
51 private List<String> singularLaunchArgs = new ArrayList<String>(); 55 private List<String> singularLaunchArgs = new ArrayList<String>();
52 56
53 private Map<String, String> launchArgs; 57 private Map<String, String> launchArgs;
@@ -145,8 +149,16 @@ public class LiteLoaderTweaker implements ITweaker @@ -145,8 +149,16 @@ public class LiteLoaderTweaker implements ITweaker
145 @Override 149 @Override
146 public void injectIntoClassLoader(LaunchClassLoader classLoader) 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 classLoader.registerTransformer(LiteLoaderTransformer.class.getName()); 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 @Override 164 @Override
@@ -175,29 +187,37 @@ public class LiteLoaderTweaker implements ITweaker @@ -175,29 +187,37 @@ public class LiteLoaderTweaker implements ITweaker
175 return args.toArray(new String[args.size()]); 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 return false; 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 * @param url URL to add 222 * @param url URL to add
203 */ 223 */