Merge remote-tracking branch 'pathoschild/stable' into develop

# Conflicts:
#	src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
#	src/SMAPI/Framework/SCore.cs
This commit is contained in:
yangzhi 2023-07-18 09:48:01 +08:00
commit bfb0adb849
28 changed files with 204 additions and 126 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">
<PropertyGroup>
<!--set general build properties -->
<Version>3.18.3</Version>
<Version>3.18.4</Version>
<Product>SMAPI</Product>
<LangVersion>latest</LangVersion>
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
<DefineConstants Condition="$(OS) == 'Windows_NT' AND '$(BUILD_FOR_MOBILE)' == ''">$(DefineConstants);SMAPI_DEPRECATED;SMAPI_FOR_WINDOWS</DefineConstants>
<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-->
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
<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).exe" 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)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.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>
<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="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" />
</Target>
@ -70,12 +68,10 @@ This assumes `find-game-folder.targets` has already been imported and validated.
<!-- toolkit -->
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit'" AfterTargets="PostBuildEvent">
<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" />
</Target>
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces'" AfterTargets="PostBuildEvent">
<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" />
</Target>
</Project>

View File

@ -122,7 +122,7 @@ for folder in ${folders[@]}; do
fi
# 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
name="$name.exe"
fi
@ -134,7 +134,7 @@ for folder in ${folders[@]}; do
cp -r "$smapiBin/i18n" "$bundlePath/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"
done
@ -164,7 +164,6 @@ for folder in ${folders[@]}; do
mkdir "$targetPath" --parents
cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/$modName.pdb" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath"
if [ -d "$fromPath/i18n" ]; then
cp -r "$fromPath/i18n" "$targetPath"

View File

