Commit 71a4ac62e63faeaccb8a86e6dd43a153f6db85e4

Authored by Mumfrey
1 parent decde838

Liteloader 1.3.2_02

+ Optifine compatibility
 + Improved chat hook
 + Added late init interface
 + Added ModUtilities class with useful functions in it
 + Added PrivateFields reflection class from Macros
 + Improved error message verbosity
 + Fixed issue with writing server log file to launcher folder
java/com/mumfrey/liteloader/InitCompleteListener.java 0 → 100644
  1 +package com.mumfrey.liteloader;
  2 +
  3 +import net.minecraft.client.Minecraft;
  4 +
  5 +import com.mumfrey.liteloader.core.LiteLoader;
  6 +
  7 +public interface InitCompleteListener extends Tickable
  8 +{
  9 + public abstract void onInitCompleted(Minecraft minecraft, LiteLoader loader);
  10 +}
java/com/mumfrey/liteloader/LiteMod.java
1 package com.mumfrey.liteloader; 1 package com.mumfrey.liteloader;
2 2
  3 +/**
  4 + * Base interface for mods
  5 + *
  6 + * @author Adam Mummery-Smith
  7 + */
3 public interface LiteMod 8 public interface LiteMod
4 { 9 {
  10 + /**
  11 + * Get the mod's display name
  12 + *
  13 + * @return display name
  14 + */
5 public abstract String getName(); 15 public abstract String getName();
6 16
  17 + /**
  18 + * Get the mod version string
  19 + *
  20 + * @return
  21 + */
7 public abstract String getVersion(); 22 public abstract String getVersion();
8 23
  24 + /**
  25 + * Do startup stuff here, minecraft is not fully initialised when this function is called so mods *must not*
  26 + * interact with minecraft in any way here
  27 + */
9 public abstract void init(); 28 public abstract void init();
10 } 29 }
java/com/mumfrey/liteloader/core/HookChat.java
1 package com.mumfrey.liteloader.core; 1 package com.mumfrey.liteloader.core;
2 2
  3 +import java.io.DataInputStream;
  4 +import java.io.DataOutputStream;
  5 +import java.io.IOException;
  6 +import java.util.Map;
  7 +
  8 +import net.minecraft.src.IntHashMap;
3 import net.minecraft.src.NetHandler; 9 import net.minecraft.src.NetHandler;
  10 +import net.minecraft.src.Packet;
4 import net.minecraft.src.Packet3Chat; 11 import net.minecraft.src.Packet3Chat;
5 12
  13 +import com.mumfrey.liteloader.util.PrivateFields;
  14 +
  15 +/**
  16 + * Proxy packet which we will register in place of the original chat packet. The class will proxy the function calls
  17 + * through to the replaced class via reflection if the original (replaced) class is NOT the basic Packet3Chat (this
  18 + * is to maintain compatibility with things like WorldEditCUI.
  19 + *
  20 + * @author Adam Mummery-Smith
  21 + *
  22 + */
6 public class HookChat extends Packet3Chat 23 public class HookChat extends Packet3Chat
7 { 24 {
8 - public static LiteLoader loader; 25 + /**
  26 + * True if this class was registered with the base class
  27 + */
  28 + private static boolean registered = false;
9 29
  30 + /**
  31 + * Handler module which is registered to handle inbound chat packets
  32 + */
  33 + private static LiteLoader packetHandler;
  34 +
  35 + /**
  36 + * Class which was overridden and will be instanced for new packets
  37 + */
  38 + private static Class<? extends Packet> proxyClass;
  39 +
  40 + /**
  41 + * Instance of the proxy packet for this packet instance
  42 + */
  43 + private Packet proxyPacket;
  44 +
  45 + /**
  46 + * Create a new chat packet proxy
  47 + */
10 public HookChat() 48 public HookChat()
11 { 49 {
12 super(); 50 super();
  51 +
  52 + try
  53 + {
  54 + if (proxyClass != null)
  55 + {
  56 + proxyPacket = (Packet)proxyClass.newInstance();
  57 + }
  58 + }
  59 + catch (Exception ex) {}
13 } 60 }
14 61
15 - public HookChat(String par1Str) 62 + /**
  63 + * Create a new chat proxy with the specified message
  64 + * @param message
  65 + */
  66 + public HookChat(String message)
16 { 67 {
17 - super(par1Str); 68 + super(message);
  69 +
  70 + try
  71 + {
  72 + if (proxyClass != null)
  73 + {
  74 + proxyPacket = (Packet)proxyClass.newInstance();
  75 + }
  76 + }
  77 + catch (Exception ex) {}
18 } 78 }
19 -  
20 - public HookChat(String par1Str, boolean par2) 79 +
  80 + @Override
  81 + public void readPacketData(DataInputStream datainputstream) throws IOException
  82 + {
  83 + if (proxyPacket != null)
  84 + {
  85 + proxyPacket.readPacketData(datainputstream);
  86 + this.message = ((Packet3Chat)proxyPacket).message;
  87 + }
  88 + else
  89 + super.readPacketData(datainputstream);
  90 + }
  91 +
  92 + @Override
  93 + public void writePacketData(DataOutputStream dataoutputstream) throws IOException
21 { 94 {
22 - super(par1Str, par2); 95 + if (proxyPacket != null)
  96 + proxyPacket.writePacketData(dataoutputstream);
  97 + else
  98 + super.writePacketData(dataoutputstream);
23 } 99 }
24 100
25 - /* (non-Javadoc)  
26 - * @see net.minecraft.src.Packet3Chat#processPacket(net.minecraft.src.NetHandler)  
27 - */  
28 @Override 101 @Override
29 - public void processPacket(NetHandler par1NetHandler) 102 + public void processPacket(NetHandler nethandler)
30 { 103 {
31 - if (loader == null || loader.onChat(this)) 104 + if (packetHandler == null || packetHandler.onChat(this))
32 { 105 {
33 - super.processPacket(par1NetHandler); 106 + if (proxyPacket != null)
  107 + proxyPacket.processPacket(nethandler);
  108 + else
  109 + super.processPacket(nethandler);
  110 + }
  111 + }
  112 +
  113 + @Override
  114 + public int getPacketSize()
  115 + {
  116 + if (proxyPacket != null)
  117 + return proxyPacket.getPacketSize();
  118 + else
  119 + return super.getPacketSize();
  120 + }
  121 +
  122 + /**
  123 + * Register the specified handler as the packet handler for this packet
  124 + * @param handler
  125 + */
  126 + public static void RegisterPacketHandler(LiteLoader handler)
  127 + {
  128 + packetHandler = handler;
  129 + }
  130 +
  131 + /**
  132 + * Register this packet as the new packet for packet ID 3
  133 + */
  134 + public static void Register()
  135 + {
  136 + Register(false);
  137 + }
  138 +
  139 + /**
  140 + * Register this packet as the new packet for packet ID 3 and optionally force re-registration even
  141 + * if registration was performed already.
  142 + *
  143 + * @param force Force registration even if registration was already performed previously.
  144 + */
  145 + public static void Register(boolean force)
  146 + {
  147 + System.out.println("LITELOADER REGISTER");
  148 + if (!registered || force)
  149 + {
  150 + try
  151 + {
  152 + IntHashMap packetIdToClassMap = Packet.packetIdToClassMap;
  153 + proxyClass = (Class)packetIdToClassMap.lookup(3);
  154 +
  155 + if (proxyClass.equals(Packet3Chat.class))
  156 + {
  157 + proxyClass = null;
  158 + }
  159 +
  160 + packetIdToClassMap.removeObject(3);
  161 + packetIdToClassMap.addKey(3, HookChat.class);
  162 +
  163 + Map packetClassToIdMap = PrivateFields.StaticFields.packetClassToIdMap.Get();
  164 + packetClassToIdMap.put(HookChat.class, Integer.valueOf(3));
  165 +
  166 + registered = true;
  167 + }
  168 + catch (Exception ex)
  169 + {
  170 + ex.printStackTrace();
  171 + }
34 } 172 }
35 } 173 }
36 } 174 }
java/com/mumfrey/liteloader/core/LiteLoader.java
@@ -8,15 +8,16 @@ import java.io.FilenameFilter; @@ -8,15 +8,16 @@ import java.io.FilenameFilter;
8 import java.io.IOException; 8 import java.io.IOException;
9 import java.io.InputStream; 9 import java.io.InputStream;
10 import java.io.InputStreamReader; 10 import java.io.InputStreamReader;
11 -import java.lang.reflect.Field; 11 +import java.io.PrintWriter;
  12 +import java.lang.reflect.Constructor;
