add experimental 'aggressive memory optimization' flag (#757)
This commit is contained in:
parent
b2a6933efb
commit
7e8f451876
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
## Upcoming release
|
## Upcoming release
|
||||||
* For players:
|
* For players:
|
||||||
|
* Added _aggressive memory optimization_ option. This is experimental and disabled by default; you can enable it in `smapi-internal/config.json` if you experience `OutOfMemoryException` crashes.
|
||||||
* Fixed error running `install on Windows.bat` in very rare cases.
|
* Fixed error running `install on Windows.bat` in very rare cases.
|
||||||
|
|
||||||
* For modders:
|
* For modders:
|
||||||
|
|
|
@ -26,6 +26,9 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>An asset key prefix for assets from SMAPI mod folders.</summary>
|
/// <summary>An asset key prefix for assets from SMAPI mod folders.</summary>
|
||||||
private readonly string ManagedPrefix = "SMAPI";
|
private readonly string ManagedPrefix = "SMAPI";
|
||||||
|
|
||||||
|
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
|
||||||
|
private readonly bool AggressiveMemoryOptimizations;
|
||||||
|
|
||||||
/// <summary>Encapsulates monitoring and logging.</summary>
|
/// <summary>Encapsulates monitoring and logging.</summary>
|
||||||
private readonly IMonitor Monitor;
|
private readonly IMonitor Monitor;
|
||||||
|
|
||||||
|
@ -91,8 +94,10 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <param name="reflection">Simplifies access to private code.</param>
|
/// <param name="reflection">Simplifies access to private code.</param>
|
||||||
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
|
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
|
||||||
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
|
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
|
||||||
public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset)
|
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
||||||
|
public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations)
|
||||||
{
|
{
|
||||||
|
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
||||||
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
|
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
|
||||||
this.Reflection = reflection;
|
this.Reflection = reflection;
|
||||||
this.JsonHelper = jsonHelper;
|
this.JsonHelper = jsonHelper;
|
||||||
|
@ -108,11 +113,12 @@ namespace StardewModdingAPI.Framework
|
||||||
monitor: monitor,
|
monitor: monitor,
|
||||||
reflection: reflection,
|
reflection: reflection,
|
||||||
onDisposing: this.OnDisposing,
|
onDisposing: this.OnDisposing,
|
||||||
onLoadingFirstAsset: onLoadingFirstAsset
|
onLoadingFirstAsset: onLoadingFirstAsset,
|
||||||
|
aggressiveMemoryOptimizations: aggressiveMemoryOptimizations
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
|
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
|
||||||
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager.AssertAndNormalizeAssetName, reflection);
|
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager.AssertAndNormalizeAssetName, reflection, aggressiveMemoryOptimizations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
|
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
|
||||||
|
@ -130,7 +136,8 @@ namespace StardewModdingAPI.Framework
|
||||||
monitor: this.Monitor,
|
monitor: this.Monitor,
|
||||||
reflection: this.Reflection,
|
reflection: this.Reflection,
|
||||||
onDisposing: this.OnDisposing,
|
onDisposing: this.OnDisposing,
|
||||||
onLoadingFirstAsset: this.OnLoadingFirstAsset
|
onLoadingFirstAsset: this.OnLoadingFirstAsset,
|
||||||
|
aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations
|
||||||
);
|
);
|
||||||
this.ContentManagers.Add(manager);
|
this.ContentManagers.Add(manager);
|
||||||
return manager;
|
return manager;
|
||||||
|
@ -157,7 +164,8 @@ namespace StardewModdingAPI.Framework
|
||||||
monitor: this.Monitor,
|
monitor: this.Monitor,
|
||||||
reflection: this.Reflection,
|
reflection: this.Reflection,
|
||||||
jsonHelper: this.JsonHelper,
|
jsonHelper: this.JsonHelper,
|
||||||
onDisposing: this.OnDisposing
|
onDisposing: this.OnDisposing,
|
||||||
|
aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations
|
||||||
);
|
);
|
||||||
this.ContentManagers.Add(manager);
|
this.ContentManagers.Add(manager);
|
||||||
return manager;
|
return manager;
|
||||||
|
|
|
@ -11,6 +11,7 @@ using StardewModdingAPI.Framework.Content;
|
||||||
using StardewModdingAPI.Framework.Exceptions;
|
using StardewModdingAPI.Framework.Exceptions;
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
using xTile;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework.ContentManagers
|
namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
{
|
{
|
||||||
|
@ -29,6 +30,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <summary>Encapsulates monitoring and logging.</summary>
|
/// <summary>Encapsulates monitoring and logging.</summary>
|
||||||
protected readonly IMonitor Monitor;
|
protected readonly IMonitor Monitor;
|
||||||
|
|
||||||
|
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
|
||||||
|
protected readonly bool AggressiveMemoryOptimizations;
|
||||||
|
|
||||||
/// <summary>Whether the content coordinator has been disposed.</summary>
|
/// <summary>Whether the content coordinator has been disposed.</summary>
|
||||||
private bool IsDisposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
|
@ -75,7 +79,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <param name="reflection">Simplifies access to private code.</param>
|
/// <param name="reflection">Simplifies access to private code.</param>
|
||||||
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
|
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
|
||||||
/// <param name="isNamespaced">Whether this content manager handles managed asset keys (e.g. to load assets from a mod folder).</param>
|
/// <param name="isNamespaced">Whether this content manager handles managed asset keys (e.g. to load assets from a mod folder).</param>
|
||||||
protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, bool isNamespaced)
|
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
||||||
|
protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, bool isNamespaced, bool aggressiveMemoryOptimizations)
|
||||||
: base(serviceProvider, rootDirectory, currentCulture)
|
: base(serviceProvider, rootDirectory, currentCulture)
|
||||||
{
|
{
|
||||||
// init
|
// init
|
||||||
|
@ -85,6 +90,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
|
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
|
||||||
this.OnDisposing = onDisposing;
|
this.OnDisposing = onDisposing;
|
||||||
this.IsNamespaced = isNamespaced;
|
this.IsNamespaced = isNamespaced;
|
||||||
|
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
||||||
|
|
||||||
// get asset data
|
// get asset data
|
||||||
this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.OrdinalIgnoreCase);
|
this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.OrdinalIgnoreCase);
|
||||||
|
@ -198,14 +204,22 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
{
|
{
|
||||||
this.ParseCacheKey(key, out string assetName, out _);
|
this.ParseCacheKey(key, out string assetName, out _);
|
||||||
|
|
||||||
if (removeAssets.ContainsKey(assetName))
|
// check if asset should be removed
|
||||||
return true;
|
bool remove = removeAssets.ContainsKey(assetName);
|
||||||
if (predicate(assetName, asset.GetType()))
|
if (!remove && predicate(assetName, asset.GetType()))
|
||||||
{
|
{
|
||||||
removeAssets[assetName] = asset;
|
removeAssets[assetName] = asset;
|
||||||
return true;
|
remove = true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
// dispose if safe
|
||||||
|
if (remove && this.AggressiveMemoryOptimizations)
|
||||||
|
{
|
||||||
|
if (asset is Map map)
|
||||||
|
map.DisposeTileSheets(Game1.mapDisplayDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
return remove;
|
||||||
}, dispose);
|
}, dispose);
|
||||||
|
|
||||||
return removeAssets;
|
return removeAssets;
|
||||||
|
|
|
@ -52,8 +52,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <param name="reflection">Simplifies access to private code.</param>
|
/// <param name="reflection">Simplifies access to private code.</param>
|
||||||
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
|
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
|
||||||
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
|
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
|
||||||
public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset)
|
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
||||||
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false)
|
public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations)
|
||||||
|
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
|
||||||
{
|
{
|
||||||
this.OnLoadingFirstAsset = onLoadingFirstAsset;
|
this.OnLoadingFirstAsset = onLoadingFirstAsset;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
|
||||||
/// <param name="reflection">Simplifies access to private code.</param>
|
/// <param name="reflection">Simplifies access to private code.</param>
|
||||||
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</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>
|
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
|
||||||
public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing)
|
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
||||||
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true)
|
public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing, bool aggressiveMemoryOptimizations)
|
||||||
|
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
|
||||||
{
|
{
|
||||||
this.GameContentManager = gameContentManager;
|
this.GameContentManager = gameContentManager;
|
||||||
this.JsonHelper = jsonHelper;
|
this.JsonHelper = jsonHelper;
|
||||||
|
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using StardewModdingAPI.Framework.Commands;
|
using StardewModdingAPI.Framework.Commands;
|
||||||
|
using StardewModdingAPI.Framework.Models;
|
||||||
using StardewModdingAPI.Framework.ModLoading;
|
using StardewModdingAPI.Framework.ModLoading;
|
||||||
using StardewModdingAPI.Internal.ConsoleWriting;
|
using StardewModdingAPI.Internal.ConsoleWriting;
|
||||||
using StardewModdingAPI.Toolkit.Framework.ModData;
|
using StardewModdingAPI.Toolkit.Framework.ModData;
|
||||||
|
@ -284,19 +285,24 @@ namespace StardewModdingAPI.Framework.Logging
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Log details for settings that don't match the default.</summary>
|
/// <summary>Log details for settings that don't match the default.</summary>
|
||||||
/// <param name="isDeveloperMode">Whether to enable full console output for developers.</param>
|
/// <param name="settings">The settings to log.</param>
|
||||||
/// <param name="checkForUpdates">Whether to check for newer versions of SMAPI and mods on startup.</param>
|
public void LogSettingsHeader(SConfig settings)
|
||||||
/// <param name="rewriteMods">Whether to rewrite mods for compatibility.</param>
|
|
||||||
public void LogSettingsHeader(bool isDeveloperMode, bool checkForUpdates, bool rewriteMods)
|
|
||||||
{
|
{
|
||||||
if (isDeveloperMode)
|
// developer mode
|
||||||
this.Monitor.Log("You have SMAPI for developers, so the console will be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI.", LogLevel.Info);
|
if (settings.DeveloperMode)
|
||||||
if (!checkForUpdates)
|
this.Monitor.Log("You enabled developer mode, so the console will be much more verbose. You can disable it by installing the non-developer version of SMAPI.", LogLevel.Info);
|
||||||
this.Monitor.Log("You configured SMAPI to not check for updates. Running an old version of SMAPI is not recommended. You can enable update checks by reinstalling SMAPI.", LogLevel.Warn);
|
|
||||||
if (!rewriteMods)
|
// warnings
|
||||||
this.Monitor.Log("You configured SMAPI to not rewrite broken mods. Many older mods may fail to load. You can undo this by reinstalling SMAPI.", LogLevel.Warn);
|
if (!settings.CheckForUpdates)
|
||||||
|
this.Monitor.Log("You disabled update checks, so you won't be notified of new SMAPI or mod updates. Running an old version of SMAPI is not recommended. You can undo this by reinstalling SMAPI.", LogLevel.Warn);
|
||||||
|
if (settings.AggressiveMemoryOptimizations)
|
||||||
|
this.Monitor.Log("You enabled aggressive memory optimizations. This is an experimental option which may cause errors or crashes. You can undo this by reinstalling SMAPI.", LogLevel.Warn);
|
||||||
|
if (!settings.RewriteMods)
|
||||||
|
this.Monitor.Log("You disabled rewriting broken mods, so many older mods may fail to load. You can undo this by reinstalling SMAPI.", LogLevel.Info);
|
||||||
if (!this.Monitor.WriteToConsole)
|
if (!this.Monitor.WriteToConsole)
|
||||||
this.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn);
|
this.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn);
|
||||||
|
|
||||||
|
// verbose logging
|
||||||
this.Monitor.VerboseLog("Verbose logging enabled.");
|
this.Monitor.VerboseLog("Verbose logging enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ namespace StardewModdingAPI.Framework.Models
|
||||||
[nameof(WebApiBaseUrl)] = "https://smapi.io/api/",
|
[nameof(WebApiBaseUrl)] = "https://smapi.io/api/",
|
||||||
[nameof(VerboseLogging)] = false,
|
[nameof(VerboseLogging)] = false,
|
||||||
[nameof(LogNetworkTraffic)] = false,
|
[nameof(LogNetworkTraffic)] = false,
|
||||||
[nameof(RewriteMods)] = true
|
[nameof(RewriteMods)] = true,
|
||||||
|
[nameof(AggressiveMemoryOptimizations)] = false
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>The default values for <see cref="SuppressUpdateChecks"/>, to log changes if different.</summary>
|
/// <summary>The default values for <see cref="SuppressUpdateChecks"/>, to log changes if different.</summary>
|
||||||
|
@ -60,6 +61,9 @@ namespace StardewModdingAPI.Framework.Models
|
||||||
/// <summary>Whether SMAPI should rewrite mods for compatibility.</summary>
|
/// <summary>Whether SMAPI should rewrite mods for compatibility.</summary>
|
||||||
public bool RewriteMods { get; set; } = (bool)SConfig.DefaultValues[nameof(SConfig.RewriteMods)];
|
public bool RewriteMods { get; set; } = (bool)SConfig.DefaultValues[nameof(SConfig.RewriteMods)];
|
||||||
|
|
||||||
|
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
|
||||||
|
public bool AggressiveMemoryOptimizations { get; set; } = (bool)SConfig.DefaultValues[nameof(SConfig.AggressiveMemoryOptimizations)];
|
||||||
|
|
||||||
/// <summary>Whether SMAPI should log network traffic. Best combined with <see cref="VerboseLogging"/>, which includes network metadata.</summary>
|
/// <summary>Whether SMAPI should log network traffic. Best combined with <see cref="VerboseLogging"/>, which includes network metadata.</summary>
|
||||||
public bool LogNetworkTraffic { get; set; }
|
public bool LogNetworkTraffic { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -277,7 +277,7 @@ namespace StardewModdingAPI.Framework
|
||||||
|
|
||||||
// log basic info
|
// log basic info
|
||||||
this.LogManager.HandleMarkerFiles();
|
this.LogManager.HandleMarkerFiles();
|
||||||
this.LogManager.LogSettingsHeader(this.Settings.DeveloperMode, this.Settings.CheckForUpdates, this.Settings.RewriteMods);
|
this.LogManager.LogSettingsHeader(this.Settings);
|
||||||
|
|
||||||
// set window titles
|
// set window titles
|
||||||
this.SetWindowTitles(
|
this.SetWindowTitles(
|
||||||
|
@ -1149,7 +1149,7 @@ namespace StardewModdingAPI.Framework
|
||||||
// Game1._temporaryContent initializing from SGame constructor
|
// Game1._temporaryContent initializing from SGame constructor
|
||||||
if (this.ContentCore == null)
|
if (this.ContentCore == null)
|
||||||
{
|
{
|
||||||
this.ContentCore = new ContentCoordinator(serviceProvider, rootDirectory, Thread.CurrentThread.CurrentUICulture, this.Monitor, this.Reflection, this.Toolkit.JsonHelper, this.InitializeBeforeFirstAssetLoaded);
|
this.ContentCore = new ContentCoordinator(serviceProvider, rootDirectory, Thread.CurrentThread.CurrentUICulture, this.Monitor, this.Reflection, this.Toolkit.JsonHelper, this.InitializeBeforeFirstAssetLoaded, this.Settings.AggressiveMemoryOptimizations);
|
||||||
if (this.ContentCore.Language != this.Translator.LocaleEnum)
|
if (this.ContentCore.Language != this.Translator.LocaleEnum)
|
||||||
this.Translator.SetLocale(this.ContentCore.GetLocale(), this.ContentCore.Language);
|
this.Translator.SetLocale(this.ContentCore.GetLocale(), this.ContentCore.Language);
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@ namespace StardewModdingAPI.Metadata
|
||||||
/*********
|
/*********
|
||||||
** Fields
|
** Fields
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
|
||||||
|
private readonly bool AggressiveMemoryOptimizations;
|
||||||
|
|
||||||
/// <summary>Normalizes an asset key to match the cache key and assert that it's valid.</summary>
|
/// <summary>Normalizes an asset key to match the cache key and assert that it's valid.</summary>
|
||||||
private readonly Func<string, string> AssertAndNormalizeAssetName;
|
private readonly Func<string, string> AssertAndNormalizeAssetName;
|
||||||
|
|
||||||
|
@ -55,10 +58,12 @@ namespace StardewModdingAPI.Metadata
|
||||||
/// <summary>Initialize the core asset data.</summary>
|
/// <summary>Initialize the core asset data.</summary>
|
||||||
/// <param name="assertAndNormalizeAssetName">Normalizes an asset key to match the cache key and assert that it's valid.</param>
|
/// <param name="assertAndNormalizeAssetName">Normalizes an asset key to match the cache key and assert that it's valid.</param>
|
||||||
/// <param name="reflection">Simplifies access to private code.</param>
|
/// <param name="reflection">Simplifies access to private code.</param>
|
||||||
public CoreAssetPropagator(Func<string, string> assertAndNormalizeAssetName, Reflector reflection)
|
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
|
||||||
|
public CoreAssetPropagator(Func<string, string> assertAndNormalizeAssetName, Reflector reflection, bool aggressiveMemoryOptimizations)
|
||||||
{
|
{
|
||||||
this.AssertAndNormalizeAssetName = assertAndNormalizeAssetName;
|
this.AssertAndNormalizeAssetName = assertAndNormalizeAssetName;
|
||||||
this.Reflection = reflection;
|
this.Reflection = reflection;
|
||||||
|
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Reload one of the game's core assets (if applicable).</summary>
|
/// <summary>Reload one of the game's core assets (if applicable).</summary>
|
||||||
|
@ -582,7 +587,7 @@ namespace StardewModdingAPI.Metadata
|
||||||
titleMenu.aboutButton.texture = texture;
|
titleMenu.aboutButton.texture = texture;
|
||||||
titleMenu.languageButton.texture = texture;
|
titleMenu.languageButton.texture = texture;
|
||||||
foreach (ClickableTextureComponent button in titleMenu.buttons)
|
foreach (ClickableTextureComponent button in titleMenu.buttons)
|
||||||
button.texture = titleMenu.titleButtonsTexture;
|
button.texture = texture;
|
||||||
foreach (TemporaryAnimatedSprite bird in titleMenu.birds)
|
foreach (TemporaryAnimatedSprite bird in titleMenu.birds)
|
||||||
bird.texture = texture;
|
bird.texture = texture;
|
||||||
|
|
||||||
|
@ -785,6 +790,9 @@ namespace StardewModdingAPI.Metadata
|
||||||
/// <param name="location">The location whose map to reload.</param>
|
/// <param name="location">The location whose map to reload.</param>
|
||||||
private void ReloadMap(GameLocation location)
|
private void ReloadMap(GameLocation location)
|
||||||
{
|
{
|
||||||
|
if (this.AggressiveMemoryOptimizations)
|
||||||
|
location.map.DisposeTileSheets(Game1.mapDisplayDevice);
|
||||||
|
|
||||||
// reload map
|
// reload map
|
||||||
location.interiorDoors.Clear(); // prevent errors when doors try to update tiles which no longer exist
|
location.interiorDoors.Clear(); // prevent errors when doors try to update tiles which no longer exist
|
||||||
location.reloadMap();
|
location.reloadMap();
|
||||||
|
@ -843,7 +851,7 @@ namespace StardewModdingAPI.Metadata
|
||||||
// update sprite
|
// update sprite
|
||||||
foreach (var target in characters)
|
foreach (var target in characters)
|
||||||
{
|
{
|
||||||
target.Npc.Sprite.spriteTexture = content.Load<Texture2D>(target.Key);
|
target.Npc.Sprite.spriteTexture = this.DisposeIfNeeded(target.Npc.Sprite.spriteTexture, content.Load<Texture2D>(target.Key));
|
||||||
propagated[target.Key] = true;
|
propagated[target.Key] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -881,7 +889,7 @@ namespace StardewModdingAPI.Metadata
|
||||||
// update portrait
|
// update portrait
|
||||||
foreach (var target in characters)
|
foreach (var target in characters)
|
||||||
{
|
{
|
||||||
target.Npc.Portrait = content.Load<Texture2D>(target.Key);
|
target.Npc.Portrait = this.DisposeIfNeeded(target.Npc.Portrait, content.Load<Texture2D>(target.Key));
|
||||||
propagated[target.Key] = true;
|
propagated[target.Key] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1146,5 +1154,16 @@ namespace StardewModdingAPI.Metadata
|
||||||
{
|
{
|
||||||
return this.GetSegments(path).Length;
|
return this.GetSegments(path).Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Dispose a texture if <see cref="AggressiveMemoryOptimizations"/> are enabled and it's different from the new instance.</summary>
|
||||||
|
/// <param name="oldTexture">The previous texture to dispose.</param>
|
||||||
|
/// <param name="newTexture">The new texture being loaded.</param>
|
||||||
|
private Texture2D DisposeIfNeeded(Texture2D oldTexture, Texture2D newTexture)
|
||||||
|
{
|
||||||
|
if (this.AggressiveMemoryOptimizations && oldTexture != null && !oldTexture.IsDisposed && !object.ReferenceEquals(oldTexture, newTexture))
|
||||||
|
oldTexture.Dispose();
|
||||||
|
|
||||||
|
return newTexture;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,12 @@ copy all the settings, or you may cause bugs due to overridden changes in future
|
||||||
*/
|
*/
|
||||||
"RewriteMods": true,
|
"RewriteMods": true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable more aggressive memory optimizations.
|
||||||
|
* THIS IS EXPERIMENTAL AND MAY CAUSE ERRORS OR CRASHES.
|
||||||
|
*/
|
||||||
|
"AggressiveMemoryOptimizations": true,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to add a section to the 'mod issues' list for mods which directly use potentially
|
* Whether to add a section to the 'mod issues' list for mods which directly use potentially
|
||||||
* sensitive .NET APIs like file or shell access. Note that many mods do this legitimately as
|
* sensitive .NET APIs like file or shell access. Note that many mods do this legitimately as
|
||||||
|
|
Loading…
Reference in New Issue