diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2017-08-21 16:39:21 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2017-08-21 16:39:21 -0400 |
commit | 8ba54a682fd7de3756b6ddd262b232cf40d23ea0 (patch) | |
tree | 7e44d53b733d95b05b232da46306a0d3d57e9231 /src/StardewModdingAPI/Framework/ModHelpers | |
parent | 674ad0d90f8780130a5fcefb3869acfe2315df2a (diff) | |
parent | eea5100acea0bceaf440f9d1bd50ee2b24cf8ebc (diff) | |
download | SMAPI-8ba54a682fd7de3756b6ddd262b232cf40d23ea0.tar.gz SMAPI-8ba54a682fd7de3756b6ddd262b232cf40d23ea0.tar.bz2 SMAPI-8ba54a682fd7de3756b6ddd262b232cf40d23ea0.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/StardewModdingAPI/Framework/ModHelpers')
-rw-r--r-- | src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs | 38 | ||||
-rw-r--r-- | src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs | 46 |
2 files changed, 78 insertions, 6 deletions
diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs index 5f72176e..ffa78ff6 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; @@ -33,10 +33,19 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <summary>The friendly mod name for use in errors.</summary> private readonly string ModName; + /// <summary>Encapsulates monitoring and logging for a given module.</summary> + private readonly IMonitor Monitor; + /********* ** Accessors *********/ + /// <summary>The game's current locale code (like <c>pt-BR</c>).</summary> + public string CurrentLocale => this.ContentManager.GetLocale(); + + /// <summary>The game's current locale as an enum value.</summary> + public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.ContentManager.GetCurrentLanguage(); + /// <summary>The observable implementation of <see cref="AssetEditors"/>.</summary> internal ObservableCollection<IAssetEditor> ObservableAssetEditors { get; } = new ObservableCollection<IAssetEditor>(); @@ -44,10 +53,10 @@ namespace StardewModdingAPI.Framework.ModHelpers internal ObservableCollection<IAssetLoader> ObservableAssetLoaders { get; } = new ObservableCollection<IAssetLoader>(); /// <summary>Interceptors which provide the initial versions of matching content assets.</summary> - internal IList<IAssetLoader> AssetLoaders => this.ObservableAssetLoaders; + public IList<IAssetLoader> AssetLoaders => this.ObservableAssetLoaders; /// <summary>Interceptors which edit matching content assets after they're loaded.</summary> - internal IList<IAssetEditor> AssetEditors => this.ObservableAssetEditors; + public IList<IAssetEditor> AssetEditors => this.ObservableAssetEditors; /********* @@ -58,13 +67,15 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="modFolderPath">The absolute path to the mod folder.</param> /// <param name="modID">The unique ID of the relevant mod.</param> /// <param name="modName">The friendly mod name for use in errors.</param> - public ContentHelper(SContentManager contentManager, string modFolderPath, string modID, string modName) + /// <param name="monitor">Encapsulates monitoring and logging.</param> + public ContentHelper(SContentManager contentManager, string modFolderPath, string modID, string modName, IMonitor monitor) : base(modID) { this.ContentManager = contentManager; this.ModFolderPath = modFolderPath; this.ModName = modName; this.ModFolderPathFromContent = this.GetRelativePath(contentManager.FullRootDirectory, modFolderPath); + this.Monitor = monitor; } /// <summary>Load content from the game folder or mod 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> @@ -176,6 +187,25 @@ namespace StardewModdingAPI.Framework.ModHelpers } } + /// <summary>Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content.</summary> + /// <param name="key">The asset key to invalidate in the content folder.</param> + /// <exception cref="ArgumentException">The <paramref name="key"/> is empty or contains invalid characters.</exception> + /// <returns>Returns whether the given asset key was cached.</returns> + public bool InvalidateCache(string key) + { + this.Monitor.Log($"Requested cache invalidation for '{key}'.", LogLevel.Trace); + string actualKey = this.GetActualAssetKey(key, ContentSource.GameContent); + return this.ContentManager.InvalidateCache((otherKey, type) => otherKey.Equals(actualKey, StringComparison.InvariantCultureIgnoreCase)); + } + + /// <summary>Remove all assets of the given type from the cache so they're reloaded on the next request. <b>This can be a very expensive operation and should only be used in very specific cases.</b> This will reload core game assets if needed, but references to the former assets will still show the previous content.</summary> + /// <typeparam name="T">The asset type to remove from the cache.</typeparam> + /// <returns>Returns whether any assets were invalidated.</returns> + public bool InvalidateCache<T>() + { + this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.", LogLevel.Trace); + return this.ContentManager.InvalidateCache((key, type) => typeof(T).IsAssignableFrom(type)); + } /********* ** Private methods diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs index 9411a97a..14a339da 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using StardewModdingAPI.Framework.Reflection; namespace StardewModdingAPI.Framework.ModHelpers @@ -13,16 +13,21 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <summary>The underlying reflection helper.</summary> private readonly Reflector Reflector; + /// <summary>The mod name for error messages.</summary> + private readonly string ModName; + /********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="modID">The unique ID of the relevant mod.</param> + /// <param name="modName">The mod name for error messages.</param> /// <param name="reflector">The underlying reflection helper.</param> - public ReflectionHelper(string modID, Reflector reflector) + public ReflectionHelper(string modID, string modName, Reflector reflector) : base(modID) { + this.ModName = modName; this.Reflector = reflector; } @@ -37,6 +42,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <returns>Returns the field wrapper, or <c>null</c> if the field doesn't exist and <paramref name="required"/> is <c>false</c>.</returns> public IPrivateField<TValue> GetPrivateField<TValue>(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateField<TValue>(obj, name, required); } @@ -47,6 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private field is not found.</param> public IPrivateField<TValue> GetPrivateField<TValue>(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateField<TValue>(type, name, required); } @@ -60,6 +67,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private property is not found.</param> public IPrivateProperty<TValue> GetPrivateProperty<TValue>(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateProperty<TValue>(obj, name, required); } @@ -70,6 +78,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private property is not found.</param> public IPrivateProperty<TValue> GetPrivateProperty<TValue>(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateProperty<TValue>(type, name, required); } @@ -89,6 +98,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// </remarks> public TValue GetPrivateValue<TValue>(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); IPrivateField<TValue> field = this.GetPrivateField<TValue>(obj, name, required); return field != null ? field.GetValue() @@ -107,6 +117,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// </remarks> public TValue GetPrivateValue<TValue>(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); IPrivateField<TValue> field = this.GetPrivateField<TValue>(type, name, required); return field != null ? field.GetValue() @@ -122,6 +133,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private field is not found.</param> public IPrivateMethod GetPrivateMethod(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateMethod(obj, name, required); } @@ -131,6 +143,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private field is not found.</param> public IPrivateMethod GetPrivateMethod(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateMethod(type, name, required); } @@ -144,6 +157,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private field is not found.</param> public IPrivateMethod GetPrivateMethod(object obj, string name, Type[] argumentTypes, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateMethod(obj, name, argumentTypes, required); } @@ -154,7 +168,35 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="required">Whether to throw an exception if the private field is not found.</param> public IPrivateMethod GetPrivateMethod(Type type, string name, Type[] argumentTypes, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateMethod(type, name, argumentTypes, required); } + + + /********* + ** Private methods + *********/ + /// <summary>Assert that mods can use the reflection helper to access the given type.</summary> + /// <param name="type">The type being accessed.</param> + private void AssertAccessAllowed(Type type) + { +#if !SMAPI_1_x + // validate type namespace + if (type.Namespace != null) + { + string rootSmapiNamespace = typeof(Program).Namespace; + if (type.Namespace == rootSmapiNamespace || type.Namespace.StartsWith(rootSmapiNamespace + ".")) + throw new InvalidOperationException($"SMAPI blocked access by {this.ModName} to its internals through the reflection API. Accessing the SMAPI internals is strongly discouraged since they're subject to change, which means the mod can break without warning."); + } +#endif + } + + /// <summary>Assert that mods can use the reflection helper to access the given type.</summary> + /// <param name="obj">The object being accessed.</param> + private void AssertAccessAllowed(object obj) + { + if (obj != null) + this.AssertAccessAllowed(obj.GetType()); + } } } |