From a0f7a244740de91fc5104f9f33b844493cd4a3a8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 4 Aug 2018 16:32:46 -0400 Subject: fixed false compat error when constructing multidimensional arrays --- .../Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs index cf5a3175..3a26660f 100644 --- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs +++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs @@ -70,7 +70,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders // method reference MethodReference methodReference = RewriteHelper.AsMethodReference(instruction); - if (methodReference != null && this.ShouldValidate(methodReference.DeclaringType)) + if (methodReference != null && !this.IsUnsupported(methodReference) && this.ShouldValidate(methodReference.DeclaringType)) { // get potential targets MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray(); @@ -106,6 +106,14 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders return type != null && this.ValidateReferencesToAssemblies.Contains(type.Scope.Name); } + /// Get whether a method reference is a special case that's not currently supported (e.g. array methods). + /// The method reference. + private bool IsUnsupported(MethodReference method) + { + return + method.DeclaringType.Name.Contains("["); // array methods + } + /// Get a shorter type name for display. /// The type reference. private string GetFriendlyTypeName(TypeReference type) -- cgit From a1a93ac4bf20458d49028ed40905e715aa1ae152 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 6 Aug 2018 22:21:10 -0400 Subject: fix spring tilesheets always used for custom festival maps (#577) --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 671dc21e..a8b24a13 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -259,7 +259,7 @@ namespace StardewModdingAPI.Framework.ModHelpers // get seasonal name (if applicable) string seasonalImageSource = null; - if (Game1.currentSeason != null) + if (Context.IsSaveLoaded && Game1.currentSeason != null) { string filename = Path.GetFileName(imageSource) ?? throw new InvalidOperationException($"The '{imageSource}' tilesheet couldn't be loaded: filename is unexpectedly null."); bool hasSeasonalPrefix = -- cgit From b7907293349e95f84583e682f38e0eb491ac2e5d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 11 Aug 2018 20:33:21 -0400 Subject: add support for loading unpacked .json files through content API (#576) --- src/SMAPI/Framework/ContentCoordinator.cs | 10 ++++-- .../Framework/ContentManagers/ModContentManager.cs | 31 +++++++++++++++--- src/SMAPI/Framework/ContentPack.cs | 4 ++- src/SMAPI/Framework/ModHelpers/ModHelper.cs | 4 ++- src/SMAPI/Framework/SGame.cs | 26 +++++++-------- src/SMAPI/Framework/SGameConstructorHack.cs | 37 ++++++++++++++++++++++ 6 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 src/SMAPI/Framework/SGameConstructorHack.cs (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index d9b2109a..9eb7b5f9 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -9,6 +9,7 @@ using StardewModdingAPI.Framework.Content; using StardewModdingAPI.Framework.ContentManagers; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Metadata; +using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; @@ -32,6 +33,9 @@ namespace StardewModdingAPI.Framework /// Simplifies access to private code. private readonly Reflector Reflection; + /// Encapsulates SMAPI's JSON file parsing. + private readonly JsonHelper JsonHelper; + /// The loaded content managers (including the ). private readonly IList ContentManagers = new List(); @@ -67,10 +71,12 @@ namespace StardewModdingAPI.Framework /// The current culture for which to localise content. /// Encapsulates monitoring and logging. /// Simplifies access to private code. - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection) + /// Encapsulates SMAPI's JSON file parsing. + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper) { this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); this.Reflection = reflection; + this.JsonHelper = jsonHelper; this.FullRootDirectory = Path.Combine(Constants.ExecutionPath, rootDirectory); this.ContentManagers.Add( this.MainContentManager = new GameContentManager("Game1.content", serviceProvider, rootDirectory, currentCulture, this, monitor, reflection, this.OnDisposing) @@ -92,7 +98,7 @@ namespace StardewModdingAPI.Framework /// The root directory to search for content (or null for the default). public ModContentManager CreateModContentManager(string name, string rootDirectory) { - ModContentManager manager = new ModContentManager(name, this.MainContentManager.ServiceProvider, rootDirectory, this.MainContentManager.CurrentCulture, this, this.Monitor, this.Reflection, this.OnDisposing); + ModContentManager manager = new ModContentManager(name, this.MainContentManager.ServiceProvider, rootDirectory, this.MainContentManager.CurrentCulture, this, this.Monitor, this.Reflection, this.JsonHelper, this.OnDisposing); this.ContentManagers.Add(manager); return manager; } diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs index 80bf37e9..24ce69ea 100644 --- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Reflection; +using StardewModdingAPI.Toolkit.Serialisation; using StardewValley; namespace StardewModdingAPI.Framework.ContentManagers @@ -12,6 +13,13 @@ namespace StardewModdingAPI.Framework.ContentManagers /// A content manager which handles reading files from a SMAPI mod folder with support for unpacked files. internal class ModContentManager : BaseContentManager { + /********* + ** Properties + *********/ + /// Encapsulates SMAPI's JSON file parsing. + private readonly JsonHelper JsonHelper; + + /********* ** Public methods *********/ @@ -23,9 +31,13 @@ namespace StardewModdingAPI.Framework.ContentManagers /// The central coordinator which manages content managers. /// Encapsulates monitoring and logging. /// Simplifies access to private code. + /// Encapsulates SMAPI's JSON file parsing. /// A callback to invoke when the content manager is being disposed. - public ModContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing) - : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isModFolder: true) { } + public ModContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onDisposing) + : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isModFolder: true) + { + this.JsonHelper = jsonHelper; + } /// Load an asset that has been processed by the content pipeline. /// The type of asset to load. @@ -95,9 +107,14 @@ namespace StardewModdingAPI.Framework.ContentManagers case ".xnb": return base.Load(relativePath, language); - // unpacked map - case ".tbin": - throw GetContentError($"can't read unpacked map file directly from the underlying content manager. It must be loaded through the mod's {typeof(IModHelper)}.{nameof(IModHelper.Content)} helper."); + // unpacked data + case ".json": + { + if (!this.JsonHelper.ReadJsonFileIfExists(file.FullName, out T data)) + throw GetContentError("the JSON file is invalid."); // should never happen since we check for file existence above + + return data; + } // unpacked image case ".png": @@ -114,6 +131,10 @@ namespace StardewModdingAPI.Framework.ContentManagers return (T)(object)texture; } + // unpacked map + case ".tbin": + throw GetContentError($"can't read unpacked map file directly from the underlying content manager. It must be loaded through the mod's {typeof(IModHelper)}.{nameof(IModHelper.Content)} helper."); + default: throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.png', '.tbin', or '.xnb'."); } diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs index 4a4adb90..62d8b80d 100644 --- a/src/SMAPI/Framework/ContentPack.cs +++ b/src/SMAPI/Framework/ContentPack.cs @@ -54,7 +54,9 @@ namespace StardewModdingAPI.Framework public TModel ReadJsonFile(string path) where TModel : class { path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); - return this.JsonHelper.ReadJsonFile(path); + return this.JsonHelper.ReadJsonFileIfExists(path, out TModel model) + ? model + : null; } /// Load content from the content pack folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index d9498e83..0ba258b4 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -138,7 +138,9 @@ namespace StardewModdingAPI.Framework.ModHelpers where TModel : class { path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); - return this.JsonHelper.ReadJsonFile(path); + return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data) + ? data + : null; } /// Save to a JSON file. diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 05fedc3d..83e8c9a7 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -17,6 +17,7 @@ using StardewModdingAPI.Framework.Input; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Framework.StateTracking; using StardewModdingAPI.Framework.Utilities; +using StardewModdingAPI.Toolkit.Serialisation; using StardewValley; using StardewValley.BellsAndWhistles; using StardewValley.Buildings; @@ -36,16 +37,6 @@ namespace StardewModdingAPI.Framework /********* ** Properties *********/ - /**** - ** Constructor hack - ****/ - /// A static instance of to use while is initialising, which happens before the constructor runs. - internal static IMonitor MonitorDuringInitialisation; - - /// A static instance of to use while is initialising, which happens before the constructor runs. - internal static Reflector ReflectorDuringInitialisation; - - /**** ** SMAPI state ****/ @@ -83,6 +74,9 @@ namespace StardewModdingAPI.Framework /// Simplifies access to private game code. private readonly Reflector Reflection; + /// Encapsulates SMAPI's JSON file parsing. + private readonly JsonHelper JsonHelper; + /**** ** Game state ****/ @@ -105,6 +99,9 @@ namespace StardewModdingAPI.Framework /********* ** Accessors *********/ + /// Static state to use while is initialising, which happens before the constructor runs. + internal static SGameConstructorHack ConstructorHack { get; set; } + /// SMAPI's content manager. public ContentCoordinator ContentCore { get; private set; } @@ -132,10 +129,13 @@ namespace StardewModdingAPI.Framework /// Encapsulates monitoring and logging. /// Simplifies access to private game code. /// Manages SMAPI events for mods. + /// Encapsulates SMAPI's JSON file parsing. /// A callback to invoke after the game finishes initialising. /// A callback to invoke when the game exits. - internal SGame(IMonitor monitor, Reflector reflection, EventManager eventManager, Action onGameInitialised, Action onGameExiting) + internal SGame(IMonitor monitor, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, Action onGameInitialised, Action onGameExiting) { + SGame.ConstructorHack = null; + // check expectations if (this.ContentCore == null) throw new InvalidOperationException($"The game didn't initialise its first content manager before SMAPI's {nameof(SGame)} constructor. This indicates an incompatible lifecycle change."); @@ -147,6 +147,7 @@ namespace StardewModdingAPI.Framework this.Monitor = monitor; this.Events = eventManager; this.Reflection = reflection; + this.JsonHelper = jsonHelper; this.OnGameInitialised = onGameInitialised; this.OnGameExiting = onGameExiting; Game1.input = new SInputState(); @@ -191,8 +192,7 @@ namespace StardewModdingAPI.Framework // NOTE: this method is called before the SGame constructor runs. Don't depend on anything being initialised at this point. if (this.ContentCore == null) { - this.ContentCore = new ContentCoordinator(serviceProvider, rootDirectory, Thread.CurrentThread.CurrentUICulture, SGame.MonitorDuringInitialisation, SGame.ReflectorDuringInitialisation); - SGame.MonitorDuringInitialisation = null; + this.ContentCore = new ContentCoordinator(serviceProvider, rootDirectory, Thread.CurrentThread.CurrentUICulture, SGame.ConstructorHack.Monitor, SGame.ConstructorHack.Reflection, SGame.ConstructorHack.JsonHelper); this.NextContentManagerIsMain = true; return this.ContentCore.CreateGameContentManager("Game1._temporaryContent"); } diff --git a/src/SMAPI/Framework/SGameConstructorHack.cs b/src/SMAPI/Framework/SGameConstructorHack.cs new file mode 100644 index 00000000..494bab99 --- /dev/null +++ b/src/SMAPI/Framework/SGameConstructorHack.cs @@ -0,0 +1,37 @@ +using StardewModdingAPI.Framework.Reflection; +using StardewModdingAPI.Toolkit.Serialisation; +using StardewValley; + +namespace StardewModdingAPI.Framework +{ + /// The static state to use while is initialising, which happens before the constructor runs. + internal class SGameConstructorHack + { + /********* + ** Accessors + *********/ + /// Encapsulates monitoring and logging. + public IMonitor Monitor { get; } + + /// Simplifies access to private game code. + public Reflector Reflection { get; } + + /// Encapsulates SMAPI's JSON file parsing. + public JsonHelper JsonHelper { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Encapsulates monitoring and logging. + /// Simplifies access to private game code. + /// Encapsulates SMAPI's JSON file parsing. + public SGameConstructorHack(IMonitor monitor, Reflector reflection, JsonHelper jsonHelper) + { + this.Monitor = monitor; + this.Reflection = reflection; + this.JsonHelper = jsonHelper; + } + } +} -- cgit