diff options
31 files changed, 347 insertions, 407 deletions
diff --git a/build/common.targets b/build/common.targets index 44ee0caf..0436ed5b 100644 --- a/build/common.targets +++ b/build/common.targets @@ -1,7 +1,7 @@ <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <!--set general build properties --> - <Version>3.14.1</Version> + <Version>3.14.2</Version> <Product>SMAPI</Product> <LangVersion>latest</LangVersion> <AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths> diff --git a/docs/release-notes.md b/docs/release-notes.md index 82cf51db..98747613 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,17 @@ ← [README](README.md) # Release notes +## 3.14.2 +Released 08 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Enabled case-insensitive file paths by default for Android and Linux players. + _This was temporarily disabled in SMAPI 3.14.1, and will remain disabled by default on macOS and Windows since their filesystems are already case-insensitive._ + * Various performance improvements. +* For mod authors: + * Dynamic content packs created via `helper.ContentPacks.CreateTemporary` or `CreateFake` are now listed in the log file. + * Fixed assets loaded through a fake content pack not working correctly since 3.14.0. + ## 3.14.1 Released 06 May 2022 for Stardew Valley 1.5.6 or later. diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index edbdd081..c263456a 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,9 +1,9 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "3.14.1", + "Version": "3.14.2", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "3.14.1" + "MinimumApiVersion": "3.14.2" } diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json index af67453d..6e6a271f 100644 --- a/src/SMAPI.Mods.ErrorHandler/manifest.json +++ b/src/SMAPI.Mods.ErrorHandler/manifest.json @@ -1,9 +1,9 @@ { "Name": "Error Handler", "Author": "SMAPI", - "Version": "3.14.1", + "Version": "3.14.2", "Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.", "UniqueID": "SMAPI.ErrorHandler", "EntryDll": "ErrorHandler.dll", - "MinimumApiVersion": "3.14.1" + "MinimumApiVersion": "3.14.2" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index 14b68cb4..5ba91568 100644 --- a/src/SMAPI.Mods.SaveBackup/manifest.json +++ b/src/SMAPI.Mods.SaveBackup/manifest.json @@ -1,9 +1,9 @@ { "Name": "Save Backup", "Author": "SMAPI", - "Version": "3.14.1", + "Version": "3.14.2", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "3.14.1" + "MinimumApiVersion": "3.14.2" } diff --git a/src/SMAPI.Tests/Core/ModResolverTests.cs b/src/SMAPI.Tests/Core/ModResolverTests.cs index 3dfc9461..70c782ab 100644 --- a/src/SMAPI.Tests/Core/ModResolverTests.cs +++ b/src/SMAPI.Tests/Core/ModResolverTests.cs @@ -133,7 +133,7 @@ namespace SMAPI.Tests.Core [Test(Description = "Assert that validation doesn't fail if there are no mods installed.")] public void ValidateManifests_NoMods_DoesNothing() { - new ModResolver().ValidateManifests(Array.Empty<ModMetadata>(), apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance, validateFilesExist: false); + new ModResolver().ValidateManifests(Array.Empty<ModMetadata>(), apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup, validateFilesExist: false); } [Test(Description = "Assert that validation skips manifests that have already failed without calling any other properties.")] @@ -144,7 +144,7 @@ namespace SMAPI.Tests.Core mock.Setup(p => p.Status).Returns(ModMetadataStatus.Failed); // act - new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance, validateFilesExist: false); + new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup, validateFilesExist: false); // assert mock.VerifyGet(p => p.Status, Times.Once, "The validation did not check the manifest status."); @@ -161,7 +161,7 @@ namespace SMAPI.Tests.Core }); // act - new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance, validateFilesExist: false); + new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup, validateFilesExist: false); // assert mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata."); @@ -175,7 +175,7 @@ namespace SMAPI.Tests.Core mock.Setup(p => p.Manifest).Returns(this.GetManifest(minimumApiVersion: "1.1")); // act - new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance, validateFilesExist: false); + new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup, validateFilesExist: false); // assert mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata."); @@ -190,7 +190,7 @@ namespace SMAPI.Tests.Core Directory.CreateDirectory(directoryPath); // act - new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance); + new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup); // assert mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata."); @@ -207,7 +207,7 @@ namespace SMAPI.Tests.Core Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest(id: "Mod A", name: "Mod B", version: "1.0"), allowStatusChange: true); // act - new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance, validateFilesExist: false); + new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup, validateFilesExist: false); // assert modA.Verify(p => p.SetStatus(ModMetadataStatus.Failed, ModFailReason.Duplicate, It.IsAny<string>(), It.IsAny<string>()), Times.AtLeastOnce, "The validation did not fail the first mod with a unique ID."); @@ -233,7 +233,7 @@ namespace SMAPI.Tests.Core mock.Setup(p => p.DirectoryPath).Returns(modFolder); // act - new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFilePathLookup: _ => MinimalPathLookup.Instance); + new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null, getFileLookup: this.GetFileLookup); // assert // if Moq doesn't throw a method-not-setup exception, the validation didn't override the status. @@ -483,6 +483,13 @@ namespace SMAPI.Tests.Core return Path.Combine(Path.GetTempPath(), "smapi-unit-tests", Guid.NewGuid().ToString("N")); } + /// <summary>Get a file lookup for a given directory.</summary> + /// <param name="rootDirectory">The full path to the directory.</param> + private IFileLookup GetFileLookup(string rootDirectory) + { + return MinimalFileLookup.GetCachedFor(rootDirectory); + } + /// <summary>Get a randomized basic manifest.</summary> /// <param name="id">The <see cref="IManifest.UniqueID"/> value, or <c>null</c> for a generated value.</param> /// <param name="name">The <see cref="IManifest.Name"/> value, or <c>null</c> for a generated value.</param> diff --git a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs index aa4c3338..a85ef109 100644 --- a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -114,10 +114,11 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// <summary>Extract information from a mod folder.</summary> /// <param name="root">The root folder containing mods.</param> /// <param name="searchFolder">The folder to search for a mod.</param> - public ModFolder ReadFolder(DirectoryInfo root, DirectoryInfo searchFolder) + /// <param name="useCaseInsensitiveFilePaths">Whether to match file paths case-insensitively, even on Linux.</param> + public ModFolder ReadFolder(DirectoryInfo root, DirectoryInfo searchFolder, bool useCaseInsensitiveFilePaths) { // find manifest.json - FileInfo? manifestFile = this.FindManifest(searchFolder); + FileInfo? manifestFile = this.FindManifest(searchFolder, useCaseInsensitiveFilePaths); // set appropriate invalid-mod error if (manifestFile == null) @@ -225,7 +226,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning // treat as mod folder else - yield return this.ReadFolder(root, folder); + yield return this.ReadFolder(root, folder, useCaseInsensitiveFilePaths); } /// <summary>Consolidate adjacent folders into one mod folder, if possible.</summary> @@ -250,7 +251,8 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// <summary>Find the manifest for a mod folder.</summary> /// <param name="folder">The folder to search.</param> - private FileInfo? FindManifest(DirectoryInfo folder) + /// <param name="useCaseInsensitiveFilePaths">Whether to match file paths case-insensitively, even on Linux.</param> + private FileInfo? FindManifest(DirectoryInfo folder, bool useCaseInsensitiveFilePaths) { // check for conventional manifest in current folder const string defaultName = "manifest.json"; @@ -259,14 +261,14 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning return file; // check for manifest with incorrect capitalization + if (useCaseInsensitiveFilePaths) { - CaseInsensitivePathLookup pathLookup = new(folder.FullName, SearchOption.TopDirectoryOnly); // don't use GetCachedFor, since we only need it temporarily - string realName = pathLookup.GetFilePath(defaultName); - if (realName != defaultName) - file = new(Path.Combine(folder.FullName, realName)); + CaseInsensitiveFileLookup fileLookup = new(folder.FullName, SearchOption.TopDirectoryOnly); // don't use GetCachedFor, since we only need it temporarily + file = fileLookup.GetFile(defaultName); + return file.Exists + ? file + : null; } - if (file.Exists) - return file; // not found return null; diff --git a/src/SMAPI.Toolkit/Utilities/PathLookups/CaseInsensitivePathLookup.cs b/src/SMAPI.Toolkit/Utilities/PathLookups/CaseInsensitiveFileLookup.cs index 9cc00737..496d54c3 100644 --- a/src/SMAPI.Toolkit/Utilities/PathLookups/CaseInsensitivePathLookup.cs +++ b/src/SMAPI.Toolkit/Utilities/PathLookups/CaseInsensitiveFileLookup.cs @@ -4,8 +4,8 @@ using System.IO; namespace StardewModdingAPI.Toolkit.Utilities.PathLookups { - /// <summary>An API for case-insensitive relative path lookups within a root directory.</summary> - internal class CaseInsensitivePathLookup : IFilePathLookup + /// <summary>An API for case-insensitive file lookups within a root directory.</summary> + internal class CaseInsensitiveFileLookup : IFileLookup { /********* ** Fields @@ -16,8 +16,8 @@ namespace StardewModdingAPI.Toolkit.Utilities.PathLookups /// <summary>A case-insensitive lookup of file paths within the <see cref="RootPath"/>. Each path is listed in both file path and asset name format, so it's usable in both contexts without needing to re-parse paths.</summary> private readonly Lazy<Dictionary<string, string>> RelativePathCache; - /// <summary>The case-insensitive path caches by root path.</summary> - private static readonly Dictionary<string, CaseInsensitivePathLookup> CachedRoots = new(StringComparer.OrdinalIgnoreCase); + /// <summary>The case-insensitive file lookups by root path.</summary> + private static readonly Dictionary<string, CaseInsensitiveFileLookup> CachedRoots = new(StringComparer.OrdinalIgnoreCase); /********* @@ -26,22 +26,28 @@ namespace StardewModdingAPI.Toolkit.Utilities.PathLookups /// <summary>Construct an instance.</summary> /// <param name="rootPath">The root directory path for relative paths.</param> /// <param name="searchOption">Which directories to scan from the root.</param> - public CaseInsensitivePathLookup(string rootPath, SearchOption searchOption = SearchOption.AllDirectories) + public CaseInsensitiveFileLookup(string rootPath, SearchOption searchOption = SearchOption.AllDirectories) { - this.RootPath = rootPath; + this.RootPath = PathUtilities.NormalizePath(rootPath); this.RelativePathCache = new(() => this.GetRelativePathCache(searchOption)); } /// <inheritdoc /> - public string GetFilePath(string relativePath) + public FileInfo GetFile(string relativePath) { - return this.GetImpl(PathUtilities.NormalizePath(relativePath)); - } + // invalid path + if (string.IsNullOrWhiteSpace(relativePath)) + throw new InvalidOperationException("Can't get a file from an empty relative path."); - /// <inheritdoc /> - public string GetAssetName(string relativePath) - { - return this.GetImpl(PathUtilities.NormalizeAssetName(relativePath)); + // already cached + if (this.RelativePathCache.Value.TryGetValue(relativePath, out string? resolved)) + return new(Path.Combine(this.RootPath, resolved)); + + // keep capitalization as-is + FileInfo file = new(Path.Combine(this.RootPath, relativePath)); + if (file.Exists) + this.RelativePathCache.Value[relativePath] = relativePath; + return file; } /// <inheritdoc /> @@ -61,17 +67,17 @@ namespace StardewModdingAPI.Toolkit.Utilities.PathLookups throw new InvalidOperationException($"Can't add relative path '{relativePath}' to the case-insensitive cache for '{this.RootPath}' because that file doesn't exist."); // cache path - this.CacheRawPath(this.RelativePathCache.Value, relativePath); + this.RelativePathCache.Value[relativePath] = relativePath; } /// <summary>Get a cached dictionary of relative paths within a root path, for case-insensitive file lookups.</summary> /// <param name="rootPath">The root path to scan.</param> - public static CaseInsensitivePathLookup GetCachedFor(string rootPath) + public static CaseInsensitiveFileLookup GetCachedFor(string rootPath) { rootPath = PathUtilities.NormalizePath(rootPath); - if (!CaseInsensitivePathLookup.CachedRoots.TryGetValue(rootPath, out CaseInsensitivePathLookup? cache)) - CaseInsensitivePathLookup.CachedRoots[rootPath] = cache = new CaseInsensitivePathLookup(rootPath); + if (!CaseInsensitiveFileLookup.CachedRoots.TryGetValue(rootPath, out CaseInsensitiveFileLookup? cache)) + CaseInsensitiveFileLookup.CachedRoots[rootPath] = cache = new CaseInsensitiveFileLookup(rootPath); return cache; } @@ -80,29 +86,6 @@ namespace StardewModdingAPI.Toolkit.Utilities.PathLookups /********* ** Private methods *********/ - /// <summary>Get the exact capitalization for a given relative path.</summary> - /// <param name="relativePath">The relative path. This must already be normalized into asset name or file path format (i.e. using <see cref="PathUtilities.NormalizeAssetName"/> or <see cref="PathUtilities.NormalizePath"/> respectively).</param> - /// <remarks>Returns the resolved path in the same format if found, else returns the path as-is.</remarks> - private string GetImpl(string relativePath) - { - // invalid path - if (string.IsNullOrWhiteSpace(relativePath)) - return relativePath; - - // already cached - if (this.RelativePathCache.Value.TryGetValue(relativePath, out string? resolved)) - return resolved; - - // keep capitalization as-is - if (File.Exists(Path.Combine(this.RootPath, relativePath))) - { - // file exists but isn't cached for some reason - // cache it now so any later references to it are case-insensitive - this.CacheRawPath(this.RelativePathCache.Value, relativePath); - } - return relativePath; - } - /// <summary>Get a case-insensitive lookup of file paths (see <see cref="RelativePathCache"/>).</summary> /// <param name="searchOption">Which directories to scan from the root.</param> private Dictionary<string, string> GetRelativePathCache(SearchOption searchOption) @@ -112,23 +95,10 @@ namespace StardewModdingAPI.Toolkit.Utilities.PathLookups foreach (string path in Directory.EnumerateFiles(this.RootPath, "*", searchOption)) { string relativePath = path.Substring(this.RootPath.Length + 1); - - this.CacheRawPath(cache, relativePath); + cache[relativePath] = relativePath; } return cache; } - - /// <summary>Add a raw relative path to the cache.</summary> - /// <param name="cache">The cache to update.</param> - /// <param name="relativePath">The relative path to cache, with its exact filesystem capitalization.</param> - private void CacheRawPath(IDictionary<string, string> cache, string relativePath) - { - string filePath = PathUtilities.NormalizePath(relativePath); - string assetName = PathUtilities.NormalizeAssetName(relativePath); - - cache[filePath] = filePath; - cache[assetName] = assetName; - } } } diff --git a/src/SMAPI.Toolkit/Utilities/PathLookups/IFileLookup.cs b/src/SMAPI.Toolkit/Utilities/PathLookups/IFileLookup.cs new file mode 100644 index 00000000..d43b5141 --- /dev/null +++ b/src/SMAPI.Toolkit/Utilities/PathLookups/IFileLookup.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace StardewModdingAPI.Toolkit.Utilities.PathLookups +{ + /// <summary>An API for file lookups within a root directory.</summary> + internal interface IFileLookup + { + /// <summary>Get the file for a given relative file path, if it exists.</summary> + /// <param name="relativePath">The relative path.</param> + FileInfo GetFile(string relativePath); + + /// <summary>Add a relative path that was just created by a SMAPI API.</summary> + /// <param name="relativePath">The relative path.</param> + void Add(string relativePath); + } +} diff --git a/src/SMAPI.Toolkit/Utilities/PathLookups/IFilePathLookup.cs b/src/SMAPI.Toolkit/Utilities/PathLookups/IFilePathLookup.cs deleted file mode 100644 index 678e1383..00000000 --- a/src/SMAPI.Toolkit/Utilities/PathLookups/IFilePathLookup.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace StardewModdingAPI.Toolkit.Utilities.PathLookups -{ - /// <summary>An API for relative path lookups within a root directory.</summary> - internal interface IFilePathLookup - { - /// <summary>Get the actual path for a given relative file path.</summary> - /// <param name="relativePath">The relative path.</param> - /// <remarks>Returns the resolved path in file path format, else the normalized <paramref name="relativePath"/>.</remarks> - string GetFilePath(string relativePath); - - /// <summary>Get the actual path for a given asset name.</summary> - /// <param name="relativePath">The relative path.</param> - /// <remarks>Returns the resolved path in asset name format, else the normalized <paramref name="relativePath"/>.</remarks> - string GetAssetName(string relativePath); - - /// <summary>Add a relative path that was just created by a SMAPI API.</summary> - /// <param name="relativePath">The relative path. This must already be normalized in asset name or file path format.</param> - void Add(string relativePath); - } -} diff --git a/src/SMAPI.Toolkit/Utilities/PathLookups/MinimalFileLookup.cs b/src/SMAPI.Toolkit/Utilities/PathLookups/MinimalFileLookup.cs new file mode 100644 index 00000000..414b569b --- /dev/null +++ b/src/SMAPI.Toolkit/Utilities/PathLookups/MinimalFileLookup.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.IO; + +namespace StardewModdingAPI.Toolkit.Utilities.PathLookups +{ + /// <summary>An API for file lookups within a root directory with minimal preprocessing.</summary> + internal class MinimalFileLookup : IFileLookup + { + /********* + ** Accessors + *********/ + /// <summary>The file lookups by root path.</summary> + private static readonly Dictionary<string, MinimalFileLookup> CachedRoots = new(); + + /// <summary>The root directory path for relative paths.</summary> + private readonly string RootPath; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="rootPath">The root directory path for relative paths.</param> + public MinimalFileLookup(string rootPath) + { + this.RootPath = rootPath; + } + + /// <inheritdoc /> + public FileInfo GetFile(string relativePath) + { + return new( + Path.Combine(this.RootPath, PathUtilities.NormalizePath(relativePath)) + ); + } + + /// <inheritdoc /> + public void Add(string relativePath) { } + + /// <summary>Get a cached dictionary of relative paths within a root path, for case-insensitive file lookups.</summary> + /// <param name="rootPath">The root path to scan.</param> + public static MinimalFileLookup GetCachedFor(string rootPath) + { + rootPath = PathUtilities.NormalizePath(rootPath); + + if (!MinimalFileLookup.CachedRoots.TryGetValue(rootPath, out MinimalFileLookup? lookup)) + MinimalFileLookup.CachedRoots[rootPath] = lookup = new MinimalFileLookup(rootPath); + + return lookup; + } + } +} diff --git a/src/SMAPI.Toolkit/Utilities/PathLookups/MinimalPathLookup.cs b/src/SMAPI.Toolkit/Utilities/PathLookups/MinimalPathLookup.cs deleted file mode 100644 index 2cf14704..00000000 --- a/src/SMAPI.Toolkit/Utilities/PathLookups/MinimalPathLookup.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace StardewModdingAPI.Toolkit.Utilities.PathLookups -{ - /// <summary>An API for relative path lookups within a root directory with minimal preprocessing.</summary> - internal class MinimalPathLookup : IFilePathLookup - { - /********* - ** Accessors - *********/ - /// <summary>A singleton instance for reuse.</summary> - public static readonly MinimalPathLookup Instance = new(); - - - /********* - ** Public methods - *********/ - /// <inheritdoc /> - public string GetFilePath(string relativePath) - { - return PathUtilities.NormalizePath(relativePath); - } - - /// <inheritdoc /> - public string GetAssetName(string relativePath) - { - return PathUtilities.NormalizeAssetName(relativePath); - } - - /// <inheritdoc /> - public void Add(string relativePath) { } - } -} diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index b1a9cc82..a289ce4b 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -50,7 +50,7 @@ namespace StardewModdingAPI internal static int? LogScreenId { get; set; } /// <summary>SMAPI's current raw semantic version.</summary> - internal static string RawApiVersion = "3.14.1"; + internal static string RawApiVersion = "3.14.2"; } /// <summary>Contains SMAPI's constants and assumptions.</summary> diff --git a/src/SMAPI/Events/AssetRequestedEventArgs.cs b/src/SMAPI/Events/AssetRequestedEventArgs.cs index 3bcf83b9..d0aef1db 100644 --- a/src/SMAPI/Events/AssetRequestedEventArgs.cs +++ b/src/SMAPI/Events/AssetRequestedEventArgs.cs @@ -19,19 +19,22 @@ namespace StardewModdingAPI.Events /// <summary>Get the mod metadata for a content pack, if it's a valid content pack for the mod.</summary> private readonly Func<IModMetadata, string?, string, IModMetadata?> GetOnBehalfOf; + /// <summary>The asset info being requested.</summary> + private readonly IAssetInfo AssetInfo; + /********* ** Accessors *********/ /// <summary>The name of the asset being requested.</summary> - public IAssetName Name { get; } + public IAssetName Name => this.AssetInfo.Name; /// <summary>The <see cref="Name"/> with any locale codes stripped.</summary> /// <remarks>For example, if <see cref="Name"/> contains a locale like <c>Data/Bundles.fr-FR</c>, this will be the name without locale like <c>Data/Bundles</c>. If the name has no locale, this field is equivalent.</remarks> - public IAssetName NameWithoutLocale { get; } + public IAssetName NameWithoutLocale => this.AssetInfo.NameWithoutLocale; /// <summary>The requested data type.</summary> - public Type DataType { get; } + public Type DataType => this.AssetInfo.DataType; /// <summary>The load operations requested by the event handler.</summary> internal IList<AssetLoadOperation> LoadOperations { get; } = new List<AssetLoadOperation>(); @@ -45,16 +48,12 @@ namespace StardewModdingAPI.Events *********/ /// <summary>Construct an instance.</summary> /// <param name="mod">The mod handling the event.</param> - /// <param name="name">The name of the asset being requested.</param> - /// <param name="dataType">The requested data type.</param> - /// <param name="nameWithoutLocale">The <paramref name="name"/> with any locale codes stripped.</param> + /// <param name="assetInfo">The asset info being requested.</param> /// <param name="getOnBehalfOf">Get the mod metadata for a content pack, if it's a valid content pack for the mod.</param> - internal AssetRequestedEventArgs(IModMetadata mod, IAssetName name, IAssetName nameWithoutLocale, Type dataType, Func<IModMetadata, string?, string, IModMetadata?> getOnBehalfOf) + internal AssetRequestedEventArgs(IModMetadata mod, IAssetInfo assetInfo, Func<IModMetadata, string?, string, IModMetadata?> getOnBehalfOf) { this.Mod = mod; - this.Name = name; - this.NameWithoutLocale = nameWithoutLocale; - this.DataType = dataType; + this.AssetInfo = assetInfo; this.GetOnBehalfOf = getOnBehalfOf; } @@ -73,10 +72,10 @@ namespace StardewModdingAPI.Events { this.LoadOperations.Add( new AssetLoadOperation( - mod: this.Mod, - priority: priority, - onBehalfOf: this.GetOnBehalfOf(this.Mod, onBehalfOf, "load assets"), - getData: _ => load() + Mod: this.Mod, + OnBehalfOf: this.GetOnBehalfOf(this.Mod, onBehalfOf, "load assets"), + Priority: priority, + GetData: _ => load() ) ); } @@ -97,10 +96,10 @@ namespace StardewModdingAPI.Events { this.LoadOperations.Add( new AssetLoadOperation( - mod: this.Mod, - priority: priority, - onBehalfOf: null, - _ => this.Mod.Mod!.Helper.ModContent.Load<TAsset>(relativePath) + Mod: this.Mod, + OnBehalfOf: null, + Priority: priority, + GetData: _ => this.Mod.Mod!.Helper.ModContent.Load<TAsset>(relativePath) |