@ -142,7 +142,7 @@ foreach ($folder in $folders) {
}
# 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") {
$name = "$name.exe"
}
@ -154,7 +154,7 @@ foreach ($folder in $folders) {
cp -Recurse "$smapiBin/i18n" "$bundlePath/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"
}
@ -189,7 +189,6 @@ foreach ($folder in $folders) {
mkdir "$targetPath" > $null
cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/$modName.pdb" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath"
if (Test-Path "$fromPath/i18n" -PathType Container) {
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._
-->
## 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
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 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).
* Improved translations. Thganks to stylemate (updated Korean)!
* Improved translations. Thanks to stylemate (updated Korean)!
* For mod authors:
* 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.
## 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
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!).
* Fixed game DLLs not excluded from the release zip when they're referenced explicitly but `BundleExtraAssemblies` isn't set.
### 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!).
* Fixed `BundleExtraAssemblies` option being partly case-sensitive.
* Fixed `BundleExtraAssemblies` not applying `All` value to game assemblies.
### 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.
* Fixed "_conflicts between different versions of Microsoft.Win32.Registry_" warnings in recent SMAPI versions.
* Internal refactoring.
### 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.)
* Added `IgnoreModFilePaths` option to ignore literal paths.
@ -459,7 +464,7 @@ Released 30 November 2021.
documentation](#configure).
### 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 `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.
### 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.
* Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder)
files to the ignore list.
### 3.2.1
Released 11 September 2020.
Released 11 September 2020 for SMAPI 3.0.0 or later.
* Added more detailed logging.
* Fixed _path's format is not supported_ error when using default `Mods` path in 3.2.
### 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.
* Rewrote documentation to make it easier to read.
### 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.
* `0Harmony.dll` is now ignored if the mod references Harmony directly (it's bundled with SMAPI).
### 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.
* 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.
`--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.
`--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
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.exe");
yield return GetInstallPath("StardewModdingAPI.exe.config");
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // Linux/macOS only
yield return GetInstallPath("StardewModdingAPI.pdb"); // Windows only
yield return GetInstallPath("StardewModdingAPI.exe.mdb"); // before 3.18.4 (Linux/macOS only)
yield return GetInstallPath("StardewModdingAPI.pdb"); // before 3.18.4 (Windows only)
yield return GetInstallPath("StardewModdingAPI.runtimeconfig.json");
yield return GetInstallPath("StardewModdingAPI.xml");
yield return GetInstallPath("smapi-internal");
@ -135,35 +135,24 @@ namespace StardewModdingApi.Installer
Console.Title = $"SMAPI {context.GetInstallerVersion()} installer on {context.Platform} {context.PlatformName}";
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
****/
// get action from CLI
// get input mode
bool allowUserInput = !args.Contains("--no-prompt");
// get action
bool installArg = args.Contains("--install");
bool uninstallArg = args.Contains("--uninstall");
if (installArg && uninstallArg)
{
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;
}
@ -175,12 +164,31 @@ namespace StardewModdingApi.Installer
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)
*********/
MonitorColorScheme scheme = MonitorColorScheme.AutoDetect;
if (context.IsUnix)
if (context.IsUnix && allowUserInput)
{
/****
** print header
@ -245,7 +253,7 @@ namespace StardewModdingApi.Installer
if (installDir == null)
{
this.PrintError("Failed finding your game path.");
Console.ReadLine();
this.AwaitConfirmation(allowUserInput);
return;
}
@ -262,7 +270,7 @@ namespace StardewModdingApi.Installer
if (!File.Exists(paths.GameDllPath))
{
this.PrintError("The detected game install path doesn't contain a Stardew Valley executable.");
Console.ReadLine();
this.AwaitConfirmation(allowUserInput);
return;
}
Console.Clear();
@ -340,7 +348,7 @@ namespace StardewModdingApi.Installer
if (context.IsUnix && File.Exists(paths.BackupLaunchScriptPath))
{
this.PrintDebug("Removing SMAPI launcher...");
this.InteractivelyDelete(paths.VanillaLaunchScriptPath);
this.InteractivelyDelete(paths.VanillaLaunchScriptPath, allowUserInput);
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...");
foreach (string path in removePaths)
this.InteractivelyDelete(path);
this.InteractivelyDelete(path, allowUserInput);
}
// move global save data folder (changed in 3.2)
@ -364,7 +372,7 @@ namespace StardewModdingApi.Installer
if (oldDir.Exists)
{
if (newDir.Exists)
this.InteractivelyDelete(oldDir.FullName);
this.InteractivelyDelete(oldDir.FullName, allowUserInput);
else
oldDir.MoveTo(newDir.FullName);
}
@ -379,7 +387,7 @@ namespace StardewModdingApi.Installer
this.PrintDebug("Adding SMAPI files...");
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);
}
@ -394,7 +402,7 @@ namespace StardewModdingApi.Installer
if (!File.Exists(paths.BackupLaunchScriptPath))
File.Move(paths.VanillaLaunchScriptPath, paths.BackupLaunchScriptPath);
else
this.InteractivelyDelete(paths.VanillaLaunchScriptPath);
this.InteractivelyDelete(paths.VanillaLaunchScriptPath, allowUserInput);
}
// add new launcher
@ -464,7 +472,7 @@ namespace StardewModdingApi.Installer
// remove existing folder
if (targetFolder.Exists)
this.InteractivelyDelete(targetFolder.FullName);
this.InteractivelyDelete(targetFolder.FullName, allowUserInput);
// copy files
this.RecursiveCopy(sourceMod.Directory, paths.ModsDir, filter: this.ShouldCopy);
@ -482,7 +490,7 @@ namespace StardewModdingApi.Installer
#if SMAPI_DEPRECATED
// remove obsolete appdata mods
this.InteractivelyRemoveAppDataMods(paths.ModsDir, bundledModsDir);
this.InteractivelyRemoveAppDataMods(paths.ModsDir, bundledModsDir, allowUserInput);
#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>
/// <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)
{
@ -594,7 +603,7 @@ namespace StardewModdingApi.Installer
{
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.");
Console.ReadKey();
this.AwaitConfirmation(allowUserInput);
}
}
}
@ -814,7 +823,8 @@ namespace StardewModdingApi.Installer
/// <summary>Interactively move mods out of the app data directory.</summary>
/// <param name="properModsDir">The directory which should contain all 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
string[] packagedModNames = packagedModsDir.GetDirectories().Select(p => p.Name).ToArray();
@ -841,7 +851,7 @@ namespace StardewModdingApi.Installer
if (isDir && packagedModNames.Contains(entry.Name, StringComparer.OrdinalIgnoreCase))
{
this.PrintDebug($" Deleting {entry.Name} because it's bundled into SMAPI...");
this.InteractivelyDelete(entry.FullName);
this.InteractivelyDelete(entry.FullName, allowUserInput);
continue;
}
@ -906,5 +916,13 @@ namespace StardewModdingApi.Installer
_ => 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
if [ "$USE_CURRENT_SHELL" == "false" ]; then
# 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
export TERMINAL_NAME=$terminal
break;
@ -132,6 +132,11 @@ else
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)
# consumes all trailing arguments
exec "$TERMINAL_NAME" env TERM=xterm $LAUNCH_FILE "$@"

View File

@ -6,9 +6,9 @@
<ItemGroup>
<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="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>

View File

@ -10,7 +10,7 @@
<!--NuGet package-->
<PackageId>Pathoschild.Stardew.ModBuildConfig</PackageId>
<Title>Build package for SMAPI mods</Title>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
<Authors>Pathoschild</Authors>
<Description>Automates the build configuration for crossplatform Stardew Valley SMAPI mods. For SMAPI 3.13.0 or later.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
@ -24,7 +24,7 @@
<ItemGroup>
<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

View File

@ -8,8 +8,9 @@
** Set build options
**********************************************-->
<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>
<DebugType>embedded</DebugType>
<!-- don't create the 'refs' folder (which isn't useful for mods) -->
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>

View File

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

View File

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

View File

@ -1,9 +1,9 @@
{
"Name": "Error Handler",
"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.",
"UniqueID": "SMAPI.ErrorHandler",
"EntryDll": "ErrorHandler.dll",
"MinimumApiVersion": "3.18.3"
"MinimumApiVersion": "3.18.4"
}

View File

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

View File

@ -14,12 +14,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.8.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<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="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>

View File

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

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization.Models;
using StardewModdingAPI.Toolkit.Utilities;
@ -9,14 +10,25 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <summary>The info about a mod read from its folder.</summary>
public class ModFolder
{
/*********
** Fields
*********/
/// <summary>The backing field for <see cref="Directory"/>.</summary>
private DirectoryInfo? DirectoryImpl;
/*********
** Accessors
*********/
/// <summary>A suggested display name for the mod folder.</summary>
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>
public DirectoryInfo Directory { get; }
[JsonIgnore]
public DirectoryInfo Directory => this.DirectoryImpl ??= new DirectoryInfo(this.DirectoryPath);
/// <summary>The mod type.</summary>
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)
{
// save info
this.Directory = directory;
this.DirectoryImpl = directory;
this.DirectoryPath = directory.FullName;
this.Type = type;
this.Manifest = manifest;
this.ManifestParseError = manifestParseError;
@ -64,6 +77,24 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
: 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>
/// <param name="manifest">The mod manifest.</param>
public IEnumerable<string> GetUpdateKeys(Manifest manifest)

View File

@ -17,8 +17,8 @@
</ItemGroup>
<ItemGroup Condition="'$(BUILD_FOR_MOBILE)' == ''">
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.48" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.3.0" />
<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'" />

View File

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

View File

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

View File

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

View File

@ -74,7 +74,11 @@ namespace StardewModdingAPI
internal static int? LogScreenId { get; set; }
/// <summary>SMAPI's current raw semantic version.</summary>
internal static string RawApiVersion = "3.18.3";
#if !SMAPI_FOR_MOBILE
internal static string RawApiVersion = "3.18.4";
#else
internal static string RawApiVersion = "3.18.4.1";
#endif
}
/// <summary>Contains SMAPI's constants and assumptions.</summary>
@ -91,9 +95,6 @@ namespace StardewModdingAPI
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion);
#else
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion);
/// <summary>Android SMAPI's current semantic version.</summary>
public static ISemanticVersion AndroidApiVersion { get; } = new Toolkit.SemanticVersion("0.8.8");
#endif
/// <summary>The minimum supported version of Stardew Valley.</summary>

