Commit 3c957d788ff474d8a80aad15c5c096c01eb2c9e2

Authored by Mumfrey
1 parent bd15e763

check for mod transformer injection errors and treat as critical errors

java/client/com/mumfrey/liteloader/client/ClientEvents.java
@@ -481,7 +481,7 @@ public class ClientEvents extends Events<Minecraft, IntegratedServer> @@ -481,7 +481,7 @@ public class ClientEvents extends Events<Minecraft, IntegratedServer>
481 catch (Throwable th) 481 catch (Throwable th)
482 { 482 {
483 this.mods.onLateInitFailed(initMod, th); 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,6 +191,12 @@ public class LiteLoaderPanelManager implements PanelManager<GuiScreen>
191 { 191 {
192 return this.mods.getStartupErrorCount(); 192 return this.mods.getStartupErrorCount();
193 } 193 }
  194 +
  195 + @Override
  196 + public int getCriticalErrorCount()
  197 + {
  198 + return this.mods.getCriticalErrorCount();
  199 + }
194 200
195 private boolean isPanelSupportedOnScreen(GuiScreen guiScreen) 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,7 +121,7 @@ public class GuiLiteLoaderPanel extends GuiScreen
121 121
122 private boolean mouseOverLogo = false; 122 private boolean mouseOverLogo = false;
123 123
124 - private int startupErrorCount = 0; 124 + private int startupErrorCount = 0, criticalErrorCount = 0;
125 125
126 /** 126 /**
127 * @param minecraft 127 * @param minecraft
@@ -144,6 +144,7 @@ public class GuiLiteLoaderPanel extends GuiScreen @@ -144,6 +144,7 @@ public class GuiLiteLoaderPanel extends GuiScreen
144 this.settingsPanel = new GuiPanelSettings(this, minecraft); 144 this.settingsPanel = new GuiPanelSettings(this, minecraft);
145 145
146 this.startupErrorCount = mods.getStartupErrorCount(); 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,7 +466,7 @@ public class GuiLiteLoaderPanel extends GuiScreen
465 466
466 private void drawErrorTooltip(int left, int top) 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 /* (non-Javadoc) 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,6 +99,9 @@ public abstract class Events<TClient, TServer extends MinecraftServer> implement
99 */ 99 */
100 protected void onStartupComplete() 100 protected void onStartupComplete()
101 { 101 {
  102 + LoadingProgress.setMessage("Checking mods...");
  103 + this.mods.onStartupComplete();
  104 +
102 LoadingProgress.setMessage("Initialising CoreProviders..."); 105 LoadingProgress.setMessage("Initialising CoreProviders...");
103 this.loader.onStartupComplete(); 106 this.loader.onStartupComplete();
104 107
java/common/com/mumfrey/liteloader/core/LiteLoaderMods.java
@@ -7,6 +7,7 @@ import java.util.HashMap; @@ -7,6 +7,7 @@ import java.util.HashMap;
7 import java.util.LinkedList; 7 import java.util.LinkedList;
8 import java.util.List; 8 import java.util.List;
9 import java.util.Map; 9 import java.util.Map;
  10 +import java.util.Set;
10 11
11 import javax.activity.InvalidActivityException; 12 import javax.activity.InvalidActivityException;
12 13
@@ -18,6 +19,9 @@ import com.mumfrey.liteloader.common.LoadingProgress; @@ -18,6 +19,9 @@ import com.mumfrey.liteloader.common.LoadingProgress;
18 import com.mumfrey.liteloader.interfaces.LoaderEnumerator; 19 import com.mumfrey.liteloader.interfaces.LoaderEnumerator;
19 import com.mumfrey.liteloader.interfaces.Loadable; 20 import com.mumfrey.liteloader.interfaces.Loadable;
20 import com.mumfrey.liteloader.interfaces.LoadableMod; 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 import com.mumfrey.liteloader.launch.LoaderEnvironment; 25 import com.mumfrey.liteloader.launch.LoaderEnvironment;
22 import com.mumfrey.liteloader.launch.LoaderProperties; 26 import com.mumfrey.liteloader.launch.LoaderProperties;
23 import com.mumfrey.liteloader.modconfig.ConfigManager; 27 import com.mumfrey.liteloader.modconfig.ConfigManager;
@@ -88,7 +92,7 @@ public class LiteLoaderMods @@ -88,7 +92,7 @@ public class LiteLoaderMods
88 */ 92 */
89 protected final LinkedList<NonMod> disabledMods = new LinkedList<NonMod>(); 93 protected final LinkedList<NonMod> disabledMods = new LinkedList<NonMod>();
90 94
91 - private int startupErrorCount; 95 + private int startupErrorCount, criticalErrorCount;
92 96
93 LiteLoaderMods(LiteLoader loader, LoaderEnvironment environment, LoaderProperties properties, ConfigManager configManager) 97 LiteLoaderMods(LiteLoader loader, LoaderEnvironment environment, LoaderProperties properties, ConfigManager configManager)
94 { 98 {
@@ -162,6 +166,11 @@ public class LiteLoaderMods @@ -162,6 +166,11 @@ public class LiteLoaderMods
162 return this.startupErrorCount; 166 return this.startupErrorCount;
163 } 167 }
164 168
  169 + public int getCriticalErrorCount()
  170 + {
  171 + return this.criticalErrorCount;
  172 + }
  173 +
165 public ModInfo<?> getModInfo(LiteMod instance) 174 public ModInfo<?> getModInfo(LiteMod instance)
166 { 175 {
167 for (Mod mod : this.allMods) 176 for (Mod mod : this.allMods)
@@ -670,6 +679,47 @@ public class LiteLoaderMods @@ -670,6 +679,47 @@ public class LiteLoaderMods
670 return String.format("version.%s", modName.toLowerCase().replaceAll("[^a-z0-9_\\-\\.]", "")); 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 * @param instance 724 * @param instance
675 * @param th 725 * @param th
@@ -685,7 +735,33 @@ public class LiteLoaderMods @@ -685,7 +735,33 @@ public class LiteLoaderMods
685 735
686 private void registerModStartupError(ModInfo<?> mod, Throwable th) 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 this.startupErrorCount++; 763 this.startupErrorCount++;
  764 + if (critical) this.criticalErrorCount++;
689 mod.registerStartupError(th); 765 mod.registerStartupError(th);
690 } 766 }
691 767
java/common/com/mumfrey/liteloader/interfaces/PanelManager.java
@@ -54,4 +54,9 @@ public interface PanelManager&lt;TParentScreen&gt; extends TickObserver, PostRenderObs @@ -54,4 +54,9 @@ public interface PanelManager&lt;TParentScreen&gt; extends TickObserver, PostRenderObs
54 * @return 54 * @return
55 */ 55 */
56 public abstract int getStartupErrorCount(); 56 public abstract int getStartupErrorCount();
  57 +
  58 + /**
  59 + * @return
  60 + */
  61 + public abstract int getCriticalErrorCount();
57 } 62 }
java/common/com/mumfrey/liteloader/launch/ClassTransformerManager.java
1 package com.mumfrey.liteloader.launch; 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 import java.util.Map.Entry; 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 import net.minecraft.launchwrapper.IClassTransformer; 11 import net.minecraft.launchwrapper.IClassTransformer;
15 import net.minecraft.launchwrapper.LaunchClassLoader; 12 import net.minecraft.launchwrapper.LaunchClassLoader;
  13 +import net.minecraft.launchwrapper.LogWrapper;
16 14
17 import com.mumfrey.liteloader.transformers.PacketTransformer; 15 import com.mumfrey.liteloader.transformers.PacketTransformer;
18 import com.mumfrey.liteloader.util.SortableValue; 16 import com.mumfrey.liteloader.util.SortableValue;
@@ -33,7 +31,7 @@ public class ClassTransformerManager @@ -33,7 +31,7 @@ public class ClassTransformerManager
33 /** 31 /**
34 * Transformers to inject 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 * Transformers to inject after preInit but before the game starts, necessary for anything that needs to be downstream of forge 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,11 +49,60 @@ public class ClassTransformerManager
51 private final List<String> requiredTransformers; 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 * @param requiredTransformers 81 * @param requiredTransformers
55 */ 82 */
56 public ClassTransformerManager(List<String> requiredTransformers) 83 public ClassTransformerManager(List<String> requiredTransformers)
57 { 84 {
58 this.requiredTransformers = requiredTransformers; 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,7 +113,7 @@ public class ClassTransformerManager
66 { 113 {
67 if (!this.gameStarted) 114 if (!this.gameStarted)
68 { 115 {
69 - this.injectedTransformers.add(transformerClass); 116 + this.pendingTransformers.add(transformerClass);
70 return true; 117 return true;
71 } 118 }
72 119
@@ -81,7 +128,7 @@ public class ClassTransformerManager @@ -81,7 +128,7 @@ public class ClassTransformerManager
81 { 128 {
82 if (!this.gameStarted) 129 if (!this.gameStarted)
83 { 130 {
84 - this.injectedTransformers.addAll(transformerClasses); 131 + this.pendingTransformers.addAll(transformerClasses);
85 return true; 132 return true;
86 } 133 }
87 134
@@ -96,7 +143,7 @@ public class ClassTransformerManager @@ -96,7 +143,7 @@ public class ClassTransformerManager
96 { 143 {
97 if (!this.gameStarted) 144 if (!this.gameStarted)
98 { 145 {
99 - this.injectedTransformers.addAll(Arrays.asList(transformerClasses)); 146 + this.pendingTransformers.addAll(Arrays.asList(transformerClasses));
100 return true; 147 return true;
101 } 148 }
102 149
@@ -108,12 +155,12 @@ public class ClassTransformerManager @@ -108,12 +155,12 @@ public class ClassTransformerManager
108 */ 155 */
109 void injectUpstreamTransformers(LaunchClassLoader classLoader) 156 void injectUpstreamTransformers(LaunchClassLoader classLoader)
110 { 157 {
111 - this.sieveAndSortPacketTransformers(classLoader, this.injectedTransformers); 158 + this.sieveAndSortPacketTransformers(classLoader, this.pendingTransformers);
112 159
113 for (String requiredTransformerClassName : this.requiredTransformers) 160 for (String requiredTransformerClassName : this.requiredTransformers)
114 { 161 {
115 LiteLoaderLogger.info("Injecting required class transformer '%s'", requiredTransformerClassName); 162 LiteLoaderLogger.info("Injecting required class transformer '%s'", requiredTransformerClassName);
116 - classLoader.registerTransformer(requiredTransformerClassName); 163 + this.injectTransformer(classLoader, requiredTransformerClassName);
117 } 164 }
118 165
119 for (Entry<String, TreeSet<SortableValue<String>>> packetClassTransformers : this.packetTransformers.entrySet()) 166 for (Entry<String, TreeSet<SortableValue<String>>> packetClassTransformers : this.packetTransformers.entrySet())
@@ -121,14 +168,15 @@ public class ClassTransformerManager @@ -121,14 +168,15 @@ public class ClassTransformerManager
121 for (SortableValue<String> transformerInfo : packetClassTransformers.getValue()) 168 for (SortableValue<String> transformerInfo : packetClassTransformers.getValue())
122 { 169 {
123 String packetClass = packetClassTransformers.getKey(); 170 String packetClass = packetClassTransformers.getKey();
  171 + String transformerClassName = transformerInfo.getValue();
124 if (packetClass.lastIndexOf('.') != -1) packetClass = packetClass.substring(packetClass.lastIndexOf('.') + 1); 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 // inject any transformers received after this point directly into the downstreamTransformers set 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,7 +190,7 @@ public class ClassTransformerManager
142 for (String transformerClassName : this.downstreamTransformers) 190 for (String transformerClassName : this.downstreamTransformers)
143 { 191 {
144 LiteLoaderLogger.info("Injecting additional class transformer class '%s'", transformerClassName); 192 LiteLoaderLogger.info("Injecting additional class transformer class '%s'", transformerClassName);
145 - classLoader.registerTransformer(transformerClassName); 193 + this.injectTransformer(classLoader, transformerClassName);
146 } 194 }
147 195
148 this.downstreamTransformers.clear(); 196 this.downstreamTransformers.clear();
@@ -211,4 +259,58 @@ public class ClassTransformerManager @@ -211,4 +259,58 @@ public class ClassTransformerManager
211 259
212 LiteLoaderLogger.info("Added %d packet transformer classes to the transformer list", registeredTransformers); 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,4 +70,4 @@ gui.log.closedialog=Close
70 70
71 gui.error.title=Startup errors for %s 71 gui.error.title=Startup errors for %s
72 72
73 -gui.error.tooltip=%d mod startup error(s) detected  
74 \ No newline at end of file 73 \ No newline at end of file
  74 +gui.error.tooltip=%d mod startup error(s) detected (%d critical)
75 \ No newline at end of file 75 \ No newline at end of file