12 import java.lang.reflect.Method; 13 import java.lang.reflect.Method;
13 -import java.lang.reflect.Modifier;  
14 import java.net.URL; 14 import java.net.URL;
15 import java.net.URLClassLoader; 15 import java.net.URLClassLoader;
  16 +import java.util.Arrays;
16 import java.util.HashMap; 17 import java.util.HashMap;
17 import java.util.Iterator; 18 import java.util.Iterator;
18 import java.util.LinkedList; 19 import java.util.LinkedList;
19 -import java.util.Map; 20 +import java.util.List;
20 import java.util.logging.ConsoleHandler; 21 import java.util.logging.ConsoleHandler;
21 import java.util.logging.FileHandler; 22 import java.util.logging.FileHandler;
22 import java.util.logging.Formatter; 23 import java.util.logging.Formatter;
@@ -28,33 +29,39 @@ import java.util.zip.ZipInputStream; @@ -28,33 +29,39 @@ import java.util.zip.ZipInputStream;
28 29
29 import net.minecraft.client.Minecraft; 30 import net.minecraft.client.Minecraft;
30 import net.minecraft.src.ConsoleLogManager; 31 import net.minecraft.src.ConsoleLogManager;
31 -import net.minecraft.src.IntHashMap;  
32 import net.minecraft.src.NetHandler; 32 import net.minecraft.src.NetHandler;
33 -import net.minecraft.src.Packet;  
34 import net.minecraft.src.Packet1Login; 33 import net.minecraft.src.Packet1Login;
35 import net.minecraft.src.Packet3Chat; 34 import net.minecraft.src.Packet3Chat;
36 import net.minecraft.src.Timer; 35 import net.minecraft.src.Timer;
37 36
38 import com.mumfrey.liteloader.ChatFilter; 37 import com.mumfrey.liteloader.ChatFilter;
39 import com.mumfrey.liteloader.ChatListener; 38 import com.mumfrey.liteloader.ChatListener;
  39 +import com.mumfrey.liteloader.InitCompleteListener;
40 import com.mumfrey.liteloader.LiteMod; 40 import com.mumfrey.liteloader.LiteMod;
41 import com.mumfrey.liteloader.LoginListener; 41 import com.mumfrey.liteloader.LoginListener;
42 import com.mumfrey.liteloader.Tickable; 42 import com.mumfrey.liteloader.Tickable;
  43 +import com.mumfrey.liteloader.util.ModUtilities;
  44 +import com.mumfrey.liteloader.util.PrivateFields;