View File

@ -88,7 +88,7 @@ namespace StardewModdingAPI
public static bool IsSplitScreen => LocalMultiplayer.IsLocalMultiplayer();
/// <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>
#if SMAPI_FOR_MOBILE
@ -100,8 +100,7 @@ namespace StardewModdingAPI
/*********
** Public methods
*********/
/// <summary>Get whether a screen ID is still active.</summary>
/// <param name="id">The screen ID.</param>
/// <summary>Get whether a screen ID is still active.</summary> <param name="id">The screen ID.</param>
public static bool HasScreenId(int id)
{
return Context.ActiveScreenIds.Contains(id);

View File

@ -462,6 +462,10 @@ namespace StardewModdingAPI.Framework.ContentManagers
tilesheet.ImageSource = this.NormalizePathSeparators(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
if (fixEagerPathPrefixes && relativeMapFolder.Length > 0 && imageSource.StartsWith(relativeMapFolder))
imageSource = imageSource[(relativeMapFolder.Length + 1)..];

View File

@ -968,6 +968,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log(context);
// add context to window titles
this.UpdateWindowTitles();
// raise events
this.OnLoadStageChanged(LoadStage.Ready);
events.SaveLoaded.RaiseEmpty();
@ -1355,6 +1358,7 @@ namespace StardewModdingAPI.Framework
case LoadStage.None:
this.JustReturnedToTitle = true;
this.UpdateWindowTitles();
break;
case LoadStage.Loaded:
@ -1606,15 +1610,14 @@ namespace StardewModdingAPI.Framework
string consoleTitle = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}";
string gameTitle = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}";
string suffix = "";
if (this.ModRegistry.AreAllModsLoaded)
{
int modsLoaded = this.ModRegistry.GetAll().Count();
consoleTitle += $" with {modsLoaded} mods";
gameTitle += $" with {modsLoaded} mods";
}
suffix += $" with {this.ModRegistry.GetAll().Count()} mods";
if (Context.IsMultiplayer)
suffix += $" [{(Context.IsMainPlayer ? "main player" : "farmhand")}]";
this.Game.Window.Title = gameTitle;
this.LogManager.SetConsoleTitle(consoleTitle);
this.Game.Window.Title = gameTitle + suffix;
this.LogManager.SetConsoleTitle(consoleTitle + suffix);
#endif
}

View File

@ -76,9 +76,9 @@
<ItemGroup Condition="'$(BUILD_FOR_MOBILE)' == ''">
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
<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="Pintail" Version="2.2.2" />
<PackageReference Include="Pintail" Version="2.3.0" />
<PackageReference Include="Platonymous.TMXTile" Version="1.5.9" />
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />