Merge branch 'develop' into stable

This commit is contained in:
Jesse Plamondon-Willard 2023-06-24 16:53:42 -04:00
commit 0ce39e2330
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
28 changed files with 200 additions and 123 deletions

View File

@ -7,13 +7,16 @@ repo. It imports the other MSBuild files as needed.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<!--set general build properties --> <!--set general build properties -->
<Version>3.18.3</Version> <Version>3.18.4</Version>
<Product>SMAPI</Product> <Product>SMAPI</Product>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths> <AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
<DefineConstants>$(DefineConstants);SMAPI_DEPRECATED</DefineConstants> <DefineConstants>$(DefineConstants);SMAPI_DEPRECATED</DefineConstants>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<!--embed symbols for error stack trace line numbers on Linux/macOS: https://github.com/dotnet/runtime/issues/39987-->
<DebugType>embedded</DebugType>
<!--enable nullable annotations, except in .NET Standard 2.0 where they aren't supported--> <!--enable nullable annotations, except in .NET Standard 2.0 where they aren't supported-->
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable> <Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
<NoWarn Condition="'$(TargetFramework)' == 'netstandard2.0'">$(NoWarn);CS8632</NoWarn> <NoWarn Condition="'$(TargetFramework)' == 'netstandard2.0'">$(NoWarn);CS8632</NoWarn>

View File

@ -18,7 +18,6 @@ This assumes `find-game-folder.targets` has already been imported and validated.
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" Condition="$(OS) == 'Windows_NT'" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" Condition="$(OS) == 'Windows_NT'" />
<Copy SourceFiles="$(TargetDir)\$(TargetName)" DestinationFolder="$(GamePath)" Condition="$(OS) != 'Windows_NT'" /> <Copy SourceFiles="$(TargetDir)\$(TargetName)" DestinationFolder="$(GamePath)" Condition="$(OS) != 'Windows_NT'" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.json" /> <Copy SourceFiles="$(TargetDir)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.json" />
<Copy SourceFiles="$(TargetDir)\SMAPI.metadata.json" DestinationFiles="$(GamePath)\smapi-internal\metadata.json" /> <Copy SourceFiles="$(TargetDir)\SMAPI.metadata.json" DestinationFiles="$(GamePath)\smapi-internal\metadata.json" />
@ -62,7 +61,6 @@ This assumes `find-game-folder.targets` has already been imported and validated.
</ItemGroup> </ItemGroup>
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" Condition="Exists('$(TargetDir)\$(TargetName).pdb')" />
<Copy SourceFiles="$(TargetDir)\manifest.json" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" /> <Copy SourceFiles="$(TargetDir)\manifest.json" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" />
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" /> <Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" />
</Target> </Target>
@ -70,12 +68,10 @@ This assumes `find-game-folder.targets` has already been imported and validated.
<!-- toolkit --> <!-- toolkit -->
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit'" AfterTargets="PostBuildEvent"> <Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
</Target> </Target>
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces'" AfterTargets="PostBuildEvent"> <Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" /> <Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
</Target> </Target>
</Project> </Project>

View File

@ -122,7 +122,7 @@ for folder in ${folders[@]}; do
fi fi
# bundle root files # bundle root files
for name in "StardewModdingAPI" "StardewModdingAPI.dll" "StardewModdingAPI.pdb" "StardewModdingAPI.xml" "steam_appid.txt"; do for name in "StardewModdingAPI" "StardewModdingAPI.dll" "StardewModdingAPI.xml" "steam_appid.txt"; do
if [ $name == "StardewModdingAPI" ] && [ $folder == "windows" ]; then if [ $name == "StardewModdingAPI" ] && [ $folder == "windows" ]; then
name="$name.exe" name="$name.exe"
fi fi
@ -134,7 +134,7 @@ for folder in ${folders[@]}; do
cp -r "$smapiBin/i18n" "$bundlePath/smapi-internal" cp -r "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal # bundle smapi-internal
for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.pdb" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.pdb" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do
cp "$smapiBin/$name" "$bundlePath/smapi-internal" cp "$smapiBin/$name" "$bundlePath/smapi-internal"
done done
@ -164,7 +164,6 @@ for folder in ${folders[@]}; do
mkdir "$targetPath" --parents mkdir "$targetPath" --parents
cp "$fromPath/$modName.dll" "$targetPath" cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/$modName.pdb" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath" cp "$fromPath/manifest.json" "$targetPath"
if [ -d "$fromPath/i18n" ]; then if [ -d "$fromPath/i18n" ]; then
cp -r "$fromPath/i18n" "$targetPath" cp -r "$fromPath/i18n" "$targetPath"

View File

