summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2018-08-11 20:33:21 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2018-08-11 20:33:21 -0400
commitb7907293349e95f84583e682f38e0eb491ac2e5d (patch)
tree3a2e2405729eb3a42b036049957ad741e9968f83 /src/SMAPI/Framework
parentef731de8318c7f01567baf2e23ae9a09789b4bdd (diff)
downloadSMAPI-b7907293349e95f84583e682f38e0eb491ac2e5d.tar.gz
SMAPI-b7907293349e95f84583e682f38e0eb491ac2e5d.tar.bz2
SMAPI-b7907293349e95f84583e682f38e0eb491ac2e5d.zip
add support for loading unpacked .json files through content API (#576)
Diffstat (limited to 'src/SMAPI/Framework')
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs10
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs31
-rw-r--r--src/SMAPI/Framework/ContentPack.cs4
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs4
-rw-r--r--src/SMAPI/Framework/SGame.cs26
-rw-r--r--src/SMAPI/Framework/SGameConstructorHack.cs37
6 files changed, 90 insertions, 22 deletions
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
/// <summary>Simplifies access to private code.</summary>
private readonly Reflector Reflection;
+ /// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
+ private readonly JsonHelper JsonHelper;
+
/// <summary>The loaded content managers (including the <see cref="MainContentManager"/>).</summary>
private readonly IList<IContentManager> ContentManagers = new List<IContentManager>();
@@ -67,10 +71,12 @@ namespace StardewModdingAPI.Framework
/// <param name="currentCulture">The current culture for which to localise content.</param>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="reflection">Simplifies access to private code.</param>
- public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection)
+ /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
+ 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
/// <param name="rootDirectory">The root directory to search for content (or <c>null</c> for the default).</param>
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
@@ -13,6 +14,13 @@ namespace StardewModdingAPI.Framework.ContentManagers
internal class ModContentManager : BaseContentManager
{
/*********
+ ** Properties
+ *********/
+ /// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
+ private readonly JsonHelper JsonHelper;
+
+
+ /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -23,9 +31,13 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="coordinator">The central coordinator which manages content managers.</param>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="reflection">Simplifies access to private code.</param>
+ /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
- public ModContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> 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<BaseContentManager> onDisposing)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isModFolder: true)
+ {
+ this.JsonHelper = jsonHelper;
+ }
/// <summary>Load an asset that has been processed by the content pipeline.</summary>
/// <typeparam name="T">The type of asset to load.</typeparam>
@@ -95,9 +107,14 @@ namespace StardewModdingAPI.Framework.ContentManagers
case ".xnb":
return base.Load<T>(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<TModel>(string path) where TModel : class
{
path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path));
- return this.JsonHelper.ReadJsonFile<TModel>(path);
+ return this.JsonHelper.ReadJsonFileIfExists(path, out TModel model)
+ ? model
+ : null;
}
/// <summary>Load content from the content pack folder (if not already cached), and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary>
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<TModel>(path);
+ return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data)
+ ? data
+ : null;
}
/// <summary>Save to a JSON file.</summary>
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;
@@ -37,16 +38,6 @@ namespace StardewModdingAPI.Framework
** Properties
*********/
/****
- ** Constructor hack
- ****/
- /// <summary>A static instance of <see cref="Monitor"/> to use while <see cref="Game1"/> is initialising, which happens before the <see cref="SGame"/> constructor runs.</summary>
- internal static IMonitor MonitorDuringInitialisation;
-
- /// <summary>A static instance of <see cref="Reflection"/> to use while <see cref="Game1"/> is initialising, which happens before the <see cref="SGame"/> constructor runs.</summary>
- internal static Reflector ReflectorDuringInitialisation;
-
-
- /****
** SMAPI state
****/
/// <summary>Encapsulates monitoring and logging.</summary>
@@ -83,6 +74,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Simplifies access to private game code.</summary>
private readonly Reflector Reflection;
+ /// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
+ private readonly JsonHelper JsonHelper;
+
/****
** Game state
****/
@@ -105,6 +99,9 @@ namespace StardewModdingAPI.Framework
/*********
** Accessors
*********/
+ /// <summary>Static state to use while <see cref="Game1"/> is initialising, which happens before the <see cref="SGame"/> constructor runs.</summary>
+ internal static SGameConstructorHack ConstructorHack { get; set; }
+
/// <summary>SMAPI's content manager.</summary>
public ContentCoordinator ContentCore { get; private set; }
@@ -132,10 +129,13 @@ namespace StardewModdingAPI.Framework
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="reflection">Simplifies access to private game code.</param>
/// <param name="eventManager">Manages SMAPI events for mods.</param>
+ /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="onGameInitialised">A callback to invoke after the game finishes initialising.</param>
/// <param name="onGameExiting">A callback to invoke when the game exits.</param>
- 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
+{
+ /// <summary>The static state to use while <see cref="Game1"/> is initialising, which happens before the <see cref="SGame"/> constructor runs.</summary>
+ internal class SGameConstructorHack
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>Encapsulates monitoring and logging.</summary>
+ public IMonitor Monitor { get; }
+
+ /// <summary>Simplifies access to private game code.</summary>
+ public Reflector Reflection { get; }
+
+ /// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
+ public JsonHelper JsonHelper { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="monitor">Encapsulates monitoring and logging.</param>
+ /// <param name="reflection">Simplifies access to private game code.</param>
+ /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
+ public SGameConstructorHack(IMonitor monitor, Reflector reflection, JsonHelper jsonHelper)
+ {
+ this.Monitor = monitor;
+ this.Reflection = reflection;
+ this.JsonHelper = jsonHelper;
+ }
+ }
+}