diff options
Diffstat (limited to 'src/SMAPI/Framework')
| -rw-r--r-- | src/SMAPI/Framework/Content/AssetDataForMap.cs | 90 | ||||
| -rw-r--r-- | src/SMAPI/Framework/Content/ContentCache.cs | 21 | ||||
| -rw-r--r-- | src/SMAPI/Framework/ContentCoordinator.cs | 41 | ||||
| -rw-r--r-- | src/SMAPI/Framework/ContentManagers/BaseContentManager.cs | 17 | ||||
| -rw-r--r-- | src/SMAPI/Framework/ContentManagers/GameContentManager.cs | 2 | ||||
| -rw-r--r-- | src/SMAPI/Framework/ContentManagers/ModContentManager.cs | 14 | ||||
| -rw-r--r-- | src/SMAPI/Framework/Input/GamePadStateBuilder.cs | 19 | ||||
| -rw-r--r-- | src/SMAPI/Framework/InternalExtensions.cs | 7 | ||||
| -rw-r--r-- | src/SMAPI/Framework/Logging/LogManager.cs | 33 | ||||
| -rw-r--r-- | src/SMAPI/Framework/ModLoading/AssemblyLoader.cs | 5 | ||||
| -rw-r--r-- | src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs | 10 | ||||
| -rw-r--r-- | src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs | 2 | ||||
| -rw-r--r-- | src/SMAPI/Framework/SCore.cs | 33 | ||||
| -rw-r--r-- | src/SMAPI/Framework/SGame.cs | 162 | ||||
| -rw-r--r-- | src/SMAPI/Framework/SModHooks.cs | 30 |
15 files changed, 241 insertions, 245 deletions
diff --git a/src/SMAPI/Framework/Content/AssetDataForMap.cs b/src/SMAPI/Framework/Content/AssetDataForMap.cs index 4f810948..0a5fa7e7 100644 --- a/src/SMAPI/Framework/Content/AssetDataForMap.cs +++ b/src/SMAPI/Framework/Content/AssetDataForMap.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using StardewModdingAPI.Toolkit.Utilities; +using StardewValley; using xTile; using xTile.Layers; using xTile.Tiles; @@ -25,18 +26,17 @@ namespace StardewModdingAPI.Framework.Content : base(locale, assetName, data, getNormalizedPath, onDataReplaced) { } /// <inheritdoc /> - /// <remarks>Derived from <see cref="StardewValley.GameLocation.ApplyMapOverride"/> with a few changes: + /// <remarks>Derived from <see cref="GameLocation.ApplyMapOverride(Map,string,Rectangle?,Rectangle?)"/> with a few changes: /// - can be applied directly to the maps when loading, before the location is created; - /// - added support for source/target areas; + /// - added support for patch modes (overlay, replace by layer, or fully replace); /// - added disambiguation if source has a modified version of the same tilesheet, instead of copying tiles into the target tilesheet; - /// - changed to always overwrite tiles within the target area (to avoid edge cases where some tiles are only partly applied); /// - fixed copying tilesheets (avoid "The specified TileSheet was not created for use with this map" error); /// - fixed tilesheets not added at the end (via z_ prefix), which can cause crashes in game code which depends on hardcoded tilesheet indexes; /// - fixed issue where different tilesheets are linked by ID. /// </remarks> - public void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null) + public void PatchMap(Map source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMapMode patchMode = PatchMapMode.Overlay) { - var target = this.Data; + Map target = this.Data; // get areas { @@ -84,10 +84,13 @@ namespace StardewModdingAPI.Framework.Content tilesheetMap[sourceSheet] = targetSheet; } - // get layer map - IDictionary<Layer, Layer> layerMap = source.Layers.ToDictionary(p => p, p => target.GetLayer(p.Id)); + // get target layers + IDictionary<Layer, Layer> sourceToTargetLayers = source.Layers.ToDictionary(p => p, p => target.GetLayer(p.Id)); + HashSet<Layer> orphanedTargetLayers = new HashSet<Layer>(target.Layers.Except(sourceToTargetLayers.Values)); // apply tiles + bool replaceAll = patchMode == PatchMapMode.Replace; + bool replaceByLayer = patchMode == PatchMapMode.ReplaceByLayer; for (int x = 0; x < sourceArea.Value.Width; x++) { for (int y = 0; y < sourceArea.Value.Height; y++) @@ -96,47 +99,37 @@ namespace StardewModdingAPI.Framework.Content Point sourcePos = new Point(sourceArea.Value.X + x, sourceArea.Value.Y + y); Point targetPos = new Point(targetArea.Value.X + x, targetArea.Value.Y + y); + // replace tiles on target-only layers + if (replaceAll) + { + foreach (Layer targetLayer in orphanedTargetLayers) + targetLayer.Tiles[targetPos.X, targetPos.Y] = null; + } + // merge layers foreach (Layer sourceLayer in source.Layers) { // get layer - Layer targetLayer = layerMap[sourceLayer]; + Layer targetLayer = sourceToTargetLayers[sourceLayer]; if (targetLayer == null) { target.AddLayer(targetLayer = new Layer(sourceLayer.Id, target, target.Layers[0].LayerSize, Layer.m_tileSize)); - layerMap[sourceLayer] = target.GetLayer(sourceLayer.Id); + sourceToTargetLayers[sourceLayer] = target.GetLayer(sourceLayer.Id); } // copy layer properties targetLayer.Properties.CopyFrom(sourceLayer.Properties); - // copy tiles + // create new tile Tile sourceTile = sourceLayer.Tiles[sourcePos.X, sourcePos.Y]; - Tile targetTile; - switch (sourceTile) - { - case StaticTile _: - targetTile = new StaticTile(targetLayer, tilesheetMap[sourceTile.TileSheet], sourceTile.BlendMode, sourceTile.TileIndex); - break; - - case AnimatedTile animatedTile: - { - StaticTile[] tileFrames = new StaticTile[animatedTile.TileFrames.Length]; - for (int frame = 0; frame < animatedTile.TileFrames.Length; ++frame) - { - StaticTile frameTile = animatedTile.TileFrames[frame]; - tileFrames[frame] = new StaticTile(targetLayer, tilesheetMap[frameTile.TileSheet], frameTile.BlendMode, frameTile.TileIndex); - } - targetTile = new AnimatedTile(targetLayer, tileFrames, animatedTile.FrameInterval); - } - break; - - default: // null or unhandled type - targetTile = null; - break; - } - targetTile?.Properties.CopyFrom(sourceTile.Properties); - targetLayer.Tiles[targetPos.X, targetPos.Y] = targetTile; + Tile newTile = sourceTile != null + ? this.CreateTile(sourceTile, targetLayer, tilesheetMap[sourceTile.TileSheet]) + : null; + newTile?.Properties.CopyFrom(sourceTile.Properties); + + // replace tile + if (newTile != null || replaceByLayer || replaceAll) + targetLayer.Tiles[targetPos.X, targetPos.Y] = newTile; } } } @@ -146,6 +139,33 @@ namespace StardewModdingAPI.Framework.Content /********* ** Private methods *********/ + /// <summary>Create a new tile for the target map.</summary> + /// <param name="sourceTile">The source tile to copy.</param> + /// <param name="targetLayer">The target layer.</param> + /// <param name="targetSheet">The target tilesheet.</param> + private Tile CreateTile(Tile sourceTile, Layer targetLayer, TileSheet targetSheet) + { + switch (sourceTile) + { + case StaticTile _: + return new StaticTile(targetLayer, targetSheet, sourceTile.BlendMode, sourceTile.TileIndex); + + case AnimatedTile animatedTile: + { + StaticTile[] tileFrames = new StaticTile[animatedTile.TileFrames.Length]; + for (int frame = 0; frame < animatedTile.TileFrames.Length; ++frame) + { + StaticTile frameTile = animatedTile.TileFrames[frame]; + tileFrames[frame] = new StaticTile(targetLayer, targetSheet, frameTile.BlendMode, frameTile.TileIndex); + } + + return new AnimatedTile(targetLayer, tileFrames, animatedTile.FrameInterval); + } + + default: // null or unhandled type + return null; + } + } /// <summary>Normalize a map tilesheet path for comparison. This value should *not* be used as the actual tilesheet path.</summary> /// <param name="path">The path to normalize.</param> private string NormalizeTilesheetPathForComparison(string path) diff --git a/src/SMAPI/Framework/Content/ContentCache.cs b/src/SMAPI/Framework/Content/ContentCache.cs index 7edc9ab9..8e0c6228 100644 --- a/src/SMAPI/Framework/Content/ContentCache.cs +++ b/src/SMAPI/Framework/Content/ContentCache.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; -using Microsoft.Xna.Framework; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; @@ -18,9 +17,6 @@ namespace StardewModdingAPI.Framework.Content /// <summary>The underlying asset cache.</summary> private readonly IDictionary<string, object> Cache; - /// <summary>Applies platform-specific asset key normalization so it's consistent with the underlying cache.</summary> - private readonly Func<string, string> NormalizeAssetNameForPlatform; - /********* ** Accessors @@ -48,17 +44,7 @@ namespace StardewModdingAPI.Framework.Content /// <param name="reflection">Simplifies access to private game code.</param> public ContentCache(LocalizedContentManager contentManager, Reflector reflection) { - // init this.Cache = reflection.GetField<Dictionary<string, object>>(contentManager, "loadedAssets").GetValue(); - - // get key normalization logic - if (Constants.GameFramework == GameFramework.Xna) - { - IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath"); - this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path); - } - else - this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic } /**** @@ -75,23 +61,24 @@ namespace StardewModdingAPI.Framework.Content /**** ** Normalize ****/ - /// <summary>Normalize path separators in a file path. For asset keys, see <see cref="NormalizeKey"/> instead.</summary> + /// <summary>Normalize path separators in an asset name.</summary> /// <param name="path">The file path to normalize.</param> [Pure] public string NormalizePathSeparators(string path) { - return PathUtilities.NormalizePath(path); + return PathUtilities.NormalizeAssetName(path); } /// <summary>Normalize a cache key so it's consistent with the underlying cache.</summary> /// <param name="key">The asset key.</param> + /// <remarks>This is equivalent to <see cref="NormalizePathSeparators"/> with added file extension logic.</remarks> [Pure] public string NormalizeKey(string key) { key = this.NormalizePathSeparators(key); return key.EndsWith(".xnb", StringComparison.OrdinalIgnoreCase) ? key.Substring(0, key.Length - 4) - : this.NormalizeAssetNameForPlatform(key); + : key; } /**** diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index d0e759c2..b6f1669a 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -64,6 +64,9 @@ namespace StardewModdingAPI.Framework /// <summary>An unmodified content manager which doesn't intercept assets, used to compare asset data.</summary> private readonly LocalizedContentManager VanillaContentManager; + /// <summary>The language enum values indexed by locale code.</summary> + private Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>> LocaleCodes; + /********* ** Accessors @@ -133,6 +136,7 @@ namespace StardewModdingAPI.Framework this.ContentManagers.Add(contentManagerForAssetPropagation); this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory); this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations); + this.LocaleCodes = new Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>>(this.GetLocaleCodes); } /// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary> @@ -195,6 +199,10 @@ namespace StardewModdingAPI.Framework /// <summary>Perform any cleanup needed when the locale changes.</summary> public void OnLocaleChanged() { + // rebuild locale cache (which may change due to custom mod languages) + this.LocaleCodes = new Lazy<IDictionary<string, LocalizedContentManager.LanguageCode>>(this.GetLocaleCodes); + + // reload affected content this.ContentManagerLock.InReadLock(() => { foreach (IContentManager contentManager in this.ContentManagers) @@ -408,6 +416,25 @@ namespace StardewModdingAPI.Framework return tilesheets ?? new TilesheetReference[0]; } + /// <summary>Get the language enum which corresponds to a locale code (e.g. <see cref="LocalizedContentManager.LanguageCode.fr"/> given <c>fr-FR</c>).</summary> + /// <param name="locale">The locale code to search. This must exactly match the language; no fallback is performed.</param> + /// <param name="language">The matched language enum, if any.</param> + /// <returns>Returns whether a valid language was found.</returns> + public bool TryGetLanguageEnum(string locale, out LocalizedContentManager.LanguageCode language) + { + return this.LocaleCodes.Value.TryGetValue(locale, out language); + } + + /// <summary>Get the locale code which corresponds to a language enum (e.g. <c>fr-FR</c> given <see cref="LocalizedContentManager.LanguageCode.fr"/>).</summary> + /// <param name="language">The language enum to search.</param> + public string GetLocaleCode(LocalizedContentManager.LanguageCode language) + { + if (language == LocalizedContentManager.LanguageCode.mod && LocalizedContentManager.CurrentModLanguage == null) + return null; + + return Game1.content.LanguageCodeString(language); + } + /// <summary>Dispose held resources.</summary> public void Dispose() { @@ -457,5 +484,19 @@ namespace StardewModdingAPI.Framework return false; } } + + /// <summary>Get the language enums (like <see cref="LocalizedContentManager.LanguageCode.ja"/>) indexed by locale code (like <c>ja-JP</c>).</summary> + private IDictionary<string, LocalizedContentManager.LanguageCode> GetLocaleCodes() + { + IDictionary<string, LocalizedContentManager.LanguageCode> map = new Dictionary<string, LocalizedContentManager.LanguageCode>(); + foreach (LocalizedContentManager.LanguageCode code in Enum.GetValues(typeof(LocalizedContentManager.LanguageCode))) + { + string locale = this.GetLocaleCode(code); + if (locale != null) + map[locale] = code; + } + + return map; + } } } diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs index 7244a534..5645c0fa 100644 --- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs @@ -38,9 +38,6 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <summary>A callback to invoke when the content manager is being disposed.</summary> private readonly Action<BaseContentManager> OnDisposing; - /// <summary>The language enum values indexed by locale code.</summary> - protected IDictionary<string, LanguageCode> LanguageCodes { get; } - /// <summary>A list of disposable assets.</summary> private readonly List<WeakReference<IDisposable>> Disposables = new List<WeakReference<IDisposable>>(); @@ -92,7 +89,6 @@ namespace StardewModdingAPI.Framework.ContentManagers this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; // get asset data - this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.OrdinalIgnoreCase); this.BaseDisposableReferences = reflection.GetField<List<IDisposable>>(this, "disposableAssets").GetValue(); } @@ -292,7 +288,7 @@ namespace StardewModdingAPI.Framework.ContentManagers if (lastSepIndex >= 0) { string suffix = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1); - if (this.LanguageCodes.ContainsKey(suffix)) + if (this.Coordinator.TryGetLanguageEnum(suffix, out _)) { assetName = cacheKey.Substring(0, lastSepIndex); localeCode = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1); @@ -311,17 +307,6 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <param name="language">The language to check.</param> protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language); - /// <summary>Get the locale codes (like <c>ja-JP</c>) used in asset keys.</summary> - private IDictionary<LanguageCode, string> GetKeyLocales() - { - // create locale => code map - IDictionary<LanguageCode, string> map = new Dictionary<LanguageCode, string>(); - foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode))) - map[code] = this.GetLocale(code); - - return map; - } - /// <summary>Get the asset name from a cache key.</summary> /// <param name="cacheKey">The input cache key.</param> private string GetAssetName(string cacheKey) diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index 38bcf153..7a49dd36 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -249,7 +249,7 @@ namespace StardewModdingAPI.Framework.ContentManagers // extract language code int splitIndex = rawAsset.LastIndexOf('.'); - if (splitIndex != -1 && this.LanguageCodes.TryGetValue(rawAsset.Substring(splitIndex + 1), out language)) + if (splitIndex != -1 && this.Coordinator.TryGetLanguageEnum(rawAsset.Substring(splitIndex + 1), out language)) { assetName = rawAsset.Substring(0, splitIndex); return true; diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs index d24ffb81..beb90a5d 100644 --- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs @@ -2,12 +2,12 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using BmFont; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Reflection; -using StardewModdingAPI.Internal; using StardewModdingAPI.Toolkit.Serialization; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; @@ -130,6 +130,14 @@ namespace StardewModdingAPI.Framework.ContentManagers } break; + // unpacked Bitmap font + case ".fnt": + { + string source = File.ReadAllText(file.FullName); + asset = (T)(object)new XmlSource(source); + } + break; + // unpacked data case ".json": { @@ -172,13 +180,11 @@ namespace StardewModdingAPI.Framework.ContentManagers break; default: - throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.json', '.png', '.tbin', or '.xnb'."); + throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.fnt', '.json', '.png', '.tbin', or '.xnb'."); } } catch (Exception ex) when (!(ex is SContentLoadException)) { - if (ex.GetInnermostException() is DllNotFoundException dllEx && dllEx.Message == "libgdiplus.dylib") - throw GetContentError("couldn't find libgdiplus, which is needed to load mod images. Make sure Mono is installed and you're running the game through the normal launcher."); throw new SContentLoadException($"The content manager failed loading content asset '{assetName}' from {this.Name}.", ex); } diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs index f5f2d916..b0bb7f80 100644 --- a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs +++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; @@ -157,11 +158,8 @@ namespace StardewModdingAPI.Framework.Input yield break; // buttons - foreach (var pair in this.ButtonStates) - { - if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button)) - yield return button.ToSButton(); - } + foreach (Buttons button in this.GetPressedGamePadButtons()) + yield return button.ToSButton(); // triggers if (this.LeftTrigger > 0.2f) @@ -201,7 +199,7 @@ namespace StardewModdingAPI.Framework.Input rightThumbStick: this.RightStickPos, leftTrigger: this.LeftTrigger, rightTrigger: this.RightTrigger, - buttons: this.GetButtonBitmask() // MonoGame requires one bitmask here; don't specify multiple values + buttons: this.GetPressedGamePadButtons().ToArray() ); return this.State.Value; @@ -211,17 +209,14 @@ namespace StardewModdingAPI.Framework.Input /********* ** Private methods *********/ - /// <summary>Get a bitmask representing the pressed buttons.</summary> - private Buttons GetButtonBitmask() + /// <summary>Get the pressed gamepad buttons.</summary> + private IEnumerable<Buttons> GetPressedGamePadButtons() { - Buttons flag = 0; foreach (var pair in this.ButtonStates) { if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button)) - flag |= button; + yield return button; } - - return flag; } } } diff --git a/src/SMAPI/Framework/InternalExtensions.cs b/src/SMAPI/Framework/InternalExtensions.cs index 6c9a5f3b..4cb77a45 100644 --- a/src/SMAPI/Framework/InternalExtensions.cs +++ b/src/SMAPI/Framework/InternalExtensions.cs @@ -6,7 +6,6 @@ using System.Threading; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Reflection; -using StardewValley; using StardewValley.Menus; namespace StardewModdingAPI.Framework @@ -150,11 +149,7 @@ namespace StardewModdingAPI.Framework /// <param name="reflection">The reflection helper with which to access private fields.</param> public static bool IsOpen(this SpriteBatch spriteBatch, Reflector reflection) { - string fieldName = Constants.GameFramework == GameFramework.Xna - ? "inBeginEndPair" - : "_beginCalled"; - - return reflection.GetField<bool>(Game1.spriteBatch, fieldName).GetValue(); + return reflection.GetField<bool>(spriteBatch, "_beginCalled").GetValue(); } } } diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index f2876146..5a291d0a 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -250,36 +250,7 @@ namespace StardewModdingAPI.Framework.Logging /// <param name="exception">The exception details.</param> public void LogFatalLaunchError(Exception exception) { - switch (exception) - { - // audio crash - case InvalidOperationException ex when ex.Source == "Microsoft.Xna.Framework.Xact" && ex.StackTrace.Contains("Microsoft.Xna.Framework.Audio.AudioEngine..ctor"): - this.Monitor.Log("The game couldn't load audio. Do you have speakers or headphones plugged in?", LogLevel.Error); - this.Monitor.Log($"Technical details: {ex.GetLogSummary()}"); - break; - - // missing content folder exception - case FileNotFoundException ex when ex.Message == "Couldn't find file 'C:\\Program Files (x86)\\Steam\\SteamApps\\common\\Stardew Valley\\Content\\XACT\\FarmerSounds.xgs'.": // path in error is hardcoded regardless of install path - this.Monitor.Log("The game can't find its Content\\XACT\\FarmerSounds.xgs file. You can usually fix this by resetting your content files (see https://smapi.io/troubleshoot#reset-content ), or by uninstalling and reinstalling the game.", LogLevel.Error); - this.Monitor.Log($"Technical details: {ex.GetLogSummary()}"); - break; - - // path too long exception - case PathTooLongException _: - { - string[] affectedPaths = PathUtilities.GetTooLongPaths(Constants.ModsPath).ToArray(); - string message = affectedPaths.Any() - ? $"SMAPI can't launch because some of your mod files exceed the maximum path length on {Constants.Platform}.\nIf you need help fixing this error, see https://smapi.io/help\n\nAffected paths:\n {string.Join("\n ", affectedPaths)}" - : $"The game failed to launch: {exception.GetLogSummary()}"; - this.MonitorForGame.Log(message, LogLevel.Error); - } - break; - - // generic exception - default: - this.MonitorForGame.Log($"The game failed to launch: {exception.GetLogSummary()}", LogLevel.Error); - break; - } + this.MonitorForGame.Log($"The game failed to launch: {exception.GetLogSummary()}", LogLevel.Error); } /**** @@ -290,7 +261,7 @@ namespace StardewModdingAPI.Framework.Logging /// <param name="customSettings">The custom SMAPI settings.</param> public void LogIntro(string modsPath, IDictionary<string, object> customSettings) { - // log platform & patches + // log platform this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info); // log basic info diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs index 57a76a35..cb5fa2ae 100644 --- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs @@ -53,16 +53,15 @@ namespace StardewModdingAPI.Framework.ModLoading *********/ /// <summary>Construct an instance.</summary> /// <param name="targetPlatform">The current game platform.</param> - /// <param name="framework">The game framework running the game.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> /// <param name="paranoidMode">Whether to detect paranoid mode issues.</param> /// <param name="rewriteMods">Whether to rewrite mods for compatibility.</param> - public AssemblyLoader(Platform targetPlatform, GameFramework framework, IMonitor monitor, bool paranoidMode, bool rewriteMods) + public AssemblyLoader(Platform targetPlatform, IMonitor monitor, bool paranoidMode, bool rewriteMods) { this.Monitor = monitor; this.ParanoidMode = paranoidMode; this.RewriteMods = rewriteMods; - this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform, framework)); + this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform)); // init resolver this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver()); diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs index aefd1c20..a064f503 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs @@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics; namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { - /// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/macOS or Windows.</summary> + /// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility with mods written for XNA Framework before Stardew Valley 1.5.5.</summary> /// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks> [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")] [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")] @@ -19,14 +19,6 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades /**** - ** MonoGame signatures - ****/ - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity); - } - - /**** ** XNA signatures ****/ public new void Begin() diff --git a/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs b/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs index 8d1b6034..5acba569 100644 --- a/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs +++ b/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs @@ -24,7 +24,7 @@ namespace StardewModdingAPI.Framework.Reflection /// <summary>Construct an instance.</summary> public InterfaceProxyFactory() { - AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run); + AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run); this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("StardewModdingAPI.Proxies"); } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 6dffb1de..55a7f083 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -11,14 +11,10 @@ using System.Runtime.ExceptionServices; using System.Security; using System.Text; using System.Threading; -using System.Threading.Tasks; using Microsoft.Xna.Framework; #if SMAPI_FOR_WINDOWS using Microsoft.Win32; #endif -#if SMAPI_FOR_XNA -using System.Windows.Forms; -#endif using Newtonsoft.Json; using StardewModdingAPI.Enums; using StardewModdingAPI.Events; @@ -224,10 +220,6 @@ namespace StardewModdingAPI.Framework this.Toolkit.JsonHelper.JsonSettings.Converters.Add(converter); // add error handlers -#if SMAPI_FOR_XNA - Application.ThreadException += (sender, e) => this.Monitor.Log($"Critical thread exception: {e.Exception.GetLogSummary()}", LogLevel.Error); - Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); -#endif AppDomain.CurrentDomain.UnhandledException += (sender, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error); // add more lenient assembly resolver @@ -243,7 +235,7 @@ namespace StardewModdingAPI.Framework monitor: this.Monitor, reflection: this.Reflection, eventManager: this.EventManager, - modHooks: new SModHooks(this.OnNewDayAfterFade), + modHooks: new SModHooks(this.OnNewDayAfterFade, this.Monitor), multiplayer: this.Multiplayer, exitGameImmediately: this.ExitGameImmediately, @@ -657,13 +649,6 @@ namespace StardewModdingAPI.Framework this.Monitor.Log("Game loader done."); } - if (instance.NewDayTask?.Status == TaskStatus.Created) - { - this.Monitor.Log("New day task synchronizing..."); - instance.NewDayTask.RunSynchronously(); - this.Monitor.Log("New day task done."); - } - // While a background task is in progress, the game may make changes to the game // state while mods are running their code. This is risky, because data changes can // conflict (e.g. collection changed during enumeration errors) and data may change @@ -673,7 +658,7 @@ namespace StardewModdingAPI.Framework // a small chance that the task will finish after we defer but before the game checks, // which means technically events should be raised, but the effects of missing one // update tick are negligible and not worth the complications of bypassing Game1.Update. - if (instance.NewDayTask |
