summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs28
-rw-r--r--src/SMAPI/Framework/SContentManager.cs31
-rw-r--r--src/SMAPI/Framework/SGame.cs2
3 files changed, 55 insertions, 6 deletions
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 86ebc5c3..397a9d90 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -30,6 +30,9 @@ namespace StardewModdingAPI.Framework
/// <summary>The loaded content managers (including the <see cref="MainContentManager"/>).</summary>
private readonly IList<SContentManager> ContentManagers = new List<SContentManager>();
+ /// <summary>Whether the content coordinator has been disposed.</summary>
+ private bool IsDisposed;
+
/*********
** Accessors
@@ -65,7 +68,7 @@ namespace StardewModdingAPI.Framework
this.Reflection = reflection;
this.FullRootDirectory = Path.Combine(Constants.ExecutionPath, rootDirectory);
this.ContentManagers.Add(
- this.MainContentManager = new SContentManager("Game1.content", serviceProvider, rootDirectory, currentCulture, this, monitor, reflection, isModFolder: false)
+ this.MainContentManager = new SContentManager("Game1.content", serviceProvider, rootDirectory, currentCulture, this, monitor, reflection, this.OnDisposing, isModFolder: false)
);
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager.NormaliseAssetName, reflection);
}
@@ -76,7 +79,9 @@ namespace StardewModdingAPI.Framework
/// <param name="rootDirectory">The root directory to search for content (or <c>null</c>. for the default)</param>
public SContentManager CreateContentManager(string name, bool isModFolder, string rootDirectory = null)
{
- return new SContentManager(name, this.MainContentManager.ServiceProvider, rootDirectory ?? this.MainContentManager.RootDirectory, this.MainContentManager.CurrentCulture, this, this.Monitor, this.Reflection, isModFolder);
+ SContentManager manager = new SContentManager(name, this.MainContentManager.ServiceProvider, rootDirectory ?? this.MainContentManager.RootDirectory, this.MainContentManager.CurrentCulture, this, this.Monitor, this.Reflection, this.OnDisposing, isModFolder);
+ this.ContentManagers.Add(manager);
+ return manager;
}
/// <summary>Get the current content locale.</summary>
@@ -162,8 +167,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Dispose held resources.</summary>
public void Dispose()
{
- if (this.MainContentManager == null)
- return; // already disposed
+ if (this.IsDisposed)
+ return;
+ this.IsDisposed = true;
this.Monitor.Log("Disposing the content coordinator. Content managers will no longer be usable after this point.", LogLevel.Trace);
foreach (SContentManager contentManager in this.ContentManagers)
@@ -171,5 +177,19 @@ namespace StardewModdingAPI.Framework
this.ContentManagers.Clear();
this.MainContentManager = null;
}
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>A callback invoked when a content manager is disposed.</summary>
+ /// <param name="contentManager">The content manager being disposed.</param>
+ private void OnDisposing(SContentManager contentManager)
+ {
+ if (this.IsDisposed)
+ return;
+
+ this.ContentManagers.Remove(contentManager);
+ }
}
}
diff --git a/src/SMAPI/Framework/SContentManager.cs b/src/SMAPI/Framework/SContentManager.cs
index 8f008041..e9f46acb 100644
--- a/src/SMAPI/Framework/SContentManager.cs
+++ b/src/SMAPI/Framework/SContentManager.cs
@@ -43,12 +43,18 @@ namespace StardewModdingAPI.Framework
/// <summary>The path prefix for assets in mod folders.</summary>
private readonly string ModContentPrefix;
+ /// <summary>A callback to invoke when the content manager is being disposed.</summary>
+ private readonly Action<SContentManager> OnDisposing;
+
/// <summary>Interceptors which provide the initial versions of matching assets.</summary>
private IDictionary<IModMetadata, IList<IAssetLoader>> Loaders => this.Coordinator.Loaders;
/// <summary>Interceptors which edit matching assets after they're loaded.</summary>
private IDictionary<IModMetadata, IList<IAssetEditor>> Editors => this.Coordinator.Editors;
+ /// <summary>Whether the content coordinator has been disposed.</summary>
+ private bool IsDisposed;
+
/*********
** Accessors
@@ -78,7 +84,8 @@ namespace StardewModdingAPI.Framework
/// <param name="monitor">Encapsulates monitoring and logging.</param>
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="isModFolder">Whether this content manager is wrapped around a mod folder.</param>
- public SContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, bool isModFolder)
+ /// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
+ public SContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<SContentManager> onDisposing, bool isModFolder)
: base(serviceProvider, rootDirectory, currentCulture)
{
// init
@@ -88,6 +95,7 @@ namespace StardewModdingAPI.Framework
this.Cache = new ContentCache(this, reflection);
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
this.ModContentPrefix = this.GetAssetNameFromFilePath(Constants.ModPath, ContentSource.GameContent);
+ this.OnDisposing = onDisposing;
// get asset data
this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.InvariantCultureIgnoreCase);
@@ -291,6 +299,27 @@ namespace StardewModdingAPI.Framework
return removeAssetNames;
}
+ /// <summary>Dispose held resources.</summary>
+ /// <param name="isDisposing">Whether the content manager is being disposed (rather than finalized).</param>
+ protected override void Dispose(bool isDisposing)
+ {
+ if (this.IsDisposed)
+ return;
+ this.IsDisposed = true;
+
+ this.OnDisposing(this);
+ base.Dispose(isDisposing);
+ }
+
+ /// <inheritdoc />
+ public override void Unload()
+ {
+ if (this.IsDisposed)
+ return; // base logic doesn't allow unloading twice, which happens due to SMAPI and the game both unloading
+
+ base.Unload();
+ }
+
/*********
** Private methods
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 48a70688..8a4f987b 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -209,7 +209,7 @@ namespace StardewModdingAPI.Framework
if (this.NextContentManagerIsMain)
{
this.NextContentManagerIsMain = false;
- return this.ContentCore.CreateContentManager("Game1.content", isModFolder: false, rootDirectory: rootDirectory);
+ return this.ContentCore.MainContentManager;
}
// any other content manager