diff --git a/.editorconfig b/.editorconfig index d600d602..2aeaeadd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,6 +22,9 @@ insert_final_newline = false [README.txt] end_of_line=crlf +[*.{command,sh}] +end_of_line=lf + ########## ## C# formatting ## documentation: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference diff --git a/.gitattributes b/.gitattributes index 1161a204..00ae145b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ # normalize line endings * text=auto -README.txt text=crlf +README.txt text eol=crlf + +*.command text eol=lf +*.sh text eol=lf diff --git a/build/common.targets b/build/common.targets index 578076a9..1021c2a1 100644 --- a/build/common.targets +++ b/build/common.targets @@ -1,13 +1,29 @@ - 3.13.1 + 3.13.2 SMAPI latest $(AssemblySearchPaths);{GAC} $(DefineConstants);SMAPI_FOR_WINDOWS + true + + + None + + + $(NoWarn);CS0436;CA1416;CS0809;NU1701 @@ -19,7 +35,7 @@ - + diff --git a/build/prepare-install-package.targets b/build/prepare-install-package.targets deleted file mode 100644 index ef5624ad..00000000 --- a/build/prepare-install-package.targets +++ /dev/null @@ -1,130 +0,0 @@ - - - - - windows - unix - - $(SolutionDir) - $(SolutionDir)\..\bin - - $(BuildRootPath)\SMAPI\bin\$(Configuration) - $(BuildRootPath)\SMAPI.Toolkit\bin\$(Configuration)\net5.0 - $(BuildRootPath)\SMAPI.Mods.ConsoleCommands\bin\$(Configuration) - $(BuildRootPath)\SMAPI.Mods.ErrorHandler\bin\$(Configuration) - $(BuildRootPath)\SMAPI.Mods.SaveBackup\bin\$(Configuration) - - $(OutRootPath)\SMAPI installer - $(OutRootPath)\SMAPI installer for developers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/unix/prepare-install-package.sh b/build/unix/prepare-install-package.sh new file mode 100644 index 00000000..9a89f8d4 --- /dev/null +++ b/build/unix/prepare-install-package.sh @@ -0,0 +1,211 @@ +#!/bin/bash + +# +# +# This is the Bash equivalent of ../windows/prepare-install-package.ps1. +# When making changes, both scripts should be updated. +# +# + + +########## +## Constants +########## +# paths +gamePath="/home/pathoschild/Stardew Valley" +bundleModNames=("ConsoleCommands" "ErrorHandler" "SaveBackup") + +# build configuration +buildConfig="Release" +folders=("linux" "macOS" "windows") +declare -A runtimes=(["linux"]="linux-x64" ["macOS"]="osx-x64" ["windows"]="win-x64") +declare -A msBuildPlatformNames=(["linux"]="Unix" ["macOS"]="OSX" ["windows"]="Windows_NT") + + +########## +## Move to SMAPI root +########## +cd "`dirname "$0"`/../.." + + +########## +## Clear old build files +########## +echo "Clearing old builds..." +echo "-------------------------------------------------" +for path in bin */**/bin */**/obj; do + echo "$path" + rm -rf $path +done +echo "" + +########## +## Compile files +########## +for folder in ${folders[@]}; do + runtime=${runtimes[$folder]} + msbuildPlatformName=${msBuildPlatformNames[$folder]} + + echo "Compiling SMAPI for $folder..." + echo "-------------------------------------------------" + dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true + echo "" + echo "" + + echo "Compiling installer for $folder..." + echo "-------------------------------------------------" + dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" -p:PublishTrimmed=True -p:TrimMode=Link --self-contained true + echo "" + echo "" + + for modName in ${bundleModNames[@]}; do + echo "Compiling $modName for $folder..." + echo "-------------------------------------------------" + dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" + echo "" + echo "" + done +done + + +########## +## Prepare install package +########## +echo "Preparing install package..." +echo "-------------------------------------------------" + +# init paths +installAssets="src/SMAPI.Installer/assets" +packagePath="bin/SMAPI installer" +packageDevPath="bin/SMAPI installer for developers" + +# init structure +for folder in ${folders[@]}; do + mkdir "$packagePath/internal/$folder/bundle/smapi-internal" --parents +done + +# copy base installer files +for name in "install on Linux.sh" "install on macOS.command" "install on Windows.bat" "README.txt"; do + cp "$installAssets/$name" "$packagePath" +done + +# copy per-platform files +for folder in ${folders[@]}; do + runtime=${runtimes[$folder]} + + # get paths + smapiBin="src/SMAPI/bin/$buildConfig/$runtime/publish" + internalPath="$packagePath/internal/$folder" + bundlePath="$internalPath/bundle" + + # installer files + cp -r "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish"/* "$internalPath" + rm -rf "$internalPath/assets" + + # runtime config for SMAPI + # This is identical to the one generated by the build, except that the min runtime version is + # set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of + # minor. + cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json" + + # installer DLL config + if [ $folder == "windows" ]; then + cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config" + fi + + # bundle root files + for name in "StardewModdingAPI" "StardewModdingAPI.dll" "StardewModdingAPI.pdb" "StardewModdingAPI.xml" "steam_appid.txt"; do + if [ $name == "StardewModdingAPI" ] && [ $folder == "windows" ]; then + name="$name.exe" + fi + + cp "$smapiBin/$name" "$bundlePath" + done + + # bundle i18n + 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" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.pdb" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.pdb" "SMAPI.Toolkit.CoreInterfaces.xml"; do + cp "$smapiBin/$name" "$bundlePath/smapi-internal" + done + + cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json" + cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json" + if [ $folder == "linux" ] || [ $folder == "macOS" ]; then + cp "$installAssets/unix-launcher.sh" "$bundlePath" + cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal" + else + cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config" + fi + + # copy .NET dependencies + cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal" + cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal" + cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal" + if [ $folder == "windows" ]; then + cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal" + fi + + # copy bundled mods + for modName in ${bundleModNames[@]}; do + fromPath="src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish" + targetPath="$bundlePath/Mods/$modName" + + 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" + fi + done +done + +# mark scripts executable +for path in "install on Linux.sh" "install on macOS.command" "bundle/unix-launcher.sh"; do + if [ -f "$packagePath/$path" ]; then + chmod 755 "$packagePath/$path" + fi +done + +# split into main + for-dev folders +cp -r "$packagePath" "$packageDevPath" +for folder in ${folders[@]}; do + # disable developer mode in main package + sed --in-place --expression="s/\"DeveloperMode\": true/\"DeveloperMode\": false/" "$packagePath/internal/$folder/bundle/smapi-internal/config.json" + + # convert bundle folder into final 'install.dat' files + for path in "$packagePath/internal/$folder" "$packageDevPath/internal/$folder"; do + pushd "$path/bundle" > /dev/null + zip "install.dat" * --recurse-paths --quiet + popd > /dev/null + mv "$path/bundle/install.dat" "$path/install.dat" + rm -rf "$path/bundle" + done +done + + +########## +## Create release zips +########## +# get version number +version="$1" +if [ $# -eq 0 ]; then + echo "SMAPI release version (like '4.0.0'):" + read version +fi + +# rename folders +mv "$packagePath" "bin/SMAPI $version installer" +mv "$packageDevPath" "bin/SMAPI $version installer for developers" + +# package files +pushd bin > /dev/null +zip -9 "SMAPI $version installer.zip" "SMAPI $version installer" --recurse-paths --quiet +zip -9 "SMAPI $version installer for developers.zip" "SMAPI $version installer for developers" --recurse-paths --quiet +popd > /dev/null + +echo "" +echo "Done! Package created in $(pwd)/bin" diff --git a/build/unix/set-smapi-version.sh b/build/unix/set-smapi-version.sh new file mode 100644 index 00000000..0c0cbeb0 --- /dev/null +++ b/build/unix/set-smapi-version.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# +# +# This is the Bash equivalent of ../windows/set-smapi-version.ps1. +# When making changes, both scripts should be updated. +# +# + + +# get version number +version="$1" +if [ $# -eq 0 ]; then + echo "SMAPI release version (like '4.0.0'):" + read version +fi + +# move to SMAPI root +cd "`dirname "$0"`/../.." + +# apply changes +sed "s/.+<\/Version>/$version<\/Version>/" "build/common.targets" --in-place --regexp-extended +sed "s/RawApiVersion = \".+?\";/RawApiVersion = \"$version\";/" "src/SMAPI/Constants.cs" --in-place --regexp-extended +for modName in "ConsoleCommands" "ErrorHandler" "SaveBackup"; do + sed "s/\"(Version|MinimumApiVersion)\": \".+?\"/\"\1\": \"$version\"/g" "src/SMAPI.Mods.$modName/manifest.json" --in-place --regexp-extended +done diff --git a/build/windows/finalize-install-package.sh b/build/windows/finalize-install-package.sh new file mode 100755 index 00000000..0996e3ed --- /dev/null +++ b/build/windows/finalize-install-package.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +########## +## Read config +########## +# get SMAPI version +version="$1" +if [ $# -eq 0 ]; then + echo "SMAPI release version (like '4.0.0'):" + read version +fi + +# get Windows bin path +windowsBinPath="$2" +if [ $# -le 1 ]; then + echo "Windows compiled bin path:" + read windowsBinPath +fi + +# installer internal folders +buildFolders=("linux" "macOS" "windows") + + +########## +## Finalize release package +########## +for folderName in "SMAPI $version installer" "SMAPI $version installer for developers"; do + # move files to Linux filesystem + echo "Preparing $folderName.zip..." + echo "-------------------------------------------------" + echo "copying '$windowsBinPath/$folderName' to Linux filesystem..." + cp -r "$windowsBinPath/$folderName" . + + # fix permissions + echo "fixing permissions..." + find "$folderName" -type d -exec chmod 755 {} \; + find "$folderName" -type f -exec chmod 644 {} \; + find "$folderName" -name "*.sh" -exec chmod 755 {} \; + find "$folderName" -name "*.command" -exec chmod 755 {} \; + find "$folderName" -name "SMAPI.Installer" -exec chmod 755 {} \; + find "$folderName" -name "StardewModdingAPI" -exec chmod 755 {} \; + + # convert bundle folder into final 'install.dat' files + for build in ${buildFolders[@]}; do + echo "packaging $folderName/internal/$build/install.dat..." + pushd "$folderName/internal/$build/bundle" > /dev/null + zip "install.dat" * --recurse-paths --quiet + mv install.dat ../ + popd > /dev/null + + rm -rf "$folderName/internal/$build/bundle" + done + + # zip installer + echo "packaging installer..." + zip -9 "$folderName.zip" "$folderName" --recurse-paths --quiet + + # move zip back to Windows bin path + echo "moving release zip to $windowsBinPath/$folderName.zip..." + mv "$folderName.zip" "$windowsBinPath" + rm -rf "$folderName" + + echo "" + echo "" +done + +echo "Done!" diff --git a/build/windows/lib/in-place-regex.ps1 b/build/windows/lib/in-place-regex.ps1 new file mode 100644 index 00000000..7b309342 --- /dev/null +++ b/build/windows/lib/in-place-regex.ps1 @@ -0,0 +1,11 @@ +function In-Place-Regex { + param ( + [Parameter(Mandatory)][string]$Path, + [Parameter(Mandatory)][string]$Search, + [Parameter(Mandatory)][string]$Replace + ) + + $content = (Get-Content "$Path" -Encoding UTF8) + $content = ($content -replace "$Search", "$Replace") + [System.IO.File]::WriteAllLines((Get-Item "$Path").FullName, $content) +} diff --git a/build/windows/prepare-install-package.ps1 b/build/windows/prepare-install-package.ps1 new file mode 100644 index 00000000..db7fadcb --- /dev/null +++ b/build/windows/prepare-install-package.ps1 @@ -0,0 +1,217 @@ +# +# +# This is the PowerShell equivalent of ../unix/prepare-install-package.sh, *except* that it doesn't +# set Linux permissions, create the install.dat files, or create the final zip. Due to limitations +# in PowerShell, the final changes are handled by the windows/finalize-install-package.sh file in +# WSL. +# +# When making changes, make sure to update ../unix/prepare-install-package.ps1 too. +# +# + + +. "$PSScriptRoot\lib\in-place-regex.ps1" + +########## +## Constants +########## +# paths +$gamePath = "C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley" +$bundleModNames = "ConsoleCommands", "ErrorHandler", "SaveBackup" + +# build configuration +$buildConfig = "Release" +$folders = "linux", "macOS", "windows" +$runtimes = @{ linux = "linux-x64"; macOS = "osx-x64"; windows = "win-x64" } +$msBuildPlatformNames = @{ linux = "Unix"; macOS = "OSX"; windows = "Windows_NT" } + + +########## +## Move to SMAPI root +########## +cd "$PSScriptRoot/../.." + + +########## +## Clear old build files +########## +echo "Clearing old builds..." +echo "-------------------------------------------------" + +foreach ($path in (dir -Recurse -Include ('bin', 'obj'))) { + echo "$path" + rm -Recurse -Force "$path" +} +echo "" + + +########## +## Compile files +########## +ForEach ($folder in $folders) { + $runtime = $runtimes[$folder] + $msbuildPlatformName = $msBuildPlatformNames[$folder] + + echo "Compiling SMAPI for $folder..." + echo "-------------------------------------------------" + dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true + echo "" + echo "" + + echo "Compiling installer for $folder..." + echo "-------------------------------------------------" + dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" -p:PublishTrimmed=True -p:TrimMode=Link --self-contained true + echo "" + echo "" + + foreach ($modName in $bundleModNames) { + echo "Compiling $modName for $folder..." + echo "-------------------------------------------------" + dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" + echo "" + echo "" + } +} + + +########## +## Prepare install package +########## +echo "Preparing install package..." +echo "----------------------------" + +# init paths +$installAssets = "src/SMAPI.Installer/assets" +$packagePath = "bin/SMAPI installer" +$packageDevPath = "bin/SMAPI installer for developers" + +# init structure +foreach ($folder in $folders) { + mkdir "$packagePath/internal/$folder/bundle/smapi-internal" > $null +} + +# copy base installer files +foreach ($name in @("install on Linux.sh", "install on macOS.command", "install on Windows.bat", "README.txt")) { + cp "$installAssets/$name" "$packagePath" +} + +# copy per-platform files +foreach ($folder in $folders) { + $runtime = $runtimes[$folder] + + # get paths + $smapiBin = "src/SMAPI/bin/$buildConfig/$runtime/publish" + $internalPath = "$packagePath/internal/$folder" + $bundlePath = "$internalPath/bundle" + + # installer files + cp "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish/*" "$internalPath" -Recurse + rm -Recurse -Force "$internalPath/assets" + + # runtime config for SMAPI + # This is identical to the one generated by the build, except that the min runtime version is + # set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of + # minor. + cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json" + + # installer DLL config + if ($folder -eq "windows") { + cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config" + } + + # bundle root files + foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.pdb", "StardewModdingAPI.xml", "steam_appid.txt")) { + if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") { + $name = "$name.exe" + } + + cp "$smapiBin/$name" "$bundlePath" + } + + # bundle i18n + 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", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.pdb", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.pdb", "SMAPI.Toolkit.CoreInterfaces.xml")) { + cp "$smapiBin/$name" "$bundlePath/smapi-internal" + } + + cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json" + cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json" + if ($folder -eq "linux" -or $folder -eq "macOS") { + cp "$installAssets/unix-launcher.sh" "$bundlePath" + cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal" + } + else { + cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config" + } + + # copy .NET dependencies + cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal" + cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal" + cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal" + if ($folder -eq "windows") { + cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal" + } + + # copy bundled mods + foreach ($modName in $bundleModNames) { + $fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish" + $targetPath = "$bundlePath/Mods/$modName" + + 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" + } + } +} + +# DISABLED: will be handled by Linux script +# mark scripts executable +#ForEach ($path in @("install on Linux.sh", "install on macOS.command", "bundle/unix-launcher.sh")) { +# if (Test-Path "$packagePath/$path" -PathType Leaf) { +# chmod 755 "$packagePath/$path" +# } +#} + +# split into main + for-dev folders +cp -Recurse "$packagePath" "$packageDevPath" +foreach ($folder in $folders) { + # disable developer mode in main package + In-Place-Regex -Path "$packagePath/internal/$folder/bundle/smapi-internal/config.json" -Search "`"DeveloperMode`": true" -Replace "`"DeveloperMode`": false" + + # DISABLED: will be handled by Linux script + # convert bundle folder into final 'install.dat' files + #foreach ($path in @("$packagePath/internal/$folder", "$packageDevPath/internal/$folder")) + #{ + # Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.zip" + # mv "$path/install.zip" "$path/install.dat" + # rm -Recurse -Force "$path/bundle" + #} +} + + +########### +### Create release zips +########### +# get version number +$version = $args[0] +if (!$version) { + $version = Read-Host "SMAPI release version (like '4.0.0')" +} + +# rename folders +mv "$packagePath" "bin/SMAPI $version installer" +mv "$packageDevPath" "bin/SMAPI $version installer for developers" + +# DISABLED: will be handled by Linux script +## package files +#Compress-Archive -Path "bin/SMAPI $version installer" -DestinationPath "bin/SMAPI $version installer.zip" -CompressionLevel Optimal +#Compress-Archive -Path "bin/SMAPI $version installer for developers" -DestinationPath "bin/SMAPI $version installer for developers.zip" -CompressionLevel Optimal + +echo "" +echo "Done! See docs/technical/smapi.md to create the release zips." diff --git a/build/windows/set-smapi-version.ps1 b/build/windows/set-smapi-version.ps1 new file mode 100644 index 00000000..ff6b2096 --- /dev/null +++ b/build/windows/set-smapi-version.ps1 @@ -0,0 +1,25 @@ +# +# +# This is the PowerShell equivalent of ../unix/set-smapi-version.sh. +# When making changes, both scripts should be updated. +# +# + + +. "$PSScriptRoot\lib\in-place-regex.ps1" + +# get version number +$version=$args[0] +if (!$version) { + $version = Read-Host "SMAPI release version (like '4.0.0')" +} + +# move to SMAPI root +cd "$PSScriptRoot/../.." + +# apply changes +In-Place-Regex -Path "build/common.targets" -Search ".+" -Replace "$version" +In-Place-Regex -Path "src/SMAPI/Constants.cs" -Search "RawApiVersion = `".+?`";" -Replace "RawApiVersion = `"$version`";" +ForEach ($modName in "ConsoleCommands","ErrorHandler","SaveBackup") { + In-Place-Regex -Path "src/SMAPI.Mods.$modName/manifest.json" -Search "`"(Version|MinimumApiVersion)`": `".+?`"" -Replace "`"`$1`": `"$version`"" +} diff --git a/docs/release-notes.md b/docs/release-notes.md index d50a923d..499fa322 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,11 +1,27 @@ ← [README](README.md) # Release notes +## 3.13.2 +Released 05 December 2021 for Stardew Valley 1.5.5 or later. + +* For players: + * You no longer need .NET 5 to install or use SMAPI. + * The installer now detects when the game folder contains an incompatible legacy game version. + * Updated for the latest Stardew Valley 1.5.5 hotfix. + * Updated compatibility list. + +* For the web UI: + * Fixed the JSON validator marking `.fnt` files invalid in Content Patcher files. + +* For SMAPI maintainers: + * Added [release package scripts](technical/smapi.md) to streamline preparing SMAPI releases. + ## 3.13.1 Released 30 November 2021 for Stardew Valley 1.5.5 or later. -* Improved .NET 5 validation in Windows installer to better explain how to get the right version. -* Fixed installer failing on Windows when run from the game folder. +* For players: + * Improved .NET 5 validation in Windows installer to better explain how to get the right version. + * Fixed installer failing on Windows when run from the game folder. ## 3.13.0 Released 30 November 2021 for Stardew Valley 1.5.5 or later. diff --git a/docs/technical/mod-package.md b/docs/technical/mod-package.md index 93e0009d..41f808a5 100644 --- a/docs/technical/mod-package.md +++ b/docs/technical/mod-package.md @@ -412,7 +412,9 @@ The NuGet package is generated automatically in `StardewModdingAPI.ModBuildConfi when you compile it. ## Release notes -## Upcoming release +## 4.0.0 +Released 30 November 2021. + * 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 `BundleExtraAssemblies` option to copy bundled DLLs into the mod zip/folder. diff --git a/docs/technical/smapi.md b/docs/technical/smapi.md index 29fa43a8..d9aad875 100644 --- a/docs/technical/smapi.md +++ b/docs/technical/smapi.md @@ -11,11 +11,12 @@ This document is about SMAPI itself; see also [mod build package](mod-package.md * [Configuration file](#configuration-file) * [Command-line arguments](#command-line-arguments) * [Compile flags](#compile-flags) -* [For SMAPI developers](#for-smapi-developers) - * [Compiling from source](#compiling-from-source) - * [Debugging a local build](#debugging-a-local-build) - * [Preparing a release](#preparing-a-release) - * [Using a custom Harmony build](#using-a-custom-harmony-build) +* [Compile from source code](#compile-from-source-code) + * [Main project](#main-project) + * [Custom Harmony build](#custom-harmony-build) +* [Prepare a release](#prepare-a-release) + * [On any platform](#on-any-platform) + * [On Windows](#on-windows) * [Release notes](#release-notes) ## Customisation @@ -58,39 +59,42 @@ flag | purpose ---- | ------- `SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/macOS. Set automatically in `common.targets`. -## For SMAPI developers -### Compiling from source +## Compile from source code +### Main project Using an official SMAPI release is recommended for most users, but you can compile from source -directly if needed. There are no special steps (just open the project and compile), but SMAPI often -uses the latest C# syntax. You may need the latest version of your IDE to compile it. +directly if needed. Just open the project in an IDE like [Visual +Studio](https://visualstudio.microsoft.com/vs/community/) or [Rider](https://www.jetbrains.com/rider/), +and build the `SMAPI` project. The project will automatically adjust the build settings for your +current OS and Stardew Valley install path. -SMAPI uses build configuration derived from the [crossplatform mod config](https://smapi.io/package/readme) -to detect your current OS automatically and load the correct references. Compile output will be -placed in a `bin` folder at the root of the Git repository. - -### Debugging a local build Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting -the `SMAPI` project with debugging from Visual Studio (on macOS or Windows) will launch SMAPI with -the debugger attached, so you can intercept errors and step through the code being executed. That -doesn't work in MonoDevelop on Linux, unfortunately. +the `SMAPI` project with debugging from Visual Studio or Rider should launch SMAPI with the +debugger attached, so you can intercept errors and step through the code being executed. -### Preparing a release -To prepare a crossplatform SMAPI release, you'll need to compile it on two platforms: Windows and -Linux. The instructions below assume you have Windows 11, but you can adapt them for -a different setup if needed. +### Custom Harmony build +SMAPI uses [a custom build of Harmony](https://github.com/Pathoschild/Harmony#readme), which is +included in the `build` folder. To use a different build, just replace `0Harmony.dll` in that +folder before compiling. -#### Initial setup -First-time setup on Windows: -1. [Install Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install). -2. Install the needed software in WSL: - 1. Run `sudo apt update` to update the package list. - 2. Install [the .NET 5 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu) - (for Stardew Valley 1.5.5+) or [`mono-complete`](https://www.mono-project.com/download/stable/) - (for earlier versions). - _You can run `lsb_release -a` to get the Ubuntu version number._ - 3. [Install Steam](https://linuxconfig.org/how-to-install-steam-on-ubuntu-20-04-focal-fossa-linux). - 4. Launch `steam` and install the game like usual. - 5. Download and install your preferred IDE. For the [latest standalone Rider +## Prepare a release +### On any platform +**⚠ Ideally we'd have one set of instructions for all platforms. The instructions in this section +will produce a fully functional release for all supported platfrms, _except_ that the application +icon for SMAPI on Windows will disappear due to [.NET runtime bug +3828](https://github.com/dotnet/runtime/issues/3828). Until that's fixed, see the _[on +Windows](#on-windows)_ section below to create a build that retains the icon.** + +#### First-time setup +1. On Windows only: + 1. [Install Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install). + 2. Run `sudo apt update` in WSL to update the package list. + 3. The rest of the instructions below should be run in WSL. +2. Install the required software: + 1. Install the [.NET 5 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu). + _For Ubuntu-based systems, you can run `lsb_release -a` to get the Ubuntu version number._ + 2. [Install Steam](https://linuxconfig.org/how-to-install-steam-on-ubuntu-20-04-focal-fossa-linux). + 3. Launch `steam` and install the game like usual. + 4. Download and install your preferred IDE. For the [latest standalone Rider version](https://www.jetbrains.com/help/rider/Installation_guide.html#prerequisites): ```sh wget "" -O rider-install.tar.gz @@ -98,47 +102,67 @@ First-time setup on Windows: ln -s "/opt/JetBrains Rider-/bin/rider.sh" ./rider.sh ``` - 3. Clone the SMAPI repo in WSL: - ```sh - git clone https://github.com/Pathoschild/SMAPI.git - ``` +3. Clone the SMAPI repo: + ```sh + git clone https://github.com/Pathoschild/SMAPI.git + ``` -To compile SMAPI in WSL: -1. Run `./rider.sh` to open the Rider GUI. -2. Use the GUI to compile the solution. - -To launch the game: -1. Open a WSL terminal. -2. Run these commands to start Steam: +### Launch the game +1. Run these commands to start Steam: ```sh export TERM=xterm steam ``` -3. Launch the game through the Steam UI. +2. Launch the game through the Steam UI. -#### Prepare the release -1. Update the version numbers in `build/common.targets`, `Constants`, and the `manifest.json` for - bundled mods. Make sure you use a [semantic version](https://semver.org). Recommended format: +### Prepare the release +1. Run `build/unix/set-smapi-version.sh` to set the SMAPI version. Make sure you use a [semantic + version](https://semver.org). Recommended format: build type | format | example :--------- | :----------------------- | :------ - dev build | `-alpha.` | `3.0.0-alpha.20171230` - prerelease | `-beta.` | `3.0.0-beta.20171230` - release | `` | `3.0.0` -2. In Windows: - 1. Rebuild the solution with the _release_ solution configuration. - 2. Copy the `bin/SMAPI installer` and `bin/SMAPI installer for developers` folders to Linux. -4. In Linux: - 1. Rebuild the solution with the _release_ solution configuration. - 2. Add the `windows-install.*` files from Windows to the `bin/SMAPI installer` and - `bin/SMAPI installer for developers` folders compiled on Linux. - 3. Rename the folders to `SMAPI installer` and `SMAPI installer for developers`. - 4. Zip the two folders. + dev build | `-alpha.` | `4.0.0-alpha.20251230` + prerelease | `-beta.` | `4.0.0-beta.20251230` + release | `` | `4.0.0` -### Custom Harmony build -SMAPI uses [a custom build of Harmony](https://github.com/Pathoschild/Harmony#readme), which is -included in the `build` folder. To use a different build, just replace `0Harmony.dll` in that -folder before compiling. +2. Run `build/unix/prepare-install-package.sh` to create the release package in the root `bin` + folder. + +### On Windows +#### First-time setup +1. Set up Windows Subsystem for Linux (WSL): + 1. [Install WSL](https://docs.microsoft.com/en-us/windows/wsl/install). + 2. Run `sudo apt update` in WSL to update the package list. + 3. The rest of the instructions below should be run in WSL. +2. Install the required software: + 1. Install the [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0). + 2. Install [Stardew Valley](https://www.stardewvalley.net/). +3. Clone the SMAPI repo: + ```sh + git clone https://github.com/Pathoschild/SMAPI.git + ``` + +### Prepare the release +1. Run `build/windows/set-smapi-version.ps1` in PowerShell to set the SMAPI version. Make sure you + use a [semantic version](https://semver.org). Recommended format: + + build type | format | example + :--------- | :----------------------- | :------ + dev build | `-alpha.` | `4.0.0-alpha.20251230` + prerelease | `-beta.` | `4.0.0-beta.20251230` + release | `` | `4.0.0` + +2. Run `build/windows/prepare-install-package.ps1` in PowerShell to create the release package + folders in the root `bin` folder. + +3. Launch WSL and run this script: + ```bash + # edit to match the build created in steps 1-2 + # In WSL, `/mnt/c/example` accesses `C:\example` on the Windows filesystem. + version="4.0.0" + binFolder="/mnt/e/source/_Stardew/SMAPI/bin" + build/windows/finalize-install-package.sh "$version" "$binFolder" + ``` ## Release notes See [release notes](../release-notes.md). diff --git a/src/SMAPI.Installer/Framework/InstallerContext.cs b/src/SMAPI.Installer/Framework/InstallerContext.cs index 95df32ca..bb973230 100644 --- a/src/SMAPI.Installer/Framework/InstallerContext.cs +++ b/src/SMAPI.Installer/Framework/InstallerContext.cs @@ -54,5 +54,12 @@ namespace StardewModdingAPI.Installer.Framework { return this.GameScanner.LooksLikeGameFolder(dir); } + + /// Get whether a folder seems to contain the game, and which version it contains if so. + /// The folder to check. + public GameFolderType GetGameFolderType(DirectoryInfo dir) + { + return this.GameScanner.GetGameFolderType(dir); + } } } diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index d8c27a2d..1257f12b 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -10,6 +10,7 @@ using StardewModdingAPI.Installer.Framework; using StardewModdingAPI.Internal.ConsoleWriting; using StardewModdingAPI.Toolkit; using StardewModdingAPI.Toolkit.Framework; +using StardewModdingAPI.Toolkit.Framework.GameScanning; using StardewModdingAPI.Toolkit.Framework.ModScanning; using StardewModdingAPI.Toolkit.Utilities; @@ -633,18 +634,39 @@ namespace StardewModdingApi.Installer // use specified path if (specifiedPath != null) { + string errorPrefix = $"You specified --game-path \"{specifiedPath}\", but"; + var dir = new DirectoryInfo(specifiedPath); if (!dir.Exists) { - this.PrintError($"You specified --game-path \"{specifiedPath}\", but that folder doesn't exist."); + this.PrintError($"{errorPrefix} that folder doesn't exist."); return null; } - if (!context.LooksLikeGameFolder(dir)) + + switch (context.GetGameFolderType(dir)) { - this.PrintError($"You specified --game-path \"{specifiedPath}\", but that folder doesn't contain the Stardew Valley executable."); - return null; + case GameFolderType.Valid: + return dir; + + case GameFolderType.Legacy154OrEarlier: + this.PrintWarning($"{errorPrefix} that directory seems to have Stardew Valley 1.5.4 or earlier."); + this.PrintWarning("Please update your game to the latest version to use SMAPI."); + return null; + + case GameFolderType.LegacyCompatibilityBranch: + this.PrintWarning($"{errorPrefix} that directory seems to have the Stardew Valley legacy 'compatibility' branch."); + this.PrintWarning("Unfortunately SMAPI is only compatible with the modern version of the game."); + this.PrintWarning("Please update your game to the main branch to use SMAPI."); + return null; + + case GameFolderType.NoGameFound: + this.PrintWarning($"{errorPrefix} that directory doesn't contain a Stardew Valley executable."); + return null; + + default: + this.PrintWarning($"{errorPrefix} that directory doesn't seem to contain a valid game install."); + return null; } - return dir; } // let user choose detected path @@ -702,15 +724,32 @@ namespace StardewModdingApi.Installer this.PrintWarning("That directory doesn't seem to exist."); continue; } - if (!context.LooksLikeGameFolder(directory)) - { - this.PrintWarning("That directory doesn't contain a Stardew Valley executable."); - continue; - } - // looks OK - this.PrintInfo(" OK!"); - return directory; + switch (context.GetGameFolderType(directory)) + { + case GameFolderType.Valid: + this.PrintInfo(" OK!"); + return directory; + + case GameFolderType.Legacy154OrEarlier: + this.PrintWarning("That directory seems to have Stardew Valley 1.5.4 or earlier."); + this.PrintWarning("Please update your game to the latest version to use SMAPI."); + continue; + + case GameFolderType.LegacyCompatibilityBranch: + this.PrintWarning("That directory seems to have the Stardew Valley legacy 'compatibility' branch."); + this.PrintWarning("Unfortunately SMAPI is only compatible with the modern version of the game."); + this.PrintWarning("Please update your game to the main branch to use SMAPI."); + continue; + + case GameFolderType.NoGameFound: + this.PrintWarning("That directory doesn't contain a Stardew Valley executable."); + continue; + + default: + this.PrintWarning("That directory doesn't seem to contain a valid game install."); + continue; + } } } diff --git a/src/SMAPI.Installer/SMAPI.Installer.csproj b/src/SMAPI.Installer/SMAPI.Installer.csproj index e3e01467..928e5c18 100644 --- a/src/SMAPI.Installer/SMAPI.Installer.csproj +++ b/src/SMAPI.Installer/SMAPI.Installer.csproj @@ -17,5 +17,4 @@ - diff --git a/src/SMAPI.Installer/assets/README.txt b/src/SMAPI.Installer/assets/README.txt index 5c20529a..08e99887 100644 --- a/src/SMAPI.Installer/assets/README.txt +++ b/src/SMAPI.Installer/assets/README.txt @@ -14,22 +14,27 @@ SMAPI lets you run Stardew Valley with mods. Don't forget to download mods separately. -Player's guide +Automated install -------------------------------- See https://stardewvalleywiki.com/Modding:Player_Guide for help installing SMAPI, adding mods, etc. Manual install -------------------------------- -THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See instructions above instead. +THIS IS NOT RECOMMENDED FOR MOST PLAYERS. See the instructions above instead. If you really want to install SMAPI manually, here's how. -1. Unzip "internal/windows/install.dat" (on Windows) or "internal/unix/install.dat" (on - Linux/macOS). You can change '.dat' to '.zip', it's just a normal zip file renamed to prevent +1. Unzip "internal/windows/install.dat" (on Windows) or "internal/unix/install.dat" (on Linux or + macOS). You can change '.dat' to '.zip', it's just a normal zip file renamed to prevent confusion. + 2. Copy the files from the folder you just unzipped into your game folder. The `StardewModdingAPI.exe` file should be right next to the game's executable. -3. + +3. Copy `Stardew Valley.deps.json` in the game folder, and rename the copy to + `StardewModdingAPI.deps.json`. + +4. - Windows only: if you use Steam, see the install guide above to enable achievements and overlay. Otherwise, just run StardewModdingAPI.exe in your game folder to play with mods. @@ -38,8 +43,5 @@ If you really want to install SMAPI manually, here's how. play with mods. When installing on Linux or macOS: -- Make sure Mono is installed (normally the installer checks for you). While it's not required, - many mods won't work correctly without it. (Specifically, mods which load PNG images may crash or - freeze the game.) - To configure the color scheme, edit the `smapi-internal/config.json` file and see instructions there for the 'ColorScheme' setting. diff --git a/src/SMAPI.Installer/assets/install on Linux.sh b/src/SMAPI.Installer/assets/install on Linux.sh new file mode 100644 index 00000000..3b7eae9c --- /dev/null +++ b/src/SMAPI.Installer/assets/install on Linux.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd "`dirname "$0"`" +internal/linux/SMAPI.Installer diff --git a/src/SMAPI.Installer/assets/windows-install.bat b/src/SMAPI.Installer/assets/install on Windows.bat similarity index 54% rename from src/SMAPI.Installer/assets/windows-install.bat rename to src/SMAPI.Installer/assets/install on Windows.bat index e34b9554..b0d9ae81 100644 --- a/src/SMAPI.Installer/assets/windows-install.bat +++ b/src/SMAPI.Installer/assets/install on Windows.bat @@ -12,36 +12,6 @@ if %ERRORLEVEL% EQU 0 ( exit ) -REM make sure .NET 5 is installed -SET hasNet5=1 -WHERE dotnet /q -if !ERRORLEVEL! NEQ 0 ( - SET hasNet5=0 -) else ( - dotnet --info | findstr /C:"Microsoft.WindowsDesktop.App 5." 1>nul - if !ERRORLEVEL! NEQ 0 ( - SET hasNet5=0 - ) -) -if "%hasNet5%" == "0" ( - echo Oops! You don't have the required .NET version installed. - echo. - echo To install it: - echo 1. Go to https://dotnet.microsoft.com/download/dotnet/5.0/runtime - - if "%PROCESSOR_ARCHITECTURE%" == "ARM64" ( - echo 2. Under "Run desktop apps", click "Download Arm64". - ) else ( - echo 2. Under "Run desktop apps", click "Download x64". - ) - - echo 3. Run the downloaded installer. - echo 4. Restart your computer. - echo. - pause - exit -) - REM make sure an antivirus hasn't deleted the installer DLL if not exist %installerDir%"internal\windows\SMAPI.Installer.dll" ( echo Oops! SMAPI is missing one of its files. Your antivirus might have deleted it. @@ -50,9 +20,16 @@ if not exist %installerDir%"internal\windows\SMAPI.Installer.dll" ( pause exit ) +if not exist %installerDir%"internal\windows\SMAPI.Installer.exe" ( + echo Oops! SMAPI is missing one of its files. Your antivirus might have deleted it. + echo Missing file: %installerDir%internal\windows\SMAPI.Installer.exe + echo. + pause + exit +) REM start installer -dotnet internal\windows\SMAPI.Installer.dll +internal\windows\SMAPI.Installer.exe REM keep window open if it failed if %ERRORLEVEL% NEQ 0 ( diff --git a/src/SMAPI.Installer/assets/install on macOS.command b/src/SMAPI.Installer/assets/install on macOS.command new file mode 100644 index 00000000..abd21dc8 --- /dev/null +++ b/src/SMAPI.Installer/assets/install on macOS.command @@ -0,0 +1,6 @@ +#!/bin/bash + +cd "`dirname "$0"`" + +xattr -r -d com.apple.quarantine internal +internal/macOS/SMAPI.Installer diff --git a/src/SMAPI.Installer/assets/runtimeconfig.unix.json b/src/SMAPI.Installer/assets/runtimeconfig.json similarity index 50% rename from src/SMAPI.Installer/assets/runtimeconfig.unix.json rename to src/SMAPI.Installer/assets/runtimeconfig.json index 8a01ceb1..34018b8a 100644 --- a/src/SMAPI.Installer/assets/runtimeconfig.unix.json +++ b/src/SMAPI.Installer/assets/runtimeconfig.json @@ -4,11 +4,13 @@ "includedFrameworks": [ { "name": "Microsoft.NETCore.App", - "version": "5.0.7" + "version": "5.0.0", + "rollForward": "latestMinor" } ], "configProperties": { - "System.Runtime.TieredCompilation": false + "System.Runtime.TieredCompilation": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false } } } diff --git a/src/SMAPI.Installer/assets/runtimeconfig.windows.json b/src/SMAPI.Installer/assets/runtimeconfig.windows.json deleted file mode 100644 index b693d809..00000000 --- a/src/SMAPI.Installer/assets/runtimeconfig.windows.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "runtimeOptions": { - "tfm": "net5.0", - "framework": { - "name": "Microsoft.NETCore.App", - "version": "5.0.0" - }, - "configProperties": { - "System.Runtime.TieredCompilation": false - } - } -} diff --git a/src/SMAPI.Installer/assets/unix-install.sh b/src/SMAPI.Installer/assets/unix-install.sh deleted file mode 100644 index 07df4e6c..00000000 --- a/src/SMAPI.Installer/assets/unix-install.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Move to script's directory -cd "`dirname "$0"`" - -# make sure .NET 5 is installed -if ! command -v dotnet >/dev/null 2>&1; then - echo "Oops! You must have .NET 5 installed to use SMAPI: https://dotnet.microsoft.com/download"; - read - exit 1 -fi - -# run installer -dotnet internal/unix/SMAPI.Installer.dll diff --git a/src/SMAPI.Installer/assets/unix-launcher.sh b/src/SMAPI.Installer/assets/unix-launcher.sh index 58f7a5ae..e8b9ae62 100644 --- a/src/SMAPI.Installer/assets/unix-launcher.sh +++ b/src/SMAPI.Installer/assets/unix-launcher.sh @@ -49,20 +49,13 @@ if [ ! -f "Stardew Valley.dll" ]; then exit 1 fi -# .NET 5 must be installed -if ! command -v dotnet >/dev/null 2>&1; then - echo "Oops! You must have .NET 5 installed to use SMAPI: https://dotnet.microsoft.com/download"; - read - exit 1 -fi - ########## ## Launch SMAPI ########## # macOS if [ "$(uname)" == "Darwin" ]; then - dotnet StardewModdingAPI.dll "$@" + ./StardewModdingAPI "$@" # Linux else diff --git a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs index 1efc1616..e03c72de 100644 --- a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs +++ b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs @@ -315,7 +315,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer return false; // conversion to implemented interface is OK - if (fromType.AllInterfaces.Contains(toType)) + if (fromType.AllInterfaces.Contains(toType, SymbolEqualityComparer.Default)) return false; // avoid any other conversions diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index ac6ff6ea..216a4c32 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,9 +1,9 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "3.13.1", + "Version": "3.13.2", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "3.13.1" + "MinimumApiVersion": "3.13.2" } diff --git a/src/SMAPI.Mods.ErrorHandler/i18n/pl.json b/src/SMAPI.Mods.ErrorHandler/i18n/pl.json index 05a9d411..f080bcd4 100644 --- a/src/SMAPI.Mods.ErrorHandler/i18n/pl.json +++ b/src/SMAPI.Mods.ErrorHandler/i18n/pl.json @@ -1,4 +1,4 @@ -{ - // warning messages - "warn.invalid-content-removed": "Nieprawidłowa zawartość została usunięta, aby zapobiec awarii (zobacz konsolę SMAPI po więcej informacji)." -} +{ + // warning messages + "warn.invalid-content-removed": "Nieprawidłowa zawartość została usunięta, aby zapobiec awarii (zobacz konsolę SMAPI po więcej informacji)." +} diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json index e19a6a7f..beb52020 100644 --- a/src/SMAPI.Mods.ErrorHandler/manifest.json +++ b/src/SMAPI.Mods.ErrorHandler/manifest.json @@ -1,9 +1,9 @@ { "Name": "Error Handler", "Author": "SMAPI", - "Version": "3.13.1", + "Version": "3.13.2", "Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.", "UniqueID": "SMAPI.ErrorHandler", "EntryDll": "ErrorHandler.dll", - "MinimumApiVersion": "3.13.1" + "MinimumApiVersion": "3.13.2" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index 3e55ce42..2bd20a63 100644 --- a/src/SMAPI.Mods.SaveBackup/manifest.json +++ b/src/SMAPI.Mods.SaveBackup/manifest.json @@ -1,9 +1,9 @@ { "Name": "Save Backup", "Author": "SMAPI", - "Version": "3.13.1", + "Version": "3.13.2", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "3.13.1" + "MinimumApiVersion": "3.13.2" } diff --git a/src/SMAPI.Toolkit/Framework/GameScanning/GameFolderType.cs b/src/SMAPI.Toolkit/Framework/GameScanning/GameFolderType.cs new file mode 100644 index 00000000..d18af59b --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/GameScanning/GameFolderType.cs @@ -0,0 +1,21 @@ +namespace StardewModdingAPI.Toolkit.Framework.GameScanning +{ + /// The detected validity for a Stardew Valley game folder based on file structure heuristics. + public enum GameFolderType + { + /// The folder seems to contain a valid Stardew Valley 1.5.5+ install. + Valid, + + /// The folder doesn't contain Stardew Valley. + NoGameFound, + + /// The folder contains Stardew Valley 1.5.4 or earlier. This version uses XNA Framework and 32-bit .NET Framework 4.5.2 on Windows and Mono on Linux/macOS, and isn't compatible with current versions of SMAPI. + Legacy154OrEarlier, + + /// The folder contains Stardew Valley from the game's legacy compatibility branch, which backports newer changes to the format. + LegacyCompatibilityBranch, + + /// The folder seems to contain Stardew Valley files, but they failed to load for unknown reasons (e.g. corrupted executable). + InvalidUnknown + } +} diff --git a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs index 7553c07f..37e4f263 100644 --- a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs +++ b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Xml.Linq; using System.Xml.XPath; using StardewModdingAPI.Toolkit.Utilities; +using System.Reflection; #if SMAPI_FOR_WINDOWS using Microsoft.Win32; #endif @@ -54,11 +55,59 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning /// The folder to check. public bool LooksLikeGameFolder(DirectoryInfo dir) { - return - dir.Exists - && dir.EnumerateFiles("Stardew Valley.dll").Any(); + return this.GetGameFolderType(dir) == GameFolderType.Valid; } + /// Detect the validity of a game folder based on file structure heuristics. + /// The folder to check. + public GameFolderType GetGameFolderType(DirectoryInfo dir) + { + // no such folder + if (!dir.Exists) + return GameFolderType.NoGameFound; + + // apparently valid + if (dir.EnumerateFiles("Stardew Valley.dll").Any()) + return GameFolderType.Valid; + + // doesn't contain any version of Stardew Valley + FileInfo executable = new(Path.Combine(dir.FullName, "Stardew Valley.exe")); + if (!executable.Exists) + executable = new(Path.Combine(dir.FullName, "StardewValley.exe")); // pre-1.5.5 Linux/macOS executable + if (!executable.Exists) + return GameFolderType.NoGameFound; + + // get assembly version + Version version; + try + { + version = AssemblyName.GetAssemblyName(executable.FullName).Version; + } + catch + { + // The executable exists but it doesn't seem to be a valid assembly. This would + // happen with Stardew Valley 1.5.5+, but that should have been flagged as a valid + // folder before this point. + return GameFolderType.InvalidUnknown; + } + + // ignore Stardew Valley 1.5.5+ at this point + if (version.Major == 1 && version.Minor == 3 && version.Build == 37) + return GameFolderType.InvalidUnknown; + + // incompatible version + if (version.Major == 1 && version.Minor < 4) + { + // Stardew Valley 1.5.4 and earlier have assembly versions <= 1.3.7853.31734 + if (version.Minor < 3 || version.Build <= 7853) + return GameFolderType.Legacy154OrEarlier; + + // Stardew Valley 1.5.5+ legacy compatibility branch + return GameFolderType.LegacyCompatibilityBranch; + } + + return GameFolderType.InvalidUnknown; + } /********* ** Private methods diff --git a/src/SMAPI.Web/wwwroot/SMAPI.metadata.json b/src/SMAPI.Web/wwwroot/SMAPI.metadata.json index bb166017..eb76b29a 100644 --- a/src/SMAPI.Web/wwwroot/SMAPI.metadata.json +++ b/src/SMAPI.Web/wwwroot/SMAPI.metadata.json @@ -215,6 +215,11 @@ "~0.4.1 | Status": "AssumeBroken", "~0.4.1 | StatusReasonDetails": "causes freeze during game launch" }, + "UI Info Suite": { + "ID": "Cdaragorn.UiInfoSuite", + "~2.0.0 | Status": "AssumeBroken", + "~2.0.0 | StatusReasonDetails": "causes lag, errors, or crashes in-game" + }, "Video Player": { "ID": "aedenthorn.VideoPlayer", "~0.2.5 | Status": "AssumeBroken", diff --git a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json index d39574ef..6b80f260 100644 --- a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json +++ b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json @@ -214,7 +214,7 @@ }, "FromFile": { "title": "Source file", - "description": "The relative file path in your content pack folder to load instead (like 'assets/dinosaur.png'), or multiple comma-delimited values. This can be a .json (data), .png (image), .tbin or .tmx (map), or .xnb file. This field supports tokens and capitalization doesn't matter.", + "description": "The relative file path in your content pack folder to load instead (like 'assets/dinosaur.png'), or multiple comma-delimited values. This can be a .fnt (font), .json (data), .png (image), .tbin or .tmx (map), or .xnb file. This field supports tokens and capitalization doesn't matter.", "type": "string", "allOf": [ { @@ -223,12 +223,12 @@ } }, { - "pattern": "\\.(json|png|tbin|tmx|xnb) *$" + "pattern": "\\.(fnt|json|png|tbin|tmx|xnb) *$" } ], "@errorMessages": { "allOf:indexes: 0": "Invalid value; must not contain directory climbing (like '../').", - "allOf:indexes: 1": "Invalid value; must be a file path ending with .json, .png, .tbin, .tmx, or .xnb." + "allOf:indexes: 1": "Invalid value; must be a file path ending with .fnt, .json, .png, .tbin, .tmx, or .xnb." } }, "FromArea": { diff --git a/src/SMAPI.sln b/src/SMAPI.sln index 92c6cb24..be5326f7 100644 --- a/src/SMAPI.sln +++ b/src/SMAPI.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28729.10 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".root", ".root", "{86C452BE-D2D8-45B4-B63F-E329EB06CEDA}" ProjectSection(SolutionItems) = preProject @@ -28,7 +28,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{09CF91E5 ProjectSection(SolutionItems) = preProject ..\build\common.targets = ..\build\common.targets ..\build\find-game-folder.targets = ..\build\find-game-folder.targets - ..\build\prepare-install-package.targets = ..\build\prepare-install-package.targets EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{EB35A917-67B9-4EFA-8DFC-4FB49B3949BB}" @@ -84,6 +83,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Toolkit.CoreInterface EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SMAPI.Web", "SMAPI.Web\SMAPI.Web.csproj", "{80EFD92F-728F-41E0-8A5B-9F6F49A91899}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "windows", "windows", "{4D661178-38FB-43E4-AA5F-9B0406919344}" + ProjectSection(SolutionItems) = preProject + ..\build\windows\finalize-install-package.sh = ..\build\windows\finalize-install-package.sh + ..\build\windows\prepare-install-package.ps1 = ..\build\windows\prepare-install-package.ps1 + ..\build\windows\set-smapi-version.ps1 = ..\build\windows\set-smapi-version.ps1 + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unix", "unix", "{CAA1488E-842B-433D-994D-1D3D0B5DD125}" + ProjectSection(SolutionItems) = preProject + ..\build\unix\prepare-install-package.sh = ..\build\unix\prepare-install-package.sh + ..\build\unix\set-smapi-version.sh = ..\build\unix\set-smapi-version.sh + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{3B5BF14D-F612-4C83-9EF6-E3EBFCD08766}" + ProjectSection(SolutionItems) = preProject + ..\build\windows\lib\in-place-regex.ps1 = ..\build\windows\lib\in-place-regex.ps1 + EndProjectSection +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution SMAPI.Internal\SMAPI.Internal.projitems*{0634ea4c-3b8f-42db-aea6-ca9e4ef6e92f}*SharedItemsImports = 5 @@ -167,6 +184,9 @@ Global {0634EA4C-3B8F-42DB-AEA6-CA9E4EF6E92F} = {AE9A4D46-E910-4293-8BC4-673F85FFF6A5} {491E775B-EAD0-44D4-B6CA-F1FC3E316D33} = {AE9A4D46-E910-4293-8BC4-673F85FFF6A5} {CD53AD6F-97F4-4872-A212-50C2A0FD3601} = {AE9A4D46-E910-4293-8BC4-673F85FFF6A5} + {4D661178-38FB-43E4-AA5F-9B0406919344} = {09CF91E5-5BAB-4650-A200-E5EA9A633046} + {CAA1488E-842B-433D-994D-1D3D0B5DD125} = {09CF91E5-5BAB-4650-A200-E5EA9A633046} + {3B5BF14D-F612-4C83-9EF6-E3EBFCD08766} = {4D661178-38FB-43E4-AA5F-9B0406919344} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {70143042-A862-47A8-A677-7C819DDC90DC} diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index c5ad215c..5de28f84 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -49,7 +49,7 @@ namespace StardewModdingAPI internal static int? LogScreenId { get; set; } /// SMAPI's current raw semantic version. - internal static string RawApiVersion = "3.13.1"; + internal static string RawApiVersion = "3.13.2"; } /// Contains SMAPI's constants and assumptions. diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs index 8e4320b3..be2a1c58 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs @@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using HarmonyLib; +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member: This is internal code to support rewriters that shouldn't be called directly. + namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { /// Maps Harmony 1.x methods to Harmony 2.x to avoid breaking older mods. diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs index 54b91679..135bd218 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs @@ -5,6 +5,8 @@ using System.Reflection; using System.Reflection.Emit; using HarmonyLib; +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member: This is internal code to support rewriters that shouldn't be called directly. + namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { /// Maps Harmony 1.x HarmonyInstance methods to Harmony 2.x's to avoid breaking older mods. diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs index 44c97401..5162dda4 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs @@ -3,6 +3,8 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using HarmonyLib; +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member: This is internal code to support rewriters that shouldn't be called directly. + namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { /// Maps Harmony 1.x methods to Harmony 2.x to avoid breaking older mods. diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs index a064f503..5f68f8d9 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs @@ -2,6 +2,9 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +#pragma warning disable CS0109 // Member does not hide an inherited member, new keyword is not required: This is deliberate to support legacy XNA Framework platforms. +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member: This is internal code to support rewriters that shouldn't be called directly. + namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { /// Provides method signatures that can be injected into mod code for compatibility with mods written for XNA Framework before Stardew Valley 1.5.5. diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 898ed1f5..104cf330 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -776,10 +776,10 @@ namespace StardewModdingAPI.Framework } if (!Game1.eventUp && Game1.farmEvent == null && Game1.currentBillboard == 0 && Game1.gameMode == 3 && !this.takingMapScreenshot && Game1.isOutdoorMapSmallerThanViewport()) { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, -Math.Min(Game1.viewport.X, GameRunner.MaxTextureSize), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64, 0, Math.Min(GameRunner.MaxTextureSize, Game1.graphics.GraphicsDevice.Viewport.Width - (-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64)), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, Game1.graphics.GraphicsDevice.Viewport.Width, -Math.Min(Game1.viewport.Y, GameRunner.MaxTextureSize)), Color.Black); - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, -Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64, Game1.graphics.GraphicsDevice.Viewport.Width, Math.Min(GameRunner.MaxTextureSize, Game1.graphics.GraphicsDevice.Viewport.Height - (-Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64))), Color.Black); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, -Game1.viewport.X, Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64, 0, Game1.graphics.GraphicsDevice.Viewport.Width - (-Game1.viewport.X + Game1.currentLocation.map.Layers[0].LayerWidth * 64), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, 0, Game1.graphics.GraphicsDevice.Viewport.Width, -Game1.viewport.Y), Color.Black); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Microsoft.Xna.Framework.Rectangle(0, -Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64, Game1.graphics.GraphicsDevice.Viewport.Width, Game1.graphics.GraphicsDevice.Viewport.Height - (-Game1.viewport.Y + Game1.currentLocation.map.Layers[0].LayerHeight * 64)), Color.Black); } Game1.spriteBatch.End(); Game1.PushUIMode(); diff --git a/src/SMAPI/SButton.cs b/src/SMAPI/SButton.cs index cc412946..ae825696 100644 --- a/src/SMAPI/SButton.cs +++ b/src/SMAPI/SButton.cs @@ -6,7 +6,7 @@ using StardewValley; namespace StardewModdingAPI { /// A unified button constant which includes all controller, keyboard, and mouse buttons. - /// Derived from , , and . + /// Derived from , , and System.Windows.Forms.MouseButtons. public enum SButton { /// No valid key. diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj index b99028da..f07ede87 100644 --- a/src/SMAPI/SMAPI.csproj +++ b/src/SMAPI/SMAPI.csproj @@ -13,6 +13,9 @@ true + + + false diff --git a/src/SMAPI/Utilities/Keybind.cs b/src/SMAPI/Utilities/Keybind.cs index dd8d2861..87b867a9 100644 --- a/src/SMAPI/Utilities/Keybind.cs +++ b/src/SMAPI/Utilities/Keybind.cs @@ -105,7 +105,9 @@ namespace StardewModdingAPI.Utilities /// Get the keybind state relative to the previous tick. public SButtonState GetState() { +#pragma warning disable CS0618 // Type or member is obsolete: deliberate call to GetButtonState() for unit tests SButtonState[] states = this.Buttons.Select(this.GetButtonState).Distinct().ToArray(); +#pragma warning restore CS0618 // single state if (states.Length == 1) diff --git a/src/SMAPI/i18n/pl.json b/src/SMAPI/i18n/pl.json index b9c788fc..fa4650a2 100644 --- a/src/SMAPI/i18n/pl.json +++ b/src/SMAPI/i18n/pl.json @@ -1,6 +1,6 @@ -{ - // short date format for SDate - // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) - "generic.date": "{{day}} {{seasonLowercase}}", - "generic.date-with-year": "{{day}} {{seasonLowercase}} w roku {{year}}" -} +{ + // short date format for SDate + // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) + "generic.date": "{{day}} {{seasonLowercase}}", + "generic.date-with-year": "{{day}} {{seasonLowercase}} w roku {{year}}" +}