diff options
5 files changed, 25 insertions, 10 deletions
diff --git a/release-notes.md b/release-notes.md index 5e01a264..6d430c7a 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,8 +3,9 @@ <!--See [log](https://github.com/Pathoschild/SMAPI/compare/1.10...2.0).--> For players: -* The SMAPI console is now much simpler and easier to read. -* The SMAPI console now adjusts its colors when you have a light terminal background. +* The console is now simpler and easier to read. +* The console now adjusts its colors when you have a light terminal background. +* SMAPI now detects mods which may impact game stability and shows a warning in the console. * Updated compatibility list. For mod developers: diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs index b78bf6bb..835ef631 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs @@ -83,12 +83,13 @@ namespace StardewModdingAPI.Framework.ModLoading // rewrite & load assemblies in leaf-to-root order bool oneAssembly = assemblies.Length == 1; Assembly lastAssembly = null; + HashSet<string> loggedMessages = new HashSet<string>(); foreach (AssemblyParseResult assembly in assemblies) { if (assembly.Status == AssemblyLoadStatus.AlreadyLoaded) continue; - bool changed = this.RewriteAssembly(mod, assembly.Definition, assumeCompatible, logPrefix: " "); + bool changed = this.RewriteAssembly(mod, assembly.Definition, assumeCompatible, loggedMessages, logPrefix: " "); if (changed) { if (!oneAssembly) @@ -178,13 +179,13 @@ namespace StardewModdingAPI.Framework.ModLoading /// <param name="mod">The mod for which the assembly is being loaded.</param> /// <param name="assembly">The assembly to rewrite.</param> /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param> + /// <param name="loggedMessages">The messages that have already been logged for this mod.</param> /// <param name="logPrefix">A string to prefix to log messages.</param> /// <returns>Returns whether the assembly was modified.</returns> /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception> - private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, bool assumeCompatible, string logPrefix) + private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, bool assumeCompatible, HashSet<string> loggedMessages, string logPrefix) { ModuleDefinition module = assembly.MainModule; - HashSet<string> loggedMessages = new HashSet<string>(); string filename = $"{assembly.Name.Name}.dll"; // swap assembly references if needed (e.g. XNA => MonoGame) @@ -221,7 +222,7 @@ namespace StardewModdingAPI.Framework.ModLoading foreach (IInstructionHandler handler in handlers) { InstructionHandleResult result = handler.Handle(mod, module, method, this.AssemblyMap, platformChanged); - this.ProcessInstructionHandleResult(handler, result, loggedMessages, logPrefix, assumeCompatible, filename); + this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename); if (result == InstructionHandleResult.Rewritten) anyRewritten = true; } @@ -233,7 +234,7 @@ namespace StardewModdingAPI.Framework.ModLoading foreach (IInstructionHandler handler in handlers) { InstructionHandleResult result = handler.Handle(mod, module, cil, instruction, this.AssemblyMap, platformChanged); - this.ProcessInstructionHandleResult(handler, result, loggedMessages, logPrefix, assumeCompatible, filename); + this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename); if (result == InstructionHandleResult.Rewritten) anyRewritten = true; } @@ -244,13 +245,14 @@ namespace StardewModdingAPI.Framework.ModLoading } /// <summary>Process the result from an instruction handler.</summary> + /// <param name="mod">The mod being analysed.</param> /// <param name="handler">The instruction handler.</param> /// <param name="result">The result returned by the handler.</param> /// <param name="loggedMessages">The messages already logged for the current mod.</param> /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param> /// <param name="logPrefix">A string to prefix to log messages.</param> /// <param name="filename">The assembly filename for log messages.</param> - private void ProcessInstructionHandleResult(IInstructionHandler handler, InstructionHandleResult result, HashSet<string> loggedMessages, string logPrefix, bool assumeCompatible, string filename) + private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet<string> loggedMessages, string logPrefix, bool assumeCompatible, string filename) { switch (result) { @@ -264,6 +266,11 @@ namespace StardewModdingAPI.Framework.ModLoading this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); break; + case InstructionHandleResult.DetectedGamePatch: + this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected {handler.NounPhrase} in assembly {filename}."); + this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} patches the game in a way that may impact game stability (detected {handler.NounPhrase}).", LogLevel.Warn); + break; + case InstructionHandleResult.None: break; diff --git a/src/StardewModdingAPI/Framework/ModLoading/InstructionHandleResult.cs b/src/StardewModdingAPI/Framework/ModLoading/InstructionHandleResult.cs index 3921e9c4..f4abb095 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/InstructionHandleResult.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/InstructionHandleResult.cs @@ -10,6 +10,9 @@ namespace StardewModdingAPI.Framework.ModLoading Rewritten, /// <summary>The instruction is not compatible and can't be rewritten for compatibility.</summary> - NotCompatible + NotCompatible, + + /// <summary>The instruction is compatible, but patches the game in a way that may impact stability.</summary> + DetectedGamePatch } } diff --git a/src/StardewModdingAPI/Metadata/InstructionMetadata.cs b/src/StardewModdingAPI/Metadata/InstructionMetadata.cs index fc4f112c..c53755ae 100644 --- a/src/StardewModdingAPI/Metadata/InstructionMetadata.cs +++ b/src/StardewModdingAPI/Metadata/InstructionMetadata.cs @@ -70,6 +70,11 @@ namespace StardewModdingAPI.Metadata #endif /**** + ** detect code which may impact game stability + ****/ + new TypeFinder("Harmony.HarmonyInstance", InstructionHandleResult.DetectedGamePatch), + + /**** ** rewrite CIL to fix incompatible code ****/ // crossplatform diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 175fc3f3..8da93bf4 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -110,7 +110,6 @@ <Compile Include="Framework\ContentManagerShim.cs" /> <Compile Include="Framework\Exceptions\SAssemblyLoadFailedException.cs" /> <Compile Include="Framework\ModLoading\AssemblyLoadStatus.cs" /> - <Compile Include="Framework\ModLoading\DangerousCodeWarner.cs" /> <Compile Include="Framework\Utilities\ContextHash.cs" /> <Compile Include="Metadata\CoreAssets.cs" /> <Compile Include="ContentSource.cs" /> |