Commit 71a4ac62e63faeaccb8a86e6dd43a153f6db85e4
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
Showing
7 changed files
with
678 additions
and
101 deletions
java/com/mumfrey/liteloader/InitCompleteListener.java
0 → 100644
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 | + | ... | ... |