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 1 package com.mumfrey.liteloader;
2 2  
  3 +/**
  4 + * Base interface for mods
  5 + *
  6 + * @author Adam Mummery-Smith
  7 + */
3 8 public interface LiteMod
4 9 {
  10 + /**
  11 + * Get the mod's display name
  12 + *
  13 + * @return display name
  14 + */
5 15 public abstract String getName();
6 16  
  17 + /**
  18 + * Get the mod version string
  19 + *
  20 + * @return
  21 + */
7 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 28 public abstract void init();
10 29 }
... ...
java/com/mumfrey/liteloader/core/HookChat.java
1 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 9 import net.minecraft.src.NetHandler;
  10 +import net.minecraft.src.Packet;
4 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 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 48 public HookChat()
11 49 {
12 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 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 8 import java.io.IOException;
9 9 import java.io.InputStream;
10 10 import java.io.InputStreamReader;
11   -import java.lang.reflect.Field;
  11 +import java.io.PrintWriter;
  12 +import java.lang.reflect.Constructor;
12 13 import java.lang.reflect.Method;
13   -import java.lang.reflect.Modifier;
14 14 import java.net.URL;
15 15 import java.net.URLClassLoader;
  16 +import java.util.Arrays;
16 17 import java.util.HashMap;
17 18 import java.util.Iterator;
18 19 import java.util.LinkedList;
19   -import java.util.Map;
  20 +import java.util.List;
20 21 import java.util.logging.ConsoleHandler;
21 22 import java.util.logging.FileHandler;
22 23 import java.util.logging.Formatter;
... ... @@ -28,33 +29,39 @@ import java.util.zip.ZipInputStream;
28 29  
29 30 import net.minecraft.client.Minecraft;
30 31 import net.minecraft.src.ConsoleLogManager;
31   -import net.minecraft.src.IntHashMap;
32 32 import net.minecraft.src.NetHandler;
33   -import net.minecraft.src.Packet;
34 33 import net.minecraft.src.Packet1Login;
35 34 import net.minecraft.src.Packet3Chat;
36 35 import net.minecraft.src.Timer;
37 36  
38 37 import com.mumfrey.liteloader.ChatFilter;
39 38 import com.mumfrey.liteloader.ChatListener;
  39 +import com.mumfrey.liteloader.InitCompleteListener;
40 40 import com.mumfrey.liteloader.LiteMod;
41 41 import com.mumfrey.liteloader.LoginListener;
42 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 47 * LiteLoader is a simple loader which provides tick events to loaded mods
46 48 *
47 49 * @author Adam Mummery-Smith
48   - * @version 1.3.2_01
  50 + * @version 1.3.2_02
49 51 */
50 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 61 * against the version.txt value in mod files to prevent outdated mods being
55 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 67 * LiteLoader is a singleton, this is the singleton instance
... ... @@ -64,7 +71,7 @@ public final class LiteLoader implements FilenameFilter
64 71 /**
65 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 77 * "mods" folder which contains mods and config files
... ... @@ -90,7 +97,12 @@ public final class LiteLoader implements FilenameFilter
90 97 * List of mods which implement Tickable interface and will receive tick
91 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 108 * List of mods which implement ChatListener interface and will receive chat
... ... @@ -114,6 +126,8 @@ public final class LiteLoader implements FilenameFilter
114 126 */
115 127 private Method mAddUrl;
116 128  
  129 + private boolean initDone = false;
  130 +
117 131 /**
118 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 143 return instance;
130 144 }
131 145  
  146 + public static final Logger getLogger()
  147 + {
  148 + return logger;
  149 + }
  150 +
132 151 /**
133 152 * LiteLoader constructor
134 153 */
... ... @@ -137,7 +156,7 @@ public final class LiteLoader implements FilenameFilter
137 156 // Set up loader, initialises any reflection methods needed
138 157 prepareLoader();
139 158  
140   - logger.info("Liteloader for " + MINECRAFT_VERSION + " starting up...");
  159 + logger.info("Liteloader " + LOADER_VERSION + " starting up...");
141 160  
142 161 // Examines the class path and mods folder and locates loadable mods
143 162 prepareMods();
... ... @@ -159,25 +178,37 @@ public final class LiteLoader implements FilenameFilter
159 178 // addURL method is used by the class loader to
160 179 mAddUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
161 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 197 logger.setUseParentHandlers(false);
167 198  
168 199 StreamHandler consoleHandler = new ConsoleHandler();
169   - consoleHandler.setFormatter(minecraftLogFormatter);
  200 + if (minecraftLogFormatter != null) consoleHandler.setFormatter(minecraftLogFormatter);
170 201 logger.addHandler(consoleHandler);
171 202  
172 203 FileHandler logFileHandler = new FileHandler(new File(Minecraft.getMinecraftDir(), "LiteLoader.txt").getAbsolutePath());
173   - logFileHandler.setFormatter(minecraftLogFormatter);
  204 + if (minecraftLogFormatter != null) logFileHandler.setFormatter(minecraftLogFormatter);
174 205 logger.addHandler(logFileHandler);
175 206  
176 207 }
177 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 280 */
250 281 protected void findModFiles(File modFolder, LinkedList<File> modFiles)
251 282 {
  283 + List<String> supportedVerions = Arrays.asList(SUPPORTED_VERSIONS);
  284 +
252 285 for (File modFile : modFolder.listFiles(this))
253 286 {
254 287 try
... ... @@ -266,7 +299,7 @@ public final class LiteLoader implements FilenameFilter
266 299 versionReader.close();
267 300  
268 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 304 modFiles.add(modFile);
272 305 }
... ... @@ -368,7 +401,8 @@ public final class LiteLoader implements FilenameFilter
368 401 }
369 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 418  
385 419 try
386 420 {
  421 + logger.info("Initialising mod " + mod.getName() + " version " + mod.getVersion());
  422 +
387 423 mod.init();
388 424  
389 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 435 if (mod instanceof ChatFilter)
... ... @@ -408,7 +449,8 @@ public final class LiteLoader implements FilenameFilter
408 449 }
409 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 454 iter.remove();
413 455 }
414 456 }
... ... @@ -424,32 +466,24 @@ public final class LiteLoader implements FilenameFilter
424 466 // Chat hook
425 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 473 // Login hook
432 474 if (loginListeners.size() > 0)
433 475 {
434   - registerPacketOverride(1, HookLogin.class);
  476 + ModUtilities.registerPacketOverride(1, HookLogin.class);
435 477 HookLogin.loader = this;
436 478 }
437 479  
438 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 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 511 }
478 512 catch (Throwable th)
479 513 {
480   - logger.warning(th.getMessage());
  514 + logger.warning(th.toString());
  515 + th.printStackTrace();
481 516 }
482 517  
483 518 return classes;
... ... @@ -591,50 +626,12 @@ public final class LiteLoader implements FilenameFilter
591 626 }
592 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 635 * Add a URL to the Minecraft classloader class path
639 636 *
640 637 * @param classUrl URL of the resource to add
... ... @@ -652,11 +649,36 @@ public final class LiteLoader implements FilenameFilter
652 649 }
653 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 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 684 * Callback from the tick hook, ticks all tickable mods
... ... @@ -670,14 +692,7 @@ public final class LiteLoader implements FilenameFilter
670 692 // Try to get the minecraft timer object and determine the value of the partialTicks
671 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 697 // Hooray, we got the timer reference
683 698 if (minecraftTimer != null)
... ... @@ -690,7 +705,7 @@ public final class LiteLoader implements FilenameFilter
690 705 boolean inGame = minecraft.renderViewEntity != null && minecraft.renderViewEntity.worldObj != null;
691 706  
692 707 // Iterate tickable mods
693   - for (Tickable tickable : tickMods)
  708 + for (Tickable tickable : tickListeners)
