diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2017-08-17 17:46:45 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2017-08-17 17:46:45 -0400 |
commit | 723ddc255e1c2b399dfb734306fd00912a741e62 (patch) | |
tree | 387def74bf23cfc686ef5cf4c42226cce58214dd | |
parent | 9e1d01d4feb4107448840f30fa39e1c827bf5fdc (diff) | |
download | SMAPI-723ddc255e1c2b399dfb734306fd00912a741e62.tar.gz SMAPI-723ddc255e1c2b399dfb734306fd00912a741e62.tar.bz2 SMAPI-723ddc255e1c2b399dfb734306fd00912a741e62.zip |
break loops when loading assets through a mod loader
-rw-r--r-- | src/StardewModdingAPI/Framework/SContentManager.cs | 22 | ||||
-rw-r--r-- | src/StardewModdingAPI/Framework/SGame.cs | 5 | ||||
-rw-r--r-- | src/StardewModdingAPI/Framework/Utilities/ContextHash.cs | 62 | ||||
-rw-r--r-- | src/StardewModdingAPI/Framework/Utilities/Countdown.cs (renamed from src/StardewModdingAPI/Framework/Countdown.cs) | 2 | ||||
-rw-r--r-- | src/StardewModdingAPI/StardewModdingAPI.csproj | 5 |
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" /> |