From dab1ef6acc243726247cee57877c3b3100106522 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 20 Jun 2022 18:01:48 -0400 Subject: add flag to disable deprecated code --- src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/SMAPI.Toolkit') diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs index 32c2ed6d..338192af 100644 --- a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs +++ b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs @@ -18,9 +18,11 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData /// The mod patches the game in a way that may impact stability. PatchesGame = 4, +#if SMAPI_FOR_WINDOWS /// The mod uses the dynamic keyword which won't work on Linux/macOS. [Obsolete("This value is no longer used by SMAPI and will be removed in the upcoming SMAPI 4.0.0.")] UsesDynamic = 8, +#endif /// The mod references specialized 'unvalidated update tick' events which may impact stability. UsesUnvalidatedUpdateTick = 16, @@ -37,6 +39,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData /// Uses .NET APIs for shell or process access. AccessesShell = 256, +#if SMAPI_DEPRECATED /// References the legacy System.Configuration.ConfigurationManager assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0. DetectedLegacyConfigurationDll = 512, @@ -45,5 +48,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData /// References the legacy System.Security.Permissions assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0. DetectedLegacyPermissionsDll = 2048 +#endif } } -- cgit From 1021c3291088d8e47dc23c608aaa7ff1f81c9fd7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 22 Jun 2022 20:15:25 -0400 Subject: update fluent HTTP client --- src/SMAPI.Toolkit/SMAPI.Toolkit.csproj | 2 +- src/SMAPI.Web/SMAPI.Web.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/SMAPI.Toolkit') diff --git a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj index e021993f..7b79105f 100644 --- a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj +++ b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 4c2569e1..0a460e9e 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -25,7 +25,7 @@ - + -- cgit From 6d11c41facb2e1397a25110517cc281f87be2caf Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 28 Jun 2022 18:17:27 -0400 Subject: migrate update checks to FluentHttpClient WebClient isn't needed for compatibility with macOS after the .NET 5 update in Stardew Valley 1.5.5, and causes noticeable lag for some players even when running on a background thread. --- build/deploy-local-smapi.targets | 4 ++ build/unix/prepare-install-package.sh | 2 +- build/windows/prepare-install-package.ps1 | 2 +- docs/release-notes.md | 5 +- .../Framework/Clients/WebApi/WebApiClient.cs | 56 ++++++++-------------- src/SMAPI.Toolkit/Serialization/JsonHelper.cs | 26 ++++++---- src/SMAPI.Web/Startup.cs | 2 +- src/SMAPI/Framework/SCore.cs | 44 +++++++++++------ src/SMAPI/SMAPI.csproj | 1 + 9 files changed, 78 insertions(+), 64 deletions(-) (limited to 'src/SMAPI.Toolkit') diff --git a/build/deploy-local-smapi.targets b/build/deploy-local-smapi.targets index cb330e24..6ea5f0a2 100644 --- a/build/deploy-local-smapi.targets +++ b/build/deploy-local-smapi.targets @@ -35,6 +35,10 @@ This assumes `find-game-folder.targets` has already been imported and validated. + + + + diff --git a/build/unix/prepare-install-package.sh b/build/unix/prepare-install-package.sh index 01cd2080..1d805e00 100755 --- a/build/unix/prepare-install-package.sh +++ b/build/unix/prepare-install-package.sh @@ -134,7 +134,7 @@ for folder in ${folders[@]}; do cp -r "$smapiBin/i18n" "$bundlePath/smapi-internal" # bundle smapi-internal - for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.pdb" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.pdb" "SMAPI.Toolkit.CoreInterfaces.xml"; do + for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.pdb" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.pdb" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do cp "$smapiBin/$name" "$bundlePath/smapi-internal" done diff --git a/build/windows/prepare-install-package.ps1 b/build/windows/prepare-install-package.ps1 index 7e3c6c86..87a4fe01 100644 --- a/build/windows/prepare-install-package.ps1 +++ b/build/windows/prepare-install-package.ps1 @@ -154,7 +154,7 @@ foreach ($folder in $folders) { cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal" # bundle smapi-internal - foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.pdb", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.pdb", "SMAPI.Toolkit.CoreInterfaces.xml")) { + foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.pdb", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.pdb", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) { cp "$smapiBin/$name" "$bundlePath/smapi-internal" } diff --git a/docs/release-notes.md b/docs/release-notes.md index 4c504150..414396f4 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -9,9 +9,12 @@ ## Upcoming release * For players: - * Minor optimizations. + * Fixed lag which occurred for some players since Stardew Valley 1.5.5. * Fixed `smapi-internal/config.user.json` overrides not applied after SMAPI 3.14.0. +* For mod authors: + * The [FluentHttpClient package](https://github.com/Pathoschild/FluentHttpClient#readme) is now loaded by SMAPI. + * For the web UI: * Updated the JSON validator/schema for Content Patcher 1.27.0. * Fixed the mod count in the log parser metadata. diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs index d4282617..ef1904d4 100644 --- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs +++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs @@ -1,27 +1,24 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; -using Newtonsoft.Json; +using System.Threading.Tasks; +using Pathoschild.Http.Client; using StardewModdingAPI.Toolkit.Serialization; using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi { /// Provides methods for interacting with the SMAPI web API. - public class WebApiClient + public class WebApiClient : IDisposable { /********* ** Fields *********/ - /// The base URL for the web API. - private readonly Uri BaseUrl; - /// The API version number. private readonly ISemanticVersion Version; - /// The JSON serializer settings to use. - private readonly JsonSerializerSettings JsonSettings = new JsonHelper().JsonSettings; + /// The underlying HTTP client. + private readonly IClient Client; /********* @@ -32,8 +29,11 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi /// The web API version. public WebApiClient(string baseUrl, ISemanticVersion version) { - this.BaseUrl = new Uri(baseUrl); this.Version = version; + this.Client = new FluentClient(baseUrl) + .SetUserAgent($"SMAPI/{version}"); + + this.Client.Formatters.JsonFormatter.SerializerSettings = JsonHelper.CreateDefaultSettings(); } /// Get metadata about a set of mods from the web API. @@ -42,36 +42,22 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi /// The Stardew Valley version installed by the player. /// The OS on which the player plays. /// Whether to include extended metadata for each mod. - public IDictionary GetModInfo(ModSearchEntryModel[] mods, ISemanticVersion apiVersion, ISemanticVersion gameVersion, Platform platform, bool includeExtendedMetadata = false) + public async Task> GetModInfoAsync(ModSearchEntryModel[] mods, ISemanticVersion apiVersion, ISemanticVersion gameVersion, Platform platform, bool includeExtendedMetadata = false) { - return this.Post( - $"v{this.Version}/mods", - new ModSearchModel(mods, apiVersion, gameVersion, platform, includeExtendedMetadata) - ).ToDictionary(p => p.ID); + ModEntryModel[] result = await this.Client + .PostAsync( + $"v{this.Version}/mods", + new ModSearchModel(mods, apiVersion, gameVersion, platform, includeExtendedMetadata) + ) + .As(); + + return result.ToDictionary(p => p.ID); } - - /********* - ** Private methods - *********/ - /// Fetch the response from the backend API. - /// The body content type. - /// The expected response type. - /// The request URL, optionally excluding the base URL. - /// The body content to post. - private TResult Post(string url, TBody content) + /// + public void Dispose() { - // note: avoid HttpClient for macOS compatibility - using WebClient client = new(); - - Uri fullUrl = new(this.BaseUrl, url); - string data = JsonConvert.SerializeObject(content); - - client.Headers["Content-Type"] = "application/json"; - client.Headers["User-Agent"] = $"SMAPI/{this.Version}"; - string response = client.UploadString(fullUrl, data); - return JsonConvert.DeserializeObject(response, this.JsonSettings) - ?? throw new InvalidOperationException($"Could not parse the response from POST {url}."); + this.Client.Dispose(); } } } diff --git a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs index 1a003c51..a5d7e2e8 100644 --- a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs +++ b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs @@ -15,21 +15,27 @@ namespace StardewModdingAPI.Toolkit.Serialization ** Accessors *********/ /// The JSON settings to use when serializing and deserializing files. - public JsonSerializerSettings JsonSettings { get; } = new() - { - Formatting = Formatting.Indented, - ObjectCreationHandling = ObjectCreationHandling.Replace, // avoid issue where default ICollection values are duplicated each time the config is loaded - Converters = new List - { - new SemanticVersionConverter(), - new StringEnumConverter() - } - }; + public JsonSerializerSettings JsonSettings { get; } = JsonHelper.CreateDefaultSettings(); /********* ** Public methods *********/ + /// Create an instance of the default JSON serializer settings. + public static JsonSerializerSettings CreateDefaultSettings() + { + return new() + { + Formatting = Formatting.Indented, + ObjectCreationHandling = ObjectCreationHandling.Replace, // avoid issue where default ICollection values are duplicated each time the config is loaded + Converters = new List + { + new SemanticVersionConverter(), + new StringEnumConverter() + } + }; + } + /// Read a JSON file. /// The model type. /// The absolute file path. diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 9980d00c..54c25979 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -199,7 +199,7 @@ namespace StardewModdingAPI.Web /// The serializer settings to edit. private void ConfigureJsonNet(JsonSerializerSettings settings) { - foreach (JsonConverter converter in new JsonHelper().JsonSettings.Converters) + foreach (JsonConverter converter in JsonHelper.CreateDefaultSettings().Converters) settings.Converters.Add(converter); settings.Formatting = Formatting.Indented; diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 16c168a0..fdfe70fc 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -10,6 +10,7 @@ using System.Runtime.ExceptionServices; using System.Security; using System.Text; using System.Threading; +using System.Threading.Tasks; using Microsoft.Xna.Framework; #if SMAPI_FOR_WINDOWS using Microsoft.Win32; @@ -406,7 +407,7 @@ namespace StardewModdingAPI.Framework this.CheckForSoftwareConflicts(); // check for updates - this.CheckForUpdatesAsync(mods); + _ = this.CheckForUpdatesAsync(mods); // ignore task since the main thread doesn't need to wait for it } // update window titles @@ -1450,16 +1451,15 @@ namespace StardewModdingAPI.Framework /// Asynchronously check for a new version of SMAPI and any installed mods, and print alerts to the console if an update is available. /// The mods to include in the update check (if eligible). - private void CheckForUpdatesAsync(IModMetadata[] mods) + private async Task CheckForUpdatesAsync(IModMetadata[] mods) { - if (!this.Settings.CheckForUpdates) - return; - - new Thread(() => + try { + if (!this.Settings.CheckForUpdates) + return; + // create client - string url = this.Settings.WebApiBaseUrl; - WebApiClient client = new(url, Constants.ApiVersion); + using WebApiClient client = new(this.Settings.WebApiBaseUrl, Constants.ApiVersion); this.Monitor.Log("Checking for updates..."); // check SMAPI version @@ -1469,9 +1469,15 @@ namespace StardewModdingAPI.Framework try { // fetch update check - ModEntryModel response = client.GetModInfo(new[] { new ModSearchEntryModel("Pathoschild.SMAPI", Constants.ApiVersion, new[] { $"GitHub:{this.Settings.GitHubProjectName}" }) }, apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform).Single().Value; - updateFound = response.SuggestedUpdate?.Version; - updateUrl = response.SuggestedUpdate?.Url; + IDictionary response = await client.GetModInfoAsync( + mods: new[] { new ModSearchEntryModel("Pathoschild.SMAPI", Constants.ApiVersion, new[] { $"GitHub:{this.Settings.GitHubProjectName}" }) }, + apiVersion: Constants.ApiVersion, + gameVersion: Constants.GameVersion, + platform: Constants.Platform + ); + ModEntryModel updateInfo = response.Single().Value; + updateFound = updateInfo.SuggestedUpdate?.Version; + updateUrl = updateInfo.SuggestedUpdate?.Url; // log message if (updateFound != null) @@ -1480,10 +1486,10 @@ namespace StardewModdingAPI.Framework this.Monitor.Log(" SMAPI okay."); // show errors - if (response.Errors.Any()) + if (updateInfo.Errors.Any()) { this.Monitor.Log("Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.", LogLevel.Warn); - this.Monitor.Log($"Error: {string.Join("\n", response.Errors)}"); + this.Monitor.Log($"Error: {string.Join("\n", updateInfo.Errors)}"); } } catch (Exception ex) @@ -1523,7 +1529,7 @@ namespace StardewModdingAPI.Framework // fetch results this.Monitor.Log($" Checking for updates to {searchMods.Count} mods..."); - IDictionary results = client.GetModInfo(searchMods.ToArray(), apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform); + IDictionary results = await client.GetModInfoAsync(searchMods.ToArray(), apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform); // extract update alerts & errors var updates = new List>(); @@ -1573,7 +1579,15 @@ namespace StardewModdingAPI.Framework ); } } - }).Start(); + } + catch (Exception ex) + { + this.Monitor.Log("Couldn't check for updates. This won't affect your game, but you won't be notified of SMAPI or mod updates if this keeps happening.", LogLevel.Warn); + this.Monitor.Log(ex is WebException && ex.InnerException == null + ? ex.Message + : ex.ToString() + ); + } } /// Create a directory path if it doesn't exist. diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj index 3abefeab..c05512e9 100644 --- a/src/SMAPI/SMAPI.csproj +++ b/src/SMAPI/SMAPI.csproj @@ -25,6 +25,7 @@ + -- cgit From 6b411d1dee49a636a8a8c9a88d9cfc0720c7c1dd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 28 Jun 2022 20:48:28 -0400 Subject: fix deprecation check --- src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI.Toolkit') diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs index 338192af..4c76f417 100644 --- a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs +++ b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData /// The mod patches the game in a way that may impact stability. PatchesGame = 4, -#if SMAPI_FOR_WINDOWS +#if SMAPI_DEPRECATED /// The mod uses the dynamic keyword which won't work on Linux/macOS. [Obsolete("This value is no longer used by SMAPI and will be removed in the upcoming SMAPI 4.0.0.")] UsesDynamic = 8, -- cgit From 4d9fd63d9e890a10029508c7d7e31dcc0b579db7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 6 Jul 2022 19:24:49 -0400 Subject: update code annotations --- src/SMAPI.Installer/InteractiveInstaller.cs | 1 + src/SMAPI.ModBuildConfig/DeployModTask.cs | 3 +-- src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs | 2 +- src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs | 10 +++++----- src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs | 7 +++++-- src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs | 2 +- src/SMAPI/Framework/ContentManagers/BaseContentManager.cs | 3 +-- src/SMAPI/Framework/Logging/LogManager.cs | 2 +- src/SMAPI/Framework/ModLoading/ModMetadata.cs | 2 +- src/SMAPI/Framework/ModLoading/ModResolver.cs | 4 ++-- .../ModLoading/RewriteFacades/HarmonyInstanceFacade.cs | 2 +- .../Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs | 1 - src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs | 2 +- src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs | 2 +- src/SMAPI/Framework/SCore.cs | 6 +++--- src/SMAPI/Framework/SGame.cs | 3 +++ src/SMAPI/Metadata/CoreAssetPropagator.cs | 2 +- src/SMAPI/Translation.cs | 2 +- src/SMAPI/Utilities/SDate.cs | 2 +- 19 files changed, 31 insertions(+), 27 deletions(-) (limited to 'src/SMAPI.Toolkit') diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index 5a6aa747..fd1a6047 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -453,6 +453,7 @@ namespace StardewModdingApi.Installer } // find target folder + // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract -- avoid error if the Mods folder has invalid mods, since they're not validated yet ModFolder? targetMod = targetMods.FirstOrDefault(p => p.Manifest?.UniqueID?.Equals(sourceMod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase) == true); DirectoryInfo defaultTargetFolder = new(Path.Combine(paths.ModsPath, sourceMod.Directory.Name)); DirectoryInfo targetFolder = targetMod?.Directory ?? defaultTargetFolder; diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs index c7026ee1..88412d92 100644 --- a/src/SMAPI.ModBuildConfig/DeployModTask.cs +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -227,8 +227,7 @@ namespace StardewModdingAPI.ModBuildConfig string fromPath = entry.Value.FullName; string toPath = Path.Combine(modFolderPath, entry.Key); - // ReSharper disable once AssignNullToNotNullAttribute -- not applicable in this context - Directory.CreateDirectory(Path.GetDirectoryName(toPath)); + Directory.CreateDirectory(Path.GetDirectoryName(toPath)!); File.Copy(fromPath, toPath, overwrite: true); } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs index 3722e155..88ddfe6b 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs @@ -30,7 +30,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework /// Get all spawnable items. /// The item types to fetch (or null for any type). /// Whether to include flavored variants like "Sunflower Honey". - [SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = "TryCreate invokes the lambda immediately.")] + [SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = $"{nameof(ItemRepository.TryCreate)} invokes the lambda immediately.")] public IEnumerable GetAll(ItemType[]? itemTypes = null, bool includeVariants = true) { // diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs index 7f06d170..3bdd145a 100644 --- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs +++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs @@ -283,8 +283,8 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki } /// The response model for the MediaWiki parse API. - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local", Justification = "Used via JSON deserialization.")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local", Justification = "Used via JSON deserialization.")] private class ResponseModel { /********* @@ -306,9 +306,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki } /// The inner response model for the MediaWiki parse API. - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] - [SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")] - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local", Justification = "Used via JSON deserialization.")] + [SuppressMessage("ReSharper", "CollectionNeverUpdated.Local", Justification = "Used via JSON deserialization.")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local", Justification = "Used via JSON deserialization.")] private class ResponseParseModel { /********* diff --git a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs index 6978567e..118b71b2 100644 --- a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs +++ b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs @@ -21,6 +21,7 @@ namespace StardewModdingAPI.Toolkit.Framework /// Get the OS name from the system uname command. /// The buffer to fill with the resulting string. [DllImport("libc")] + [SuppressMessage("ReSharper", "IdentifierTypo", Justification = "This is the actual external command name.")] static extern int uname(IntPtr buffer); @@ -51,7 +52,6 @@ namespace StardewModdingAPI.Toolkit.Framework /// Get the human-readable OS name and version. /// The current platform. - [SuppressMessage("ReSharper", "EmptyGeneralCatchClause", Justification = "Error suppressed deliberately to fallback to default behaviour.")] public static string GetFriendlyPlatformName(string platform) { #if SMAPI_FOR_WINDOWS @@ -65,7 +65,10 @@ namespace StardewModdingAPI.Toolkit.Framework return result ?? "Windows"; } - catch { } + catch + { + // fallback to default behavior + } #endif string name = Environment.OSVersion.ToString(); diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs index c60b2c90..f5a5f930 100644 --- a/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs +++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs @@ -42,7 +42,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.ModDrop /// Get update check info about a mod. /// The mod ID. - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The nullability is validated in this method.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "The nullability is validated in this method.")] public async Task GetModData(string id) { IModPage page = new GenericModPage(this.SiteKey, id); diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs index d7be0c37..54f8e2a2 100644 --- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs @@ -111,7 +111,6 @@ namespace StardewModdingAPI.Framework.ContentManagers } /// - [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", Justification = "Copied as-is from game code")] public sealed override string LoadBaseString(string path) { try @@ -119,7 +118,7 @@ namespace StardewModdingAPI.Framework.ContentManagers // copied as-is from LocalizedContentManager.LoadBaseString // This is only changed to call this.Load instead of base.Load, to support mod assets this.ParseStringPath(path, out string assetName, out string key); - Dictionary strings = this.Load>(assetName, LanguageCode.en); + Dictionary? strings = this.Load?>(assetName, LanguageCode.en); return strings != null && strings.ContainsKey(key) ? this.GetString(strings, key) : path; diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index d811ed5c..c0b7c0ba 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -400,7 +400,7 @@ namespace StardewModdingAPI.Framework.Logging /// The loaded mods. /// The mods which could not be loaded. /// Whether to log issues for mods which directly use potentially sensitive .NET APIs like file or shell access. - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "Manifests aren't guaranteed non-null at this point in the loading process.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "Manifests aren't guaranteed non-null at this point in the loading process.")] private void LogModWarnings(IEnumerable mods, IModMetadata[] skippedMods, bool logParanoidWarnings) { // get mods with warnings diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index aa4d2d8c..ac7a6bbd 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -83,7 +83,7 @@ namespace StardewModdingAPI.Framework.ModLoading /// [MemberNotNullWhen(true, nameof(ModMetadata.ContentPack))] - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The manifest may be null for broken mods while loading.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "The manifest may be null for broken mods while loading.")] public bool IsContentPack => this.Manifest?.ContentPackFor != null; /// The fake content packs created by this mod, if any. diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 3e7144f9..abc46d47 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -60,8 +60,8 @@ namespace StardewModdingAPI.Framework.ModLoading /// Get an update URL for an update key (if valid). /// Get a file lookup for the given directory. /// Whether to validate that files referenced in the manifest (like ) exist on disk. This can be disabled to only validate the manifest itself. - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "Manifest values may be null before they're validated.")] - [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", Justification = "Manifest values may be null before they're validated.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "Manifest values may be null before they're validated.")] + [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract", Justification = "Manifest values may be null before they're validated.")] public void ValidateManifests(IEnumerable mods, ISemanticVersion apiVersion, Func getUpdateUrl, Func getFileLookup, bool validateFilesExist = true) { mods = mods.ToArray(); diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs index 9c8ba2b0..be45272e 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades return new Harmony(id); } - [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", Justification = "If the user passes a null original method, we let it fail in the underlying Harmony instance instead of handling it here.")] + [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract", Justification = "If the user passes a null original method, we let it fail in the underlying Harmony instance instead of handling it here.")] public DynamicMethod Patch(MethodBase original, HarmonyMethod? prefix = null, HarmonyMethod? postfix = null, HarmonyMethod? transpiler = null) { // In Harmony 1.x you could target a virtual method that's not implemented by the diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs index 67569424..3eb31df3 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs @@ -10,7 +10,6 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades /// Provides method signatures that can be injected into mod code for compatibility with mods written for XNA Framework before Stardew Valley 1.5.5. /// This is public to support SMAPI rewriting and should not be referenced directly by mods. [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")] - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")] [SuppressMessage("ReSharper", "CS1591", Justification = "Documentation not needed for facade classes.")] public class SpriteBatchFacade : SpriteBatch { diff --git a/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs b/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs index 1e150508..cc936489 100644 --- a/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs +++ b/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs @@ -22,7 +22,7 @@ namespace StardewModdingAPI.Framework.Networking *********/ /// Construct an instance. /// The mod metadata. - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The ID shouldn't be null, but we should handle it to avoid an error just in case.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "The ID shouldn't be null, but we should handle it to avoid an error just in case.")] public MultiplayerPeerMod(RemoteContextModModel mod) { this.Name = mod.Name; diff --git a/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs b/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs index 94b13378..dac41629 100644 --- a/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs +++ b/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs @@ -14,7 +14,7 @@ namespace StardewModdingAPI.Framework.Rendering { /// A map display device which reimplements the default logic. /// This is an exact copy of , except that private fields are protected and all methods are virtual. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Field naming deliberately matches " + nameof(XnaDisplayDevice) + " to minimize differences.")] + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = $"Field naming deliberately matches {nameof(XnaDisplayDevice)} to minimize differences.")] internal class SXnaDisplayDevice : IDisplayDevice { /********* diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index d0effb03..d9969d4d 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -325,7 +325,7 @@ namespace StardewModdingAPI.Framework } /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "May be disposed before SMAPI is fully initialized.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "May be disposed before SMAPI is fully initialized.")] public void Dispose() { // skip if already disposed @@ -1285,7 +1285,7 @@ namespace StardewModdingAPI.Framework private LocalizedContentManager CreateContentManager(IServiceProvider serviceProvider, string rootDirectory) { // Game1._temporaryContent initializing from SGame constructor - // ReSharper disable once ConditionIsAlwaysTrueOrFalse -- this is the method that initializes it + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- this is the method that initializes it if (this.ContentCore == null) { this.ContentCore = new ContentCoordinator( @@ -1808,7 +1808,7 @@ namespace StardewModdingAPI.Framework string relativePath = mod.GetRelativePathWithRoot(); if (mod.IsContentPack) this.Monitor.Log($" {mod.DisplayName} (from {relativePath}) [content pack]..."); - // ReSharper disable once ConstantConditionalAccessQualifier -- mod may be invalid at this point + // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract -- mod may be invalid at this point else if (mod.Manifest?.EntryDll != null) this.Monitor.Log($" {mod.DisplayName} (from {relativePath}{Path.DirectorySeparatorChar}{mod.Manifest.EntryDll})..."); // don't use Path.Combine here, since EntryDLL might not be valid else diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 38043e1c..feb0988a 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -252,6 +252,7 @@ namespace StardewModdingAPI.Framework [SuppressMessage("ReSharper", "LocalVariableHidesMember", Justification = "copied from game code as-is")] [SuppressMessage("ReSharper", "MergeIntoPattern", Justification = "copied from game code as-is")] [SuppressMessage("ReSharper", "PossibleLossOfFraction", Justification = "copied from game code as-is")] + [SuppressMessage("ReSharper", "PossibleNullReferenceException", Justification = "copied from game code as-is")] [SuppressMessage("ReSharper", "RedundantArgumentDefaultValue", Justification = "copied from game code as-is")] [SuppressMessage("ReSharper", "RedundantBaseQualifier", Justification = "copied from game code as-is")] [SuppressMessage("ReSharper", "RedundantCast", Justification = "copied from game code as-is")] @@ -261,6 +262,8 @@ namespace StardewModdingAPI.Framework [SuppressMessage("ReSharper", "MergeIntoPattern", Justification = "copied from game code as-is")] [SuppressMessage("SMAPI.CommonErrors", "AvoidImplicitNetFieldCast", Justification = "copied from game code as-is")] [SuppressMessage("SMAPI.CommonErrors", "AvoidNetField", Justification = "copied from game code as-is")] + + [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", Justification = "Deliberate to minimize chance of errors when copying event calls into new versions of this code.")] private void DrawImpl(GameTime gameTime, RenderTarget2D target_screen) { var events = this.Events; diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs index 1b4d1737..1ef9a8f2 100644 --- a/src/SMAPI/Metadata/CoreAssetPropagator.cs +++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs @@ -966,7 +966,7 @@ namespace StardewModdingAPI.Metadata { // get suspension bridges field var field = this.Reflection.GetField?>(location, nameof(IslandNorth.suspensionBridges), required: false); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse -- field is nullable when required: false + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- field is nullable when required: false if (field == null || !typeof(IEnumerable).IsAssignableFrom(field.FieldInfo.FieldType)) continue; diff --git a/src/SMAPI/Translation.cs b/src/SMAPI/Translation.cs index 01cb92b2..5cc119d9 100644 --- a/src/SMAPI/Translation.cs +++ b/src/SMAPI/Translation.cs @@ -121,7 +121,7 @@ namespace StardewModdingAPI /// Get a string representation of the given translation. /// The translation key. /// Limitation with nullable reference types: if there's no text and you disabled the fallback via , this will return null but the return value will still be marked non-nullable. - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The null check is required due to limitations in nullable type annotations (see remarks).")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "The null check is required due to limitations in nullable type annotations (see remarks).")] public static implicit operator string(Translation translation) { return translation?.ToString()!; diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs index 1d4e4489..c7ad41bd 100644 --- a/src/SMAPI/Utilities/SDate.cs +++ b/src/SMAPI/Utilities/SDate.cs @@ -250,7 +250,7 @@ namespace StardewModdingAPI.Utilities /// The year. /// Whether to allow 0 spring Y1 as a valid date. /// One of the arguments has an invalid value (like day 35). - [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The nullability is validated in this constructor.")] + [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "The nullability is validated in this constructor.")] private SDate(int day, string season, int year, bool allowDayZero) { season = season?.Trim().ToLowerInvariant()!; // null-checked below -- cgit From 9c9552531f8f085cded6ed5158503e75b00be1ff Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 6 Jul 2022 19:25:15 -0400 Subject: fix build warnings --- src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs | 2 +- src/SMAPI.Toolkit/ModToolkit.cs | 2 +- .../Serialization/Converters/SemanticVersionConverter.cs | 7 ++++++- .../Serialization/Converters/SimpleReadOnlyConverter.cs | 7 ++++++- src/SMAPI/Framework/ModHelpers/ModHelper.cs | 1 + src/SMAPI/Framework/Models/SConfig.cs | 2 +- src/SMAPI/Framework/SCore.cs | 2 +- src/SMAPI/Framework/Serialization/KeybindConverter.cs | 5 ++++- src/SMAPI/Utilities/SDate.cs | 4 ++-- 9 files changed, 23 insertions(+), 9 deletions(-) (limited to 'src/SMAPI.Toolkit') diff --git a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs index 118b71b2..f464f4bb 100644 --- a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs +++ b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs @@ -22,7 +22,7 @@ namespace StardewModdingAPI.Toolkit.Framework /// The buffer to fill with the resulting string. [DllImport("libc")] [SuppressMessage("ReSharper", "IdentifierTypo", Justification = "This is the actual external command name.")] - static extern int uname(IntPtr buffer); + private static extern int uname(IntPtr buffer); /********* diff --git a/src/SMAPI.Toolkit/ModToolkit.cs b/src/SMAPI.Toolkit/ModToolkit.cs index 0df75a31..55b9bdd8 100644 --- a/src/SMAPI.Toolkit/ModToolkit.cs +++ b/src/SMAPI.Toolkit/ModToolkit.cs @@ -65,7 +65,7 @@ namespace StardewModdingAPI.Toolkit /// The file path for the SMAPI metadata file. public ModDatabase GetModDatabase(string metadataPath) { - MetadataModel metadata = JsonConvert.DeserializeObject(File.ReadAllText(metadataPath)); + MetadataModel metadata = JsonConvert.DeserializeObject(File.ReadAllText(metadataPath)) ?? new MetadataModel(); ModDataRecord[] records = metadata.ModData.Select(pair => new ModDataRecord(pair.Key, pair.Value)).ToArray(); return new ModDatabase(records, this.GetUpdateUrl); } diff --git a/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs b/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs index c32c3185..913d54e0 100644 --- a/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs +++ b/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs @@ -48,7 +48,12 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters return this.ReadObject(JObject.Load(reader)); case JsonToken.String: - return this.ReadString(JToken.Load(reader).Value(), path); + { + string? value = JToken.Load(reader).Value(); + return value is not null + ? this.ReadString(value, path) + : null; + } default: throw new SParseException($"Can't parse {nameof(ISemanticVersion)} from {reader.TokenType} node (path: {reader.Path})."); diff --git a/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs b/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs index 1c59f5e7..cdf2ed77 100644 --- a/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs +++ b/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs @@ -42,7 +42,12 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters return this.ReadObject(JObject.Load(reader), path); case JsonToken.String: - return this.ReadString(JToken.Load(reader).Value(), path); + { + string? value = JToken.Load(reader).Value(); + return value is not null + ? this.ReadString(value, path) + : null; + } default: throw new SParseException($"Can't parse {typeof(T).Name} from {reader.TokenType} node (path: {reader.Path})."); diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 9ac3b6f7..caa66bad 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -88,6 +88,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The full path to the mod's folder. /// Manages the game's input state for the current player instance. That may not be the main player in split-screen mode. /// Manages access to events raised by SMAPI. + /// An API for loading content assets. /// An API for loading content assets from the game's Content folder or via . /// An API for loading content assets from your mod's files. /// An API for managing content packs. diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs index 62b15405..9444c046 100644 --- a/src/SMAPI/Framework/Models/SConfig.cs +++ b/src/SMAPI/Framework/Models/SConfig.cs @@ -100,7 +100,7 @@ namespace StardewModdingAPI.Framework.Models /// Whether SMAPI should log network traffic. /// The colors to use for text written to the SMAPI console. /// The mod IDs SMAPI should ignore when performing update checks or validating update keys. - public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? usePintail, bool? useRawImageLoading, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, string[]? suppressUpdateChecks) + public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useRawImageLoading, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, string[]? suppressUpdateChecks) { this.DeveloperMode = developerMode; this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)]; diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index d9969d4d..46d65f6a 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -191,7 +191,7 @@ namespace StardewModdingAPI.Framework string logPath = this.GetLogPath(); // init basics - this.Settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)); + this.Settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)) ?? throw new InvalidOperationException("The 'smapi-internal/config.json' file is missing or invalid. You can reinstall SMAPI to fix this."); if (File.Exists(Constants.ApiUserConfigPath)) JsonConvert.PopulateObject(File.ReadAllText(Constants.ApiUserConfigPath), this.Settings); if (developerMode.HasValue) diff --git a/src/SMAPI/Framework/Serialization/KeybindConverter.cs b/src/SMAPI/Framework/Serialization/KeybindConverter.cs index 539f1291..f7b8e67e 100644 --- a/src/SMAPI/Framework/Serialization/KeybindConverter.cs +++ b/src/SMAPI/Framework/Serialization/KeybindConverter.cs @@ -49,7 +49,10 @@ namespace StardewModdingAPI.Framework.Serialization case JsonToken.String: { - string str = JToken.Load(reader).Value(); + string? str = JToken.Load(reader).Value(); + + if (str is null) + return new Keybind(Array.Empty()); if (objectType == typeof(Keybind)) { diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs index c7ad41bd..06ee8b91 100644 --- a/src/SMAPI/Utilities/SDate.cs +++ b/src/SMAPI/Utilities/SDate.cs @@ -278,11 +278,11 @@ namespace StardewModdingAPI.Utilities /// Get whether a date represents 0 spring Y1, which is the date during the in-game intro. /// The day of month. - /// The season name. + /// The normalized season name. /// The year. private bool IsDayZero(int day, string season, int year) { - return day == 0 && season?.Trim().ToLower() == "spring" && year == 1; + return day == 0 && season == "spring" && year == 1; } /// Get the day of week for a given date. -- cgit