43 45
44 /** 46 /**
45 * LiteLoader is a simple loader which provides tick events to loaded mods 47 * LiteLoader is a simple loader which provides tick events to loaded mods
46 * 48 *
47 * @author Adam Mummery-Smith 49 * @author Adam Mummery-Smith
48 - * @version 1.3.2_01 50 + * @version 1.3.2_02
49 */ 51 */
50 public final class LiteLoader implements FilenameFilter 52 public final class LiteLoader implements FilenameFilter
51 { 53 {
52 /** 54 /**
53 - * Minecraft version that we will load mods for, this will be compared 55 + * Liteloader version
  56 + */
  57 + private static final String LOADER_VERSION = "1.3.2_02";
  58 +
  59 + /**
  60 + * Minecraft versions that we will load mods for, this will be compared
54 * against the version.txt value in mod files to prevent outdated mods being 61 * against the version.txt value in mod files to prevent outdated mods being
55 * loaded!!! 62 * loaded!!!
56 */ 63 */
57 - private static final String MINECRAFT_VERSION = "1.3.1"; 64 + private static final String[] SUPPORTED_VERSIONS = { "1.3.1", "1.3.2" };
58 65
59 /** 66 /**
60 * LiteLoader is a singleton, this is the singleton instance 67 * LiteLoader is a singleton, this is the singleton instance
@@ -64,7 +71,7 @@ public final class LiteLoader implements FilenameFilter @@ -64,7 +71,7 @@ public final class LiteLoader implements FilenameFilter
64 /** 71 /**
65 * Logger for LiteLoader events 72 * Logger for LiteLoader events
66 */ 73 */
67 - private static Logger logger = Logger.getLogger("liteloader"); 74 + public static Logger logger = Logger.getLogger("liteloader");
68 75
69 /** 76 /**
70 * "mods" folder which contains mods and config files 77 * "mods" folder which contains mods and config files
@@ -90,7 +97,12 @@ public final class LiteLoader implements FilenameFilter @@ -90,7 +97,12 @@ public final class LiteLoader implements FilenameFilter
90 * List of mods which implement Tickable interface and will receive tick 97 * List of mods which implement Tickable interface and will receive tick
91 * events 98 * events
92 */ 99 */
93 - private LinkedList<Tickable> tickMods = new LinkedList<Tickable>(); 100 + private LinkedList<Tickable> tickListeners = new LinkedList<Tickable>();
  101 +
  102 + /**
  103 + *
  104 + */
  105 + private LinkedList<InitCompleteListener> initListeners = new LinkedList<InitCompleteListener>();
94 106
95 /** 107 /**
96 * List of mods which implement ChatListener interface and will receive chat 108 * List of mods which implement ChatListener interface and will receive chat
@@ -114,6 +126,8 @@ public final class LiteLoader implements FilenameFilter @@ -114,6 +126,8 @@ public final class LiteLoader implements FilenameFilter
114 */ 126 */
115 private Method mAddUrl; 127 private Method mAddUrl;
116 128
  129 + private boolean initDone = false;
  130 +
117 /** 131 /**
118 * Get the singleton instance of LiteLoader, initialises the loader if necessary 132 * Get the singleton instance of LiteLoader, initialises the loader if necessary
119 * 133 *
@@ -129,6 +143,11 @@ public final class LiteLoader implements FilenameFilter @@ -129,6 +143,11 @@ public final class LiteLoader implements FilenameFilter
129 return instance; 143 return instance;
130 } 144 }
131 145
  146 + public static final Logger getLogger()
  147 + {
  148 + return logger;
  149 + }
  150 +
132 /** 151 /**
133 * LiteLoader constructor 152 * LiteLoader constructor
134 */ 153 */
@@ -137,7 +156,7 @@ public final class LiteLoader implements FilenameFilter @@ -137,7 +156,7 @@ public final class LiteLoader implements FilenameFilter
137 // Set up loader, initialises any reflection methods needed 156 // Set up loader, initialises any reflection methods needed
138 prepareLoader(); 157 prepareLoader();
139 158
140 - logger.info("Liteloader for " + MINECRAFT_VERSION + " starting up..."); 159 + logger.info("Liteloader " + LOADER_VERSION + " starting up...");
141 160
142 // Examines the class path and mods folder and locates loadable mods 161 // Examines the class path and mods folder and locates loadable mods
143 prepareMods(); 162 prepareMods();
@@ -159,25 +178,37 @@ public final class LiteLoader implements FilenameFilter @@ -159,25 +178,37 @@ public final class LiteLoader implements FilenameFilter
159 // addURL method is used by the class loader to 178 // addURL method is used by the class loader to
160 mAddUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); 179 mAddUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
161 mAddUrl.setAccessible(true); 180 mAddUrl.setAccessible(true);
162 -  
163 - ConsoleLogManager.func_73699_a();  
164 - Formatter minecraftLogFormatter = ConsoleLogManager.loggerLogManager.getHandlers()[0].getFormatter(); 181 +
  182 + Formatter minecraftLogFormatter = null;
  183 +
  184 + try
  185 + {
  186 + Class formatterClass = Minecraft.class.getClassLoader().loadClass(ModUtilities.getObfuscatedFieldName("net.minecraft.src.ConsoleLogFormatter", "em"));
  187 + Constructor defaultConstructor = formatterClass.getDeclaredConstructor();
  188 + defaultConstructor.setAccessible(true);
  189 + minecraftLogFormatter = (Formatter)defaultConstructor.newInstance();
  190 + }
  191 + catch (Exception ex)
  192 + {
  193 + ConsoleLogManager.func_73699_a();
  194 + minecraftLogFormatter = ConsoleLogManager.loggerLogManager.getHandlers()[0].getFormatter();
  195 + }
165 196
166 logger.setUseParentHandlers(false); 197 logger.setUseParentHandlers(false);
167 198
168 StreamHandler consoleHandler = new ConsoleHandler(); 199 StreamHandler consoleHandler = new ConsoleHandler();
169 - consoleHandler.setFormatter(minecraftLogFormatter); 200 + if (minecraftLogFormatter != null) consoleHandler.setFormatter(minecraftLogFormatter);
170 logger.addHandler(consoleHandler); 201 logger.addHandler(consoleHandler);
171 202
172 FileHandler logFileHandler = new FileHandler(new File(Minecraft.getMinecraftDir(), "LiteLoader.txt").getAbsolutePath()); 203 FileHandler logFileHandler = new FileHandler(new File(Minecraft.getMinecraftDir(), "LiteLoader.txt").getAbsolutePath());
173 - logFileHandler.setFormatter(minecraftLogFormatter); 204 + if (minecraftLogFormatter != null) logFileHandler.setFormatter(minecraftLogFormatter);
174 logger.addHandler(logFileHandler); 205 logger.addHandler(logFileHandler);
175 206
176 } 207 }
177 catch (Exception ex) 208 catch (Exception ex)
178 { 209 {
179 - // TODO Auto-generated catch block  
180 - ex.printStackTrace(); 210 + logger.severe(ex.toString());
  211 + logger.severe(ex.getStackTrace().toString());
181 } 212 }
182 213
183 } 214 }
@@ -249,6 +280,8 @@ public final class LiteLoader implements FilenameFilter @@ -249,6 +280,8 @@ public final class LiteLoader implements FilenameFilter
249 */ 280 */
250 protected void findModFiles(File modFolder, LinkedList<File> modFiles) 281 protected void findModFiles(File modFolder, LinkedList<File> modFiles)
251 { 282 {
  283 + List<String> supportedVerions = Arrays.asList(SUPPORTED_VERSIONS);
  284 +
252 for (File modFile : modFolder.listFiles(this)) 285 for (File modFile : modFolder.listFiles(this))
253 { 286 {
254 try 287 try
@@ -266,7 +299,7 @@ public final class LiteLoader implements FilenameFilter @@ -266,7 +299,7 @@ public final class LiteLoader implements FilenameFilter
266 versionReader.close(); 299 versionReader.close();
267 300
268 // Only add the mod if the version matches and we were able to successfully add it to the class path 301 // Only add the mod if the version matches and we were able to successfully add it to the class path
269 - if (strVersion.equals(MINECRAFT_VERSION) && addURLToClassPath(modFile.toURI().toURL())) 302 + if (supportedVerions.contains(strVersion) && addURLToClassPath(modFile.toURI().toURL()))
270 { 303 {
271 modFiles.add(modFile); 304 modFiles.add(modFile);
272 } 305 }
@@ -368,7 +401,8 @@ public final class LiteLoader implements FilenameFilter @@ -368,7 +401,8 @@ public final class LiteLoader implements FilenameFilter
368 } 401 }
369 catch (Throwable th) 402 catch (Throwable th)
370 { 403 {
371 - logger.warning(th.getMessage()); 404 + logger.warning(th.toString());
  405 + th.printStackTrace();
372 } 406 }
373 } 407 }
374 } 408 }
@@ -384,11 +418,18 @@ public final class LiteLoader implements FilenameFilter @@ -384,11 +418,18 @@ public final class LiteLoader implements FilenameFilter
384 418
385 try 419 try
386 { 420 {
  421 + logger.info("Initialising mod " + mod.getName() + " version " + mod.getVersion());
  422 +
387 mod.init(); 423 mod.init();
388 424
389 if (mod instanceof Tickable) 425 if (mod instanceof Tickable)
390 { 426 {
391 - tickMods.add((Tickable)mod); 427 + tickListeners.add((Tickable)mod);
  428 + }
  429 +
  430 + if (mod instanceof InitCompleteListener)
  431 + {
  432 + initListeners.add((InitCompleteListener)mod);
392 } 433 }
393 434
394 if (mod instanceof ChatFilter) 435 if (mod instanceof ChatFilter)
@@ -408,7 +449,8 @@ public final class LiteLoader implements FilenameFilter @@ -408,7 +449,8 @@ public final class LiteLoader implements FilenameFilter
408 } 449 }
409 catch (Throwable th) 450 catch (Throwable th)
410 { 451 {
411 - logger.warning("Error initialising mod '" + mod.getName() + "': " + th.getMessage()); 452 + logger.warning("Error initialising mod '" + mod.getName() + "': " + th.toString());
  453 + th.printStackTrace();
412 iter.remove(); 454 iter.remove();
413 } 455 }
414 } 456 }
@@ -424,32 +466,24 @@ public final class LiteLoader implements FilenameFilter @@ -424,32 +466,24 @@ public final class LiteLoader implements FilenameFilter
424 // Chat hook 466 // Chat hook
425 if (chatListeners.size() > 0 || chatFilters.size() > 0) 467 if (chatListeners.size() > 0 || chatFilters.size() > 0)
426 { 468 {
427 - registerPacketOverride(3, HookChat.class);  
428 - HookChat.loader = this; 469 + HookChat.Register();
  470 + HookChat.RegisterPacketHandler(this);
429 } 471 }
430 472
431 // Login hook 473 // Login hook
432 if (loginListeners.size() > 0) 474 if (loginListeners.size() > 0)
433 { 475 {
434 - registerPacketOverride(1, HookLogin.class); 476 + ModUtilities.registerPacketOverride(1, HookLogin.class);
435 HookLogin.loader = this; 477 HookLogin.loader = this;
436 } 478 }
437 479
438 // Tick hook 480 // Tick hook
439 - if (tickMods.size() > 0)  
440 - {  
441 - Field modifiers = Field.class.getDeclaredField("modifiers");  
442 - modifiers.setAccessible(true);  
443 -  
444 - Field profiler = Minecraft.class.getDeclaredField(getObfuscatedFieldName("mcProfiler", "I"));  
445 - modifiers.setInt(profiler, profiler.getModifiers() & ~Modifier.FINAL);  
446 - profiler.setAccessible(true);  
447 - profiler.set(minecraft, new LiteLoaderHook(this, logger));  
448 - } 481 + PrivateFields.minecraftProfiler.SetFinal(minecraft, new LiteLoaderHook(this, logger));
449 } 482 }
450 catch (Exception ex) 483 catch (Exception ex)
451 { 484 {
452 - logger.warning("Error creating hooks: " + ex.getMessage()); 485 + logger.warning("Error creating hooks: " + ex.toString());
  486 + ex.printStackTrace();
453 } 487 }
454 } 488 }
455 489
@@ -477,7 +511,8 @@ public final class LiteLoader implements FilenameFilter @@ -477,7 +511,8 @@ public final class LiteLoader implements FilenameFilter
477 } 511 }
478 catch (Throwable th) 512 catch (Throwable th)
479 { 513 {
480 - logger.warning(th.getMessage()); 514 + logger.warning(th.toString());
  515 + th.printStackTrace();
481 } 516 }
482 517
483 return classes; 518 return classes;
@@ -591,50 +626,12 @@ public final class LiteLoader implements FilenameFilter @@ -591,50 +626,12 @@ public final class LiteLoader implements FilenameFilter
591 } 626 }
592 catch (Throwable th) 627 catch (Throwable th)
593 { 628 {
594 - logger.warning(th.getMessage());  
595 - }  
596 - }  
597 -  
598 - /**  
599 - * Register a packet override  
600 - *  
601 - * @param packetId  
602 - * @param newPacket  
603 - */  
604 - private static boolean registerPacketOverride(int packetId, Class newPacket)  
605 - {  
606 - try  
607 - {  
608 - IntHashMap packetIdToClassMap = Packet.packetIdToClassMap;  
609 - Field fPacketClassToIdMap = Packet.class.getDeclaredField(getObfuscatedFieldName("packetClassToIdMap", "a"));  
610 - fPacketClassToIdMap.setAccessible(true);  
611 - Map packetClassToIdMap = (Map)fPacketClassToIdMap.get(null);  
612 -  
613 - packetIdToClassMap.removeObject(packetId);  
614 - packetIdToClassMap.addKey(packetId, newPacket);  
615 - packetClassToIdMap.put(newPacket, Integer.valueOf(packetId));  
616 -  
617 - return true;  
618 - }  
619 - catch (Exception ex)  
620 - {  
621 - logger.warning("Error registering packet override for packet id " + packetId + ": " + ex.getMessage());  
622 - return false; 629 + logger.warning(th.toString());
  630 + th.printStackTrace();
623 } 631 }
624 } 632 }
625 633
626 /** 634 /**
627 - * Abstraction helper function  
628 - *  
629 - * @param fieldName Name of field to get, returned unmodified if in debug mode  
630 - * @return Obfuscated field name if present  
631 - */  
632 - private static String getObfuscatedFieldName(String fieldName, String obfuscatedFieldName)  
633 - {  
634 - return (!net.minecraft.src.Tessellator.instance.getClass().getSimpleName().equals("Tessellator")) ? obfuscatedFieldName : fieldName;  
635 - }  
636 -  
637 - /**  
638 * Add a URL to the Minecraft classloader class path 635 * Add a URL to the Minecraft classloader class path
639 * 636 *
640 * @param classUrl URL of the resource to add 637 * @param classUrl URL of the resource to add
@@ -652,11 +649,36 @@ public final class LiteLoader implements FilenameFilter @@ -652,11 +649,36 @@ public final class LiteLoader implements FilenameFilter
652 } 649 }
653 catch (Throwable th) 650 catch (Throwable th)
654 { 651 {
655 - logger.warning("Error adding class path entry: " + th.getMessage()); 652 + logger.warning("Error adding class path entry: " + th.toString());
  653 + th.printStackTrace();
656 } 654 }
657 655
658 return false; 656 return false;
659 } 657 }
  658 +
  659 + /**
  660 + * Late initialisation callback
  661 + */
  662 + public void onInit()
  663 + {
  664 + if (!initDone)
  665 + {
  666 + initDone = true;
  667 +
  668 + for (InitCompleteListener initMod : initListeners)
  669 + {
  670 + try
  671 + {
  672 + logger.info("Calling late init for mod " + initMod.getName());
  673 + initMod.onInitCompleted(minecraft, this);
  674 + }
  675 + catch (Throwable th)
  676 + {
  677 + logger.warning("Error initialising mod " + initMod.getName() + ": " + th.getClass().getSimpleName() + " - " + th.getMessage());
  678 + }
  679 + }
  680 + }
  681 + }
