From 3a161a30a7faa2d69ebe08938cd68f43921b4a81 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 18 May 2022 19:23:26 -0400 Subject: update for the new CurseForge API --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'docs/release-notes.md') diff --git a/docs/release-notes.md b/docs/release-notes.md index c29bc39c..1f394849 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,10 @@ ← [README](README.md) # Release notes +## Upcoming release +* For players: + * Fixed CurseForge update checks for the new CurseForge API. + ## 3.14.4 Released 15 May 2022 for Stardew Valley 1.5.6 or later. -- cgit From 7332879351f396572abd1785abf5d7807ef97ca4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 19 May 2022 21:04:32 -0400 Subject: defer asset reload during propagation when possible --- docs/release-notes.md | 1 + src/SMAPI/Metadata/CoreAssetPropagator.cs | 128 ++++++++++++++++-------------- 2 files changed, 70 insertions(+), 59 deletions(-) (limited to 'docs/release-notes.md') diff --git a/docs/release-notes.md b/docs/release-notes.md index 1f394849..8dc53e19 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,6 +3,7 @@ # Release notes ## Upcoming release * For players: + * Improved performance when mods change some asset types (including NPC portraits/sprites). * Fixed CurseForge update checks for the new CurseForge API. ## 3.14.4 diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs index dabd802d..ce9ba7a8 100644 --- a/src/SMAPI/Metadata/CoreAssetPropagator.cs +++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs @@ -219,7 +219,7 @@ namespace StardewModdingAPI.Metadata ** Animals ****/ case "animals/horse": - return !ignoreWorld && this.UpdatePetOrHorseSprites(content, assetName); + return !ignoreWorld && this.UpdatePetOrHorseSprites(assetName); /**** ** Buildings @@ -486,7 +486,7 @@ namespace StardewModdingAPI.Metadata return true; case "tilesheets/critters": // Critter constructor - return !ignoreWorld && this.UpdateCritterTextures(content, assetName); + return !ignoreWorld && this.UpdateCritterTextures(assetName); case "tilesheets/crops": // Game1.LoadContent Game1.cropSpriteSheet = content.Load(key); @@ -555,27 +555,27 @@ namespace StardewModdingAPI.Metadata return true; case "terrainfeatures/mushroom_tree": // from Tree - return !ignoreWorld && this.UpdateTreeTextures(content, assetName, Tree.mushroomTree); + return !ignoreWorld && this.UpdateTreeTextures(Tree.mushroomTree); case "terrainfeatures/tree_palm": // from Tree - return !ignoreWorld && this.UpdateTreeTextures(content, assetName, Tree.palmTree); + return !ignoreWorld && this.UpdateTreeTextures(Tree.palmTree); case "terrainfeatures/tree1_fall": // from Tree case "terrainfeatures/tree1_spring": // from Tree case "terrainfeatures/tree1_summer": // from Tree case "terrainfeatures/tree1_winter": // from Tree - return !ignoreWorld && this.UpdateTreeTextures(content, assetName, Tree.bushyTree); + return !ignoreWorld && this.UpdateTreeTextures(Tree.bushyTree); case "terrainfeatures/tree2_fall": // from Tree case "terrainfeatures/tree2_spring": // from Tree case "terrainfeatures/tree2_summer": // from Tree case "terrainfeatures/tree2_winter": // from Tree - return !ignoreWorld && this.UpdateTreeTextures(content, assetName, Tree.leafyTree); + return !ignoreWorld && this.UpdateTreeTextures(Tree.leafyTree); case "terrainfeatures/tree3_fall": // from Tree case "terrainfeatures/tree3_spring": // from Tree case "terrainfeatures/tree3_winter": // from Tree - return !ignoreWorld && this.UpdateTreeTextures(content, assetName, Tree.pineTree); + return !ignoreWorld && this.UpdateTreeTextures(Tree.pineTree); } /**** @@ -585,11 +585,11 @@ namespace StardewModdingAPI.Metadata { // dynamic textures if (assetName.StartsWith("animals/cat")) - return this.UpdatePetOrHorseSprites(content, assetName); + return this.UpdatePetOrHorseSprites(assetName); if (assetName.StartsWith("animals/dog")) - return this.UpdatePetOrHorseSprites(content, assetName); + return this.UpdatePetOrHorseSprites(assetName); if (assetName.IsDirectlyUnderPath("Animals")) - return this.UpdateFarmAnimalSprites(content, assetName); + return this.UpdateFarmAnimalSprites(assetName); if (assetName.IsDirectlyUnderPath("Buildings")) return this.UpdateBuildings(assetName); @@ -643,33 +643,29 @@ namespace StardewModdingAPI.Metadata /// Update the sprites for matching pets or horses. /// The animal type. - /// The content manager through which to update the asset. /// The asset name to update. /// Returns whether any references were updated. - private bool UpdatePetOrHorseSprites(LocalizedContentManager content, IAssetName assetName) + private bool UpdatePetOrHorseSprites(IAssetName assetName) where TAnimal : NPC { // find matches TAnimal[] animals = this.GetCharacters() .OfType() - .Where(p => this.IsSameBaseName(assetName, p.Sprite?.Texture?.Name)) + .Where(p => this.IsSameBaseName(assetName, p.Sprite?.spriteTexture?.Name)) .ToArray(); - if (!animals.Any()) - return false; // update sprites - Texture2D texture = content.Load(assetName.BaseName); + bool changed = false; foreach (TAnimal animal in animals) - animal.Sprite.spriteTexture = texture; - return true; + changed |= this.MarkSpriteDirty(animal.Sprite); + return changed; } /// Update the sprites for matching farm animals. - /// The content manager through which to update the asset. /// The asset name to update. /// Returns whether any references were updated. /// Derived from . - private bool UpdateFarmAnimalSprites(LocalizedContentManager content, IAssetName assetName) + private bool UpdateFarmAnimalSprites(IAssetName assetName) { // find matches FarmAnimal[] animals = this.GetFarmAnimals().ToArray(); @@ -677,7 +673,7 @@ namespace StardewModdingAPI.Metadata return false; // update sprites - Lazy texture = new Lazy(() => content.Load(assetName.BaseName)); + bool changed = true; foreach (FarmAnimal animal in animals) { // get expected key @@ -690,9 +686,9 @@ namespace StardewModdingAPI.Metadata // reload asset if (this.IsSameBaseName(assetName, expectedKey)) - animal.Sprite.spriteTexture = texture.Value; + changed |= this.MarkSpriteDirty(animal.Sprite); } - return texture.IsValueCreated; + return changed; } /// Update building textures. @@ -707,7 +703,7 @@ namespace StardewModdingAPI.Metadata // get building type string type = Path.GetFileName(assetName.BaseName); if (isPaintMask) - type = type.Substring(0, type.Length - paintMaskSuffix.Length); + type = type[..^paintMaskSuffix.Length]; // get buildings Building[] buildings = this.GetLocations(buildingInteriors: false) @@ -747,7 +743,7 @@ namespace StardewModdingAPI.Metadata foreach (MapSeat seat in location.mapSeats.Where(p => p != null)) { if (this.IsSameBaseName(assetName, seat._loadedTextureFile)) - seat.overlayTexture = MapSeat.mapChairTexture; + seat._loadedTextureFile = null; } } } @@ -756,10 +752,9 @@ namespace StardewModdingAPI.Metadata } /// Update critter textures. - /// The content manager through which to reload the asset. /// The asset name to update. /// Returns whether any references were updated. - private bool UpdateCritterTextures(LocalizedContentManager content, IAssetName assetName) + private bool UpdateCritterTextures(IAssetName assetName) { // get critters Critter[] critters = @@ -767,19 +762,16 @@ namespace StardewModdingAPI.Metadata from location in this.GetLocations() where location.critters != null from Critter critter in location.critters - where this.IsSameBaseName(assetName, critter.sprite?.Texture?.Name) + where this.IsSameBaseName(assetName, critter.sprite?.spriteTexture?.Name) select critter ) .ToArray(); - if (!critters.Any()) - return false; // update sprites - Texture2D texture = content.Load(assetName.BaseName); + bool changed = false; foreach (Critter entry in critters) - entry.sprite.spriteTexture = texture; - - return true; + changed |= this.MarkSpriteDirty(entry.sprite); + return changed; } /// Update the sprites for interior doors. @@ -814,7 +806,7 @@ namespace StardewModdingAPI.Metadata private bool UpdateFenceTextures(IAssetName assetName) { // get fence type (e.g. LooseSprites/Fence3 => 3) - if (!int.TryParse(this.GetSegments(assetName.BaseName)[1].Substring("Fence".Length), out int fenceType)) + if (!int.TryParse(this.GetSegments(assetName.BaseName)[1]["Fence".Length..], out int fenceType)) return false; // get fences @@ -830,9 +822,16 @@ namespace StardewModdingAPI.Metadata .ToArray(); // update fence textures + bool changed = false; foreach (Fence fence in fences) - fence.fenceTexture = new Lazy(fence.loadFenceTexture); - return true; + { + if (fence.fenceTexture.IsValueCreated) + { + fence.fenceTexture = new Lazy(fence.loadFenceTexture); + changed = true; + } + } + return changed; } /// Update tree textures. @@ -850,15 +849,16 @@ namespace StardewModdingAPI.Metadata ) .ToArray(); - if (grasses.Any()) + bool changed = false; + foreach (Grass grass in grasses) { - Lazy texture = new Lazy(() => content.Load(assetName.BaseName)); - foreach (Grass grass in grasses) - grass.texture = texture; - return true; + if (grass.texture.IsValueCreated) + { + grass.texture = new Lazy(() => content.Load(assetName.BaseName)); + changed = true; + } } - - return false; + return changed; } /// Update the sprites for matching NPCs. @@ -869,19 +869,17 @@ namespace StardewModdingAPI.Metadata var characters = ( from npc in this.GetCharacters() - let key = this.ParseAssetNameOrNull(npc.Sprite?.Texture?.Name)?.GetBaseAssetName() + let key = this.ParseAssetNameOrNull(npc.Sprite?.spriteTexture?.Name)?.GetBaseAssetName() where key != null && propagated.ContainsKey(key) select new { Npc = npc, AssetName = key } ) .ToArray(); - if (!characters.Any()) - return; // update sprite foreach (var target in characters) { - target.Npc.Sprite.spriteTexture = this.LoadTexture(target.AssetName.BaseName); - propagated[target.AssetName] = true; + if (this.MarkSpriteDirty(target.Npc.Sprite)) + propagated[target.AssetName] = true; } } @@ -919,7 +917,7 @@ namespace StardewModdingAPI.Metadata // update portrait foreach (var target in characters) { - target.Npc.Portrait = this.LoadTexture(target.AssetName.BaseName); + target.Npc.resetPortrait(); propagated[target.AssetName] = true; } } @@ -976,26 +974,38 @@ namespace StardewModdingAPI.Metadata } /// Update tree textures. - /// The content manager through which to reload the asset. - /// The asset name to update. /// The type to update. /// Returns whether any references were updated. - private bool UpdateTreeTextures(LocalizedContentManager content, IAssetName assetName, int type) + private bool UpdateTreeTextures(int type) { Tree[] trees = this.GetLocations() .SelectMany(p => p.terrainFeatures.Values.OfType()) .Where(tree => tree.treeType.Value == type) .ToArray(); - if (trees.Any()) + bool changed = false; + foreach (Tree tree in trees) { - Lazy texture = new Lazy(() => content.Load(assetName.BaseName)); - foreach (Tree tree in trees) - tree.texture = texture; - return true; + if (tree.texture.IsValueCreated) + { + this.Reflection.GetMethod(tree, "resetTexture").Invoke(); + changed = true; + } } + return changed; + } - return false; + /// Mark an animated sprite's texture dirty, so it's reloaded next time it's rendered. + /// The animated sprite to change. + /// Returns whether the sprite was changed. + private bool MarkSpriteDirty(AnimatedSprite sprite) + { + if (sprite.loadedTexture is null && sprite.spriteTexture is null) + return false; + + sprite.loadedTexture = null; + sprite.spriteTexture = null; + return true; } /**** -- cgit From 7e7ac459a54a607de5843be37ea4b222058d5d3e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 21 May 2022 18:06:23 -0400 Subject: fix error when mod localizes an unlocalizable asset and then stops doing so --- docs/release-notes.md | 1 + src/SMAPI/Framework/ContentCoordinator.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'docs/release-notes.md') diff --git a/docs/release-notes.md b/docs/release-notes.md index 8dc53e19..9de06f3f 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,7 @@ * For players: * Improved performance when mods change some asset types (including NPC portraits/sprites). * Fixed CurseForge update checks for the new CurseForge API. + * Fixed _could not find file_ error if a mod provides a localized version of a normally unlocalized asset and then stops providing it. ## 3.14.4 Released 15 May 2022 for Stardew Valley 1.5.6 or later. diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index dd3d2917..fc61b44b 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -407,6 +407,18 @@ namespace StardewModdingAPI.Framework } } + // forget localized flags + // A mod might provide a localized variant of a normally non-localized asset (like + // `Maps/MovieTheater.fr-FR`). When the asset is invalidated, we need to recheck + // whether the asset is localized in case it stops providing it. + foreach (IAssetName assetName in invalidatedAssets.Keys) + { + LocalizedContentManager.localizedAssetNames.Remove(assetName.Name); + + if (LocalizedContentManager.localizedAssetNames.TryGetValue(assetName.BaseName, out string? targetForBaseKey) && targetForBaseKey == assetName.Name) + LocalizedContentManager.localizedAssetNames.Remove(assetName.BaseName); + } + // special case: maps may be loaded through a temporary content manager that's removed while the map is still in use. // This notably affects the town and farmhouse maps. if (Game1.locations != null) -- cgit From 336cc1cc0f250c96ee23d45e1e08569b67a2e562 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 22 May 2022 14:38:33 -0400 Subject: prepare for release --- build/common.targets | 2 +- docs/release-notes.md | 6 ++++-- src/SMAPI.Mods.ConsoleCommands/manifest.json | 4 ++-- src/SMAPI.Mods.ErrorHandler/manifest.json | 4 ++-- src/SMAPI.Mods.SaveBackup/manifest.json | 4 ++-- src/SMAPI/Constants.cs | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) (limited to 'docs/release-notes.md') diff --git a/build/common.targets b/build/common.targets index 76343b8d..7b59e4ae 100644 --- a/build/common.targets +++ b/build/common.targets @@ -1,7 +1,7 @@ - 3.14.4 + 3.14.5 SMAPI latest $(AssemblySearchPaths);{GAC} diff --git a/docs/release-notes.md b/docs/release-notes.md index 9de06f3f..cd177f61 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,11 +1,13 @@ ← [README](README.md) # Release notes -## Upcoming release +## 3.14.5 +Released 22 May 2022 for Stardew Valley 1.5.6 or later. + * For players: * Improved performance when mods change some asset types (including NPC portraits/sprites). - * Fixed CurseForge update checks for the new CurseForge API. * Fixed _could not find file_ error if a mod provides a localized version of a normally unlocalized asset and then stops providing it. + * Fixed CurseForge update checks for the new CurseForge API. ## 3.14.4 Released 15 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 78d26802..7b403a75 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.4", + "Version": "3.14.5", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "3.14.4" + "MinimumApiVersion": "3.14.5" } diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json index be1030f4..2ac959bb 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.4", + "Version": "3.14.5", "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.4" + "MinimumApiVersion": "3.14.5" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index 8a50162e..707b6d8a 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.4", + "Version": "3.14.5", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "3.14.4" + "MinimumApiVersion": "3.14.5" } diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 357b8db8..b2916a8d 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -50,7 +50,7 @@ namespace StardewModdingAPI internal static int? LogScreenId { get; set; } /// SMAPI's current raw semantic version. - internal static string RawApiVersion = "3.14.4"; + internal static string RawApiVersion = "3.14.5"; } /// Contains SMAPI's constants and assumptions. -- cgit