694 709 {
695 710 tickable.onTick(minecraft, partialTicks, inGame, tick);
696 711 }
... ...
java/com/mumfrey/liteloader/core/LiteLoaderHook.java
1 1 package com.mumfrey.liteloader.core;
2 2  
  3 +import java.lang.reflect.Field;
  4 +import java.lang.reflect.Method;
3 5 import java.util.LinkedList;
4 6 import java.util.logging.Logger;
5 7  
  8 +import net.minecraft.client.Minecraft;
  9 +import net.minecraft.src.GameSettings;
6 10 import net.minecraft.src.Profiler;
7 11  
  12 +/**
  13 + * Main LiteLoader tick hook
  14 + *
  15 + * @author Adam Mummery-Smith
  16 + */
8 17 public class LiteLoaderHook extends Profiler
9 18 {
  19 + /**
  20 + * Logger instance
  21 + */
10 22 private Logger logger;
11 23  
  24 + /**
  25 + * LiteLoader instance which will receive callbacks
  26 + */
12 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 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 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 60 public LiteLoaderHook(LiteLoader core, Logger logger)
19 61 {
20 62 this.core = core;
21 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 121 /* (non-Javadoc)
25 122 * @see net.minecraft.src.Profiler#startSection(java.lang.String)
26 123 */
27 124 @Override
28 125 public void startSection(String sectionName)
29 126 {
  127 + if (!initDone)
  128 + {
  129 + initDone = true;
  130 + core.onInit();
  131 + }
  132 +
30 133 if (sectionName.equals("animateTick")) tick = true;
31 134 sections.add(sectionName);
32 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 154 /* (non-Javadoc)
... ... @@ -39,7 +158,7 @@ public class LiteLoaderHook extends Profiler
39 158 public void endSection()
40 159 {
41 160 super.endSection();
42   -
  161 +
43 162 String endingSection = sections.removeLast();
44 163  
45 164 if (endingSection.equalsIgnoreCase("gameRenderer") && sections.getLast().equalsIgnoreCase("root"))
... ... @@ -52,5 +171,4 @@ public class LiteLoaderHook extends Profiler
52 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 +
... ...