660 682
661 /** 683 /**
662 * Callback from the tick hook, ticks all tickable mods 684 * Callback from the tick hook, ticks all tickable mods
@@ -670,14 +692,7 @@ public final class LiteLoader implements FilenameFilter @@ -670,14 +692,7 @@ public final class LiteLoader implements FilenameFilter
670 // Try to get the minecraft timer object and determine the value of the partialTicks 692 // Try to get the minecraft timer object and determine the value of the partialTicks
671 if (tick || minecraftTimer == null) 693 if (tick || minecraftTimer == null)
672 { 694 {
673 - try  
674 - {  
675 - Field fTimer = Minecraft.class.getDeclaredField(getObfuscatedFieldName("Timer", "T"));  
676 - fTimer.setAccessible(true);  
677 - minecraftTimer = (Timer)fTimer.get(minecraft);  
678 -  
679 - }  
680 - catch (Exception ex) {} 695 + minecraftTimer = PrivateFields.minecraftTimer.Get(minecraft);
681 696
682 // Hooray, we got the timer reference 697 // Hooray, we got the timer reference
683 if (minecraftTimer != null) 698 if (minecraftTimer != null)
@@ -690,7 +705,7 @@ public final class LiteLoader implements FilenameFilter @@ -690,7 +705,7 @@ public final class LiteLoader implements FilenameFilter
690 boolean inGame = minecraft.renderViewEntity != null && minecraft.renderViewEntity.worldObj != null; 705 boolean inGame = minecraft.renderViewEntity != null && minecraft.renderViewEntity.worldObj != null;
691 706
692 // Iterate tickable mods 707 // Iterate tickable mods
693 - for (Tickable tickable : tickMods) 708 + for (Tickable tickable : tickListeners)
694 { 709 {
695 tickable.onTick(minecraft, partialTicks, inGame, tick); 710 tickable.onTick(minecraft, partialTicks, inGame, tick);
696 } 711 }
java/com/mumfrey/liteloader/core/LiteLoaderHook.java
1 package com.mumfrey.liteloader.core; 1 package com.mumfrey.liteloader.core;
2 2
  3 +import java.lang.reflect.Field;
  4 +import java.lang.reflect.Method;
3 import java.util.LinkedList; 5 import java.util.LinkedList;
4 import java.util.logging.Logger; 6 import java.util.logging.Logger;
5 7
  8 +import net.minecraft.client.Minecraft;
  9 +import net.minecraft.src.GameSettings;
6 import net.minecraft.src.Profiler; 10 import net.minecraft.src.Profiler;
7 11
  12 +/**
  13 + * Main LiteLoader tick hook
  14 + *
  15 + * @author Adam Mummery-Smith
  16 + */
