Merge branch 'develop' into stable
This commit is contained in:
commit
5731b015a0
|
@ -1,7 +1,7 @@
|
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<!--set general build properties -->
|
||||
<Version>3.14.3</Version>
|
||||
<Version>3.14.4</Version>
|
||||
<Product>SMAPI</Product>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
← [README](README.md)
|
||||
|
||||
# Release notes
|
||||
## 3.14.4
|
||||
Released 15 May 2022 for Stardew Valley 1.5.6 or later.
|
||||
|
||||
* For players:
|
||||
* Improved performance for mods using deprecated APIs.
|
||||
|
||||
* For mod authors:
|
||||
* Removed warning for mods which use `dynamic`.
|
||||
_This no longer causes errors on Linux/macOS after Stardew Valley 1.5.5._
|
||||
|
||||
## 3.14.3
|
||||
Released 12 May 2022 for Stardew Valley 1.5.6 or later.
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"Name": "Console Commands",
|
||||
"Author": "SMAPI",
|
||||
"Version": "3.14.3",
|
||||
"Version": "3.14.4",
|
||||
"Description": "Adds SMAPI console commands that let you manipulate the game.",
|
||||
"UniqueID": "SMAPI.ConsoleCommands",
|
||||
"EntryDll": "ConsoleCommands.dll",
|
||||
"MinimumApiVersion": "3.14.3"
|
||||
"MinimumApiVersion": "3.14.4"
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"Name": "Error Handler",
|
||||
"Author": "SMAPI",
|
||||
"Version": "3.14.3",
|
||||
"Version": "3.14.4",
|
||||
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
|
||||
"UniqueID": "SMAPI.ErrorHandler",
|
||||
"EntryDll": "ErrorHandler.dll",
|
||||
"MinimumApiVersion": "3.14.3"
|
||||
"MinimumApiVersion": "3.14.4"
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"Name": "Save Backup",
|
||||
"Author": "SMAPI",
|
||||
"Version": "3.14.3",
|
||||
"Version": "3.14.4",
|
||||
"Description": "Automatically backs up all your saves once per day into its folder.",
|
||||
"UniqueID": "SMAPI.SaveBackup",
|
||||
"EntryDll": "SaveBackup.dll",
|
||||
"MinimumApiVersion": "3.14.3"
|
||||
"MinimumApiVersion": "3.14.4"
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
|
|||
PatchesGame = 4,
|
||||
|
||||
/// <summary>The mod uses the <c>dynamic</c> keyword which won't work on Linux/macOS.</summary>
|
||||
[Obsolete("This value is no longer used by SMAPI and will be removed in the upcoming SMAPI 4.0.0.")]
|
||||
UsesDynamic = 8,
|
||||
|
||||
/// <summary>The mod references specialized 'unvalidated update tick' events which may impact stability.</summary>
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace StardewModdingAPI.Toolkit
|
|||
/// <summary>Extract mod metadata from the wiki compatibility list.</summary>
|
||||
public async Task<WikiModList> GetWikiCompatibilityListAsync()
|
||||
{
|
||||
WikiClient client = new(this.UserAgent);
|
||||
using WikiClient client = new(this.UserAgent);
|
||||
return await client.FetchModsAsync();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace StardewModdingAPI
|
|||
internal static int? LogScreenId { get; set; }
|
||||
|
||||
/// <summary>SMAPI's current raw semantic version.</summary>
|
||||
internal static string RawApiVersion = "3.14.3";
|
||||
internal static string RawApiVersion = "3.14.4";
|
||||
}
|
||||
|
||||
/// <summary>Contains SMAPI's constants and assumptions.</summary>
|
||||
|
@ -84,7 +84,7 @@ namespace StardewModdingAPI
|
|||
get
|
||||
{
|
||||
SCore.DeprecationManager.Warn(
|
||||
source: SCore.DeprecationManager.GetModFromStack(),
|
||||
source: null,
|
||||
nounPhrase: $"{nameof(Constants)}.{nameof(Constants.ExecutionPath)}",
|
||||
version: "3.14.0",
|
||||
severity: DeprecationLevel.Notice
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using StardewModdingAPI.Enums;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Utilities;
|
||||
using StardewValley;
|
||||
using StardewValley.Menus;
|
||||
|
@ -41,6 +42,10 @@ namespace StardewModdingAPI
|
|||
/// <summary>Whether the in-game world is completely unloaded and not in the process of being loaded. The world may still exist in memory at this point, but should be ignored.</summary>
|
||||
internal static bool IsWorldFullyUnloaded => Context.LoadStage is LoadStage.ReturningToTitle or LoadStage.None;
|
||||
|
||||
/// <summary>If SMAPI is currently waiting for mod code, the mods to which it belongs (with the most recent at the top of the stack).</summary>
|
||||
/// <remarks><strong>This is heuristic only.</strong> It provides a quick way to identify the most likely mod for deprecation warnings, but it should be followed with a more accurate check if needed.</remarks>
|
||||
internal static Stack<IModMetadata> HeuristicModsRunningCode { get; } = new();
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace StardewModdingAPI.Framework.Content
|
|||
get
|
||||
{
|
||||
SCore.DeprecationManager.Warn(
|
||||
source: SCore.DeprecationManager.GetModFromStack(),
|
||||
source: null,
|
||||
nounPhrase: $"{nameof(IAssetInfo)}.{nameof(IAssetInfo.AssetName)}",
|
||||
version: "3.14.0",
|
||||
severity: DeprecationLevel.Notice,
|
||||
|
@ -76,7 +76,7 @@ namespace StardewModdingAPI.Framework.Content
|
|||
public bool AssetNameEquals(string path)
|
||||
{
|
||||
SCore.DeprecationManager.Warn(
|
||||
source: SCore.DeprecationManager.GetModFromStack(),
|
||||
source: null,
|
||||
nounPhrase: $"{nameof(IAssetInfo)}.{nameof(IAssetInfo.AssetNameEquals)}",
|
||||
version: "3.14.0",
|
||||
severity: DeprecationLevel.Notice,
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace StardewModdingAPI.Framework.Content
|
|||
// check edit
|
||||
if (this.Instance is IAssetEditor editor)
|
||||
{
|
||||
Context.HeuristicModsRunningCode.Push(this.Mod);
|
||||
try
|
||||
{
|
||||
if (editor.CanEdit<TAsset>(asset))
|
||||
|
@ -73,11 +74,16 @@ namespace StardewModdingAPI.Framework.Content
|
|||
{
|
||||
this.Mod.LogAsMod($"Mod failed when checking whether it could edit asset '{asset.Name}'. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
}
|
||||
|
||||
// check load
|
||||
if (this.Instance is IAssetLoader loader)
|
||||
{
|
||||
Context.HeuristicModsRunningCode.Push(this.Mod);
|
||||
try
|
||||
{
|
||||
if (loader.CanLoad<TAsset>(asset))
|
||||
|
@ -87,6 +93,10 @@ namespace StardewModdingAPI.Framework.Content
|
|||
{
|
||||
this.Mod.LogAsMod($"Mod failed when checking whether it could load asset '{asset.Name}'. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -595,6 +595,7 @@ namespace StardewModdingAPI.Framework
|
|||
foreach (ModLinked<IAssetLoader> loader in this.Loaders)
|
||||
{
|
||||
// check if loader applies
|
||||
Context.HeuristicModsRunningCode.Push(loader.Mod);
|
||||
try
|
||||
{
|
||||
if (!loader.Data.CanLoad<T>(legacyInfo))
|
||||
|
@ -605,6 +606,10 @@ namespace StardewModdingAPI.Framework
|
|||
loader.Mod.LogAsMod($"Mod failed when checking whether it could load asset '{legacyInfo.Name}', and will be ignored. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
|
||||
// add operation
|
||||
group ??= new AssetOperationGroup(new List<AssetLoadOperation>(), new List<AssetEditOperation>());
|
||||
|
@ -617,9 +622,7 @@ namespace StardewModdingAPI.Framework
|
|||
Mod: loader.Mod,
|
||||
OnBehalfOf: null,
|
||||
Priority: AssetLoadPriority.Exclusive,
|
||||
GetData: assetInfo => loader.Data.Load<T>(
|
||||
this.GetLegacyAssetInfo(assetInfo)
|
||||
)
|
||||
GetData: assetInfo => loader.Data.Load<T>(this.GetLegacyAssetInfo(assetInfo))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -629,6 +632,7 @@ namespace StardewModdingAPI.Framework
|
|||
foreach (var editor in this.Editors)
|
||||
{
|
||||
// check if editor applies
|
||||
Context.HeuristicModsRunningCode.Push(editor.Mod);
|
||||
try
|
||||
{
|
||||
if (!editor.Data.CanEdit<T>(legacyInfo))
|
||||
|
@ -639,6 +643,10 @@ namespace StardewModdingAPI.Framework
|
|||
editor.Mod.LogAsMod($"Mod crashed when checking whether it could edit asset '{legacyInfo.Name}', and will be ignored. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
|
||||
// HACK
|
||||
//
|
||||
|
@ -672,9 +680,7 @@ namespace StardewModdingAPI.Framework
|
|||
Mod: editor.Mod,
|
||||
OnBehalfOf: null,
|
||||
Priority: priority,
|
||||
ApplyEdit: assetData => editor.Data.Edit<T>(
|
||||
this.GetLegacyAssetData(assetData)
|
||||
)
|
||||
ApplyEdit: assetData => editor.Data.Edit<T>(this.GetLegacyAssetData(assetData))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -177,6 +177,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
|||
// fetch asset from loader
|
||||
IModMetadata mod = loader.Mod;
|
||||
T data;
|
||||
Context.HeuristicModsRunningCode.Push(loader.Mod);
|
||||
try
|
||||
{
|
||||
data = (T)loader.GetData(info);
|
||||
|
@ -187,6 +188,10 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
|||
mod.LogAsMod($"Mod crashed when loading asset '{info.Name}'{this.GetOnBehalfOfLabel(loader.OnBehalfOf)}. SMAPI will use the default asset instead. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
|
||||
// return matched asset
|
||||
return this.TryFixAndValidateLoadedAsset(info, data, loader)
|
||||
|
@ -229,6 +234,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
|||
|
||||
// try edit
|
||||
object prevAsset = asset.Data;
|
||||
Context.HeuristicModsRunningCode.Push(editor.Mod);
|
||||
try
|
||||
{
|
||||
editor.ApplyEdit(asset);
|
||||
|
@ -238,9 +244,13 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
|||
{
|
||||
mod.LogAsMod($"Mod crashed when editing asset '{info.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)}, which may cause errors in-game. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
|
||||
// validate edit
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse -- it's only guaranteed non-null after this method
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- it's only guaranteed non-null after this method
|
||||
if (asset.Data == null)
|
||||
{
|
||||
mod.LogAsMod($"Mod incorrectly set asset '{info.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)} to a null value; ignoring override.", LogLevel.Warn);
|
||||
|
|
|
@ -121,7 +121,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
|||
}
|
||||
catch (Exception ex) when (ex is not SContentLoadException)
|
||||
{
|
||||
throw this.GetLoadError(assetName, ContentLoadErrorType.Other, "an unexpected occurred.", ex);
|
||||
throw this.GetLoadError(assetName, ContentLoadErrorType.Other, "an unexpected error occurred.", ex);
|
||||
}
|
||||
|
||||
// track & return asset
|
||||
|
|
|
@ -37,13 +37,6 @@ namespace StardewModdingAPI.Framework.Deprecations
|
|||
this.ModRegistry = modRegistry;
|
||||
}
|
||||
|
||||
/// <summary>Get a mod for the closest assembly registered as a source of deprecation warnings.</summary>
|
||||
/// <returns>Returns the source name, or <c>null</c> if no registered assemblies were found.</returns>
|
||||
public IModMetadata? GetModFromStack()
|
||||
{
|
||||
return this.ModRegistry.GetFromStack();
|
||||
}
|
||||
|
||||
/// <summary>Get a mod from its unique ID.</summary>
|
||||
/// <param name="modId">The mod's unique ID.</param>
|
||||
public IModMetadata? GetMod(string modId)
|
||||
|
@ -52,7 +45,7 @@ namespace StardewModdingAPI.Framework.Deprecations
|
|||
}
|
||||
|
||||
/// <summary>Log a deprecation warning.</summary>
|
||||
/// <param name="source">The mod which used the deprecated code, if known.</param>
|
||||
/// <param name="source">The mod which used the deprecated code, or <c>null</c> to get it heuristically. Note that getting it heuristically is very slow in some cases, and should be avoided if at all possible.</param>
|
||||
/// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
|
||||
/// <param name="version">The SMAPI version which deprecated it.</param>
|
||||
/// <param name="severity">How deprecated the code is.</param>
|
||||
|
@ -60,18 +53,38 @@ namespace StardewModdingAPI.Framework.Deprecations
|
|||
/// <param name="logStackTrace">Whether to log a stack trace showing where the deprecated code is in the mod.</param>
|
||||
public void Warn(IModMetadata? source, string nounPhrase, string version, DeprecationLevel severity, string[]? unlessStackIncludes = null, bool logStackTrace = true)
|
||||
{
|
||||
// get heuristic source
|
||||
// The call stack is usually the most reliable way to get the source if it's unknown. This is *very* slow
|
||||
// though, especially before we check whether this is a duplicate warning. The initial cache check uses a
|
||||
// quick heuristic method if at all possible to avoid that.
|
||||
IModMetadata? heuristicSource = source;
|
||||
ImmutableStackTrace? stack = null;
|
||||
if (heuristicSource is null)
|
||||
Context.HeuristicModsRunningCode.TryPeek(out heuristicSource);
|
||||
if (heuristicSource is null)
|
||||
{
|
||||
stack = ImmutableStackTrace.Get(skipFrames: 1);
|
||||
heuristicSource = this.ModRegistry.GetFromStack(stack.GetFrames());
|
||||
}
|
||||
|
||||
// skip if already warned
|
||||
string cacheKey = $"{source?.DisplayName ?? "<unknown>"}::{nounPhrase}::{version}";
|
||||
string cacheKey = $"{heuristicSource?.Manifest.UniqueID ?? "<unknown>"}::{nounPhrase}::{version}";
|
||||
if (this.LoggedDeprecations.Contains(cacheKey))
|
||||
return;
|
||||
this.LoggedDeprecations.Add(cacheKey);
|
||||
|
||||
// warn if valid
|
||||
ImmutableStackTrace stack = ImmutableStackTrace.Get(skipFrames: 1);
|
||||
if (!this.ShouldSuppress(stack, unlessStackIncludes))
|
||||
// get more accurate source
|
||||
if (stack is not null)
|
||||
source ??= heuristicSource!;
|
||||
else
|
||||
{
|
||||
this.LoggedDeprecations.Add(cacheKey);
|
||||
this.QueuedWarnings.Add(new DeprecationWarning(source, nounPhrase, version, severity, stack, logStackTrace));
|
||||
stack ??= ImmutableStackTrace.Get(skipFrames: 1);
|
||||
source ??= this.ModRegistry.GetFromStack(stack.GetFrames());
|
||||
}
|
||||
|
||||
// log unless suppressed
|
||||
if (!this.ShouldSuppress(stack, unlessStackIncludes))
|
||||
this.QueuedWarnings.Add(new DeprecationWarning(source, nounPhrase, version, severity, stack, logStackTrace));
|
||||
}
|
||||
|
||||
/// <summary>A placeholder method used to track deprecated code for which a separate warning will be shown.</summary>
|
||||
|
|
|
@ -104,6 +104,8 @@ namespace StardewModdingAPI.Framework.Events
|
|||
// raise event
|
||||
foreach (ManagedEventHandler<TEventArgs> handler in this.GetHandlers())
|
||||
{
|
||||
Context.HeuristicModsRunningCode.Push(handler.SourceMod);
|
||||
|
||||
try
|
||||
{
|
||||
handler.Handler(null, args);
|
||||
|
@ -112,6 +114,10 @@ namespace StardewModdingAPI.Framework.Events
|
|||
{
|
||||
this.LogError(handler, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +132,8 @@ namespace StardewModdingAPI.Framework.Events
|
|||
// raise event
|
||||
foreach (ManagedEventHandler<TEventArgs> handler in this.GetHandlers())
|
||||
{
|
||||
Context.HeuristicModsRunningCode.Push(handler.SourceMod);
|
||||
|
||||
try
|
||||
{
|
||||
invoke(handler.SourceMod, args => handler.Handler(null, args));
|
||||
|
@ -134,6 +142,10 @@ namespace StardewModdingAPI.Framework.Events
|
|||
{
|
||||
this.LogError(handler, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -511,11 +511,6 @@ namespace StardewModdingAPI.Framework.Logging
|
|||
"These mods have no update keys in their manifest. SMAPI may not notify you about updates for these",
|
||||
"mods. Consider notifying the mod authors about this problem."
|
||||
);
|
||||
|
||||
// not crossplatform
|
||||
this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesDynamic, LogLevel.Debug, "Not crossplatform",
|
||||
"These mods use the 'dynamic' keyword, and won't work on Linux/macOS."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
|
|||
public bool Trigger(string name, string[] arguments)
|
||||
{
|
||||
SCore.DeprecationManager.Warn(
|
||||
source: SCore.DeprecationManager.GetMod(this.ModID),
|
||||
source: this.Mod,
|
||||
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.ConsoleCommands)}.{nameof(ICommandHelper.Trigger)}",
|
||||
version: "3.8.1",
|
||||
severity: DeprecationLevel.Notice
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
|
|||
get
|
||||
{
|
||||
SCore.DeprecationManager.Warn(
|
||||
source: SCore.DeprecationManager.GetMod(this.ModID),
|
||||
source: this.Mod,
|
||||
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.Content)}",
|
||||
version: "3.14.0",
|
||||
severity: DeprecationLevel.Notice
|
||||
|
|
|
@ -414,11 +414,6 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
mod.SetWarning(ModWarning.UsesUnvalidatedUpdateTick);
|
||||
break;
|
||||
|
||||
case InstructionHandleResult.DetectedDynamic:
|
||||
template = $"{logPrefix}Detected 'dynamic' keyword ($phrase) in assembly {filename}.";
|
||||
mod.SetWarning(ModWarning.UsesDynamic);
|
||||
break;
|
||||
|
||||
case InstructionHandleResult.DetectedConsoleAccess:
|
||||
template = $"{logPrefix}Detected direct console access ($phrase) in assembly {filename}.";
|
||||
mod.SetWarning(ModWarning.AccessesConsole);
|
||||
|
|
|
@ -20,9 +20,6 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
/// <summary>The instruction is compatible, but affects the save serializer in a way that may make saves unloadable without the mod.</summary>
|
||||
DetectedSaveSerializer,
|
||||
|
||||
/// <summary>The instruction is compatible, but uses the <c>dynamic</c> keyword which won't work on Linux/macOS.</summary>
|
||||
DetectedDynamic,
|
||||
|
||||
/// <summary>The instruction is compatible, but references <see cref="ISpecializedEvents.UnvalidatedUpdateTicking"/> or <see cref="ISpecializedEvents.UnvalidatedUpdateTicked"/> which may impact stability.</summary>
|
||||
DetectedUnvalidatedUpdateTick,
|
||||
|
||||
|
|
|
@ -99,14 +99,10 @@ namespace StardewModdingAPI.Framework
|
|||
}
|
||||
|
||||
/// <summary>Get the mod metadata from the closest assembly registered as a source of deprecation warnings.</summary>
|
||||
/// <param name="frames">The call stack to analyze.</param>
|
||||
/// <returns>Returns the mod's metadata, or <c>null</c> if no registered assemblies were found.</returns>
|
||||
public IModMetadata? GetFromStack()
|
||||
public IModMetadata? GetFromStack(StackFrame[] frames)
|
||||
{
|
||||
// get stack frames
|
||||
StackTrace stack = new();
|
||||
StackFrame[] frames = stack.GetFrames();
|
||||
|
||||
// search stack for a source assembly
|
||||
foreach (StackFrame frame in frames)
|
||||
{
|
||||
IModMetadata? mod = this.GetFrom(frame);
|
||||
|
@ -114,7 +110,6 @@ namespace StardewModdingAPI.Framework
|
|||
return mod;
|
||||
}
|
||||
|
||||
// no known assembly found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1677,6 +1677,7 @@ namespace StardewModdingAPI.Framework
|
|||
#pragma warning restore CS0612, CS0618
|
||||
|
||||
// call entry method
|
||||
Context.HeuristicModsRunningCode.Push(metadata);
|
||||
try
|
||||
{
|
||||
IMod mod = metadata.Mod!;
|
||||
|
@ -1705,6 +1706,7 @@ namespace StardewModdingAPI.Framework
|
|||
{
|
||||
this.Monitor.Log($"Failed loading mod-provided API for {metadata.DisplayName}. Integrations with other mods may not work. Error: {ex.GetLogSummary()}", LogLevel.Error);
|
||||
}
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
|
||||
// unlock mod integrations
|
||||
|
@ -1852,7 +1854,7 @@ namespace StardewModdingAPI.Framework
|
|||
try
|
||||
{
|
||||
// get mod instance
|
||||
if (!this.TryLoadModEntry(modAssembly, out Mod? modEntry, out errorReasonPhrase))
|
||||
if (!this.TryLoadModEntry(mod, modAssembly, out Mod? modEntry, out errorReasonPhrase))
|
||||
{
|
||||
failReason = ModFailReason.LoadFailed;
|
||||
return false;
|
||||
|
@ -1954,11 +1956,12 @@ namespace StardewModdingAPI.Framework
|
|||
}
|
||||
|
||||
/// <summary>Load a mod's entry class.</summary>
|
||||
/// <param name="metadata">The mod metadata whose entry class is being loaded.</param>
|
||||
/// <param name="modAssembly">The mod assembly.</param>
|
||||
/// <param name="mod">The loaded instance.</param>
|
||||
/// <param name="error">The error indicating why loading failed (if applicable).</param>
|
||||
/// <returns>Returns whether the mod entry class was successfully loaded.</returns>
|
||||
private bool TryLoadModEntry(Assembly modAssembly, [NotNullWhen(true)] out Mod? mod, [NotNullWhen(false)] out string? error)
|
||||
private bool TryLoadModEntry(IModMetadata metadata, Assembly modAssembly, [NotNullWhen(true)] out Mod? mod, [NotNullWhen(false)] out string? error)
|
||||
{
|
||||
mod = null;
|
||||
|
||||
|
@ -1976,7 +1979,16 @@ namespace StardewModdingAPI.Framework
|
|||
}
|
||||
|
||||
// get implementation
|
||||
mod = (Mod?)modAssembly.CreateInstance(modEntries[0].ToString());
|
||||
Context.HeuristicModsRunningCode.Push(metadata);
|
||||
try
|
||||
{
|
||||
mod = (Mod?)modAssembly.CreateInstance(modEntries[0].ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Context.HeuristicModsRunningCode.TryPop(out _);
|
||||
}
|
||||
|
||||
if (mod == null)
|
||||
{
|
||||
error = "its entry class couldn't be instantiated.";
|
||||
|
|
|
@ -67,7 +67,6 @@ namespace StardewModdingAPI.Metadata
|
|||
/****
|
||||
** detect code which may impact game stability
|
||||
****/
|
||||
yield return new TypeFinder("System.Runtime.CompilerServices.CallSite", InstructionHandleResult.DetectedDynamic);
|
||||
yield return new FieldFinder(typeof(SaveGame).FullName!, new[] { nameof(SaveGame.serializer), nameof(SaveGame.farmerSerializer), nameof(SaveGame.locationSerializer) }, InstructionHandleResult.DetectedSaveSerializer);
|
||||
yield return new EventFinder(typeof(ISpecializedEvents).FullName!, new[] { nameof(ISpecializedEvents.UnvalidatedUpdateTicked), nameof(ISpecializedEvents.UnvalidatedUpdateTicking) }, InstructionHandleResult.DetectedUnvalidatedUpdateTick);
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace StardewModdingAPI.Utilities
|
|||
if (!nullExpected)
|
||||
{
|
||||
SCore.DeprecationManager.Warn(
|
||||
SCore.DeprecationManager.GetModFromStack(),
|
||||
null,
|
||||
$"calling the {nameof(PerScreen<T>)} constructor with null",
|
||||
"3.14.0",
|
||||
DeprecationLevel.Notice
|
||||
|
|
Loading…
Reference in New Issue