@ -142,7 +142,7 @@ foreach ($folder in $folders) {
} }
# bundle root files # bundle root files
foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.pdb", "StardewModdingAPI.xml", "steam_appid.txt")) { foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.xml", "steam_appid.txt")) {
if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") { if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") {
$name = "$name.exe" $name = "$name.exe"
} }
@ -154,7 +154,7 @@ foreach ($folder in $folders) {
cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal" cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal # bundle smapi-internal
foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.pdb", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.pdb", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) { foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) {
cp "$smapiBin/$name" "$bundlePath/smapi-internal" cp "$smapiBin/$name" "$bundlePath/smapi-internal"
} }
@ -189,7 +189,6 @@ foreach ($folder in $folders) {
mkdir "$targetPath" > $null mkdir "$targetPath" > $null
cp "$fromPath/$modName.dll" "$targetPath" cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/$modName.pdb" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath" cp "$fromPath/manifest.json" "$targetPath"
if (Test-Path "$fromPath/i18n" -PathType Container) { if (Test-Path "$fromPath/i18n" -PathType Container) {
cp -Recurse "$fromPath/i18n" "$targetPath" cp -Recurse "$fromPath/i18n" "$targetPath"

View File

@ -7,6 +7,29 @@
_If needed, you can update to SMAPI 3.16.0 first and then install the latest version._ _If needed, you can update to SMAPI 3.16.0 first and then install the latest version._
--> -->
## 3.18.4
Released 24 June 2023 for Stardew Valley 1.5.6 or later.
* For players:
* In multiplayer, the game/SMAPI window titles now show whether you're the main player or a farmhand.
* The `test_input` console command now logs input until the command is run again (instead of for 30 seconds).
* Fixed logged SMAPI errors not having line numbers on Linux/macOS.
* Fixed wezterm terminal support on Linux/macoS (thanks to romangraef!).
* Fixed install error if a game folder has an invalid symlink.
* For mod authors:
* Added `--no-prompt` installer command-line argument for automated tools (thanks to NyCodeGHG!).
* Added clearer error message when a map tilesheet has no image source (thanks to atravita!).
* Fixed `Context.HasRemotePlayers` being true when there's no farmhands connected.
* Fixed error loading a mod if it explicitly sets `"MinimumApiVersion": null`.
* Updated Newtonsoft.Json 13.0.2 → 13.0.3 (see [changes](https://github.com/JamesNK/Newtonsoft.Json/releases/tag/13.0.3)) and Pintail 2.2.2 → 2.3.0 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#230)).
* For SMAPI toolkit users:
* Fixed `ModFolder` not being JSON-serializable.
* For the web API:
* Fixed manifest schema format for the `examples` field (thanks to boneskull!).
## 3.18.3 ## 3.18.3
Released 09 April 2023 for Stardew Valley 1.5.6 or later. Released 09 April 2023 for Stardew Valley 1.5.6 or later.
@ -15,7 +38,7 @@ Released 09 April 2023 for Stardew Valley 1.5.6 or later.
* Fixed installer error for some Linux players due to a non-portable shebang (thanks to freyacoded!). * Fixed installer error for some Linux players due to a non-portable shebang (thanks to freyacoded!).
* Fixed error using load order overrides when there are broken mods installed (thanks to atravita!). * Fixed error using load order overrides when there are broken mods installed (thanks to atravita!).
* Removed `LargeAddressAware` flag on SMAPI (no longer needed since it's 64-bit now). * Removed `LargeAddressAware` flag on SMAPI (no longer needed since it's 64-bit now).
* Improved translations. Thganks to stylemate (updated Korean)! * Improved translations. Thanks to stylemate (updated Korean)!
* For mod authors: * For mod authors:
* Added `IsActiveForScreen()` method to `PerScreen<T>`. * Added `IsActiveForScreen()` method to `PerScreen<T>`.

View File

@ -416,28 +416,33 @@ The NuGet package is generated automatically in `StardewModdingAPI.ModBuildConfi
when you compile it. when you compile it.
## Release notes ## Release notes
## 4.1.1
Released 24 June 2023 for SMAPI 3.13.0 or later.
* Replaced `.pdb` files with embedded symbols by default. This fixes logged errors not having line numbers on Linux/macOS.
### 4.1.0 ### 4.1.0
Released 08 January 2023. Released 08 January 2023 for SMAPI 3.13.0 or later.
* Added `manifest.json` format validation on build (thanks to tylergibbs2!). * Added `manifest.json` format validation on build (thanks to tylergibbs2!).
* Fixed game DLLs not excluded from the release zip when they're referenced explicitly but `BundleExtraAssemblies` isn't set. * Fixed game DLLs not excluded from the release zip when they're referenced explicitly but `BundleExtraAssemblies` isn't set.
### 4.0.2 ### 4.0.2
Released 09 October 2022. Released 09 October 2022 for SMAPI 3.13.0 or later.
* Switched to the newer crossplatform `portable` debug symbols (thanks to lanturnalis!). * Switched to the newer crossplatform `portable` debug symbols (thanks to lanturnalis!).
* Fixed `BundleExtraAssemblies` option being partly case-sensitive. * Fixed `BundleExtraAssemblies` option being partly case-sensitive.
* Fixed `BundleExtraAssemblies` not applying `All` value to game assemblies. * Fixed `BundleExtraAssemblies` not applying `All` value to game assemblies.
### 4.0.1 ### 4.0.1
Released 14 April 2022. Released 14 April 2022 for SMAPI 3.13.0 or later.
* Added detection for Xbox app game folders. * Added detection for Xbox app game folders.
* Fixed "_conflicts between different versions of Microsoft.Win32.Registry_" warnings in recent SMAPI versions. * Fixed "_conflicts between different versions of Microsoft.Win32.Registry_" warnings in recent SMAPI versions.
* Internal refactoring. * Internal refactoring.
### 4.0.0 ### 4.0.0
Released 30 November 2021. Released 30 November 2021 for SMAPI 3.13.0 or later.
* Updated for Stardew Valley 1.5.5 and SMAPI 3.13.0. (Older versions are no longer supported.) * Updated for Stardew Valley 1.5.5 and SMAPI 3.13.0. (Older versions are no longer supported.)
* Added `IgnoreModFilePaths` option to ignore literal paths. * Added `IgnoreModFilePaths` option to ignore literal paths.
@ -459,7 +464,7 @@ Released 30 November 2021.
documentation](#configure). documentation](#configure).
### 3.3.0 ### 3.3.0
Released 30 March 2021. Released 30 March 2021 for SMAPI 3.0.0 or later.
* Added a build warning when the mod isn't compiled for `Any CPU`. * Added a build warning when the mod isn't compiled for `Any CPU`.
* Added a `GameFramework` build property set to `MonoGame` or `Xna` based on the platform. This can * Added a `GameFramework` build property set to `MonoGame` or `Xna` based on the platform. This can
@ -468,32 +473,32 @@ Released 30 March 2021.
* The package now suppresses the misleading 'processor architecture mismatch' warnings. * The package now suppresses the misleading 'processor architecture mismatch' warnings.
### 3.2.2 ### 3.2.2
Released 23 September 2020. Released 23 September 2020 for SMAPI 3.0.0 or later.
* Reworked and streamlined how the package is compiled. * Reworked and streamlined how the package is compiled.
* Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder) * Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder)
files to the ignore list. files to the ignore list.
### 3.2.1 ### 3.2.1
Released 11 September 2020. Released 11 September 2020 for SMAPI 3.0.0 or later.
* Added more detailed logging. * Added more detailed logging.
* Fixed _path's format is not supported_ error when using default `Mods` path in 3.2. * Fixed _path's format is not supported_ error when using default `Mods` path in 3.2.
### 3.2.0 ### 3.2.0
Released 07 September 2020. Released 07 September 2020 for SMAPI 3.0.0 or later.
* Added option to change `Mods` folder path. * Added option to change `Mods` folder path.
* Rewrote documentation to make it easier to read. * Rewrote documentation to make it easier to read.
### 3.1.0 ### 3.1.0
Released 01 February 2020. Released 01 February 2020 for SMAPI 3.0.0 or later.
* Added support for semantic versioning 2.0. * Added support for semantic versioning 2.0.
* `0Harmony.dll` is now ignored if the mod references Harmony directly (it's bundled with SMAPI). * `0Harmony.dll` is now ignored if the mod references Harmony directly (it's bundled with SMAPI).
### 3.0.0 ### 3.0.0
Released 26 November 2019. Released 26 November 2019 for SMAPI 3.0.0 or later.
* Updated for SMAPI 3.0 and Stardew Valley 1.4. * Updated for SMAPI 3.0 and Stardew Valley 1.4.
* Added automatic support for `assets` folders. * Added automatic support for `assets` folders.

View File

@ -32,6 +32,7 @@ argument | purpose
`--install` | Preselects the install action, skipping the prompt asking what the user wants to do. `--install` | Preselects the install action, skipping the prompt asking what the user wants to do.
`--uninstall` | Preselects the uninstall action, skipping the prompt asking what the user wants to do. `--uninstall` | Preselects the uninstall action, skipping the prompt asking what the user wants to do.
`--game-path "path"` | Specifies the full path to the folder containing the Stardew Valley executable, skipping automatic detection and any prompt to choose a path. If the path is not valid, the installer displays an error. `--game-path "path"` | Specifies the full path to the folder containing the Stardew Valley executable, skipping automatic detection and any prompt to choose a path. If the path is not valid, the installer displays an error.
`--no-prompt` | Don't let the installer wait for user input (e.g. for cases where it's being run by a script). If the installer is unable to continue without user input, it'll fail instead.
SMAPI itself recognises five arguments, but these are meant for internal use or testing, and might SMAPI itself recognises five arguments, but these are meant for internal use or testing, and might
change without warning. **On Linux/macOS**, command-line arguments won't work; see _environment change without warning. **On Linux/macOS**, command-line arguments won't work; see _environment

View File

@ -47,8 +47,8 @@ namespace StardewModdingApi.Installer
yield return GetInstallPath("StardewModdingAPI.dll"); yield return GetInstallPath("StardewModdingAPI.dll");
yield return GetInstallPath("StardewModdingAPI.exe"); yield return GetInstallPath("StardewModdingAPI.exe");
yield return GetInstallPath("StardewModdingAPI.exe.config"); yield return GetInstallPath("StardewModdingAPI.exe.config");
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // Linux/macOS only yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // before 3.18.4 (Linux/macOS only)
yield return GetInstallPath("StardewModdingAPI.pdb"); // Windows only yield return GetInstallPath("StardewModdingAPI.pdb"); // before 3.18.4 (Windows only)
yield return GetInstallPath("StardewModdingAPI.runtimeconfig.json"); yield return GetInstallPath("StardewModdingAPI.runtimeconfig.json");
yield return GetInstallPath("StardewModdingAPI.xml"); yield return GetInstallPath("StardewModdingAPI.xml");
yield return GetInstallPath("smapi-internal"); yield return GetInstallPath("smapi-internal");
@ -135,35 +135,24 @@ namespace StardewModdingApi.Installer
Console.Title = $"SMAPI {context.GetInstallerVersion()} installer on {context.Platform} {context.PlatformName}"; Console.Title = $"SMAPI {context.GetInstallerVersion()} installer on {context.Platform} {context.PlatformName}";
Console.WriteLine(); Console.WriteLine();
/****
** Check if correct installer
****/
#if SMAPI_FOR_WINDOWS
if (context.IsUnix)
{
this.PrintError($"This is the installer for Windows. Run the 'install on {context.Platform}.{(context.Platform == Platform.Mac ? "command" : "sh")}' file instead.");
Console.ReadLine();
return;
}
#else
if (context.IsWindows)
{
this.PrintError($"This is the installer for Linux/macOS. Run the 'install on Windows.exe' file instead.");
Console.ReadLine();
return;
}
#endif
/**** /****
** read command-line arguments ** read command-line arguments
****/ ****/
// get action from CLI // get input mode
bool allowUserInput = !args.Contains("--no-prompt");
// get action
bool installArg = args.Contains("--install"); bool installArg = args.Contains("--install");
bool uninstallArg = args.Contains("--uninstall"); bool uninstallArg = args.Contains("--uninstall");
if (installArg && uninstallArg) if (installArg && uninstallArg)
{ {
this.PrintError("You can't specify both --install and --uninstall command-line flags."); this.PrintError("You can't specify both --install and --uninstall command-line flags.");
Console.ReadLine(); this.AwaitConfirmation(allowUserInput);
return;
}
if (!allowUserInput && !installArg && !uninstallArg)
{
this.PrintError("You must specify --install or --uninstall when running with --no-prompt.");
return; return;
} }
@ -175,12 +164,31 @@ namespace StardewModdingApi.Installer
gamePathArg = args[pathIndex]; gamePathArg = args[pathIndex];
} }
/****
** Check if correct installer
****/
#if SMAPI_FOR_WINDOWS
if (context.IsUnix)
{
this.PrintError($"This is the installer for Windows. Run the 'install on {context.Platform}.{(context.Platform == Platform.Mac ? "command" : "sh")}' file instead.");
this.AwaitConfirmation(allowUserInput);
return;
}
#else
if (context.IsWindows)
{
this.PrintError($"This is the installer for Linux/macOS. Run the 'install on Windows.exe' file instead.");
this.AwaitConfirmation(allowUserInput);
return;
}
#endif
/********* /*********
** Step 2: choose a theme (can't auto-detect on Linux/macOS) ** Step 2: choose a theme (can't auto-detect on Linux/macOS)
*********/ *********/
MonitorColorScheme scheme = MonitorColorScheme.AutoDetect; MonitorColorScheme scheme = MonitorColorScheme.AutoDetect;
if (context.IsUnix) if (context.IsUnix && allowUserInput)
{ {
/**** /****
** print header ** print header
@ -245,7 +253,7 @@ namespace StardewModdingApi.Installer
if (installDir == null) if (installDir == null)
{ {
this.PrintError("Failed finding your game path."); this.PrintError("Failed finding your game path.");
Console.ReadLine(); this.AwaitConfirmation(allowUserInput);
return; return;
} }
@ -262,7 +270,7 @@ namespace StardewModdingApi.Installer
if (!File.Exists(paths.GameDllPath)) if (!File.Exists(paths.GameDllPath))
{ {
this.PrintError("The detected game install path doesn't contain a Stardew Valley executable."); this.PrintError("The detected game install path doesn't contain a Stardew Valley executable.");
Console.ReadLine(); this.AwaitConfirmation(allowUserInput);
return; return;
} }
Console.Clear(); Console.Clear();
@ -340,7 +348,7 @@ namespace StardewModdingApi.Installer
if (context.IsUnix && File.Exists(paths.BackupLaunchScriptPath)) if (context.IsUnix && File.Exists(paths.BackupLaunchScriptPath))
{ {
this.PrintDebug("Removing SMAPI launcher..."); this.PrintDebug("Removing SMAPI launcher...");
this.InteractivelyDelete(paths.VanillaLaunchScriptPath); this.InteractivelyDelete(paths.VanillaLaunchScriptPath, allowUserInput);
File.Move(paths.BackupLaunchScriptPath, paths.VanillaLaunchScriptPath); File.Move(paths.BackupLaunchScriptPath, paths.VanillaLaunchScriptPath);
} }
@ -352,7 +360,7 @@ namespace StardewModdingApi.Installer
{ {
this.PrintDebug(action == ScriptAction.Install ? "Removing previous SMAPI files..." : "Removing SMAPI files..."); this.PrintDebug(action == ScriptAction.Install ? "Removing previous SMAPI files..." : "Removing SMAPI files...");
foreach (string path in removePaths) foreach (string path in removePaths)
this.InteractivelyDelete(path); this.InteractivelyDelete(path, allowUserInput);
} }
// move global save data folder (changed in 3.2) // move global save data folder (changed in 3.2)
@ -364,7 +372,7 @@ namespace StardewModdingApi.Installer
if (oldDir.Exists) if (oldDir.Exists)
{ {
if (newDir.Exists) if (newDir.Exists)
this.InteractivelyDelete(oldDir.FullName); this.InteractivelyDelete(oldDir.FullName, allowUserInput);
else else
oldDir.MoveTo(newDir.FullName); oldDir.MoveTo(newDir.FullName);
} }
@ -379,7 +387,7 @@ namespace StardewModdingApi.Installer
this.PrintDebug("Adding SMAPI files..."); this.PrintDebug("Adding SMAPI files...");
foreach (FileSystemInfo sourceEntry in paths.BundleDir.EnumerateFileSystemInfos().Where(this.ShouldCopy)) foreach (FileSystemInfo sourceEntry in paths.BundleDir.EnumerateFileSystemInfos().Where(this.ShouldCopy))
{ {
this.InteractivelyDelete(Path.Combine(paths.GameDir.FullName, sourceEntry.Name)); this.InteractivelyDelete(Path.Combine(paths.GameDir.FullName, sourceEntry.Name), allowUserInput);
this.RecursiveCopy(sourceEntry, paths.GameDir); this.RecursiveCopy(sourceEntry, paths.GameDir);
} }
@ -394,7 +402,7 @@ namespace StardewModdingApi.Installer
if (!File.Exists(paths.BackupLaunchScriptPath)) if (!File.Exists(paths.BackupLaunchScriptPath))
File.Move(paths.VanillaLaunchScriptPath, paths.BackupLaunchScriptPath); File.Move(paths.VanillaLaunchScriptPath, paths.BackupLaunchScriptPath);
else else
this.InteractivelyDelete(paths.VanillaLaunchScriptPath); this.InteractivelyDelete(paths.VanillaLaunchScriptPath, allowUserInput);
} }
// add new launcher // add new launcher
@ -464,7 +472,7 @@ namespace StardewModdingApi.Installer
// remove existing folder // remove existing folder
if (targetFolder.Exists) if (targetFolder.Exists)
this.InteractivelyDelete(targetFolder.FullName); this.InteractivelyDelete(targetFolder.FullName, allowUserInput);
// copy files // copy files
this.RecursiveCopy(sourceMod.Directory, paths.ModsDir, filter: this.ShouldCopy); this.RecursiveCopy(sourceMod.Directory, paths.ModsDir, filter: this.ShouldCopy);
@ -482,7 +490,7 @@ namespace StardewModdingApi.Installer
#if SMAPI_DEPRECATED #if SMAPI_DEPRECATED
// remove obsolete appdata mods // remove obsolete appdata mods
this.InteractivelyRemoveAppDataMods(paths.ModsDir, bundledModsDir); this.InteractivelyRemoveAppDataMods(paths.ModsDir, bundledModsDir, allowUserInput);
#endif #endif
} }
} }
@ -513,7 +521,7 @@ namespace StardewModdingApi.Installer
); );
} }
Console.ReadKey(); this.AwaitConfirmation(allowUserInput);
} }
@ -581,7 +589,8 @@ namespace StardewModdingApi.Installer
/// <summary>Interactively delete a file or folder path, and block until deletion completes.</summary> /// <summary>Interactively delete a file or folder path, and block until deletion completes.</summary>
/// <param name="path">The file or folder path.</param> /// <param name="path">The file or folder path.</param>
private void InteractivelyDelete(string path) /// <param name="allowUserInput">Whether the installer can ask for user input from the terminal.</param>
private void InteractivelyDelete(string path, bool allowUserInput)
{ {
while (true) while (true)
{ {
@ -594,7 +603,7 @@ namespace StardewModdingApi.Installer
{ {
this.PrintError($"Oops! The installer couldn't delete {path}: [{ex.GetType().Name}] {ex.Message}."); this.PrintError($"Oops! The installer couldn't delete {path}: [{ex.GetType().Name}] {ex.Message}.");
this.PrintError("Try rebooting your computer and then run the installer again. If that doesn't work, try deleting it yourself then press any key to retry."); this.PrintError("Try rebooting your computer and then run the installer again. If that doesn't work, try deleting it yourself then press any key to retry.");
Console.ReadKey(); this.AwaitConfirmation(allowUserInput);
} }
} }
} }
@ -814,7 +823,8 @@ namespace StardewModdingApi.Installer
/// <summary>Interactively move mods out of the app data directory.</summary> /// <summary>Interactively move mods out of the app data directory.</summary>
/// <param name="properModsDir">The directory which should contain all mods.</param> /// <param name="properModsDir">The directory which should contain all mods.</param>
/// <param name="packagedModsDir">The installer directory containing packaged mods.</param> /// <param name="packagedModsDir">The installer directory containing packaged mods.</param>
private void InteractivelyRemoveAppDataMods(DirectoryInfo properModsDir, DirectoryInfo packagedModsDir) /// <param name="allowUserInput">Whether the installer can ask for user input from the terminal.</param>
private void InteractivelyRemoveAppDataMods(DirectoryInfo properModsDir, DirectoryInfo packagedModsDir, bool allowUserInput)
{ {
// get packaged mods to delete // get packaged mods to delete
string[] packagedModNames = packagedModsDir.GetDirectories().Select(p => p.Name).ToArray(); string[] packagedModNames = packagedModsDir.GetDirectories().Select(p => p.Name).ToArray();
@ -841,7 +851,7 @@ namespace StardewModdingApi.Installer
if (isDir && packagedModNames.Contains(entry.Name, StringComparer.OrdinalIgnoreCase)) if (isDir && packagedModNames.Contains(entry.Name, StringComparer.OrdinalIgnoreCase))
{ {
this.PrintDebug($" Deleting {entry.Name} because it's bundled into SMAPI..."); this.PrintDebug($" Deleting {entry.Name} because it's bundled into SMAPI...");
this.InteractivelyDelete(entry.FullName); this.InteractivelyDelete(entry.FullName, allowUserInput);
continue; continue;
} }
@ -906,5 +916,13 @@ namespace StardewModdingApi.Installer
_ => true _ => true
}; };
} }
/// <summary>Wait until the user presses enter to confirm, if user input is allowed.</summary>
/// <param name="allowUserInput">Whether the installer can ask for user input from the terminal.</param>
private void AwaitConfirmation(bool allowUserInput)
{
if (allowUserInput)
Console.ReadLine();
}
} }
} }

View File

@ -93,7 +93,7 @@ else
# run in terminal # run in terminal
if [ "$USE_CURRENT_SHELL" == "false" ]; then if [ "$USE_CURRENT_SHELL" == "false" ]; then
# select terminal (prefer xterm for best compatibility, then known supported terminals) # select terminal (prefer xterm for best compatibility, then known supported terminals)
for terminal in xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite alacritty mate-terminal x-terminal-emulator; do for terminal in xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite alacritty mate-terminal x-terminal-emulator wezterm; do
if command -v "$terminal" 2>/dev/null; then if command -v "$terminal" 2>/dev/null; then
export TERMINAL_NAME=$terminal export TERMINAL_NAME=$terminal
break; break;
@ -132,6 +132,11 @@ else
exec "$TERMINAL_NAME" -- env TERM=xterm $LAUNCH_FILE "$@" exec "$TERMINAL_NAME" -- env TERM=xterm $LAUNCH_FILE "$@"
;; ;;
wezterm)
# consumes all arguments after start --. does not copy working directory automatically, needs --cwd for that.
exec "$TERMINAL_NAME" start --cwd "$(pwd)" -- env TERM=xterm $LAUNCH_FILE "$@"
;;
kitty) kitty)
# consumes all trailing arguments # consumes all trailing arguments
exec "$TERMINAL_NAME" env TERM=xterm $LAUNCH_FILE "$@" exec "$TERMINAL_NAME" env TERM=xterm $LAUNCH_FILE "$@"

View File

@ -6,9 +6,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" /> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -10,7 +10,7 @@
<!--NuGet package--> <!--NuGet package-->
<PackageId>Pathoschild.Stardew.ModBuildConfig</PackageId> <PackageId>Pathoschild.Stardew.ModBuildConfig</PackageId>
<Title>Build package for SMAPI mods</Title> <Title>Build package for SMAPI mods</Title>
<Version>4.1.0</Version> <Version>4.1.1</Version>
<Authors>Pathoschild</Authors> <Authors>Pathoschild</Authors>
<Description>Automates the build configuration for crossplatform Stardew Valley SMAPI mods. For SMAPI 3.13.0 or later.</Description> <Description>Automates the build configuration for crossplatform Stardew Valley SMAPI mods. For SMAPI 3.13.0 or later.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
@ -24,7 +24,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10" /> <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<!-- <!--
This is imported through Microsoft.Build.Utilities.Core. When installed by a mod, NuGet This is imported through Microsoft.Build.Utilities.Core. When installed by a mod, NuGet

View File

@ -8,8 +8,9 @@
** Set build options ** Set build options
**********************************************--> **********************************************-->
<PropertyGroup> <PropertyGroup>
<!-- enable line numbers in stack traces --> <!-- enable line numbers in stack traces (needs to embedded for Linux/macOS: https://github.com/dotnet/runtime/issues/39987) -->
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<!-- don't create the 'refs' folder (which isn't useful for mods) --> <!-- don't create the 'refs' folder (which isn't useful for mods) -->
<ProduceReferenceAssembly>false</ProduceReferenceAssembly> <ProduceReferenceAssembly>false</ProduceReferenceAssembly>

View File

@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
@ -10,11 +9,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/********* /*********
** Fields ** Fields
*********/ *********/
/// <summary>The number of seconds for which to log input.</summary> /// <summary>Whether the command should print input.</summary>
private readonly int LogSeconds = 30; private bool Enabled;
/// <summary>When the command should stop printing input, or <c>null</c> if currently disabled.</summary>
private long? ExpiryTicks;
/********* /*********
@ -30,21 +26,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/// <param name="args">The command arguments.</param> /// <param name="args">The command arguments.</param>
public override void Handle(IMonitor monitor, string command, ArgumentParser args) public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{ {
this.ExpiryTicks = DateTime.UtcNow.Add(TimeSpan.FromSeconds(this.LogSeconds)).Ticks; this.Enabled = !this.Enabled;
monitor.Log($"OK, logging all player input for {this.LogSeconds} seconds.", LogLevel.Info);
}
/// <summary>Perform any logic needed on update tick.</summary> monitor.Log(
/// <param name="monitor">Writes messages to the console and log file.</param> this.Enabled ? "OK, logging all player input until you run this command again." : "OK, no longer logging player input.",
public override void OnUpdated(IMonitor monitor) LogLevel.Info
{ );
// handle expiry
if (this.ExpiryTicks != null && this.ExpiryTicks <= DateTime.UtcNow.Ticks)
{
monitor.Log("No longer logging input.", LogLevel.Info);
this.ExpiryTicks = null;
return;
}
} }
/// <summary>Perform any logic when input is received.</summary> /// <summary>Perform any logic when input is received.</summary>
@ -52,7 +39,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
/// <param name="button">The button that was pressed.</param> /// <param name="button">The button that was pressed.</param>
public override void OnButtonPressed(IMonitor monitor, SButton button) public override void OnButtonPressed(IMonitor monitor, SButton button)
{ {
if (this.ExpiryTicks != null) if (this.Enabled)
monitor.Log($"Pressed {button}", LogLevel.Info); monitor.Log($"Pressed {button}", LogLevel.Info);
} }
} }

View File

@ -1,9 +1,9 @@
{ {
"Name": "Console Commands", "Name": "Console Commands",
"Author": "SMAPI", "Author": "SMAPI",
"Version": "3.18.3", "Version": "3.18.4",
"Description": "Adds SMAPI console commands that let you manipulate the game.", "Description": "Adds SMAPI console commands that let you manipulate the game.",
"UniqueID": "SMAPI.ConsoleCommands", "UniqueID": "SMAPI.ConsoleCommands",
"EntryDll": "ConsoleCommands.dll", "EntryDll": "ConsoleCommands.dll",
"MinimumApiVersion": "3.18.3" "MinimumApiVersion": "3.18.4"
} }

View File

@ -1,9 +1,9 @@
{ {
"Name": "Error Handler", "Name": "Error Handler",
"Author": "SMAPI", "Author": "SMAPI",
"Version": "3.18.3", "Version": "3.18.4",
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.", "Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
"UniqueID": "SMAPI.ErrorHandler", "UniqueID": "SMAPI.ErrorHandler",
"EntryDll": "ErrorHandler.dll", "EntryDll": "ErrorHandler.dll",
"MinimumApiVersion": "3.18.3" "MinimumApiVersion": "3.18.4"
} }

View File

@ -1,9 +1,9 @@
{ {
"Name": "Save Backup", "Name": "Save Backup",
"Author": "SMAPI", "Author": "SMAPI",
"Version": "3.18.3", "Version": "3.18.4",
"Description": "Automatically backs up all your saves once per day into its folder.", "Description": "Automatically backs up all your saves once per day into its folder.",
"UniqueID": "SMAPI.SaveBackup", "UniqueID": "SMAPI.SaveBackup",
"EntryDll": "SaveBackup.dll", "EntryDll": "SaveBackup.dll",
"MinimumApiVersion": "3.18.3" "MinimumApiVersion": "3.18.4"
} }

View File

@ -14,12 +14,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.8.0" /> <PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<PackageReference Include="Moq" Version="4.18.4" /> <PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" /> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -73,7 +73,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
return GameFolderType.NoGameFound; return GameFolderType.NoGameFound;
// apparently valid // apparently valid
if (dir.EnumerateFiles("Stardew Valley.dll").Any()) if (File.Exists(Path.Combine(dir.FullName, "Stardew Valley.dll")))
return GameFolderType.Valid; return GameFolderType.Valid;
// doesn't contain any version of Stardew Valley // doesn't contain any version of Stardew Valley

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization.Models; using StardewModdingAPI.Toolkit.Serialization.Models;
using StardewModdingAPI.Toolkit.Utilities; using StardewModdingAPI.Toolkit.Utilities;
@ -9,14 +10,25 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <summary>The info about a mod read from its folder.</summary> /// <summary>The info about a mod read from its folder.</summary>
public class ModFolder public class ModFolder
{ {
/*********
** Fields
*********/
/// <summary>The backing field for <see cref="Directory"/>.</summary>
private DirectoryInfo? DirectoryImpl;
/********* /*********
** Accessors ** Accessors
*********/ *********/
/// <summary>A suggested display name for the mod folder.</summary> /// <summary>A suggested display name for the mod folder.</summary>
public string DisplayName { get; } public string DisplayName { get; }
/// <summary>The folder path containing the mod's manifest.json.</summary>
public string DirectoryPath { get; }
/// <summary>The folder containing the mod's manifest.json.</summary> /// <summary>The folder containing the mod's manifest.json.</summary>
public DirectoryInfo Directory { get; } [JsonIgnore]
public DirectoryInfo Directory => this.DirectoryImpl ??= new DirectoryInfo(this.DirectoryPath);
/// <summary>The mod type.</summary> /// <summary>The mod type.</summary>
public ModType Type { get; } public ModType Type { get; }
@ -52,7 +64,8 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
public ModFolder(DirectoryInfo root, DirectoryInfo directory, ModType type, Manifest? manifest, ModParseError manifestParseError, string? manifestParseErrorText) public ModFolder(DirectoryInfo root, DirectoryInfo directory, ModType type, Manifest? manifest, ModParseError manifestParseError, string? manifestParseErrorText)
{ {
// save info // save info
this.Directory = directory; this.DirectoryImpl = directory;
this.DirectoryPath = directory.FullName;
this.Type = type; this.Type = type;
this.Manifest = manifest; this.Manifest = manifest;
this.ManifestParseError = manifestParseError; this.ManifestParseError = manifestParseError;
@ -64,6 +77,24 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
: PathUtilities.GetRelativePath(root.FullName, directory.FullName); : PathUtilities.GetRelativePath(root.FullName, directory.FullName);
} }
/// <summary>Construct an instance.</summary>
/// <param name="displayName">A suggested display name for the mod folder.</param>
/// <param name="directoryPath">The folder path containing the mod's manifest.json.</param>
/// <param name="type">The mod type.</param>
/// <param name="manifest">The mod manifest.</param>
/// <param name="manifestParseError">The error which occurred parsing the manifest, if any.</param>
/// <param name="manifestParseErrorText">A human-readable message for the <paramref name="manifestParseError"/>, if any.</param>
[JsonConstructor]
public ModFolder(string displayName, string directoryPath, ModType type, Manifest? manifest, ModParseError manifestParseError, string? manifestParseErrorText)
{
this.DisplayName = displayName;
this.DirectoryPath = directoryPath;
this.Type = type;
this.Manifest = manifest;
this.ManifestParseError = manifestParseError;
this.ManifestParseErrorText = manifestParseErrorText;
}
/// <summary>Get the update keys for a mod.</summary> /// <summary>Get the update keys for a mod.</summary>
/// <param name="manifest">The mod manifest.</param> /// <param name="manifest">The mod manifest.</param>
public IEnumerable<string> GetUpdateKeys(Manifest manifest) public IEnumerable<string> GetUpdateKeys(Manifest manifest)

View File

@ -9,8 +9,8 @@
<Import Project="..\..\build\common.targets" /> <Import Project="..\..\build\common.targets" />
<ItemGroup> <ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.48" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" /> <PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" />
<PackageReference Include="System.Management" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" /> <PackageReference Include="System.Management" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />

View File

@ -44,6 +44,9 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
string path = reader.Path; string path = reader.Path;
switch (reader.TokenType) switch (reader.TokenType)
{ {
case JsonToken.Null:
return null;
case JsonToken.StartObject: case JsonToken.StartObject:
return this.ReadObject(JObject.Load(reader)); return this.ReadObject(JObject.Load(reader));

View File

@ -15,15 +15,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.14.1" /> <PackageReference Include="Azure.Storage.Blobs" Version="12.16.0" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.32" /> <PackageReference Include="Hangfire.AspNetCore" Version="1.8.2" />
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" /> <PackageReference Include="Hangfire.MemoryStorage" Version="1.8.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.48" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" /> <PackageReference Include="JetBrains.Annotations" Version="2022.3.1" />
<PackageReference Include="Markdig" Version="0.30.4" /> <PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.1" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.8" />
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" /> <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
<PackageReference Include="Pathoschild.FluentNexus" Version="1.0.5" /> <PackageReference Include="Pathoschild.FluentNexus" Version="1.0.5" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" /> <PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" />
</ItemGroup> </ItemGroup>

View File

@ -43,7 +43,7 @@
"description": "The DLL filename SMAPI should load for this mod. Mutually exclusive with ContentPackFor.", "description": "The DLL filename SMAPI should load for this mod. Mutually exclusive with ContentPackFor.",
"type": "string", "type": "string",
"pattern": "^[a-zA-Z0-9_.-]+\\.dll$", "pattern": "^[a-zA-Z0-9_.-]+\\.dll$",
"examples": "LookupAnything.dll", "examples": ["LookupAnything.dll"],
"@errorMessages": { "@errorMessages": {
"pattern": "Invalid value; must be a filename ending with .dll." "pattern": "Invalid value; must be a filename ending with .dll."
} }

View File

@ -52,7 +52,7 @@ namespace StardewModdingAPI
internal static int? LogScreenId { get; set; } internal static int? LogScreenId { get; set; }
/// <summary>SMAPI's current raw semantic version.</summary> /// <summary>SMAPI's current raw semantic version.</summary>
internal static string RawApiVersion = "3.18.3"; internal static string RawApiVersion = "3.18.4";
} }
/// <summary>Contains SMAPI's constants and assumptions.</summary> /// <summary>Contains SMAPI's constants and assumptions.</summary>

View File

@ -88,7 +88,7 @@ namespace StardewModdingAPI
public static bool IsSplitScreen => LocalMultiplayer.IsLocalMultiplayer(); public static bool IsSplitScreen => LocalMultiplayer.IsLocalMultiplayer();
/// <summary>Whether there are players connected over the network.</summary> /// <summary>Whether there are players connected over the network.</summary>
public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly; public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly && Game1.getOnlineFarmers().Count > 1;
/// <summary>Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer.</summary> /// <summary>Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer.</summary>
public static bool IsMainPlayer => Game1.IsMasterGame && Context.ScreenId == 0 && TitleMenu.subMenu is not FarmhandMenu; public static bool IsMainPlayer => Game1.IsMasterGame && Context.ScreenId == 0 && TitleMenu.subMenu is not FarmhandMenu;
@ -97,8 +97,7 @@ namespace StardewModdingAPI
/********* /*********
** Public methods ** Public methods
*********/ *********/
/// <summary>Get whether a screen ID is still active.</summary> /// <summary>Get whether a screen ID is still active.</summary> <param name="id">The screen ID.</param>
/// <param name="id">The screen ID.</param>
public static bool HasScreenId(int id) public static bool HasScreenId(int id)
{ {
return Context.ActiveScreenIds.Contains(id); return Context.ActiveScreenIds.Contains(id);

View File

@ -436,6 +436,10 @@ namespace StardewModdingAPI.Framework.ContentManagers
tilesheet.ImageSource = this.NormalizePathSeparators(tilesheet.ImageSource); tilesheet.ImageSource = this.NormalizePathSeparators(tilesheet.ImageSource);
string imageSource = tilesheet.ImageSource; string imageSource = tilesheet.ImageSource;
// validate image source
if (string.IsNullOrWhiteSpace(imageSource))
throw new SContentLoadException(ContentLoadErrorType.InvalidData, $"{this.ModName} loaded map '{relativeMapPath}' with invalid tilesheet '{tilesheet.Id}'. This tilesheet has no image source.");
// reverse incorrect eager tilesheet path prefixing // reverse incorrect eager tilesheet path prefixing
if (fixEagerPathPrefixes && relativeMapFolder.Length > 0 && imageSource.StartsWith(relativeMapFolder)) if (fixEagerPathPrefixes && relativeMapFolder.Length > 0 && imageSource.StartsWith(relativeMapFolder))
imageSource = imageSource[(relativeMapFolder.Length + 1)..]; imageSource = imageSource[(relativeMapFolder.Length + 1)..];

View File

@ -852,6 +852,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log(context); this.Monitor.Log(context);
// add context to window titles
this.UpdateWindowTitles();
// raise events // raise events
this.OnLoadStageChanged(LoadStage.Ready); this.OnLoadStageChanged(LoadStage.Ready);
events.SaveLoaded.RaiseEmpty(); events.SaveLoaded.RaiseEmpty();
@ -1203,6 +1206,7 @@ namespace StardewModdingAPI.Framework
case LoadStage.None: case LoadStage.None:
this.JustReturnedToTitle = true; this.JustReturnedToTitle = true;
this.UpdateWindowTitles();
break; break;
case LoadStage.Loaded: case LoadStage.Loaded:
@ -1450,15 +1454,14 @@ namespace StardewModdingAPI.Framework
string consoleTitle = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}"; string consoleTitle = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}";
string gameTitle = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}"; string gameTitle = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}";
string suffix = "";
if (this.ModRegistry.AreAllModsLoaded) if (this.ModRegistry.AreAllModsLoaded)
{ suffix += $" with {this.ModRegistry.GetAll().Count()} mods";
int modsLoaded = this.ModRegistry.GetAll().Count(); if (Context.IsMultiplayer)
consoleTitle += $" with {modsLoaded} mods"; suffix += $" [{(Context.IsMainPlayer ? "main player" : "farmhand")}]";
gameTitle += $" with {modsLoaded} mods";
}
this.Game.Window.Title = gameTitle; this.Game.Window.Title = gameTitle + suffix;
this.LogManager.SetConsoleTitle(consoleTitle); this.LogManager.SetConsoleTitle(consoleTitle + suffix);
} }
/// <summary>Log a warning if software known to cause issues is installed.</summary> /// <summary>Log a warning if software known to cause issues is installed.</summary>

View File

@ -23,9 +23,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.4" /> <PackageReference Include="Mono.Cecil" Version="0.11.4" />
<PackageReference Include="MonoMod.Common" Version="22.3.5.1" /> <PackageReference Include="MonoMod.Common" Version="22.3.5.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" /> <PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" />
<PackageReference Include="Pintail" Version="2.2.2" /> <PackageReference Include="Pintail" Version="2.3.0" />
<PackageReference Include="Platonymous.TMXTile" Version="1.5.9" /> <PackageReference Include="Platonymous.TMXTile" Version="1.5.9" />
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" /> <PackageReference Include="System.Reflection.Emit" Version="4.7.0" />