From adebec4dd4d3bf4b45f23377a6ab1525fa2d78ef Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 3 Mar 2018 17:49:24 -0500 Subject: automatically detect broken code (#453) --- src/SMAPI/Metadata/InstructionMetadata.cs | 48 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 5bb461c1..9e2b7967 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -21,7 +21,27 @@ namespace StardewModdingAPI.Metadata return new IInstructionHandler[] { /**** - ** throw exception for incompatible code + ** rewrite CIL to fix incompatible code + ****/ + // crossplatform + new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchMethods), onlyIfPlatformChanged: true), + + // Stardew Valley 1.2 + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.currentMinigame)), + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)), + new FieldReplaceRewriter(typeof(Game1), "borderFont", nameof(Game1.smallFont)), + new FieldReplaceRewriter(typeof(Game1), "smoothFont", nameof(Game1.smallFont)), + + // SMAPI 1.9 + new TypeReferenceRewriter("StardewModdingAPI.Inheritance.ItemStackChange", typeof(ItemStackChange)), + + // SMAPI 2.0 + new VirtualEntryCallRemover(), // Mod.Entry changed from virtual to abstract in SMAPI 2.0, which breaks the few mods which called base.Entry() + + /**** + ** detect incompatible code ****/ // changes in Stardew Valley 1.2 (with no rewriters) new FieldFinder("StardewValley.Item", "set_Name", InstructionHandleResult.NotCompatible), @@ -66,6 +86,10 @@ namespace StardewModdingAPI.Metadata new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigFolder", InstructionHandleResult.NotCompatible), new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigPath", InstructionHandleResult.NotCompatible), + // broken code + new ReferenceToMissingMemberFinder(), + new ReferenceToMemberWithUnexpectedTypeFinder(), + /**** ** detect code which may impact game stability ****/ @@ -74,27 +98,7 @@ namespace StardewModdingAPI.Metadata new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.serializer), InstructionHandleResult.DetectedSaveSerialiser), new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.farmerSerializer), InstructionHandleResult.DetectedSaveSerialiser), new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.locationSerializer), InstructionHandleResult.DetectedSaveSerialiser), - new EventFinder(typeof(SpecialisedEvents).FullName, nameof(SpecialisedEvents.UnvalidatedUpdateTick), InstructionHandleResult.DetectedUnvalidatedUpdateTick), - - /**** - ** rewrite CIL to fix incompatible code - ****/ - // crossplatform - new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchMethods), onlyIfPlatformChanged: true), - - // Stardew Valley 1.2 - new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), - new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.currentMinigame)), - new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), - new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)), - new FieldReplaceRewriter(typeof(Game1), "borderFont", nameof(Game1.smallFont)), - new FieldReplaceRewriter(typeof(Game1), "smoothFont", nameof(Game1.smallFont)), - - // SMAPI 1.9 - new TypeReferenceRewriter("StardewModdingAPI.Inheritance.ItemStackChange", typeof(ItemStackChange)), - - // SMAPI 2.0 - new VirtualEntryCallRemover() // Mod.Entry changed from virtual to abstract in SMAPI 2.0, which breaks the few mods which called base.Entry() + new EventFinder(typeof(SpecialisedEvents).FullName, nameof(SpecialisedEvents.UnvalidatedUpdateTick), InstructionHandleResult.DetectedUnvalidatedUpdateTick) }; } } -- cgit From 8689fe65642d07fa6a2513aa36c1389479e50d0c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 5 Mar 2018 19:07:22 -0500 Subject: fix compatibility heuristics incorrectly flagging mods with missing optional references (#453) --- src/SMAPI/Metadata/InstructionMetadata.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 9e2b7967..4ed1e38e 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -12,6 +12,14 @@ namespace StardewModdingAPI.Metadata /// Provides CIL instruction handlers which rewrite mods for compatibility and throw exceptions for incompatible code. internal class InstructionMetadata { + /********* + ** Properties + *********/ + /// The assembly names to which to heuristically detect broken references. + /// The current implementation only works correctly with assemblies that should always be present. + private readonly string[] ValidateReferencesToAssemblies = { "StardewModdingAPI", "Stardew Valley", "StardewValley" }; + + /********* ** Public methods *********/ @@ -87,8 +95,8 @@ namespace StardewModdingAPI.Metadata new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigPath", InstructionHandleResult.NotCompatible), // broken code - new ReferenceToMissingMemberFinder(), - new ReferenceToMemberWithUnexpectedTypeFinder(), + new ReferenceToMissingMemberFinder(this.ValidateReferencesToAssemblies), + new ReferenceToMemberWithUnexpectedTypeFinder(this.ValidateReferencesToAssemblies), /**** ** detect code which may impact game stability -- cgit From 41715cefcde3c838bb079cb37aac5a3b2dcb1004 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 11 Mar 2018 19:09:08 -0400 Subject: add initial compatibility with Stardew Valley 1.3 (#453) --- src/SMAPI/Metadata/CoreAssets.cs | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/CoreAssets.cs b/src/SMAPI/Metadata/CoreAssets.cs index 5a98da4b..c027df25 100644 --- a/src/SMAPI/Metadata/CoreAssets.cs +++ b/src/SMAPI/Metadata/CoreAssets.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Reflection; using StardewValley; using StardewValley.BellsAndWhistles; using StardewValley.Buildings; @@ -31,7 +32,8 @@ namespace StardewModdingAPI.Metadata *********/ /// Initialise the core asset data. /// Normalises an asset key to match the cache key. - public CoreAssets(Func getNormalisedPath) + /// Simplifies access to private code. + public CoreAssets(Func getNormalisedPath, Reflector reflection) { this.GetNormalisedPath = getNormalisedPath; this.SingletonSetters = @@ -82,6 +84,25 @@ namespace StardewModdingAPI.Metadata // from Game1.ResetToolSpriteSheet ["TileSheets\\tools"] = (content, key) => Game1.ResetToolSpriteSheet(), +#if STARDEW_VALLEY_1_3 + // from Bush + ["TileSheets\\bushes"] = (content, key) => reflection.GetField>(typeof(Bush), "texture").SetValue(new Lazy(() => content.Load(key))), + + // from Farm + ["Buildings\\houses"] = (content, key) => reflection.GetField(typeof(Farm), nameof(Farm.houseTextures)).SetValue(content.Load(key)), + + // from Farmer + ["Characters\\Farmer\\farmer_base"] = (content, key) => + { + if (Game1.player != null && Game1.player.isMale) + Game1.player.FarmerRenderer = new FarmerRenderer(key); + }, + ["Characters\\Farmer\\farmer_girl_base"] = (content, key) => + { + if (Game1.player != null && !Game1.player.isMale) + Game1.player.FarmerRenderer = new FarmerRenderer(key); + }, +#else // from Bush ["TileSheets\\bushes"] = (content, key) => Bush.texture = content.Load(key), @@ -107,6 +128,7 @@ namespace StardewModdingAPI.Metadata if (Game1.player != null && !Game1.player.isMale) Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); }, +#endif // from Flooring ["TerrainFeatures\\Flooring"] = (content, key) => Flooring.floorsTexture = content.Load(key), @@ -144,9 +166,15 @@ namespace StardewModdingAPI.Metadata Building[] buildings = this.GetAllBuildings().Where(p => key == this.GetNormalisedPath($"Buildings\\{p.buildingType}")).ToArray(); if (buildings.Any()) { +#if STARDEW_VALLEY_1_3 + foreach (Building building in buildings) + building.texture = new Lazy(() => content.Load(key)); +#else Texture2D texture = content.Load(key); foreach (Building building in buildings) building.texture = texture; +#endif + return true; } return false; @@ -162,9 +190,11 @@ namespace StardewModdingAPI.Metadata /// Get all player-constructed buildings in the world. private IEnumerable GetAllBuildings() { - return Game1.locations - .OfType() - .SelectMany(p => p.buildings); + foreach (BuildableGameLocation location in Game1.locations.OfType()) + { + foreach (Building building in location.buildings) + yield return building; + } } } } -- cgit From b8f17e6afb00b78ac31df98f7dc3bbe071a99e47 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 11 Mar 2018 19:10:08 -0400 Subject: update rewriters for Stardew Valley 1.3 (#453) --- src/SMAPI/Metadata/InstructionMetadata.cs | 37 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 4ed1e38e..603a43cb 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -6,6 +6,9 @@ using StardewModdingAPI.Framework.ModLoading; using StardewModdingAPI.Framework.ModLoading.Finders; using StardewModdingAPI.Framework.ModLoading.Rewriters; using StardewValley; +#if STARDEW_VALLEY_1_3 +using SObject = StardewValley.Object; +#endif namespace StardewModdingAPI.Metadata { @@ -31,10 +34,11 @@ namespace StardewModdingAPI.Metadata /**** ** rewrite CIL to fix incompatible code ****/ - // crossplatform + // rewrite for crossplatform compatibility new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchMethods), onlyIfPlatformChanged: true), - // Stardew Valley 1.2 +#if !STARDEW_VALLEY_1_3 + // rewrite for Stardew Valley 1.2 new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.currentMinigame)), new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), @@ -42,19 +46,33 @@ namespace StardewModdingAPI.Metadata new FieldReplaceRewriter(typeof(Game1), "borderFont", nameof(Game1.smallFont)), new FieldReplaceRewriter(typeof(Game1), "smoothFont", nameof(Game1.smallFont)), - // SMAPI 1.9 + // rewrite for SMAPI 1.9 new TypeReferenceRewriter("StardewModdingAPI.Inheritance.ItemStackChange", typeof(ItemStackChange)), +#endif - // SMAPI 2.0 - new VirtualEntryCallRemover(), // Mod.Entry changed from virtual to abstract in SMAPI 2.0, which breaks the few mods which called base.Entry() + // rewrite for SMAPI 2.0 + new VirtualEntryCallRemover(), + + // rewrite for Stardew Valley 1.3 +#if STARDEW_VALLEY_1_3 + new StaticFieldToConstantRewriter(typeof(Game1), "tileSize", Game1.tileSize), + new FieldToPropertyRewriter(typeof(Character), nameof(Character.name), nameof(Character.Name)), + new FieldToPropertyRewriter(typeof(GameLocation), nameof(GameLocation.isFarm), nameof(GameLocation.IsFarm)), + new FieldToPropertyRewriter(typeof(GameLocation), nameof(GameLocation.isOutdoors), nameof(GameLocation.isOutdoors)), + new FieldToPropertyRewriter(typeof(GameLocation), nameof(GameLocation.name), nameof(GameLocation.Name)), + new FieldToPropertyRewriter(typeof(Item), nameof(Item.category), nameof(Item.Category)), + new FieldToPropertyRewriter(typeof(SObject), nameof(SObject.quality), nameof(SObject.Quality)), + new FieldToPropertyRewriter(typeof(SObject), nameof(SObject.stack), nameof(SObject.Stack)), +#endif /**** ** detect incompatible code ****/ - // changes in Stardew Valley 1.2 (with no rewriters) + #if !STARDEW_VALLEY_1_3 + // detect changes in Stardew Valley 1.2 new FieldFinder("StardewValley.Item", "set_Name", InstructionHandleResult.NotCompatible), - // APIs removed in SMAPI 1.9 + // detect APIs removed in SMAPI 1.9 new TypeFinder("StardewModdingAPI.Advanced.ConfigFile", InstructionHandleResult.NotCompatible), new TypeFinder("StardewModdingAPI.Advanced.IConfigFile", InstructionHandleResult.NotCompatible), new TypeFinder("StardewModdingAPI.Entities.SPlayer", InstructionHandleResult.NotCompatible), @@ -71,7 +89,7 @@ namespace StardewModdingAPI.Metadata new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderHudEventNoCheck", InstructionHandleResult.NotCompatible), new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck", InstructionHandleResult.NotCompatible), - // APIs removed in SMAPI 2.0 + // detect APIs removed in SMAPI 2.0 new TypeFinder("StardewModdingAPI.Command", InstructionHandleResult.NotCompatible), new TypeFinder("StardewModdingAPI.Config", InstructionHandleResult.NotCompatible), new TypeFinder("StardewModdingAPI.Log", InstructionHandleResult.NotCompatible), @@ -93,8 +111,9 @@ namespace StardewModdingAPI.Metadata new PropertyFinder("StardewModdingAPI.Mod", "BaseConfigPath", InstructionHandleResult.NotCompatible), new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigFolder", InstructionHandleResult.NotCompatible), new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigPath", InstructionHandleResult.NotCompatible), + #endif - // broken code + // detect broken code new ReferenceToMissingMemberFinder(this.ValidateReferencesToAssemblies), new ReferenceToMemberWithUnexpectedTypeFinder(this.ValidateReferencesToAssemblies), -- cgit From 9c49d090a06c96ff2c1f4978da67e864177c2a2e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 11 Mar 2018 20:03:52 -0400 Subject: reorganise and update core content logic for Stardew Valley 1.3 (#453) --- src/SMAPI/Metadata/CoreAssets.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/CoreAssets.cs b/src/SMAPI/Metadata/CoreAssets.cs index c027df25..378117a8 100644 --- a/src/SMAPI/Metadata/CoreAssets.cs +++ b/src/SMAPI/Metadata/CoreAssets.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework.Graphics; -using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Reflection; using StardewValley; using StardewValley.BellsAndWhistles; @@ -24,7 +23,7 @@ namespace StardewModdingAPI.Metadata protected readonly Func GetNormalisedPath; /// Setters which update static or singleton texture fields indexed by normalised asset key. - private readonly IDictionary> SingletonSetters; + private readonly IDictionary> SingletonSetters; /********* @@ -37,7 +36,7 @@ namespace StardewModdingAPI.Metadata { this.GetNormalisedPath = getNormalisedPath; this.SingletonSetters = - new Dictionary> + new Dictionary> { // from CraftingRecipe.InitShared ["Data\\CraftingRecipes"] = (content, key) => CraftingRecipe.craftingRecipes = content.Load>(key), @@ -151,10 +150,10 @@ namespace StardewModdingAPI.Metadata /// The content manager through which to reload the asset. /// The asset key to reload. /// Returns whether an asset was reloaded. - public bool ReloadForKey(SContentManager content, string key) + public bool ReloadForKey(LocalizedContentManager content, string key) { // static assets - if (this.SingletonSetters.TryGetValue(key, out Action reload)) + if (this.SingletonSetters.TryGetValue(key, out Action reload)) { reload(content, key); return true; -- cgit From de17f87d87f5964483e2abbae8169beff19a89f8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 11 Mar 2018 20:31:19 -0400 Subject: fix some title menu assets not being editable (#453, #413) --- src/SMAPI/Metadata/CoreAssets.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/CoreAssets.cs b/src/SMAPI/Metadata/CoreAssets.cs index 378117a8..87629682 100644 --- a/src/SMAPI/Metadata/CoreAssets.cs +++ b/src/SMAPI/Metadata/CoreAssets.cs @@ -7,6 +7,7 @@ using StardewValley; using StardewValley.BellsAndWhistles; using StardewValley.Buildings; using StardewValley.Locations; +using StardewValley.Menus; using StardewValley.Objects; using StardewValley.Projectiles; using StardewValley.TerrainFeatures; @@ -140,6 +141,26 @@ namespace StardewModdingAPI.Metadata ["TerrainFeatures\\hoeDirtDark"] = (content, key) => HoeDirt.darkTexture = content.Load(key), ["TerrainFeatures\\hoeDirtSnow"] = (content, key) => HoeDirt.snowTexture = content.Load(key), + // from TitleMenu + ["Minigames\\Clouds"] = (content, key) => + { + if (Game1.activeClickableMenu is TitleMenu) + reflection.GetField(Game1.activeClickableMenu, "cloudsTexture").SetValue(content.Load(key)); + }, + ["Minigames\\TitleButtons"] = (content, key) => + { + if (Game1.activeClickableMenu is TitleMenu titleMenu) + { + reflection.GetField(titleMenu, "titleButtonsTexture").SetValue(content.Load(key)); + foreach (TemporaryAnimatedSprite bird in reflection.GetField>(titleMenu, "birds").GetValue()) +#if STARDEW_VALLEY_1_3 + bird.texture = content.Load(key); +#else + bird.Texture = content.Load(key); +#endif + } + }, + // from Wallpaper ["Maps\\walls_and_floors"] = (content, key) => Wallpaper.wallpaperTexture = content.Load(key) } -- cgit From a9b6b0ffa799cccbb1ad473357a8f503731da517 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 13 Mar 2018 19:43:15 -0400 Subject: don't rewrite for field to property changes in Stardew Valley 1.3 (#456) The fields are still available (even if they changed return type). That will cause unnecessary rewrites for some mods after they're updated, so they can't be debugged into. --- src/SMAPI/Metadata/InstructionMetadata.cs | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/SMAPI/Metadata') diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 603a43cb..4960a458 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -56,13 +56,6 @@ namespace StardewModdingAPI.Metadata // rewrite for Stardew Valley 1.3 #if STARDEW_VALLEY_1_3 new StaticFieldToConstantRewriter(typeof(Game1), "tileSize", Game1.tileSize), - new FieldToPropertyRewriter(typeof(Character), nameof(Character.name), nameof(Character.Name)), - new FieldToPropertyRewriter(typeof(GameLocation), nameof(GameLocation.isFarm), nameof(GameLocation.IsFarm)), - new FieldToPropertyRewriter(typeof(GameLocation), nameof(GameLocation.isOutdoors), nameof(GameLocation.isOutdoors)), - new FieldToPropertyRewriter(typeof(GameLocation), nameof(GameLocation.name), nameof(GameLocation.Name)), - new FieldToPropertyRewriter(typeof(Item), nameof(Item.category), nameof(Item.Category)), - new FieldToPropertyRewriter(typeof(SObject), nameof(SObject.quality), nameof(SObject.Quality)), - new FieldToPropertyRewriter(typeof(SObject), nameof(SObject.stack), nameof(SObject.Stack)), #endif /**** -- cgit