Commit 3c957d788ff474d8a80aad15c5c096c01eb2c9e2
1 parent
bd15e763
check for mod transformer injection errors and treat as critical errors
Showing
8 changed files
with
217 additions
and
24 deletions
java/client/com/mumfrey/liteloader/client/ClientEvents.java
... | ... | @@ -481,7 +481,7 @@ public class ClientEvents extends Events<Minecraft, IntegratedServer> |
481 | 481 | catch (Throwable th) |
482 | 482 | { |
483 | 483 | this.mods.onLateInitFailed(initMod, th); |
484 | - LiteLoaderLogger.warning(th, "Error initialising mod %s", initMod.getName()); | |
484 | + LiteLoaderLogger.warning(th, "Error calling late init for mod %s", initMod.getName()); | |
485 | 485 | } |
486 | 486 | } |
487 | 487 | } | ... | ... |
java/client/com/mumfrey/liteloader/client/LiteLoaderPanelManager.java
... | ... | @@ -191,6 +191,12 @@ public class LiteLoaderPanelManager implements PanelManager<GuiScreen> |
191 | 191 | { |
192 | 192 | return this.mods.getStartupErrorCount(); |
193 | 193 | } |
194 | + | |
195 | + @Override | |
196 | + public int getCriticalErrorCount() | |
197 | + { | |
198 | + return this.mods.getCriticalErrorCount(); | |
199 | + } | |
194 | 200 | |
195 | 201 | private boolean isPanelSupportedOnScreen(GuiScreen guiScreen) |
196 | 202 | { | ... | ... |
java/client/com/mumfrey/liteloader/client/gui/GuiLiteLoaderPanel.java
... | ... | @@ -121,7 +121,7 @@ public class GuiLiteLoaderPanel extends GuiScreen |
121 | 121 | |
122 | 122 | private boolean mouseOverLogo = false; |
123 | 123 | |
124 | - private int startupErrorCount = 0; | |
124 | + private int startupErrorCount = 0, criticalErrorCount = 0; | |
125 | 125 | |
126 | 126 | /** |
127 | 127 | * @param minecraft |
... | ... | @@ -144,6 +144,7 @@ public class GuiLiteLoaderPanel extends GuiScreen |
144 | 144 | this.settingsPanel = new GuiPanelSettings(this, minecraft); |
145 | 145 | |
146 | 146 | this.startupErrorCount = mods.getStartupErrorCount(); |
147 | + this.criticalErrorCount = mods.getCriticalErrorCount(); | |
147 | 148 | } |
148 | 149 | |
149 | 150 | /** |
... | ... | @@ -465,7 +466,7 @@ public class GuiLiteLoaderPanel extends GuiScreen |
465 | 466 | |
466 | 467 | private void drawErrorTooltip(int left, int top) |
467 | 468 | { |
468 | - GuiLiteLoaderPanel.drawTooltip(this.fontRendererObj, I18n.format("gui.error.tooltip", this.startupErrorCount), left, top, this.width, this.height, 0xFF5555, 0xB0330000); | |
469 | + GuiLiteLoaderPanel.drawTooltip(this.fontRendererObj, I18n.format("gui.error.tooltip", this.startupErrorCount, this.criticalErrorCount), left, top, this.width, this.height, 0xFF5555, 0xB0330000); | |
469 | 470 | } |
470 | 471 | |
471 | 472 | /* (non-Javadoc) | ... | ... |
java/common/com/mumfrey/liteloader/core/Events.java
... | ... | @@ -99,6 +99,9 @@ public abstract class Events<TClient, TServer extends MinecraftServer> implement |
99 | 99 | */ |
100 | 100 | protected void onStartupComplete() |
101 | 101 | { |
102 | + LoadingProgress.setMessage("Checking mods..."); | |
103 | + this.mods.onStartupComplete(); | |
104 | + | |
102 | 105 | LoadingProgress.setMessage("Initialising CoreProviders..."); |
103 | 106 | this.loader.onStartupComplete(); |
104 | 107 | ... | ... |
java/common/com/mumfrey/liteloader/core/LiteLoaderMods.java
... | ... | @@ -7,6 +7,7 @@ import java.util.HashMap; |
7 | 7 | import java.util.LinkedList; |
8 | 8 | import java.util.List; |
9 | 9 | import java.util.Map; |
10 | +import java.util.Set; | |
10 | 11 | |
11 | 12 | import javax.activity.InvalidActivityException; |
12 | 13 | |
... | ... | @@ -18,6 +19,9 @@ import com.mumfrey.liteloader.common.LoadingProgress; |
18 | 19 | import com.mumfrey.liteloader.interfaces.LoaderEnumerator; |
19 | 20 | import com.mumfrey.liteloader.interfaces.Loadable; |
20 | 21 | import com.mumfrey.liteloader.interfaces.LoadableMod; |
22 | +import com.mumfrey.liteloader.interfaces.TweakContainer; | |
23 | +import com.mumfrey.liteloader.launch.ClassTransformerManager; | |
24 | +import com.mumfrey.liteloader.launch.LiteLoaderTweaker; | |
21 | 25 | import com.mumfrey.liteloader.launch.LoaderEnvironment; |
22 | 26 | import com.mumfrey.liteloader.launch.LoaderProperties; |
23 | 27 | import com.mumfrey.liteloader.modconfig.ConfigManager; |
... | ... | @@ -88,7 +92,7 @@ public class LiteLoaderMods |
88 | 92 | */ |
89 | 93 | protected final LinkedList<NonMod> disabledMods = new LinkedList<NonMod>(); |
90 | 94 | |
91 | - private int startupErrorCount; | |
95 | + private int startupErrorCount, criticalErrorCount; | |
92 | 96 | |
93 | 97 | LiteLoaderMods(LiteLoader loader, LoaderEnvironment environment, LoaderProperties properties, ConfigManager configManager) |
94 | 98 | { |
... | ... | @@ -162,6 +166,11 @@ public class LiteLoaderMods |
162 | 166 | return this.startupErrorCount; |
163 | 167 | } |
164 | 168 | |
169 | + public int getCriticalErrorCount() | |
170 | + { | |
171 | + return this.criticalErrorCount; | |
172 | + } | |
173 | + | |
165 | 174 | public ModInfo<?> getModInfo(LiteMod instance) |
166 | 175 | { |
167 | 176 | for (Mod mod : this.allMods) |
... | ... | @@ -670,6 +679,47 @@ public class LiteLoaderMods |
670 | 679 | return String.format("version.%s", modName.toLowerCase().replaceAll("[^a-z0-9_\\-\\.]", "")); |
671 | 680 | } |
672 | 681 | |
682 | + void onStartupComplete() | |
683 | + { | |
684 | + this.validateModTransformers(); | |
685 | + } | |
686 | + | |
687 | + /** | |
688 | + * Check that all specified mod transformers were injected successfully, tag mods with failed transformers | |
689 | + * as critically errored | |
690 | + */ | |
691 | + private void validateModTransformers() | |
692 | + { | |
693 | + ClassTransformerManager transformerManager = LiteLoaderTweaker.getTransformerManager(); | |
694 | + Set<String> injectedTransformers = transformerManager.getInjectedTransformers(); | |
695 | + | |
696 | + for (Mod mod : this.loadedMods) | |
697 | + { | |
698 | + if (mod.hasClassTransformers()) | |
699 | + { | |
700 | + List<String> modTransformers = ((TweakContainer<?>)mod.getContainer()).getClassTransformerClassNames(); | |
701 | + for (String modTransformer : modTransformers) | |
702 | + { | |
703 | + if (!injectedTransformers.contains(modTransformer)) | |
704 | + { | |
705 | + List<Throwable> throwables = transformerManager.getTransformerStartupErrors(modTransformer); | |
706 | + if (throwables != null) | |
707 | + { | |
708 | + for (Throwable th : throwables) | |
709 | + { | |
710 | + this.registerModStartupError(mod, th, true); | |
711 | + } | |
712 | + } | |
713 | + else | |
714 | + { | |
715 | + this.registerModStartupError(mod, new RuntimeException("Missing class transformer " + modTransformer), true); | |
716 | + } | |
717 | + } | |
718 | + } | |
719 | + } | |
720 | + } | |
721 | + } | |
722 | + | |
673 | 723 | /** |
674 | 724 | * @param instance |
675 | 725 | * @param th |
... | ... | @@ -685,7 +735,33 @@ public class LiteLoaderMods |
685 | 735 | |
686 | 736 | private void registerModStartupError(ModInfo<?> mod, Throwable th) |
687 | 737 | { |
738 | + // This is a critical error if a mod has already injected a transformer, since it may have injected | |
739 | + // callbacks which it is not in a position to handle! | |
740 | + boolean critical = this.hasModInjectedTransformers(mod); | |
741 | + | |
742 | + this.registerModStartupError(mod, th, critical); | |
743 | + } | |
744 | + | |
745 | + private boolean hasModInjectedTransformers(ModInfo<?> mod) | |
746 | + { | |
747 | + if (!mod.hasClassTransformers()) return false; | |
748 | + | |
749 | + Set<String> injectedTransformers = LiteLoaderTweaker.getTransformerManager().getInjectedTransformers(); | |
750 | + List<String> modTransformers = ((TweakContainer<?>)mod.getContainer()).getClassTransformerClassNames(); | |
751 | + | |
752 | + for (String modTransformer : modTransformers) | |
753 | + { | |
754 | + if (injectedTransformers.contains(modTransformer)) | |
755 | + return true; | |
756 | + } | |
757 | + | |
758 | + return false; | |
759 | + } | |
760 | + | |
761 | + private void registerModStartupError(ModInfo<?> mod, Throwable th, boolean critical) | |
762 | + { | |
688 | 763 | this.startupErrorCount++; |
764 | + if (critical) this.criticalErrorCount++; | |
689 | 765 | mod.registerStartupError(th); |
690 | 766 | } |
691 | 767 | ... | ... |
java/common/com/mumfrey/liteloader/interfaces/PanelManager.java
java/common/com/mumfrey/liteloader/launch/ClassTransformerManager.java
1 | 1 | package com.mumfrey.liteloader.launch; |
2 | 2 | |
3 | -import java.util.Arrays; | |
4 | -import java.util.Collection; | |
5 | -import java.util.HashMap; | |
6 | -import java.util.HashSet; | |
7 | -import java.util.Iterator; | |
8 | -import java.util.List; | |
9 | -import java.util.Map; | |
10 | -import java.util.Set; | |
11 | -import java.util.TreeSet; | |
3 | +import java.lang.reflect.Field; | |
4 | +import java.util.*; | |
12 | 5 | import java.util.Map.Entry; |
13 | 6 | |
7 | +import org.apache.logging.log4j.Logger; | |
8 | +import org.apache.logging.log4j.core.LogEvent; | |
9 | +import org.apache.logging.log4j.core.appender.AbstractAppender; | |
10 | + | |
14 | 11 | import net.minecraft.launchwrapper.IClassTransformer; |
15 | 12 | import net.minecraft.launchwrapper.LaunchClassLoader; |
13 | +import net.minecraft.launchwrapper.LogWrapper; | |
16 | 14 | |
17 | 15 | import com.mumfrey.liteloader.transformers.PacketTransformer; |
18 | 16 | import com.mumfrey.liteloader.util.SortableValue; |
... | ... | @@ -33,7 +31,7 @@ public class ClassTransformerManager |
33 | 31 | /** |
34 | 32 | * Transformers to inject |
35 | 33 | */ |
36 | - private Set<String> injectedTransformers = new HashSet<String>(); | |
34 | + private Set<String> pendingTransformers = new HashSet<String>(); | |
37 | 35 | |
38 | 36 | /** |
39 | 37 | * Transformers to inject after preInit but before the game starts, necessary for anything that needs to be downstream of forge |
... | ... | @@ -51,11 +49,60 @@ public class ClassTransformerManager |
51 | 49 | private final List<String> requiredTransformers; |
52 | 50 | |
53 | 51 | /** |
52 | + * Transformers successfully injected by us | |
53 | + */ | |
54 | + private final Set<String> injectedTransformers = new HashSet<String>(); | |
55 | + | |
56 | + /** | |
57 | + * Catalogue of transformer startup failures | |
58 | + */ | |
59 | + private final Map<String, List<Throwable>> transformerStartupErrors = new HashMap<String, List<Throwable>>(); | |
60 | + | |
61 | + private Logger attachedLog; | |
62 | + | |
63 | + private String pendingTransformer; | |
64 | + | |
65 | + class ThrowableObserver extends AbstractAppender | |
66 | + { | |
67 | + public ThrowableObserver() | |
68 | + { | |
69 | + super("Throwable Observer", null, null); | |
70 | + this.start(); | |
71 | + } | |
72 | + | |
73 | + @Override | |
74 | + public void append(LogEvent event) | |
75 | + { | |
76 | + ClassTransformerManager.this.observeThrowable(event.getThrown()); | |
77 | + } | |
78 | + } | |
79 | + | |
80 | + /** | |
54 | 81 | * @param requiredTransformers |
55 | 82 | */ |
56 | 83 | public ClassTransformerManager(List<String> requiredTransformers) |
57 | 84 | { |
58 | 85 | this.requiredTransformers = requiredTransformers; |
86 | + | |
87 | + this.appendObserver(); | |
88 | + } | |
89 | + | |
90 | + private void appendObserver() | |
91 | + { | |
92 | + try | |
93 | + { | |
94 | + Field fLogger = LogWrapper.class.getDeclaredField("myLog"); | |
95 | + fLogger.setAccessible(true); | |
96 | + this.attachedLog = (Logger)fLogger.get(LogWrapper.log); | |
97 | + if (this.attachedLog instanceof org.apache.logging.log4j.core.Logger) | |
98 | + { | |
99 | + ((org.apache.logging.log4j.core.Logger)this.attachedLog).addAppender(new ThrowableObserver()); | |
100 | + } | |
101 | + } | |
102 | + catch (Exception ex) | |
103 | + { | |
104 | + LiteLoaderLogger.warning("Failed to append ThrowableObserver to LogWrapper, transformer startup exceptions may not be logged"); | |
105 | + } | |
59 | 106 | } |
60 | 107 | |
61 | 108 | /** |
... | ... | @@ -66,7 +113,7 @@ public class ClassTransformerManager |
66 | 113 | { |
67 | 114 | if (!this.gameStarted) |
68 | 115 | { |
69 | - this.injectedTransformers.add(transformerClass); | |
116 | + this.pendingTransformers.add(transformerClass); | |
70 | 117 | return true; |
71 | 118 | } |
72 | 119 | |
... | ... | @@ -81,7 +128,7 @@ public class ClassTransformerManager |
81 | 128 | { |
82 | 129 | if (!this.gameStarted) |
83 | 130 | { |
84 | - this.injectedTransformers.addAll(transformerClasses); | |
131 | + this.pendingTransformers.addAll(transformerClasses); | |
85 | 132 | return true; |
86 | 133 | } |
87 | 134 | |
... | ... | @@ -96,7 +143,7 @@ public class ClassTransformerManager |
96 | 143 | { |
97 | 144 | if (!this.gameStarted) |
98 | 145 | { |
99 | - this.injectedTransformers.addAll(Arrays.asList(transformerClasses)); | |
146 | + this.pendingTransformers.addAll(Arrays.asList(transformerClasses)); | |
100 | 147 | return true; |
101 | 148 | } |
102 | 149 | |
... | ... | @@ -108,12 +155,12 @@ public class ClassTransformerManager |
108 | 155 | */ |
109 | 156 | void injectUpstreamTransformers(LaunchClassLoader classLoader) |
110 | 157 | { |
111 | - this.sieveAndSortPacketTransformers(classLoader, this.injectedTransformers); | |
158 | + this.sieveAndSortPacketTransformers(classLoader, this.pendingTransformers); | |
112 | 159 | |
113 | 160 | for (String requiredTransformerClassName : this.requiredTransformers) |
114 | 161 | { |
115 | 162 | LiteLoaderLogger.info("Injecting required class transformer '%s'", requiredTransformerClassName); |
116 | - classLoader.registerTransformer(requiredTransformerClassName); | |
163 | + this.injectTransformer(classLoader, requiredTransformerClassName); | |
117 | 164 | } |
118 | 165 | |
119 | 166 | for (Entry<String, TreeSet<SortableValue<String>>> packetClassTransformers : this.packetTransformers.entrySet()) |
... | ... | @@ -121,14 +168,15 @@ public class ClassTransformerManager |
121 | 168 | for (SortableValue<String> transformerInfo : packetClassTransformers.getValue()) |
122 | 169 | { |
123 | 170 | String packetClass = packetClassTransformers.getKey(); |
171 | + String transformerClassName = transformerInfo.getValue(); | |
124 | 172 | if (packetClass.lastIndexOf('.') != -1) packetClass = packetClass.substring(packetClass.lastIndexOf('.') + 1); |
125 | - LiteLoaderLogger.info("Injecting packet class transformer '%s' for packet class '%s' with priority %d", transformerInfo.getValue(), packetClass, transformerInfo.getPriority()); | |
126 | - classLoader.registerTransformer(transformerInfo.getValue()); | |
173 | + LiteLoaderLogger.info("Injecting packet class transformer '%s' for packet class '%s' with priority %d", transformerClassName, packetClass, transformerInfo.getPriority()); | |
174 | + this.injectTransformer(classLoader, transformerClassName); | |
127 | 175 | } |
128 | 176 | } |
129 | 177 | |
130 | 178 | // inject any transformers received after this point directly into the downstreamTransformers set |
131 | - this.injectedTransformers = this.downstreamTransformers; | |
179 | + this.pendingTransformers = this.downstreamTransformers; | |
132 | 180 | } |
133 | 181 | |
134 | 182 | /** |
... | ... | @@ -142,7 +190,7 @@ public class ClassTransformerManager |
142 | 190 | for (String transformerClassName : this.downstreamTransformers) |
143 | 191 | { |
144 | 192 | LiteLoaderLogger.info("Injecting additional class transformer class '%s'", transformerClassName); |
145 | - classLoader.registerTransformer(transformerClassName); | |
193 | + this.injectTransformer(classLoader, transformerClassName); | |
146 | 194 | } |
147 | 195 | |
148 | 196 | this.downstreamTransformers.clear(); |
... | ... | @@ -211,4 +259,58 @@ public class ClassTransformerManager |
211 | 259 | |
212 | 260 | LiteLoaderLogger.info("Added %d packet transformer classes to the transformer list", registeredTransformers); |
213 | 261 | } |
262 | + | |
263 | + private synchronized void injectTransformer(LaunchClassLoader classLoader, String transformerClassName) | |
264 | + { | |
265 | + // Assign pendingTransformer so that logged errors during transformer init can be put in the map | |
266 | + this.pendingTransformer = transformerClassName; | |
267 | + | |
268 | + // Register the transformer | |
269 | + classLoader.registerTransformer(transformerClassName); | |
270 | + | |
271 | + // Unassign pending transformer now init is completed | |
272 | + this.pendingTransformer = null; | |
273 | + | |
274 | + // Check whether the transformer was successfully injected, look for it in the transformer list | |
275 | + if (this.findTransformer(classLoader, transformerClassName) != null) | |
276 | + { | |
277 | + this.injectedTransformers.add(transformerClassName); | |
278 | + } | |
279 | + } | |
280 | + | |
281 | + public void observeThrowable(Throwable th) | |
282 | + { | |
283 | + if (th != null && this.pendingTransformer != null) | |
284 | + { | |
285 | + List<Throwable> transformerErrors = this.transformerStartupErrors.get(this.pendingTransformer); | |
286 | + if (transformerErrors == null) | |
287 | + { | |
288 | + transformerErrors = new ArrayList<Throwable>(); | |
289 | + this.transformerStartupErrors.put(this.pendingTransformer, transformerErrors); | |
290 | + } | |
291 | + transformerErrors.add(th); | |
292 | + } | |
293 | + } | |
294 | + | |
295 | + private IClassTransformer findTransformer(LaunchClassLoader classLoader, String transformerClassName) | |
296 | + { | |
297 | + for (IClassTransformer transformer : classLoader.getTransformers()) | |
298 | + { | |
299 | + if (transformer.getClass().getName().equals(transformerClassName)) | |
300 | + return transformer; | |
301 | + } | |
302 | + | |
303 | + return null; | |
304 | + } | |
305 | + | |
306 | + public Set<String> getInjectedTransformers() | |
307 | + { | |
308 | + return Collections.unmodifiableSet(this.injectedTransformers); | |
309 | + } | |
310 | + | |
311 | + public List<Throwable> getTransformerStartupErrors(String transformerClassName) | |
312 | + { | |
313 | + List<Throwable> errorList = this.transformerStartupErrors.get(transformerClassName); | |
314 | + return errorList != null ? Collections.unmodifiableList(errorList) : null; | |
315 | + } | |
214 | 316 | } | ... | ... |
resources/assets/liteloader/lang/en_US.lang
... | ... | @@ -70,4 +70,4 @@ gui.log.closedialog=Close |
70 | 70 | |
71 | 71 | gui.error.title=Startup errors for %s |
72 | 72 | |
73 | -gui.error.tooltip=%d mod startup error(s) detected | |
74 | 73 | \ No newline at end of file |
74 | +gui.error.tooltip=%d mod startup error(s) detected (%d critical) | |
75 | 75 | \ No newline at end of file | ... | ... |