From 94d41cd67a1591fff01c17e3aece48e1f19d297b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 5 Aug 2021 12:21:18 -0400 Subject: correct release date --- docs/release-notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 4e9dacbf..20fdd2c1 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,7 +2,7 @@ # Release notes ## 3.12.2 -Released 04 August 2021 for Stardew Valley 1.5.4 or later. +Released 05 August 2021 for Stardew Valley 1.5.4 or later. * For players: * Fixed error creating a new save or joining a multiplayer world in 3.12.1. -- cgit From 5e16ed0eea2cae21badd525afa0d464700bb8647 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 5 Aug 2021 14:28:29 -0400 Subject: prevent weird null reference exception in error-handling --- docs/release-notes.md | 4 ++++ src/SMAPI.Internal/ExceptionExtensions.cs | 27 +++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 20fdd2c1..8c98af5d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,10 @@ ← [README](README.md) # Release notes +## Upcoming release +* For mod authors: + * Fixed rare `NullReferenceException` in SMAPI's error-handling. + ## 3.12.2 Released 05 August 2021 for Stardew Valley 1.5.4 or later. diff --git a/src/SMAPI.Internal/ExceptionExtensions.cs b/src/SMAPI.Internal/ExceptionExtensions.cs index 5f1ee1fa..d8189048 100644 --- a/src/SMAPI.Internal/ExceptionExtensions.cs +++ b/src/SMAPI.Internal/ExceptionExtensions.cs @@ -13,19 +13,26 @@ namespace StardewModdingAPI.Internal /// The error to summarize. public static string GetLogSummary(this Exception exception) { - switch (exception) + try { - case TypeLoadException ex: - return $"Failed loading type '{ex.TypeName}': {exception}"; + switch (exception) + { + case TypeLoadException ex: + return $"Failed loading type '{ex.TypeName}': {exception}"; - case ReflectionTypeLoadException ex: - string summary = ex.ToString(); - foreach (Exception childEx in ex.LoaderExceptions ?? new Exception[0]) - summary += $"\n\n{childEx?.GetLogSummary()}"; - return summary; + case ReflectionTypeLoadException ex: + string summary = ex.ToString(); + foreach (Exception childEx in ex.LoaderExceptions ?? new Exception[0]) + summary += $"\n\n{childEx?.GetLogSummary()}"; + return summary; - default: - return exception.ToString(); + default: + return exception?.ToString() ?? $"\n{Environment.StackTrace}"; + } + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed handling {exception?.GetType().FullName} (original message: {exception?.Message})", ex); } } -- cgit From 885808fb66233caf3057f0baa6368f4763a8eade Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 8 Aug 2021 00:21:28 -0400 Subject: move assembly resolver setup into Constants to centralize hardcoded logic --- src/SMAPI/Constants.cs | 16 ++++++++++++++++ src/SMAPI/Framework/ModLoading/AssemblyLoader.cs | 4 +--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 6cbdeb8e..7f633a46 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using Mono.Cecil; using StardewModdingAPI.Enums; using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.ModLoading; @@ -229,6 +230,21 @@ namespace StardewModdingAPI } } + /// Configure the Mono.Cecil assembly resolver. + /// The assembly resolver. + internal static void ConfigureAssemblyResolver(AssemblyDefinitionResolver resolver) + { + // add search paths + resolver.AddSearchDirectory(Constants.ExecutionPath); + resolver.AddSearchDirectory(Constants.InternalFilesPath); + + // add SMAPI explicitly + // Normally this would be handled automatically by the search paths, but for some reason there's a specific + // case involving unofficial 64-bit Stardew Valley when launched through Steam (for some players only) + // where Mono.Cecil can't resolve references to SMAPI. + resolver.Add(AssemblyDefinition.ReadAssembly(typeof(SGame).Assembly.Location)); + } + /// Get metadata for mapping assemblies to the current platform. /// The target game platform. /// The game framework running the game. diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs index 2b71038a..86b43990 100644 --- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs @@ -59,9 +59,7 @@ namespace StardewModdingAPI.Framework.ModLoading // init resolver this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver()); - this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.ExecutionPath); - this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.InternalFilesPath); - this.AssemblyDefinitionResolver.Add(AssemblyDefinition.ReadAssembly(typeof(SGame).Assembly.Location)); // for some reason Mono.Cecil can't resolve SMAPI in very specific cases involving unofficial 64-bit Stardew Valley when launched through Steam (for some players only) + Constants.ConfigureAssemblyResolver(this.AssemblyDefinitionResolver); // generate type => assembly lookup for types which should be rewritten this.TypeAssemblies = new Dictionary(); -- cgit From 976c66537c9f4493ce859c574675bb8651b5323f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 8 Aug 2021 00:24:20 -0400 Subject: fix edge case where Netcode references aren't rewritten correctly --- docs/release-notes.md | 1 + src/SMAPI/Constants.cs | 11 +++++++++++ .../Framework/ModLoading/AssemblyDefinitionResolver.cs | 16 +++++++++++----- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 8c98af5d..9e42a847 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ## Upcoming release * For mod authors: * Fixed rare `NullReferenceException` in SMAPI's error-handling. + * Internal changes to prepare for upcoming releases. ## 3.12.2 Released 05 August 2021 for Stardew Valley 1.5.4 or later. diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 7f633a46..de6b63d6 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -243,6 +243,17 @@ namespace StardewModdingAPI // case involving unofficial 64-bit Stardew Valley when launched through Steam (for some players only) // where Mono.Cecil can't resolve references to SMAPI. resolver.Add(AssemblyDefinition.ReadAssembly(typeof(SGame).Assembly.Location)); + + // make sure game assembly names can be resolved + // The game assembly can have one of three names depending how the mod was compiled: + // - 'StardewValley': assembly name on Linux/macOS; + // - 'Stardew Valley': assembly name on Windows; + // - 'Netcode': an assembly that's separate on Windows only. + resolver.Add(AssemblyDefinition.ReadAssembly(typeof(Game1).Assembly.Location), "StardewValley", "Stardew Valley" +#if !SMAPI_FOR_WINDOWS + , "Netcode" +#endif + ); } /// Get metadata for mapping assemblies to the current platform. diff --git a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs index aefb0126..b3415609 100644 --- a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs +++ b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs @@ -21,11 +21,17 @@ namespace StardewModdingAPI.Framework.ModLoading public void Add(params AssemblyDefinition[] assemblies) { foreach (AssemblyDefinition assembly in assemblies) - { - this.RegisterAssembly(assembly); - this.Lookup[assembly.Name.Name] = assembly; - this.Lookup[assembly.Name.FullName] = assembly; - } + this.Add(assembly, assembly.Name.Name, assembly.Name.FullName); + } + + /// Add known assemblies to the resolver. + /// The assembly to add. + /// The assembly names for which it should be returned. + public void Add(AssemblyDefinition assembly, params string[] names) + { + this.RegisterAssembly(assembly); + foreach (string name in names) + this.Lookup[name] = assembly; } /// Resolve an assembly reference. -- cgit From a4c6c6168405bb0441a40e4f6f4e86dfa46a76b2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 8 Aug 2021 00:32:19 -0400 Subject: enable parallel analyzer execution --- docs/technical/mod-package.md | 33 ++++++++++++---------- .../NetFieldAnalyzer.cs | 3 ++ .../ObsoleteFieldAnalyzer.cs | 3 ++ 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/docs/technical/mod-package.md b/docs/technical/mod-package.md index 91da971b..7eefc7a4 100644 --- a/docs/technical/mod-package.md +++ b/docs/technical/mod-package.md @@ -365,7 +365,10 @@ The NuGet package is generated automatically in `StardewModdingAPI.ModBuildConfi when you compile it. ## Release notes -## 3.3 +## Upcoming release +* Improved analyzer performance by enabling parallel execution. + +## 3.3.0 Released 30 March 2021. * Added a build warning when the mod isn't compiled for `Any CPU`. @@ -385,19 +388,19 @@ Released 11 September 2020. * Added more detailed logging. * Fixed _path's format is not supported_ error when using default `Mods` path in 3.2. -### 3.2 +### 3.2.0 Released 07 September 2020. * Added option to change `Mods` folder path. * Rewrote documentation to make it easier to read. -### 3.1 +### 3.1.0 Released 01 February 2020. * Added support for semantic versioning 2.0. * `0Harmony.dll` is now ignored if the mod references Harmony directly (it's bundled with SMAPI). -### 3.0 +### 3.0.0 Released 26 November 2019. * Updated for SMAPI 3.0 and Stardew Valley 1.4. @@ -412,14 +415,14 @@ Released 26 November 2019. * Dropped support for older versions of SMAPI and Visual Studio. * Migrated package icon to NuGet's new format. -### 2.2 +### 2.2.0 Released 28 October 2018. * Added support for SMAPI 2.8+ (still compatible with earlier versions). * Added default game paths for 32-bit Windows. * Fixed valid manifests marked invalid in some cases. -### 2.1 +### 2.1.0 Released 27 July 2018. * Added support for Stardew Valley 1.3. @@ -439,7 +442,7 @@ Released 11 October 2017. * Fixed mod deploy failing to create subfolders if they don't already exist. -### 2.0 +### 2.0.0 Released 11 October 2017. * Added: mods are now copied into the `Mods` folder automatically (configurable). @@ -457,7 +460,7 @@ Released 28 July 2017. * The manifest/i18n files in the project now take precedence over those in the build output if both are present. -### 1.7 +### 1.7.0 Released 28 July 2017. * Added option to create release zips on build. @@ -474,19 +477,19 @@ Released 09 July 2017. * Improved crossplatform game path detection. -### 1.6 +### 1.6.0 Released 05 June 2017. * Added support for deploying mod files into `Mods` automatically. * Added a build error if a game folder is found, but doesn't contain Stardew Valley or SMAPI. -### 1.5 +### 1.5.0 Released 23 January 2017. * Added support for setting a custom game path globally. * Added default GOG path on macOS. -### 1.4 +### 1.4.0 Released 11 January 2017. * Fixed detection of non-default game paths on 32-bit Windows. @@ -494,22 +497,22 @@ Released 11 January 2017. * Removed support for overriding the target platform (no longer needed since SMAPI crossplatforms mods automatically). -### 1.3 +### 1.3.0 Released 31 December 2016. * Added support for non-default game paths on Windows. -### 1.2 +### 1.2.0 Released 24 October 2016. * Exclude game binaries from mod build output. -### 1.1 +### 1.1.0 Released 21 October 2016. * Added support for overriding the target platform. -### 1.0 +### 1.0.0 Released 21 October 2016. * Initial release. diff --git a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs index a9b981bd..1efc1616 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs @@ -174,6 +174,9 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer /// The analysis context. public override void Initialize(AnalysisContext context) { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction( this.AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression, diff --git a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs index d071f0c1..722d5227 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs @@ -56,6 +56,9 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer /// The analysis context. public override void Initialize(AnalysisContext context) { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction( this.AnalyzeObsoleteFields, SyntaxKind.SimpleMemberAccessExpression, -- cgit From 6cf7c49f34eac8edd18012d41d1f49a149fb1f5a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 15 Aug 2021 01:25:56 -0400 Subject: add GetInAllLocales to translation API --- docs/release-notes.md | 1 + .../Framework/ModHelpers/TranslationHelper.cs | 6 ++ src/SMAPI/Framework/Translator.cs | 65 ++++++++++++++++++---- src/SMAPI/ITranslationHelper.cs | 7 ++- 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 9e42a847..409c4bd5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,6 +3,7 @@ # Release notes ## Upcoming release * For mod authors: + * Added `GetInAllLocales` method in translation API, to get a translation in every available locale. * Fixed rare `NullReferenceException` in SMAPI's error-handling. * Internal changes to prepare for upcoming releases. diff --git a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs index a88ca9c9..869664fe 100644 --- a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs @@ -55,6 +55,12 @@ namespace StardewModdingAPI.Framework.ModHelpers return this.Translator.Get(key, tokens); } + /// + public IDictionary GetInAllLocales(string key, bool withFallback = false) + { + return this.Translator.GetInAllLocales(key, withFallback); + } + /// Set the translations to use. /// The translations to use. internal TranslationHelper SetTranslations(IDictionary> translations) diff --git a/src/SMAPI/Framework/Translator.cs b/src/SMAPI/Framework/Translator.cs index 11ec983b..4492b17f 100644 --- a/src/SMAPI/Framework/Translator.cs +++ b/src/SMAPI/Framework/Translator.cs @@ -46,18 +46,10 @@ namespace StardewModdingAPI.Framework this.LocaleEnum = localeEnum; this.ForLocale = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (string next in this.GetRelevantLocales(this.Locale)) + foreach (string key in this.GetAllKeysRaw()) { - // skip if locale not defined - if (!this.All.TryGetValue(next, out IDictionary translations)) - continue; - - // add missing translations - foreach (var pair in translations) - { - if (!this.ForLocale.ContainsKey(pair.Key)) - this.ForLocale.Add(pair.Key, new Translation(this.Locale, pair.Key, pair.Value)); - } + string text = this.GetRaw(key, locale, withFallback: true); + this.ForLocale.Add(key, new Translation(this.Locale, key, text)); } } @@ -83,6 +75,25 @@ namespace StardewModdingAPI.Framework return this.Get(key).Tokens(tokens); } + /// Get a translation in every locale for which it's defined. + /// The translation key. + /// Whether to add duplicate translations for locale fallback. For example, if a translation is defined in default.json but not fr.json, setting this to true will add a fr entry which duplicates the default text. + public IDictionary GetInAllLocales(string key, bool withFallback) + { + IDictionary translations = new Dictionary(); + + foreach (var localeSet in this.All) + { + string locale = localeSet.Key; + string text = this.GetRaw(key, locale, withFallback); + + if (text != null) + translations[locale] = new Translation(locale, key, text); + } + + return translations; + } + /// Set the translations to use. /// The translations to use. internal Translator SetTranslations(IDictionary> translations) @@ -102,6 +113,38 @@ namespace StardewModdingAPI.Framework /********* ** Private methods *********/ + /// Get all translation keys in the underlying translation data, ignoring the cache. + private IEnumerable GetAllKeysRaw() + { + return new HashSet( + this.All.SelectMany(p => p.Value.Keys), + StringComparer.OrdinalIgnoreCase + ); + } + + /// Get a translation from the underlying translation data, ignoring the cache. + /// The translation key. + /// The locale to get. + /// Whether to add duplicate translations for locale fallback. For example, if a translation is defined in default.json but not fr.json, setting this to true will add a fr entry which duplicates the default text. + private string GetRaw(string key, string locale, bool withFallback) + { + foreach (string next in this.GetRelevantLocales(locale)) + { + string translation = null; + bool hasTranslation = + this.All.TryGetValue(next, out IDictionary translations) + && translations.TryGetValue(key, out translation); + + if (hasTranslation) + return translation; + + if (!withFallback) + break; + } + + return null; + } + /// Get the locales which can provide translations for the given locale, in precedence order. /// The locale for which to find valid locales. private IEnumerable GetRelevantLocales(string locale) diff --git a/src/SMAPI/ITranslationHelper.cs b/src/SMAPI/ITranslationHelper.cs index c4b72444..b30d9b14 100644 --- a/src/SMAPI/ITranslationHelper.cs +++ b/src/SMAPI/ITranslationHelper.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using StardewValley; namespace StardewModdingAPI @@ -30,5 +30,10 @@ namespace StardewModdingAPI /// The translation key. /// An object containing token key/value pairs. This can be an anonymous object (like new { value = 42, name = "Cranberries" }), a dictionary, or a class instance. Translation Get(string key, object tokens); + + /// Get a translation in every locale for which it's defined. + /// The translation key. + /// Whether to add duplicate translations for locale fallback. For example, if a translation is defined in default.json but not fr.json, setting this to true will add a fr entry which duplicates the default text. + IDictionary GetInAllLocales(string key, bool withFallback = false); } } -- cgit From c20fcec169d75b395636b06adcbec8d5101447fd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 16 Aug 2021 17:21:44 -0400 Subject: expand data helper docs (#799) --- src/SMAPI/IDataHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SMAPI/IDataHelper.cs b/src/SMAPI/IDataHelper.cs index 252030bd..4922c8da 100644 --- a/src/SMAPI/IDataHelper.cs +++ b/src/SMAPI/IDataHelper.cs @@ -38,7 +38,7 @@ namespace StardewModdingAPI /// Save arbitrary data to the current save slot. This is only possible if a save has been loaded, and the data will be lost if the player exits without saving the current day. /// The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types. /// The unique key identifying the data. - /// The arbitrary data to save. + /// The arbitrary data to save, or null to remove the entry. /// The player hasn't loaded a save file yet or isn't the main player. void WriteSaveData(string key, TModel data) where TModel : class; @@ -55,7 +55,7 @@ namespace StardewModdingAPI /// Save arbitrary data to the local computer, synchronised by GOG/Steam if applicable. /// The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types. /// The unique key identifying the data. - /// The arbitrary data to save. + /// The arbitrary data to save, or null to delete the file. void WriteGlobalData(string key, TModel data) where TModel : class; } } -- cgit From 68e629f17c349b685ef3c4836552adcbed0c4976 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 16 Aug 2021 17:22:44 -0400 Subject: fix data helper's WriteJsonFile not deleting file if data is null (#799) --- docs/release-notes.md | 5 +++-- src/SMAPI/Framework/ModHelpers/DataHelper.cs | 6 +++++- src/SMAPI/IDataHelper.cs | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 409c4bd5..d0b794ee 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,8 +3,9 @@ # Release notes ## Upcoming release * For mod authors: - * Added `GetInAllLocales` method in translation API, to get a translation in every available locale. - * Fixed rare `NullReferenceException` in SMAPI's error-handling. + * Added `helper.Translation.GetInAllLocales` to get a translation in every available locale. + * Fixed `helper.Data.WriteJsonFile` not deleting the file if the model is null, unlike the other `Write*` methods. + * Fixed error-handling for `StackOverflowException` thrown on Linux/macOS. * Internal changes to prepare for upcoming releases. ## 3.12.2 diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs index 0fe3209f..4cbfd73f 100644 --- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs @@ -58,7 +58,11 @@ namespace StardewModdingAPI.Framework.ModHelpers throw new InvalidOperationException($"You must call {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteJsonFile)} with a relative path (without directory climbing)."); path = Path.Combine(this.ModFolderPath, PathUtilities.NormalizePath(path)); - this.JsonHelper.WriteJsonFile(path, data); + + if (data != null) + this.JsonHelper.WriteJsonFile(path, data); + else + File.Delete(path); } /**** diff --git a/src/SMAPI/IDataHelper.cs b/src/SMAPI/IDataHelper.cs index 4922c8da..901266d7 100644 --- a/src/SMAPI/IDataHelper.cs +++ b/src/SMAPI/IDataHelper.cs @@ -21,7 +21,7 @@ namespace StardewModdingAPI /// Save data to a JSON file in the mod's folder. /// The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types. /// The file path relative to the mod folder. - /// The arbitrary data to save. + /// The arbitrary data to save, or null to delete the file. /// The is not relative or contains directory climbing (../). void WriteJsonFile(string path, TModel data) where TModel : class; -- cgit From 596a4616b343bc527c0a90df0f0be0ece970c3c7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 18 Aug 2021 23:38:23 -0400 Subject: no longer abort input thread when exiting `thread.Abort` is unimplemented in .NET 5, but it's not needed anyway since all threads will be aborted when the game exits. --- src/SMAPI/Framework/Logging/LogManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index a3d4f23d..7f1b76e7 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -162,8 +162,6 @@ namespace StardewModdingAPI.Framework.Logging // keep console thread alive while the game is running while (continueWhile()) Thread.Sleep(1000 / 10); - if (inputThread.ThreadState == ThreadState.Running) - inputThread.Abort(); } /// Show a 'press any key to exit' message, and exit when they press a key. -- cgit From 26a629f41b98faafed8af38a89f25d9b821fac0f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 Aug 2021 21:21:57 -0400 Subject: fix prerelease update alerts shown for non-prerelease players --- docs/release-notes.md | 3 ++ src/SMAPI.Toolkit/SemanticVersionComparer.cs | 32 ++++++++++++ src/SMAPI.Web/Framework/ModSiteManager.cs | 73 +++++++++++++++++++--------- 3 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 src/SMAPI.Toolkit/SemanticVersionComparer.cs diff --git a/docs/release-notes.md b/docs/release-notes.md index d0b794ee..85971e1d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,9 @@ * Fixed error-handling for `StackOverflowException` thrown on Linux/macOS. * Internal changes to prepare for upcoming releases. +* For the web API: + * Fixed update checks recommending prerelease versions if the player has a working non-prerelease version. + ## 3.12.2 Released 05 August 2021 for Stardew Valley 1.5.4 or later. diff --git a/src/SMAPI.Toolkit/SemanticVersionComparer.cs b/src/SMAPI.Toolkit/SemanticVersionComparer.cs new file mode 100644 index 00000000..9f6b57a2 --- /dev/null +++ b/src/SMAPI.Toolkit/SemanticVersionComparer.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace StardewModdingAPI.Toolkit +{ + /// A comparer for semantic versions based on the field. + public class SemanticVersionComparer : IComparer + { + /********* + ** Accessors + *********/ + /// A singleton instance of the comparer. + public static SemanticVersionComparer Instance { get; } = new SemanticVersionComparer(); + + + /********* + ** Public methods + *********/ + /// + public int Compare(ISemanticVersion x, ISemanticVersion y) + { + if (object.ReferenceEquals(x, y)) + return 0; + + if (x is null) + return -1; + if (y is null) + return 1; + + return x.CompareTo(y); + } + } +} diff --git a/src/SMAPI.Web/Framework/ModSiteManager.cs b/src/SMAPI.Web/Framework/ModSiteManager.cs index 68b4c6ac..8f21d2f5 100644 --- a/src/SMAPI.Web/Framework/ModSiteManager.cs +++ b/src/SMAPI.Web/Framework/ModSiteManager.cs @@ -110,41 +110,70 @@ namespace StardewModdingAPI.Web.Framework main = null; preview = null; - ISemanticVersion ParseVersion(string raw) + // parse all versions from the mod page + IEnumerable<(string name, string description, ISemanticVersion version)> GetAllVersions() { - raw = this.NormalizeVersion(raw); - return this.GetMappedVersion(raw, mapRemoteVersions, allowNonStandardVersions); + if (mod != null) + { + ISemanticVersion ParseAndMapVersion(string raw) + { + raw = this.NormalizeVersion(raw); + return this.GetMappedVersion(raw, mapRemoteVersions, allowNonStandardVersions); + } + + // get mod version + ISemanticVersion modVersion = ParseAndMapVersion(mod.Version); + if (modVersion != null) + yield return (name: null, description: null, version: ParseAndMapVersion(mod.Version)); + + // get file versions + foreach (IModDownload download in mod.Downloads) + { + ISemanticVersion cur = ParseAndMapVersion(download.Version); + if (cur != null) + yield return (download.Name, download.Description, cur); + } + } } + var versions = GetAllVersions() + .OrderByDescending(p => p.version, SemanticVersionComparer.Instance) + .ToArray(); - if (mod != null) + // get main + preview versions + void TryGetVersions(out ISemanticVersion mainVersion, out ISemanticVersion previewVersion, Func<(string name, string description, ISemanticVersion version), bool> filter = null) { - // get mod version - if (subkey == null) - main = ParseVersion(mod.Version); + mainVersion = null; + previewVersion = null; - // get file versions - foreach (IModDownload download in mod.Downloads) + // get latest main + preview version + foreach (var entry in versions) { - // check for subkey if specified - if (subkey != null && download.Name?.Contains(subkey, StringComparison.OrdinalIgnoreCase) != true && download.Description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) != true) + if (filter?.Invoke(entry) == false) continue; - // parse version - ISemanticVersion cur = ParseVersion(download.Version); - if (cur == null) - continue; + if (entry.version.IsPrerelease()) + previewVersion ??= entry.version; + else + mainVersion ??= entry.version; - // track highest versions - if (main == null || cur.IsNewerThan(main)) - main = cur; - if (cur.IsPrerelease() && (preview == null || cur.IsNewerThan(preview))) - preview = cur; + if (mainVersion != null) + break; // any other values will be older } - if (preview != null && !preview.IsNewerThan(main)) - preview = null; + // normalize values + if (previewVersion is not null) + { + mainVersion ??= previewVersion; // if every version is prerelease, latest one is the main version + if (!previewVersion.IsNewerThan(mainVersion)) + previewVersion = null; + } } + if (subkey is not null) + TryGetVersions(out main, out preview, entry => entry.name?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true || entry.description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true); + if (main is null) + TryGetVersions(out main, out preview); + return main != null; } -- cgit From 3804ae6284aeca449e1ca95c6ae7291519ed6937 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 22 Aug 2021 12:14:59 -0400 Subject: fix some installer errors not showing info header --- docs/release-notes.md | 3 +++ src/SMAPI.Installer/InteractiveInstaller.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 85971e1d..9ad95a43 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,9 @@ # Release notes ## Upcoming release +* For players: + * Fixed some installer errors now show info header. + * For mod authors: * Added `helper.Translation.GetInAllLocales` to get a translation in every available locale. * Fixed `helper.Data.WriteJsonFile` not deleting the file if the model is null, unlike the other `Write*` methods. diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index ab07c864..b91d0dd3 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -272,7 +272,6 @@ namespace StardewModdingApi.Installer DirectoryInfo bundleDir = new DirectoryInfo(this.BundlePath); paths = new InstallerPaths(bundleDir, installDir, context.ExecutableName); } - Console.Clear(); /********* @@ -309,6 +308,7 @@ namespace StardewModdingApi.Installer return; } } + Console.Clear(); /********* -- cgit From e1d8838587329bd64f3627c4cdc4ac57d187ad7f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 24 Aug 2021 18:29:03 -0400 Subject: fix update checks not recommending prerelease mod versions for SMAPI beta --- docs/release-notes.md | 4 +++- src/SMAPI.Web/Controllers/ModsApiController.cs | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 9ad95a43..e71457af 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -12,7 +12,9 @@ * Internal changes to prepare for upcoming releases. * For the web API: - * Fixed update checks recommending prerelease versions if the player has a working non-prerelease version. + * Fixed update checks... + * not recommending prerelease mod versions if the player has a beta SMAPI version; + * recommending prerelease versions if the player has a working non-prerelease version. ## 3.12.2 Released 05 August 2021 for Stardew Valley 1.5.4 or later. diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 1956bf29..c6e9a713 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -123,6 +123,10 @@ namespace StardewModdingAPI.Web.Controllers ModOverrideConfig overrides = this.Config.Value.ModOverrides.FirstOrDefault(p => p.ID.Equals(search.ID?.Trim(), StringComparison.OrdinalIgnoreCase)); bool allowNonStandardVersions = overrides?.AllowNonStandardVersions ?? false; + // SMAPI versions with a '-beta' tag indicate major changes that may need beta mod versions. + // This doesn't apply to normal prerelease versions which have an '-alpha' tag. + bool isSmapiBeta = apiVersion.IsPrerelease() && apiVersion.PrereleaseTag.StartsWith("beta"); + // get latest versions ModEntryModel result = new ModEntryModel { ID = search.ID }; IList errors = new List(); @@ -198,7 +202,7 @@ namespace StardewModdingAPI.Web.Controllers List updates = new List(); if (this.IsRecommendedUpdate(installedVersion, main?.Version, useBetaChannel: true)) updates.Add(main); - if (this.IsRecommendedUpdate(installedVersion, optional?.Version, useBetaChannel: installedVersion.IsPrerelease() || search.IsBroken)) + if (this.IsRecommendedUpdate(installedVersion, optional?.Version, useBetaChannel: isSmapiBeta || installedVersion.IsPrerelease() || search.IsBroken)) updates.Add(optional); if (this.IsRecommendedUpdate(installedVersion, unofficial?.Version, useBetaChannel: true)) updates.Add(unofficial); -- cgit From f8c76bde39ba2afb11540c9af5d88aad4c73f789 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 24 Aug 2021 21:59:52 -0400 Subject: add 64-bit compatibility check before loading mods That reduces time spent trying to rewrite them (which won't work anyway), and shows a more informative message than the default 'DLL couldn't be loaded' error. --- docs/release-notes.md | 1 + src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs | 6 +++--- src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs | 7 +++++++ src/SMAPI/Framework/SCore.cs | 15 ++++++++++++--- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index e71457af..a00c73da 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,6 +3,7 @@ # Release notes ## Upcoming release * For players: + * Added friendly error in 64-bit mode when a mod is 32-bit only. * Fixed some installer errors now show info header. * For mod authors: diff --git a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs index be0c18ce..2636aae0 100644 --- a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs +++ b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs @@ -90,10 +90,10 @@ namespace StardewModdingAPI.Toolkit.Framework } /// Get whether an executable is 64-bit. - /// The absolute path to the executable file. - public static bool Is64BitAssembly(string executablePath) + /// The absolute path to the assembly file. + public static bool Is64BitAssembly(string path) { - return AssemblyName.GetAssemblyName(executablePath).ProcessorArchitecture != ProcessorArchitecture.X86; + return AssemblyName.GetAssemblyName(path).ProcessorArchitecture != ProcessorArchitecture.X86; } diff --git a/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs b/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs index 62bd13cd..6de79a85 100644 --- a/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs +++ b/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs @@ -46,5 +46,12 @@ namespace StardewModdingAPI.Toolkit.Utilities { return LowLevelEnvironmentUtility.GetExecutableName(platform.ToString()); } + + /// Get whether an executable is 64-bit. + /// The absolute path to the assembly file. + public static bool Is64BitAssembly(string path) + { + return LowLevelEnvironmentUtility.Is64BitAssembly(path); + } } } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 5fb4aa03..dff0fc55 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1704,12 +1704,21 @@ namespace StardewModdingAPI.Framework // load as mod else { + // get mod info IManifest manifest = mod.Manifest; + string assemblyPath = Path.Combine(mod.DirectoryPath, manifest.EntryDll); + + // assert 64-bit +#if SMAPI_FOR_WINDOWS_64BIT_HACK + if (!EnvironmentUtility.Is64BitAssembly(assemblyPath)) + { + errorReasonPhrase = "it needs to be updated for 64-bit mode."; + failReason = ModFailReason.LoadFailed; + return false; + } +#endif // load mod - string assemblyPath = manifest?.EntryDll != null - ? Path.Combine(mod.DirectoryPath, manifest.EntryDll) - : null; Assembly modAssembly; try { -- cgit From 911843e1beb72a3fc0337a697f732215b66304c0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 25 Aug 2021 18:26:02 -0400 Subject: fix console encoding issues (#798) --- docs/release-notes.md | 1 + src/SMAPI/Framework/Logging/LogManager.cs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index a00c73da..79129024 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ## Upcoming release * For players: * Added friendly error in 64-bit mode when a mod is 32-bit only. + * Fixed console encoding issues on Linux/macOS. * Fixed some installer errors now show info header. * For mod authors: diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index 7f1b76e7..c6faa90d 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -109,9 +109,12 @@ namespace StardewModdingAPI.Framework.Logging output.OnMessageIntercepted += message => this.HandleConsoleMessage(this.MonitorForGame, message); Console.SetOut(output); - // enable Unicode handling + // enable Unicode handling on Windows + // (the terminal defaults to UTF-8 on Linux/macOS) +#if SMAPI_FOR_WINDOWS Console.InputEncoding = Encoding.Unicode; Console.OutputEncoding = Encoding.Unicode; +#endif } /// Get a monitor instance derived from SMAPI's current settings. -- cgit From b8dac41da8aa7ea8f42b89acd1e9a74829d70532 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 25 Aug 2021 18:26:15 -0400 Subject: fix beta label in web UI --- src/SMAPI.Web/Views/Mods/Index.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml index fa77c220..7dcd0718 100644 --- a/src/SMAPI.Web/Views/Mods/Index.cshtml +++ b/src/SMAPI.Web/Views/Mods/Index.cshtml @@ -9,7 +9,7 @@ TimeSpan staleAge = DateTimeOffset.UtcNow - Model.LastUpdated; bool hasBeta = Model.BetaVersion != null; - string betaLabel = "SDV @Model.BetaVersion only"; + string betaLabel = $"SDV {Model.BetaVersion} only"; } @section Head { -- cgit From c5c7201151709bed374931268b9c592ca526bfc9 Mon Sep 17 00:00:00 2001 From: Chase Warrington Date: Thu, 19 Aug 2021 17:19:42 -0400 Subject: Fix assembly rewriting causing VS to error/crash when debugging --- src/SMAPI/Framework/ModLoading/AssemblyLoader.cs | 31 ++++++++++-------- .../Framework/ModLoading/SymbolReaderProvider.cs | 37 ++++++++++++++++++++++ .../Framework/ModLoading/SymbolWriterProvider.cs | 25 +++++++++++++++ 3 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs create mode 100644 src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs index 86b43990..98154b53 100644 --- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs @@ -34,6 +34,9 @@ namespace StardewModdingAPI.Framework.ModLoading /// A minimal assembly definition resolver which resolves references to known loaded assemblies. private readonly AssemblyDefinitionResolver AssemblyDefinitionResolver; + private readonly SymbolReaderProvider SymbolReaderProvider; + private readonly SymbolWriterProvider SymbolWriterProvider; + /// The objects to dispose as part of this instance. private readonly HashSet Disposables = new HashSet(); @@ -61,6 +64,9 @@ namespace StardewModdingAPI.Framework.ModLoading this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver()); Constants.ConfigureAssemblyResolver(this.AssemblyDefinitionResolver); + this.SymbolReaderProvider = new SymbolReaderProvider(); + this.SymbolWriterProvider = new SymbolWriterProvider(); + // generate type => assembly lookup for types which should be rewritten this.TypeAssemblies = new Dictionary(); foreach (Assembly assembly in this.AssemblyMap.Targets) @@ -114,7 +120,7 @@ namespace StardewModdingAPI.Framework.ModLoading // rewrite assembly bool changed = this.RewriteAssembly(mod, assembly.Definition, loggedMessages, logPrefix: " "); - + // detect broken assembly reference foreach (AssemblyNameReference reference in assembly.Definition.MainModule.AssemblyReferences) { @@ -134,20 +140,12 @@ namespace StardewModdingAPI.Framework.ModLoading if (!oneAssembly) this.Monitor.Log($" Loading {assembly.File.Name} (rewritten)...", LogLevel.Trace); - // load PDB file if present - byte[] symbols; - { - string symbolsPath = Path.Combine(Path.GetDirectoryName(assemblyPath), Path.GetFileNameWithoutExtension(assemblyPath)) + ".pdb"; - symbols = File.Exists(symbolsPath) - ? File.ReadAllBytes(symbolsPath) - : null; - } - // load assembly using MemoryStream outStream = new MemoryStream(); - assembly.Definition.Write(outStream); + using MemoryStream outSymbolStream = new MemoryStream(); + assembly.Definition.Write(outStream, new WriterParameters() { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider } ); byte[] bytes = outStream.ToArray(); - lastAssembly = Assembly.Load(bytes, symbols); + lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray()); } else { @@ -234,10 +232,15 @@ namespace StardewModdingAPI.Framework.ModLoading if (!file.Exists) yield break; // not a local assembly - // read assembly + // read assembly and PDB (if present) byte[] assemblyBytes = File.ReadAllBytes(file.FullName); Stream readStream = this.TrackForDisposal(new MemoryStream(assemblyBytes)); - AssemblyDefinition assembly = this.TrackForDisposal(AssemblyDefinition.ReadAssembly(readStream, new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = assemblyResolver, InMemory = true })); + { + string symbolsPath = Path.Combine(Path.GetDirectoryName(file.FullName), Path.GetFileNameWithoutExtension(file.FullName)) + ".pdb"; + if ( File.Exists( symbolsPath ) ) + this.SymbolReaderProvider.AddSymbolMapping( Path.GetFileName( file.FullName ), this.TrackForDisposal( new MemoryStream( File.ReadAllBytes( symbolsPath ) ) ) ); + } + AssemblyDefinition assembly = this.TrackForDisposal(AssemblyDefinition.ReadAssembly(readStream, new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = assemblyResolver, InMemory = true, ReadSymbols = true, SymbolReaderProvider = this.SymbolReaderProvider })); // skip if already visited if (visitedAssemblyNames.Contains(assembly.Name.Name)) diff --git a/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs b/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs new file mode 100644 index 00000000..a651c167 --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Pdb; + +namespace StardewModdingAPI.Framework.ModLoading +{ + internal class SymbolReaderProvider : ISymbolReaderProvider + { + private readonly ISymbolReaderProvider BaseProvider = new DefaultSymbolReaderProvider(); + + private readonly Dictionary SymbolMapping = new Dictionary(); + + public void AddSymbolMapping( string dllName, Stream symbolStream ) + { + this.SymbolMapping.Add( dllName, symbolStream ); + } + + public ISymbolReader GetSymbolReader( ModuleDefinition module, string fileName ) + { + if ( this.SymbolMapping.ContainsKey( module.Name ) ) + return new NativePdbReaderProvider().GetSymbolReader( module, this.SymbolMapping[ module.Name ] ); + + return this.BaseProvider.GetSymbolReader( module, fileName ); + } + + public ISymbolReader GetSymbolReader( ModuleDefinition module, Stream symbolStream ) + { + if ( this.SymbolMapping.ContainsKey( module.Name ) ) + return new PortablePdbReaderProvider().GetSymbolReader( module, this.SymbolMapping[ module.Name ] ); + + return this.BaseProvider.GetSymbolReader( module, symbolStream ); + } + } +} diff --git a/src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs b/src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs new file mode 100644 index 00000000..116e341a --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Pdb; + +namespace StardewModdingAPI.Framework.ModLoading +{ + internal class SymbolWriterProvider : ISymbolWriterProvider + { + private readonly ISymbolWriterProvider BaseProvider = new DefaultSymbolWriterProvider(); + + public ISymbolWriter GetSymbolWriter( ModuleDefinition module, string fileName ) + { + return this.BaseProvider.GetSymbolWriter( module, fileName ); + } + + public ISymbolWriter GetSymbolWriter( ModuleDefinition module, Stream symbolStream ) + { + // Not implemented in default native pdb writer, so fallback to portable + return new PortablePdbWriterProvider().GetSymbolWriter( module, symbolStream ); + } + } +} -- cgit From 5030e965dcfa4b8d9c6ed2285ceed5f7cdc472a9 Mon Sep 17 00:00:00 2001 From: Chase Warrington Date: Fri, 20 Aug 2021 01:25:52 -0400 Subject: Fix for loading both types of PDBs --- src/SMAPI/Framework/ModLoading/MySymbolReader.cs | 55 ++++++++++++++++++++++ .../Framework/ModLoading/SymbolReaderProvider.cs | 4 +- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/SMAPI/Framework/ModLoading/MySymbolReader.cs diff --git a/src/SMAPI/Framework/ModLoading/MySymbolReader.cs b/src/SMAPI/Framework/ModLoading/MySymbolReader.cs new file mode 100644 index 00000000..215631c6 --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/MySymbolReader.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Pdb; + +namespace StardewModdingAPI.Framework.ModLoading +{ + internal class MySymbolReader : ISymbolReader + { + private ModuleDefinition Module; + private Stream Stream; + private ISymbolReader Using; + + public MySymbolReader( ModuleDefinition module, Stream stream ) + { + this.Module = module; + this.Stream = stream; + this.Using = new NativePdbReaderProvider().GetSymbolReader( module, stream ); + } + + public void Dispose() + { + this.Using.Dispose(); + } + + public ISymbolWriterProvider GetWriterProvider() + { + return new PortablePdbWriterProvider(); + } + + public bool ProcessDebugHeader( ImageDebugHeader header ) + { + try + { + return this.Using.ProcessDebugHeader( header ); + } + catch (Exception e) + { + this.Using.Dispose(); + this.Using = new PortablePdbReaderProvider().GetSymbolReader( this.Module, this.Stream ); + return this.Using.ProcessDebugHeader( header ); + } + } + + public MethodDebugInformation Read( MethodDefinition method ) + { + return Using.Read( method ); + } + } +} diff --git a/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs b/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs index a651c167..c92e1e4d 100644 --- a/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs +++ b/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs @@ -21,7 +21,7 @@ namespace StardewModdingAPI.Framework.ModLoading public ISymbolReader GetSymbolReader( ModuleDefinition module, string fileName ) { if ( this.SymbolMapping.ContainsKey( module.Name ) ) - return new NativePdbReaderProvider().GetSymbolReader( module, this.SymbolMapping[ module.Name ] ); + return new MySymbolReader( module, this.SymbolMapping[ module.Name ] ); return this.BaseProvider.GetSymbolReader( module, fileName ); } @@ -29,7 +29,7 @@ namespace StardewModdingAPI.Framework.ModLoading public ISymbolReader GetSymbolReader( ModuleDefinition module, Stream symbolStream ) { if ( this.SymbolMapping.ContainsKey( module.Name ) ) - return new PortablePdbReaderProvider().GetSymbolReader( module, this.SymbolMapping[ module.Name ] ); + return new MySymbolReader( module, this.SymbolMapping[ module.Name ] ); return this.BaseProvider.GetSymbolReader( module, symbolStream ); } -- cgit From e3b38a70f8d710af07732b325a6013ed18aa0fb0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 25 Aug 2021 19:17:29 -0400 Subject: reorganize new classes --- src/SMAPI/Framework/ModLoading/AssemblyLoader.cs | 1 + src/SMAPI/Framework/ModLoading/MySymbolReader.cs | 55 ---------------------- .../Framework/ModLoading/SymbolReaderProvider.cs | 37 --------------- .../Framework/ModLoading/SymbolWriterProvider.cs | 25 ---------- .../Framework/ModLoading/Symbols/SymbolReader.cs | 51 ++++++++++++++++++++ .../ModLoading/Symbols/SymbolReaderProvider.cs | 35 ++++++++++++++ .../ModLoading/Symbols/SymbolWriterProvider.cs | 22 +++++++++ 7 files changed, 109 insertions(+), 117 deletions(-) delete mode 100644 src/SMAPI/Framework/ModLoading/MySymbolReader.cs delete mode 100644 src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs delete mode 100644 src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs create mode 100644 src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs create mode 100644 src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs create mode 100644 src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs index 98154b53..72cfd119 100644 --- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs @@ -7,6 +7,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.ModLoading.Framework; +using StardewModdingAPI.Framework.ModLoading.Symbols; using StardewModdingAPI.Metadata; using StardewModdingAPI.Toolkit.Framework.ModData; using StardewModdingAPI.Toolkit.Utilities; diff --git a/src/SMAPI/Framework/ModLoading/MySymbolReader.cs b/src/SMAPI/Framework/ModLoading/MySymbolReader.cs deleted file mode 100644 index 215631c6..00000000 --- a/src/SMAPI/Framework/ModLoading/MySymbolReader.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Pdb; - -namespace StardewModdingAPI.Framework.ModLoading -{ - internal class MySymbolReader : ISymbolReader - { - private ModuleDefinition Module; - private Stream Stream; - private ISymbolReader Using; - - public MySymbolReader( ModuleDefinition module, Stream stream ) - { - this.Module = module; - this.Stream = stream; - this.Using = new NativePdbReaderProvider().GetSymbolReader( module, stream ); - } - - public void Dispose() - { - this.Using.Dispose(); - } - - public ISymbolWriterProvider GetWriterProvider() - { - return new PortablePdbWriterProvider(); - } - - public bool ProcessDebugHeader( ImageDebugHeader header ) - { - try - { - return this.Using.ProcessDebugHeader( header ); - } - catch (Exception e) - { - this.Using.Dispose(); - this.Using = new PortablePdbReaderProvider().GetSymbolReader( this.Module, this.Stream ); - return this.Using.ProcessDebugHeader( header ); - } - } - - public MethodDebugInformation Read( MethodDefinition method ) - { - return Using.Read( method ); - } - } -} diff --git a/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs b/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs deleted file mode 100644 index c92e1e4d..00000000 --- a/src/SMAPI/Framework/ModLoading/SymbolReaderProvider.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Pdb; - -namespace StardewModdingAPI.Framework.ModLoading -{ - internal class SymbolReaderProvider : ISymbolReaderProvider - { - private readonly ISymbolReaderProvider BaseProvider = new DefaultSymbolReaderProvider(); - - private readonly Dictionary SymbolMapping = new Dictionary(); - - public void AddSymbolMapping( string dllName, Stream symbolStream ) - { - this.SymbolMapping.Add( dllName, symbolStream ); - } - - public ISymbolReader GetSymbolReader( ModuleDefinition module, string fileName ) - { - if ( this.SymbolMapping.ContainsKey( module.Name ) ) - return new MySymbolReader( module, this.SymbolMapping[ module.Name ] ); - - return this.BaseProvider.GetSymbolReader( module, fileName ); - } - - public ISymbolReader GetSymbolReader( ModuleDefinition module, Stream symbolStream ) - { - if ( this.SymbolMapping.ContainsKey( module.Name ) ) - return new MySymbolReader( module, this.SymbolMapping[ module.Name ] ); - - return this.BaseProvider.GetSymbolReader( module, symbolStream ); - } - } -} diff --git a/src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs b/src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs deleted file mode 100644 index 116e341a..00000000 --- a/src/SMAPI/Framework/ModLoading/SymbolWriterProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Pdb; - -namespace StardewModdingAPI.Framework.ModLoading -{ - internal class SymbolWriterProvider : ISymbolWriterProvider - { - private readonly ISymbolWriterProvider BaseProvider = new DefaultSymbolWriterProvider(); - - public ISymbolWriter GetSymbolWriter( ModuleDefinition module, string fileName ) - { - return this.BaseProvider.GetSymbolWriter( module, fileName ); - } - - pub