8 public class LiteLoaderHook extends Profiler 17 public class LiteLoaderHook extends Profiler
9 { 18 {
  19 + /**
  20 + * Logger instance
  21 + */
10 private Logger logger; 22 private Logger logger;
11 23
  24 + /**
  25 + * LiteLoader instance which will receive callbacks
  26 + */
12 private LiteLoader core; 27 private LiteLoader core;
13 28
  29 + /**
  30 + * Section list, used as a kind of stack to determine where we are in the profiler stack
  31 + */
14 private LinkedList<String> sections = new LinkedList<String>(); 32 private LinkedList<String> sections = new LinkedList<String>();
  33 +
  34 + /**
  35 + * Initialisation done
  36 + */
  37 + private boolean initDone = false;
15 38
  39 + /**
  40 + * Tick clock, sent as a flag to the core onTick so that mods know it's a new tick
  41 + */
16 private boolean tick; 42 private boolean tick;
17 43
  44 + /**
  45 + * Optifine compatibility, pointer to the "Profiler" setting so we can enable it if it's disabled
  46 + */
  47 + private Field ofProfiler;
  48 +
  49 + /**
  50 + * Minecraft reference, only set if optifine compatibility is enabled
  51 + */
  52 + private Minecraft mc;
  53 +
  54 + /**
  55 + * .ctor
  56 + *
  57 + * @param core LiteLoader object which will get callbacks
  58 + * @param logger Logger instance
  59 + */
18 public LiteLoaderHook(LiteLoader core, Logger logger) 60 public LiteLoaderHook(LiteLoader core, Logger logger)
19 { 61 {
20 this.core = core; 62 this.core = core;
21 this.logger = logger; 63 this.logger = logger;
  64 +
  65 + // Detect optifine (duh!)
  66 + DetectOptifine();
22 } 67 }
23 68
  69 + /**
  70 + * Try to detect optifine using reflection
  71 + *
  72 + * @param logger
  73 + */
  74 + private void DetectOptifine()
  75 + {
  76 + try
  77 + {
  78 + ofProfiler = GameSettings.class.getDeclaredField("ofProfiler");
  79 + }
  80 + catch (SecurityException ex) {}
  81 + catch (NoSuchFieldException ex)
  82 + {
  83 + logger.info("Optifine not detected");
  84 + }
  85 + finally
  86 + {
  87 + if (ofProfiler != null)
  88 + {
  89 + logger.info(String.format("Optifine version %s detected, enabling compatibility check", GetOptifineVersion()));
  90 + mc = Minecraft.getMinecraft();
  91 + }
  92 + }
  93 + }
  94 +
  95 + /**
  96 + * Try to get the optifine version using reflection
  97 + *
  98 + * @return
  99 + */
  100 + private String GetOptifineVersion()
  101 + {
  102 + try
  103 + {
  104 + Class config = Class.forName("Config");
  105 +
  106 + if (config != null)
  107 + {
  108 + Method getVersion = config.getDeclaredMethod("getVersion");
  109 +
  110 + if (getVersion != null)
  111 + {
  112 + return (String)getVersion.invoke(null);
  113 + }
  114 + }
  115 + }
  116 + catch (Exception ex) {}
  117 +
  118 + return "Unknown";
  119 + }
  120 +
24 /* (non-Javadoc) 121 /* (non-Javadoc)
25 * @see net.minecraft.src.Profiler#startSection(java.lang.String) 122 * @see net.minecraft.src.Profiler#startSection(java.lang.String)
26 */ 123 */
27 @Override 124 @Override
28 public void startSection(String sectionName) 125 public void startSection(String sectionName)
29 { 126 {
  127 + if (!initDone)
  128 + {
  129 + initDone = true;
  130 + core.onInit();
  131 + }
  132 +
30 if (sectionName.equals("animateTick")) tick = true; 133 if (sectionName.equals("animateTick")) tick = true;
31 sections.add(sectionName); 134 sections.add(sectionName);
32 super.startSection(sectionName); 135 super.startSection(sectionName);
  136 +
  137 + if (ofProfiler != null)
  138 + {
  139 + try
  140 + {
  141 + ofProfiler.set(mc.gameSettings, true);
  142 + }
  143 + catch (IllegalArgumentException ex)
  144 + {
  145 + ofProfiler = null;
  146 + }
  147 + catch (IllegalAccessException ex)
  148 + {
  149 + ofProfiler = null;
  150 + }
  151 + }
33 } 152 }
34 153
35 /* (non-Javadoc) 154 /* (non-Javadoc)
@@ -39,7 +158,7 @@ public class LiteLoaderHook extends Profiler @@ -39,7 +158,7 @@ public class LiteLoaderHook extends Profiler
39 public void endSection() 158 public void endSection()
40 { 159 {
41 super.endSection(); 160 super.endSection();
42 - 161 +
43 String endingSection = sections.removeLast(); 162 String endingSection = sections.removeLast();
44 163
45 if (endingSection.equalsIgnoreCase("gameRenderer") && sections.getLast().equalsIgnoreCase("root")) 164 if (endingSection.equalsIgnoreCase("gameRenderer") && sections.getLast().equalsIgnoreCase("root"))
@@ -52,5 +171,4 @@ public class LiteLoaderHook extends Profiler @@ -52,5 +171,4 @@ public class LiteLoaderHook extends Profiler
52 super.endSection(); 171 super.endSection();
53 } 172 }
54 } 173 }
55 -  
56 } 174 }
java/com/mumfrey/liteloader/util/ModUtilities.java 0 → 100644
  1 +package com.mumfrey.liteloader.util;
  2 +
  3 +import java.lang.reflect.Field;
  4 +import java.util.Arrays;
  5 +import java.util.LinkedList;
  6 +import java.util.Map;
  7 +
  8 +import net.minecraft.client.Minecraft;
  9 +import net.minecraft.src.IntHashMap;
  10 +import net.minecraft.src.KeyBinding;
  11 +import net.minecraft.src.Packet;
  12 +import net.minecraft.src.Render;
  13 +import net.minecraft.src.RenderManager;
  14 +import net.minecraft.src.Tessellator;
  15 +
  16 +import com.mumfrey.liteloader.core.LiteLoader;
  17 +
  18 +public abstract class ModUtilities
  19 +{
  20 + /**
  21 + * Add a renderer map entry for the specified entity class
  22 + *
  23 + * @param entityClass
  24 + * @param renderer
  25 + */
  26 + public static void addRenderer(Class entityClass, Render renderer)
  27 + {
  28 + Map entityRenderMap = PrivateFields.entityRenderMap.Get(RenderManager.instance);
  29 + entityRenderMap.put(entityClass, renderer);
  30 + renderer.setRenderManager(RenderManager.instance);
  31 + }
  32 +
  33 + /**
  34 + * Register a packet override
  35 + *
  36 + * @param packetId
  37 + * @param newPacket
  38 + */
  39 + public static boolean registerPacketOverride(int packetId, Class newPacket)
  40 + {
  41 + try
  42 + {
  43 + IntHashMap packetIdToClassMap = Packet.packetIdToClassMap;
  44 + PrivateFields.StaticFields.packetClassToIdMap.Get();
  45 + Map packetClassToIdMap = PrivateFields.StaticFields.packetClassToIdMap.Get();
  46 +
  47 + packetIdToClassMap.removeObject(packetId);
  48 + packetIdToClassMap.addKey(packetId, newPacket);
  49 + packetClassToIdMap.put(newPacket, Integer.valueOf(packetId));
  50 +
  51 + return true;
  52 + }
  53 + catch (Exception ex)
  54 + {
  55 + LiteLoader.logger.warning("Error registering packet override for packet id " + packetId + ": " + ex.getMessage());
  56 + return false;
  57 + }
  58 + }
  59 +
  60 + /**
  61 + * Abstraction helper function
  62 + *
  63 + * @param fieldName Name of field to get, returned unmodified if in debug mode
  64 + * @return Obfuscated field name if present
  65 + */
  66 + public static String getObfuscatedFieldName(String fieldName, String obfuscatedFieldName)
  67 + {
  68 + return (!net.minecraft.src.Tessellator.instance.getClass().getSimpleName().equals("Tessellator")) ? obfuscatedFieldName : fieldName;
  69 + }
  70 +
  71 + /**
  72 + * Registers a keybind with the game settings class so that it is configurable in the "controls" screen
  73 + *
  74 + * @param newBinding key binding to add
  75 + */
  76 + public static void registerKey(KeyBinding newBinding)
  77 + {
  78 + Minecraft mc = Minecraft.getMinecraft();
  79 +
  80 + if (mc == null || mc.gameSettings == null) return;
  81 +
  82 + LinkedList<KeyBinding> keyBindings = new LinkedList<KeyBinding>();
  83 + keyBindings.addAll(Arrays.asList(mc.gameSettings.keyBindings));
  84 +
  85 + if (!keyBindings.contains(newBinding))
  86 + {
  87 + keyBindings.add(newBinding);
  88 + mc.gameSettings.keyBindings = keyBindings.toArray(new KeyBinding[0]);
  89 + }
  90 + }
  91 +
  92 + /**
  93 + * Unregisters a registered keybind with the game settings class, thus removing it from the "controls" screen
  94 + *
  95 + * @param removeBinding
  96 + */
  97 + public static void unRegisterKey(KeyBinding removeBinding)
  98 + {
  99 + Minecraft mc = Minecraft.getMinecraft();
  100 +
  101 + if (mc == null || mc.gameSettings == null) return;
  102 +
  103 + LinkedList<KeyBinding> keyBindings = new LinkedList<KeyBinding>();
  104 + keyBindings.addAll(Arrays.asList(mc.gameSettings.keyBindings));
  105 +
  106 + if (keyBindings.contains(removeBinding))
  107 + {
  108 + keyBindings.remove(removeBinding);
  109 + mc.gameSettings.keyBindings = keyBindings.toArray(new KeyBinding[0]);
  110 + }
  111 + }
  112 +}
java/com/mumfrey/liteloader/util/PrivateFields.java 0 → 100644
  1 +package com.mumfrey.liteloader.util;
  2 +
  3 +import java.io.File;
  4 +import java.lang.reflect.Field;
  5 +import java.lang.reflect.Modifier;
  6 +import java.util.Map;
  7 +import java.util.Properties;
  8 +import java.util.zip.ZipFile;
  9 +
  10 +import net.minecraft.client.Minecraft;
  11 +import net.minecraft.src.GuiButton;
  12 +import net.minecraft.src.GuiIngame;
  13 +import net.minecraft.src.GuiScreen;
  14 +import net.minecraft.src.GuiSlot;
  15 +import net.minecraft.src.GuiTexturePacks;
  16 +import net.minecraft.src.NetClientHandler;
  17 +import net.minecraft.src.NetworkManager;
  18 +import net.minecraft.src.Packet;
  19 +import net.minecraft.src.Profiler;
  20 +import net.minecraft.src.RenderEngine;
  21 +import net.minecraft.src.RenderGlobal;
  22 +import net.minecraft.src.RenderManager;
  23 +import net.minecraft.src.SoundManager;
  24 +import net.minecraft.src.SoundPool;
  25 +import net.minecraft.src.StringTranslate;
  26 +import net.minecraft.src.TexturePackCustom;
  27 +import net.minecraft.src.TexturePackImplementation;
  28 +import net.minecraft.src.TexturePackList;
  29 +import net.minecraft.src.TileEntity;
  30 +import net.minecraft.src.Timer;
  31 +import net.minecraft.src.WorldInfo;
  32 +import net.minecraft.src.WorldRenderer;
  33 +import net.minecraft.src.WorldType;
  34 +
  35 +/**
  36 + * Wrapper for obf/mcp reflection-accessed private fields, mainly added to centralise the locations I have to update the obfuscated field names
  37 + *
  38 + * @author Adam Mummery-Smith
  39 + *
  40 + * @param <P> Parent class type, the type of the class that owns the field
  41 + * @param <T> Field type, the type of the field value
  42 + */
  43 +public class PrivateFields<P, T>
  44 +{
  45 + /**
  46 + * Class to which this field belongs
  47 + */
  48 + public final Class parentClass;
  49 +
  50 + /**
  51 + * MCP name for this field
  52 + */
  53 + public final String mcpName;
  54 +
  55 + /**
  56 + * Real (obfuscated) name for this field
  57 + */
  58 + public final String name;
  59 +
  60 + /**
  61 + * Name used to access the field, determined at init
  62 + */
  63 + private final String fieldName;
  64 +
  65 + /**
  66 + * Creates a new private field entry
  67 + *
  68 + * @param owner
  69 + * @param mcpName
  70 + * @param name
  71 + */
  72 + private PrivateFields(Class owner, String mcpName, String name)
  73 + {
  74 + this.parentClass = owner;
  75 + this.mcpName = mcpName;
  76 + this.name = name;
  77 +
  78 + this.fieldName = ModUtilities.getObfuscatedFieldName(mcpName, name);
  79 + }
  80 +
  81 + /**
  82 + * Get the current value of this field on the instance class supplied
  83 + *
  84 + * @param instance Class to get the value of
  85 + * @return field value or null if errors occur
  86 + */
  87 + public T Get(P instance)
  88 + {
  89 + try
  90 + {
  91 + Field field = parentClass.getDeclaredField(fieldName);
  92 + field.setAccessible(true);
  93 + return (T)field.get(instance);
  94 + }
  95 + catch (Exception ex)
  96 + {
  97 + return null;
  98 + }
  99 + }
  100 +
  101 + /**
  102 + * Set the value of this field on the instance class supplied
  103 + *
  104 + * @param instance Object to set the value of the field on
  105 + * @param value value to set
  106 + * @return value
  107 + */
  108 + public T Set(P instance, T value)
  109 + {
  110 + try
  111 + {
  112 + Field field = parentClass.getDeclaredField(fieldName);
  113 + field.setAccessible(true);
  114 + field.set(instance, value);
  115 + }
  116 + catch (Exception ex) {}
  117 +
  118 + return value;
  119 + }
  120 +
  121 + /**
  122 + * Set the value of this FINAL field on the instance class supplied
  123 + *
  124 + * @param instance Object to set the value of the field on
  125 + * @param value value to set
  126 + * @return value
  127 + */
  128 + public T SetFinal(P instance, T value)
  129 + {
  130 + try
  131 + {
  132 + Field modifiers = Field.class.getDeclaredField("modifiers");
  133 + modifiers.setAccessible(true);
  134 +
  135 + Field field = parentClass.getDeclaredField(fieldName);
  136 + modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
  137 + field.setAccessible(true);
  138 + field.set(instance, value);
  139 + }
  140 + catch (Exception ex) {}
  141 +
  142 + return value;
  143 + }
  144 +
  145 + /**
  146 + * Static private fields
  147 + *
  148 + * @param <P> Parent class type, the type of the class that owns the field
  149 + * @param <T> Field type, the type of the field value
  150 + */
  151 + public static final class StaticFields<P, T> extends PrivateFields<P, T>
  152 + {
  153 + public StaticFields(Class owner, String mcpName, String name) { super(owner, mcpName, name); }
  154 + public T Get() { return Get(null); }
  155 + public void Set(T value) { Set(null, value); }
  156 +
  157 + public static final StaticFields<Packet, Map> packetClassToIdMap = new StaticFields<Packet, Map> (Packet.class, "packetClassToIdMap", "a");
  158 + public static final StaticFields<TileEntity, Map> tileEntityNameToClassMap = new StaticFields<TileEntity, Map> (TileEntity.class, "nameToClassMap", "a");
  159 + }
  160 +
  161 + public static final PrivateFields<Minecraft, Timer> minecraftTimer = new PrivateFields<Minecraft, Timer> (Minecraft.class, "timer", "T");
  162 + public static final PrivateFields<RenderManager, Map> entityRenderMap = new PrivateFields<RenderManager, Map> (RenderManager.class, "entityRenderMap", "o");
  163 + public static final PrivateFields<Minecraft, Profiler> minecraftProfiler = new PrivateFields<Minecraft, Profiler> (Minecraft.class, "mcProfiler", "I");
  164 +}
  165 +