diff --git a/docs/release-notes.md b/docs/release-notes.md
index 11cccee2..2fff0c58 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -3,8 +3,10 @@
# Release notes
## Upcoming release
* For players:
- * Case-insensitive file paths (introduced in 3.14.0) are now disabled by default.
- _You can enable them via `smapi-internal/config.json` if needed. These will be re-enabled in a later version after reworking them to reduce performance impact._
+ * Disabled case-insensitive file paths (introduced in 3.14.0) by default.
+ _You can enable them by editing `smapi-internal/config.json` if needed. They'll be re-enabled in a later version after they're reworked to reduce performance impact._
+ * Removed experimental 'aggressive memory optimizations' option.
+ _This was disabled by default and is no longer needed in most cases. Memory usage will be better reduced by reworked asset propagation in the upcoming SMAPI 4.0.0._
* Updated compatibility list.
## 3.14.0
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 1a82d194..ef442fbe 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -32,9 +32,6 @@ namespace StardewModdingAPI.Framework
/// An asset key prefix for assets from SMAPI mod folders.
private readonly string ManagedPrefix = "SMAPI";
- /// Whether to enable more aggressive memory optimizations.
- private readonly bool AggressiveMemoryOptimizations;
-
/// Get a file path lookup for the given directory.
private readonly Func GetFilePathLookup;
@@ -118,13 +115,11 @@ namespace StardewModdingAPI.Framework
/// Encapsulates SMAPI's JSON file parsing.
/// A callback to invoke the first time *any* game content manager loads an asset.
/// A callback to invoke when an asset is fully loaded.
- /// Whether to enable more aggressive memory optimizations.
/// Get a file path lookup for the given directory.
/// A callback to invoke when any asset names have been invalidated from the cache.
/// Get the load/edit operations to apply to an asset by querying registered event handlers.
- public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations, Func getFilePathLookup, Action> onAssetsInvalidated, Func> requestAssetOperations)
+ public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action onAssetLoaded, Func getFilePathLookup, Action> onAssetsInvalidated, Func> requestAssetOperations)
{
- this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
this.GetFilePathLookup = getFilePathLookup;
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
this.Reflection = reflection;
@@ -145,26 +140,11 @@ namespace StardewModdingAPI.Framework
reflection: reflection,
onDisposing: this.OnDisposing,
onLoadingFirstAsset: onLoadingFirstAsset,
- onAssetLoaded: onAssetLoaded,
- aggressiveMemoryOptimizations: aggressiveMemoryOptimizations
+ onAssetLoaded: onAssetLoaded
)
);
- var contentManagerForAssetPropagation = new GameContentManagerForAssetPropagation(
- name: nameof(GameContentManagerForAssetPropagation),
- serviceProvider: serviceProvider,
- rootDirectory: rootDirectory,
- currentCulture: currentCulture,
- coordinator: this,
- monitor: monitor,
- reflection: reflection,
- onDisposing: this.OnDisposing,
- onLoadingFirstAsset: onLoadingFirstAsset,
- onAssetLoaded: onAssetLoaded,
- aggressiveMemoryOptimizations: aggressiveMemoryOptimizations
- );
- this.ContentManagers.Add(contentManagerForAssetPropagation);
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
- this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations, name => this.ParseAssetName(name, allowLocales: true));
+ this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, this.Monitor, reflection, name => this.ParseAssetName(name, allowLocales: true));
this.LocaleCodes = new Lazy>(() => this.GetLocaleCodes(customLanguages: Enumerable.Empty()));
}
@@ -184,8 +164,7 @@ namespace StardewModdingAPI.Framework
reflection: this.Reflection,
onDisposing: this.OnDisposing,
onLoadingFirstAsset: this.OnLoadingFirstAsset,
- onAssetLoaded: this.OnAssetLoaded,
- aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations
+ onAssetLoaded: this.OnAssetLoaded
);
this.ContentManagers.Add(manager);
return manager;
@@ -213,7 +192,6 @@ namespace StardewModdingAPI.Framework
reflection: this.Reflection,
jsonHelper: this.JsonHelper,
onDisposing: this.OnDisposing,
- aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations,
relativePathLookup: this.GetFilePathLookup(rootDirectory)
);
this.ContentManagers.Add(manager);
diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
index b2e3ec0f..db2934a0 100644
--- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
@@ -11,7 +11,6 @@ using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
using StardewValley;
-using xTile;
namespace StardewModdingAPI.Framework.ContentManagers
{
@@ -33,9 +32,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// Simplifies access to private code.
protected readonly Reflector Reflection;
- /// Whether to enable more aggressive memory optimizations.
- protected readonly bool AggressiveMemoryOptimizations;
-
/// Whether to automatically try resolving keys to a localized form if available.
protected bool TryLocalizeKeys = true;
@@ -82,8 +78,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// Simplifies access to private code.
/// A callback to invoke when the content manager is being disposed.
/// Whether this content manager handles managed asset keys (e.g. to load assets from a mod folder).
- /// Whether to enable more aggressive memory optimizations.
- protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, bool isNamespaced, bool aggressiveMemoryOptimizations)
+ protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, bool isNamespaced)
: base(serviceProvider, rootDirectory, currentCulture)
{
// init
@@ -95,7 +90,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
this.Reflection = reflection;
this.OnDisposing = onDisposing;
this.IsNamespaced = isNamespaced;
- this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
// get asset data
this.BaseDisposableReferences = reflection.GetField?>(this, "disposableAssets").GetValue()
@@ -231,14 +225,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
removeAssets[baseAssetName] = asset;
remove = true;
}
-
- // dispose if safe
- if (remove && this.AggressiveMemoryOptimizations)
- {
- if (asset is Map map)
- map.DisposeTileSheets(Game1.mapDisplayDevice);
- }
-
return remove;
}, dispose);
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 083df454..c53040e1 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -51,9 +51,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// A callback to invoke when the content manager is being disposed.
/// A callback to invoke the first time *any* game content manager loads an asset.
/// A callback to invoke when an asset is fully loaded.
- /// Whether to enable more aggressive memory optimizations.
- public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations)
- : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
+ public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, Action onAssetLoaded)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false)
{
this.OnLoadingFirstAsset = onLoadingFirstAsset;
this.OnAssetLoaded = onAssetLoaded;
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
index 1b0e1016..5c574a1a 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
@@ -21,8 +21,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
** Public methods
*********/
///
- public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, Action onAssetLoaded, bool aggressiveMemoryOptimizations)
- : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, onAssetLoaded, aggressiveMemoryOptimizations) { }
+ public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action onDisposing, Action onLoadingFirstAsset, Action onAssetLoaded)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, onAssetLoaded) { }
///
public override T LoadExact(IAssetName assetName, bool useCache)
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index 91de769f..65dffd8b 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -55,10 +55,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// Simplifies access to private code.
/// Encapsulates SMAPI's JSON file parsing.
/// A callback to invoke when the content manager is being disposed.
- /// Whether to enable more aggressive memory optimizations.
/// A lookup for relative paths within the .
- public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onDisposing, bool aggressiveMemoryOptimizations, IFilePathLookup relativePathLookup)
- : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
+ public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onDisposing, IFilePathLookup relativePathLookup)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true)
{
this.GameContentManager = gameContentManager;
this.RelativePathLookup = relativePathLookup;
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index b4184abb..80d0d9ba 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -22,7 +22,6 @@ namespace StardewModdingAPI.Framework.Models
[nameof(VerboseLogging)] = false,
[nameof(LogNetworkTraffic)] = false,
[nameof(RewriteMods)] = true,
- [nameof(AggressiveMemoryOptimizations)] = false,
[nameof(UsePintail)] = true,
[nameof(UseCaseInsensitivePaths)] = false
};
@@ -63,9 +62,6 @@ namespace StardewModdingAPI.Framework.Models
/// Whether SMAPI should rewrite mods for compatibility.
public bool RewriteMods { get; }
- /// Whether to enable more aggressive memory optimizations.
- public bool AggressiveMemoryOptimizations { get; }
-
/// Whether to use the experimental Pintail API proxying library, instead of the original proxying built into SMAPI itself.
public bool UsePintail { get; }
@@ -94,13 +90,12 @@ namespace StardewModdingAPI.Framework.Models
/// The base URL for SMAPI's web API, used to perform update checks.
/// Whether SMAPI should log more information about the game context.
/// Whether SMAPI should rewrite mods for compatibility.
- /// Whether to enable more aggressive memory optimizations.
/// Whether to use the experimental Pintail API proxying library, instead of the original proxying built into SMAPI itself.
/// >Whether to make SMAPI file APIs case-insensitive, even on Linux.
/// Whether SMAPI should log network traffic.
/// The colors to use for text written to the SMAPI console.
/// The mod IDs SMAPI should ignore when performing update checks or validating update keys.
- public SConfig(bool developerMode, bool checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, bool verboseLogging, bool? rewriteMods, bool? aggressiveMemoryOptimizations, bool? usePintail, bool? useCaseInsensitivePaths, bool logNetworkTraffic, ColorSchemeConfig consoleColors, string[]? suppressUpdateChecks)
+ public SConfig(bool developerMode, bool checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, bool verboseLogging, bool? rewriteMods, bool? usePintail, bool? useCaseInsensitivePaths, bool logNetworkTraffic, ColorSchemeConfig consoleColors, string[]? suppressUpdateChecks)
{
this.DeveloperMode = developerMode;
this.CheckForUpdates = checkForUpdates;
@@ -110,7 +105,6 @@ namespace StardewModdingAPI.Framework.Models
this.WebApiBaseUrl = webApiBaseUrl;
this.VerboseLogging = verboseLogging;
this.RewriteMods = rewriteMods ?? (bool)SConfig.DefaultValues[nameof(SConfig.RewriteMods)];
- this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations ?? (bool)SConfig.DefaultValues[nameof(SConfig.AggressiveMemoryOptimizations)];
this.UsePintail = usePintail ?? (bool)SConfig.DefaultValues[nameof(SConfig.UsePintail)];
this.UseCaseInsensitivePaths = useCaseInsensitivePaths ?? (bool)SConfig.DefaultValues[nameof(SConfig.UseCaseInsensitivePaths)];
this.LogNetworkTraffic = logNetworkTraffic;
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 50765083..a9296d9b 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -1253,7 +1253,6 @@ namespace StardewModdingAPI.Framework
onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded,
onAssetLoaded: this.OnAssetLoaded,
onAssetsInvalidated: this.OnAssetsInvalidated,
- aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations,
getFilePathLookup: this.GetFilePathLookup,
requestAssetOperations: this.RequestAssetOperations
);
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index 5dee2c4d..12b73515 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
-using StardewModdingAPI.Framework.ContentManagers;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Internal;
using StardewModdingAPI.Toolkit.Utilities;
@@ -33,18 +32,12 @@ namespace StardewModdingAPI.Metadata
/// The main content manager through which to reload assets.
private readonly LocalizedContentManager MainContentManager;
- /// An internal content manager used only for asset propagation. See remarks on .
- private readonly GameContentManagerForAssetPropagation DisposableContentManager;
-
/// Writes messages to the console.
private readonly IMonitor Monitor;
/// Simplifies access to private game code.
private readonly Reflector Reflection;
- /// Whether to enable more aggressive memory optimizations.
- private readonly bool AggressiveMemoryOptimizations;
-
/// Parse a raw asset name.
private readonly Func ParseAssetName;
@@ -67,18 +60,14 @@ namespace StardewModdingAPI.Metadata
*********/
/// Initialize the core asset data.
/// The main content manager through which to reload assets.
- /// An internal content manager used only for asset propagation.
/// Writes messages to the console.
/// Simplifies access to private code.
- /// Whether to enable more aggressive memory optimizations.
/// Parse a raw asset name.
- public CoreAssetPropagator(LocalizedContentManager mainContent, GameContentManagerForAssetPropagation disposableContent, IMonitor monitor, Reflector reflection, bool aggressiveMemoryOptimizations, Func parseAssetName)
+ public CoreAssetPropagator(LocalizedContentManager mainContent, IMonitor monitor, Reflector reflection, Func parseAssetName)
{
this.MainContentManager = mainContent;
- this.DisposableContentManager = disposableContent;
this.Monitor = monitor;
this.Reflection = reflection;
- this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
this.ParseAssetName = parseAssetName;
}
@@ -230,7 +219,7 @@ namespace StardewModdingAPI.Metadata
** Buildings
****/
case "buildings/houses": // Farm
- Farm.houseTextures = this.LoadAndDisposeIfNeeded(Farm.houseTextures, key);
+ Farm.houseTextures = this.LoadTexture(key);
return true;
case "buildings/houses_paintmask": // Farm
@@ -247,7 +236,7 @@ namespace StardewModdingAPI.Metadata
** Content\Characters\Farmer
****/
case "characters/farmer/accessories": // Game1.LoadContent
- FarmerRenderer.accessoriesTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.accessoriesTexture, key);
+ FarmerRenderer.accessoriesTexture = this.LoadTexture(key);
return true;
case "characters/farmer/farmer_base": // Farmer
@@ -257,19 +246,19 @@ namespace StardewModdingAPI.Metadata
return !ignoreWorld && this.ReloadPlayerSprites(assetName);
case "characters/farmer/hairstyles": // Game1.LoadContent
- FarmerRenderer.hairStylesTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.hairStylesTexture, key);
+ FarmerRenderer.hairStylesTexture = this.LoadTexture(key);
return true;
case "characters/farmer/hats": // Game1.LoadContent
- FarmerRenderer.hatsTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.hatsTexture, key);
+ FarmerRenderer.hatsTexture = this.LoadTexture(key);
return true;
case "characters/farmer/pants": // Game1.LoadContent
- FarmerRenderer.pantsTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.pantsTexture, key);
+ FarmerRenderer.pantsTexture = this.LoadTexture(key);
return true;
case "characters/farmer/shirts": // Game1.LoadContent
- FarmerRenderer.shirtsTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.shirtsTexture, key);
+ FarmerRenderer.shirtsTexture = this.LoadTexture(key);
return true;
/****
@@ -905,9 +894,6 @@ namespace StardewModdingAPI.Metadata
GameLocation location = locationInfo.Location;
Vector2? playerPos = Game1.player?.Position;
- if (this.AggressiveMemoryOptimizations)
- location.map.DisposeTileSheets(Game1.mapDisplayDevice);
-
// reload map
location.interiorDoors.Clear(); // prevent errors when doors try to update tiles which no longer exist
location.reloadMap();
@@ -973,7 +959,7 @@ namespace StardewModdingAPI.Metadata
// update sprite
foreach (var target in characters)
{
- target.Npc.Sprite.spriteTexture = this.LoadAndDisposeIfNeeded(target.Npc.Sprite.spriteTexture, target.AssetName.BaseName);
+ target.Npc.Sprite.spriteTexture = this.LoadTexture(target.AssetName.BaseName);
propagated[target.AssetName] = true;
}
}
@@ -1012,7 +998,7 @@ namespace StardewModdingAPI.Metadata
// update portrait
foreach (var target in characters)
{
- target.Npc.Portrait = this.LoadAndDisposeIfNeeded(target.Npc.Portrait, target.AssetName.BaseName);
+ target.Npc.Portrait = this.LoadTexture(target.AssetName.BaseName);
propagated[target.AssetName] = true;
}
}
@@ -1284,25 +1270,10 @@ namespace StardewModdingAPI.Metadata
: Array.Empty();
}
- /// Load a texture, and dispose the old one if is enabled and it's different from the new instance.
- /// The previous texture to dispose.
+ /// Load a texture from the main content manager.
/// The asset key to load.
- private Texture2D LoadAndDisposeIfNeeded(Texture2D? oldTexture, string key)
+ private Texture2D LoadTexture(string key)
{
- // if aggressive memory optimizations are enabled, load the asset from the disposable
- // content manager and dispose the old instance if needed.
- if (this.AggressiveMemoryOptimizations)
- {
- GameContentManagerForAssetPropagation content = this.DisposableContentManager;
-
- Texture2D newTexture = content.Load(key);
- if (oldTexture?.IsDisposed == false && !object.ReferenceEquals(oldTexture, newTexture) && content.IsResponsibleFor(oldTexture))
- oldTexture.Dispose();
-
- return newTexture;
- }
-
- // else just (re)load it from the main content manager
return this.MainContentManager.Load(key);
}
diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json
index 544aeaec..bdd6374a 100644
--- a/src/SMAPI/SMAPI.config.json
+++ b/src/SMAPI/SMAPI.config.json
@@ -39,13 +39,6 @@ copy all the settings, or you may cause bugs due to overridden changes in future
*/
"RewriteMods": true,
- /**
- * Whether to enable more aggressive memory optimizations.
- * If you get frequent 'OutOfMemoryException' errors, you can try enabling this to reduce their
- * frequency. This may cause crashes for farmhands in multiplayer.
- */
- "AggressiveMemoryOptimizations": false,
-
/**
* Whether to make SMAPI file APIs case-insensitive, even on Linux.
* This is experimental, and the initial implementation may impact load times.