summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs5
-rw-r--r--src/SMAPI/Framework/ContentManagers/BaseContentManager.cs11
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs29
-rw-r--r--src/SMAPI/Framework/ContentManagers/IContentManager.cs3
-rw-r--r--src/SMAPI/Metadata/CoreAssetPropagator.cs98
5 files changed, 66 insertions, 80 deletions
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index a4d29068..ee8b6893 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -230,12 +230,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Perform any updates needed when the locale changes.</summary>
public void OnLocaleChanged()
{
- // reload affected content
+ // reset baseline cache
this.ContentManagerLock.InReadLock(() =>
{
- foreach (IContentManager contentManager in this.ContentManagers)
- contentManager.OnLocaleChanged();
-
this.VanillaContentManager.Unload();
});
}
diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
index b1ace259..1ca84792 100644
--- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
@@ -137,16 +137,18 @@ namespace StardewModdingAPI.Framework.ContentManagers
try
{
- this.LoadExact<T>(localizedName, useCache: useCache);
+ T data = this.LoadExact<T>(localizedName, useCache: useCache);
LocalizedContentManager.localizedAssetNames[assetName.Name] = localizedName.Name;
+ return data;
}
catch (ContentLoadException)
{
localizedName = new AssetName(assetName.BaseName + "_international", null, null);
try
{
- this.LoadExact<T>(localizedName, useCache: useCache);
+ T data = this.LoadExact<T>(localizedName, useCache: useCache);
LocalizedContentManager.localizedAssetNames[assetName.Name] = localizedName.Name;
+ return data;
}
catch (ContentLoadException)
{
@@ -158,7 +160,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
// use cached key
string rawName = LocalizedContentManager.localizedAssetNames[assetName.Name];
if (assetName.Name != rawName)
- assetName = this.Coordinator.ParseAssetName(assetName.Name);
+ assetName = this.Coordinator.ParseAssetName(rawName);
return this.LoadExact<T>(assetName, useCache: useCache);
}
@@ -166,9 +168,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
public abstract T LoadExact<T>(IAssetName assetName, bool useCache);
/// <inheritdoc />
- public virtual void OnLocaleChanged() { }
-
- /// <inheritdoc />
[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local", Justification = "Parameter is only used for assertion checks by design.")]
public string AssertAndNormalizeAssetName(string assetName)
{
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 500c0191..0fcad30a 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -25,9 +25,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>The assets currently being intercepted by <see cref="IAssetLoader"/> instances. This is used to prevent infinite loops when a loader loads a new asset.</summary>
private readonly ContextHash<string> AssetsBeingLoaded = new();
- /// <summary>Maps asset names to their localized form, like <c>LooseSprites\Billboard => LooseSprites\Billboard.fr-FR</c> (localized) or <c>Maps\AnimalShop => Maps\AnimalShop</c> (not localized).</summary>
- private IDictionary<string, string> LocalizedAssetNames => LocalizedContentManager.localizedAssetNames;
-
/// <summary>Whether the next load is the first for any game content manager.</summary>
private static bool IsFirstLoad = true;
@@ -140,32 +137,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
}
/// <inheritdoc />
- public override void OnLocaleChanged()
- {
- base.OnLocaleChanged();
-
- // find assets for which a translatable version was loaded
- HashSet<string> removeAssetNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
- foreach (string key in this.LocalizedAssetNames.Where(p => p.Key != p.Value).Select(p => p.Key))
- {
- IAssetName assetName = this.Coordinator.ParseAssetName(key);
- removeAssetNames.Add(assetName.BaseName);
- }
-
- // invalidate translatable assets
- string[] invalidated = this
- .InvalidateCache((key, _) =>
- removeAssetNames.Contains(key)
- || removeAssetNames.Contains(this.Coordinator.ParseAssetName(key).BaseName)
- )
- .Select(p => p.Key)
- .OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
- .ToArray();
- if (invalidated.Any())
- this.Monitor.Log($"Invalidated {invalidated.Length} asset names: {string.Join(", ", invalidated)} for locale change.");
- }
-
- /// <inheritdoc />
public override LocalizedContentManager CreateTemporary()
{
return this.Coordinator.CreateGameContentManager("(temporary)");
diff --git a/src/SMAPI/Framework/ContentManagers/IContentManager.cs b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
index 4de9a8c3..774b20d9 100644
--- a/src/SMAPI/Framework/ContentManagers/IContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
@@ -65,8 +65,5 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="dispose">Whether to dispose invalidated assets. This should only be <c>true</c> when they're being invalidated as part of a dispose, to avoid crashing the game.</param>
/// <returns>Returns the invalidated asset names and instances.</returns>
IDictionary<string, object> InvalidateCache(Func<string, Type, bool> predicate, bool dispose = false);
-
- /// <summary>Perform any cleanup needed when the locale changes.</summary>
- void OnLocaleChanged();
}
}
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index f645470e..832148aa 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -89,6 +89,14 @@ namespace StardewModdingAPI.Metadata
/// <param name="updatedNpcWarps">Whether the NPC pathfinding cache was reloaded.</param>
public void Propagate(IDictionary<IAssetName, Type> assets, bool ignoreWorld, out IDictionary<IAssetName, bool> propagatedAssets, out bool updatedNpcWarps)
{
+ // get base name lookup
+ propagatedAssets = assets
+ .Select(asset => asset.Key.GetBaseAssetName())
+ .Distinct()
+ .ToDictionary(name => name, _ => false);
+
+ this.Monitor.Log($"Propagating: {propagatedAssets.Keys.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase)}", LogLevel.Alert);
+
// group into optimized lists
var buckets = assets.GroupBy(p =>
{
@@ -102,7 +110,6 @@ namespace StardewModdingAPI.Metadata
});
// reload assets
- propagatedAssets = assets.ToDictionary(p => p.Key, _ => false);
updatedNpcWarps = false;
foreach (var bucket in buckets)
{
@@ -110,12 +117,12 @@ namespace StardewModdingAPI.Metadata
{
case AssetBucket.Sprite:
if (!ignoreWorld)
- this.ReloadNpcSprites(bucket.Select(p => p.Key), propagatedAssets);
+ this.ReloadNpcSprites(propagatedAssets);
break;
case AssetBucket.Portrait:
if (!ignoreWorld)
- this.ReloadNpcPortraits(bucket.Select(p => p.Key), propagatedAssets);
+ this.ReloadNpcPortraits(propagatedAssets);
break;
default:
@@ -158,7 +165,7 @@ namespace StardewModdingAPI.Metadata
private bool PropagateOther(IAssetName assetName, Type type, bool ignoreWorld, out bool changedWarps)
{
var content = this.MainContentManager;
- string key = assetName.Name;
+ string key = assetName.BaseName;
changedWarps = false;
/****
@@ -170,7 +177,7 @@ namespace StardewModdingAPI.Metadata
{
foreach (TileSheet tilesheet in Game1.currentLocation.map.TileSheets)
{
- if (assetName.IsEquivalentTo(tilesheet.ImageSource))
+ if (this.IsSameBaseName(assetName, tilesheet.ImageSource))
Game1.mapDisplayDevice.LoadTileSheet(tilesheet);
}
}
@@ -188,7 +195,7 @@ namespace StardewModdingAPI.Metadata
{
GameLocation location = info.Location;
- if (assetName.IsEquivalentTo(location.mapPath.Value))
+ if (this.IsSameBaseName(assetName, location.mapPath.Value))
{
static ISet<string> GetWarpSet(GameLocation location)
{
@@ -213,7 +220,7 @@ namespace StardewModdingAPI.Metadata
/****
** Propagate by key
****/
- switch (assetName.Name.ToLower().Replace("\\", "/")) // normalized key so we can compare statically
+ switch (assetName.BaseName.ToLower().Replace("\\", "/")) // normalized key so we can compare statically
{
/****
** Animals
@@ -624,7 +631,7 @@ namespace StardewModdingAPI.Metadata
{
if (Game1.activeClickableMenu is TitleMenu titleMenu)
{
- Texture2D texture = content.Load<Texture2D>(assetName.Name);
+ Texture2D texture = content.Load<Texture2D>(assetName.BaseName);
titleMenu.titleButtonsTexture = texture;
titleMenu.backButton.texture = texture;
@@ -652,13 +659,13 @@ namespace StardewModdingAPI.Metadata
// find matches
TAnimal[] animals = this.GetCharacters()
.OfType<TAnimal>()
- .Where(p => assetName.IsEquivalentTo(p.Sprite?.Texture?.Name))
+ .Where(p => this.IsSameBaseName(assetName, p.Sprite?.Texture?.Name))
.ToArray();
if (!animals.Any())
return false;
// update sprites
- Texture2D texture = content.Load<Texture2D>(assetName.Name);
+ Texture2D texture = content.Load<Texture2D>(assetName.BaseName);
foreach (TAnimal animal in animals)
animal.Sprite.spriteTexture = texture;
return true;
@@ -677,7 +684,7 @@ namespace StardewModdingAPI.Metadata
return false;
// update sprites
- Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.Name));
+ Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.BaseName));
foreach (FarmAnimal animal in animals)
{
// get expected key
@@ -689,7 +696,7 @@ namespace StardewModdingAPI.Metadata
expectedKey = $"Animals/{expectedKey}";
// reload asset
- if (assetName.IsEquivalentTo(expectedKey))
+ if (this.IsSameBaseName(assetName, expectedKey))
animal.Sprite.spriteTexture = texture.Value;
}
return texture.IsValueCreated;
@@ -705,7 +712,7 @@ namespace StardewModdingAPI.Metadata
bool isPaintMask = assetName.BaseName.EndsWith(paintMaskSuffix, StringComparison.OrdinalIgnoreCase);
// get building type
- string type = Path.GetFileName(assetName.Name)!;
+ string type = Path.GetFileName(assetName.BaseName)!;
if (isPaintMask)
type = type.Substring(0, type.Length - paintMaskSuffix.Length);
@@ -738,7 +745,7 @@ namespace StardewModdingAPI.Metadata
/// <returns>Returns whether any textures were reloaded.</returns>
private bool ReloadChairTiles(LocalizedContentManager content, IAssetName assetName, bool ignoreWorld)
{
- MapSeat.mapChairTexture = content.Load<Texture2D>(assetName.Name);
+ MapSeat.mapChairTexture = content.Load<Texture2D>(assetName.BaseName);
if (!ignoreWorld)
{
@@ -746,7 +753,7 @@ namespace StardewModdingAPI.Metadata
{
foreach (MapSeat seat in location.mapSeats.Where(p => p != null))
{
- if (assetName.IsEquivalentTo(seat._loadedTextureFile))
+ if (this.IsSameBaseName(assetName, seat._loadedTextureFile))
seat.overlayTexture = MapSeat.mapChairTexture;
}
}
@@ -767,7 +774,7 @@ namespace StardewModdingAPI.Metadata
from location in this.GetLocations()
where location.critters != null
from Critter critter in location.critters
- where assetName.IsEquivalentTo(critter.sprite?.Texture?.Name)
+ where this.IsSameBaseName(assetName, critter.sprite?.Texture?.Name)
select critter
)
.ToArray();
@@ -775,7 +782,7 @@ namespace StardewModdingAPI.Metadata
return 0;
// update sprites
- Texture2D texture = content.Load<Texture2D>(assetName.Name);
+ Texture2D texture = content.Load<Texture2D>(assetName.BaseName);
foreach (var entry in critters)
entry.sprite.spriteTexture = texture;
@@ -788,7 +795,7 @@ namespace StardewModdingAPI.Metadata
/// <returns>Returns whether any doors were affected.</returns>
private bool ReloadDoorSprites(LocalizedContentManager content, IAssetName assetName)
{
- Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.Name));
+ Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.BaseName));
foreach (GameLocation location in this.GetLocations())
{
@@ -802,7 +809,7 @@ namespace StardewModdingAPI.Metadata
continue;
string curKey = this.Reflection.GetField<string>(door.Sprite, "textureName").GetValue();
- if (assetName.IsEquivalentTo(curKey))
+ if (this.IsSameBaseName(assetName, curKey))
door.Sprite.texture = texture.Value;
}
}
@@ -862,14 +869,14 @@ namespace StardewModdingAPI.Metadata
(
from location in this.GetLocations()
from grass in location.terrainFeatures.Values.OfType<Grass>()
- where assetName.IsEquivalentTo(grass.textureName())
+ where this.IsSameBaseName(assetName, grass.textureName())
select grass
)
.ToArray();
if (grasses.Any())
{
- Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.Name));
+ Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.BaseName));
foreach (Grass grass in grasses)
grass.texture = texture;
return true;
@@ -935,7 +942,7 @@ namespace StardewModdingAPI.Metadata
/// <returns>Returns whether any NPCs were affected.</returns>
private bool ReloadNpcDispositions(LocalizedContentManager content, IAssetName assetName)
{
- IDictionary<string, string> data = content.Load<Dictionary<string, string>>(assetName.Name);
+ IDictionary<string, string> data = content.Load<Dictionary<string, string>>(assetName.BaseName);
bool changed = false;
foreach (NPC npc in this.GetCharacters())
{
@@ -950,15 +957,14 @@ namespace StardewModdingAPI.Metadata
}
/// <summary>Reload the sprites for matching NPCs.</summary>
- /// <param name="keys">The asset keys to reload.</param>
- /// <param name="propagated">The asset keys which have been propagated.</param>
- private void ReloadNpcSprites(IEnumerable<IAssetName> keys, IDictionary<IAssetName, bool> propagated)
+ /// <param name="propagated">The asset keys which are being propagated.</param>
+ private void ReloadNpcSprites(IDictionary<IAssetName, bool> propagated)
{
// get NPCs
var characters =
(
from npc in this.GetCharacters()
- let key = this.ParseAssetNameOrNull(npc.Sprite?.Texture?.Name)
+ let key = this.ParseAssetNameOrNull(npc.Sprite?.Texture?.Name)?.GetBaseAssetName()
where key != null && propagated.ContainsKey(key)
select new { Npc = npc, AssetName = key }
)
@@ -969,15 +975,14 @@ namespace StardewModdingAPI.Metadata
// update sprite
foreach (var target in characters)
{
- target.Npc.Sprite.spriteTexture = this.LoadAndDisposeIfNeeded(target.Npc.Sprite.spriteTexture, target.AssetName.Name);
+ target.Npc.Sprite.spriteTexture = this.LoadAndDisposeIfNeeded(target.Npc.Sprite.spriteTexture, target.AssetName.BaseName);
propagated[target.AssetName] = true;
}
}
/// <summary>Reload the portraits for matching NPCs.</summary>
- /// <param name="keys">The asset key to reload.</param>
- /// <param name="propagated">The asset keys which have been propagated.</param>
- private void ReloadNpcPortraits(IEnumerable<IAssetName> keys, IDictionary<IAssetName, bool> propagated)
+ /// <param name="propagated">The asset keys which are being propagated.</param>
+ private void ReloadNpcPortraits(IDictionary<IAssetName, bool> propagated)
{
// get NPCs
var characters =
@@ -985,7 +990,7 @@ namespace StardewModdingAPI.Metadata
from npc in this.GetCharacters()
where npc.isVillager()
- let key = this.ParseAssetNameOrNull(npc.Portrait?.Name)
+ let key = this.ParseAssetNameOrNull(npc.Portrait?.Name)?.GetBaseAssetName()
where key != null && propagated.ContainsKey(key)
select new { Npc = npc, AssetName = key }
)
@@ -1005,7 +1010,7 @@ namespace StardewModdingAPI.Metadata
// update portrait
foreach (var target in characters)
{
- target.Npc.Portrait = this.LoadAndDisposeIfNeeded(target.Npc.Portrait, target.AssetName.Name);
+ target.Npc.Portrait = this.LoadAndDisposeIfNeeded(target.Npc.Portrait, target.AssetName.BaseName);
propagated[target.AssetName] = true;
}
}
@@ -1017,7 +1022,7 @@ namespace StardewModdingAPI.Metadata
Farmer[] players =
(
from player in Game1.getOnlineFarmers()
- where assetName.IsEquivalentTo(player.getTexture())
+ where this.IsSameBaseName(assetName, player.getTexture())
select player
)
.ToArray();
@@ -1037,7 +1042,7 @@ namespace StardewModdingAPI.Metadata
/// <returns>Returns whether any textures were reloaded.</returns>
private bool ReloadSuspensionBridges(LocalizedContentManager content, IAssetName assetName)
{
- Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.Name));
+ Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.BaseName));
foreach (GameLocation location in this.GetLocations(buildingInteriors: false))
{
@@ -1068,7 +1073,7 @@ namespace StardewModdingAPI.Metadata
if (trees.Any())
{
- Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.Name));
+ Lazy<Texture2D> texture = new Lazy<Texture2D>(() => content.Load<Texture2D>(assetName.BaseName));
foreach (Tree tree in trees)
tree.texture = texture;
return true;
@@ -1086,7 +1091,7 @@ namespace StardewModdingAPI.Metadata
private bool ReloadNpcDialogue(IAssetName assetName)
{
// get NPCs
- string name = Path.GetFileName(assetName.Name);
+ string name = Path.GetFileName(assetName.BaseName);
NPC[] villagers = this.GetCharacters().Where(npc => npc.Name == name && npc.isVillager()).ToArray();
if (!villagers.Any())
return false;
@@ -1116,7 +1121,7 @@ namespace StardewModdingAPI.Metadata
private bool ReloadNpcSchedules(IAssetName assetName)
{
// get NPCs
- string name = Path.GetFileName(assetName.Name);
+ string name = Path.GetFileName(assetName.BaseName);
NPC[] villagers = this.GetCharacters().Where(npc => npc.Name == name && npc.isVillager()).ToArray();
if (!villagers.Any())
return false;
@@ -1230,6 +1235,23 @@ namespace StardewModdingAPI.Metadata
}
}
+ /// <summary>Get whether two asset names are equivalent if you ignore the locale code.</summary>
+ /// <param name="left">The first value to compare.</param>
+ /// <param name="right">The second value to compare.</param>
+ private bool IsSameBaseName(IAssetName left, string right)
+ {
+ IAssetName parsedB = this.ParseAssetNameOrNull(right);
+ return this.IsSameBaseName(left, parsedB);
+ }
+
+ /// <summary>Get whether two asset names are equivalent if you ignore the locale code.</summary>
+ /// <param name="left">The first value to compare.</param>
+ /// <param name="right">The second value to compare.</param>
+ private bool IsSameBaseName(IAssetName left, IAssetName right)
+ {
+ return left.IsEquivalentTo(right.BaseName, useBaseName: true);
+ }
+
/// <summary>Normalize an asset key to match the cache key and assert that it's valid, but don't raise an error for null or empty values.</summary>
/// <param name="path">The asset key to normalize.</param>
private IAssetName ParseAssetNameOrNull(string path)
@@ -1281,7 +1303,7 @@ namespace StardewModdingAPI.Metadata
BuildingPainter.paintMaskLookup = new Dictionary<string, List<List<int>>>(BuildingPainter.paintMaskLookup, StringComparer.OrdinalIgnoreCase);
// remove key from cache
- return BuildingPainter.paintMaskLookup.Remove(assetName.Name);
+ return BuildingPainter.paintMaskLookup.Remove(assetName.BaseName);
}
/// <summary>Metadata about a location used in asset propagation.</summary>