diff --git a/src/SMAPI/Events/AssetReadyEventArgs.cs b/src/SMAPI/Events/AssetReadyEventArgs.cs index 946c9173..2c308f18 100644 --- a/src/SMAPI/Events/AssetReadyEventArgs.cs +++ b/src/SMAPI/Events/AssetReadyEventArgs.cs @@ -11,15 +11,21 @@ namespace StardewModdingAPI.Events /// The name of the asset being requested. public IAssetName Name { get; } + /// The with any locale codes stripped. + /// For example, if contains a locale like Data/Bundles.fr-FR, this will be the name without locale like Data/Bundles. If the name has no locale, this field is equivalent. + public IAssetName NameWithoutLocale { get; } + /********* ** Public methods *********/ /// Construct an instance. /// The name of the asset being requested. - internal AssetReadyEventArgs(IAssetName name) + /// The with any locale codes stripped. + internal AssetReadyEventArgs(IAssetName name, IAssetName nameWithoutLocale) { this.Name = name; + this.NameWithoutLocale = nameWithoutLocale; } } } diff --git a/src/SMAPI/Events/AssetRequestedEventArgs.cs b/src/SMAPI/Events/AssetRequestedEventArgs.cs index d022a4de..9e2cde7f 100644 --- a/src/SMAPI/Events/AssetRequestedEventArgs.cs +++ b/src/SMAPI/Events/AssetRequestedEventArgs.cs @@ -26,6 +26,10 @@ namespace StardewModdingAPI.Events /// The name of the asset being requested. public IAssetName Name { get; } + /// The with any locale codes stripped. + /// For example, if contains a locale like Data/Bundles.fr-FR, this will be the name without locale like Data/Bundles. If the name has no locale, this field is equivalent. + public IAssetName NameWithoutLocale { get; } + /// The load operations requested by the event handler. internal IList LoadOperations { get; } = new List(); @@ -39,11 +43,13 @@ namespace StardewModdingAPI.Events /// Construct an instance. /// The mod handling the event. /// The name of the asset being requested. + /// The with any locale codes stripped. /// Get the mod metadata for a content pack, if it's a valid content pack for the mod. - internal AssetRequestedEventArgs(IModMetadata mod, IAssetName name, Func getOnBehalfOf) + internal AssetRequestedEventArgs(IModMetadata mod, IAssetName name, IAssetName nameWithoutLocale, Func getOnBehalfOf) { this.Mod = mod; this.Name = name; + this.NameWithoutLocale = nameWithoutLocale; this.GetOnBehalfOf = getOnBehalfOf; } diff --git a/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs index f3d83dd6..614cdf49 100644 --- a/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs +++ b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; namespace StardewModdingAPI.Events { @@ -14,15 +13,21 @@ namespace StardewModdingAPI.Events /// The asset names that were invalidated. public IReadOnlySet Names { get; } + /// The with any locale codes stripped. + /// For example, if contains a locale like Data/Bundles.fr-FR, this will have the name without locale like Data/Bundles. If the name has no locale, this field is equivalent. + public IReadOnlySet NamesWithoutLocale { get; } + /********* ** Public methods *********/ /// Construct an instance. /// The asset names that were invalidated. - internal AssetsInvalidatedEventArgs(IEnumerable names) + /// The with any locale codes stripped. + internal AssetsInvalidatedEventArgs(IEnumerable names, IEnumerable namesWithoutLocale) { this.Names = names.ToImmutableHashSet(); + this.NamesWithoutLocale = namesWithoutLocale.ToImmutableHashSet(); } } } diff --git a/src/SMAPI/Framework/Content/AssetInfo.cs b/src/SMAPI/Framework/Content/AssetInfo.cs index 6a5b4f31..556f1c2a 100644 --- a/src/SMAPI/Framework/Content/AssetInfo.cs +++ b/src/SMAPI/Framework/Content/AssetInfo.cs @@ -23,8 +23,11 @@ namespace StardewModdingAPI.Framework.Content public IAssetName Name { get; } /// - [Obsolete($"Use {nameof(Name)} instead.")] - public string AssetName => this.Name.Name; + public IAssetName NameWithoutLocale { get; } + + /// + [Obsolete($"Use {nameof(Name)} or {nameof(NameWithoutLocale)} instead.")] + public string AssetName => this.NameWithoutLocale.Name; /// public Type DataType { get; } @@ -42,15 +45,16 @@ namespace StardewModdingAPI.Framework.Content { this.Locale = locale; this.Name = assetName; + this.NameWithoutLocale = assetName.GetBaseAssetName(); this.DataType = type; this.GetNormalizedPath = getNormalizedPath; } /// - [Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} instead.")] + [Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} or {nameof(NameWithoutLocale)}.{nameof(IAssetName.IsEquivalentTo)} instead.")] public bool AssetNameEquals(string path) { - return this.Name.IsEquivalentTo(path); + return this.NameWithoutLocale.IsEquivalentTo(path); } diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs index 992647f8..7ce0f8ee 100644 --- a/src/SMAPI/Framework/Content/AssetName.cs +++ b/src/SMAPI/Framework/Content/AssetName.cs @@ -142,11 +142,20 @@ namespace StardewModdingAPI.Framework.Content } + /// public bool IsDirectlyUnderPath(string assetFolder) { return this.StartsWith(assetFolder + "/", allowPartialWord: false, allowSubfolder: false); } + /// + IAssetName IAssetName.GetBaseAssetName() + { + return this.LocaleCode == null + ? this + : new AssetName(this.BaseName, null, null); + } + /// public bool Equals(IAssetName other) { diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 144832b2..fc137e50 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -53,7 +53,7 @@ namespace StardewModdingAPI.Framework private readonly Action OnAssetLoaded; /// A callback to invoke when any asset names have been invalidated from the cache. - private readonly Action> OnAssetsInvalidated; + private readonly Action> OnAssetsInvalidated; /// Get the load/edit operations to apply to an asset by querying registered event handlers. private readonly Func> RequestAssetOperations; @@ -118,7 +118,7 @@ namespace StardewModdingAPI.Framework /// Whether to enable more aggressive memory optimizations. /// A callback to invoke when any asset names have been invalidated from the cache. /// Get the load/edit operations to apply to an asset by querying registered event handlers. - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations, Action> onAssetsInvalidated, Func> requestAssetOperations) + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations, Action> onAssetsInvalidated, Func> requestAssetOperations) { this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); @@ -414,7 +414,7 @@ namespace StardewModdingAPI.Framework this.AssetOperationsByKey.Remove(name); // raise event - this.OnAssetsInvalidated(invalidatedAssets.Keys); + this.OnAssetsInvalidated(invalidatedAssets.Keys.ToArray()); // propagate changes to the game this.CoreAssets.Propagate( diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index dd682e40..dd952dee 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1113,15 +1113,15 @@ namespace StardewModdingAPI.Framework private void OnAssetLoaded(IContentManager contentManager, IAssetName assetName) { if (this.EventManager.AssetReady.HasListeners()) - this.EventManager.AssetReady.Raise(new AssetReadyEventArgs(assetName)); + this.EventManager.AssetReady.Raise(new AssetReadyEventArgs(assetName, assetName.GetBaseAssetName())); } /// A callback invoked after assets have been invalidated from the content cache. /// The invalidated asset names. - private void OnAssetsInvalidated(IEnumerable assetNames) + private void OnAssetsInvalidated(IList assetNames) { if (this.EventManager.AssetsInvalidated.HasListeners()) - this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames)); + this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames, assetNames.Select(p => p.GetBaseAssetName()))); } /// Get the load/edit operations to apply to an asset by querying registered event handlers. @@ -1133,7 +1133,7 @@ namespace StardewModdingAPI.Framework this.EventManager.AssetRequested.Raise( invoke: (mod, invoke) => { - AssetRequestedEventArgs args = new(mod, asset.Name, this.GetOnBehalfOfContentPack); + AssetRequestedEventArgs args = new(mod, asset.Name, asset.NameWithoutLocale, this.GetOnBehalfOfContentPack); invoke(args); diff --git a/src/SMAPI/IAssetInfo.cs b/src/SMAPI/IAssetInfo.cs index 6ac8358d..c7b2ab62 100644 --- a/src/SMAPI/IAssetInfo.cs +++ b/src/SMAPI/IAssetInfo.cs @@ -14,8 +14,12 @@ namespace StardewModdingAPI /// The asset name being read. public IAssetName Name { get; } + /// The with any locale codes stripped. + /// For example, if contains a locale like Data/Bundles.fr-FR, this will be the name without locale like Data/Bundles. If the name has no locale, this field is equivalent. + public IAssetName NameWithoutLocale { get; } + /// The normalized asset name being read. The format may change between platforms; see to compare with a known path. - [Obsolete($"Use {nameof(Name)} instead.")] + [Obsolete($"Use {nameof(Name)} or {nameof(NameWithoutLocale)} instead.")] string AssetName { get; } /// The content data type. @@ -27,7 +31,7 @@ namespace StardewModdingAPI *********/ /// Get whether the asset name being loaded matches a given name after normalization. /// The expected asset path, relative to the game's content folder and without the .xnb extension or locale suffix (like 'Data\ObjectInformation'). - [Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} instead.")] + [Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} or {nameof(NameWithoutLocale)}.{nameof(IAssetName.IsEquivalentTo)} instead.")] bool AssetNameEquals(string path); } } diff --git a/src/SMAPI/IAssetName.cs b/src/SMAPI/IAssetName.cs index a5bfea93..89f02adf 100644 --- a/src/SMAPI/IAssetName.cs +++ b/src/SMAPI/IAssetName.cs @@ -40,5 +40,8 @@ namespace StardewModdingAPI /// For example, Characters/Dialogue/Abigail is directly under Characters/Dialogue but not Characters or Characters/Dialogue/Ab. To allow sub-paths, use instead. /// The asset path to check. This doesn't need a trailing slash. bool IsDirectlyUnderPath(string assetFolder); + + /// Get an asset name representing the without locale. + internal IAssetName GetBaseAssetName(); } }