summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2017-08-17 17:46:45 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2017-08-17 17:46:45 -0400
commit723ddc255e1c2b399dfb734306fd00912a741e62 (patch)
tree387def74bf23cfc686ef5cf4c42226cce58214dd /src/StardewModdingAPI
parent9e1d01d4feb4107448840f30fa39e1c827bf5fdc (diff)
downloadSMAPI-723ddc255e1c2b399dfb734306fd00912a741e62.tar.gz
SMAPI-723ddc255e1c2b399dfb734306fd00912a741e62.tar.bz2
SMAPI-723ddc255e1c2b399dfb734306fd00912a741e62.zip
break loops when loading assets through a mod loader
Diffstat (limited to 'src/StardewModdingAPI')
-rw-r--r--src/StardewModdingAPI/Framework/SContentManager.cs22
-rw-r--r--src/StardewModdingAPI/Framework/SGame.cs5
-rw-r--r--src/StardewModdingAPI/Framework/Utilities/ContextHash.cs62
-rw-r--r--src/StardewModdingAPI/Framework/Utilities/Countdown.cs (renamed from src/StardewModdingAPI/Framework/Countdown.cs)2
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.csproj5
5 files changed, 87 insertions, 9 deletions
diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs
index 9e086870..25775291 100644
--- a/src/StardewModdingAPI/Framework/SContentManager.cs
+++ b/src/StardewModdingAPI/Framework/SContentManager.cs
@@ -9,6 +9,7 @@ using Microsoft.Xna.Framework.Content;
using StardewModdingAPI.AssemblyRewriters;
using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Framework.Utilities;
using StardewModdingAPI.Metadata;
using StardewValley;
@@ -44,6 +45,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Provides metadata for core game assets.</summary>
private readonly CoreAssets CoreAssets;
+ /// <summary>The assets currently being intercepted by <see cref="IAssetLoader"/> instances. This is used to prevent infinite loops when a loader loads a new asset.</summary>
+ private readonly ContextHash<string> AssetsBeingLoaded = new ContextHash<string>();
+
/*********
** Accessors
@@ -139,11 +143,21 @@ namespace StardewModdingAPI.Framework
// load asset
T data;
+ if (this.AssetsBeingLoaded.Contains(assetName))
{
- IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName);
- IAssetData asset = this.ApplyLoader<T>(info) ?? new AssetDataForObject(info, base.Load<T>(assetName), this.NormaliseAssetName);
- asset = this.ApplyEditors<T>(info, asset);
- data = (T)asset.Data;
+ this.Monitor.Log($"Broke loop while loading asset '{assetName}'.", LogLevel.Warn);
+ this.Monitor.Log($"Bypassing mod loaders for this asset. Stack trace:\n{Environment.StackTrace}", LogLevel.Trace);
+ data = base.Load<T>(assetName);
+ }
+ else
+ {
+ data = this.AssetsBeingLoaded.Track(assetName, () =>
+ {
+ IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName);
+ IAssetData asset = this.ApplyLoader<T>(info) ?? new AssetDataForObject(info, base.Load<T>(assetName), this.NormaliseAssetName);
+ asset = this.ApplyEditors<T>(info, asset);
+ return (T)asset.Data;
+ });
}
// update cache & return data
diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs
index 755f0274..997e0c8c 100644
--- a/src/StardewModdingAPI/Framework/SGame.cs
+++ b/src/StardewModdingAPI/Framework/SGame.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
@@ -11,6 +11,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Events;
using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Framework.Utilities;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.BellsAndWhistles;
@@ -322,7 +323,7 @@ namespace StardewModdingAPI.Framework
#if !SMAPI_1_x
if (Game1.dayOfMonth != 0) // wait until new-game intro finishes (world not fully initialised yet)
#endif
- this.AfterLoadTimer--;
+ this.AfterLoadTimer--;
if (this.AfterLoadTimer == 0)
{
diff --git a/src/StardewModdingAPI/Framework/Utilities/ContextHash.cs b/src/StardewModdingAPI/Framework/Utilities/ContextHash.cs
new file mode 100644
index 00000000..0d8487bb
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/Utilities/ContextHash.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace StardewModdingAPI.Framework.Utilities
+{
+ /// <summary>A <see cref="HashSet{T}"/> wrapper meant for tracking recursive contexts.</summary>
+ /// <typeparam name="T">The key type.</typeparam>
+ internal class ContextHash<T> : HashSet<T>
+ {
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public ContextHash() { }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> implementation to use when comparing values in the set, or <c>null</c> to use the default comparer for the set type.</param>
+ public ContextHash(IEqualityComparer<T> comparer)
+ : base(comparer) { }
+
+ /// <summary>Add a key while an action is in progress, and remove it when it completes.</summary>
+ /// <param name="key">The key to add.</param>
+ /// <param name="action">The action to perform.</param>
+ /// <exception cref="InvalidOperationException">The specified key is already added.</exception>
+ public void Track(T key, Action action)
+ {
+ if(this.Contains(key))
+ throw new InvalidOperationException($"Can't track context for key {key} because it's already added.");
+
+ this.Add(key);
+ try
+ {
+ action();
+ }
+ finally
+ {
+ this.Remove(key);
+ }
+ }
+
+ /// <summary>Add a key while an action is in progress, and remove it when it completes.</summary>
+ /// <typeparam name="TResult">The value type returned by the method.</typeparam>
+ /// <param name="key">The key to add.</param>
+ /// <param name="action">The action to perform.</param>
+ public TResult Track<TResult>(T key, Func<TResult> action)
+ {
+ if (this.Contains(key))
+ throw new InvalidOperationException($"Can't track context for key {key} because it's already added.");
+
+ this.Add(key);
+ try
+ {
+ return action();
+ }
+ finally
+ {
+ this.Remove(key);
+ }
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/Countdown.cs b/src/StardewModdingAPI/Framework/Utilities/Countdown.cs
index 25ca2546..921a35ce 100644
--- a/src/StardewModdingAPI/Framework/Countdown.cs
+++ b/src/StardewModdingAPI/Framework/Utilities/Countdown.cs
@@ -1,4 +1,4 @@
-namespace StardewModdingAPI.Framework
+namespace StardewModdingAPI.Framework.Utilities
{
/// <summary>Counts down from a baseline value.</summary>
internal class Countdown
diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj
index d7e10ca5..73112983 100644
--- a/src/StardewModdingAPI/StardewModdingAPI.csproj
+++ b/src/StardewModdingAPI/StardewModdingAPI.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -91,6 +91,7 @@
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Command.cs" />
+ <Compile Include="Framework\Utilities\ContextHash.cs" />
<Compile Include="Metadata\CoreAssets.cs" />
<Compile Include="ContentSource.cs" />
<Compile Include="Events\ContentEvents.cs" />
@@ -126,7 +127,7 @@
<Compile Include="Events\EventArgsStringChanged.cs" />
<Compile Include="Events\GameEvents.cs" />
<Compile Include="Events\GraphicsEvents.cs" />
- <Compile Include="Framework\Countdown.cs" />
+ <Compile Include="Framework\Utilities\Countdown.cs" />
<Compile Include="Framework\GameVersion.cs" />
<Compile Include="Framework\IModMetadata.cs" />
<Compile Include="Framework\Models\DisabledMod.cs" />