summaryrefslogtreecommitdiff
path: root/src/SMAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-08-20 17:01:59 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-08-20 17:01:59 -0400
commita1bc96d365dc40275f198668d3f4c09bd7a92613 (patch)
tree5a130399bf8031aa70defb71a121b740d7c6cd7a /src/SMAPI
parentd51ffe58f7b7450cd4c4a7ee3d8b4da1cf55e7e4 (diff)
parentf3a79219e85c9af18f2f6d8b2aaa794afa08578d (diff)
downloadSMAPI-a1bc96d365dc40275f198668d3f4c09bd7a92613.tar.gz
SMAPI-a1bc96d365dc40275f198668d3f4c09bd7a92613.tar.bz2
SMAPI-a1bc96d365dc40275f198668d3f4c09bd7a92613.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI')
-rw-r--r--src/SMAPI/Constants.cs8
-rw-r--r--src/SMAPI/Framework/Content/AssetInfo.cs4
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs4
-rw-r--r--src/SMAPI/Framework/Deprecations/DeprecationManager.cs2
-rw-r--r--src/SMAPI/Framework/ExitState.cs15
-rw-r--r--src/SMAPI/Framework/ModHelpers/CommandHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs8
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs70
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs13
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs6
-rw-r--r--src/SMAPI/Framework/SCore.cs67
-rw-r--r--src/SMAPI/SMAPI.csproj3
-rw-r--r--src/SMAPI/Utilities/PerScreen.cs2
-rw-r--r--src/SMAPI/app.manifest41
-rw-r--r--src/SMAPI/i18n/tr.json2
16 files changed, 203 insertions, 46 deletions
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index d733e61e..c79a72ef 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -52,7 +52,7 @@ namespace StardewModdingAPI
internal static int? LogScreenId { get; set; }
/// <summary>SMAPI's current raw semantic version.</summary>
- internal static string RawApiVersion = "3.15.1";
+ internal static string RawApiVersion = "3.16.0";
}
/// <summary>Contains SMAPI's constants and assumptions.</summary>
@@ -90,7 +90,7 @@ namespace StardewModdingAPI
source: null,
nounPhrase: $"{nameof(Constants)}.{nameof(Constants.ExecutionPath)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice
+ severity: DeprecationLevel.Info
);
return Constants.GamePath;
@@ -244,8 +244,8 @@ namespace StardewModdingAPI
internal static void ConfigureAssemblyResolver(AssemblyDefinitionResolver resolver)
{
// add search paths
- resolver.AddSearchDirectory(Constants.GamePath);
- resolver.AddSearchDirectory(Constants.InternalFilesPath);
+ resolver.TryAddSearchDirectory(Constants.GamePath);
+ resolver.TryAddSearchDirectory(Constants.InternalFilesPath);
// add SMAPI explicitly
// Normally this would be handled automatically by the search paths, but for some reason there's a specific
diff --git a/src/SMAPI/Framework/Content/AssetInfo.cs b/src/SMAPI/Framework/Content/AssetInfo.cs
index 43feed27..af000300 100644
--- a/src/SMAPI/Framework/Content/AssetInfo.cs
+++ b/src/SMAPI/Framework/Content/AssetInfo.cs
@@ -45,7 +45,7 @@ namespace StardewModdingAPI.Framework.Content
source: null,
nounPhrase: $"{nameof(IAssetInfo)}.{nameof(IAssetInfo.AssetName)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice,
+ severity: DeprecationLevel.Info,
unlessStackIncludes: new[]
{
$"{typeof(AssetInterceptorChange).FullName}.{nameof(AssetInterceptorChange.CanIntercept)}",
@@ -84,7 +84,7 @@ namespace StardewModdingAPI.Framework.Content
source: null,
nounPhrase: $"{nameof(IAssetInfo)}.{nameof(IAssetInfo.AssetNameEquals)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice,
+ severity: DeprecationLevel.Info,
unlessStackIncludes: new[]
{
$"{typeof(AssetInterceptorChange).FullName}.{nameof(AssetInterceptorChange.CanIntercept)}",
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index f3cf05d9..8ecbc4cc 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -254,6 +254,10 @@ namespace StardewModdingAPI.Framework.ContentManagers
{
using FileStream stream = File.OpenRead(file.FullName);
using SKBitmap bitmap = SKBitmap.Decode(stream);
+
+ if (bitmap is null)
+ throw new InvalidDataException($"Failed to load {file.FullName}. This doesn't seem to be a valid PNG image.");
+
rawPixels = SKPMColor.PreMultiply(bitmap.Pixels);
width = bitmap.Width;
height = bitmap.Height;
diff --git a/src/SMAPI/Framework/Deprecations/DeprecationManager.cs b/src/SMAPI/Framework/Deprecations/DeprecationManager.cs
index 4597a764..f58f085e 100644
--- a/src/SMAPI/Framework/Deprecations/DeprecationManager.cs
+++ b/src/SMAPI/Framework/Deprecations/DeprecationManager.cs
@@ -124,7 +124,7 @@ namespace StardewModdingAPI.Framework.Deprecations
}
// log message
- if (level == LogLevel.Trace)
+ if (level is LogLevel.Trace or LogLevel.Debug)
{
if (warning.LogStackTrace)
message += $"\n{this.GetSimplifiedStackTrace(warning.StackTrace, warning.Mod)}";
diff --git a/src/SMAPI/Framework/ExitState.cs b/src/SMAPI/Framework/ExitState.cs
new file mode 100644
index 00000000..bc022d91
--- /dev/null
+++ b/src/SMAPI/Framework/ExitState.cs
@@ -0,0 +1,15 @@
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>The SMAPI exit state.</summary>
+ internal enum ExitState
+ {
+ /// <summary>SMAPI didn't trigger an explicit exit.</summary>
+ None,
+
+ /// <summary>The game is exiting normally.</summary>
+ GameExit,
+
+ /// <summary>The game is exiting due to an error.</summary>
+ Crash
+ }
+}
diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
index 21435f62..b7d4861f 100644
--- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
source: this.Mod,
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.ConsoleCommands)}.{nameof(ICommandHelper.Trigger)}",
version: "3.8.1",
- severity: DeprecationLevel.Notice
+ severity: DeprecationLevel.Info
);
return this.CommandManager.Trigger(name, arguments);
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index 9992cb52..0a1633bf 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -62,7 +62,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
source: this.Mod,
nounPhrase: $"{nameof(IContentHelper)}.{nameof(IContentHelper.AssetLoaders)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice
+ severity: DeprecationLevel.Info
);
return this.ObservableAssetLoaders;
@@ -78,7 +78,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
source: this.Mod,
nounPhrase: $"{nameof(IContentHelper)}.{nameof(IContentHelper.AssetEditors)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice
+ severity: DeprecationLevel.Info
);
return this.ObservableAssetEditors;
@@ -126,7 +126,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Mod,
"loading assets from the Content folder with a .xnb file extension",
"3.14.0",
- DeprecationLevel.Notice
+ DeprecationLevel.Info
);
}
@@ -150,7 +150,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Mod,
"loading XNB files from the mod folder without the .xnb file extension",
"3.14.0",
- DeprecationLevel.Notice
+ DeprecationLevel.Info
);
return data;
}
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index caa66bad..1cdd8536 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -41,7 +41,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
source: this.Mod,
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.Content)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice
+ severity: DeprecationLevel.Info
);
return this.ContentImpl;
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
index b3378ad1..5a850255 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
@@ -4,18 +4,31 @@ using Mono.Cecil;
namespace StardewModdingAPI.Framework.ModLoading
{
/// <summary>A minimal assembly definition resolver which resolves references to known assemblies.</summary>
- internal class AssemblyDefinitionResolver : DefaultAssemblyResolver
+ internal class AssemblyDefinitionResolver : IAssemblyResolver
{
/*********
** Fields
*********/
+ /// <summary>The underlying assembly resolver.</summary>
+ private readonly DefaultAssemblyResolverWrapper Resolver = new();
+
/// <summary>The known assemblies.</summary>
private readonly IDictionary<string, AssemblyDefinition> Lookup = new Dictionary<string, AssemblyDefinition>();
+ /// <summary>The directory paths to search for assemblies.</summary>
+ private readonly HashSet<string> SearchPaths = new();
+
/*********
** Public methods
*********/
+ /// <summary>Construct an instance.</summary>
+ public AssemblyDefinitionResolver()
+ {
+ foreach (string path in this.Resolver.GetSearchDirectories())
+ this.SearchPaths.Add(path);
+ }
+
/// <summary>Add known assemblies to the resolver.</summary>
/// <param name="assemblies">The known assemblies.</param>
public void Add(params AssemblyDefinition[] assemblies)
@@ -29,7 +42,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="names">The assembly names for which it should be returned.</param>
public void AddWithExplicitNames(AssemblyDefinition assembly, params string[] names)
{
- this.RegisterAssembly(assembly);
+ this.Resolver.AddAssembly(assembly);
foreach (string name in names)
this.Lookup[name] = assembly;
}
@@ -37,18 +50,52 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Resolve an assembly reference.</summary>
/// <param name="name">The assembly name.</param>
/// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception>
- public override AssemblyDefinition Resolve(AssemblyNameReference name)
+ public AssemblyDefinition Resolve(AssemblyNameReference name)
{
- return this.ResolveName(name.Name) ?? base.Resolve(name);
+ return this.ResolveName(name.Name) ?? this.Resolver.Resolve(name);
}
/// <summary>Resolve an assembly reference.</summary>
/// <param name="name">The assembly name.</param>
/// <param name="parameters">The assembly reader parameters.</param>
/// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception>
- public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
+ public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
- return this.ResolveName(name.Name) ?? base.Resolve(name, parameters);
+ return this.ResolveName(name.Name) ?? this.Resolver.Resolve(name, parameters);
+ }
+
+ /// <summary>Add a directory path to search for assemblies, if it's non-null and not already added.</summary>
+ /// <param name="path">The path to search.</param>
+ /// <returns>Returns whether the path was successfully added.</returns>
+ public bool TryAddSearchDirectory(string? path)
+ {
+ if (path is not null && this.SearchPaths.Add(path))
+ {
+ this.Resolver.AddSearchDirectory(path);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>Remove a directory path to search for assemblies, if it's non-null.</summary>
+ /// <param name="path">The path to remove.</param>
+ /// <returns>Returns whether the path was in the list and removed.</returns>
+ public bool RemoveSearchDirectory(string? path)
+ {
+ if (path is not null && this.SearchPaths.Remove(path))
+ {
+ this.Resolver.RemoveSearchDirectory(path);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ this.Resolver.Dispose();
}
@@ -63,5 +110,16 @@ namespace StardewModdingAPI.Framework.ModLoading
? match
: null;
}
+
+ /// <summary>An internal wrapper around <see cref="DefaultAssemblyResolver"/> to allow access to its protected methods.</summary>
+ private class DefaultAssemblyResolverWrapper : DefaultAssemblyResolver
+ {
+ /// <summary>Add an assembly to the resolver.</summary>
+ /// <param name="assembly">The assembly to add.</param>
+ public void AddAssembly(AssemblyDefinition assembly)
+ {
+ this.RegisterAssembly(assembly);
+ }
+ }
}
}
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index eb940c41..01037870 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -264,8 +264,15 @@ namespace StardewModdingAPI.Framework.ModLoading
if (!file.Exists)
yield break; // not a local assembly
+ // add the assembly's directory temporarily if needed
+ // this is needed by F# mods which bundle FSharp.Core.dll, for example
+ string? temporarySearchDir = null;
+ if (this.AssemblyDefinitionResolver.TryAddSearchDirectory(file.DirectoryName))
+ temporarySearchDir = file.DirectoryName;
+
// read assembly
AssemblyDefinition assembly;
+ try
{
byte[] assemblyBytes = File.ReadAllBytes(file.FullName);
Stream readStream = this.TrackForDisposal(new MemoryStream(assemblyBytes));
@@ -286,6 +293,12 @@ namespace StardewModdingAPI.Framework.ModLoading
assembly = this.TrackForDisposal(AssemblyDefinition.ReadAssembly(readStream, new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = assemblyResolver, InMemory = true }));
}
}
+ finally
+ {
+ // clean up temporary search directory
+ if (temporarySearchDir is not null)
+ this.AssemblyDefinitionResolver.RemoveSearchDirectory(temporarySearchDir);
+ }
// skip if already visited
if (visitedAssemblyNames.Contains(assembly.Name.Name))
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index abc46d47..c5648c74 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -47,7 +47,7 @@ namespace StardewModdingAPI.Framework.ModLoading
IModMetadata metadata = new ModMetadata(folder.DisplayName, folder.Directory.FullName, rootPath, manifest, dataRecord, isIgnored: shouldIgnore);
if (shouldIgnore)
metadata.SetStatus(status, ModFailReason.DisabledByDotConvention, "disabled by dot convention");
- else
+ else if (status == ModMetadataStatus.Failed)
metadata.SetStatus(status, ModFailReason.InvalidManifest, folder.ManifestParseErrorText);
yield return metadata;
@@ -223,8 +223,8 @@ namespace StardewModdingAPI.Framework.ModLoading
{
foreach (IModMetadata mod in group)
{
- if (mod.Status == ModMetadataStatus.Failed)
- continue; // don't replace metadata error
+ if (mod.Status == ModMetadataStatus.Failed && mod.FailReason != ModFailReason.InvalidManifest)
+ continue;
string folderList = string.Join(", ", group.Select(p => p.GetRelativePathWithRoot()).OrderBy(p => p));
mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.Duplicate, $"you have multiple copies of this mod installed. To fix this, delete these folders and reinstall the mod: {folderList}.");
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 46d65f6a..0f86ed6b 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
@@ -50,6 +49,7 @@ using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using StardewValley.Objects;
+using StardewValley.SDKs;
using xTile.Display;
using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode;
using MiniMonoModHotfix = MonoMod.Utils.MiniMonoModHotfix;
@@ -67,8 +67,11 @@ namespace StardewModdingAPI.Framework
/****
** Low-level components
****/
+ /// <summary>A state which indicates whether SMAPI should exit immediately and any pending initialization should be cancelled.</summary>
+ private ExitState ExitState;
+
/// <summary>Whether the game should exit immediately and any pending initialization should be cancelled.</summary>
- private bool IsExiting;
+ private bool IsExiting => this.ExitState != ExitState.None;
/// <summary>Manages the SMAPI console window and log file.</summary>
private readonly LogManager LogManager;
@@ -297,22 +300,13 @@ namespace StardewModdingAPI.Framework
this.IsGameRunning = true;
StardewValley.Program.releaseBuild = true; // game's debug logic interferes with SMAPI opening the game window
this.Game.Run();
+ this.Dispose(isError: false);
}
catch (Exception ex)
{
this.LogManager.LogFatalLaunchError(ex);
this.LogManager.PressAnyKeyToExit();
- }
- finally
- {
- try
- {
- this.Dispose();
- }
- catch (Exception ex)
- {
- this.Monitor.Log($"The game ended, but SMAPI wasn't able to dispose correctly. Technical details: {ex}", LogLevel.Error);
- }
+ this.Dispose(isError: true);
}
}
@@ -328,6 +322,14 @@ namespace StardewModdingAPI.Framework
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "May be disposed before SMAPI is fully initialized.")]
public void Dispose()
{
+ this.Dispose(isError: true); // if we got here, SMAPI didn't detect the exit before it happened
+ }
+
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ /// <param name="isError">Whether the process is exiting due to an error or crash.</param>
+ [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "May be disposed before SMAPI is fully initialized.")]
+ public void Dispose(bool isError)
+ {
// skip if already disposed
if (this.IsDisposed)
return;
@@ -349,13 +351,29 @@ namespace StardewModdingAPI.Framework
// dispose core components
this.IsGameRunning = false;
- this.IsExiting = true;
+ if (this.ExitState == ExitState.None || isError)
+ this.ExitState = isError ? ExitState.Crash : ExitState.GameExit;
this.ContentCore?.Dispose();
this.Game?.Dispose();
this.LogManager.Dispose(); // dispose last to allow for any last-second log messages
- // end game (moved from Game1.OnExiting to let us clean up first)
- Process.GetCurrentProcess().Kill();
+ // clean up SDK
+ // This avoids Steam connection errors when it exits unexpectedly. The game avoids this
+ // by killing the entire process, but we can't set the error code if we do that.
+ try
+ {
+ FieldInfo? field = typeof(StardewValley.Program).GetField("_sdk", BindingFlags.NonPublic | BindingFlags.Static);
+ SDKHelper? sdk = field?.GetValue(null) as SDKHelper;
+ sdk?.Shutdown();
+ }
+ catch
+ {
+ // well, at least we tried
+ }
+
+ // end game with error code
+ // This helps the OS decide whether to keep the window open (e.g. Windows may keep it open on error).
+ Environment.Exit(this.ExitState == ExitState.Crash ? 1 : 0);
}
@@ -387,7 +405,14 @@ namespace StardewModdingAPI.Framework
{
string[] looseFiles = new DirectoryInfo(this.ModsPath).GetFiles().Select(p => p.Name).ToArray();
if (looseFiles.Any())
+ {
+ if (looseFiles.Any(name => name.Equals("manifest.json", StringComparison.OrdinalIgnoreCase) || name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)))
+ {
+ this.Monitor.Log($"Detected mod files directly inside the '{Path.GetFileName(this.ModsPath)}' folder. These will be ignored. Each mod must have its own subfolder instead.", LogLevel.Error);
+ }
+
this.Monitor.Log($" Ignored loose files: {string.Join(", ", looseFiles.OrderBy(p => p, StringComparer.OrdinalIgnoreCase))}");
+ }
}
// load manifests
@@ -1250,7 +1275,7 @@ namespace StardewModdingAPI.Framework
private void OnGameExiting()
{
this.Multiplayer.Disconnect(StardewValley.Multiplayer.DisconnectType.ClosedGame);
- this.Dispose();
+ this.Dispose(isError: false);
}
/// <summary>Raised when a mod network message is received.</summary>
@@ -1670,7 +1695,7 @@ namespace StardewModdingAPI.Framework
source: metadata,
nounPhrase: $"{nameof(IAssetEditor)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice,
+ severity: DeprecationLevel.Info,
logStackTrace: false
);
@@ -1683,7 +1708,7 @@ namespace StardewModdingAPI.Framework
source: metadata,
nounPhrase: $"{nameof(IAssetLoader)}",
version: "3.14.0",
- severity: DeprecationLevel.Notice,
+ severity: DeprecationLevel.Info,
logStackTrace: false
);
@@ -1715,7 +1740,7 @@ namespace StardewModdingAPI.Framework
metadata,
$"using {name} without bundling it",
"3.14.7",
- DeprecationLevel.Notice,
+ DeprecationLevel.Info,
logStackTrace: false
);
}
@@ -2239,7 +2264,7 @@ namespace StardewModdingAPI.Framework
this.Monitor.LogFatal(message);
this.LogManager.WriteCrashLog();
- this.IsExiting = true;
+ this.ExitState = ExitState.Crash;
this.Game.Exit();
}
diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj
index c05512e9..36db0545 100644
--- a/src/SMAPI/SMAPI.csproj
+++ b/src/SMAPI/SMAPI.csproj
@@ -16,6 +16,7 @@
<!-- tiered compilation breaks Harmony -->
<TieredCompilation>false</TieredCompilation>
+ <ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<Import Project="..\..\build\common.targets" />
@@ -26,7 +27,7 @@
<PackageReference Include="MonoMod.Common" Version="22.3.5.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.1" />
- <PackageReference Include="Pintail" Version="2.2.0" />
+ <PackageReference Include="Pintail" Version="2.2.1" />
<PackageReference Include="Platonymous.TMXTile" Version="1.5.9" />
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
diff --git a/src/SMAPI/Utilities/PerScreen.cs b/src/SMAPI/Utilities/PerScreen.cs
index 54657ade..468df0bd 100644
--- a/src/SMAPI/Utilities/PerScreen.cs
+++ b/src/SMAPI/Utilities/PerScreen.cs
@@ -59,7 +59,7 @@ namespace StardewModdingAPI.Utilities
null,
$"calling the {nameof(PerScreen<T>)} constructor with null",
"3.14.0",
- DeprecationLevel.Notice
+ DeprecationLevel.Info
);
#else
throw new ArgumentNullException(nameof(createNewState));
diff --git a/src/SMAPI/app.manifest b/src/SMAPI/app.manifest
new file mode 100644
index 00000000..42faff59
--- /dev/null
+++ b/src/SMAPI/app.manifest
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+ <assemblyIdentity version="1.0.0.0" name="StardropEngine"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- A list of the Windows versions that this application has been tested on and is
+ is designed to work with. Uncomment the appropriate elements and Windows will
+ automatically selected the most compatible environment. -->
+
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+ </application>
+ </compatibility>
+
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
+ </windowsSettings>
+ </application>
+</assembly>
diff --git a/src/SMAPI/i18n/tr.json b/src/SMAPI/i18n/tr.json
index e97a48ba..ba0c33c9 100644
--- a/src/SMAPI/i18n/tr.json
+++ b/src/SMAPI/i18n/tr.json
@@ -2,5 +2,5 @@
// short date format for SDate
// tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2)
"generic.date": "{{day}} {{season}}",
- "generic.date-with-year": "{{day}} {{season}} года {{year}}"
+ "generic.date-with-year": "{{day}} {{season}} Yıl {{year}}"
}