From d8831d278fcbbb17289dfa364fced6aea2bf19b1 Mon Sep 17 00:00:00 2001 From: Alan Date: Sun, 11 Jan 2026 12:36:45 +0000 Subject: [PATCH 01/26] Add SMAPI workflow. --- .github/workflows/build-smapi.yml | 147 ++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/build-smapi.yml diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml new file mode 100644 index 00000000..b7e5759e --- /dev/null +++ b/.github/workflows/build-smapi.yml @@ -0,0 +1,147 @@ +name: Build SMAPI + +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" # On any d.d.d tag push. + branches: + - develop + +jobs: + build_and_release: + runs-on: ubuntu-latest + permissions: + contents: write # For creating releases. + id-token: write # For creating attestations. + attestations: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get trigger type. + id: trigger-type # Possible values: tag, dev-branch. + run: | + # If we ever decide to add manual workflow runs, this will need to be reworked. + + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "Building regular release." + echo "trigger_type=tag" >> $GITHUB_OUTPUT + elif [[ "${{ github.ref_name }}" == "develop" ]]; then + echo "Building in-development release." + echo "trigger_type=dev-branch" >> $GITHUB_OUTPUT + else + echo "Ran into an unexpected trigger type. Aborting." + echo "Debug info:" + echo "ref_type: ${{ github.ref_type }}" + echo "ref_name: ${{ github.ref_name }}" + + exit 1 + fi + + - name: Extract current SMAPI version. + id: smapi-version + run: | + version=$(pwsh -Command "([xml](Get-Content build/common.targets)).Project.PropertyGroup.Version") + pattern="^([0-9]+)\.([0-9]+)\.([0-9]+)$" + + if [[ "$version" =~ $pattern ]]; then + MAJOR="${BASH_REMATCH[1]}" + MINOR="${BASH_REMATCH[2]}" + PATCH="${BASH_REMATCH[3]}" + else + echo "Input semver did not match the strict x.y.z format. Aborting." + echo "Debug info:" + echo "Extracted version: $version" + + exit 1 + fi + + echo "smapi_version=$version" >> $GITHUB_OUTPUT + echo "smapi_major=$MAJOR" >> $GITHUB_OUTPUT + echo "smapi_minor=$MINOR" >> $GITHUB_OUTPUT + echo "smapi_patch=$PATCH" >> $GITHUB_OUTPUT + echo "VERSION=$MAJOR.$MINOR.$PATCH" >> $GITHUB_ENV + echo "Version: $MAJOR.$MINOR.$PATCH" + + - name: Verify SMAPI version matches tag. + if: steps.trigger-type.outputs.trigger_type == 'tag' + run: | + if [[ "$VERSION" != "${{ github.ref_name }}" ]]; then + echo "The pushed tag doesn't match the version we have in build/common.targets. Aborting." + echo "Debug info: " + echo "Tag: ${{ github.ref_name }}" + echo "In common.targets: $VERSION" + + exit 1 + fi + + - name: Update SMAPI's current version for dev builds. + id: version-parsing + if: steps.trigger-type.outputs.trigger_type == 'dev-branch' + run: | + updated_patch=$((${{steps.smapi-version.outputs.smapi_patch}} + 1)) + date=$(date +"%Y%m%d") + updated_version=${{steps.smapi-version.outputs.smapi_major}}.${{steps.smapi-version.outputs.smapi_minor}}.$updated_patch-alpha.$date + echo "VERSION=$updated_version" >> $GITHUB_ENV + + echo "This is a dev version. Building as version: $updated_version" + + build/unix/set-smapi-version.sh "$updated_version" + echo "Version updated using build/unix/set-smapi-version.sh." + + - name: Checkout reference assemblies + uses: actions/checkout@v4 + with: + repository: "StardewModders/mod-reference-assemblies" + path: "stardew-reference-assemblies" + fetch-depth: 1 + + - name: Symlink reference assemblies to a valid location. # Simply cloning the repo to this location doesn't seem to work. + run: | + ln -s "$(pwd)/stardew-reference-assemblies" "$HOME/StardewValley" + + - name: Build SMAPI. + run: | + echo "Building SMAPI $VERSION." + build/unix/prepare-install-package.sh "$VERSION" + + - name: Rename build zips. + run: | + mv "bin/SMAPI ${{ env.VERSION }} installer.zip" "bin/SMAPI-${{ env.VERSION }}-installer.zip" + mv "bin/SMAPI ${{ env.VERSION }} installer for developers.zip" "bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip" + + - name: Upload regular installer. + uses: actions/upload-artifact@v4 + with: + name: 'SMAPI ${{ env.VERSION }} installer' + path: 'bin/SMAPI-${{ env.VERSION }}-installer.zip' + if-no-files-found: 'error' + + - name: Upload installer for developers. + uses: actions/upload-artifact@v4 + with: + name: "SMAPI ${{ env.VERSION }} installer for developers" + path: "bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip" + if-no-files-found: 'error' + + - name: Create release and upload artifact + uses: ncipollo/release-action@v1 + if: steps.trigger-type.outputs.trigger_type == 'tag' + with: + artifacts: "bin/SMAPI-${{ env.VERSION }}*.zip" + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ github.ref_name }} + name: "${{ env.VERSION }}" + body: | + Draft. + draft: true + + - name: Generate artifact attestations + uses: actions/attest-build-provenance@v2 + id: attestation-step + with: + subject-path: | + bin/SMAPI-${{ env.VERSION }}-installer.zip + bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip \ No newline at end of file From a92937d5439e37bbef37ca3e05f2813331479193 Mon Sep 17 00:00:00 2001 From: Alan Date: Sun, 11 Jan 2026 12:54:08 +0000 Subject: [PATCH 02/26] Missed a newline at the end of the file. --- .github/workflows/build-smapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index b7e5759e..916c8edf 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -144,4 +144,4 @@ jobs: with: subject-path: | bin/SMAPI-${{ env.VERSION }}-installer.zip - bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip \ No newline at end of file + bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip From 124c8ccd8afbeb2d4d4e5a880c936a0f2efd5bc6 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 11 Jan 2026 17:36:34 -0500 Subject: [PATCH 03/26] update JSON schemas for Content Patcher 2.9.0 --- docs/release-notes.md | 6 +++++- src/SMAPI.Web/wwwroot/schemas/content-patcher.json | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 563d5f85..10021f3a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,10 @@ ← [README](README.md) # Release notes +## Upcoming release +* For the web UI: + * Updated [JSON schema](technical/web.md#using-a-schema-file-directly) for Content Patcher 2.9.0. + ## 4.4.0 Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705). @@ -27,7 +31,7 @@ Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highli * For the web UI: * Improved mod compatibility list: * Added support for mod links in warnings. - * Improved Content Patcher [JSON schemas](technical/web.md#using-a-schema-file-directly): + * Improved Content Patcher [JSON schema](technical/web.md#using-a-schema-file-directly): * Updated for Content Patcher 2.8.0. * Fixed schema requiring `AddNPCWarps` instead of `AddNpcWarps`. * Fixed validation error if a warp field contains tokens or consecutive spaces (thanks to irocendar!). diff --git a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json index bafcb5bd..3b215a10 100644 --- a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json +++ b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json @@ -14,9 +14,9 @@ "title": "Format version", "description": "The format version. You should always use the latest version to enable the latest features, avoid obsolete behavior, and reduce load times.", "type": "string", - "pattern": "^2\\.8\\.[0-9]+$", + "pattern": "^2\\.9\\.[0-9]+$", "@errorMessages": { - "pattern": "Incorrect value '@value'. You should always use the latest format version (currently 2.8.0) to enable the latest features, avoid obsolete behavior, and reduce load times." + "pattern": "Incorrect value '@value'. You should always use the latest format version (currently 2.9.0) to enable the latest features, avoid obsolete behavior, and reduce load times." } }, "ConfigSchema": { @@ -473,7 +473,7 @@ "title": "Patch mode", "description": "How to apply FromArea to ToArea. Defaults to Replace.", "type": "string", - "enum": [ "Replace", "Overlay" ], + "enum": [ "Replace", "Overlay", "Mask" ], "default": "Replace" }, From b7ddb40369a9cd3b8dd40d464a1f71f3120e41d1 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 13 Jan 2026 19:52:41 -0500 Subject: [PATCH 04/26] update release notes --- docs/release-notes.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 10021f3a..5ffe1fbf 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,8 +2,18 @@ # Release notes ## Upcoming release -* For the web UI: - * Updated [JSON schema](technical/web.md#using-a-schema-file-directly) for Content Patcher 2.9.0. +**SMAPI release builds are automated on GitHub** (thanks to DecidedlyHuman!). + +The main benefits include... +- Significantly improved security: + - This eliminates the risk of hidden malware infecting SMAPI releases. + - When downloading a release, you can compare the download's file hash against the GitHub build to + guarantee that (a) it was compiled from the code on GitHub and (b) it has not been tampered with. +- You can now download a preview of the next SMAPI release at any time, since every commit produces an alpha build. + +This is also a big step towards future improvements, like... +- signing builds to reduce antivirus alerts on Windows and macOS; +- and automating mod builds too. ## 4.4.0 Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705). @@ -32,7 +42,7 @@ Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highli * Improved mod compatibility list: * Added support for mod links in warnings. * Improved Content Patcher [JSON schema](technical/web.md#using-a-schema-file-directly): - * Updated for Content Patcher 2.8.0. + * Updated for Content Patcher 2.8.0 and 2.9.0. * Fixed schema requiring `AddNPCWarps` instead of `AddNpcWarps`. * Fixed validation error if a warp field contains tokens or consecutive spaces (thanks to irocendar!). * Fixed validation error if a `Target` contains multiple targets (thanks to irocendar!). From 63ddc30e2fc44e920e944d397259ce7a5202062a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 00:10:36 -0500 Subject: [PATCH 05/26] migrate GitHub build step scripts to PowerShell --- .github/workflows/build-smapi.yml | 133 ++++++++++++++++-------------- src/SMAPI.slnx | 1 + 2 files changed, 72 insertions(+), 62 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 916c8edf..15b90090 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -13,83 +13,91 @@ jobs: permissions: contents: write # For creating releases. id-token: write # For creating attestations. - attestations: write + attestations: write steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Get trigger type. + - name: Get trigger type. id: trigger-type # Possible values: tag, dev-branch. + shell: pwsh run: | # If we ever decide to add manual workflow runs, this will need to be reworked. - if [[ "${{ github.ref_type }}" == "tag" ]]; then - echo "Building regular release." - echo "trigger_type=tag" >> $GITHUB_OUTPUT - elif [[ "${{ github.ref_name }}" == "develop" ]]; then - echo "Building in-development release." - echo "trigger_type=dev-branch" >> $GITHUB_OUTPUT - else - echo "Ran into an unexpected trigger type. Aborting." - echo "Debug info:" - echo "ref_type: ${{ github.ref_type }}" - echo "ref_name: ${{ github.ref_name }}" - + if ('${{github.ref_type}}' -eq 'tag') { + Write-Host 'Building regular release.' + 'trigger_type=tag' | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } + elseif ('${{github.ref_name}}' -eq 'develop') { + Write-Host 'Building in-development release.' + 'trigger_type=dev-branch' | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } + else { + Write-Error 'Ran into an unexpected trigger type. Aborting.' + Write-Host 'Debug info:' + Write-Host 'ref_type: ${{github.ref_type}}' + Write-Host 'ref_name: ${{github.ref_name}}' exit 1 - fi + } - name: Extract current SMAPI version. id: smapi-version + shell: pwsh run: | - version=$(pwsh -Command "([xml](Get-Content build/common.targets)).Project.PropertyGroup.Version") - pattern="^([0-9]+)\.([0-9]+)\.([0-9]+)$" - - if [[ "$version" =~ $pattern ]]; then - MAJOR="${BASH_REMATCH[1]}" - MINOR="${BASH_REMATCH[2]}" - PATCH="${BASH_REMATCH[3]}" - else - echo "Input semver did not match the strict x.y.z format. Aborting." - echo "Debug info:" - echo "Extracted version: $version" + [xml]$xml = Get-Content build/common.targets + [string]$version = $xml.Project.PropertyGroup.Version + $version = $version.Trim() + $pattern = '^(\d+)\.(\d+)\.(\d+)$' + if ($version -match $pattern) { + $major = $Matches[1] + $minor = $Matches[2] + $patch = $Matches[3] + } + else { + Write-Error 'Input semver did not match the strict x.y.z format. Aborting.' + Write-Host 'Debug info:' + Write-Host "Extracted version: $version" exit 1 - fi + } - echo "smapi_version=$version" >> $GITHUB_OUTPUT - echo "smapi_major=$MAJOR" >> $GITHUB_OUTPUT - echo "smapi_minor=$MINOR" >> $GITHUB_OUTPUT - echo "smapi_patch=$PATCH" >> $GITHUB_OUTPUT - echo "VERSION=$MAJOR.$MINOR.$PATCH" >> $GITHUB_ENV - echo "Version: $MAJOR.$MINOR.$PATCH" + "smapi_version=$version" | Out-File $env:GITHUB_OUTPUT -Append + "smapi_major=$major" | Out-File $env:GITHUB_OUTPUT -Append + "smapi_minor=$minor" | Out-File $env:GITHUB_OUTPUT -Append + "smapi_patch=$patch" | Out-File $env:GITHUB_OUTPUT -Append + "VERSION=$major.$minor.$patch" | Out-File $env:GITHUB_ENV -Append + + Write-Host "Version: $major.$minor.$patch" - name: Verify SMAPI version matches tag. if: steps.trigger-type.outputs.trigger_type == 'tag' + shell: pwsh run: | - if [[ "$VERSION" != "${{ github.ref_name }}" ]]; then - echo "The pushed tag doesn't match the version we have in build/common.targets. Aborting." - echo "Debug info: " - echo "Tag: ${{ github.ref_name }}" - echo "In common.targets: $VERSION" - + if ($env:VERSION -ne '${{github.ref_name}}') { + Write-Error 'The pushed tag doesn't match the version we have in build/common.targets. Aborting.' + Write-Host 'Debug info:' + Write-Host 'Tag: ${{github.ref_name}}' + Write-Host "In common.targets: $env:VERSION" exit 1 - fi + } - name: Update SMAPI's current version for dev builds. - id: version-parsing + id: version-parsing if: steps.trigger-type.outputs.trigger_type == 'dev-branch' + shell: pwsh run: | - updated_patch=$((${{steps.smapi-version.outputs.smapi_patch}} + 1)) - date=$(date +"%Y%m%d") - updated_version=${{steps.smapi-version.outputs.smapi_major}}.${{steps.smapi-version.outputs.smapi_minor}}.$updated_patch-alpha.$date - echo "VERSION=$updated_version" >> $GITHUB_ENV + $updatedPatch = [int]${{steps.smapi-version.outputs.smapi_patch}} + 1 + $date = Get-Date -Format 'yyyyMMdd' + $updatedVersion = "${{steps.smapi-version.outputs.smapi_major}}.${{steps.smapi-version.outputs.smapi_minor}}.$updatedPatch-alpha.$date" - echo "This is a dev version. Building as version: $updated_version" + "VERSION=$updatedVersion" | Out-File $env:GITHUB_ENV -Append - build/unix/set-smapi-version.sh "$updated_version" - echo "Version updated using build/unix/set-smapi-version.sh." + Write-Host "This is a dev version. Building as version: $updatedVersion" + + bash build/unix/set-smapi-version.sh "$updatedVersion" + Write-Host 'Version updated using build/unix/set-smapi-version.sh.' - name: Checkout reference assemblies uses: actions/checkout@v4 @@ -103,37 +111,38 @@ jobs: ln -s "$(pwd)/stardew-reference-assemblies" "$HOME/StardewValley" - name: Build SMAPI. + shell: pwsh run: | - echo "Building SMAPI $VERSION." - build/unix/prepare-install-package.sh "$VERSION" + Write-Host "Building SMAPI $env:VERSION." + bash build/unix/prepare-install-package.sh "$env:VERSION" - name: Rename build zips. run: | - mv "bin/SMAPI ${{ env.VERSION }} installer.zip" "bin/SMAPI-${{ env.VERSION }}-installer.zip" - mv "bin/SMAPI ${{ env.VERSION }} installer for developers.zip" "bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip" + mv "bin/SMAPI ${{env.VERSION}} installer.zip" "bin/SMAPI-${{env.VERSION}}-installer.zip" + mv "bin/SMAPI ${{env.VERSION}} installer for developers.zip" "bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip" - name: Upload regular installer. uses: actions/upload-artifact@v4 with: - name: 'SMAPI ${{ env.VERSION }} installer' - path: 'bin/SMAPI-${{ env.VERSION }}-installer.zip' + name: 'SMAPI ${{env.VERSION}} installer' + path: 'bin/SMAPI-${{env.VERSION}}-installer.zip' if-no-files-found: 'error' - name: Upload installer for developers. uses: actions/upload-artifact@v4 with: - name: "SMAPI ${{ env.VERSION }} installer for developers" - path: "bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip" + name: 'SMAPI ${{env.VERSION}} installer for developers' + path: 'bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip' if-no-files-found: 'error' - name: Create release and upload artifact uses: ncipollo/release-action@v1 if: steps.trigger-type.outputs.trigger_type == 'tag' with: - artifacts: "bin/SMAPI-${{ env.VERSION }}*.zip" - token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ github.ref_name }} - name: "${{ env.VERSION }}" + artifacts: 'bin/SMAPI-${{env.VERSION}}*.zip' + token: '${{secrets.GITHUB_TOKEN}}' + tag: '${{github.ref_name}}' + name: "${{env.VERSION}}" body: | Draft. draft: true @@ -143,5 +152,5 @@ jobs: id: attestation-step with: subject-path: | - bin/SMAPI-${{ env.VERSION }}-installer.zip - bin/SMAPI-${{ env.VERSION }}-installer-for-developers.zip + bin/SMAPI-${{env.VERSION}}-installer.zip + bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip diff --git a/src/SMAPI.slnx b/src/SMAPI.slnx index 2e93538e..10fe0b9d 100644 --- a/src/SMAPI.slnx +++ b/src/SMAPI.slnx @@ -9,6 +9,7 @@ + From 199fb9e3899bedc5390ec328b19c27a41e54c993 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 00:10:36 -0500 Subject: [PATCH 06/26] add more info to build error logs --- .github/workflows/build-smapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 15b90090..2067e620 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -57,7 +57,7 @@ jobs: $patch = $Matches[3] } else { - Write-Error 'Input semver did not match the strict x.y.z format. Aborting.' + Write-Error "The version '$version' found in build/common.targets did not match the strict x.y.z format. Aborting." Write-Host 'Debug info:' Write-Host "Extracted version: $version" exit 1 @@ -76,7 +76,7 @@ jobs: shell: pwsh run: | if ($env:VERSION -ne '${{github.ref_name}}') { - Write-Error 'The pushed tag doesn't match the version we have in build/common.targets. Aborting.' + Write-Error "The pushed tag '${{github.ref_name}}' doesn't match the version '$env:VERSION' found in build/common.targets. Aborting." Write-Host 'Debug info:' Write-Host 'Tag: ${{github.ref_name}}' Write-Host "In common.targets: $env:VERSION" From 851b0c712c4bab1d2c2f690cdc2d94b1618fcb87 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 00:10:36 -0500 Subject: [PATCH 07/26] tweak build step names --- .github/workflows/build-smapi.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 2067e620..10f551ff 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -3,7 +3,7 @@ name: Build SMAPI on: push: tags: - - "[0-9]+.[0-9]+.[0-9]+" # On any d.d.d tag push. + - "[0-9]+.[0-9]+.[0-9]+" # stable version tag branches: - develop @@ -11,8 +11,8 @@ jobs: build_and_release: runs-on: ubuntu-latest permissions: - contents: write # For creating releases. - id-token: write # For creating attestations. + contents: write # for creating releases + id-token: write # for creating attestations attestations: write steps: - name: Checkout code @@ -20,8 +20,8 @@ jobs: with: fetch-depth: 0 - - name: Get trigger type. - id: trigger-type # Possible values: tag, dev-branch. + - name: Get trigger type # Possible values: tag, dev-branch + id: trigger-type shell: pwsh run: | # If we ever decide to add manual workflow runs, this will need to be reworked. @@ -42,7 +42,7 @@ jobs: exit 1 } - - name: Extract current SMAPI version. + - name: Get SMAPI assembly version id: smapi-version shell: pwsh run: | @@ -71,7 +71,7 @@ jobs: Write-Host "Version: $major.$minor.$patch" - - name: Verify SMAPI version matches tag. + - name: Verify SMAPI assembly version matches tag if: steps.trigger-type.outputs.trigger_type == 'tag' shell: pwsh run: | @@ -83,7 +83,7 @@ jobs: exit 1 } - - name: Update SMAPI's current version for dev builds. + - name: Update SMAPI assembly version id: version-parsing if: steps.trigger-type.outputs.trigger_type == 'dev-branch' shell: pwsh @@ -99,43 +99,43 @@ jobs: bash build/unix/set-smapi-version.sh "$updatedVersion" Write-Host 'Version updated using build/unix/set-smapi-version.sh.' - - name: Checkout reference assemblies + - name: Checkout game reference assemblies uses: actions/checkout@v4 with: repository: "StardewModders/mod-reference-assemblies" path: "stardew-reference-assemblies" fetch-depth: 1 - - name: Symlink reference assemblies to a valid location. # Simply cloning the repo to this location doesn't seem to work. + - name: Symlink game reference assemblies # simply cloning the repo to this location doesn't seem to work run: | ln -s "$(pwd)/stardew-reference-assemblies" "$HOME/StardewValley" - - name: Build SMAPI. + - name: Build SMAPI shell: pwsh run: | Write-Host "Building SMAPI $env:VERSION." bash build/unix/prepare-install-package.sh "$env:VERSION" - - name: Rename build zips. + - name: Rename build zips run: | mv "bin/SMAPI ${{env.VERSION}} installer.zip" "bin/SMAPI-${{env.VERSION}}-installer.zip" mv "bin/SMAPI ${{env.VERSION}} installer for developers.zip" "bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip" - - name: Upload regular installer. + - name: Upload installer uses: actions/upload-artifact@v4 with: name: 'SMAPI ${{env.VERSION}} installer' path: 'bin/SMAPI-${{env.VERSION}}-installer.zip' if-no-files-found: 'error' - - name: Upload installer for developers. + - name: Upload installer for developers uses: actions/upload-artifact@v4 with: name: 'SMAPI ${{env.VERSION}} installer for developers' path: 'bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip' if-no-files-found: 'error' - - name: Create release and upload artifact + - name: Create GitHub release uses: ncipollo/release-action@v1 if: steps.trigger-type.outputs.trigger_type == 'tag' with: From 6d05ca4353317a6f102aca4aef3d36bf8872eaaa Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 00:10:36 -0500 Subject: [PATCH 08/26] merge build steps which checkout game reference assemblies --- .github/workflows/build-smapi.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 10f551ff..7a97f1dd 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -100,15 +100,8 @@ jobs: Write-Host 'Version updated using build/unix/set-smapi-version.sh.' - name: Checkout game reference assemblies - uses: actions/checkout@v4 - with: - repository: "StardewModders/mod-reference-assemblies" - path: "stardew-reference-assemblies" - fetch-depth: 1 - - - name: Symlink game reference assemblies # simply cloning the repo to this location doesn't seem to work run: | - ln -s "$(pwd)/stardew-reference-assemblies" "$HOME/StardewValley" + git clone --depth 1 https://github.com/StardewModders/mod-reference-assemblies.git "$HOME/StardewValley" - name: Build SMAPI shell: pwsh From c5ee55d0bfc52c9c2a76c48b781482e338bb9459 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 00:10:36 -0500 Subject: [PATCH 09/26] remove unneeded build step IDs --- .github/workflows/build-smapi.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 7a97f1dd..7de33b0a 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -20,7 +20,7 @@ jobs: with: fetch-depth: 0 - - name: Get trigger type # Possible values: tag, dev-branch + - name: Get trigger type # possible values: tag, dev-branch id: trigger-type shell: pwsh run: | @@ -84,7 +84,6 @@ jobs: } - name: Update SMAPI assembly version - id: version-parsing if: steps.trigger-type.outputs.trigger_type == 'dev-branch' shell: pwsh run: | @@ -142,7 +141,6 @@ jobs: - name: Generate artifact attestations uses: actions/attest-build-provenance@v2 - id: attestation-step with: subject-path: | bin/SMAPI-${{env.VERSION}}-installer.zip From e76e99532056d24fad32c00741a1fd5b88a3178a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 00:10:36 -0500 Subject: [PATCH 10/26] add hour + minute to dev alpha versions --- .github/workflows/build-smapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 7de33b0a..589b1110 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -88,8 +88,8 @@ jobs: shell: pwsh run: | $updatedPatch = [int]${{steps.smapi-version.outputs.smapi_patch}} + 1 - $date = Get-Date -Format 'yyyyMMdd' - $updatedVersion = "${{steps.smapi-version.outputs.smapi_major}}.${{steps.smapi-version.outputs.smapi_minor}}.$updatedPatch-alpha.$date" + $timestamp = Get-Date -Format 'yyyyMMdd-HHmm' + $updatedVersion = "${{steps.smapi-version.outputs.smapi_major}}.${{steps.smapi-version.outputs.smapi_minor}}.$updatedPatch-alpha.$timestamp" "VERSION=$updatedVersion" | Out-File $env:GITHUB_ENV -Append From 8cdb0eae0fb95574737c372ccfdb8b7a8e5ba385 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 01:10:36 -0500 Subject: [PATCH 11/26] add CI build test branch to workflow This just avoids needing to run build tests on the main `develop` branch. --- .github/workflows/build-smapi.yml | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 589b1110..314799ed 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -6,6 +6,7 @@ on: - "[0-9]+.[0-9]+.[0-9]+" # stable version tag branches: - develop + - build-test jobs: build_and_release: @@ -20,28 +21,6 @@ jobs: with: fetch-depth: 0 - - name: Get trigger type # possible values: tag, dev-branch - id: trigger-type - shell: pwsh - run: | - # If we ever decide to add manual workflow runs, this will need to be reworked. - - if ('${{github.ref_type}}' -eq 'tag') { - Write-Host 'Building regular release.' - 'trigger_type=tag' | Out-File -FilePath $env:GITHUB_OUTPUT -Append - } - elseif ('${{github.ref_name}}' -eq 'develop') { - Write-Host 'Building in-development release.' - 'trigger_type=dev-branch' | Out-File -FilePath $env:GITHUB_OUTPUT -Append - } - else { - Write-Error 'Ran into an unexpected trigger type. Aborting.' - Write-Host 'Debug info:' - Write-Host 'ref_type: ${{github.ref_type}}' - Write-Host 'ref_name: ${{github.ref_name}}' - exit 1 - } - - name: Get SMAPI assembly version id: smapi-version shell: pwsh @@ -72,7 +51,7 @@ jobs: Write-Host "Version: $major.$minor.$patch" - name: Verify SMAPI assembly version matches tag - if: steps.trigger-type.outputs.trigger_type == 'tag' + if: github.ref_type == 'tag' shell: pwsh run: | if ($env:VERSION -ne '${{github.ref_name}}') { @@ -84,7 +63,7 @@ jobs: } - name: Update SMAPI assembly version - if: steps.trigger-type.outputs.trigger_type == 'dev-branch' + if: github.ref_type != 'tag' shell: pwsh run: | $updatedPatch = [int]${{steps.smapi-version.outputs.smapi_patch}} + 1 @@ -129,7 +108,7 @@ jobs: - name: Create GitHub release uses: ncipollo/release-action@v1 - if: steps.trigger-type.outputs.trigger_type == 'tag' + if: github.ref_type == 'tag' with: artifacts: 'bin/SMAPI-${{env.VERSION}}*.zip' token: '${{secrets.GITHUB_TOKEN}}' From b0f10770c8c6a4a04d6652b6e1c707074f7c106f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 23:20:25 -0500 Subject: [PATCH 12/26] move Windows build scripts for reuse --- .../finalize-install-package.sh | 0 .../{windows => scripts}/lib/in-place-regex.ps1 | 0 .../prepare-install-package.ps1 | 0 build/{windows => scripts}/set-smapi-version.ps1 | 0 docs/technical/smapi.md | 4 ++-- src/SMAPI.slnx | 16 ++++++++-------- 6 files changed, 10 insertions(+), 10 deletions(-) rename build/{windows => scripts}/finalize-install-package.sh (100%) mode change 100755 => 100644 rename build/{windows => scripts}/lib/in-place-regex.ps1 (100%) rename build/{windows => scripts}/prepare-install-package.ps1 (100%) rename build/{windows => scripts}/set-smapi-version.ps1 (100%) diff --git a/build/windows/finalize-install-package.sh b/build/scripts/finalize-install-package.sh old mode 100755 new mode 100644 similarity index 100% rename from build/windows/finalize-install-package.sh rename to build/scripts/finalize-install-package.sh diff --git a/build/windows/lib/in-place-regex.ps1 b/build/scripts/lib/in-place-regex.ps1 similarity index 100% rename from build/windows/lib/in-place-regex.ps1 rename to build/scripts/lib/in-place-regex.ps1 diff --git a/build/windows/prepare-install-package.ps1 b/build/scripts/prepare-install-package.ps1 similarity index 100% rename from build/windows/prepare-install-package.ps1 rename to build/scripts/prepare-install-package.ps1 diff --git a/build/windows/set-smapi-version.ps1 b/build/scripts/set-smapi-version.ps1 similarity index 100% rename from build/windows/set-smapi-version.ps1 rename to build/scripts/set-smapi-version.ps1 diff --git a/docs/technical/smapi.md b/docs/technical/smapi.md index 82896157..801a2679 100644 --- a/docs/technical/smapi.md +++ b/docs/technical/smapi.md @@ -152,7 +152,7 @@ above for the unified process._ ``` ### Prepare the release -1. Run `build/windows/prepare-install-package.ps1 VERSION_HERE` in PowerShell to create the release +1. Run `build/scripts/prepare-install-package.ps1 VERSION_HERE` in PowerShell to create the release package folders in the root `bin` folder. Make sure you use a [semantic version](https://semver.org). Recommended format: @@ -169,7 +169,7 @@ above for the unified process._ # 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" + build/scripts/finalize-install-package.sh "$version" "$binFolder" ``` Note: to prepare a test Windows-only build, you can pass `--windows-only` in the first step and diff --git a/src/SMAPI.slnx b/src/SMAPI.slnx index 10fe0b9d..256010c3 100644 --- a/src/SMAPI.slnx +++ b/src/SMAPI.slnx @@ -20,18 +20,18 @@ + + + + + + + + - - - - - - - - From a12950ba7a37e7f62a39d0ea2a61497b66385dd0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 23:20:25 -0500 Subject: [PATCH 13/26] unify Unix/Windows build scripts with crossplatform PowerShell --- .github/workflows/build-smapi.yml | 6 +- build/scripts/lib/in-place-regex.ps1 | 2 +- build/scripts/prepare-install-package.ps1 | 231 +++++++++++++--------- build/scripts/set-smapi-version.ps1 | 10 +- build/unix/prepare-install-package.sh | 228 --------------------- build/unix/set-smapi-version.sh | 26 --- docs/technical/smapi.md | 19 +- src/SMAPI.slnx | 4 - 8 files changed, 151 insertions(+), 375 deletions(-) delete mode 100755 build/unix/prepare-install-package.sh delete mode 100755 build/unix/set-smapi-version.sh diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 314799ed..828e4fef 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -74,8 +74,8 @@ jobs: Write-Host "This is a dev version. Building as version: $updatedVersion" - bash build/unix/set-smapi-version.sh "$updatedVersion" - Write-Host 'Version updated using build/unix/set-smapi-version.sh.' + ./build/scripts/set-smapi-version.ps1 "$updatedVersion" + Write-Host 'Version updated.' - name: Checkout game reference assemblies run: | @@ -85,7 +85,7 @@ jobs: shell: pwsh run: | Write-Host "Building SMAPI $env:VERSION." - bash build/unix/prepare-install-package.sh "$env:VERSION" + ./build/scripts/prepare-install-package.ps1 "$env:VERSION" - name: Rename build zips run: | diff --git a/build/scripts/lib/in-place-regex.ps1 b/build/scripts/lib/in-place-regex.ps1 index 7b309342..ce4f2c3e 100644 --- a/build/scripts/lib/in-place-regex.ps1 +++ b/build/scripts/lib/in-place-regex.ps1 @@ -1,4 +1,4 @@ -function In-Place-Regex { +function In-Place-Regex { param ( [Parameter(Mandatory)][string]$Path, [Parameter(Mandatory)][string]$Search, diff --git a/build/scripts/prepare-install-package.ps1 b/build/scripts/prepare-install-package.ps1 index a5a6b043..2218cfe9 100644 --- a/build/scripts/prepare-install-package.ps1 +++ b/build/scripts/prepare-install-package.ps1 @@ -1,33 +1,63 @@ # # -# 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 (unless you specify -# --windows-only). 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. +# Note: On Windows, this script *does not* set Linux permissions. The final changes are handled by the +# finalize-install-package.sh file in WSL. # # . "$PSScriptRoot/lib/in-place-regex.ps1" +########## +## Read arguments +########## +$windowsOnly = $false # Windows-only build +$skipBundleDeletion = $false # skip bundle deletion (only applies when using WSL to finalize the build on Windows) + foreach ($arg in $args) { + if ($arg -eq "--windows-only" -and $IsWindows) { + $windowsOnly = $true + } + elseif ($arg -eq "--skip-bundle-deletion") { + $skipBundleDeletion = $true + } +} + + ########## ## Find the game folder ########## -$possibleGamePaths=( - # GOG - "C:\Program Files\GalaxyClient\Games\Stardew Valley", - "C:\Program Files\GOG Galaxy\Games\Stardew Valley", - "C:\Program Files\GOG Games\Stardew Valley", - "C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley", - "C:\Program Files (x86)\GOG Galaxy\Games\Stardew Valley", - "C:\Program Files (x86)\GOG Games\Stardew Valley", +if ($IsWindows) { + $possibleGamePaths=( + # GOG + "C:\Program Files\GalaxyClient\Games\Stardew Valley", + "C:\Program Files\GOG Galaxy\Games\Stardew Valley", + "C:\Program Files\GOG Games\Stardew Valley", + "C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley", + "C:\Program Files (x86)\GOG Galaxy\Games\Stardew Valley", + "C:\Program Files (x86)\GOG Games\Stardew Valley", + + # Steam + "C:\Program Files\Steam\steamapps\common\Stardew Valley", + "C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley" + ) +} +else { + $possibleGamePaths=( + # override + "$HOME/StardewValley", + + # Linux + "$HOME/GOG Games/Stardew Valley/game", + "$HOME/.steam/steam/steamapps/common/Stardew Valley", + "$HOME/.local/share/Steam/steamapps/common/Stardew Valley", + "$HOME/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/Stardew Valley", + + # macOS + "/Applications/Stardew Valley.app/Contents/MacOS", + "$HOME/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS" + ) +} - # Steam - "C:\Program Files\Steam\steamapps\common\Stardew Valley", - "C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley" -) $gamePath = "" foreach ($possibleGamePath in $possibleGamePaths) { if (Test-Path $possibleGamePath -PathType Container) { @@ -46,9 +76,16 @@ $bundleModNames = "ConsoleCommands", "SaveBackup" # build configuration $buildConfig = "Release" $framework = "net6.0" -$folders = "linux", "macOS", "windows" -$runtimes = @{ linux = "linux-x64"; macOS = "osx-x64"; windows = "win-x64" } -$msBuildPlatformNames = @{ linux = "Unix"; macOS = "OSX"; windows = "Windows_NT" } +if ($windowsOnly) { + $folders = "windows" + $runtimes = @{ windows = "win-x64" } + $msBuildPlatformNames = @{ windows = "Windows_NT" } +} +else { + $folders = "linux", "macOS", "windows" + $runtimes = @{ linux = "linux-x64"; macOS = "osx-x64"; windows = "win-x64" } + $msBuildPlatformNames = @{ linux = "Unix"; macOS = "OSX"; windows = "Windows_NT" } +} # version number $version = $args[0] @@ -56,35 +93,24 @@ if (!$version) { $version = Read-Host "SMAPI release version (like '4.0.0')" } -# Windows-only build -$windowsOnly = $false -foreach ($arg in $args) { - if ($arg -eq "--windows-only") { - $windowsOnly = $true - $folders = "windows" - $runtimes = @{ windows = "win-x64" } - $msBuildPlatformNames = @{ windows = "Windows_NT" } - } -} - ########## ## Move to SMAPI root ########## -cd "$PSScriptRoot/../.." +Set-Location "$PSScriptRoot/../.." ########## ## Clear old build files ########## -echo "Clearing old builds..." -echo "-------------------------------------------------" +Write-Output "Clearing old builds..." +Write-Output "-------------------------------------------------" -foreach ($path in (dir -Recurse -Include ('bin', 'obj'))) { - echo "$path" - rm -Recurse -Force "$path" +foreach ($path in (Get-ChildItem -Recurse -Include ('bin', 'obj'))) { + Write-Output "$path" + Remove-Item -Recurse -Force "$path" } -echo "" +Write-Output "" ########## @@ -95,24 +121,24 @@ foreach ($folder in $folders) { $runtime = $runtimes[$folder] $msbuildPlatformName = $msBuildPlatformNames[$folder] - echo "Compiling SMAPI for $folder..." - echo "-------------------------------------------------" + Write-Output "Compiling SMAPI for $folder..." + Write-Output "-------------------------------------------------" dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" --framework "$framework" -p:OS="$msbuildPlatformName" -p:TargetFrameworks="$framework" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true - echo "" - echo "" + Write-Output "" + Write-Output "" - echo "Compiling installer for $folder..." - echo "-------------------------------------------------" + Write-Output "Compiling installer for $folder..." + Write-Output "-------------------------------------------------" dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" --framework "$framework" -p:OS="$msbuildPlatformName" -p:TargetFrameworks="$framework" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true - echo "" - echo "" + Write-Output "" + Write-Output "" foreach ($modName in $bundleModNames) { - echo "Compiling $modName for $folder..." - echo "-------------------------------------------------" + Write-Output "Compiling $modName for $folder..." + Write-Output "-------------------------------------------------" dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" --framework "$framework" -p:OS="$msbuildPlatformName" -p:TargetFrameworks="$framework" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained false - echo "" - echo "" + Write-Output "" + Write-Output "" } } @@ -120,8 +146,8 @@ foreach ($folder in $folders) { ########## ## Prepare install package ########## -echo "Preparing install package..." -echo "----------------------------" +Write-Output "Preparing install package..." +Write-Output "----------------------------" # init paths $installAssets = "src/SMAPI.Installer/assets" @@ -130,7 +156,16 @@ $packageDevPath = "bin/SMAPI installer for developers" # init structure foreach ($folder in $folders) { - mkdir "$packagePath/internal/$folder/bundle/smapi-internal" > $null + $folderPath = "$packagePath/internal/$folder/bundle/smapi-internal" + + if ($IsWindows) { + # On Windows, mkdir creates parent directories automatically and the --parents argument isn't recognized. + mkdir "$folderPath" > $null + } + else + { + mkdir "$folderPath" --parents + } } # copy base installer files @@ -139,7 +174,7 @@ foreach ($name in @("install on Linux.sh", "install on macOS.command", "install continue; } - cp "$installAssets/$name" "$packagePath" + Copy-Item "$installAssets/$name" "$packagePath" } # copy per-platform files @@ -152,18 +187,18 @@ foreach ($folder in $folders) { $bundlePath = "$internalPath/bundle" # installer files - cp "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish/*" "$internalPath" -Recurse - rm -Recurse -Force "$internalPath/assets" + Copy-Item "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish/*" "$internalPath" -Recurse + Remove-Item -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 + # set to 6.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" + Copy-Item "$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" + Copy-Item "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config" } # bundle root files @@ -172,29 +207,29 @@ foreach ($folder in $folders) { $name = "$name.exe" } - cp "$smapiBin/$name" "$bundlePath" + Copy-Item "$smapiBin/$name" "$bundlePath" } # bundle i18n - cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal" + Copy-Item -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal" # bundle smapi-internal foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Markdig.dll", "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" + Copy-Item "$smapiBin/$name" "$bundlePath/smapi-internal" } if ($folder -eq "windows") { - cp "$smapiBin/VdfConverter.dll" "$bundlePath/smapi-internal" + Copy-Item "$smapiBin/VdfConverter.dll" "$bundlePath/smapi-internal" } - cp "$smapiBin/SMAPI.blacklist.json" "$bundlePath/smapi-internal/blacklist.json" - cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json" - cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json" + Copy-Item "$smapiBin/SMAPI.blacklist.json" "$bundlePath/smapi-internal/blacklist.json" + Copy-Item "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json" + Copy-Item "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json" if ($folder -eq "linux" -or $folder -eq "macOS") { - cp "$installAssets/unix-launcher.sh" "$bundlePath" + Copy-Item "$installAssets/unix-launcher.sh" "$bundlePath" } else { - cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config" + Copy-Item "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config" } # copy bundled mods @@ -202,38 +237,47 @@ foreach ($folder in $folders) { $fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish" $targetPath = "$bundlePath/Mods/$modName" - mkdir "$targetPath" > $null + if ($IsWindows) { + # On Windows, mkdir creates parent directories automatically and the --parents argument isn't recognized. + mkdir "$targetPath" > $null + } + else + { + mkdir "$targetPath" --parents + } - cp "$fromPath/$modName.dll" "$targetPath" - cp "$fromPath/manifest.json" "$targetPath" + Copy-Item "$fromPath/$modName.dll" "$targetPath" + Copy-Item "$fromPath/manifest.json" "$targetPath" if (Test-Path "$fromPath/i18n" -PathType Container) { - cp -Recurse "$fromPath/i18n" "$targetPath" + Copy-Item -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" -# } -#} +if ($IsWindows) { + Write-Warning "Can't set Unix file permissions on Windows. This may cause issues for Linux/macOS players." +} +else { + 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" +Copy-Item -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" # convert bundle folder into final 'install.dat' files - if ($windowsOnly) + foreach ($path in @("$packagePath/internal/$folder", "$packageDevPath/internal/$folder")) { - 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" + Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.dat" + if (!$skipBundleDeletion) { + Remove-Item -Recurse -Force "$path/bundle" } } } @@ -243,15 +287,12 @@ foreach ($folder in $folders) { ### Create release zips ########### # rename folders -mv "$packagePath" "bin/SMAPI $version installer" -mv "$packageDevPath" "bin/SMAPI $version installer for developers" +Move-Item "$packagePath" "bin/SMAPI $version installer" +Move-Item "$packageDevPath" "bin/SMAPI $version installer for developers" # package files -if ($windowsOnly) -{ - 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 -} +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." +Write-Output "" +Write-Output "Done! Package created in ${pwd.Path}/bin." diff --git a/build/scripts/set-smapi-version.ps1 b/build/scripts/set-smapi-version.ps1 index 1e109975..a8960865 100644 --- a/build/scripts/set-smapi-version.ps1 +++ b/build/scripts/set-smapi-version.ps1 @@ -1,11 +1,3 @@ -# -# -# 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 @@ -15,7 +7,7 @@ if (!$version) { } # move to SMAPI root -cd "$PSScriptRoot/../.." +Set-Location "$PSScriptRoot/../.." # apply changes In-Place-Regex -Path "build/common.targets" -Search ".+" -Replace "$version" diff --git a/build/unix/prepare-install-package.sh b/build/unix/prepare-install-package.sh deleted file mode 100755 index 837f8884..00000000 --- a/build/unix/prepare-install-package.sh +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env bash - -# -# -# This is the Bash equivalent of ../windows/prepare-install-package.ps1. -# When making changes, both scripts should be updated. -# -# - - -########## -## Find the game folder -########## -possibleGamePaths=( - # override - "$HOME/StardewValley" - - # Linux - "$HOME/GOG Games/Stardew Valley/game" - "$HOME/.steam/steam/steamapps/common/Stardew Valley" - "$HOME/.local/share/Steam/steamapps/common/Stardew Valley" - "$HOME/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/Stardew Valley" - - # macOS - "/Applications/Stardew Valley.app/Contents/MacOS" - "$HOME/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS" -) -gamePath="" -for possibleGamePath in "${possibleGamePaths[@]}"; do - if [[ -d "$possibleGamePath" ]]; then - gamePath="$possibleGamePath" - break - fi -done - - -########## -## Preset values -########## -# paths -bundleModNames=("ConsoleCommands" "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") - -# 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"`/../.." - - -########## -## 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 -########## -. ${0%/*}/set-smapi-version.sh "$version" -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" --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" --self-contained 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.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" "Markdig.dll" "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 - - cp "$smapiBin/SMAPI.blacklist.json" "$bundlePath/smapi-internal/blacklist.json" - 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" - else - cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config" - 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/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 -########## -# 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 deleted file mode 100755 index 3ab48eb5..00000000 --- a/build/unix/set-smapi-version.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env 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" "SaveBackup"; do - sed "s/\"(Version|MinimumApiVersion)\": \".+?\"/\"\1\": \"$version\"/g" "src/SMAPI.Mods.$modName/manifest.json" --in-place --regexp-extended -done diff --git a/docs/technical/smapi.md b/docs/technical/smapi.md index 801a2679..837b9488 100644 --- a/docs/technical/smapi.md +++ b/docs/technical/smapi.md @@ -93,12 +93,13 @@ option._ #### 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. Follow the rest of the instructions inside 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). + 1. Install... + - [.NET 6 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu) (run + `lsb_release -a` if you need the Ubuntu version number); + - [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-linux); + - and [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): @@ -122,7 +123,7 @@ option._ 2. Launch the game through the Steam UI. ### Prepare the release -1. Run `build/unix/prepare-install-package.sh VERSION_HERE` to create the release package in the +1. Run `pwsh build/scripts/prepare-install-package.sh VERSION_HERE` to create the release package in the root `bin` folder. Make sure you use a [semantic version](https://semver.org). Recommended format: @@ -152,8 +153,8 @@ above for the unified process._ ``` ### Prepare the release -1. Run `build/scripts/prepare-install-package.ps1 VERSION_HERE` in PowerShell to create the release - package folders in the root `bin` folder. +1. Run `pwsh build/scripts/prepare-install-package.ps1 VERSION_HERE --skip-bundle-deletion` to + create the release package folders in the root `bin` folder. Make sure you use a [semantic version](https://semver.org). Recommended format: @@ -169,7 +170,7 @@ above for the unified process._ # In WSL, `/mnt/c/example` accesses `C:\example` on the Windows filesystem. version="4.0.0" binFolder="/mnt/e/source/_Stardew/SMAPI/bin" - build/scripts/finalize-install-package.sh "$version" "$binFolder" + pwsh build/scripts/finalize-install-package.sh "$version" "$binFolder" ``` Note: to prepare a test Windows-only build, you can pass `--windows-only` in the first step and diff --git a/src/SMAPI.slnx b/src/SMAPI.slnx index 256010c3..8a76672c 100644 --- a/src/SMAPI.slnx +++ b/src/SMAPI.slnx @@ -28,10 +28,6 @@ - - - - From 668203b40d32f4a01d903bb81b08ea02c13e5b4c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Jan 2026 23:20:25 -0500 Subject: [PATCH 14/26] update technical docs for build pipeline --- docs/technical/smapi.md | 141 +++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/docs/technical/smapi.md b/docs/technical/smapi.md index 837b9488..6c1f16d8 100644 --- a/docs/technical/smapi.md +++ b/docs/technical/smapi.md @@ -14,9 +14,11 @@ This document is about SMAPI itself; see also [mod build package](mod-package.md * [Compile from source code](#compile-from-source-code) * [Main project](#main-project) * [Custom Harmony build](#custom-harmony-build) +* [Version format](#version-format) * [Prepare a release](#prepare-a-release) - * [On any platform](#on-any-platform) - * [On Windows](#on-windows) + * [Automated build pipeline](#automated-build-pipeline) + * [Manual release on any platform](#manual-release-on-any-platform) + * [Manual release On Windows](#manual-release-on-windows) * [Release notes](#release-notes) ## Customisation @@ -67,6 +69,15 @@ 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`. +## Version format +SMAPI uses [semantic versioning](https://semver.org). Typical format: + +build type | format | example +:--------- | :----------------------- | :------ +dev build | `-alpha.` | `4.0.0-alpha.20251230` +prerelease | `-beta.` | `4.0.0-beta.20251230` +release | `` | `4.0.0` + ## Compile from source code ### Main project Using an official SMAPI release is recommended for most users, but you can compile from source @@ -80,89 +91,86 @@ the `SMAPI` project with debugging from Visual Studio or Rider should launch SMA debugger attached, so you can intercept errors and step through the code being executed. ### Custom Harmony build -SMAPI uses [a custom build of Harmony 2.2.2](https://github.com/Pathoschild/Harmony#readme), which -is included in the `build` folder. To use a different build, just replace `0Harmony.dll` in that +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. ## Prepare a release -### On any platform -_This is the unified process that works on any platform. However, it needs a few extra steps on -Windows (e.g. running Steam in WSL); see ['On Windows'](#on-windows) below for an alternative quick -option._ +### Automated build pipeline +SMAPI releases can be compiled automatically on GitHub. This is the recommended approach for +official releases, since... +- It eliminates the risk of malware on your computer infecting the release. +- It creates an [attestation](https://docs.github.com/en/actions/concepts/security/artifact-attestations), + so security-savvy users can verify that the release was compiled from the code on GitHub and + hasn't been tampered with. +- It benefits from future workflow improvements like code signing. -#### First-time setup +The typical process is: + +1. Commit changes on the `develop` branch. + * _Note: for an alpha version, you're done! All commits on `develop` are compiled into alpha + releases. See the 'Actions' tab on GitHub._ +2. Set SMAPI's [version](#version-format) in `build/common.targets`, `src/SMAPI/Constants.cs`, and + each mod's `manifest.json`. + * _You can run `pwsh build/scripts/set-smapi-version.ps1 VERSION_HERE` to do it automatically. + On Linux/macOS, you'll need to [install PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell)._ +3. Update the [release notes](../release-notes.md). +4. Commit. +5. Merge `develop` into `stable`. +6. Tag the merge commit with the version (e.g. `5.0.0`). +7. Check the 'Actions' tab on GitHub for the build, and download its artifacts when it finishes. + +### Manual release on any platform +> [!WARNING] +> This lets you manually prepare a release, which is fine for personal use. However, official +> releases should use the [automated build pipeline](#automated-build-pipeline) instead. + +> [!TIP] +> This approach works on any platform, but it's a bit complicated on Windows. See also [_Manual +> release on Windows_](#manual-release-on-windows) for a simpler approach. + +First-time setup: 1. On Windows only: 1. [Install Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install). 2. Follow the rest of the instructions inside WSL. -2. Install the required software: - 1. Install... - - [.NET 6 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu) (run - `lsb_release -a` if you need the Ubuntu version number); - - [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-linux); - - and [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 - sudo tar -xzvf rider-install.tar.gz -C /opt - ln -s "/opt/JetBrains Rider-/bin/rider.sh" - ./rider.sh - ``` -3. Clone the SMAPI repo: +2. Install... + - [.NET 6 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu) (run + `lsb_release -a` if you need the Ubuntu version number); + - [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell); + - and Steam (see [Linux instructions](https://linuxconfig.org/how-to-install-steam-on-ubuntu-20-04-focal-fossa-linux)). +3. Launch `steam` and install the game like usual. +4. Clone the SMAPI repo: ```sh git clone https://github.com/Pathoschild/SMAPI.git ``` -### Launch the game -1. Run these commands to start Steam: - ```sh - export TERM=xterm - steam - ``` -2. Launch the game through the Steam UI. - -### Prepare the release +To prepare the release: 1. Run `pwsh build/scripts/prepare-install-package.sh VERSION_HERE` to create the release package in the - root `bin` folder. + root `bin` folder. Make sure you use a [semantic version](#version-format). - Make sure you use a [semantic version](https://semver.org). Recommended format: +### Manual release On Windows +> [!WARNING] +> This lets you manually prepare a release, which is fine for personal use. However, official +> releases should use the [automated build pipeline](#automated-build-pipeline) instead. - build type | format | example - :--------- | :----------------------- | :------ - dev build | `-alpha.` | `4.0.0-alpha.20251230` - prerelease | `-beta.` | `4.0.0-beta.20251230` - release | `` | `4.0.0` +> [!TIP] +> To prepare a Windows-only build, you can skip WSL and replace `--skip-bundle-deletion` with `--windows-only` +> when calling `prepare-install-package.ps1`. -### On Windows -_This is the alternative quick process for Windows only. This avoids needing Steam installed on WSL, -and can be used to create Windows-only builds without using WSL at all. See ['on any platform'](#on-any-platform) -above for the unified process._ - -#### 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: +First-time setup: +1. Install... + - [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install); + - [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/5.0); + - [Stardew Valley](https://www.stardewvalley.net/). +2. Clone the SMAPI repo: ```sh git clone https://github.com/Pathoschild/SMAPI.git ``` -### Prepare the release +To prepare the release: 1. Run `pwsh build/scripts/prepare-install-package.ps1 VERSION_HERE --skip-bundle-deletion` to - create the release package folders in the root `bin` folder. - - 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` + create the release package folders in the root `bin` folder. Make sure you use a [semantic + version](#version-format). 2. Launch WSL and run this script: ```bash @@ -173,8 +181,5 @@ above for the unified process._ pwsh build/scripts/finalize-install-package.sh "$version" "$binFolder" ``` -Note: to prepare a test Windows-only build, you can pass `--windows-only` in the first step and -skip the second one. - ## Release notes See [release notes](../release-notes.md). From 5f94a101476a65d8fbce0ad4cda6838c22f6c711 Mon Sep 17 00:00:00 2001 From: Alan Date: Tue, 20 Jan 2026 16:32:08 +0000 Subject: [PATCH 15/26] Add shebangs to allow running PowerShell scripts directly. --- build/scripts/prepare-install-package.ps1 | 2 ++ build/scripts/set-smapi-version.ps1 | 2 ++ 2 files changed, 4 insertions(+) mode change 100644 => 100755 build/scripts/set-smapi-version.ps1 diff --git a/build/scripts/prepare-install-package.ps1 b/build/scripts/prepare-install-package.ps1 index 2218cfe9..738f9f80 100644 --- a/build/scripts/prepare-install-package.ps1 +++ b/build/scripts/prepare-install-package.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + # # # Note: On Windows, this script *does not* set Linux permissions. The final changes are handled by the diff --git a/build/scripts/set-smapi-version.ps1 b/build/scripts/set-smapi-version.ps1 old mode 100644 new mode 100755 index a8960865..6890d13d --- a/build/scripts/set-smapi-version.ps1 +++ b/build/scripts/set-smapi-version.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + . "$PSScriptRoot\lib\in-place-regex.ps1" # get version number From 5c3237bd90903f37ae5b89e050c1004463600364 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:00 -0500 Subject: [PATCH 16/26] add support for detecting known malicious loose files --- docs/release-notes.md | 16 ++--- .../LooseFileBlacklistEntryModel.cs | 37 ++++++++++++ .../ModBlacklistData/ModBlacklist.cs | 59 ++++++++++++++++++- .../ModBlacklistEntryModel.cs | 8 ++- .../ModBlacklistData/ModBlacklistModel.cs | 7 ++- src/SMAPI.Web/wwwroot/SMAPI.blacklist.json | 11 +++- src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- src/SMAPI/Framework/SCore.cs | 21 ++++++- 8 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 src/SMAPI.Toolkit/Framework/ModBlacklistData/LooseFileBlacklistEntryModel.cs diff --git a/docs/release-notes.md b/docs/release-notes.md index 5ffe1fbf..394bf5be 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,18 +2,10 @@ # Release notes ## Upcoming release -**SMAPI release builds are automated on GitHub** (thanks to DecidedlyHuman!). - -The main benefits include... -- Significantly improved security: - - This eliminates the risk of hidden malware infecting SMAPI releases. - - When downloading a release, you can compare the download's file hash against the GitHub build to - guarantee that (a) it was compiled from the code on GitHub and (b) it has not been tampered with. -- You can now download a preview of the next SMAPI release at any time, since every commit produces an alpha build. - -This is also a big step towards future improvements, like... -- signing builds to reduce antivirus alerts on Windows and macOS; -- and automating mod builds too. +* For players: + * SMAPI now uses [automated and attested builds](https://www.patreon.com/posts/automated-builds-148417912) (thanks to DecidedlyHuman)! + _This improves the security and transparency of SMAPI builds. Every step to build SMAPI from the public source code is now public and verifiable, with file signatures to let players and tools confirm the build hasn't been tampered with._ + * SMAPI can now detect known malicious loose files in the `Mods` folder. ## 4.4.0 Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705). diff --git a/src/SMAPI.Toolkit/Framework/ModBlacklistData/LooseFileBlacklistEntryModel.cs b/src/SMAPI.Toolkit/Framework/ModBlacklistData/LooseFileBlacklistEntryModel.cs new file mode 100644 index 00000000..bc437574 --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/ModBlacklistData/LooseFileBlacklistEntryModel.cs @@ -0,0 +1,37 @@ +namespace StardewModdingAPI.Toolkit.Framework.ModBlacklistData; + +/// A loose file entry in the . +public class LooseFileBlacklistEntryModel +{ + /********* + ** Accessors + *********/ + /// The file name to block (if any). + public string? Name { get; } + + /// The file extension to block (if any), including the dot. + public string? Extension { get; } + + /// The MD5 hash to block (if any). + public string? Hash { get; } + + /// A player-friendly explanation of why the mod is blocked and what they should do next. + public string? Message { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// + /// + /// + /// + public LooseFileBlacklistEntryModel(string? name, string? extension, string? hash, string message) + { + this.Name = name; + this.Extension = extension; + this.Hash = hash; + this.Message = message; + } +} diff --git a/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklist.cs b/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklist.cs index e5d0419b..411168a7 100644 --- a/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklist.cs +++ b/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklist.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.IO; using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.Toolkit.Framework.ModBlacklistData; @@ -18,7 +20,7 @@ public class ModBlacklist *********/ /// Construct an empty instance. public ModBlacklist() - : this(new ModBlacklistModel([])) { } + : this(new ModBlacklistModel([], [])) { } /// Construct an instance. /// The underlying mod blacklist data. @@ -30,7 +32,7 @@ public class ModBlacklist /// Get the blacklist entry for a mod, if any. /// The unique mod ID. /// The absolute path to the entry DLL, if this is a C# mod. - public ModBlacklistEntryModel? Get(string modId, string? entryDllPath) + public ModBlacklistEntryModel? CheckMod(string modId, string? entryDllPath) { string? entryDllHash = null; @@ -56,4 +58,57 @@ public class ModBlacklist return null; } + + /// Scan a folder to detect any files which match the list of malicious files. + /// The root path to scan. + public IEnumerable<(string FilePath, LooseFileBlacklistEntryModel Match)> CheckLooseFiles(string rootPath) + { + foreach (string path in Directory.EnumerateFiles(rootPath, "*.*", SearchOption.AllDirectories)) + { + LooseFileBlacklistEntryModel? blacklistEntry = this.CheckLooseFile(path); + + if (blacklistEntry != null) + yield return (path, blacklistEntry); + } + } + + /// Get the blacklist entry for a loose file, if any. + /// The absolute path to the file to check. + public LooseFileBlacklistEntryModel? CheckLooseFile(string fullPath) + { + string? name = null; + string? extension = null; + string? hash = null; + + foreach (LooseFileBlacklistEntryModel entry in this.Blacklist.LooseFileBlacklist) + { + // check file name + if (entry.Name != null) + { + name ??= Path.GetFileName(fullPath); + if (!string.Equals(name, entry.Name, StringComparison.OrdinalIgnoreCase)) + continue; + } + + // check file extension + if (entry.Extension != null) + { + extension ??= Path.GetExtension(fullPath); + if (!string.Equals(extension, entry.Extension, StringComparison.OrdinalIgnoreCase)) + continue; + } + + // check hash + if (entry.Hash != null) + { + hash ??= FileUtilities.GetFileHash(fullPath); + if (!string.Equals(hash, entry.Hash, StringComparison.OrdinalIgnoreCase)) + continue; + } + + return entry; + } + + return null; + } } diff --git a/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistEntryModel.cs b/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistEntryModel.cs index 8a82785f..302abd6f 100644 --- a/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistEntryModel.cs +++ b/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistEntryModel.cs @@ -3,15 +3,17 @@ namespace StardewModdingAPI.Toolkit.Framework.ModBlacklistData; /// A mod entry in the . public class ModBlacklistEntryModel { + /********* + ** Accessors + *********/ /// The manifest IDs to block (if any). public string? Id { get; } - /// The MD5 hashes of the entry DLL to block (if any). - /// Due to the chance of MD5 collisions, this should only be used in addition to the . + /// The MD5 hash of the entry DLL to block (if any). public string? EntryDllHash { get; } /// A player-friendly explanation of why the mod is blocked and what they should do next. - public string Message { get; } + public string? Message { get; } /********* diff --git a/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistModel.cs b/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistModel.cs index 3c671584..b0118f10 100644 --- a/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistModel.cs +++ b/src/SMAPI.Toolkit/Framework/ModBlacklistData/ModBlacklistModel.cs @@ -9,14 +9,19 @@ public class ModBlacklistModel /// Metadata about malicious or harmful SMAPI mods which are disabled by default. public ModBlacklistEntryModel[] Blacklist { get; } + /// Metadata about individual files which are known to be malicious, and should be blocked. If any file in a folder matches an entry, the entire folder is considered malicious. + public LooseFileBlacklistEntryModel[] LooseFileBlacklist { get; } + /********* ** Public methods *********/ /// Construct an instance. /// - public ModBlacklistModel(ModBlacklistEntryModel[]? blacklist) + /// + public ModBlacklistModel(ModBlacklistEntryModel[]? blacklist, LooseFileBlacklistEntryModel[]? looseFileBlacklist) { this.Blacklist = blacklist ?? []; + this.LooseFileBlacklist = looseFileBlacklist ?? []; } } diff --git a/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json b/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json index c3f79ad3..38109bb9 100644 --- a/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json +++ b/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json @@ -7,12 +7,19 @@ "Blacklist": [ // 2025-07 { - "Id": "OpUlarenous.PortableCommunityCenter", // 'Even Better Portable Community Center' (Nexus:35601, dfd9e53ee3e6fb997c04a2b0c7f49a50) + "Id": "OpUlarenous.PortableCommunityCenter", // 'Even Better Portable Community Center' (Nexus:35601, dfd9e53ee3e6fb997c04a2b0c7f49a50, malicious reupload of legit mod) "Message": "It downloads malicious code from a remote server and runs it on your computer. SMAPI refused to load the mod, but you should delete this mod and run a complete anti-malware scan of your computer now to be safe." }, { - "Id": "BrEvoiultion.GiveSpeedInMorning", // 'Give Speed In Morning' (Nexus:35650, 97860c755192a27cdb6c16ec32327774) + "Id": "BrEvoiultion.GiveSpeedInMorning", // 'Give Speed In Morning' (Nexus:35650, 97860c755192a27cdb6c16ec32327774, malicious reupload of legit mod) "Message": "It downloads malicious code from a remote server and runs it on your computer. SMAPI refused to load the mod, but you should delete this mod and run a complete anti-malware scan of your computer now to be safe." } + ], + + /** + * Metadata about individual files which are known to be malicious, and should be blocked. + * If any file in a folder matches an entry, the entire folder is considered malicious. + */ + "LooseFileBlacklist": [ ] } diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 33099d5a..527498cf 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -43,7 +43,7 @@ internal class ModResolver if (!File.Exists(entryDllPath)) entryDllPath = null; - blacklistEntry = modBlacklist.Get(manifest.UniqueID, entryDllPath); + blacklistEntry = modBlacklist.CheckMod(manifest.UniqueID, entryDllPath); } // parse internal data record (if any) diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index d3de474b..31166cf9 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -444,6 +444,25 @@ internal class SCore : IDisposable Constants.ApiBlacklistActualPath = Constants.ApiBlacklistPath; } + // check for malicious loose files + this.Monitor.Log("Scanning for malicious files..."); + { + bool foundMaliciousFiles = false; + + foreach ((string filePath, LooseFileBlacklistEntryModel match) in modBlacklist.CheckLooseFiles(this.ModsPath)) + { + foundMaliciousFiles = true; + + this.Monitor.LogFatal("Malicious mod file detected."); + this.Monitor.Log($"File path: '{filePath}'", LogLevel.Error); + this.Monitor.Newline(); + this.Monitor.Log(match.Message ?? "This file has been flagged as malicious. You should immediately delete the mod containing the file, and perform a full anti-malware scan of your computer to be safe.", LogLevel.Error); + } + + if (foundMaliciousFiles) + this.LogManager.PressAnyKeyToExit(); + } + // load mods { this.Monitor.Log("Loading mod metadata...", LogLevel.Debug); @@ -743,7 +762,7 @@ internal class SCore : IDisposable // conflict (e.g. collection changed during enumeration errors) and data may change // unexpectedly from one mod instruction to the next. // - // Therefore we can just run Game1.Update here without raising any SMAPI events. There's + // Therefore, we can just run Game1.Update here without raising any SMAPI events. There's // a small chance that the task will finish after we defer but before the game checks, // which means technically events should be raised, but the effects of missing one // update tick are negligible and not worth the complications of bypassing Game1.Update. From 8e1f01afddf41091904d533b84c31f3c035547c6 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 17/26] add malicious loose file to blacklist --- docs/release-notes.md | 1 + src/SMAPI.Web/wwwroot/SMAPI.blacklist.json | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 394bf5be..39e4b91d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,7 @@ * SMAPI now uses [automated and attested builds](https://www.patreon.com/posts/automated-builds-148417912) (thanks to DecidedlyHuman)! _This improves the security and transparency of SMAPI builds. Every step to build SMAPI from the public source code is now public and verifiable, with file signatures to let players and tools confirm the build hasn't been tampered with._ * SMAPI can now detect known malicious loose files in the `Mods` folder. + * Updated internal mod blacklist. ## 4.4.0 Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705). diff --git a/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json b/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json index 38109bb9..59892d63 100644 --- a/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json +++ b/src/SMAPI.Web/wwwroot/SMAPI.blacklist.json @@ -21,5 +21,11 @@ * If any file in a folder matches an entry, the entire folder is considered malicious. */ "LooseFileBlacklist": [ + // 2026-01: 'Alchemistry' (ModDrop:1731397, malicious reupload of legit mod) + { + "Name": "Auto_Alchemistry.bat", + "Hash": "8b749727775689a05cc92ab4d624ab8b", + "Message": "It downloads malicious code from a remote server and runs it on your computer. SMAPI refused to load the mod, but you should delete the mod containing this file and run a complete anti-malware scan of your computer now to be safe." + } ] } From 9cf2218b28e838cf3815f72d6097f6d216f6b107 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 18/26] add blacklist URI to SConfig constructor --- src/SMAPI/Framework/Models/SConfig.cs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs index eee297f6..b62ff1d5 100644 --- a/src/SMAPI/Framework/Models/SConfig.cs +++ b/src/SMAPI/Framework/Models/SConfig.cs @@ -124,6 +124,7 @@ internal class SConfig /// /// /// + /// /// /// /// @@ -135,7 +136,29 @@ internal class SConfig /// /// /// - public SConfig(bool developerMode, bool? checkForUpdates, bool? checkForBlacklistUpdates, bool? checkContentIntegrity, bool? listenForConsoleInput, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? fixHarmony, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, bool? logTechnicalDetailsForBrokenMods, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadEarly, string[]? modsToLoadLate) + public SConfig( + bool developerMode, + bool? checkForUpdates, + bool? checkForBlacklistUpdates, + bool? checkContentIntegrity, + bool? listenForConsoleInput, + bool? paranoidWarnings, + bool? useBetaChannel, + string gitHubProjectName, + string webApiBaseUrl, + string blacklistUrl, + string[]? verboseLogging, + bool? rewriteMods, + bool? fixHarmony, + bool? useCaseInsensitivePaths, + bool? logNetworkTraffic, + bool? logTechnicalDetailsForBrokenMods, + ColorSchemeConfig consoleColors, + bool? suppressHarmonyDebugMode, + string[]? suppressUpdateChecks, + string[]? modsToLoadEarly, + string[]? modsToLoadLate + ) { this.DeveloperMode = developerMode; this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)]; @@ -146,6 +169,7 @@ internal class SConfig this.UseBetaChannel = useBetaChannel ?? (bool)SConfig.DefaultValues[nameof(this.UseBetaChannel)]; this.GitHubProjectName = gitHubProjectName; this.WebApiBaseUrl = webApiBaseUrl; + this.BlacklistUrl = blacklistUrl; this.VerboseLogging = new HashSet(verboseLogging ?? [], StringComparer.OrdinalIgnoreCase); this.RewriteMods = rewriteMods ?? (bool)SConfig.DefaultValues[nameof(this.RewriteMods)]; this.FixHarmony = fixHarmony ?? (bool)SConfig.DefaultValues[nameof(this.FixHarmony)]; From a00592e6a6c7801f2ae9b8d580655df1c6e0620d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 19/26] split color scheme ID & colors into separate settings That will simplify making the color scheme ID editable in-game. --- src/SMAPI.Installer/InteractiveInstaller.cs | 4 +- .../ConsoleWriting/ColorSchemeConfig.cs | 30 -------- .../ConsoleWriting/ColorfulConsoleWriter.cs | 68 +++++++++---------- src/SMAPI.Internal/SMAPI.Internal.projitems | 1 - src/SMAPI/Framework/Logging/LogManager.cs | 5 +- src/SMAPI/Framework/Models/SConfig.cs | 14 ++-- src/SMAPI/Framework/Monitor.cs | 5 +- src/SMAPI/Framework/SCore.cs | 2 +- src/SMAPI/SMAPI.config.json | 59 ++++++++-------- 9 files changed, 83 insertions(+), 105 deletions(-) delete mode 100644 src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index 226cb0ab..714809fc 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -191,8 +191,8 @@ internal class InteractiveInstaller ** show theme selector ****/ // get theme writers - ColorfulConsoleWriter lightBackgroundWriter = new(context.Platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.LightBackground)); - ColorfulConsoleWriter darkBackgroundWriter = new(context.Platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.DarkBackground)); + ColorfulConsoleWriter lightBackgroundWriter = new(context.Platform, MonitorColorScheme.LightBackground, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.LightBackground)); + ColorfulConsoleWriter darkBackgroundWriter = new(context.Platform, MonitorColorScheme.DarkBackground, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.DarkBackground)); // print question this.PrintPlain("Which text looks more readable?"); diff --git a/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs b/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs deleted file mode 100644 index aaed0635..00000000 --- a/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace StardewModdingAPI.Internal.ConsoleWriting; - -/// The console color scheme options. -internal class ColorSchemeConfig -{ - /********* - ** Accessors - *********/ - /// The default color scheme ID to use, or to select one automatically. - public MonitorColorScheme UseScheme { get; set; } - - /// The available console color schemes. - public IDictionary> Schemes { get; set; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The default color scheme ID to use, or to select one automatically. - /// The available console color schemes. - public ColorSchemeConfig(MonitorColorScheme useScheme, IDictionary> schemes) - { - this.UseScheme = useScheme; - this.Schemes = schemes; - } -} diff --git a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs index d5c8fa93..4fdfc3b7 100644 --- a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs +++ b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs @@ -25,14 +25,15 @@ internal class ColorfulConsoleWriter : IConsoleWriter /// Construct an instance. /// The target platform. public ColorfulConsoleWriter(Platform platform) - : this(platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.AutoDetect)) { } + : this(platform, MonitorColorScheme.AutoDetect, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.AutoDetect)) { } /// Construct an instance. /// The target platform. + /// The color scheme ID in to use, or to select one automatically. /// The colors to use for text written to the SMAPI console. - public ColorfulConsoleWriter(Platform platform, ColorSchemeConfig colorConfig) + public ColorfulConsoleWriter(Platform platform, MonitorColorScheme colorSchemeId, Dictionary> colorConfig) { - if (colorConfig.UseScheme == MonitorColorScheme.None) + if (colorSchemeId == MonitorColorScheme.None) { this.SupportsColor = false; this.Colors = null; @@ -40,7 +41,7 @@ internal class ColorfulConsoleWriter : IConsoleWriter else { this.SupportsColor = this.TestColorSupport(); - this.Colors = this.GetConsoleColorScheme(platform, colorConfig); + this.Colors = this.GetConsoleColorScheme(platform, colorSchemeId, colorConfig); } } @@ -73,34 +74,31 @@ internal class ColorfulConsoleWriter : IConsoleWriter /// Get the default color scheme config for cases where it's not configurable (e.g. the installer). /// The default color scheme ID to use, or to select one automatically. /// The colors here should be kept in sync with the SMAPI config file. - public static ColorSchemeConfig GetDefaultColorSchemeConfig(MonitorColorScheme useScheme) + public static Dictionary> GetDefaultColorSchemeConfig(MonitorColorScheme useScheme) { - return new ColorSchemeConfig( - useScheme: useScheme, - schemes: new Dictionary> + return new Dictionary> + { + [MonitorColorScheme.DarkBackground] = new() { - [MonitorColorScheme.DarkBackground] = new Dictionary - { - [ConsoleLogLevel.Trace] = ConsoleColor.DarkGray, - [ConsoleLogLevel.Debug] = ConsoleColor.DarkGray, - [ConsoleLogLevel.Info] = ConsoleColor.White, - [ConsoleLogLevel.Warn] = ConsoleColor.Yellow, - [ConsoleLogLevel.Error] = ConsoleColor.Red, - [ConsoleLogLevel.Alert] = ConsoleColor.Magenta, - [ConsoleLogLevel.Success] = ConsoleColor.DarkGreen - }, - [MonitorColorScheme.LightBackground] = new Dictionary - { - [ConsoleLogLevel.Trace] = ConsoleColor.DarkGray, - [ConsoleLogLevel.Debug] = ConsoleColor.DarkGray, - [ConsoleLogLevel.Info] = ConsoleColor.Black, - [ConsoleLogLevel.Warn] = ConsoleColor.DarkYellow, - [ConsoleLogLevel.Error] = ConsoleColor.Red, - [ConsoleLogLevel.Alert] = ConsoleColor.DarkMagenta, - [ConsoleLogLevel.Success] = ConsoleColor.DarkGreen - } + [ConsoleLogLevel.Trace] = ConsoleColor.DarkGray, + [ConsoleLogLevel.Debug] = ConsoleColor.DarkGray, + [ConsoleLogLevel.Info] = ConsoleColor.White, + [ConsoleLogLevel.Warn] = ConsoleColor.Yellow, + [ConsoleLogLevel.Error] = ConsoleColor.Red, + [ConsoleLogLevel.Alert] = ConsoleColor.Magenta, + [ConsoleLogLevel.Success] = ConsoleColor.DarkGreen + }, + [MonitorColorScheme.LightBackground] = new() + { + [ConsoleLogLevel.Trace] = ConsoleColor.DarkGray, + [ConsoleLogLevel.Debug] = ConsoleColor.DarkGray, + [ConsoleLogLevel.Info] = ConsoleColor.Black, + [ConsoleLogLevel.Warn] = ConsoleColor.DarkYellow, + [ConsoleLogLevel.Error] = ConsoleColor.Red, + [ConsoleLogLevel.Alert] = ConsoleColor.DarkMagenta, + [ConsoleLogLevel.Success] = ConsoleColor.DarkGreen } - ); + }; } @@ -123,22 +121,22 @@ internal class ColorfulConsoleWriter : IConsoleWriter /// Get the color scheme to use for the current console. /// The target platform. + /// The color scheme ID in to use, or to select one automatically. /// The colors to use for text written to the SMAPI console. - private IDictionary GetConsoleColorScheme(Platform platform, ColorSchemeConfig colorConfig) + private IDictionary GetConsoleColorScheme(Platform platform, MonitorColorScheme colorSchemeId, Dictionary> colorConfig) { // get color scheme ID - MonitorColorScheme schemeId = colorConfig.UseScheme; - if (schemeId == MonitorColorScheme.AutoDetect) + if (colorSchemeId == MonitorColorScheme.AutoDetect) { - schemeId = platform == Platform.Mac + colorSchemeId = platform == Platform.Mac ? MonitorColorScheme.LightBackground // macOS doesn't provide console background color info, but it's usually white. : ColorfulConsoleWriter.IsDark(Console.BackgroundColor) ? MonitorColorScheme.DarkBackground : MonitorColorScheme.LightBackground; } // get colors for scheme - return colorConfig.Schemes.TryGetValue(schemeId, out IDictionary? scheme) + return colorConfig.TryGetValue(colorSchemeId, out Dictionary? scheme) ? scheme - : throw new NotSupportedException($"Unknown color scheme '{schemeId}'."); + : throw new NotSupportedException($"Unknown color scheme '{colorSchemeId}'."); } /// Get whether a console color should be considered dark, which is subjectively defined as 'white looks better than black on this text'. diff --git a/src/SMAPI.Internal/SMAPI.Internal.projitems b/src/SMAPI.Internal/SMAPI.Internal.projitems index 41d356c0..6dd68a9a 100644 --- a/src/SMAPI.Internal/SMAPI.Internal.projitems +++ b/src/SMAPI.Internal/SMAPI.Internal.projitems @@ -10,7 +10,6 @@ - diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index 5d93fc10..573d9185 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -47,18 +47,19 @@ internal class LogManager : IDisposable ****/ /// Construct an instance. /// The log file path to write. + /// The color scheme ID in to use, or to select one automatically. /// The colors to use for text written to the SMAPI console. /// Whether to output log messages to the console. /// The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting. /// Whether to enable full console output for developers. /// Get the screen ID that should be logged to distinguish between players in split-screen mode, if any. - public LogManager(string logPath, ColorSchemeConfig colorConfig, bool writeToConsole, HashSet verboseLogging, bool isDeveloperMode, Func getScreenIdForLog) + public LogManager(string logPath, MonitorColorScheme colorSchemeId, Dictionary> colorConfig, bool writeToConsole, HashSet verboseLogging, bool isDeveloperMode, Func getScreenIdForLog) { // init log file this.LogFile = new LogFileManager(logPath); // init monitor - this.GetMonitorImpl = (id, name) => new Monitor(id, name, this.LogFile, colorConfig, verboseLogging.Contains("*") || verboseLogging.Contains(id), getScreenIdForLog) + this.GetMonitorImpl = (id, name) => new Monitor(id, name, this.LogFile, colorSchemeId, colorConfig, verboseLogging.Contains("*") || verboseLogging.Contains(id), getScreenIdForLog) { WriteToConsole = writeToConsole, ShowTraceInConsole = isDeveloperMode, diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs index b62ff1d5..b8b80cee 100644 --- a/src/SMAPI/Framework/Models/SConfig.cs +++ b/src/SMAPI/Framework/Models/SConfig.cs @@ -95,8 +95,11 @@ internal class SConfig /// Whether to include more technical details about broken mods in the TRACE logs. This is mainly useful for creating compatibility rewriters. public bool LogTechnicalDetailsForBrokenMods { get; set; } + /// The color scheme ID in to use, or to select one automatically. + public MonitorColorScheme ConsoleColorScheme { get; set; } + /// The colors to use for text written to the SMAPI console. - public ColorSchemeConfig ConsoleColors { get; set; } + public Dictionary> ConsoleColorSchemes { get; set; } /// Whether to prevent mods from enabling Harmony's debug mode, which impacts performance and creates a file on your desktop. Debug mode should never be enabled by a released mod. public bool SuppressHarmonyDebugMode { get; set; } @@ -131,7 +134,8 @@ internal class SConfig /// /// /// - /// + /// + /// /// /// /// @@ -153,7 +157,8 @@ internal class SConfig bool? useCaseInsensitivePaths, bool? logNetworkTraffic, bool? logTechnicalDetailsForBrokenMods, - ColorSchemeConfig consoleColors, + MonitorColorScheme consoleColorScheme, + Dictionary>? consoleColorSchemes, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadEarly, @@ -176,7 +181,8 @@ internal class SConfig this.UseCaseInsensitivePaths = useCaseInsensitivePaths ?? (bool)SConfig.DefaultValues[nameof(this.UseCaseInsensitivePaths)]; this.LogNetworkTraffic = logNetworkTraffic ?? (bool)SConfig.DefaultValues[nameof(this.LogNetworkTraffic)]; this.LogTechnicalDetailsForBrokenMods = logTechnicalDetailsForBrokenMods ?? (bool)SConfig.DefaultValues[nameof(this.LogTechnicalDetailsForBrokenMods)]; - this.ConsoleColors = consoleColors; + this.ConsoleColorScheme = consoleColorScheme; + this.ConsoleColorSchemes = consoleColorSchemes ?? []; this.SuppressHarmonyDebugMode = suppressHarmonyDebugMode ?? (bool)SConfig.DefaultValues[nameof(this.SuppressHarmonyDebugMode)]; this.SuppressUpdateChecks = new HashSet(suppressUpdateChecks ?? [], StringComparer.OrdinalIgnoreCase); this.ModsToLoadEarly = new HashSet(modsToLoadEarly ?? [], StringComparer.OrdinalIgnoreCase); diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs index 608d87f6..031500e2 100644 --- a/src/SMAPI/Framework/Monitor.cs +++ b/src/SMAPI/Framework/Monitor.cs @@ -73,10 +73,11 @@ internal class Monitor : IMonitor /// The mod ID, if applicable. /// The name of the module which logs messages using this instance. /// The log file to which to write messages. + /// The color scheme ID in to use, or to select one automatically. /// The colors to use for text written to the SMAPI console. /// Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed. /// Get the screen ID that should be logged to distinguish between players in split-screen mode, if any. - public Monitor(string modId, string source, LogFileManager logFile, ColorSchemeConfig colorConfig, bool isVerbose, Func getScreenIdForLog) + public Monitor(string modId, string source, LogFileManager logFile, MonitorColorScheme colorSchemeId, Dictionary> colorConfig, bool isVerbose, Func getScreenIdForLog) { // validate if (string.IsNullOrWhiteSpace(source)) @@ -86,7 +87,7 @@ internal class Monitor : IMonitor this.ModId = modId; this.Source = source; this.LogFile = logFile ?? throw new ArgumentNullException(nameof(logFile), "The log file manager cannot be null."); - this.ConsoleWriter = new ColorfulConsoleWriter(Constants.Platform, colorConfig); + this.ConsoleWriter = new ColorfulConsoleWriter(Constants.Platform, colorSchemeId, colorConfig); this.IsVerbose = isVerbose; this.GetScreenIdForLog = getScreenIdForLog; } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 31166cf9..517c6a51 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -206,7 +206,7 @@ internal class SCore : IDisposable } // init basics - this.LogManager = new LogManager(logPath: logPath, colorConfig: this.Settings.ConsoleColors, writeToConsole: writeToConsole, verboseLogging: this.Settings.VerboseLogging, isDeveloperMode: this.Settings.DeveloperMode, getScreenIdForLog: this.GetScreenIdForLog); + this.LogManager = new LogManager(logPath: logPath, colorSchemeId: this.Settings.ConsoleColorScheme, colorConfig: this.Settings.ConsoleColorSchemes, writeToConsole: writeToConsole, verboseLogging: this.Settings.VerboseLogging, isDeveloperMode: this.Settings.DeveloperMode, getScreenIdForLog: this.GetScreenIdForLog); this.CommandManager = new CommandManager(this.Monitor); this.EventManager = new EventManager(this.ModRegistry); SCore.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json index 09a6f27a..8b92f354 100644 --- a/src/SMAPI/SMAPI.config.json +++ b/src/SMAPI/SMAPI.config.json @@ -16,7 +16,7 @@ in future SMAPI versions. */ { /** - * The logs for which to enable verbose logging, which may show a lot more information to + * The mods for which to enable verbose logging, which may show a lot more information to * simplify troubleshooting. * * The possible values are: @@ -57,8 +57,9 @@ in future SMAPI versions. "ListenForConsoleInput": true, /** - * Whether SMAPI should rewrite mods for compatibility. This may prevent older mods from - * loading, but bypasses a Visual Studio crash when debugging. + * Whether SMAPI should rewrite mods for compatibility. + * + * Disabling this may prevent older mods from loading. */ "RewriteMods": true, @@ -121,42 +122,44 @@ in future SMAPI versions. "LogTechnicalDetailsForBrokenMods": false, /** - * The colors to use for text written to the SMAPI console. + * The color scheme to apply. * - * The possible values for 'UseScheme' are: + * The possible values are: * - AutoDetect: SMAPI will assume a light background on macOS, and detect the background color * automatically on Linux or Windows. * - LightBackground: use darker text colors that look better on a white or light background. * - DarkBackground: use lighter text colors that look better on a black or dark background. * - None: disables all colors, so everything is written in the default terminal color. + */ + "ConsoleColorScheme": "AutoDetect", + + /** + * The colors to use for text written to the SMAPI console. * - * For available color codes, see https://docs.microsoft.com/en-us/dotnet/api/system.consolecolor. + * Only built-scheme IDs can be set here. For available color codes, see + * https://docs.microsoft.com/en-us/dotnet/api/system.consolecolor. * * (These values are synched with ColorfulConsoleWriter.GetDefaultColorSchemeConfig in the * SMAPI code.) */ - "ConsoleColors": { - "UseScheme": "AutoDetect", - - "Schemes": { - "DarkBackground": { - "Trace": "DarkGray", - "Debug": "DarkGray", - "Info": "White", - "Warn": "Yellow", - "Error": "Red", - "Alert": "Magenta", - "Success": "DarkGreen" - }, - "LightBackground": { - "Trace": "DarkGray", - "Debug": "DarkGray", - "Info": "Black", - "Warn": "DarkYellow", - "Error": "Red", - "Alert": "DarkMagenta", - "Success": "DarkGreen" - } + "ConsoleColorSchemes": { + "DarkBackground": { + "Trace": "DarkGray", + "Debug": "DarkGray", + "Info": "White", + "Warn": "Yellow", + "Error": "Red", + "Alert": "Magenta", + "Success": "DarkGreen" + }, + "LightBackground": { + "Trace": "DarkGray", + "Debug": "DarkGray", + "Info": "Black", + "Warn": "DarkYellow", + "Error": "Red", + "Alert": "DarkMagenta", + "Success": "DarkGreen" } }, From ad9cb482d0f6a00eafca5cbf1c5bdfffdb598c51 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 20/26] add integration with Generic Mod Config Menu --- docs/README.md | 32 +- docs/release-notes.md | 1 + .../ConsoleWriting/ColorfulConsoleWriter.cs | 21 +- src/SMAPI/Framework/Logging/LogManager.cs | 60 ++- src/SMAPI/Framework/Models/SConfig.cs | 39 +- src/SMAPI/Framework/Monitor.cs | 21 +- src/SMAPI/Framework/SCore.cs | 66 +++- .../GenericModConfigMenuIntegration.cs | 343 ++++++++++++++++++ .../IGenericModConfigMenuApi.cs | 55 +++ .../JsonSettingsWrapper.cs | 195 ++++++++++ src/SMAPI/SMAPI.config.json | 5 +- src/SMAPI/i18n/de.json | 51 ++- src/SMAPI/i18n/default.json | 50 ++- src/SMAPI/i18n/es.json | 51 ++- src/SMAPI/i18n/fr.json | 51 ++- src/SMAPI/i18n/hu.json | 51 ++- src/SMAPI/i18n/id.json | 51 ++- src/SMAPI/i18n/it.json | 51 ++- src/SMAPI/i18n/ja.json | 51 ++- src/SMAPI/i18n/ko.json | 51 ++- src/SMAPI/i18n/pl.json | 51 ++- src/SMAPI/i18n/pt.json | 51 ++- src/SMAPI/i18n/ru.json | 51 ++- src/SMAPI/i18n/th.json | 51 ++- src/SMAPI/i18n/tr.json | 51 ++- src/SMAPI/i18n/uk.json | 51 ++- src/SMAPI/i18n/zh.json | 51 ++- 27 files changed, 1566 insertions(+), 87 deletions(-) create mode 100644 src/SMAPI/Integrations/GenericModConfigMenu/GenericModConfigMenuIntegration.cs create mode 100644 src/SMAPI/Integrations/GenericModConfigMenu/IGenericModConfigMenuApi.cs create mode 100644 src/SMAPI/Integrations/GenericModConfigMenu/JsonSettingsWrapper.cs diff --git a/docs/README.md b/docs/README.md index 95cb9848..f4245146 100644 --- a/docs/README.md +++ b/docs/README.md @@ -58,22 +58,22 @@ contributing translations. locale | status ----------- | :---------------- -default | ✓ [fully translated](../src/SMAPI/i18n/default.json) -Chinese | ✓ [fully translated](../src/SMAPI/i18n/zh.json) -French | ✓ [fully translated](../src/SMAPI/i18n/fr.json) -German | ✓ [fully translated](../src/SMAPI/i18n/de.json) -Hungarian | ✓ [fully translated](../src/SMAPI/i18n/hu.json) -Indonesian | ✓ [fully translated](../src/SMAPI/i18n/id.json) -Italian | ✓ [fully translated](../src/SMAPI/i18n/it.json) -Japanese | ✓ [fully translated](../src/SMAPI/i18n/ja.json) -Korean | ✓ [fully translated](../src/SMAPI/i18n/ko.json) -[Polish] | ✓ [fully translated](../src/SMAPI/i18n/pl.json) -Portuguese | ✓ [fully translated](../src/SMAPI/i18n/pt.json) -Russian | ✓ [fully translated](../src/SMAPI/i18n/ru.json) -Spanish | ✓ [fully translated](../src/SMAPI/i18n/es.json) -[Thai] | ✓ [fully translated](../src/SMAPI/i18n/th.json) -Turkish | ✓ [fully translated](../src/SMAPI/i18n/tr.json) -[Ukrainian] | ✓ [fully translated](../src/SMAPI/i18n/uk.json) +default | ↻ [partly translated](../src/SMAPI/i18n/default.json) +Chinese | ↻ [partly translated](../src/SMAPI/i18n/zh.json) +French | ↻ [partly translated](../src/SMAPI/i18n/fr.json) +German | ↻ [partly translated](../src/SMAPI/i18n/de.json) +Hungarian | ↻ [partly translated](../src/SMAPI/i18n/hu.json) +Indonesian | ↻ [partly translated](../src/SMAPI/i18n/id.json) +Italian | ↻ [partly translated](../src/SMAPI/i18n/it.json) +Japanese | ↻ [partly translated](../src/SMAPI/i18n/ja.json) +Korean | ↻ [partly translated](../src/SMAPI/i18n/ko.json) +[Polish] | ↻ [partly translated](../src/SMAPI/i18n/pl.json) +Portuguese | ↻ [partly translated](../src/SMAPI/i18n/pt.json) +Russian | ↻ [partly translated](../src/SMAPI/i18n/ru.json) +Spanish | ↻ [partly translated](../src/SMAPI/i18n/es.json) +[Thai] | ↻ [partly translated](../src/SMAPI/i18n/th.json) +Turkish | ↻ [partly translated](../src/SMAPI/i18n/tr.json) +[Ukrainian] | ↻ [partly translated](../src/SMAPI/i18n/uk.json) [Polish]: https://www.nexusmods.com/stardewvalley/mods/3616 [Thai]: https://www.nexusmods.com/stardewvalley/mods/7052 diff --git a/docs/release-notes.md b/docs/release-notes.md index 39e4b91d..241ce635 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,6 +3,7 @@ # Release notes ## Upcoming release * For players: + * Added in-game config UI via [Generic Mod Config Menu](). * SMAPI now uses [automated and attested builds](https://www.patreon.com/posts/automated-builds-148417912) (thanks to DecidedlyHuman)! _This improves the security and transparency of SMAPI builds. Every step to build SMAPI from the public source code is now public and verifiable, with file signatures to let players and tools confirm the build hasn't been tampered with._ * SMAPI can now detect known malicious loose files in the `Mods` folder. diff --git a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs index 4fdfc3b7..aaea200e 100644 --- a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs +++ b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs @@ -11,12 +11,15 @@ internal class ColorfulConsoleWriter : IConsoleWriter /********* ** Fields *********/ + /// The target platform. + private readonly Platform Platform; + /// The console text color for each log level. - private readonly IDictionary? Colors; + private IDictionary? Colors; /// Whether the current console supports color formatting. [MemberNotNullWhen(true, nameof(ColorfulConsoleWriter.Colors))] - private bool SupportsColor { get; } + private bool SupportsColor { get; set; } /********* @@ -32,6 +35,16 @@ internal class ColorfulConsoleWriter : IConsoleWriter /// The color scheme ID in to use, or to select one automatically. /// The colors to use for text written to the SMAPI console. public ColorfulConsoleWriter(Platform platform, MonitorColorScheme colorSchemeId, Dictionary> colorConfig) + { + this.Platform = platform; + + this.SetColors(colorSchemeId, colorConfig); + } + + /// Set the color scheme to apply. + /// The color scheme ID in to use, or to select one automatically. + /// The colors to use for text written to the SMAPI console. + public void SetColors(MonitorColorScheme colorSchemeId, Dictionary> colorSchemes) { if (colorSchemeId == MonitorColorScheme.None) { @@ -41,7 +54,7 @@ internal class ColorfulConsoleWriter : IConsoleWriter else { this.SupportsColor = this.TestColorSupport(); - this.Colors = this.GetConsoleColorScheme(platform, colorSchemeId, colorConfig); + this.Colors = ColorfulConsoleWriter.GetConsoleColorScheme(this.Platform, colorSchemeId, colorSchemes); } } @@ -123,7 +136,7 @@ internal class ColorfulConsoleWriter : IConsoleWriter /// The target platform. /// The color scheme ID in to use, or to select one automatically. /// The colors to use for text written to the SMAPI console. - private IDictionary GetConsoleColorScheme(Platform platform, MonitorColorScheme colorSchemeId, Dictionary> colorConfig) + private static IDictionary GetConsoleColorScheme(Platform platform, MonitorColorScheme colorSchemeId, Dictionary> colorConfig) { // get color scheme ID if (colorSchemeId == MonitorColorScheme.AutoDetect) diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index 573d9185..59edad26 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -28,6 +28,12 @@ internal class LogManager : IDisposable /// Create a monitor instance given the ID and name. private readonly Func GetMonitorImpl; + /// The console writer which sends color-coded text to the console. + private readonly ColorfulConsoleWriter ConsoleWriter; + + /// The monitors managed by SMAPI. + private readonly List Monitors = []; + /********* ** Accessors @@ -59,12 +65,9 @@ internal class LogManager : IDisposable this.LogFile = new LogFileManager(logPath); // init monitor - this.GetMonitorImpl = (id, name) => new Monitor(id, name, this.LogFile, colorSchemeId, colorConfig, verboseLogging.Contains("*") || verboseLogging.Contains(id), getScreenIdForLog) - { - WriteToConsole = writeToConsole, - ShowTraceInConsole = isDeveloperMode, - ShowFullStampInConsole = isDeveloperMode - }; + this.ConsoleWriter = new ColorfulConsoleWriter(Constants.Platform, colorSchemeId, colorConfig); + this.GetMonitorImpl = (id, name) => this.CreateAndRegisterMonitor(id, name, verboseLogging, getScreenIdForLog, writeToConsole, isDeveloperMode); + this.Monitor = this.GetMonitor("SMAPI", "SMAPI"); this.MonitorForGame = this.GetMonitor("game", "game"); @@ -91,6 +94,19 @@ internal class LogManager : IDisposable Console.Title = title; } + /// Apply the SMAPI settings to the log manager and its managed monitors. + /// The color scheme ID in to use, or to select one automatically. + /// The colors to use for text written to the SMAPI console. + /// The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting. + /// Whether to enable full console output for developers. + public void ApplySettings(MonitorColorScheme colorSchemeId, Dictionary> colorSchemes, HashSet verboseLogging, bool isDeveloperMode) + { + foreach (Monitor monitor in this.Monitors) + this.ApplySettings(monitor, verboseLogging, isDeveloperMode); + + this.ConsoleWriter.SetColors(colorSchemeId, colorSchemes); + } + /**** ** Console input ****/ @@ -333,6 +349,38 @@ internal class LogManager : IDisposable /********* ** Protected methods *********/ + /// Create and register a monitor instance. + /// The mod ID, if applicable. + /// The name of the module which logs messages using this instance. + /// The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting. + /// Get the screen ID that should be logged to distinguish between players in split-screen mode, if any. + /// Whether to write anything to the console. This should be disabled if no console is available. + /// Whether to enable full console output for developers. + private Monitor CreateAndRegisterMonitor(string modId, string source, HashSet verboseLogging, Func getScreenIdForLog, bool writeToConsole, bool isDeveloperMode) + { + Monitor monitor = new(modId, source, this.LogFile, this.ConsoleWriter, getScreenIdForLog) + { + WriteToConsole = writeToConsole + }; + + this.ApplySettings(monitor, verboseLogging, isDeveloperMode); + + this.Monitors.Add(monitor); + + return monitor; + } + + /// Apply the SMAPI settings to a managed monitor. + /// The monitor to update. + /// The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting. + /// Whether to enable full console output for developers. + private void ApplySettings(Monitor monitor, HashSet verboseLogging, bool isDeveloperMode) + { + monitor.IsVerbose = verboseLogging.Contains("*") || verboseLogging.Contains(monitor.ModId); + monitor.ShowTraceInConsole = isDeveloperMode; + monitor.ShowFullStampInConsole = isDeveloperMode; + } + /// Write a summary of mod warnings to the console and log. /// The loaded mods. /// The mods which could not be loaded. diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs index b8b80cee..febb71f5 100644 --- a/src/SMAPI/Framework/Models/SConfig.cs +++ b/src/SMAPI/Framework/Models/SConfig.cs @@ -15,20 +15,21 @@ internal class SConfig /// The default config values, for fields that should be logged if different. private static readonly IDictionary DefaultValues = new Dictionary { - [nameof(CheckForUpdates)] = true, - [nameof(CheckForBlacklistUpdates)] = true, - [nameof(CheckContentIntegrity)] = true, - [nameof(ListenForConsoleInput)] = true, - [nameof(ParanoidWarnings)] = Constants.IsDebugBuild, - [nameof(UseBetaChannel)] = Constants.ApiVersion.IsPrerelease(), - [nameof(GitHubProjectName)] = "Pathoschild/SMAPI", - [nameof(WebApiBaseUrl)] = "https://smapi.io/api/", - [nameof(LogNetworkTraffic)] = false, - [nameof(LogTechnicalDetailsForBrokenMods)] = false, - [nameof(RewriteMods)] = true, - [nameof(FixHarmony)] = true, - [nameof(UseCaseInsensitivePaths)] = Constants.Platform is Platform.Android or Platform.Linux, - [nameof(SuppressHarmonyDebugMode)] = true + [nameof(SConfig.CheckForUpdates)] = true, + [nameof(SConfig.CheckForBlacklistUpdates)] = true, + [nameof(SConfig.CheckContentIntegrity)] = true, + [nameof(SConfig.ListenForConsoleInput)] = true, + [nameof(SConfig.ParanoidWarnings)] = Constants.IsDebugBuild, + [nameof(SConfig.UseBetaChannel)] = Constants.ApiVersion.IsPrerelease(), + [nameof(SConfig.GitHubProjectName)] = "Pathoschild/SMAPI", + [nameof(SConfig.WebApiBaseUrl)] = "https://smapi.io/api/", + [nameof(SConfig.LogNetworkTraffic)] = false, + [nameof(SConfig.LogTechnicalDetailsForBrokenMods)] = false, + [nameof(SConfig.RewriteMods)] = true, + [nameof(SConfig.EnableConfigMenu)] = true, + [nameof(SConfig.FixHarmony)] = true, + [nameof(SConfig.UseCaseInsensitivePaths)] = Constants.Platform is Platform.Android or Platform.Linux, + [nameof(SConfig.SuppressHarmonyDebugMode)] = true }; /// The default values for , to log changes if different. @@ -46,7 +47,7 @@ internal class SConfig // Note: properties must be writable to support merging config.user.json into it. // - /// Whether to enable development features. + /// Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players. public bool DeveloperMode { get; set; } /// Whether to check for newer versions of SMAPI and mods on startup. @@ -61,7 +62,7 @@ internal class SConfig /// Whether SMAPI should listen for console input to support console commands. public bool ListenForConsoleInput { get; set; } - /// Whether to add a section to the 'mod issues' list for mods which which directly use potentially sensitive .NET APIs like file or shell access. + /// Whether to add a section to the 'mod issues' list for mods which directly use potentially sensitive .NET APIs like file or shell access. public bool ParanoidWarnings { get; set; } /// Whether to show beta versions as valid updates. @@ -83,6 +84,9 @@ internal class SConfig /// Whether SMAPI should rewrite mods for compatibility. public bool RewriteMods { get; set; } + /// Whether SMAPI should add an in-game config menu through Generic Mod Config Menu. + public bool EnableConfigMenu { get; set; } + /// Whether to apply fixes to Harmony so it works with Stardew Valley. public bool FixHarmony { get; set; } @@ -130,6 +134,7 @@ internal class SConfig /// /// /// + /// /// /// /// @@ -153,6 +158,7 @@ internal class SConfig string blacklistUrl, string[]? verboseLogging, bool? rewriteMods, + bool? enableConfigMenu, bool? fixHarmony, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, @@ -177,6 +183,7 @@ internal class SConfig this.BlacklistUrl = blacklistUrl; this.VerboseLogging = new HashSet(verboseLogging ?? [], StringComparer.OrdinalIgnoreCase); this.RewriteMods = rewriteMods ?? (bool)SConfig.DefaultValues[nameof(this.RewriteMods)]; + this.EnableConfigMenu = enableConfigMenu ?? (bool)SConfig.DefaultValues[nameof(this.EnableConfigMenu)]; this.FixHarmony = fixHarmony ?? (bool)SConfig.DefaultValues[nameof(this.FixHarmony)]; this.UseCaseInsensitivePaths = useCaseInsensitivePaths ?? (bool)SConfig.DefaultValues[nameof(this.UseCaseInsensitivePaths)]; this.LogNetworkTraffic = logNetworkTraffic ?? (bool)SConfig.DefaultValues[nameof(this.LogNetworkTraffic)]; diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs index 031500e2..8e13ab1d 100644 --- a/src/SMAPI/Framework/Monitor.cs +++ b/src/SMAPI/Framework/Monitor.cs @@ -13,9 +13,6 @@ internal class Monitor : IMonitor /********* ** Fields *********/ - /// The mod ID, if applicable. - private readonly string ModId; - /// The name of the module which logs messages using this instance. private readonly string Source; @@ -41,6 +38,9 @@ internal class Monitor : IMonitor /********* ** Accessors *********/ + /// The mod ID, if applicable. + public string ModId { get; } + /// Whether to log basic contextual info (like buttons pressed and menus opened) even if is disabled. public static bool ForceLogContext { get; set; } @@ -54,7 +54,11 @@ internal class Monitor : IMonitor public static LogLevel ContextLogLevel => Monitor.ForceLogContext ? LogLevel.Info : LogLevel.Trace; /// - public bool IsVerbose => field || Monitor.ForceVerboseLoggingForAll || Monitor.ForceVerboseLogging.Contains(this.ModId); + public bool IsVerbose + { + get => field || Monitor.ForceVerboseLoggingForAll || Monitor.ForceVerboseLogging.Contains(this.ModId); + set => field = value; + } /// Whether to show the full log stamps (with time/level/logger) in the console. If false, shows a simplified stamp with only the logger. internal bool ShowFullStampInConsole { get; set; } @@ -73,11 +77,9 @@ internal class Monitor : IMonitor /// The mod ID, if applicable. /// The name of the module which logs messages using this instance. /// The log file to which to write messages. - /// The color scheme ID in to use, or to select one automatically. - /// The colors to use for text written to the SMAPI console. - /// Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed. + /// Handles writing text to the console. /// Get the screen ID that should be logged to distinguish between players in split-screen mode, if any. - public Monitor(string modId, string source, LogFileManager logFile, MonitorColorScheme colorSchemeId, Dictionary> colorConfig, bool isVerbose, Func getScreenIdForLog) + public Monitor(string modId, string source, LogFileManager logFile, IConsoleWriter consoleWriter, Func getScreenIdForLog) { // validate if (string.IsNullOrWhiteSpace(source)) @@ -87,8 +89,7 @@ internal class Monitor : IMonitor this.ModId = modId; this.Source = source; this.LogFile = logFile ?? throw new ArgumentNullException(nameof(logFile), "The log file manager cannot be null."); - this.ConsoleWriter = new ColorfulConsoleWriter(Constants.Platform, colorSchemeId, colorConfig); - this.IsVerbose = isVerbose; + this.ConsoleWriter = consoleWriter ?? throw new ArgumentNullException(nameof(consoleWriter), "The console writer cannot be null."); this.GetScreenIdForLog = getScreenIdForLog; } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 517c6a51..036ec76c 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -37,6 +37,7 @@ using StardewModdingAPI.Framework.Rendering; using StardewModdingAPI.Framework.Serialization; using StardewModdingAPI.Framework.StateTracking.Snapshots; using StardewModdingAPI.Framework.Utilities; +using StardewModdingAPI.Integrations.GenericModConfigMenu; using StardewModdingAPI.Internal; using StardewModdingAPI.Toolkit; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; @@ -85,12 +86,12 @@ internal class SCore : IDisposable /// Encapsulates access to SMAPI core translations. private readonly Translator Translator = new(); - /// The SMAPI configuration settings. - private readonly SConfig Settings; - /// The mod toolkit used for generic mod interactions. private readonly ModToolkit Toolkit = new(); + /// The SMAPI configuration settings. + private SConfig Settings; + /**** ** Higher-level components ****/ @@ -113,13 +114,15 @@ internal class SCore : IDisposable /// Manages SMAPI events for mods. private readonly EventManager EventManager; - /**** ** State ****/ /// The path to search for mods. private string ModsPath => Constants.ModsPath; + /// The override to apply for , or null to use the value from the settings file. + private readonly bool? OverrideDeveloperMode; + /// Whether the game is currently running. private bool IsGameRunning; @@ -178,8 +181,8 @@ internal class SCore : IDisposable /// Construct an instance. /// The path to search for mods. /// Whether to output log messages to the console. - /// Whether to enable development features, or null to use the value from the settings file. - public SCore(string modsPath, bool writeToConsole, bool? developerMode) + /// The override to apply for , or null to use the value from the settings file. + public SCore(string modsPath, bool writeToConsole, bool? overrideDeveloperMode) { SCore.Instance = this; @@ -193,17 +196,8 @@ internal class SCore : IDisposable string logPath = this.GetLogPath(); // init settings - { - var deserializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; - - this.Settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)) ?? throw new InvalidOperationException("The 'smapi-internal/config.json' file is missing or invalid. You can reinstall SMAPI to fix this."); - if (File.Exists(Constants.ApiUserConfigPath)) - JsonConvert.PopulateObject(File.ReadAllText(Constants.ApiUserConfigPath), this.Settings, deserializerSettings); - if (File.Exists(Constants.ApiModGroupConfigPath)) - JsonConvert.PopulateObject(File.ReadAllText(Constants.ApiModGroupConfigPath), this.Settings, deserializerSettings); - if (developerMode.HasValue) - this.Settings.OverrideDeveloperMode(developerMode.Value); - } + this.OverrideDeveloperMode = overrideDeveloperMode; + this.ReloadSettings(); // init basics this.LogManager = new LogManager(logPath: logPath, colorSchemeId: this.Settings.ConsoleColorScheme, colorConfig: this.Settings.ConsoleColorSchemes, writeToConsole: writeToConsole, verboseLogging: this.Settings.VerboseLogging, isDeveloperMode: this.Settings.DeveloperMode, getScreenIdForLog: this.GetScreenIdForLog); @@ -521,6 +515,10 @@ internal class SCore : IDisposable // check for updates _ = this.CheckForUpdatesAsync(mods); // ignore task since the main thread doesn't need to wait for it + + // register config menu with Generic Mod Config Menu + if (this.Settings.EnableConfigMenu) + new GenericModConfigMenuIntegration(this.Monitor, this.Translator, () => this.Settings, this.ReloadSettings).Register(this.ModRegistry); } // update window titles @@ -1406,6 +1404,40 @@ internal class SCore : IDisposable this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames, assetNames.Select(p => p.GetBaseAssetName()))); } + /// Reload the SMAPI settings. + /// The intern + [MemberNotNull(nameof(SCore.Settings))] + private void ReloadSettings() + { + JsonSerializerSettings deserializerSettings = new() { NullValueHandling = NullValueHandling.Ignore }; + + // load default settings + SConfig? settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)); + if (settings is null) + { + if (this.Settings is null) + throw new InvalidOperationException("The 'smapi-internal/config.json' file is missing or invalid. You can reinstall SMAPI to fix this."); + + this.Monitor.Log("Could not load updated settings from the 'smapi-internal/config.json' file. Keeping the settings as-is.", LogLevel.Warn); + return; + } + + // load user settings + if (File.Exists(Constants.ApiUserConfigPath)) + JsonConvert.PopulateObject(File.ReadAllText(Constants.ApiUserConfigPath), settings, deserializerSettings); + if (File.Exists(Constants.ApiModGroupConfigPath)) + JsonConvert.PopulateObject(File.ReadAllText(Constants.ApiModGroupConfigPath), settings, deserializerSettings); + if (this.OverrideDeveloperMode.HasValue) + settings.OverrideDeveloperMode(this.OverrideDeveloperMode.Value); + + // apply + this.Settings = settings; + + // update log manager if already initialized + // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract + this.LogManager?.ApplySettings(settings.ConsoleColorScheme, settings.ConsoleColorSchemes, settings.VerboseLogging, this.OverrideDeveloperMode ?? settings.DeveloperMode); + } + /// Get the load/edit operations to apply to an asset by querying registered event handlers. /// The asset info being requested. private AssetOperationGroup? RequestAssetOperations(IAssetInfo asset) diff --git a/src/SMAPI/Integrations/GenericModConfigMenu/GenericModConfigMenuIntegration.cs b/src/SMAPI/Integrations/GenericModConfigMenu/GenericModConfigMenuIntegration.cs new file mode 100644 index 00000000..1c257d5c --- /dev/null +++ b/src/SMAPI/Integrations/GenericModConfigMenu/GenericModConfigMenuIntegration.cs @@ -0,0 +1,343 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Models; +using StardewModdingAPI.Framework.ModHelpers; +using StardewModdingAPI.Framework.ModLoading; +using StardewModdingAPI.Framework.Reflection; +using StardewModdingAPI.Internal.ConsoleWriting; +using StardewModdingAPI.Toolkit.Serialization.Models; +using StardewModdingAPI.Toolkit.Utilities; +using StardewValley.Extensions; + +namespace StardewModdingAPI.Integrations.GenericModConfigMenu +{ + /// Registers the SMAPI configuration with Generic Mod Config Menu. + internal class GenericModConfigMenuIntegration + { + /********* + ** Fields + *********/ + /// The unique mod ID for Generic Mod Config Menu. + private const string GenericModConfigMenuModId = "spacechase0.GenericModConfigMenu"; + + /// The minimum version of Generic Mod Config Menu supported by this integration. + private const string MinVersion = "1.9.6"; + + /// Encapsulates monitoring and logging. + private readonly IMonitor Monitor; + + /// The core SMAPI translations. + private readonly Translator Translations; + + /// Get the current SMAPI settings. + private readonly Func GetConfig; + + /// Reload the SMAPI settings when they're changed through the config menu. + private readonly Action ReloadConfig; + + /// The custom setting values which haven't been saved yet. + private readonly Dictionary StagedSettings = []; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// + /// + /// + /// + public GenericModConfigMenuIntegration(IMonitor monitor, Translator translations, Func getConfig, Action reloadConfig) + { + this.Monitor = monitor; + this.Translations = translations; + this.GetConfig = getConfig; + this.ReloadConfig = reloadConfig; + } + + /// Register the mod config. + /// An API for fetching metadata about loaded mods. + public void Register(ModRegistry internalModRegistry) + { + try + { + // init + ModMetadata smapiMod = this.CreateFakeMod(); + IModRegistry modRegistry = this.CreateModRegistryHelper(internalModRegistry, this.Monitor, smapiMod); + IGenericModConfigMenuApi? api = this.GetGenericModConfigMenuApi(modRegistry, this.Monitor); + if (api is null) + return; + + // register config + api.Register(smapiMod.Manifest, this.OnReset, this.Save); + var getConfig = this.GetConfig; + + // add 'SMAPI features' section + api.AddSectionTitle(smapiMod.Manifest, () => this.Translations.Get("config.section.features")); + api.AddBoolOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.check-for-updates.name"), + tooltip: () => this.Translations.Get("config.check-for-updates.desc"), + getValue: () => getConfig().CheckForUpdates, + setValue: value => this.StageOption(nameof(SConfig.CheckForUpdates), value) + ); + api.AddBoolOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.check-content-integrity.name"), + tooltip: () => this.Translations.Get("config.check-content-integrity.desc"), + getValue: () => getConfig().CheckContentIntegrity, + setValue: value => this.StageOption(nameof(SConfig.CheckContentIntegrity), value) + ); + api.AddBoolOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.read-console-input.name"), + tooltip: () => this.Translations.Get("config.read-console-input.desc"), + getValue: () => getConfig().ListenForConsoleInput, + setValue: value => this.StageOption(nameof(SConfig.ListenForConsoleInput), value) + ); + + // add 'console window' section + api.AddSectionTitle(smapiMod.Manifest, () => this.Translations.Get("config.section.console-window")); + api.AddBoolOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.developer-mode.name"), + tooltip: () => this.Translations.Get("config.developer-mode.desc"), + getValue: () => getConfig().DeveloperMode, + setValue: value => this.StageOption(nameof(SConfig.DeveloperMode), value) + ); + api.AddTextOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.color-scheme.name"), + tooltip: () => this.Translations.Get("config.color-scheme.desc"), + getValue: () => getConfig().ConsoleColorScheme.ToString(), + setValue: value => this.StageOption(nameof(SConfig.ConsoleColorScheme), value), + allowedValues: Enum.GetValues().Select(p => p.ToString()).ToArray(), + formatAllowedValue: value => this.Translations.Get( + value is nameof(MonitorColorScheme.AutoDetect) && Constants.Platform == Platform.Windows + ? $"config.color-scheme.options.{value}.on-windows" + : $"config.color-scheme.options.{value}" + )); + + // add 'verbose logging' section + api.AddSectionTitle(smapiMod.Manifest, () => this.Translations.Get("config.section.verbose-logs")); + api.AddParagraph(smapiMod.Manifest, () => this.Translations.Get("config.section.verbose-logs.explanation")); + api.AddTextOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.enable-for.name"), + tooltip: () => this.Translations.Get("config.enable-for.desc"), + getValue: () => getConfig().VerboseLogging.Contains("*").ToString(), + setValue: value => this.StageVerboseLoggingOption("*", value == bool.TrueString), + allowedValues: [bool.TrueString, bool.FalseString], + formatAllowedValue: value => this.Translations.Get($"config.enable-for.options.{(value == bool.TrueString ? "all" : "selected")}") + ); + api.AddBoolOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.enable-for-smapi.name"), + tooltip: () => this.Translations.Get("config.enable-for-smapi.desc"), + getValue: () => getConfig().VerboseLogging.Contains("SMAPI"), + setValue: value => this.StageVerboseLoggingOption("SMAPI", value) + ); + foreach (IModInfo mod in modRegistry.GetAll().OrderBy(p => p.Manifest.Name, StringComparer.OrdinalIgnoreCase)) + { + if (mod.IsContentPack) + continue; + + api.AddBoolOption( + mod: smapiMod.Manifest, + name: () => this.Translations.Get("config.enable-for-mod.name", new { modName = mod.Manifest.Name }), + tooltip: () => this.Translations.Get("config.enable-for-mod.desc", new { modName = mod.Manifest.Name }), + getValue: () => getConfig().VerboseLogging.Contains(mod.Manifest.UniqueID), + setValue: value => this.StageVerboseLoggingOption(mod.Manifest.UniqueID, value) + ); + } + } + catch (Exception ex) + { + this.Monitor.Log("Couldn't register the SMAPI settings with Generic Mod Config Menu. This has no effect on SMAPI, but the in-game config menu won't be available.", LogLevel.Warn); + this.Monitor.Log(ex.ToString()); + } + } + + + /********* + ** Private methods + *********/ + /**** + ** Save events + ****/ + /// Reset the SMAPI configuration to the default values. + private void OnReset() + { + try + { + foreach (string customPath in new[] { Constants.ApiUserConfigPath, Constants.ApiModGroupConfigPath }) + { + if (File.Exists(customPath)) + File.Delete(customPath); + } + + this.ReloadConfig(); + } + catch (Exception ex) + { + this.Monitor.Log($"Couldn't reset SMAPI to the default settings from the Generic Mod Config Menu UI.\n\nTechnical details:\n{ex}", LogLevel.Error); + } + } + + /// Save the mod configuration. + private void Save() + { + try + { + // load settings + JsonSettingsWrapper? defaultSettings = JsonSettingsWrapper.TryLoadFile(Constants.ApiConfigPath, this.Monitor); + JsonSettingsWrapper? userSettings = JsonSettingsWrapper.TryLoadFile(Constants.ApiUserConfigPath, this.Monitor); + JsonSettingsWrapper? modGroupSettings = JsonSettingsWrapper.TryLoadFile(Constants.ApiModGroupConfigPath, this.Monitor); + + // skip if invalid + if (defaultSettings is null) + { + this.Monitor.Log("Couldn't save the SMAPI settings from the Generic Mod Config Menu UI. The default settings could not be loaded.", LogLevel.Error); + return; + } + + // apply edited options + if (this.StagedSettings.Count > 0) + { + // create settings file if needed + if (userSettings is null && modGroupSettings is null) + userSettings = new JsonSettingsWrapper(Constants.ApiUserConfigPath); + + // save simple options + foreach ((string fieldName, object newValue) in this.StagedSettings) + { + // replace existing property if possible + if (modGroupSettings?.SetUserOption(fieldName, newValue, defaultSettings, overwriteOnly: true) is true) + continue; + if (userSettings?.SetUserOption(fieldName, newValue, defaultSettings, overwriteOnly: true) is true) + continue; + + // else add new property + (modGroupSettings ?? userSettings)?.SetUserOption(fieldName, newValue, defaultSettings, overwriteOnly: false); + } + } + + // save to files + if (modGroupSettings?.Changed is true) + modGroupSettings.SaveOrDeleteFile(); + if (userSettings?.Changed is true) + userSettings.SaveOrDeleteFile(); + + // reload settings + this.StagedSettings.Clear(); + this.ReloadConfig(); + } + catch (Exception ex) + { + this.Monitor.Log($"Couldn't save the SMAPI settings from the Generic Mod Config Menu UI.\n\nTechnical details:\n{ex}", LogLevel.Error); + } + } + + /**** + ** Config option staging + ****/ + /// Store a value set by the player before it's saved to the config files. + /// The name of the field to change. + /// The value to stage. + private void StageOption(string fieldName, object value) + { + this.StagedSettings[fieldName] = value; + } + + /// Store a custom option before it's saved to the config files. + /// The mod ID, 'SMAPI', or '*' (all) for which to toggle verbose logging. + /// Whether verbose logging is enabled. + private void StageVerboseLoggingOption(string id, bool enable) + { + const string fieldName = nameof(SConfig.VerboseLogging); + + if (this.StagedSettings.TryGetValue(fieldName, out object? rawValue) && rawValue is HashSet set) + set.Toggle(id, enable); + else + { + HashSet newSet = new HashSet(this.GetConfig().VerboseLogging, StringComparer.OrdinalIgnoreCase); + newSet.Toggle(id, enable); + this.StagedSettings[fieldName] = newSet; + } + } + + /**** + ** Initialization + ****/ + /// Get the public API for Generic Mod Config Menu, if it's available. + /// The mod registry to query. + /// The monitor with which to log issues loading the API. + /// Returns the API if it's available, else null. + private IGenericModConfigMenuApi? GetGenericModConfigMenuApi(IModRegistry modRegistry, IMonitor monitor) + { + // check mod is installed + if (modRegistry.Get(GenericModConfigMenuIntegration.GenericModConfigMenuModId) is not { } configMod) + return null; + + // check min version + if (configMod.Manifest.Version.IsOlderThan(GenericModConfigMenuIntegration.MinVersion)) + { + monitor.Log("Can't register the SMAPI settings with Generic Mod Config Menu, because you need version 1.9.6 or later of that mod.", LogLevel.Debug); + return null; + } + + // check API + if (modRegistry.GetApi(GenericModConfigMenuIntegration.GenericModConfigMenuModId) is not { } api) + { + monitor.Log("Can't register the SMAPI settings with Generic Mod Config Menu, because it unexpectedly had no API.", LogLevel.Debug); + return null; + } + + return api; + } + + /// Create a fake mod entry to represent SMAPI itself. + private ModMetadata CreateFakeMod() + { + IManifest manifest = new Manifest( + uniqueId: "SMAPI", + name: "SMAPI", + author: "Pathoschild", + description: string.Empty, + version: Constants.ApiVersion, + minimumApiVersion: Constants.ApiVersion, + minimumGameVersion: Constants.MinimumGameVersion, + entryDll: "StardewModdingAPI.exe", + contentPackFor: null, + dependencies: [], + updateKeys: [] + ); + + return new ModMetadata( + displayName: "SMAPI", + directoryPath: Constants.GamePath, + rootPath: Constants.ModsPath, + manifest: manifest, + dataRecord: null, + isIgnored: false + ); + } + + /// Create a mod registry helper. + /// The internal mod registry. + /// Encapsulates monitoring and logging. + /// The fake mod entry representing SMAPI itself. + private IModRegistry CreateModRegistryHelper(ModRegistry internalRegistry, IMonitor monitor, ModMetadata smapiMod) + { + return new ModRegistryHelper( + mod: smapiMod, + registry: internalRegistry, + proxyFactory: new InterfaceProxyFactory(), + monitor: monitor + ); + } + } +} diff --git a/src/SMAPI/Integrations/GenericModConfigMenu/IGenericModConfigMenuApi.cs b/src/SMAPI/Integrations/GenericModConfigMenu/IGenericModConfigMenuApi.cs new file mode 100644 index 00000000..ff6e9ddc --- /dev/null +++ b/src/SMAPI/Integrations/GenericModConfigMenu/IGenericModConfigMenuApi.cs @@ -0,0 +1,55 @@ +using System; + +namespace StardewModdingAPI.Integrations.GenericModConfigMenu; + +/// The API provided by the Generic Mod Config Menu mod. +public interface IGenericModConfigMenuApi +{ + /********* + ** Methods + *********/ + /**** + ** Must be called first + ****/ + /// Register a mod whose config can be edited through the UI. + /// The mod's manifest. + /// Reset the mod's config to its default values. + /// Save the mod's current config to the config.json file. + /// Whether the options can only be edited from the title screen. + void Register(IManifest mod, Action reset, Action save, bool titleScreenOnly = false); + + + /**** + ** Basic options + ****/ + /// Add a section title at the current position in the form. + /// The mod's manifest. + /// The title text shown in the form. + /// The tooltip text shown when the cursor hovers on the title, or null to disable the tooltip. + void AddSectionTitle(IManifest mod, Func text, Func? tooltip = null); + + /// Add a paragraph of text at the current position in the form. + /// The mod's manifest. + /// The paragraph text to display. + void AddParagraph(IManifest mod, Func text); + + /// Add a boolean option at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The unique field ID for use with OnFieldChanged, or null to auto-generate a randomized ID. + void AddBoolOption(IManifest mod, Func getValue, Action setValue, Func name, Func? tooltip = null, string? fieldId = null); + + /// Add a string option at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The values that can be selected, or null to allow any. + /// Get the display text to show for a value from , or null to show the values as-is. + /// The unique field ID for use with OnFieldChanged, or null to auto-generate a randomized ID. + void AddTextOption(IManifest mod, Func getValue, Action setValue, Func name, Func? tooltip = null, string[]? allowedValues = null, Func? formatAllowedValue = null, string? fieldId = null); +} diff --git a/src/SMAPI/Integrations/GenericModConfigMenu/JsonSettingsWrapper.cs b/src/SMAPI/Integrations/GenericModConfigMenu/JsonSettingsWrapper.cs new file mode 100644 index 00000000..f2302c55 --- /dev/null +++ b/src/SMAPI/Integrations/GenericModConfigMenu/JsonSettingsWrapper.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StardewModdingAPI.Framework.Models; + +namespace StardewModdingAPI.Integrations.GenericModConfigMenu; + +/// Wraps access to a JSON file which stores fields. +internal class JsonSettingsWrapper +{ + /********* + ** Accessors + *********/ + /// The full path to the underlying file. + public string FilePath { get; } + + /// The loaded option properties. + public Dictionary Properties { get; } + + /// Whether the saved values have changed. + public bool Changed { get; private set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// + public JsonSettingsWrapper(string filePath) + : this(filePath, new Dictionary(StringComparer.OrdinalIgnoreCase)) { } + + /// Set the value for a user settings field. + /// The name of the field to set. + /// The new value to set. + /// The default settings. + /// Whether to only set the value if it's already set in this file. + /// Returns whether the value changed. + public bool SetUserOption(string fieldName, object newValue, JsonSettingsWrapper defaultSettings, bool overwriteOnly) + { + return fieldName == nameof(SConfig.VerboseLogging) + ? this.SetVerboseLoggingUserOption(fieldName, (HashSet)newValue, defaultSettings, overwriteOnly) + : this.SetScalarUserOption(fieldName, newValue, defaultSettings, overwriteOnly); + } + + /// Remove a user settings field. + /// The name of the field to set. + /// Returns whether the value was found and removed. + public bool RemoveUserOption(string fieldName) + { + bool changed = this.Properties.Remove(fieldName); + this.Changed = this.Changed || changed; + return changed; + } + + /// Save the user settings to the file on disk, or delete the files if it's empty. + public void SaveOrDeleteFile() + { + if (this.Properties.Count == 0) + File.Delete(Constants.ApiUserConfigPath); + else + { + string json = JsonConvert.SerializeObject(this.Properties, Formatting.Indented); + File.WriteAllText(this.FilePath, json, Encoding.UTF8); + } + + this.Changed = false; + } + + /// Read a raw JSON settings file. + /// The full path for the file to read. + /// The monitor with which to log errors. + /// Returns the loaded JSON wrapper, or null if the file doesn't exist or couldn't be loaded. + public static JsonSettingsWrapper? TryLoadFile(string path, IMonitor monitor) + { + try + { + if (!File.Exists(path)) + return null; + + string rawJson = File.ReadAllText(path); + JObject? json = JsonConvert.DeserializeObject(rawJson); + if (json is null) + return null; + + Dictionary properties = new(StringComparer.OrdinalIgnoreCase); + foreach (JProperty property in json.Properties()) + properties[property.Name] = property.Value; + + return new JsonSettingsWrapper(path, properties); + } + catch (Exception ex) + { + monitor.Log($"Can't read settings file at {path}.\n\nTechnical details:\n{ex}", LogLevel.Error); + return null; + } + } + + + /********* + ** Private methods + *********/ + /// Construct an instance. + /// + /// + private JsonSettingsWrapper(string filePath, Dictionary properties) + { + this.FilePath = filePath; + this.Properties = properties; + } + + /// Set the value for a scalar user settings field. + /// The name of the field to set. + /// The new value to set. + /// The default settings. + /// Whether to only set the value if it's already set in this file. + /// Returns whether the value changed. + public bool SetScalarUserOption(string fieldName, object newValue, JsonSettingsWrapper defaultSettings, bool overwriteOnly) + { + // remove default + if (defaultSettings.Properties.TryGetValue(fieldName, out JToken? rawDefaultValue) && this.TokenHasValue(rawDefaultValue, newValue)) + return this.RemoveUserOption(fieldName); + + // else overwrite value + if (this.Properties.TryGetValue(fieldName, out JToken? oldValue)) + { + if (this.TokenHasValue(oldValue, newValue)) + return false; + + this.Properties[fieldName] = JToken.FromObject(newValue); + this.Changed = true; + return true; + } + + // else add value + if (!overwriteOnly) + { + this.Properties[fieldName] = JToken.FromObject(newValue); + this.Changed = true; + return true; + } + + return false; + } + + /// Set the value for the verbose logging field. + /// The name of the field to set. + /// The new value to set. + /// The default settings. + /// Whether to only set the value if it's already set in this file. + /// Returns whether the value changed. + public bool SetVerboseLoggingUserOption(string fieldName, HashSet newValue, JsonSettingsWrapper defaultSettings, bool overwriteOnly) + { + // remove default + if (newValue.Count == 0) + return this.RemoveUserOption(fieldName); + + // else overwrite value + if (this.Properties.GetValueOrDefault(fieldName)?.ToObject>() is { } oldSet) + { + oldSet = new HashSet(oldSet, StringComparer.OrdinalIgnoreCase); + + bool changed = newValue.Count != oldSet.Count || !oldSet.All(newValue.Contains); + if (changed) + { + this.Properties[fieldName] = JToken.FromObject(newValue); + this.Changed = true; + } + + return changed; + } + + // else add value + if (!overwriteOnly) + { + this.Properties[fieldName] = JToken.FromObject(newValue); + this.Changed = true; + return true; + } + + return false; + } + + /// Get whether a token has the expected value. + /// The token whose value to check. + /// The value to match. + private bool TokenHasValue(JToken token, object expectedValue) + { + object? actualValue = token.Value()?.Value; + return expectedValue.Equals(actualValue); + } +} diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json index 8b92f354..3b999ad0 100644 --- a/src/SMAPI/SMAPI.config.json +++ b/src/SMAPI/SMAPI.config.json @@ -45,8 +45,9 @@ in future SMAPI versions. "CheckContentIntegrity": true, /** - * Whether to enable features intended for mod developers. Currently this only makes TRACE-level - * messages appear in the console. + * Whether to show much more info in the SMAPI consoler window, intended for mod developers. + * + * Not recommended for most players. */ "DeveloperMode": true, diff --git a/src/SMAPI/i18n/de.json b/src/SMAPI/i18n/de.json index 595c3eff..b417c337 100644 --- a/src/SMAPI/i18n/de.json +++ b/src/SMAPI/i18n/de.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", - "generic.date-with-year": "{{season}} {{day}} im Jahr {{year}}" + "generic.date-with-year": "{{season}} {{day}} im Jahr {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/default.json b/src/SMAPI/i18n/default.json index 7e1f9c4d..356fee76 100644 --- a/src/SMAPI/i18n/default.json +++ b/src/SMAPI/i18n/default.json @@ -1,6 +1,54 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", - "generic.date-with-year": "{{season}} {{day}} in year {{year}}" + "generic.date-with-year": "{{season}} {{day}} in year {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/es.json b/src/SMAPI/i18n/es.json index 76228d7d..0aa48179 100644 --- a/src/SMAPI/i18n/es.json +++ b/src/SMAPI/i18n/es.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{seasonLowercase}} {{day}}", - "generic.date-with-year": "{{seasonLowercase}} {{day}} del año {{year}}" + "generic.date-with-year": "{{seasonLowercase}} {{day}} del año {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/fr.json b/src/SMAPI/i18n/fr.json index e32ee712..7fa876e4 100644 --- a/src/SMAPI/i18n/fr.json +++ b/src/SMAPI/i18n/fr.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // 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}} de l'année {{year}}" + "generic.date-with-year": "{{day}} {{seasonLowercase}} de l'année {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/hu.json b/src/SMAPI/i18n/hu.json index 2e3b7264..cc1f8544 100644 --- a/src/SMAPI/i18n/hu.json +++ b/src/SMAPI/i18n/hu.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", - "generic.date-with-year": "{{year}}. év {{season}} {{day}}" + "generic.date-with-year": "{{year}}. év {{season}} {{day}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/id.json b/src/SMAPI/i18n/id.json index 4616e7c0..18a78da2 100644 --- a/src/SMAPI/i18n/id.json +++ b/src/SMAPI/i18n/id.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "Tanggal {{day}} di {{season}}", - "generic.date-with-year": "Tanggal {{day}} di {{season}} tahun ke-{{year}}" + "generic.date-with-year": "Tanggal {{day}} di {{season}} tahun ke-{{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/it.json b/src/SMAPI/i18n/it.json index 7ada11f0..24b61771 100644 --- a/src/SMAPI/i18n/it.json +++ b/src/SMAPI/i18n/it.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{day}} {{season}}", - "generic.date-with-year": "{{day}} {{season}} dell'anno {{year}}" + "generic.date-with-year": "{{day}} {{season}} dell'anno {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/ja.json b/src/SMAPI/i18n/ja.json index c95ac1b1..bf675425 100644 --- a/src/SMAPI/i18n/ja.json +++ b/src/SMAPI/i18n/ja.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}日", - "generic.date-with-year": "{{year}}年目 {{season}} {{day}}日" + "generic.date-with-year": "{{year}}年目 {{season}} {{day}}日", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/ko.json b/src/SMAPI/i18n/ko.json index 8122a9e2..c590ba97 100644 --- a/src/SMAPI/i18n/ko.json +++ b/src/SMAPI/i18n/ko.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}일", - "generic.date-with-year": "{{year}}년차 {{season}} {{day}}일" + "generic.date-with-year": "{{year}}년차 {{season}} {{day}}일", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/pl.json b/src/SMAPI/i18n/pl.json index fa4650a2..c8ade848 100644 --- a/src/SMAPI/i18n/pl.json +++ b/src/SMAPI/i18n/pl.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // 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}}" + "generic.date-with-year": "{{day}} {{seasonLowercase}} w roku {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/pt.json b/src/SMAPI/i18n/pt.json index 7a08b08f..dff8e0c7 100644 --- a/src/SMAPI/i18n/pt.json +++ b/src/SMAPI/i18n/pt.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}} {{day}}", - "generic.date-with-year": "{{season}} {{day}} no ano {{year}}" + "generic.date-with-year": "{{season}} {{day}} no ano {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/ru.json b/src/SMAPI/i18n/ru.json index b8ff55c4..1ac64673 100644 --- a/src/SMAPI/i18n/ru.json +++ b/src/SMAPI/i18n/ru.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}}, {{day}}-е число", - "generic.date-with-year": "{{season}}, {{day}}-е число, {{year}}-й год" + "generic.date-with-year": "{{season}}, {{day}}-е число, {{year}}-й год", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/th.json b/src/SMAPI/i18n/th.json index 361b7aa7..0c87c6cd 100644 --- a/src/SMAPI/i18n/th.json +++ b/src/SMAPI/i18n/th.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "วันที่ {{day}} {{season}}", - "generic.date-with-year": "วันที่ {{day}} {{season}} ปีที่ {{year}}" + "generic.date-with-year": "วันที่ {{day}} {{season}} ปีที่ {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/tr.json b/src/SMAPI/i18n/tr.json index ba0c33c9..99f6ea7f 100644 --- a/src/SMAPI/i18n/tr.json +++ b/src/SMAPI/i18n/tr.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{day}} {{season}}", - "generic.date-with-year": "{{day}} {{season}} Yıl {{year}}" + "generic.date-with-year": "{{day}} {{season}} Yıl {{year}}", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/uk.json b/src/SMAPI/i18n/uk.json index a4286363..bc98490c 100644 --- a/src/SMAPI/i18n/uk.json +++ b/src/SMAPI/i18n/uk.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}}, {{day}}-й день", - "generic.date-with-year": "{{season}}, {{day}}-й день, {{year}} рік" + "generic.date-with-year": "{{season}}, {{day}}-й день, {{year}} рік", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } diff --git a/src/SMAPI/i18n/zh.json b/src/SMAPI/i18n/zh.json index 36d459de..49c3f392 100644 --- a/src/SMAPI/i18n/zh.json +++ b/src/SMAPI/i18n/zh.json @@ -1,6 +1,55 @@ { + /********* + ** Date formatting + *********/ // short date format for SDate // tokens: {{day}} (like 15), {{season}} (like Spring), {{seasonLowercase}} (like spring), {{year}} (like 2) "generic.date": "{{season}}{{day}}日", - "generic.date-with-year": "第{{year}}年{{season}}{{day}}日" + "generic.date-with-year": "第{{year}}年{{season}}{{day}}日", + + + /********* + ** Generic Mod Config Menu UI + *********/ + // TODO + // 'SMAPI features' section + "config.section.features": "SMAPI features", + + "config.check-for-updates.name": "Check for updates", + "config.check-for-updates.desc": "Whether to automatically check for newer versions of SMAPI and mods when you load the game. If new versions are available, an alert will be shown in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background.", + + "config.check-content-integrity.name": "Check content integrity", + "config.check-content-integrity.desc": "Whether SMAPI should check whether the game's content files are present and unmodified. This should nearly always be enabled. If disabled, SMAPI will log a warning for visibility when someone helps you troubleshoot game issues.", + + "config.read-console-input.name": "Read console input", + "config.read-console-input.desc": "Whether SMAPI should listen for console input. Disabling this will prevent you from using console commands. On some specific Linux systems, disabling this may reduce CPU usage.", + + // 'Console window' section + "config.section.console-window": "Console window", + + "config.developer-mode.name": "Developer mode", + "config.developer-mode.desc": "Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.", + + "config.color-scheme.name": "Color scheme", + "config.color-scheme.desc": "The color scheme to apply to text in the SMAPI console window. This has no effect on the game, mods, or SMAPI log file.", + "config.color-scheme.options.AutoDetect": "Auto-detect", + "config.color-scheme.options.AutoDetect.on-windows": "Auto-detect (recommended)", + "config.color-scheme.options.DarkBackground": "Lighter text (for dark backgrounds)", + "config.color-scheme.options.LightBackground": "Darker text (for light backgrounds)", + "config.color-scheme.options.None": "No colors", + + // 'Verbose logging' section + "config.section.verbose-logs": "Verbose logs", + "config.section.verbose-logs.explanation": "To help troubleshoot, SMAPI and many mods can send extra info to the SMAPI log (called 'verbose logging'). This may cause performance issues, and should usually be disabled.", + + "config.enable-for.name": "Enable for", + "config.enable-for.desc": "Choose which mods have verbose logging enabled.", + "config.enable-for.options.all": "All (not recommended)", + "config.enable-for.options.selected": "Mods selected below", + + "config.enable-for-smapi.name": "SMAPI", + "config.enable-for-smapi.desc": "Whether to enable verbose logs for SMAPI itself.", + + "config.enable-for-mod.name": "{{modName}}", + "config.enable-for-mod.desc": "Whether to enable verbose logs for the '{{modName}}' mod." } From 08826b7c40eb056086e8ab1299e1fb55fcdc810f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 21/26] remove separate "SMAPI for developers' version --- .github/workflows/build-smapi.yml | 17 +---- build/scripts/finalize-install-package.sh | 69 ++++++++++---------- build/scripts/prepare-install-package.ps1 | 21 ++---- docs/release-notes.md | 3 + src/SMAPI.Web/Controllers/IndexController.cs | 16 ++--- src/SMAPI.Web/Views/Index/Index.cshtml | 1 - src/SMAPI/SMAPI.config.json | 2 +- 7 files changed, 51 insertions(+), 78 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 828e4fef..18b8d46d 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -87,10 +87,8 @@ jobs: Write-Host "Building SMAPI $env:VERSION." ./build/scripts/prepare-install-package.ps1 "$env:VERSION" - - name: Rename build zips - run: | - mv "bin/SMAPI ${{env.VERSION}} installer.zip" "bin/SMAPI-${{env.VERSION}}-installer.zip" - mv "bin/SMAPI ${{env.VERSION}} installer for developers.zip" "bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip" + - name: Rename build zip + run: mv "bin/SMAPI ${{env.VERSION}} installer.zip" "bin/SMAPI-${{env.VERSION}}-installer.zip" - name: Upload installer uses: actions/upload-artifact@v4 @@ -99,13 +97,6 @@ jobs: path: 'bin/SMAPI-${{env.VERSION}}-installer.zip' if-no-files-found: 'error' - - name: Upload installer for developers - uses: actions/upload-artifact@v4 - with: - name: 'SMAPI ${{env.VERSION}} installer for developers' - path: 'bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip' - if-no-files-found: 'error' - - name: Create GitHub release uses: ncipollo/release-action@v1 if: github.ref_type == 'tag' @@ -121,6 +112,4 @@ jobs: - name: Generate artifact attestations uses: actions/attest-build-provenance@v2 with: - subject-path: | - bin/SMAPI-${{env.VERSION}}-installer.zip - bin/SMAPI-${{env.VERSION}}-installer-for-developers.zip + subject-path: bin/SMAPI-${{env.VERSION}}-installer.zip diff --git a/build/scripts/finalize-install-package.sh b/build/scripts/finalize-install-package.sh index 117e33e5..65abf731 100644 --- a/build/scripts/finalize-install-package.sh +++ b/build/scripts/finalize-install-package.sh @@ -24,44 +24,43 @@ 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" . +# move files to Linux filesystem +folderName = "SMAPI $version installer" +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 {} \; +# 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 +# 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 "" + 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 "" + echo "Done!" diff --git a/build/scripts/prepare-install-package.ps1 b/build/scripts/prepare-install-package.ps1 index 738f9f80..fc512714 100644 --- a/build/scripts/prepare-install-package.ps1 +++ b/build/scripts/prepare-install-package.ps1 @@ -154,7 +154,6 @@ Write-Output "----------------------------" # init paths $installAssets = "src/SMAPI.Installer/assets" $packagePath = "bin/SMAPI installer" -$packageDevPath = "bin/SMAPI installer for developers" # init structure foreach ($folder in $folders) { @@ -268,19 +267,13 @@ else { } } -# split into main + for-dev folders -Copy-Item -Recurse "$packagePath" "$packageDevPath" +# convert bundle folder into final 'install.dat' files 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" + $path = "$packagePath/internal/$folder" - # 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.dat" - if (!$skipBundleDeletion) { - Remove-Item -Recurse -Force "$path/bundle" - } + Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.dat" + if (!$skipBundleDeletion) { + Remove-Item -Recurse -Force "$path/bundle" } } @@ -288,13 +281,9 @@ foreach ($folder in $folders) { ########### ### Create release zips ########### -# rename folders Move-Item "$packagePath" "bin/SMAPI $version installer" -Move-Item "$packageDevPath" "bin/SMAPI $version installer for developers" -# 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 Write-Output "" Write-Output "Done! Package created in ${pwd.Path}/bin." diff --git a/docs/release-notes.md b/docs/release-notes.md index 241ce635..3cc4567a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -9,6 +9,9 @@ * SMAPI can now detect known malicious loose files in the `Mods` folder. * Updated internal mod blacklist. +* For mod authors: + * SMAPI no longer has a separate 'for developers' version. Instead, you can now use [Generic Mod Config Menu]() to enable 'developer mode' in the console window options. + ## 4.4.0 Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705). diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs index b840f82b..f21634be 100644 --- a/src/SMAPI.Web/Controllers/IndexController.cs +++ b/src/SMAPI.Web/Controllers/IndexController.cs @@ -57,8 +57,8 @@ internal class IndexController : Controller { // choose versions ReleaseVersion[] versions = await this.GetReleaseVersionsAsync(); - ReleaseVersion? stableVersion = versions.LastOrDefault(version => !version.IsForDevs); - ReleaseVersion? stableVersionForDevs = versions.LastOrDefault(version => version.IsForDevs); + ReleaseVersion? stableVersion = versions.LastOrDefault(); + ReleaseVersion? stableVersionForDevs = versions.LastOrDefault(); // render view IndexVersionModel stableVersionModel = stableVersion != null @@ -123,12 +123,11 @@ internal class IndexController : Controller if (asset.FileName.StartsWith("Z_")) continue; - Match match = Regex.Match(asset.FileName, @"SMAPI-(?[\d\.]+(?:-.+)?)-installer(?-for-developers)?.zip"); + Match match = Regex.Match(asset.FileName, @"SMAPI-(?[\d\.]+(?:-.+)?)-installer.zip"); if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion? version)) continue; - bool isForDevs = match.Groups["forDevs"].Success; - yield return new ReleaseVersion(release, asset, version, isForDevs); + yield return new ReleaseVersion(release, asset, version); } } @@ -147,9 +146,6 @@ internal class IndexController : Controller /// The SMAPI version. public ISemanticVersion Version { get; } - /// Whether this is a 'for developers' download. - public bool IsForDevs { get; } - /********* ** Public methods @@ -158,13 +154,11 @@ internal class IndexController : Controller /// The underlying GitHub release. /// The underlying download asset. /// The SMAPI version. - /// Whether this is a 'for developers' download. - public ReleaseVersion(GitRelease release, GitAsset asset, ISemanticVersion version, bool isForDevs) + public ReleaseVersion(GitRelease release, GitAsset asset, ISemanticVersion version) { this.Release = release; this.Asset = asset; this.Version = version; - this.IsForDevs = isForDevs; } } } diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml index 077da07c..588f8b9f 100644 --- a/src/SMAPI.Web/Views/Index/Index.cshtml +++ b/src/SMAPI.Web/Views/Index/Index.cshtml @@ -91,7 +91,6 @@

For mod creators

diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json index 3b999ad0..27165ee8 100644 --- a/src/SMAPI/SMAPI.config.json +++ b/src/SMAPI/SMAPI.config.json @@ -49,7 +49,7 @@ in future SMAPI versions. * * Not recommended for most players. */ - "DeveloperMode": true, + "DeveloperMode": false, /** * Whether SMAPI should listen for console input. Disabling this will prevent you from using From a30fadcdde496f2d714118b49215349f939b941a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 22/26] standardize headers in older release notes --- docs/release-notes-archived.md | 58 +++++++++++++++++----------------- docs/release-notes.md | 26 +++++++-------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/release-notes-archived.md b/docs/release-notes-archived.md index 9801c226..f1ebc148 100644 --- a/docs/release-notes-archived.md +++ b/docs/release-notes-archived.md @@ -1,7 +1,7 @@ ← [README](README.md) # Release notes -## 3.0 and later +## 3.0.0 and later See [newer release notes](release-notes.md). ## 2.11.3 @@ -49,7 +49,7 @@ Released 17 March 2019 for Stardew Valley 1.3.36. * The log parser now collapses redundant sections by default. * Fixed log parser column resize bug. -## 2.11 +## 2.11.0 Released 01 March 2019 for Stardew Valley 1.3.36. * For players: @@ -98,7 +98,7 @@ Released 30 December 2018 for Stardew Valley 1.3.32–33. * For players: * Fixed some mod integrations not working correctly in SMAPI 2.10. -## 2.10 +## 2.10.0 Released 29 December 2018 for Stardew Valley 1.3.32–33. * For players: @@ -151,7 +151,7 @@ Released 07 December 2018 for Stardew Valley 1.3.32. * Fixed crash in SMAPI 2.9 when constructing certain buildings. * Fixed error when a map asset is reloaded in rare cases. -## 2.9 +## 2.9.0 Released 07 December 2018 for Stardew Valley 1.3.32. * For players: @@ -190,7 +190,7 @@ Released 19 November 2018 for Stardew Valley 1.3.32. * Fixed installer error on Windows with SMAPI 2.8. -## 2.8 +## 2.8.0 Released 19 November 2018 for Stardew Valley 1.3.32. * For players: @@ -259,7 +259,7 @@ Released 19 November 2018 for Stardew Valley 1.3.32. * Installer no longer special-cases Omegasis' older `SaveBackup` mod (now named `AdvancedSaveBackup`). * Fixed mod web API returning a concatenated name for mods with alternate names. -## 2.7 +## 2.7.0 Released 14 August 2018 for Stardew Valley 1.3.28. * For players: @@ -290,7 +290,7 @@ Released 14 August 2018 for Stardew Valley 1.3.28. * Dropped support for pre-SMAPI-2.6 update checks in the web API. _These are no longer useful, even if the player still has earlier versions of SMAPI. Older versions of SMAPI won't launch in Stardew Valley 1.3 (so they won't check for updates), and newer versions of SMAPI/mods won't work with older versions of the game._ -## 2.6 +## 2.6.0 Released 01 August 2018 for Stardew Valley 1.3.27. * For players: @@ -460,7 +460,7 @@ Released 24 February 2018 for Stardew Valley 1.2.30–1.2.33. * For players: * Fixed event error in rare cases. -## 2.5 +## 2.5.0 Released 24 February 2018 for Stardew Valley 1.2.30–1.2.33. * For players: @@ -492,7 +492,7 @@ Released 24 February 2018 for Stardew Valley 1.2.30–1.2.33. * Overhauled mod DB format to be more concise, reduce the memory footprint, and support versioning/defaulting more fields. * Reimplemented log parser with serverside parsing and vue.js on the frontend. -## 2.4 +## 2.4.0 Released 24 January 2018 for Stardew Valley 1.2.30–1.2.33. * For players: @@ -519,7 +519,7 @@ Released 24 January 2018 for Stardew Valley 1.2.30–1.2.33. * For SMAPI developers: * Overhauled input handling to support future input events. -## 2.3 +## 2.3.0 Released 26 December 2017 for Stardew Valley 1.2.30–1.2.33. * For players: @@ -541,7 +541,7 @@ Released 26 December 2017 for Stardew Valley 1.2.30–1.2.33. * For the [log parser](https://smapi.io/log): * Fixed broken favicon. -## 2.2 +## 2.2.0 Released 02 December 2017 for Stardew Valley 1.2.30–1.2.33. * For players: @@ -563,7 +563,7 @@ Released 02 December 2017 for Stardew Valley 1.2.30–1.2.33. * Fixed input events' `e.SuppressButton(button)` method ignoring specified button. * Fixed input events' `e.SuppressButton()` method not working with mouse buttons. -## 2.1 +## 2.1.0 Released 01 November 2017 for Stardew Valley 1.2.30–1.2.33. * For players: @@ -593,7 +593,7 @@ Released 01 November 2017 for Stardew Valley 1.2.30–1.2.33. * For SMAPI developers: * Added the installer version and platform to the installer window title to simplify troubleshooting. -## 2.0 +## 2.0.0 Released 14 October 2017 for Stardew Valley 1.2.30–1.2.33. ### Release highlights @@ -729,7 +729,7 @@ For players: For mod authors: * Fixed mod registry lookups by unique ID not being case-insensitive. -## 1.15 +## 1.15.0 Released 08 July 2017 for Stardew Valley 1.2.30–1.2.31. For players: @@ -762,7 +762,7 @@ For SMAPI developers: * Added prototype SMAPI 2.0 support for version strings in `manifest.json` (not recommended for mods yet). * Compiling SMAPI now uses your `~/stardewvalley.targets` file if present. -## 1.14 +## 1.14.0 Released 02 July 2017 for Stardew Valley 1.2.30. For players: @@ -797,7 +797,7 @@ For players: * Fixed errors when loading a mod with no name or version. * Fixed mods with no manifest `Name` field having no name (SMAPI will now shows their filename). -## 1.13 +## 1.13.0 Released 19 May 2017 for Stardew Valley 1.2.26–1.2.29. For players: @@ -820,7 +820,7 @@ For mod developers: * Fixed maps not recognising custom tilesheets added through the SMAPI content API. * Internal refactoring for upcoming features. -## 1.12 +## 1.12.0 Released 03 May 2017 for Stardew Valley 1.2.26–1.2.29. For players: @@ -836,7 +836,7 @@ For mod developers: * Fixed content API error when loading a PNG during early game init (e.g. in mod's `Entry`). * Fixed content API error when loading an XNB from the mod folder on macOS. -## 1.11 +## 1.11.0 Released 30 April 2017 for Stardew Valley 1.2.26. For players: @@ -852,7 +852,7 @@ For mod developers: * `Monitor.ExitGameImmediately` now aborts SMAPI initialization and events more quickly. * Fixed value-changed events being raised when the player loads a save due to values being initialized. -## 1.10 +## 1.10.0 Released 24 April 2017 for Stardew Valley 1.2.26. For players: @@ -872,7 +872,7 @@ For mod developers: * Fixed `monitor.ExitGameImmediately` not working correctly. * Fixed `Constants.SaveFolderName` not set for a new game until the save is created. -## 1.9 +## 1.9.0 Released 05 April 2017 for Stardew Valley 1.1–1.11. For players: @@ -914,7 +914,7 @@ For mod developers: For SMAPI developers: * Added support for debugging SMAPI on Linux/macOS if supported by the editor. -## 1.8 +## 1.8.0 Released 04 February 2017 for Stardew Valley 1.1–1.11. For players: @@ -939,7 +939,7 @@ For SMAPI developers: * no longer loads DLLs if they're not referenced; * reduces log verbosity. -## 1.7 +## 1.7.0 Released 19 January 2017 for Stardew Valley 1.1–1.11. For players: @@ -954,7 +954,7 @@ For mod developers: * Fixed some `LocationEvents`, `PlayerEvents`, and `TimeEvents` being fired during game startup. * Increased deprecation levels for `SObject`, `LogWriter` (not `Log`), and `Mod.Entry(ModHelper)` (not `Mod.Entry(IModHelper)`) to _pending removal_. Increased deprecation levels for `Mod.PerSaveConfigFolder`, `Mod.PerSaveConfigPath`, and `Version.VersionString` to _info_. -## 1.6 +## 1.6.0 Released 16 January 2017 for Stardew Valley 1.1–1.11. For players: @@ -979,7 +979,7 @@ For SMAPI developers: * Added support for custom incompatible-mod error text. * Fixed issue where `TrainerMod` used older logic to detect the game path. -## 1.5 +## 1.5.0 Released 27 December 2016 for Stardew Valley 1.1–1.11. For players: @@ -993,7 +993,7 @@ For developers: _This new implementation is [semver 2.0](https://semver.org/)-compliant, introduces `NewerThan(version)` and `OlderThan(version)` convenience methods, adds support for parsing a version string into a `SemanticVersion`, and fixes various bugs with the former implementation. This also replaces `Manifest` with `IManifest`._ * Increased deprecation levels for `SObject`, `Extensions`, `LogWriter` (not `Log`), `SPlayer`, and `Mod.Entry(ModHelper)` (not `Mod.Entry(IModHelper)`). -## 1.4 +## 1.4.0 Released 12 December 2016 for Stardew Valley 1.1–1.11. For players: @@ -1012,14 +1012,14 @@ For developers: * Added `TypeLoadException` details when intercepted by SMAPI. * Fixed an issue where you couldn't debug into an assembly because it was copied into the `.cache` directory. That will now only happen if necessary. -## 1.3 +## 1.3.0 Released 04 December 2016 for Stardew Valley 1.1–1.11. For players: * You can now run most mods on any platform (e.g. run Windows mods on Linux/macOS). * Fixed the normal uninstaller not removing files added by the 'SMAPI for developers' installer. -## 1.2 +## 1.2.0 Released 25 November 2016 for Stardew Valley 1.1–1.11. For players: @@ -1041,7 +1041,7 @@ For players: For developers: * Fixed deprecation warnings being repeated if the mod can't be identified. -## 1.1 +## 1.1.0 Released 17 November 2016 for Stardew Valley 1.1–1.11. For players: @@ -1059,7 +1059,7 @@ For developers: * Added optional `MinimumApiVersion` to `manifest.json`. * Added emergency interrupt feature for dangerous mods. -## 1.0 +## 1.0.0 Released 11 November 2016 for Stardew Valley 1.1–1.11. For players: diff --git a/docs/release-notes.md b/docs/release-notes.md index 3cc4567a..8c732746 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -157,7 +157,7 @@ Released 18 December 2024 for Stardew Valley 1.6.14 or later. > > Players on Linux or Windows can ignore this. -# 4.1.9 +## 4.1.9 Released 08 December 2024 for Stardew Valley 1.6.14 or later. * For players: @@ -390,7 +390,7 @@ Released 24 March 2024 for Stardew Valley 1.6.0 or later. _Extra Map Layers mod caused visual issues like dark shadows in all locations with extra map layers, since the game now handles them automatically. SMAPI now disables Extra Map Layers and ignores dependencies on it._ * When using a custom `Mods` folder path, SMAPI now logs the game folder path to simplify troubleshooting. -# 4.0.1 +## 4.0.1 Released 20 March 2024 for Stardew Valley 1.6.0 or later. * For players: @@ -1044,7 +1044,7 @@ Released 03 May 2021 for Stardew Valley 1.5.4. * For players: * Fixed installer leaving an unneeded `StardewModdingAPI-x64.exe` file in 32-bit game folders. -## 3.10 +## 3.10.0 Released 03 May 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/50764911). * For players: @@ -1142,7 +1142,7 @@ Released 25 January 2021 for Stardew Valley 1.5.4. * Fixed _tile contains an invalid TileSheet reference_ crash after mods change certain maps. * Fixed _patched game code_ issue shown for the bundled Error Handler mod. -## 3.9 +## 3.9.0 Released 22 January 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/46553874). * For players: @@ -1238,7 +1238,7 @@ Released 26 December 2020 for Stardew Valley 1.5.1 or later. For the web UI: * Fixed edge cases in SMAPI log parsing. -## 3.8 +## 3.8.0 Released 21 December 2020 for Stardew Valley 1.5 or later. See [release highlights](https://www.patreon.com/posts/45294737). * For players: @@ -1323,7 +1323,7 @@ Released 08 September 2020 for Stardew Valley 1.4.1 or later. * For players: * Fixed input-handling bugs in 3.7. -## 3.7 +## 3.7.0 Released 07 September 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/41341767). * For players: @@ -1379,7 +1379,7 @@ Released 21 June 2020 for Stardew Valley 1.4.1 or later. * Fixed event priority sorting. -## 3.6 +## 3.6.0 Released 20 June 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/38441800). * For players: @@ -1421,7 +1421,7 @@ Released 20 June 2020 for Stardew Valley 1.4.1 or later. See [release highlights * Merged the separate legacy redirects app on AWS into the main app on Azure. * Changed SMAPI's Harmony ID from `io.smapi` to `SMAPI` for readability in Harmony summaries. -## 3.5 +## 3.5.0 Released 27 April 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/36471055). * For players: @@ -1455,7 +1455,7 @@ Released 24 March 2020 for Stardew Valley 1.4.1 or later. * Asset changes now propagate to NPCs in an event (e.g. wedding sprites). * Fixed mouse input suppression not working in SMAPI 3.4. -## 3.4 +## 3.4.0 Released 22 March 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/35161371). * For players: @@ -1488,7 +1488,7 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later. * Fixed errors with custom spouse room mods in SMAPI 3.3. -## 3.3 +## 3.3.0 Released 22 February 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/34248719). * For players: @@ -1522,7 +1522,7 @@ Released 22 February 2020 for Stardew Valley 1.4.1 or later. See [release highli * Improved support for four-part versions to support SMAPI on Android. * The SMAPI log now prefixes the OS name with `Android` on Android. -## 3.2 +## 3.2.0 Released 01 February 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/33659728). * For players: @@ -1563,7 +1563,7 @@ Released 01 February 2020 for Stardew Valley 1.4.1 or later. See [release highli * The `/mods` web API endpoint now includes version mappings from the wiki. * Dropped API support for the pre-3.0 update-check format. -## 3.1 +## 3.1.0 Released 05 January 2019 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/32904041). * For players: @@ -1624,7 +1624,7 @@ Released 02 December 2019 for Stardew Valley 1.4 or later. * For mod authors: * `SemanticVersion` now supports [semver 2.0](https://semver.org/) build metadata. -## 3.0 +## 3.0.0 Released 26 November 2019 for Stardew Valley 1.4. ### Release highlights From 08559de167474b7f17935367fa25af8885a7b453 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 00:00:01 -0500 Subject: [PATCH 23/26] archive 3.x release notes --- docs/release-notes-archived.md | 1346 +++++++++++++++++++++++++++++++- docs/release-notes.md | 1346 +------------------------------- 2 files changed, 1346 insertions(+), 1346 deletions(-) diff --git a/docs/release-notes-archived.md b/docs/release-notes-archived.md index f1ebc148..ba7451bf 100644 --- a/docs/release-notes-archived.md +++ b/docs/release-notes-archived.md @@ -1,9 +1,1353 @@ ← [README](README.md) # Release notes -## 3.0.0 and later +## 4.0.0 and later See [newer release notes](release-notes.md). +## 3.18.6 +Released 05 October 2023 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed SpriteMaster compatibility in SMAPI 3.18.5+ with temporary workaround. + * Fixed `player_add` and `list_items` console commands not including Pickled Ginger, and returning Honey instead of Wild Honey _(in Console Commands)_. + +* For mod authors: + * Added asset propagation for `LooseSprites/chatBox` and `LooseSprites/emojis`. + +* For the web UI: + * Fixed uploaded log/JSON file expiry alway shown as renewed. + +## 3.18.5 +Released 26 August 2023 for Stardew Valley 1.5.6 or later. + +* For players: + * Reduced startup time when many mods are rewritten for compatibility. + * Fixed app icon on Linux/macOS, and for some players on Windows (thanks to Datrio!). + * Fixed error if you copy a null field into `config.user.json`. + * Fixed installer creating a "null" file in its folder. + * Fixed installer moving bundled mods back to their default location on update. It now correctly updates their existing folder instead. + +* For mod authors: + * Updated dependencies, including [Mono.Cecil](https://github.com/jbevain/cecil) 0.11.4 → 0.11.5 (see [changes](https://github.com/jbevain/cecil/releases/tag/0.11.5)). + * Fixed NPC warp cache not updated when a map edit changes door warps (thanks to atravita!). + +* For the web UI: + * Viewing an uploaded log/JSON file within 15 days of expiry now auto-renews it, to allow for discussions that last longer than the default 30-day expiry. + * Fixed log parser's summary skipping mods if some have no description. + * Fixed log parser no longer ignoring Error Handler in newer SMAPI-on-Android versions (thanks to AnotherPillow!). + +* For SMAPI developers: + * Overhauled compatibility rewriters. + _This allows simpler, more robust, and more flexible crossplatform rewrites (e.g. on Android) and prepares for the upcoming Stardew Valley 1.6. See remarks on the new `ReplaceReferencesRewriter` for more info._ + +## 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. + +* For players: + * Fixed `findstr` installer error for some players. + * 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. Thanks to stylemate (updated Korean)! + +* For mod authors: + * Added `IsActiveForScreen()` method to `PerScreen`. + * Updated to [FluentHttpClient](https://github.com/Pathoschild/FluentHttpClient#readme) 4.3.0 (see [changes](https://github.com/Pathoschild/FluentHttpClient/blob/develop/RELEASE-NOTES.md#430)). + * Adjusted `ModContentManager.HandleUnknownFileType` to let mods patch it. + * Fixed `Context.IsWorldReady` being editable by mods. + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.29.0. + +## 3.18.2 +Released 09 January 2023 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed empty save backups for some macOS players. + * Fixed `player_add` console command not handling custom slingshots correctly (thanks to DaLion!). + +* For mod authors: + * Added `DelegatingModHooks` utility for mods which need to override SMAPI's mod hooks directly. + * Updated to Newtonsoft.Json 13.0.2 (see [changes](https://github.com/JamesNK/Newtonsoft.Json/releases/tag/13.0.2)) and Pintail 2.2.2 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#222)). + +## 3.18.1 +Released 01 December 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed mod texture edits sometimes cut off (thanks to atravita!). + +* For the web UI: + * The log parser no longer warns about missing Error Handler on Android, where it doesn't exist yet (thanks to AnotherPillow!). + +## 3.18.0 +Released 12 November 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/74565278). + +* For players: + * You can now override the mod load order in `smapi-internal/config.json` (thanks to Shockah!). + * You can now disable console input in `smapi-internal/config.json`, which may reduce CPU usage on some Linux systems. + * Fixed map edits not always applied for farmhands in multiplayer (thanks to SinZ163!). + * Internal changes to prepare for the upcoming Stardew Valley 1.6 and SMAPI 4.0. + +* For mod authors: + * Optimized asset name comparisons (thanks to atravita!). + * Raised all deprecation messages to the 'pending removal' level. + * **This is the last major update before SMAPI 4.0.0, which will drop all deprecated APIs.** If you haven't [fixed deprecation warnings in your mod code](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0) (if any), you should do it soon. SMAPI 4.0.0 will release alongside the upcoming Stardew Valley 1.6. + +* For the web UI: + * The log parser now detects split-screen mode and shows which screen logged each message. + +## 3.17.2 +Released 21 October 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed installer crash if Steam's library data is invalid or in an old format; it'll now be ignored instead. +* For mod authors: + * Fixed image patches sometimes applied one pixel higher than expected after 3.17.0 (thanks to atravita!). + +## 3.17.1 +Released 10 October 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed installer error on Windows if the Steam library folder exists but doesn't contain Steam's `.vdf` library data file. + +## 3.17.0 +Released 09 October 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/73090322). + +* For players: + * You can now download SMAPI 'strict mode' from [Nexus files](https://www.nexusmods.com/stardewvalley/mods/2400/?tab=files), which removes all deprecated APIs. This may significantly improve performance, but mods which still show deprecation warnings won't work. + * The SMAPI installer now also detects game folders in Steam's `.vdf` library data on Windows (thanks to pizzaoverhead!). + * SMAPI now prevents mods from enabling Harmony debug mode, which impacts performance and creates a file on your desktop. + _You can allow debug mode by editing `smapi-internal/config.json` in your game folder._ + * Optimized performance and memory usage (thanks to atravita!). + * Other internal optimizations. + * Added more file extensions to ignore when searching for mod folders: `.7z`, `.tar`, `.tar.gz`, and `.xcf` (thanks to atravita!). + * Removed transitional `UseRawImageLoading` option added in 3.15.0. This is now always enabled, except when PyTK is installed. + * Fixed update alerts incorrectly shown for prerelease versions on GitHub that aren't marked as prerelease. + +* For mod authors: + * When [providing a mod API in a C# mod](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations), you can now get the mod requesting it as an optional parameter (thanks to KhloeLeclair!). + * SMAPI now treats square brackets in the manifest `Name` field as round ones to avoid breaking tools which parse log files. + * Made deprecation message wording stronger for the upcoming SMAPI 4.0.0 release. + * The `Texture2D.Name` field is now set earlier to support mods like SpriteMaster. + * Updated dependencies: [Harmony](https://harmony.pardeike.net) 2.2.2 (see [changes](https://github.com/pardeike/Harmony/releases/tag/v2.2.2.0)) and [FluentHttpClient](https://github.com/Pathoschild/FluentHttpClient#readme) 4.2.0 (see [changes](https://github.com/Pathoschild/FluentHttpClient/blob/develop/RELEASE-NOTES.md#420)). + * Fixed `LocationListChanged` event not raised & memory leak occurring when a generated mine/volcano is removed (thanks to tylergibbs2!). + +## 3.16.2 +Released 31 August 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed `NoSuitableGraphicsDeviceException` launch error for some players with compatible GPUs since 3.16.0. + +## 3.16.1 +Released 29 August 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Updated PyTK compatibility mode for the latest PyTK version. + * Fixed broken mods sometimes incorrectly listed as duplicate. + +## 3.16.0 +Released 22 August 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/70797008). + +* For players: + * Added error message if mod files are detected directly under `Mods` (instead of each mod having its own subfolder). + * SMAPI now sets a success/error code when the game exits. + _This is used by your OS (like Windows) to decide whether to keep the console window open when the game ends._ + * Fixed SMAPI on Windows applying different DPI awareness settings than the game (thanks to spacechase0!). + * Fixed Linux/macOS installer's color scheme question partly unreadable if the terminal background is dark. + * Fixed error message when a mod loads an invalid PNG file (thanks to atravita!). + * Fixed error message when a mod is duplicated, but one of the copies is also missing the DLL file. This now shows the duplicate-mod message instead of the missing-DLL message. + * Fixed macOS launcher using Terminal regardless of the system's default terminal (thanks to ishan!). + * Fixed best practices in Linux/macOS launcher scripts (thanks to ishan!). + * Improved translations. Thanks to KediDili (updated Turkish)! + +* For mod authors: + * While loading your mod, SMAPI now searches for indirect dependencies in your mod's folder (thanks to TehPers)! This mainly enables F# mods. + * **Raised deprecation message levels.** + _Deprecation warnings are now player-visible in the SMAPI console as faded `DEBUG` messages._ + * Updated to Pintail 2.2.1 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#221)). + * Switched SMAPI's `.pdb` files to the newer 'portable' format. This has no effect on mods. + +* For the web UI: + * Added log parser warning about performance of PyTK 1.23.0 or earlier. + * Converted images to SVG (thanks to ishan!). + * Updated log parser for the new update alert format in SMAPI 3.15.1. + * Updated the JSON validator/schema for Content Patcher 1.28.0. + * Fixed log parsing for invalid content packs. + * Fixed log parsing if a mod logged a null character. + +## 3.15.1 +Released 06 July 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Added current version to update alerts (thanks to ishan!). + * Fixed lag for some players since Stardew Valley 1.5.5. + * Fixed `smapi-internal/config.user.json` overrides not applied after SMAPI 3.14.0. + * Fixed PyTK not rescaling images correctly in some cases. + _When PyTK 1.23.0 or earlier is installed, this will disable the main performance improvements in SMAPI 3.15.0._ + * Updated compatibility list. + +* For mod authors: + * The [FluentHttpClient package](https://github.com/Pathoschild/FluentHttpClient#readme) is now loaded by SMAPI. + * Fixed `TRACE` logs not tracking reloaded map tilesheets as a propagated asset. + +* For the web UI: + * Added log parser suggested fix for missing/outdated Error Handler, and improved visual styles. + * Updated the JSON validator/schema for Content Patcher 1.27.0. + * Fixed the mod count in the log parser metadata. + +## 3.15.0 +Released 17 June 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/67877219). + +* For players: + * Optimized mod image file loading. + * Minor optimizations (thanks to Michael Kuklinski / Ameisen!). + * Updated compatibility list. + +* For mod authors: + * Added an [`IRawTextureData` asset type](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0#Raw_texture_data), to avoid creating full `Texture2D` instances in many cases. + * In `smapi-internal/config.json`, you can now enable verbose logging for specific mods (instead of all or nothing). + * Updated dependencies: + * Harmony 2.2.1 (see changes in [2.2.0](https://github.com/pardeike/Harmony/releases/tag/v2.2.0.0) and [2.2.1](https://github.com/pardeike/Harmony/releases/tag/v2.2.1.0)); + * Newtonsoft.Json 13.0.1 (see [changes](https://github.com/JamesNK/Newtonsoft.Json/releases/tag/13.0.1)); + * Pintail 2.2.0 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#220)). + * Removed transitional `UsePintail` option added in 3.14.0 (now always enabled). + * Fixed `onBehalfOf` arguments in the new content API being case-sensitive. + * Fixed map edits which change warps sometimes rebuilding the NPC pathfinding cache unnecessarily, which could cause a noticeable delay for players. + +## 3.14.7 +Released 01 June 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Optimized reflection cache to reduce frame skips for some players. + +* For mod authors: + * Removed `runtimeconfig.json` setting which impacted hot reload support. + +## 3.14.6 +Released 27 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed error in split-screen mode when a mod provides a localized asset in one screen but not another. + * Minor optimizations. + +## 3.14.5 +Released 22 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Improved performance when mods change some asset types (including NPC portraits/sprites). + * Fixed _could not find file_ error if a mod provides a localized version of a normally unlocalized asset and then stops providing it. + * Fixed CurseForge update checks for the new CurseForge API. + +## 3.14.4 +Released 15 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Improved performance for mods using deprecated APIs. + +* For mod authors: + * Removed warning for mods which use `dynamic`. + _This no longer causes errors on Linux/macOS after Stardew Valley 1.5.5._ + +## 3.14.3 +Released 12 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Reduced in-game performance impact. + +* For mod authors: + * Refactored how event handling works under the hood, particularly the new content API. This should have no effect on mod usage. + * Verbose mode now logs the in-game time. + * Fixed error when loading a `.xnb` file through the old content API without the file extension. + * Fixed asset propagation for player sprites not fully updating recolor masks in some cases. + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.26.0. + +## 3.14.2 +Released 08 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Enabled case-insensitive file paths by default for Android and Linux players. + _This was temporarily disabled in SMAPI 3.14.1, and will remain disabled by default on macOS and Windows since their filesystems are already case-insensitive._ + * Various performance improvements. +* For mod authors: + * Dynamic content packs created via `helper.ContentPacks.CreateTemporary` or `CreateFake` are now listed in the log file. + * Fixed assets loaded through a fake content pack not working correctly since 3.14.0. + +## 3.14.1 +Released 06 May 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Improved performance for mods still using the previous content API. + * Disabled case-insensitive file paths (introduced in 3.14.0) by default. + _You can enable them by editing `smapi-internal/config.json` if needed. They'll be re-enabled in an upcoming version after they're reworked a bit._ + * Removed experimental 'aggressive memory optimizations' option. + _This was disabled by default and is no longer needed in most cases. Memory usage will be better reduced by reworked asset propagation in the upcoming SMAPI 4.0.0._ + * Fixed 'content file was not found' error when the game tries to load unlocalized text from a localizable mod data asset in 3.14.0. + * Fixed error reading empty JSON files. These are now treated as if they didn't exist (matching pre-3.14.0 behavior). + * Updated compatibility list. + +## 3.14.0 +Released 01 May 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/65265507). + +### For players +This is a big update, but existing mods should all work fine. If the latest version of a mod breaks in SMAPI 3.14, please report it [on the SMAPI mod page](https://www.nexusmods.com/stardewvalley/mods/2400?tab=posts). + +* Improvements: + * SMAPI now ignores dot-prefixed files when searching for mod folders (thanks to Nuztalgia!). + * On Linux, SMAPI now fixes many case-sensitive mod path issues automatically. + * On Linux/macOS, added `--use-current-shell` [command-line argument](technical/smapi.md#command-line-arguments) to avoid opening a separate terminal window. + * Improved performance in some cases. + * Improved translations. Thanks to ChulkyBow (updated Ukrainian)! + * Dropped update checks for the unofficial 64-bit patcher (obsolete since SMAPI 3.12.6). +* Fixes: + * Fixed some movie theater textures not translated when loaded through SMAPI (specifically assets with the `_international` suffix). + * Fixed the warning text when a mod causes an asset load conflict with itself. + * Fixed `--no-terminal` [command-line argument](technical/smapi.md#command-line-arguments) on Linux/macOS still opening a terminal window, even if nothing is logged to it (thanks to Ryhon0!). + * Fixed `player_add` console command not handling journal scraps and secret notes correctly. + * Fixed `set_farm_type` console command not updating warps. +* For the web UI: + * Improved log parser UI (thanks to KhloeLeclair!): + * Added pagination for big logs. + * Added search box to filter the log. + * Added option to show/hide content packs in the mod list. + * Added jump links in the sidebar. + * The filter options now stick to the top of the screen when scrolling. + * Rewrote rendering to improve performance. + +### For mod authors +This is a big release that includes the new features planned for SMAPI 4.0.0. + +For C# mod authors: your mods should still work fine in SMAPI 3.14.0. However you should review the [migration to SMAPI 4.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0) guide and update your mods when possible. Deprecated code will be removed when SMAPI 4.0.0 releases later this year (no sooner than August 2022), and break any mods which haven't updated by that time. You can update affected mods now, there's no need to wait for 4.0.0. + +For content pack authors: SMAPI 3.14.0 and 4.0.0 don't affect content packs. They should work fine as long as +the C# mod that loads them is updated. + +* Major changes: + * Added [content events](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Content), which will replace `IAssetEditor` and `IAssetLoader` in SMAPI 4.0.0. + _These include new features not supported by the old API like load conflict resolution, edit priority, and content pack labels. They also support new cases like easily detecting when an asset has changed, and avoid data corruption issues in some edge cases._ + * Added [nullable reference type annotations](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0#Nullable_reference_type_annotations) for all APIs. + * Added [`helper.GameContent` and `helper.ModContent`](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0#Content_loading_API), which will replace `helper.Content` in SMAPI 4.0.0. + * Improved [mod-provided API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Mod-provided_APIs) proxying (thanks to Shockah!). + _This adds support for custom interfaces in return values or input arguments, custom enums if their values match, generic methods, and more. This is an internal change, you don't need to do anything different in your mod code._ + * Mod files loaded through SMAPI APIs (including `helper.Content.Load`) are now case-insensitive, even on Linux. + * Enabled deprecation notices for all deprecated APIs. These will only be shown in `TRACE` logs for at least a month after SMAPI 3.14.0 releases. +* Other improvements: + * Added `IAssetDataForImage.ExtendMap` to resize maps in asset editors. + * Added `IContentPack.ModContent` property to manage content pack assets. + * Added `Constants.ContentPath` to get the full path to the game's `Content` folder. + * Added `IAssetName` fields to the info received by `IAssetEditor`, `IAssetLoader`, and content event methods. + _This adds methods for working with asset names, parsed locales, etc._ + * Added `helper.Content.ParseAssetName` to get an `IAssetName` for an arbitrary asset key. + * Added [command-line arguments](technical/smapi.md#command-line-arguments) to toggle developer mode (thanks to Tondorian!). + * If an asset is loaded multiple times in the same tick, `IAssetLoader.CanLoad` and `IAssetEditor.CanEdit` are now cached unless invalidated by `helper.Content.InvalidateCache`. + * The `ISemanticVersion` comparison methods (`CompareTo`, `IsBetween`, `IsNewerThan`, and `IsOlderThan`) now allow null values. A null version is always considered older than any non-null version per [best practices](https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1.compareto#remarks). + * Deprecation notices now show a shorter stack trace in most cases, so it's clearer where the deprecated code is in the mod. +* Fixes: + * Fixed the `SDate` constructor being case-sensitive. + * Fixed support for using locale codes from custom languages in asset names (e.g. `Data/Achievements.eo-EU`). + * Fixed issue where suppressing `[Left|Right]Thumbstick[Down|Left]` keys would suppress the opposite direction instead. + * Fixed null handling in various edge cases. +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.25.0. + * Added `data-*` attributes to the log parser page for external tools. + * Fixed JSON validator showing incorrect error for update keys without a subkey. + +### For SMAPI contributors +* You no longer need a Nexus API key to launch the `SMAPI.Web` project locally. + +## 3.13.4 +Released 16 January 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * Fixed Linux/macOS launch error in 3.13.3. + +## 3.13.3 +Released 16 January 2022 for Stardew Valley 1.5.6 or later. + +* For players: + * **SMAPI now needs Stardew Valley 1.5.6 or later.** + * Added automatic fix for custom maps which are missing a required tilesheet. + * Added automatic save recovery when a custom farm type isn't available anymore. + * Added the game's new build number to the SMAPI console + log. + * The installer now detects Xbox app game folders. + * Reduced mod loading time a bit. + * Fixed macOS launch issue when using some terminals (thanks to bruce2409!). + * Fixed Linux/macOS terminal ignoring backspaces in Stardew Valley 1.5.5+. + * Fixed extra newlines in the SMAPI console. + * Fixed outdated instructions in Steam error message. + * Fixed uninstaller not removing `StardewModdingAPI.deps.json` file. + * Simplified [running without a terminal on Linux/macOS](https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting#SMAPI_doesn.27t_recognize_controller_.28Steam_only.29) when needed. + * Updated compatibility list. + * Improved translations. Thanks to ChulkyBow (added Ukrainian)! + +* For the web UI: + * Added log instructions for Xbox app on Windows. + * Added log download option. + * Redesigned log instruction UI. + * Fixed log parser not correctly handling multiple mods having the exact same name. + * Fixed JSON validator not recognizing manifest [update subkeys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Update_checks#Update_subkeys). + +## 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. + +* 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. See [release highlights](https://www.patreon.com/posts/59348226). + +* For players: + * Updated for Stardew Valley 1.5.5. + * Added `set_farm_type` [console command](https://stardewvalleywiki.com/Modding:Console_commands#Console_commands) to change the current farm type. + * Fixed installer window closing immediately if the installer crashed. + * Updated compatibility list. + +* For mod authors: + * Migrated to 64-bit MonoGame and .NET 5 on all platforms (see [migration guide for mod authors](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5)). + * Added support for [map overlays via `asset.AsMap().PatchMap`](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Content#Edit_a_map). + * Added support for loading BmFont `.fnt` files for [custom languages](https://stardewvalleywiki.com/Modding:Custom_languages) through the [content API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Content). + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.24.0. + +**Update note for players with older systems:** +The game now has two branches: the _main branch_ which you'll get by default, and an optional +[_compatibility branch_ for older systems](https://www.stardewvalley.net/compatibility/). The two +branches have identical content, but use [different technologies](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5#Game_compatibility_branch). + +Unfortunately **SMAPI only supports the main branch of the game**. There are formidable difficulties +across all mods in supporting all three variations, 32-bit imposes significant restrictions on what +mods can do, and the [Steam hardware stats](https://store.steampowered.com/hwsurvey) show that 99.69% +of players now have 64-bit. + +## 3.12.8 +Released 18 October 2021 for Stardew Valley 1.5.4. + +* For players: + * Fixed mod edits to the farmhouse shifting the player down one tile in some cases. + * Fixed map tile rotations/flips not working for farmhands in split-screen mode. + * Improved translations. Thanks to ellipszist (added Thai) and Zangorr (added Polish)! + _These are custom languages which require Stardew Valley 1.5.5 and the [Polish](https://www.nexusmods.com/stardewvalley/mods/3616) or [Thai](https://www.nexusmods.com/stardewvalley/mods/7052) mod._ + +* For mod authors: + * SMAPI now intercepts dictionary duplicate-key errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.) + * Fixed barn/coop exit warps being reset when you edit their interior map. + +* For the web UI: + * Added support for unified [mod data overrides](https://stardewvalleywiki.com/Modding:Mod_compatibility#Mod_data_overrides) defined on the wiki. + * The mod compatibility list now shows separate beta stats when 'show advanced info' is enabled. + +## 3.12.7 +Released 18 September 2021 for Stardew Valley 1.5.4. + +* For players: + * Added more progress updates in the log during startup. + * Simplified asset load error message. + * Simplified exception logs. + * Fixed crash loading mods with corrupted translation files. + +* For mod authors: + * Added asset propagation for `LooseSprites\Giftbox`. + * Improved SMAPI's crossplatform read/writing of `Color`, `Point`, `Rectangle`, and `Vector2` in JSON to support nullable fields too. + +* For the web UI: + * The mod compatibility list now shows the beta status by default (if any). + * Fixed JSON validator line numbers sometimes incorrect. + +## 3.12.6 +Released 03 September 2021 for Stardew Valley 1.5.4. + +* For players: + * Added friendly error when using SMAPI 3.2._x_ with Stardew Valley 1.5.5 or later. + * Improved mod compatibility in 64-bit mode (thanks to spacechase0!). + * Reduced load time when scanning/rewriting many mods for compatibility. + * **Dropped support for unofficial 64-bit mode**. You can now use the [official 64-bit Stardew Valley 1.5.5 beta](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows) instead. + * Updated compatibility list. + +* For mod authors: + * Added `PathUtilities.NormalizeAssetName` and `PathUtilities.PreferredAssetSeparator` to prepare for the upcoming Stardew Valley 1.5.5. + * **SMAPI no longer propagates changes to `Data/Bundles`.** + _You can still load/edit the asset like usual, but if bundles have already been loaded for a save, SMAPI will no longer dynamically update the in-game bundles to reflect the changes. Unfortunately this caused bundle corruption when playing in non-English._ + * Fixed content packs created via `helper.ContentPacks.CreateFake` or `CreateTemporary` not initializing translations correctly. + +* For console commands: + * Added `hurry_all` command which immediately warps all NPCs to their scheduled positions. + +**Update note for mod authors:** +Stardew Valley 1.5.5 will change how asset names are formatted. If you use `PathUtilities.NormalizePath` +to format asset names, you should switch to `PathUtilities.NormalizeAssetName` now so your code will +continue working in the next game update. + +## 3.12.5 +Released 26 August 2021 for Stardew Valley 1.5.4. + +* Fixed some mods in unofficial 64-bit mode no longer loading after SMAPI 3.12.3. + +## 3.12.4 +Released 25 August 2021 for Stardew Valley 1.5.4. + +* For players: + * Fixed error loading some mods in SMAPI 3.12.3. + +## 3.12.3 +Released 25 August 2021 for Stardew Valley 1.5.4. + +* For players: + * Added friendly error in 64-bit mode when a mod is 32-bit only. + * Fixed console encoding issues on Linux/macOS. + * Fixed some installer errors not showing info header. + +* For mod authors: + * Added `helper.Translation.GetInAllLocales` to get a translation in every available locale. + * Fixed Visual Studio debugger crash when any mods are rewritten for compatibility (thanks to spacechase0!). + * Fixed `helper.Data.WriteJsonFile` not deleting the file if the model is null, unlike the other `Write*` methods. + * Fixed error-handling for `StackOverflowException` thrown on Linux/macOS. + * Internal changes to prepare for Stardew Valley 1.5.5. + +* For the web API: + * Fixed update checks not shown for prerelease mod versions when you have a SMAPI beta. + * Fixed update checks shown for prerelease mod versions if you have a working non-prerelease version. + +## 3.12.2 +Released 05 August 2021 for Stardew Valley 1.5.4. + +* For players: + * Fixed error creating a new save or joining a multiplayer world in 3.12.1. + +* For mod authors: + * Reverted the `Constants.Save*` fix in SMAPI 3.12.1. + _The change caused a number of other issues, and is only needed for rare cases where the save folder was invalid. This may be revisited in a future version instead._ + * Fixed `NullReferenceException` in SMAPI's error-handling when trying to handle an invalid `ReflectionTypeLoadException`. + +## 3.12.1 +Released 03 August 2021 for Stardew Valley 1.5.4. + +* For players: + * The software conflict message is now shown as a warning to simplify troubleshooting. + * Fixed error loading older Harmony mods for some Windows players using unofficial 64-bit Stardew Valley. + * Updated compatibility list. + +* For mod authors: + * Fixed `Constants.Save*` fields incorrect if the save's folder name and ID don't match. + +## 3.12.0 +Released 01 August 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/54388616). + +* For players: + * Added save recovery when content mods leave null objects in the save (in _Error Handler_). + * Added error if the wrong SMAPI bitness is installed (e.g. 32-bit SMAPI with 64-bit game). + * Added error if some SMAPI files aren't updated correctly. + * Added `removable` option to the `world_clear` console command (in _Console Commands_, thanks to bladeoflight16!). + * Fixed handling of Unicode characters in console commands. + * Fixed intermittent error if a mod gets mod-provided APIs asynchronously. + * Fixed crash when creating a farm name containing characters that aren't allowed in a folder path. + +* For mod authors: + * **Updated Harmony 1.2.0.1 to 2.1.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).** + * SMAPI now intercepts `KeyNotFoundException` errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.) + * Fixed error loading `.xnb` files from the local mod folder. + * Fixed reloading a map not correctly reapplying interior doors. + +## 3.11.0 +Released 09 July 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/53514295). + +* For players: + * Updated for Stardew Valley 1.4.5 multiplayer hotfix on Linux/macOS. + * Fixed installer error on Windows when running as administrator (thanks to LostLogic!). + * Fixed installer error on some Windows systems (thanks to eddyballs!). + * Fixed error if SMAPI fails to dispose on game exit. + * Fixed `player_add` and `list_items` console commands not including some shirts _(in Console Commands)_. + +* For mod authors: + * Added `World.FurnitureListChanged` event (thanks to DiscipleOfEris!). + * Added asset propagation for building/house paint masks. + * Added log message for troubleshooting if Windows software which often causes issues is installed (currently MSI Afterburner and RivaTuner). + * Improved validation for the manifest `Dependencies` field. + * Fixed validation for mods with invalid version `0.0.0`. + * Fixed _loaded with custom settings_ trace log added when using default settings. + * Fixed `Constants.SaveFolderName` and `Constants.CurrentSavePath` not set correctly in rare cases. + +* For the web UI and JSON validator: + * Updated the JSON validator/schema for Content Patcher 1.23.0. + * Fixed [JSON schema](technical/web.md#using-a-schema-file-directly) in Visual Studio Code warning about comments and trailing commas. + * Fixed JSON schema for `i18n` files requiring the wrong value for the `$schema` field. + +## 3.10.1 +Released 03 May 2021 for Stardew Valley 1.5.4. + +* For players: + * Fixed installer leaving an unneeded `StardewModdingAPI-x64.exe` file in 32-bit game folders. + +## 3.10.0 +Released 03 May 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/50764911). + +* For players: + * Added full support for the [unofficial 64-bit Stardew Valley patch](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows), which removes memory limits. The installer detects which version of SMAPI you need, and SMAPI shows update alerts for Stardew64Installer if applicable. + * Added smarter grouping for skipped mods, so it's easier to see root dependencies to update first. + * Added crash recovery when the game can't update a map's seasonal tilesheets _(in Error Handler)_. SMAPI will log an error and keep the previous tilesheets in that case. + * Added installer option to enter a custom game path even if it detected a game folder. + * `*.ico` files are now ignored when scanning for mods. + * Fixed error for non-English players after returning to title, reloading, and entering town with a completed movie theater. + * Fixed `world_clear` console command not removing resource clumps outside the farm and secret woods. + * Fixed error running SMAPI in a strict sandbox on Linux (thanks to kuesji!). + * Fixed `StardewModdingAPI.bin.osx` on macOS overwritten with an identical file on launch which would reset file permissions (thanks to 007wayne!). + * Fixed inconsistent spelling/style for 'macOS'. + +* For modders: + * Added support for [ignoring local map tilesheet files when loading a map](https://stardewvalleywiki.com/Modding:Maps#Local_copy_of_a_vanilla_tilesheet). + * Added asset propagation for `Data\Concessions`. + * Added SMAPI version and bitness to the console title before startup to simplify troubleshooting. + * If a map loads a tilesheet path with no file extension, SMAPI now automatically links it to a `.png` version in the map folder if possible. + * Improved error-handling during asset propagation. + * Fixed `Context.IsMainPlayer` returning true for a farmhand in split-screen mode before the screen is initialized. + * Fixed error when editing bundle data while a split-screen player is joining. + * Fixed update subkeys not working in file descriptions for Nexus mods marked as adult content. + +## 3.9.5 +Released 21 March 2021 for Stardew Valley 1.5.4. + +* For players: + * Added console command to reset community center bundles _(in Console Commands)_. + * Disabled aggressive memory optimization by default. + _The option was added in SMAPI 3.9.2 to reduce errors for some players, but it can cause multiplayer crashes with some mods. If you often see `OutOfMemoryException` errors, you can edit `smapi-internal/config.json` to re-enable it. We're experimenting with making Stardew Valley 64-bit to address memory issues more systematically._ + * Fixed bundles corrupted in non-English saves created after SMAPI 3.9.2. + _If you have an affected save, you can load your save and then enter the `regenerate_bundles confirm` command in the SMAPI console to fix it._ + * Internal changes to prepare for unofficial 64-bit. + +* For mod authors: + * Improved asset propagation: + * Added for interior door sprites. + * SMAPI now updates the NPC pathfinding cache when map warps are changed through the content API. + * Reduced performance impact of invalidating cached assets before a save is loaded. + * Fixed asset changes not reapplied in the edge case where you're playing in non-English, and the changes are only applied after the save is loaded, and the player returns to title and reloads a save, and the game reloads the target asset before the save is loaded. + * Added a second `KeybindList` constructor to simplify single-key default bindings. + * Added a `Constants.GameFramework` field which indicates whether the game is using XNA Framework or MonoGame. + _Note: mods don't need to handle the difference in most cases, but some players may use MonoGame on Windows in upcoming versions. Mods which check `Constants.TargetPlatform` should review usages as needed._ + +## 3.9.4 +Released 07 March 2021 for Stardew Valley 1.5.4. + +* For players: + * Fixed installer error if the `Mods` folder doesn't exist in 3.9.3. + +## 3.9.3 +Released 07 March 2021 for Stardew Valley 1.5.4. + +* For players: + * Added descriptive error if possible when a `PathTooLongException` crashes SMAPI or the installer. + * The installer window now tries to stay open if it crashed, so you can read the error and ask for help. + * Fixed console showing _found 1 mod with warnings_ with no mods listed in some cases. + +* For mod authors: + * Added three stages to the specialised [`LoadStageChanged` event](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Specialised): `CreatedInitialLocations`, `SaveAddedLocations`, and `ReturningToTitle`. + * Fixed `RewriteMods` option ignored when rewriting for OS compatibility. + * Fixed edge case when playing as a farmhand in non-English where translatable assets loaded via `IAssetLoader` weren't reapplied immediately when the server disconnects. + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.21. + +## 3.9.2 +Released 21 February 2021 for Stardew Valley 1.5.4. + +* For players: + * Added more aggressive memory optimization to reduce `OutOfMemoryException` errors with some mods. + * Improved error when `Stardew Valley.exe` exists but can't be loaded. + * Fixed error running `install on Windows.bat` in very rare cases. + * Fixed `world_settime` command not always updating outdoor ambient lighting _(in Console Commands)_. + +* For mod authors: + * Added early detection of disposed textures so the error details are more relevant _(in Error Handler)_. + * Added error details when an event command fails _(in Error Handler)_. + * Fixed asset propagation for `TileSheets/ChairTiles` not changing existing map seats. + * Fixed edge case when playing in non-English where translatable assets loaded via `IAssetLoader` would no longer be applied after returning to the title screen unless manually invalidated from the cache. + +* For the web UI: + * Updated compatibility list for the new wiki. + * Updated the JSON validator/schema for Content Patcher 1.20. + * Fixed mod compatibility list error if a mod has no name. + +* For SMAPI developers: + * Fixed SMAPI toolkit defaulting the mod type incorrectly if a mod's `manifest.json` has neither `EntryDll` nor `ContentPackFor`. This only affects external tools, since SMAPI itself validates those fields separately. + +## 3.9.1 +Released 25 January 2021 for Stardew Valley 1.5.4. + +* For players: + * Fixed _tile contains an invalid TileSheet reference_ crash after mods change certain maps. + * Fixed _patched game code_ issue shown for the bundled Error Handler mod. + +## 3.9.0 +Released 22 January 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/46553874). + +* For players: + * Updated for Stardew Valley 1.5.4. + * Improved game detection in the installer: + * The installer now prefers paths registered by Steam or GOG Galaxy. + * The installer now detects default manual GOG installs. + * Added clearer error text for empty mod folders created by Vortex. + * Fixed the game's map changes not always reapplied correctly after mods change certain maps, which caused issues like the community center resetting to its non-repaired texture. + * Fixed compatibility for very old content packs which still load maps from `.xnb` files. These were broken by map loading changes in Stardew Valley 1.5, but SMAPI now corrects them automatically. + * Fixed some broken mods incorrectly listed as XNB mods under 'skipped mods'. + +* For mod authors: + * Added new input APIs: + * Added an [API for multi-key bindings](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#KeybindList). + * Added a new [`Input.ButtonsChanged` event](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Input.ButtonsChanged). + * Added a `buttonState.IsDown()` extension. + * Added a `helper.Input.SuppressActiveKeybinds` method to suppress the active buttons in a keybind list. + * Improved multiplayer APIs: + * `PerScreen` now lets you get/set the value for any screen, get all active values, or clear all values. + * Peer data from the multiplayer API/events now includes `IsSplitScreen` and `ScreenID` fields. + * Fixed network messages through the multiplayer API being sent to players who don't have SMAPI installed in some cases. + * Improved asset propagation: + * Updated map propagation for the changes in Stardew Valley 1.5.4. + * Added propagation for some `Strings\StringsFromCSFiles` keys (mainly short day names). + * Fixed quarry bridge not fixed if the mountain map was reloaded. + * Added an option to disable rewriting mods for compatibility (thanks to Bpendragon!). This prevents older mods from loading, but bypasses a Visual Studio crash when debugging. + * Game errors shown in the chatbox are now logged. + * Moved vanilla error-handling into a new Error Handler mod. This simplifies the core SMAPI logic, and lets users disable it if needed. + +* For the Console Commands mod: + * Removed the `inf` option for `player_sethealth`, `player_setmoney`, and `player_setstamina`. You can use mods like [CJB Cheats Menu](https://www.nexusmods.com/stardewvalley/mods/4) instead for that. + +* For the Error Handler mod: + * Added a detailed message for the _Input string was not in a correct format_ error when the game fails to parse an item text description. + +* For the web UI: + * Fixed JSON validator incorrectly marking some manifest update keys as invalid. + +## 3.8.4 +Released 15 January 2021 for Stardew Valley 1.5.3 or later. + +* For players: + * Updated for Stardew Valley 1.5.3. + * Fixed issue where title screen music didn't stop after loading a save. + +* For mod authors: + * Fixed `SemanticVersion` comparisons returning wrong value in rare cases. + +## 3.8.3 +Released 08 January 2021 for Stardew Valley 1.5.2 or later. + +* For players: + * Updated for Stardew Valley 1.5.2. + * Reduced memory usage. + * You can now enter console commands for a specific screen in split-screen mode by adding `screen=ID` to the command. + * Typing `help` in the SMAPI console is now more helpful. + +* For mod authors: + * Simplified tilesheet order warning added in SMAPI 3.8.2. + +* For the Console Commands mod: + * Removed experimental `performance` command. Unfortunately this impacted SMAPI's memory usage and performance, and the data was often misinterpreted. This may be replaced with more automatic performance alerts in a future version. + +## 3.8.2 +Released 03 January 2021 for Stardew Valley 1.5.1 or later. + +* For players: + * SMAPI now blocks farm map replacements that would crash the game in Stardew Valley 1.5. + * On Linux, the SMAPI installer now auto-detects Flatpak Steam paths. + * Updated compatibility list. + * Fixed errors when multiple players join in split-screen mode. + * Fixed 'skipped mods' section repeating mods in some cases. + * Fixed out-of-date error text. + +* For mod authors: + * Added warning when a map replacement changes the order/IDs of the original tilesheets, which may cause errors and crashes. Doing so for a farm map is blocked outright since that causes a consistent crash in Stardew Valley 1.5. + * Message data from the `ModMessageReceived` event now uses the same serializer settings as the rest of SMAPI. That mainly adds support for sending crossplatform `Color`, `Point`, `Vector2`, `Rectangle`, and `SemanticVersion` fields through network messages. + * When a mod is blocked by SMAPI's compatibility override list, the `TRACE` messages while loading it now say so and indicate why. + * Fixed how the input API handles UI scaling. This mainly affects `ICursorPosition` values returned by the API; see [the wiki docs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#ICursorPosition) for how to account for UI scaling. + +## 3.8.1 +Released 26 December 2020 for Stardew Valley 1.5.1 or later. + +* For players: + * Fixed broken community center bundles for non-English saves created in Stardew Valley 1.5. Affected saves will be fixed automatically on load. + +* For mod authors: + * World events are now raised for volcano dungeon levels. + * Added `apply_save_fix` command to reapply a save migration in exceptional cases. This should be used very carefully. Type `help apply_save_fix` for details. + * **Deprecation notice:** the `Helper.ConsoleCommands.Trigger` method is now deprecated and should no longer be used. See [integration APIs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations) for better mod integration options. It will eventually be removed in SMAPI 4.0. + +For the web UI: + * Fixed edge cases in SMAPI log parsing. + +## 3.8.0 +Released 21 December 2020 for Stardew Valley 1.5 or later. See [release highlights](https://www.patreon.com/posts/45294737). + +* For players: + * Updated for Stardew Valley 1.5, including split-screen support. + * You can now run the installer from a subfolder of your game folder to auto-detect it. That simplifies installation if you have multiple copies of the game or it can't otherwise auto-detect the game path. + * Clarified error when the SMAPI installer is in the `Mods` folder. + +* For mod authors: + * Added `PerScreen` utility and new `Context` fields to simplify split-screen support in mods. + * Added screen ID to log when playing in split-screen mode. + +* For the Console Commands mod: + * Added `furniture` option to `world_clear`. + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.19. + +## 3.7.6 +Released 21 November 2020 for Stardew Valley 1.4.1 or later. + +* For players: + * Fixed error when heuristically rewriting an outdated mod in rare cases. + * Fixed rare 'collection was modified' error when using `harmony summary` console command. + +* For mod authors: + * Updated TMXTile to 1.5.8 to fix exported `.tmx` files losing tile index properties. + +* For the Console Commands mod: + * `player_add` can now spawn shirts normally only available during character customization. + * `player_add` now applies fish pond rules for roe items. (That mainly adds Clam Roe, Sea Urchin Roe, and custom roe from mods.) + +## 3.7.5 +Released 16 October 2020 for Stardew Valley 1.4.1 or later. + +* For mod authors: + * Fixed changes to the town map asset not reapplying the game's community center, JojaMart, and Pam house changes. + +## 3.7.4 +Released 03 October 2020 for Stardew Valley 1.4.1 or later. + +* For players: + * Improved performance on some older computers (thanks to millerscout!). + * Fixed update alerts for Chucklefish forum mods broken by a recent site change. + +* For mod authors: + * Updated dependencies (including Mono.Cecil 0.11.2 → 0.11.3 and Platonymous.TMXTile 1.3.8 → 1.5.6). + * Fixed asset propagation for `Data\MoviesReactions`. + * Fixed error in content pack path handling when you pass a null path. + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.18. + +* For SMAPI developers: + * Simplified preparing a mod build config package release. + +## 3.7.3 +Released 16 September 2020 for Stardew Valley 1.4.1 or later. + +* For players: + * Fixed errors on Linux/macOS due to content packs with incorrect filename case. + * Fixed map rendering crash due to conflict between SMAPI and PyTK. + * Fixed error in heuristically-rewritten mods in rare cases (thanks to collaboration with ZaneYork!). + +* For mod authors: + * File paths accessed through `IContentPack` are now case-insensitive (even on Linux). + +* For the web UI: + * You can now renew the expiry for an uploaded JSON/log file if you need it longer. + +## 3.7.2 +Released 08 September 2020 for Stardew Valley 1.4.1 or later. + +* For players: + * Fixed mod recipe changes not always applied in 3.7. + +* For mod authors: + * Renamed `PathUtilities.NormalizePathSeparators` to `NormalizePath`, and added normalization for more cases. + +## 3.7.1 +Released 08 September 2020 for Stardew Valley 1.4.1 or later. + +* For players: + * Fixed input-handling bugs in 3.7. + +## 3.7.0 +Released 07 September 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/41341767). + +* For players: + * Added heuristic compatibility rewrites. (This improves mod compatibility with Android and future game updates.) + * Tweaked the rules for showing update alerts (see _for SMAPI developers_ below for details). + * Simplified the error shown for duplicate mods. + * Fixed crossplatform compatibility for mods which use the `[HarmonyPatch(type)]` attribute (thanks to spacechase0!). + * Fixed map tile rotation broken when you return to the title screen and reload a save. + * Fixed broken URL in update alerts for unofficial versions. + * Fixed rare error when a mod adds/removes event handlers asynchronously. + * Fixed rare issue where the console showed incorrect colors when mods wrote to it asynchronously. + * Fixed SMAPI not always detecting broken field references in mod code. + * Removed the experimental `RewriteInParallel` option added in SMAPI 3.6 (it was already disabled by default). Unfortunately this caused intermittent and unpredictable errors when enabled. + * Internal changes to prepare for upcoming game updates. + +* For mod authors: + * Added `PathUtilities` to simplify working with file/asset names. + * You can now read/write `SDate` values to JSON (e.g. for `config.json`, network mod messages, etc). + * Fixed asset propagation not updating title menu buttons immediately on Linux/macOS. + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.16 and 1.17. + +* For SMAPI developers: + * The web API now returns an update alert in two new cases: any newer unofficial update (previously only shown if the mod was incompatible), and a newer prerelease version if the installed non-prerelease version is broken (previously only shown if the installed version was prerelease). + * Reorganised the SMAPI core to reduce coupling to game types like `Game1`, make it easier to navigate, and simplify future game updates. + * SMAPI now automatically fixes code broken by these changes in game code, so manual rewriters are no longer needed: + * reference to a method with new optional parameters; + * reference to a field replaced by a property; + * reference to a field replaced by a `const` field. + * `FieldReplaceRewriter` now supports mapping to a different target type. + +## 3.6.2 +Released 02 August 2020 for Stardew Valley 1.4.1 or later. + +* For players: + * Improved compatibility with some Linux terminals (thanks to jlaw and Spatterjaaay!). + * Fixed rare error when a mod adds/removes an event handler from an event handler. + * Fixed string sorting/comparison for some special characters. + +* For the Console Commands mod: + * Fixed error opening menu when some item data is invalid. + * Fixed spawned Floor TV not functional as a TV (thanks to Platonymous!). + * Fixed spawned sturgeon roe having incorrect color. + +* For mod authors: + * Updated internal dependencies. + * SMAPI now ignores more file types when scanning for mod folders (`.doc`, `.docx`, `.rar`, and `.zip`). + * Added current GPU to trace logs to simplify troubleshooting. + +## 3.6.1 +Released 21 June 2020 for Stardew Valley 1.4.1 or later. + +* Fixed event priority sorting. + +## 3.6.0 +Released 20 June 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/38441800). + +* For players: + * Added crossplatform compatibility for mods which use the `[HarmonyPatch(type)]` attribute. + * Added experimental option to reduce startup time when loading mod DLLs (thanks to ZaneYork!). Enable `RewriteInParallel` in the `smapi-internal/config.json` to try it. + * Reduced processing time when a mod loads many unpacked images (thanks to Entoarox!). + * Mod load warnings are now listed alphabetically. + * macOS files starting with `._` are now ignored and can no longer cause skipped mods. + * Simplified paranoid warning logs and reduced their log level. + * Fixed black maps on Android for mods which use `.tmx` files. + * Fixed `BadImageFormatException` error detection. + * Fixed `reload_i18n` command not reloading content pack translations. + +* For the web UI: + * Added GitHub licenses to mod compatibility list. + * Improved JSON validator: + * added SMAPI `i18n` schema; + * editing an uploaded file now remembers the selected schema; + * changed default schema to plain JSON. + * Updated ModDrop URLs. + * Internal changes to improve performance and reliability. + +* For mod authors: + * Added [event priorities](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Custom_priority) (thanks to spacechase0!). + * Added [update subkeys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Update_checks#Update_subkeys). + * Added [a custom build of Harmony](https://github.com/Pathoschild/Harmony#readme) to provide more useful stack traces in error logs. + * Added `harmony_summary` console command to list or search current Harmony patches. + * Added `Multiplayer.PeerConnected` event. + * Added support for overriding update keys from the wiki compatibility list. + * Improved mod rewriting for compatibility to support more cases (e.g. custom attributes and generic types). + * Fixed `helper.Reflection` blocking access to game methods/properties intercepted by SMAPI. + * Fixed asset propagation for Gil's portraits. + * Fixed `.pdb` files ignored for error stack traces when mods are rewritten by SMAPI. + * Fixed `ModMessageReceived` event handlers not tracked for performance monitoring. + +* For SMAPI developers: + * Eliminated MongoDB storage in the web services, which complicated the code unnecessarily. The app still uses an abstract interface for storage, so we can wrap a distributed cache in the future if needed. + * Overhauled update checks to simplify mod site integrations, centralize common logic, and enable upcoming features. + * Merged the separate legacy redirects app on AWS into the main app on Azure. + * Changed SMAPI's Harmony ID from `io.smapi` to `SMAPI` for readability in Harmony summaries. + +## 3.5.0 +Released 27 April 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/36471055). + +* For players: + * SMAPI now prevents more game errors due to broken items, so you no longer need save editing to remove them. + * Added option to disable console colors. + * Updated compatibility list. + * Improved translations.¹ + +* For the Console Commands mod: + * Commands like `world_setday` now also affect the 'days played' stat, so in-game events/randomization match what you'd get if you played to that date normally (thanks to kdau!). + +* For the web UI: + * Updated the JSON validator/schema for Content Patcher 1.13. + * Fixed rare intermittent "CGI application encountered an error" errors. + +* For mod authors: + * Added map patching to the content API (via `asset.AsMap()`). + * Added support for using patch helpers with arbitrary data (via `helper.Content.GetPatchHelper`). + * Added `SDate` fields/methods: `SeasonIndex`, `FromDaysSinceStart`, `FromWorldDate`, `ToWorldDate`, and `ToLocaleString` (thanks to kdau!). + * Added `SDate` translations taken from the Lookup Anything mod.¹ + * Fixed asset propagation for certain maps loaded through temporary content managers. This notably fixes unreliable patches to the farmhouse and town maps. + * Fixed asset propagation on Linux/macOS for monster sprites, NPC dialogue, and NPC schedules. + * Fixed asset propagation for NPC dialogue sometimes causing a spouse to skip marriage dialogue or not allow kisses. + +¹ Date format translations were taken from the Lookup Anything mod; thanks to translators FixThisPlz (improved Russian), LeecanIt (added Italian), pomepome (added Japanese), S2SKY (added Korean), Sasara (added German), SteaNN (added Russian), ThomasGabrielDelavault (added Spanish), VincentRoth (added French), Yllelder (improved Spanish), and yuwenlan (added Chinese). Some translations for Korean, Hungarian, and Turkish were derived from the game translations. + +## 3.4.1 +Released 24 March 2020 for Stardew Valley 1.4.1 or later. + +* For mod authors: + * Asset changes now propagate to NPCs in an event (e.g. wedding sprites). + * Fixed mouse input suppression not working in SMAPI 3.4. + +## 3.4.0 +Released 22 March 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/35161371). + +* For players: + * Fixed semi-transparency issues on Linux/macOS in recent versions of Mono (e.g. pink shadows). + * Fixed `player_add` command error if you have broken XNB mods. + * Removed invalid-location check now handled by the game. + * Updated translations. Thanks to Annosz (added Hungarian)! + +* For mod authors: + * Added support for flipped and rotated map tiles (thanks to collaboration with Platonymous!). + * Added support for `.tmx` maps using zlib compression (thanks to Platonymous!). + * Added `this.Monitor.LogOnce` method. + * Mods are no longer prevented from suppressing key presses in the chatbox. + +* For the web UI: + * Added option to upload files using a file picker. + * Optimized log parser for very long multi-line log messages. + * Fixed log parser not detecting folder path in recent versions of SMAPI. + +* For SMAPI developers: + * Added internal API to send custom input to the game/mods. This is mainly meant to support Virtual Keyboard on Android, but might be exposed as a public API in future versions. + +## 3.3.2 +Released 22 February 2020 for Stardew Valley 1.4.1 or later. + +* Fixed mods receiving their own message broadcasts. + +## 3.3.1 +Released 22 February 2020 for Stardew Valley 1.4.1 or later. + +* Fixed errors with custom spouse room mods in SMAPI 3.3. + +## 3.3.0 +Released 22 February 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/34248719). + +* For players: + * Improved performance for mods which load many images. + * Reduced network traffic for mod broadcasts to players who can't process them. + * Fixed update-check errors for recent versions of SMAPI on Android. + * Updated draw logic to match recent game updates. + * Updated compatibility list. + * Updated SMAPI/game version map. + * Updated translations. Thanks to xCarloC (added Italian)! + +* For the Save Backup mod: + * Fixed warning on macOS when you have no saves yet. + * Reduced log messages. + +* For the web UI: + * Updated the JSON validator and Content Patcher schema for `.tmx` support. + * The mod compatibility page now has a sticky table header. + +* For mod authors: + * Added support for [message sending](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Message_sending) to mods on the current computer (in addition to remote computers). + * Added `ExtendImage` method to content API when editing files to resize textures. + * Added `helper.Input.GetState` to get the low-level state of a button. + * **[Breaking change]** Map tilesheets are no loaded from `Content` if they can't be found in `Content/Maps`. This reflects an upcoming change in the game to delete duplicate map tilesheets under `Content`. Most mods should be unaffected. + * Improved map tilesheet errors so they provide more info. + * When mods load an asset using a more general type like `content.Load`, SMAPI now calls `IAssetEditor` instances with the actual asset type instead of the specified one. + * Updated dependencies (including Mono.Cecil 0.11.1 → 0.11.2). + * Fixed dialogue propagation clearing marriage dialogue. + +* For SMAPI/tool developers: + * Improved support for four-part versions to support SMAPI on Android. + * The SMAPI log now prefixes the OS name with `Android` on Android. + +## 3.2.0 +Released 01 February 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/33659728). + +* For players: + * SMAPI now prevents crashes due to invalid schedule data. + * SMAPI now prevents crashes due to invalid building types. + * Added support for persistent `smapi-internal/config.json` overrides (see info in the file). + * Updated minimum game version (1.4 → 1.4.1). + * Fixed 'collection was modified' error when returning to title in rare cases. + * Fixed error when update-checking a mod with a Chucklefish page that has no version. + * Fixed rare error when building/demolishing buildings. + * Fixed SMAPI beta versions not showing update alert on next launch (thanks to danvolchek!). + +* For the Console Commands mod: + * Added `performance` command to track mod performance metrics. This is an advanced experimental feature. (Thanks to Drachenkätzchen!) + * Added `test_input` command to view button codes in the console. + +* For the Save Backup mod: + * Fixed extra files under `Saves` (e.g. manual backups) not being ignored. + * Fixed Android issue where game files were backed up. + +* For mod authors: + * Added support for `.tmx` map files. (Thanks to [Platonymous for the underlying library](https://github.com/Platonymous/TMXTile)!) + * Added special handling for `Vector2` values in `.json` files, so they work consistently crossplatform. + * Reworked the order that asset editors/loaders are called between multiple mods to support some framework mods like Content Patcher and Json Assets. Note that the order is undefined and should not be depended on. + * Fixed incorrect warning about mods adding invalid schedules in some cases. The validation was unreliable, and has been removed. + * Fixed asset propagation not updating other players' sprites. + * Fixed asset propagation for player sprites not updating recolor maps (e.g. sleeves). + * Fixed asset propagation for marriage dialogue. + * Fixed dialogue asset changes not correctly propagated until the next day. + * Fixed `helper.Data.Read`/`WriteGlobalData` using the `Saves` folder instead of the game's appdata folder. The installer will move existing folders automatically. + * Fixed issue where a mod which implemented `IAssetEditor`/`IAssetLoader` on its entry class could then remove itself from the editor/loader list. + +* For SMAPI/tool developers: + * Added internal performance monitoring (thanks to Drachenkätzchen!). This is disabled by default in the current version, but can be enabled using the `performance` console command. + * Added internal support for four-part versions to support SMAPI on Android. + * Rewrote `SemanticVersion` parsing. + * Updated links for the new r/SMAPI subreddit. + * The `/mods` web API endpoint now includes version mappings from the wiki. + * Dropped API support for the pre-3.0 update-check format. + +## 3.1.0 +Released 05 January 2019 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/32904041). + +* For players: + * Added separate group in 'skipped mods' list for broken dependencies, so it's easier to see what to fix first. + * Added friendly log message for save file-not-found errors. + * Updated for gamepad modes in Stardew Valley 1.4.1. + * Improved performance in some cases. + * Fixed compatibility with Linux Mint 18 (thanks to techge!), Arch Linux, and Linux systems with libhybris-utils installed. + * Fixed memory leak when repeatedly loading a save and returning to title. + * Fixed memory leak when mods reload assets. + * Updated translations. Thanks to L30Bola (added Portuguese), PlussRolf (added Spanish), and shirutan (added Japanese)! + +* For the Console Commands mod: + * Added new clothing items. + * Fixed spawning new flooring and rings (thanks to Mizzion!). + * Fixed spawning custom rings added by mods. + * Fixed errors when some item data is invalid. + +* For the web UI: + * Added option to edit & reupload in the JSON validator. + * File uploads are now stored in Azure storage instead of Pastebin, due to ongoing Pastebin perfomance issues. + * File uploads now expire after one month. + * Updated the JSON validator for Content Patcher 1.10 and 1.11. + * Fixed JSON validator no longer letting you change format when viewing a file. + * Fixed JSON validator for Content Patcher not requiring `Default` if `AllowBlank` was omitted. + * Fixed log parser not correctly handling content packs with no author (thanks to danvolchek!). + * Fixed main sidebar link pointing to wiki instead of home page. + +* For mod authors: + * Added `World.ChestInventoryChanged` event (thanks to collaboration with wartech0!). + * Added asset propagation for... + * grass textures; + * winter flooring textures; + * `Data\Bundles` changes (for added bundles only); + * `Characters\Farmer\farmer_girl_base_bald`. + * Added paranoid-mode warning for direct `Console` access. + * Improved error messages for `TargetParameterCountException` when using the reflection API. + * `helper.Read/WriteSaveData` can now be used while a save is being loaded (e.g. within a `Specialized.LoadStageChanged` event). + * Removed `DumpMetadata` option. It was only for specific debugging cases, but players would sometimes enable it incorrectly and then report crashes. + * Fixed private textures loaded from content packs not having their `Name` field set. + +* For SMAPI developers: + * You can now run local environments without configuring Amazon, Azure, MongoDB, and Pastebin accounts. + +## 3.0.1 +Released 02 December 2019 for Stardew Valley 1.4 or later. + +* For players: + * Updated for Stardew Valley 1.4.0.1. + * Improved compatibility with some Linux terminals (thanks to archification and DanielHeath!). + * Updated translations. Thanks to berkayylmao (added Turkish), feathershine (added Chinese), and Osiris901 (added Russian)! + +* For the web UI: + * Rebuilt web infrastructure to handle higher traffic. + * If a log can't be uploaded to Pastebin (e.g. due to rate limits), it's now uploaded to Amazon S3 instead. Logs uploaded to S3 expire after one month. + * Fixed JSON validator not letting you drag & drop a file. + +* For mod authors: + * `SemanticVersion` now supports [semver 2.0](https://semver.org/) build metadata. + +## 3.0.0 +Released 26 November 2019 for Stardew Valley 1.4. + +### Release highlights +For players: +* **Updated for Stardew Valley 1.4.** + SMAPI 3.0 adds compatibility with the latest game version, and improves mod APIs for changes in + the game code. + +* **Improved performance.** + SMAPI should have less impact on game performance and startup time for some players. + +* **Automatic save fixing and more error recovery.** + SMAPI now detects and prevents more crashes due to game/mod bugs, and automatically fixes your + save if you remove some custom-content mods. + +* **Improved mod scanning.** + SMAPI now supports some non-standard mod structures automatically, improves compatibility with + the Vortex mod manager, and improves various error/skip messages related to mod loading. + +* **Overhauled update checks.** + SMAPI update checks are now handled entirely on the web server and support community-defined + version mappings. In particular, false update alerts due to author mistakes can now be solved by + the community for all players. + +* **Fixed many bugs and edge cases.** + +For mod authors: +* **New event system.** + SMAPI 3.0 removes the deprecated static events in favor of the new `helper.Events` API. The event + engine is rewritten to make events more efficient, add events that weren't possible before, make + existing events more useful, and make event usage and behavior more consistent. When a mod makes + changes in an event handler, those changes are now also reflected in the next event raise. + +* **Improved mod build package.** + The [mod build package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig) now + includes the `assets` folder by default if present, supports the new `.csproj` project format, + enables mod `.pdb` files automatically (to provide line numbers in error messages), adds optional + Harmony support, and fixes some bugs and edge cases. This also adds compatibility with SMAPI 3.0 + and Stardew Valley 1.4, and drops support for older versions. + +* **Mods loaded earlier.** + SMAPI now loads mods much earlier, before the game is initialised. That lets mods do things that + were difficult before, like intercepting some core assets. + +* **Improved Android support.** + SMAPI now automatically detects when it's running on Android, and updates `Constants.TargetPlatform` + so mods can adjust their logic if needed. The Save Backup mod is also now Android-compatible. + +* **Improved asset propagation.** + SMAPI now automatically propagates asset changes for farm animal data, NPC default location data, + critter textures, and `DayTimeMoneyBox` buttons. Every loaded texture now also has a `Name` field + so mods can check which asset a texture was loaded for. + +* **Breaking changes:** + See _[migrate to SMAPI 3.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0)_ and + _[migrate to Stardew Valley 1.4](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.4)_ + for more info. + +### For players +* Changes: + * Updated for Stardew Valley 1.4. + * Improved performance. + * Reworked update checks and added community-defined version mapping, to reduce false update alerts due to author mistakes. + * SMAPI now removes invalid locations/NPCs when loading a save to prevent crashes. A warning is shown in-game when this happens. + * Added update checks for CurseForge mods. + * Added support for editing console colors via `smapi-internal/config.json` (for players with unusual consoles). + * Added support for setting SMAPI CLI arguments as environment variables for Linux/macOS compatibility. + * Improved mod scanning: + * Now ignores metadata files/folders (like `__MACOSX` and `__folder_managed_by_vortex`) and content files (like `.txt` or `.png`), which avoids missing-manifest errors in some cases. + * Now detects XNB mods more accurately, and consolidates multi-folder XNB mods in logged messages. + * Improved launch script compatibility on Linux (thanks to kurumushi and toastal!). + * Made error messages more user-friendly in some cases. + * Save Backup now works in the background, to avoid affecting startup time for players with a large number of saves. + * The installer now recognises custom game paths stored in [`stardewvalley.targets`](http://smapi.io/package/custom-game-path). + * Duplicate-mod errors now show the mod version in each folder. + * Update checks are now faster in some cases. + * Updated mod compatibility list. + * Updated SMAPI/game version map. + * Updated translations. Thanks to eren-kemer (added German)! +* Fixes: + * Fixed some assets not updated when you switch language to English. + * Fixed lag in some cases due to incorrect asset caching when playing in non-English. + * Fixed lag when a mod invalidates many NPC portraits/sprites at once. + * Fixed Console Commands not including upgraded tools in item commands. + * Fixed Console Commands' item commands failing if a mod adds invalid item data. + * Fixed Save Backup not pruning old backups if they're uncompressed. + * Fixed issues when a farmhand reconnects before the game notices they're disconnected. + * Fixed 'received message' logs shown in non-developer mode. + * Fixed various error messages and inconsistent spelling. + * Fixed update-check error if a Nexus mod is marked as adult content. + * Fixed update-check error if the Chucklefish page for an update key doesn't exist. + +### For the web UI +* Mod compatibility list: + * Added support for CurseForge mods. + * Added metadata links and dev notes (if any) to advanced info. + * Now loads faster (since data is fetched in a background service). + * Now continues working with cached data when the wiki is offline. + * Clicking a mod link now automatically adds it to the visible mods if the list is filtered. + +* JSON validator: + * Added JSON validator at [smapi.io/json](https://smapi.io/json), which lets you validate a JSON file against predefined mod formats. + * Added support for the `manifest.json` format. + * Added support for the Content Patcher format (thanks to TehPers!). + * Added support for referencing a schema in a JSON Schema-compatible text editor. + +* For the log parser: + * Added instructions for Android. + * The page now detects your OS and preselects the right instructions (thanks to danvolchek!). + +### For mod authors +* Breaking changes: + * Mods are now loaded much earlier in the game launch. This lets mods intercept any content asset, but the game is not fully initialized when `Entry` is called; use the `GameLaunched` event if you need to run code when the game is initialized. + * Removed all deprecated APIs. + * Removed unused APIs: `Monitor.ExitGameImmediately`, `Translation.ModName`, and `Translation.Assert`. + * Fixed `ICursorPosition.AbsolutePixels` not adjusted for zoom. + * `SemanticVersion` no longer omits `.0` patch numbers when formatting versions, for better [semver](https://semver.org/) conformity (e.g. `3.0` is now formatted as `3.0.0`). +* Changes: + * Added support for content pack translations. + * Added `IContentPack.HasFile`, `Context.IsGameLaunched`, and `SemanticVersion.TryParse`. + * Added separate `LogNetworkTraffic` option to make verbose logging less overwhelmingly verbose. + * Added asset propagation for `Data\FarmAnimals`, critter textures, and `DayTimeMoneyBox` buttons. + * Added `Texture2D.Name` values set to the asset key. + * Added trace logs for skipped loose files in the `Mods` folder and custom SMAPI settings so it's easier to troubleshoot player logs. + * `Constants.TargetPlatform` now returns `Android` when playing on an Android device. + * Trace logs for a broken mod now list all detected issues (instead of the first one). + * Trace logs when loading mods are now more clear. + * Clarified update-check errors for mods with multiple update keys. + * Updated dependencies (including Json.NET 11.0.2 → 12.0.3 and Mono.Cecil 0.10.1 → 0.11.1). +* Fixes: + * Fixed map reloads resetting tilesheet seasons. + * Fixed map reloads not updating door warps. + * Fixed outdoor tilesheets being seasonalised when added to an indoor location. + * Fixed mods needing to load custom `Map` assets before the game accesses them. SMAPI now does so automatically. + * Fixed custom maps loaded from `.xnb` files not having their tilesheet paths automatically adjusted. + * Fixed custom maps loaded from the mod folder with tilesheets in a subfolder not working crossplatform. All tilesheet paths are now normalized for the OS automatically. + * Fixed issue where mod changes weren't tracked correctly for raising events in some cases. Events now reflect a frozen snapshot of the game state, and any mod changes are reflected in the next event tick. + * Fixed issue where, when a mod's `IAssetEditor` uses `asset.ReplaceWith` on a texture asset while playing in non-English, any changes from that point forth wouldn't affect subsequent cached asset loads. + * Fixed asset propagation for NPC portraits resetting any unique portraits (e.g. Maru's hospital portrait) to the default. + * Fixed changes to `Data\NPCDispositions` not always propagated correctly to existing NPCs. + * Fixed `Rendering`/`Rendered` events not raised during minigames. + * Fixed `LoadStageChanged` event not raising correct flags in some cases when creating a new save. + * Fixed `GetApi` without an interface not checking if all mods are loaded. + +### For SMAPI maintainers +* Added support for core translation files. +* Migrated to new `.csproj` format. +* Internal refactoring. + ## 2.11.3 Released 13 September 2019 for Stardew Valley 1.3.36. diff --git a/docs/release-notes.md b/docs/release-notes.md index 8c732746..c4331501 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -430,1349 +430,5 @@ Released 19 March 2024 for Stardew Valley 1.6.0 or later. See [release highlight * For SMAPI developers: * Added `LogTechnicalDetailsForBrokenMods` option in `smapi-internal/config.json`, which adds more technical info to the SMAPI log when a mod is broken. This is mainly useful for creating compatibility rewriters. -## 3.18.6 -Released 05 October 2023 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed SpriteMaster compatibility in SMAPI 3.18.5+ with temporary workaround. - * Fixed `player_add` and `list_items` console commands not including Pickled Ginger, and returning Honey instead of Wild Honey _(in Console Commands)_. - -* For mod authors: - * Added asset propagation for `LooseSprites/chatBox` and `LooseSprites/emojis`. - -* For the web UI: - * Fixed uploaded log/JSON file expiry alway shown as renewed. - -## 3.18.5 -Released 26 August 2023 for Stardew Valley 1.5.6 or later. - -* For players: - * Reduced startup time when many mods are rewritten for compatibility. - * Fixed app icon on Linux/macOS, and for some players on Windows (thanks to Datrio!). - * Fixed error if you copy a null field into `config.user.json`. - * Fixed installer creating a "null" file in its folder. - * Fixed installer moving bundled mods back to their default location on update. It now correctly updates their existing folder instead. - -* For mod authors: - * Updated dependencies, including [Mono.Cecil](https://github.com/jbevain/cecil) 0.11.4 → 0.11.5 (see [changes](https://github.com/jbevain/cecil/releases/tag/0.11.5)). - * Fixed NPC warp cache not updated when a map edit changes door warps (thanks to atravita!). - -* For the web UI: - * Viewing an uploaded log/JSON file within 15 days of expiry now auto-renews it, to allow for discussions that last longer than the default 30-day expiry. - * Fixed log parser's summary skipping mods if some have no description. - * Fixed log parser no longer ignoring Error Handler in newer SMAPI-on-Android versions (thanks to AnotherPillow!). - -* For SMAPI developers: - * Overhauled compatibility rewriters. - _This allows simpler, more robust, and more flexible crossplatform rewrites (e.g. on Android) and prepares for the upcoming Stardew Valley 1.6. See remarks on the new `ReplaceReferencesRewriter` for more info._ - -## 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. - -* For players: - * Fixed `findstr` installer error for some players. - * 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. Thanks to stylemate (updated Korean)! - -* For mod authors: - * Added `IsActiveForScreen()` method to `PerScreen`. - * Updated to [FluentHttpClient](https://github.com/Pathoschild/FluentHttpClient#readme) 4.3.0 (see [changes](https://github.com/Pathoschild/FluentHttpClient/blob/develop/RELEASE-NOTES.md#430)). - * Adjusted `ModContentManager.HandleUnknownFileType` to let mods patch it. - * Fixed `Context.IsWorldReady` being editable by mods. - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.29.0. - -## 3.18.2 -Released 09 January 2023 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed empty save backups for some macOS players. - * Fixed `player_add` console command not handling custom slingshots correctly (thanks to DaLion!). - -* For mod authors: - * Added `DelegatingModHooks` utility for mods which need to override SMAPI's mod hooks directly. - * Updated to Newtonsoft.Json 13.0.2 (see [changes](https://github.com/JamesNK/Newtonsoft.Json/releases/tag/13.0.2)) and Pintail 2.2.2 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#222)). - -## 3.18.1 -Released 01 December 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed mod texture edits sometimes cut off (thanks to atravita!). - -* For the web UI: - * The log parser no longer warns about missing Error Handler on Android, where it doesn't exist yet (thanks to AnotherPillow!). - -## 3.18.0 -Released 12 November 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/74565278). - -* For players: - * You can now override the mod load order in `smapi-internal/config.json` (thanks to Shockah!). - * You can now disable console input in `smapi-internal/config.json`, which may reduce CPU usage on some Linux systems. - * Fixed map edits not always applied for farmhands in multiplayer (thanks to SinZ163!). - * Internal changes to prepare for the upcoming Stardew Valley 1.6 and SMAPI 4.0. - -* For mod authors: - * Optimized asset name comparisons (thanks to atravita!). - * Raised all deprecation messages to the 'pending removal' level. - * **This is the last major update before SMAPI 4.0.0, which will drop all deprecated APIs.** If you haven't [fixed deprecation warnings in your mod code](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0) (if any), you should do it soon. SMAPI 4.0.0 will release alongside the upcoming Stardew Valley 1.6. - -* For the web UI: - * The log parser now detects split-screen mode and shows which screen logged each message. - -## 3.17.2 -Released 21 October 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed installer crash if Steam's library data is invalid or in an old format; it'll now be ignored instead. -* For mod authors: - * Fixed image patches sometimes applied one pixel higher than expected after 3.17.0 (thanks to atravita!). - -## 3.17.1 -Released 10 October 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed installer error on Windows if the Steam library folder exists but doesn't contain Steam's `.vdf` library data file. - -## 3.17.0 -Released 09 October 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/73090322). - -* For players: - * You can now download SMAPI 'strict mode' from [Nexus files](https://www.nexusmods.com/stardewvalley/mods/2400/?tab=files), which removes all deprecated APIs. This may significantly improve performance, but mods which still show deprecation warnings won't work. - * The SMAPI installer now also detects game folders in Steam's `.vdf` library data on Windows (thanks to pizzaoverhead!). - * SMAPI now prevents mods from enabling Harmony debug mode, which impacts performance and creates a file on your desktop. - _You can allow debug mode by editing `smapi-internal/config.json` in your game folder._ - * Optimized performance and memory usage (thanks to atravita!). - * Other internal optimizations. - * Added more file extensions to ignore when searching for mod folders: `.7z`, `.tar`, `.tar.gz`, and `.xcf` (thanks to atravita!). - * Removed transitional `UseRawImageLoading` option added in 3.15.0. This is now always enabled, except when PyTK is installed. - * Fixed update alerts incorrectly shown for prerelease versions on GitHub that aren't marked as prerelease. - -* For mod authors: - * When [providing a mod API in a C# mod](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations), you can now get the mod requesting it as an optional parameter (thanks to KhloeLeclair!). - * SMAPI now treats square brackets in the manifest `Name` field as round ones to avoid breaking tools which parse log files. - * Made deprecation message wording stronger for the upcoming SMAPI 4.0.0 release. - * The `Texture2D.Name` field is now set earlier to support mods like SpriteMaster. - * Updated dependencies: [Harmony](https://harmony.pardeike.net) 2.2.2 (see [changes](https://github.com/pardeike/Harmony/releases/tag/v2.2.2.0)) and [FluentHttpClient](https://github.com/Pathoschild/FluentHttpClient#readme) 4.2.0 (see [changes](https://github.com/Pathoschild/FluentHttpClient/blob/develop/RELEASE-NOTES.md#420)). - * Fixed `LocationListChanged` event not raised & memory leak occurring when a generated mine/volcano is removed (thanks to tylergibbs2!). - -## 3.16.2 -Released 31 August 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed `NoSuitableGraphicsDeviceException` launch error for some players with compatible GPUs since 3.16.0. - -## 3.16.1 -Released 29 August 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Updated PyTK compatibility mode for the latest PyTK version. - * Fixed broken mods sometimes incorrectly listed as duplicate. - -## 3.16.0 -Released 22 August 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/70797008). - -* For players: - * Added error message if mod files are detected directly under `Mods` (instead of each mod having its own subfolder). - * SMAPI now sets a success/error code when the game exits. - _This is used by your OS (like Windows) to decide whether to keep the console window open when the game ends._ - * Fixed SMAPI on Windows applying different DPI awareness settings than the game (thanks to spacechase0!). - * Fixed Linux/macOS installer's color scheme question partly unreadable if the terminal background is dark. - * Fixed error message when a mod loads an invalid PNG file (thanks to atravita!). - * Fixed error message when a mod is duplicated, but one of the copies is also missing the DLL file. This now shows the duplicate-mod message instead of the missing-DLL message. - * Fixed macOS launcher using Terminal regardless of the system's default terminal (thanks to ishan!). - * Fixed best practices in Linux/macOS launcher scripts (thanks to ishan!). - * Improved translations. Thanks to KediDili (updated Turkish)! - -* For mod authors: - * While loading your mod, SMAPI now searches for indirect dependencies in your mod's folder (thanks to TehPers)! This mainly enables F# mods. - * **Raised deprecation message levels.** - _Deprecation warnings are now player-visible in the SMAPI console as faded `DEBUG` messages._ - * Updated to Pintail 2.2.1 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#221)). - * Switched SMAPI's `.pdb` files to the newer 'portable' format. This has no effect on mods. - -* For the web UI: - * Added log parser warning about performance of PyTK 1.23.0 or earlier. - * Converted images to SVG (thanks to ishan!). - * Updated log parser for the new update alert format in SMAPI 3.15.1. - * Updated the JSON validator/schema for Content Patcher 1.28.0. - * Fixed log parsing for invalid content packs. - * Fixed log parsing if a mod logged a null character. - -## 3.15.1 -Released 06 July 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Added current version to update alerts (thanks to ishan!). - * Fixed lag for some players since Stardew Valley 1.5.5. - * Fixed `smapi-internal/config.user.json` overrides not applied after SMAPI 3.14.0. - * Fixed PyTK not rescaling images correctly in some cases. - _When PyTK 1.23.0 or earlier is installed, this will disable the main performance improvements in SMAPI 3.15.0._ - * Updated compatibility list. - -* For mod authors: - * The [FluentHttpClient package](https://github.com/Pathoschild/FluentHttpClient#readme) is now loaded by SMAPI. - * Fixed `TRACE` logs not tracking reloaded map tilesheets as a propagated asset. - -* For the web UI: - * Added log parser suggested fix for missing/outdated Error Handler, and improved visual styles. - * Updated the JSON validator/schema for Content Patcher 1.27.0. - * Fixed the mod count in the log parser metadata. - -## 3.15.0 -Released 17 June 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/67877219). - -* For players: - * Optimized mod image file loading. - * Minor optimizations (thanks to Michael Kuklinski / Ameisen!). - * Updated compatibility list. - -* For mod authors: - * Added an [`IRawTextureData` asset type](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0#Raw_texture_data), to avoid creating full `Texture2D` instances in many cases. - * In `smapi-internal/config.json`, you can now enable verbose logging for specific mods (instead of all or nothing). - * Updated dependencies: - * Harmony 2.2.1 (see changes in [2.2.0](https://github.com/pardeike/Harmony/releases/tag/v2.2.0.0) and [2.2.1](https://github.com/pardeike/Harmony/releases/tag/v2.2.1.0)); - * Newtonsoft.Json 13.0.1 (see [changes](https://github.com/JamesNK/Newtonsoft.Json/releases/tag/13.0.1)); - * Pintail 2.2.0 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#220)). - * Removed transitional `UsePintail` option added in 3.14.0 (now always enabled). - * Fixed `onBehalfOf` arguments in the new content API being case-sensitive. - * Fixed map edits which change warps sometimes rebuilding the NPC pathfinding cache unnecessarily, which could cause a noticeable delay for players. - -## 3.14.7 -Released 01 June 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Optimized reflection cache to reduce frame skips for some players. - -* For mod authors: - * Removed `runtimeconfig.json` setting which impacted hot reload support. - -## 3.14.6 -Released 27 May 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed error in split-screen mode when a mod provides a localized asset in one screen but not another. - * Minor optimizations. - -## 3.14.5 -Released 22 May 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Improved performance when mods change some asset types (including NPC portraits/sprites). - * Fixed _could not find file_ error if a mod provides a localized version of a normally unlocalized asset and then stops providing it. - * Fixed CurseForge update checks for the new CurseForge API. - -## 3.14.4 -Released 15 May 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Improved performance for mods using deprecated APIs. - -* For mod authors: - * Removed warning for mods which use `dynamic`. - _This no longer causes errors on Linux/macOS after Stardew Valley 1.5.5._ - -## 3.14.3 -Released 12 May 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Reduced in-game performance impact. - -* For mod authors: - * Refactored how event handling works under the hood, particularly the new content API. This should have no effect on mod usage. - * Verbose mode now logs the in-game time. - * Fixed error when loading a `.xnb` file through the old content API without the file extension. - * Fixed asset propagation for player sprites not fully updating recolor masks in some cases. - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.26.0. - -## 3.14.2 -Released 08 May 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Enabled case-insensitive file paths by default for Android and Linux players. - _This was temporarily disabled in SMAPI 3.14.1, and will remain disabled by default on macOS and Windows since their filesystems are already case-insensitive._ - * Various performance improvements. -* For mod authors: - * Dynamic content packs created via `helper.ContentPacks.CreateTemporary` or `CreateFake` are now listed in the log file. - * Fixed assets loaded through a fake content pack not working correctly since 3.14.0. - -## 3.14.1 -Released 06 May 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Improved performance for mods still using the previous content API. - * Disabled case-insensitive file paths (introduced in 3.14.0) by default. - _You can enable them by editing `smapi-internal/config.json` if needed. They'll be re-enabled in an upcoming version after they're reworked a bit._ - * Removed experimental 'aggressive memory optimizations' option. - _This was disabled by default and is no longer needed in most cases. Memory usage will be better reduced by reworked asset propagation in the upcoming SMAPI 4.0.0._ - * Fixed 'content file was not found' error when the game tries to load unlocalized text from a localizable mod data asset in 3.14.0. - * Fixed error reading empty JSON files. These are now treated as if they didn't exist (matching pre-3.14.0 behavior). - * Updated compatibility list. - -## 3.14.0 -Released 01 May 2022 for Stardew Valley 1.5.6 or later. See [release highlights](https://www.patreon.com/posts/65265507). - -### For players -This is a big update, but existing mods should all work fine. If the latest version of a mod breaks in SMAPI 3.14, please report it [on the SMAPI mod page](https://www.nexusmods.com/stardewvalley/mods/2400?tab=posts). - -* Improvements: - * SMAPI now ignores dot-prefixed files when searching for mod folders (thanks to Nuztalgia!). - * On Linux, SMAPI now fixes many case-sensitive mod path issues automatically. - * On Linux/macOS, added `--use-current-shell` [command-line argument](technical/smapi.md#command-line-arguments) to avoid opening a separate terminal window. - * Improved performance in some cases. - * Improved translations. Thanks to ChulkyBow (updated Ukrainian)! - * Dropped update checks for the unofficial 64-bit patcher (obsolete since SMAPI 3.12.6). -* Fixes: - * Fixed some movie theater textures not translated when loaded through SMAPI (specifically assets with the `_international` suffix). - * Fixed the warning text when a mod causes an asset load conflict with itself. - * Fixed `--no-terminal` [command-line argument](technical/smapi.md#command-line-arguments) on Linux/macOS still opening a terminal window, even if nothing is logged to it (thanks to Ryhon0!). - * Fixed `player_add` console command not handling journal scraps and secret notes correctly. - * Fixed `set_farm_type` console command not updating warps. -* For the web UI: - * Improved log parser UI (thanks to KhloeLeclair!): - * Added pagination for big logs. - * Added search box to filter the log. - * Added option to show/hide content packs in the mod list. - * Added jump links in the sidebar. - * The filter options now stick to the top of the screen when scrolling. - * Rewrote rendering to improve performance. - -### For mod authors -This is a big release that includes the new features planned for SMAPI 4.0.0. - -For C# mod authors: your mods should still work fine in SMAPI 3.14.0. However you should review the [migration to SMAPI 4.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0) guide and update your mods when possible. Deprecated code will be removed when SMAPI 4.0.0 releases later this year (no sooner than August 2022), and break any mods which haven't updated by that time. You can update affected mods now, there's no need to wait for 4.0.0. - -For content pack authors: SMAPI 3.14.0 and 4.0.0 don't affect content packs. They should work fine as long as -the C# mod that loads them is updated. - -* Major changes: - * Added [content events](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Content), which will replace `IAssetEditor` and `IAssetLoader` in SMAPI 4.0.0. - _These include new features not supported by the old API like load conflict resolution, edit priority, and content pack labels. They also support new cases like easily detecting when an asset has changed, and avoid data corruption issues in some edge cases._ - * Added [nullable reference type annotations](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0#Nullable_reference_type_annotations) for all APIs. - * Added [`helper.GameContent` and `helper.ModContent`](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_4.0#Content_loading_API), which will replace `helper.Content` in SMAPI 4.0.0. - * Improved [mod-provided API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Mod-provided_APIs) proxying (thanks to Shockah!). - _This adds support for custom interfaces in return values or input arguments, custom enums if their values match, generic methods, and more. This is an internal change, you don't need to do anything different in your mod code._ - * Mod files loaded through SMAPI APIs (including `helper.Content.Load`) are now case-insensitive, even on Linux. - * Enabled deprecation notices for all deprecated APIs. These will only be shown in `TRACE` logs for at least a month after SMAPI 3.14.0 releases. -* Other improvements: - * Added `IAssetDataForImage.ExtendMap` to resize maps in asset editors. - * Added `IContentPack.ModContent` property to manage content pack assets. - * Added `Constants.ContentPath` to get the full path to the game's `Content` folder. - * Added `IAssetName` fields to the info received by `IAssetEditor`, `IAssetLoader`, and content event methods. - _This adds methods for working with asset names, parsed locales, etc._ - * Added `helper.Content.ParseAssetName` to get an `IAssetName` for an arbitrary asset key. - * Added [command-line arguments](technical/smapi.md#command-line-arguments) to toggle developer mode (thanks to Tondorian!). - * If an asset is loaded multiple times in the same tick, `IAssetLoader.CanLoad` and `IAssetEditor.CanEdit` are now cached unless invalidated by `helper.Content.InvalidateCache`. - * The `ISemanticVersion` comparison methods (`CompareTo`, `IsBetween`, `IsNewerThan`, and `IsOlderThan`) now allow null values. A null version is always considered older than any non-null version per [best practices](https://docs.microsoft.com/en-us/dotnet/api/system.icomparable-1.compareto#remarks). - * Deprecation notices now show a shorter stack trace in most cases, so it's clearer where the deprecated code is in the mod. -* Fixes: - * Fixed the `SDate` constructor being case-sensitive. - * Fixed support for using locale codes from custom languages in asset names (e.g. `Data/Achievements.eo-EU`). - * Fixed issue where suppressing `[Left|Right]Thumbstick[Down|Left]` keys would suppress the opposite direction instead. - * Fixed null handling in various edge cases. -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.25.0. - * Added `data-*` attributes to the log parser page for external tools. - * Fixed JSON validator showing incorrect error for update keys without a subkey. - -### For SMAPI contributors -* You no longer need a Nexus API key to launch the `SMAPI.Web` project locally. - -## 3.13.4 -Released 16 January 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * Fixed Linux/macOS launch error in 3.13.3. - -## 3.13.3 -Released 16 January 2022 for Stardew Valley 1.5.6 or later. - -* For players: - * **SMAPI now needs Stardew Valley 1.5.6 or later.** - * Added automatic fix for custom maps which are missing a required tilesheet. - * Added automatic save recovery when a custom farm type isn't available anymore. - * Added the game's new build number to the SMAPI console + log. - * The installer now detects Xbox app game folders. - * Reduced mod loading time a bit. - * Fixed macOS launch issue when using some terminals (thanks to bruce2409!). - * Fixed Linux/macOS terminal ignoring backspaces in Stardew Valley 1.5.5+. - * Fixed extra newlines in the SMAPI console. - * Fixed outdated instructions in Steam error message. - * Fixed uninstaller not removing `StardewModdingAPI.deps.json` file. - * Simplified [running without a terminal on Linux/macOS](https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting#SMAPI_doesn.27t_recognize_controller_.28Steam_only.29) when needed. - * Updated compatibility list. - * Improved translations. Thanks to ChulkyBow (added Ukrainian)! - -* For the web UI: - * Added log instructions for Xbox app on Windows. - * Added log download option. - * Redesigned log instruction UI. - * Fixed log parser not correctly handling multiple mods having the exact same name. - * Fixed JSON validator not recognizing manifest [update subkeys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Update_checks#Update_subkeys). - -## 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. - -* 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. See [release highlights](https://www.patreon.com/posts/59348226). - -* For players: - * Updated for Stardew Valley 1.5.5. - * Added `set_farm_type` [console command](https://stardewvalleywiki.com/Modding:Console_commands#Console_commands) to change the current farm type. - * Fixed installer window closing immediately if the installer crashed. - * Updated compatibility list. - -* For mod authors: - * Migrated to 64-bit MonoGame and .NET 5 on all platforms (see [migration guide for mod authors](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5)). - * Added support for [map overlays via `asset.AsMap().PatchMap`](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Content#Edit_a_map). - * Added support for loading BmFont `.fnt` files for [custom languages](https://stardewvalleywiki.com/Modding:Custom_languages) through the [content API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Content). - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.24.0. - -**Update note for players with older systems:** -The game now has two branches: the _main branch_ which you'll get by default, and an optional -[_compatibility branch_ for older systems](https://www.stardewvalley.net/compatibility/). The two -branches have identical content, but use [different technologies](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5#Game_compatibility_branch). - -Unfortunately **SMAPI only supports the main branch of the game**. There are formidable difficulties -across all mods in supporting all three variations, 32-bit imposes significant restrictions on what -mods can do, and the [Steam hardware stats](https://store.steampowered.com/hwsurvey) show that 99.69% -of players now have 64-bit. - -## 3.12.8 -Released 18 October 2021 for Stardew Valley 1.5.4. - -* For players: - * Fixed mod edits to the farmhouse shifting the player down one tile in some cases. - * Fixed map tile rotations/flips not working for farmhands in split-screen mode. - * Improved translations. Thanks to ellipszist (added Thai) and Zangorr (added Polish)! - _These are custom languages which require Stardew Valley 1.5.5 and the [Polish](https://www.nexusmods.com/stardewvalley/mods/3616) or [Thai](https://www.nexusmods.com/stardewvalley/mods/7052) mod._ - -* For mod authors: - * SMAPI now intercepts dictionary duplicate-key errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.) - * Fixed barn/coop exit warps being reset when you edit their interior map. - -* For the web UI: - * Added support for unified [mod data overrides](https://stardewvalleywiki.com/Modding:Mod_compatibility#Mod_data_overrides) defined on the wiki. - * The mod compatibility list now shows separate beta stats when 'show advanced info' is enabled. - -## 3.12.7 -Released 18 September 2021 for Stardew Valley 1.5.4. - -* For players: - * Added more progress updates in the log during startup. - * Simplified asset load error message. - * Simplified exception logs. - * Fixed crash loading mods with corrupted translation files. - -* For mod authors: - * Added asset propagation for `LooseSprites\Giftbox`. - * Improved SMAPI's crossplatform read/writing of `Color`, `Point`, `Rectangle`, and `Vector2` in JSON to support nullable fields too. - -* For the web UI: - * The mod compatibility list now shows the beta status by default (if any). - * Fixed JSON validator line numbers sometimes incorrect. - -## 3.12.6 -Released 03 September 2021 for Stardew Valley 1.5.4. - -* For players: - * Added friendly error when using SMAPI 3.2._x_ with Stardew Valley 1.5.5 or later. - * Improved mod compatibility in 64-bit mode (thanks to spacechase0!). - * Reduced load time when scanning/rewriting many mods for compatibility. - * **Dropped support for unofficial 64-bit mode**. You can now use the [official 64-bit Stardew Valley 1.5.5 beta](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows) instead. - * Updated compatibility list. - -* For mod authors: - * Added `PathUtilities.NormalizeAssetName` and `PathUtilities.PreferredAssetSeparator` to prepare for the upcoming Stardew Valley 1.5.5. - * **SMAPI no longer propagates changes to `Data/Bundles`.** - _You can still load/edit the asset like usual, but if bundles have already been loaded for a save, SMAPI will no longer dynamically update the in-game bundles to reflect the changes. Unfortunately this caused bundle corruption when playing in non-English._ - * Fixed content packs created via `helper.ContentPacks.CreateFake` or `CreateTemporary` not initializing translations correctly. - -* For console commands: - * Added `hurry_all` command which immediately warps all NPCs to their scheduled positions. - -**Update note for mod authors:** -Stardew Valley 1.5.5 will change how asset names are formatted. If you use `PathUtilities.NormalizePath` -to format asset names, you should switch to `PathUtilities.NormalizeAssetName` now so your code will -continue working in the next game update. - -## 3.12.5 -Released 26 August 2021 for Stardew Valley 1.5.4. - -* Fixed some mods in unofficial 64-bit mode no longer loading after SMAPI 3.12.3. - -## 3.12.4 -Released 25 August 2021 for Stardew Valley 1.5.4. - -* For players: - * Fixed error loading some mods in SMAPI 3.12.3. - -## 3.12.3 -Released 25 August 2021 for Stardew Valley 1.5.4. - -* For players: - * Added friendly error in 64-bit mode when a mod is 32-bit only. - * Fixed console encoding issues on Linux/macOS. - * Fixed some installer errors not showing info header. - -* For mod authors: - * Added `helper.Translation.GetInAllLocales` to get a translation in every available locale. - * Fixed Visual Studio debugger crash when any mods are rewritten for compatibility (thanks to spacechase0!). - * Fixed `helper.Data.WriteJsonFile` not deleting the file if the model is null, unlike the other `Write*` methods. - * Fixed error-handling for `StackOverflowException` thrown on Linux/macOS. - * Internal changes to prepare for Stardew Valley 1.5.5. - -* For the web API: - * Fixed update checks not shown for prerelease mod versions when you have a SMAPI beta. - * Fixed update checks shown for prerelease mod versions if you have a working non-prerelease version. - -## 3.12.2 -Released 05 August 2021 for Stardew Valley 1.5.4. - -* For players: - * Fixed error creating a new save or joining a multiplayer world in 3.12.1. - -* For mod authors: - * Reverted the `Constants.Save*` fix in SMAPI 3.12.1. - _The change caused a number of other issues, and is only needed for rare cases where the save folder was invalid. This may be revisited in a future version instead._ - * Fixed `NullReferenceException` in SMAPI's error-handling when trying to handle an invalid `ReflectionTypeLoadException`. - -## 3.12.1 -Released 03 August 2021 for Stardew Valley 1.5.4. - -* For players: - * The software conflict message is now shown as a warning to simplify troubleshooting. - * Fixed error loading older Harmony mods for some Windows players using unofficial 64-bit Stardew Valley. - * Updated compatibility list. - -* For mod authors: - * Fixed `Constants.Save*` fields incorrect if the save's folder name and ID don't match. - -## 3.12.0 -Released 01 August 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/54388616). - -* For players: - * Added save recovery when content mods leave null objects in the save (in _Error Handler_). - * Added error if the wrong SMAPI bitness is installed (e.g. 32-bit SMAPI with 64-bit game). - * Added error if some SMAPI files aren't updated correctly. - * Added `removable` option to the `world_clear` console command (in _Console Commands_, thanks to bladeoflight16!). - * Fixed handling of Unicode characters in console commands. - * Fixed intermittent error if a mod gets mod-provided APIs asynchronously. - * Fixed crash when creating a farm name containing characters that aren't allowed in a folder path. - -* For mod authors: - * **Updated Harmony 1.2.0.1 to 2.1.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).** - * SMAPI now intercepts `KeyNotFoundException` errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.) - * Fixed error loading `.xnb` files from the local mod folder. - * Fixed reloading a map not correctly reapplying interior doors. - -## 3.11.0 -Released 09 July 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/53514295). - -* For players: - * Updated for Stardew Valley 1.4.5 multiplayer hotfix on Linux/macOS. - * Fixed installer error on Windows when running as administrator (thanks to LostLogic!). - * Fixed installer error on some Windows systems (thanks to eddyballs!). - * Fixed error if SMAPI fails to dispose on game exit. - * Fixed `player_add` and `list_items` console commands not including some shirts _(in Console Commands)_. - -* For mod authors: - * Added `World.FurnitureListChanged` event (thanks to DiscipleOfEris!). - * Added asset propagation for building/house paint masks. - * Added log message for troubleshooting if Windows software which often causes issues is installed (currently MSI Afterburner and RivaTuner). - * Improved validation for the manifest `Dependencies` field. - * Fixed validation for mods with invalid version `0.0.0`. - * Fixed _loaded with custom settings_ trace log added when using default settings. - * Fixed `Constants.SaveFolderName` and `Constants.CurrentSavePath` not set correctly in rare cases. - -* For the web UI and JSON validator: - * Updated the JSON validator/schema for Content Patcher 1.23.0. - * Fixed [JSON schema](technical/web.md#using-a-schema-file-directly) in Visual Studio Code warning about comments and trailing commas. - * Fixed JSON schema for `i18n` files requiring the wrong value for the `$schema` field. - -## 3.10.1 -Released 03 May 2021 for Stardew Valley 1.5.4. - -* For players: - * Fixed installer leaving an unneeded `StardewModdingAPI-x64.exe` file in 32-bit game folders. - -## 3.10.0 -Released 03 May 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/50764911). - -* For players: - * Added full support for the [unofficial 64-bit Stardew Valley patch](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows), which removes memory limits. The installer detects which version of SMAPI you need, and SMAPI shows update alerts for Stardew64Installer if applicable. - * Added smarter grouping for skipped mods, so it's easier to see root dependencies to update first. - * Added crash recovery when the game can't update a map's seasonal tilesheets _(in Error Handler)_. SMAPI will log an error and keep the previous tilesheets in that case. - * Added installer option to enter a custom game path even if it detected a game folder. - * `*.ico` files are now ignored when scanning for mods. - * Fixed error for non-English players after returning to title, reloading, and entering town with a completed movie theater. - * Fixed `world_clear` console command not removing resource clumps outside the farm and secret woods. - * Fixed error running SMAPI in a strict sandbox on Linux (thanks to kuesji!). - * Fixed `StardewModdingAPI.bin.osx` on macOS overwritten with an identical file on launch which would reset file permissions (thanks to 007wayne!). - * Fixed inconsistent spelling/style for 'macOS'. - -* For modders: - * Added support for [ignoring local map tilesheet files when loading a map](https://stardewvalleywiki.com/Modding:Maps#Local_copy_of_a_vanilla_tilesheet). - * Added asset propagation for `Data\Concessions`. - * Added SMAPI version and bitness to the console title before startup to simplify troubleshooting. - * If a map loads a tilesheet path with no file extension, SMAPI now automatically links it to a `.png` version in the map folder if possible. - * Improved error-handling during asset propagation. - * Fixed `Context.IsMainPlayer` returning true for a farmhand in split-screen mode before the screen is initialized. - * Fixed error when editing bundle data while a split-screen player is joining. - * Fixed update subkeys not working in file descriptions for Nexus mods marked as adult content. - -## 3.9.5 -Released 21 March 2021 for Stardew Valley 1.5.4. - -* For players: - * Added console command to reset community center bundles _(in Console Commands)_. - * Disabled aggressive memory optimization by default. - _The option was added in SMAPI 3.9.2 to reduce errors for some players, but it can cause multiplayer crashes with some mods. If you often see `OutOfMemoryException` errors, you can edit `smapi-internal/config.json` to re-enable it. We're experimenting with making Stardew Valley 64-bit to address memory issues more systematically._ - * Fixed bundles corrupted in non-English saves created after SMAPI 3.9.2. - _If you have an affected save, you can load your save and then enter the `regenerate_bundles confirm` command in the SMAPI console to fix it._ - * Internal changes to prepare for unofficial 64-bit. - -* For mod authors: - * Improved asset propagation: - * Added for interior door sprites. - * SMAPI now updates the NPC pathfinding cache when map warps are changed through the content API. - * Reduced performance impact of invalidating cached assets before a save is loaded. - * Fixed asset changes not reapplied in the edge case where you're playing in non-English, and the changes are only applied after the save is loaded, and the player returns to title and reloads a save, and the game reloads the target asset before the save is loaded. - * Added a second `KeybindList` constructor to simplify single-key default bindings. - * Added a `Constants.GameFramework` field which indicates whether the game is using XNA Framework or MonoGame. - _Note: mods don't need to handle the difference in most cases, but some players may use MonoGame on Windows in upcoming versions. Mods which check `Constants.TargetPlatform` should review usages as needed._ - -## 3.9.4 -Released 07 March 2021 for Stardew Valley 1.5.4. - -* For players: - * Fixed installer error if the `Mods` folder doesn't exist in 3.9.3. - -## 3.9.3 -Released 07 March 2021 for Stardew Valley 1.5.4. - -* For players: - * Added descriptive error if possible when a `PathTooLongException` crashes SMAPI or the installer. - * The installer window now tries to stay open if it crashed, so you can read the error and ask for help. - * Fixed console showing _found 1 mod with warnings_ with no mods listed in some cases. - -* For mod authors: - * Added three stages to the specialised [`LoadStageChanged` event](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Specialised): `CreatedInitialLocations`, `SaveAddedLocations`, and `ReturningToTitle`. - * Fixed `RewriteMods` option ignored when rewriting for OS compatibility. - * Fixed edge case when playing as a farmhand in non-English where translatable assets loaded via `IAssetLoader` weren't reapplied immediately when the server disconnects. - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.21. - -## 3.9.2 -Released 21 February 2021 for Stardew Valley 1.5.4. - -* For players: - * Added more aggressive memory optimization to reduce `OutOfMemoryException` errors with some mods. - * Improved error when `Stardew Valley.exe` exists but can't be loaded. - * Fixed error running `install on Windows.bat` in very rare cases. - * Fixed `world_settime` command not always updating outdoor ambient lighting _(in Console Commands)_. - -* For mod authors: - * Added early detection of disposed textures so the error details are more relevant _(in Error Handler)_. - * Added error details when an event command fails _(in Error Handler)_. - * Fixed asset propagation for `TileSheets/ChairTiles` not changing existing map seats. - * Fixed edge case when playing in non-English where translatable assets loaded via `IAssetLoader` would no longer be applied after returning to the title screen unless manually invalidated from the cache. - -* For the web UI: - * Updated compatibility list for the new wiki. - * Updated the JSON validator/schema for Content Patcher 1.20. - * Fixed mod compatibility list error if a mod has no name. - -* For SMAPI developers: - * Fixed SMAPI toolkit defaulting the mod type incorrectly if a mod's `manifest.json` has neither `EntryDll` nor `ContentPackFor`. This only affects external tools, since SMAPI itself validates those fields separately. - -## 3.9.1 -Released 25 January 2021 for Stardew Valley 1.5.4. - -* For players: - * Fixed _tile contains an invalid TileSheet reference_ crash after mods change certain maps. - * Fixed _patched game code_ issue shown for the bundled Error Handler mod. - -## 3.9.0 -Released 22 January 2021 for Stardew Valley 1.5.4. See [release highlights](https://www.patreon.com/posts/46553874). - -* For players: - * Updated for Stardew Valley 1.5.4. - * Improved game detection in the installer: - * The installer now prefers paths registered by Steam or GOG Galaxy. - * The installer now detects default manual GOG installs. - * Added clearer error text for empty mod folders created by Vortex. - * Fixed the game's map changes not always reapplied correctly after mods change certain maps, which caused issues like the community center resetting to its non-repaired texture. - * Fixed compatibility for very old content packs which still load maps from `.xnb` files. These were broken by map loading changes in Stardew Valley 1.5, but SMAPI now corrects them automatically. - * Fixed some broken mods incorrectly listed as XNB mods under 'skipped mods'. - -* For mod authors: - * Added new input APIs: - * Added an [API for multi-key bindings](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#KeybindList). - * Added a new [`Input.ButtonsChanged` event](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Input.ButtonsChanged). - * Added a `buttonState.IsDown()` extension. - * Added a `helper.Input.SuppressActiveKeybinds` method to suppress the active buttons in a keybind list. - * Improved multiplayer APIs: - * `PerScreen` now lets you get/set the value for any screen, get all active values, or clear all values. - * Peer data from the multiplayer API/events now includes `IsSplitScreen` and `ScreenID` fields. - * Fixed network messages through the multiplayer API being sent to players who don't have SMAPI installed in some cases. - * Improved asset propagation: - * Updated map propagation for the changes in Stardew Valley 1.5.4. - * Added propagation for some `Strings\StringsFromCSFiles` keys (mainly short day names). - * Fixed quarry bridge not fixed if the mountain map was reloaded. - * Added an option to disable rewriting mods for compatibility (thanks to Bpendragon!). This prevents older mods from loading, but bypasses a Visual Studio crash when debugging. - * Game errors shown in the chatbox are now logged. - * Moved vanilla error-handling into a new Error Handler mod. This simplifies the core SMAPI logic, and lets users disable it if needed. - -* For the Console Commands mod: - * Removed the `inf` option for `player_sethealth`, `player_setmoney`, and `player_setstamina`. You can use mods like [CJB Cheats Menu](https://www.nexusmods.com/stardewvalley/mods/4) instead for that. - -* For the Error Handler mod: - * Added a detailed message for the _Input string was not in a correct format_ error when the game fails to parse an item text description. - -* For the web UI: - * Fixed JSON validator incorrectly marking some manifest update keys as invalid. - -## 3.8.4 -Released 15 January 2021 for Stardew Valley 1.5.3 or later. - -* For players: - * Updated for Stardew Valley 1.5.3. - * Fixed issue where title screen music didn't stop after loading a save. - -* For mod authors: - * Fixed `SemanticVersion` comparisons returning wrong value in rare cases. - -## 3.8.3 -Released 08 January 2021 for Stardew Valley 1.5.2 or later. - -* For players: - * Updated for Stardew Valley 1.5.2. - * Reduced memory usage. - * You can now enter console commands for a specific screen in split-screen mode by adding `screen=ID` to the command. - * Typing `help` in the SMAPI console is now more helpful. - -* For mod authors: - * Simplified tilesheet order warning added in SMAPI 3.8.2. - -* For the Console Commands mod: - * Removed experimental `performance` command. Unfortunately this impacted SMAPI's memory usage and performance, and the data was often misinterpreted. This may be replaced with more automatic performance alerts in a future version. - -## 3.8.2 -Released 03 January 2021 for Stardew Valley 1.5.1 or later. - -* For players: - * SMAPI now blocks farm map replacements that would crash the game in Stardew Valley 1.5. - * On Linux, the SMAPI installer now auto-detects Flatpak Steam paths. - * Updated compatibility list. - * Fixed errors when multiple players join in split-screen mode. - * Fixed 'skipped mods' section repeating mods in some cases. - * Fixed out-of-date error text. - -* For mod authors: - * Added warning when a map replacement changes the order/IDs of the original tilesheets, which may cause errors and crashes. Doing so for a farm map is blocked outright since that causes a consistent crash in Stardew Valley 1.5. - * Message data from the `ModMessageReceived` event now uses the same serializer settings as the rest of SMAPI. That mainly adds support for sending crossplatform `Color`, `Point`, `Vector2`, `Rectangle`, and `SemanticVersion` fields through network messages. - * When a mod is blocked by SMAPI's compatibility override list, the `TRACE` messages while loading it now say so and indicate why. - * Fixed how the input API handles UI scaling. This mainly affects `ICursorPosition` values returned by the API; see [the wiki docs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#ICursorPosition) for how to account for UI scaling. - -## 3.8.1 -Released 26 December 2020 for Stardew Valley 1.5.1 or later. - -* For players: - * Fixed broken community center bundles for non-English saves created in Stardew Valley 1.5. Affected saves will be fixed automatically on load. - -* For mod authors: - * World events are now raised for volcano dungeon levels. - * Added `apply_save_fix` command to reapply a save migration in exceptional cases. This should be used very carefully. Type `help apply_save_fix` for details. - * **Deprecation notice:** the `Helper.ConsoleCommands.Trigger` method is now deprecated and should no longer be used. See [integration APIs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations) for better mod integration options. It will eventually be removed in SMAPI 4.0. - -For the web UI: - * Fixed edge cases in SMAPI log parsing. - -## 3.8.0 -Released 21 December 2020 for Stardew Valley 1.5 or later. See [release highlights](https://www.patreon.com/posts/45294737). - -* For players: - * Updated for Stardew Valley 1.5, including split-screen support. - * You can now run the installer from a subfolder of your game folder to auto-detect it. That simplifies installation if you have multiple copies of the game or it can't otherwise auto-detect the game path. - * Clarified error when the SMAPI installer is in the `Mods` folder. - -* For mod authors: - * Added `PerScreen` utility and new `Context` fields to simplify split-screen support in mods. - * Added screen ID to log when playing in split-screen mode. - -* For the Console Commands mod: - * Added `furniture` option to `world_clear`. - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.19. - -## 3.7.6 -Released 21 November 2020 for Stardew Valley 1.4.1 or later. - -* For players: - * Fixed error when heuristically rewriting an outdated mod in rare cases. - * Fixed rare 'collection was modified' error when using `harmony summary` console command. - -* For mod authors: - * Updated TMXTile to 1.5.8 to fix exported `.tmx` files losing tile index properties. - -* For the Console Commands mod: - * `player_add` can now spawn shirts normally only available during character customization. - * `player_add` now applies fish pond rules for roe items. (That mainly adds Clam Roe, Sea Urchin Roe, and custom roe from mods.) - -## 3.7.5 -Released 16 October 2020 for Stardew Valley 1.4.1 or later. - -* For mod authors: - * Fixed changes to the town map asset not reapplying the game's community center, JojaMart, and Pam house changes. - -## 3.7.4 -Released 03 October 2020 for Stardew Valley 1.4.1 or later. - -* For players: - * Improved performance on some older computers (thanks to millerscout!). - * Fixed update alerts for Chucklefish forum mods broken by a recent site change. - -* For mod authors: - * Updated dependencies (including Mono.Cecil 0.11.2 → 0.11.3 and Platonymous.TMXTile 1.3.8 → 1.5.6). - * Fixed asset propagation for `Data\MoviesReactions`. - * Fixed error in content pack path handling when you pass a null path. - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.18. - -* For SMAPI developers: - * Simplified preparing a mod build config package release. - -## 3.7.3 -Released 16 September 2020 for Stardew Valley 1.4.1 or later. - -* For players: - * Fixed errors on Linux/macOS due to content packs with incorrect filename case. - * Fixed map rendering crash due to conflict between SMAPI and PyTK. - * Fixed error in heuristically-rewritten mods in rare cases (thanks to collaboration with ZaneYork!). - -* For mod authors: - * File paths accessed through `IContentPack` are now case-insensitive (even on Linux). - -* For the web UI: - * You can now renew the expiry for an uploaded JSON/log file if you need it longer. - -## 3.7.2 -Released 08 September 2020 for Stardew Valley 1.4.1 or later. - -* For players: - * Fixed mod recipe changes not always applied in 3.7. - -* For mod authors: - * Renamed `PathUtilities.NormalizePathSeparators` to `NormalizePath`, and added normalization for more cases. - -## 3.7.1 -Released 08 September 2020 for Stardew Valley 1.4.1 or later. - -* For players: - * Fixed input-handling bugs in 3.7. - -## 3.7.0 -Released 07 September 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/41341767). - -* For players: - * Added heuristic compatibility rewrites. (This improves mod compatibility with Android and future game updates.) - * Tweaked the rules for showing update alerts (see _for SMAPI developers_ below for details). - * Simplified the error shown for duplicate mods. - * Fixed crossplatform compatibility for mods which use the `[HarmonyPatch(type)]` attribute (thanks to spacechase0!). - * Fixed map tile rotation broken when you return to the title screen and reload a save. - * Fixed broken URL in update alerts for unofficial versions. - * Fixed rare error when a mod adds/removes event handlers asynchronously. - * Fixed rare issue where the console showed incorrect colors when mods wrote to it asynchronously. - * Fixed SMAPI not always detecting broken field references in mod code. - * Removed the experimental `RewriteInParallel` option added in SMAPI 3.6 (it was already disabled by default). Unfortunately this caused intermittent and unpredictable errors when enabled. - * Internal changes to prepare for upcoming game updates. - -* For mod authors: - * Added `PathUtilities` to simplify working with file/asset names. - * You can now read/write `SDate` values to JSON (e.g. for `config.json`, network mod messages, etc). - * Fixed asset propagation not updating title menu buttons immediately on Linux/macOS. - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.16 and 1.17. - -* For SMAPI developers: - * The web API now returns an update alert in two new cases: any newer unofficial update (previously only shown if the mod was incompatible), and a newer prerelease version if the installed non-prerelease version is broken (previously only shown if the installed version was prerelease). - * Reorganised the SMAPI core to reduce coupling to game types like `Game1`, make it easier to navigate, and simplify future game updates. - * SMAPI now automatically fixes code broken by these changes in game code, so manual rewriters are no longer needed: - * reference to a method with new optional parameters; - * reference to a field replaced by a property; - * reference to a field replaced by a `const` field. - * `FieldReplaceRewriter` now supports mapping to a different target type. - -## 3.6.2 -Released 02 August 2020 for Stardew Valley 1.4.1 or later. - -* For players: - * Improved compatibility with some Linux terminals (thanks to jlaw and Spatterjaaay!). - * Fixed rare error when a mod adds/removes an event handler from an event handler. - * Fixed string sorting/comparison for some special characters. - -* For the Console Commands mod: - * Fixed error opening menu when some item data is invalid. - * Fixed spawned Floor TV not functional as a TV (thanks to Platonymous!). - * Fixed spawned sturgeon roe having incorrect color. - -* For mod authors: - * Updated internal dependencies. - * SMAPI now ignores more file types when scanning for mod folders (`.doc`, `.docx`, `.rar`, and `.zip`). - * Added current GPU to trace logs to simplify troubleshooting. - -## 3.6.1 -Released 21 June 2020 for Stardew Valley 1.4.1 or later. - -* Fixed event priority sorting. - -## 3.6.0 -Released 20 June 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/38441800). - -* For players: - * Added crossplatform compatibility for mods which use the `[HarmonyPatch(type)]` attribute. - * Added experimental option to reduce startup time when loading mod DLLs (thanks to ZaneYork!). Enable `RewriteInParallel` in the `smapi-internal/config.json` to try it. - * Reduced processing time when a mod loads many unpacked images (thanks to Entoarox!). - * Mod load warnings are now listed alphabetically. - * macOS files starting with `._` are now ignored and can no longer cause skipped mods. - * Simplified paranoid warning logs and reduced their log level. - * Fixed black maps on Android for mods which use `.tmx` files. - * Fixed `BadImageFormatException` error detection. - * Fixed `reload_i18n` command not reloading content pack translations. - -* For the web UI: - * Added GitHub licenses to mod compatibility list. - * Improved JSON validator: - * added SMAPI `i18n` schema; - * editing an uploaded file now remembers the selected schema; - * changed default schema to plain JSON. - * Updated ModDrop URLs. - * Internal changes to improve performance and reliability. - -* For mod authors: - * Added [event priorities](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Custom_priority) (thanks to spacechase0!). - * Added [update subkeys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Update_checks#Update_subkeys). - * Added [a custom build of Harmony](https://github.com/Pathoschild/Harmony#readme) to provide more useful stack traces in error logs. - * Added `harmony_summary` console command to list or search current Harmony patches. - * Added `Multiplayer.PeerConnected` event. - * Added support for overriding update keys from the wiki compatibility list. - * Improved mod rewriting for compatibility to support more cases (e.g. custom attributes and generic types). - * Fixed `helper.Reflection` blocking access to game methods/properties intercepted by SMAPI. - * Fixed asset propagation for Gil's portraits. - * Fixed `.pdb` files ignored for error stack traces when mods are rewritten by SMAPI. - * Fixed `ModMessageReceived` event handlers not tracked for performance monitoring. - -* For SMAPI developers: - * Eliminated MongoDB storage in the web services, which complicated the code unnecessarily. The app still uses an abstract interface for storage, so we can wrap a distributed cache in the future if needed. - * Overhauled update checks to simplify mod site integrations, centralize common logic, and enable upcoming features. - * Merged the separate legacy redirects app on AWS into the main app on Azure. - * Changed SMAPI's Harmony ID from `io.smapi` to `SMAPI` for readability in Harmony summaries. - -## 3.5.0 -Released 27 April 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/36471055). - -* For players: - * SMAPI now prevents more game errors due to broken items, so you no longer need save editing to remove them. - * Added option to disable console colors. - * Updated compatibility list. - * Improved translations.¹ - -* For the Console Commands mod: - * Commands like `world_setday` now also affect the 'days played' stat, so in-game events/randomization match what you'd get if you played to that date normally (thanks to kdau!). - -* For the web UI: - * Updated the JSON validator/schema for Content Patcher 1.13. - * Fixed rare intermittent "CGI application encountered an error" errors. - -* For mod authors: - * Added map patching to the content API (via `asset.AsMap()`). - * Added support for using patch helpers with arbitrary data (via `helper.Content.GetPatchHelper`). - * Added `SDate` fields/methods: `SeasonIndex`, `FromDaysSinceStart`, `FromWorldDate`, `ToWorldDate`, and `ToLocaleString` (thanks to kdau!). - * Added `SDate` translations taken from the Lookup Anything mod.¹ - * Fixed asset propagation for certain maps loaded through temporary content managers. This notably fixes unreliable patches to the farmhouse and town maps. - * Fixed asset propagation on Linux/macOS for monster sprites, NPC dialogue, and NPC schedules. - * Fixed asset propagation for NPC dialogue sometimes causing a spouse to skip marriage dialogue or not allow kisses. - -¹ Date format translations were taken from the Lookup Anything mod; thanks to translators FixThisPlz (improved Russian), LeecanIt (added Italian), pomepome (added Japanese), S2SKY (added Korean), Sasara (added German), SteaNN (added Russian), ThomasGabrielDelavault (added Spanish), VincentRoth (added French), Yllelder (improved Spanish), and yuwenlan (added Chinese). Some translations for Korean, Hungarian, and Turkish were derived from the game translations. - -## 3.4.1 -Released 24 March 2020 for Stardew Valley 1.4.1 or later. - -* For mod authors: - * Asset changes now propagate to NPCs in an event (e.g. wedding sprites). - * Fixed mouse input suppression not working in SMAPI 3.4. - -## 3.4.0 -Released 22 March 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/35161371). - -* For players: - * Fixed semi-transparency issues on Linux/macOS in recent versions of Mono (e.g. pink shadows). - * Fixed `player_add` command error if you have broken XNB mods. - * Removed invalid-location check now handled by the game. - * Updated translations. Thanks to Annosz (added Hungarian)! - -* For mod authors: - * Added support for flipped and rotated map tiles (thanks to collaboration with Platonymous!). - * Added support for `.tmx` maps using zlib compression (thanks to Platonymous!). - * Added `this.Monitor.LogOnce` method. - * Mods are no longer prevented from suppressing key presses in the chatbox. - -* For the web UI: - * Added option to upload files using a file picker. - * Optimized log parser for very long multi-line log messages. - * Fixed log parser not detecting folder path in recent versions of SMAPI. - -* For SMAPI developers: - * Added internal API to send custom input to the game/mods. This is mainly meant to support Virtual Keyboard on Android, but might be exposed as a public API in future versions. - -## 3.3.2 -Released 22 February 2020 for Stardew Valley 1.4.1 or later. - -* Fixed mods receiving their own message broadcasts. - -## 3.3.1 -Released 22 February 2020 for Stardew Valley 1.4.1 or later. - -* Fixed errors with custom spouse room mods in SMAPI 3.3. - -## 3.3.0 -Released 22 February 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/34248719). - -* For players: - * Improved performance for mods which load many images. - * Reduced network traffic for mod broadcasts to players who can't process them. - * Fixed update-check errors for recent versions of SMAPI on Android. - * Updated draw logic to match recent game updates. - * Updated compatibility list. - * Updated SMAPI/game version map. - * Updated translations. Thanks to xCarloC (added Italian)! - -* For the Save Backup mod: - * Fixed warning on macOS when you have no saves yet. - * Reduced log messages. - -* For the web UI: - * Updated the JSON validator and Content Patcher schema for `.tmx` support. - * The mod compatibility page now has a sticky table header. - -* For mod authors: - * Added support for [message sending](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations#Message_sending) to mods on the current computer (in addition to remote computers). - * Added `ExtendImage` method to content API when editing files to resize textures. - * Added `helper.Input.GetState` to get the low-level state of a button. - * **[Breaking change]** Map tilesheets are no loaded from `Content` if they can't be found in `Content/Maps`. This reflects an upcoming change in the game to delete duplicate map tilesheets under `Content`. Most mods should be unaffected. - * Improved map tilesheet errors so they provide more info. - * When mods load an asset using a more general type like `content.Load`, SMAPI now calls `IAssetEditor` instances with the actual asset type instead of the specified one. - * Updated dependencies (including Mono.Cecil 0.11.1 → 0.11.2). - * Fixed dialogue propagation clearing marriage dialogue. - -* For SMAPI/tool developers: - * Improved support for four-part versions to support SMAPI on Android. - * The SMAPI log now prefixes the OS name with `Android` on Android. - -## 3.2.0 -Released 01 February 2020 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/33659728). - -* For players: - * SMAPI now prevents crashes due to invalid schedule data. - * SMAPI now prevents crashes due to invalid building types. - * Added support for persistent `smapi-internal/config.json` overrides (see info in the file). - * Updated minimum game version (1.4 → 1.4.1). - * Fixed 'collection was modified' error when returning to title in rare cases. - * Fixed error when update-checking a mod with a Chucklefish page that has no version. - * Fixed rare error when building/demolishing buildings. - * Fixed SMAPI beta versions not showing update alert on next launch (thanks to danvolchek!). - -* For the Console Commands mod: - * Added `performance` command to track mod performance metrics. This is an advanced experimental feature. (Thanks to Drachenkätzchen!) - * Added `test_input` command to view button codes in the console. - -* For the Save Backup mod: - * Fixed extra files under `Saves` (e.g. manual backups) not being ignored. - * Fixed Android issue where game files were backed up. - -* For mod authors: - * Added support for `.tmx` map files. (Thanks to [Platonymous for the underlying library](https://github.com/Platonymous/TMXTile)!) - * Added special handling for `Vector2` values in `.json` files, so they work consistently crossplatform. - * Reworked the order that asset editors/loaders are called between multiple mods to support some framework mods like Content Patcher and Json Assets. Note that the order is undefined and should not be depended on. - * Fixed incorrect warning about mods adding invalid schedules in some cases. The validation was unreliable, and has been removed. - * Fixed asset propagation not updating other players' sprites. - * Fixed asset propagation for player sprites not updating recolor maps (e.g. sleeves). - * Fixed asset propagation for marriage dialogue. - * Fixed dialogue asset changes not correctly propagated until the next day. - * Fixed `helper.Data.Read`/`WriteGlobalData` using the `Saves` folder instead of the game's appdata folder. The installer will move existing folders automatically. - * Fixed issue where a mod which implemented `IAssetEditor`/`IAssetLoader` on its entry class could then remove itself from the editor/loader list. - -* For SMAPI/tool developers: - * Added internal performance monitoring (thanks to Drachenkätzchen!). This is disabled by default in the current version, but can be enabled using the `performance` console command. - * Added internal support for four-part versions to support SMAPI on Android. - * Rewrote `SemanticVersion` parsing. - * Updated links for the new r/SMAPI subreddit. - * The `/mods` web API endpoint now includes version mappings from the wiki. - * Dropped API support for the pre-3.0 update-check format. - -## 3.1.0 -Released 05 January 2019 for Stardew Valley 1.4.1 or later. See [release highlights](https://www.patreon.com/posts/32904041). - -* For players: - * Added separate group in 'skipped mods' list for broken dependencies, so it's easier to see what to fix first. - * Added friendly log message for save file-not-found errors. - * Updated for gamepad modes in Stardew Valley 1.4.1. - * Improved performance in some cases. - * Fixed compatibility with Linux Mint 18 (thanks to techge!), Arch Linux, and Linux systems with libhybris-utils installed. - * Fixed memory leak when repeatedly loading a save and returning to title. - * Fixed memory leak when mods reload assets. - * Updated translations. Thanks to L30Bola (added Portuguese), PlussRolf (added Spanish), and shirutan (added Japanese)! - -* For the Console Commands mod: - * Added new clothing items. - * Fixed spawning new flooring and rings (thanks to Mizzion!). - * Fixed spawning custom rings added by mods. - * Fixed errors when some item data is invalid. - -* For the web UI: - * Added option to edit & reupload in the JSON validator. - * File uploads are now stored in Azure storage instead of Pastebin, due to ongoing Pastebin perfomance issues. - * File uploads now expire after one month. - * Updated the JSON validator for Content Patcher 1.10 and 1.11. - * Fixed JSON validator no longer letting you change format when viewing a file. - * Fixed JSON validator for Content Patcher not requiring `Default` if `AllowBlank` was omitted. - * Fixed log parser not correctly handling content packs with no author (thanks to danvolchek!). - * Fixed main sidebar link pointing to wiki instead of home page. - -* For mod authors: - * Added `World.ChestInventoryChanged` event (thanks to collaboration with wartech0!). - * Added asset propagation for... - * grass textures; - * winter flooring textures; - * `Data\Bundles` changes (for added bundles only); - * `Characters\Farmer\farmer_girl_base_bald`. - * Added paranoid-mode warning for direct `Console` access. - * Improved error messages for `TargetParameterCountException` when using the reflection API. - * `helper.Read/WriteSaveData` can now be used while a save is being loaded (e.g. within a `Specialized.LoadStageChanged` event). - * Removed `DumpMetadata` option. It was only for specific debugging cases, but players would sometimes enable it incorrectly and then report crashes. - * Fixed private textures loaded from content packs not having their `Name` field set. - -* For SMAPI developers: - * You can now run local environments without configuring Amazon, Azure, MongoDB, and Pastebin accounts. - -## 3.0.1 -Released 02 December 2019 for Stardew Valley 1.4 or later. - -* For players: - * Updated for Stardew Valley 1.4.0.1. - * Improved compatibility with some Linux terminals (thanks to archification and DanielHeath!). - * Updated translations. Thanks to berkayylmao (added Turkish), feathershine (added Chinese), and Osiris901 (added Russian)! - -* For the web UI: - * Rebuilt web infrastructure to handle higher traffic. - * If a log can't be uploaded to Pastebin (e.g. due to rate limits), it's now uploaded to Amazon S3 instead. Logs uploaded to S3 expire after one month. - * Fixed JSON validator not letting you drag & drop a file. - -* For mod authors: - * `SemanticVersion` now supports [semver 2.0](https://semver.org/) build metadata. - -## 3.0.0 -Released 26 November 2019 for Stardew Valley 1.4. - -### Release highlights -For players: -* **Updated for Stardew Valley 1.4.** - SMAPI 3.0 adds compatibility with the latest game version, and improves mod APIs for changes in - the game code. - -* **Improved performance.** - SMAPI should have less impact on game performance and startup time for some players. - -* **Automatic save fixing and more error recovery.** - SMAPI now detects and prevents more crashes due to game/mod bugs, and automatically fixes your - save if you remove some custom-content mods. - -* **Improved mod scanning.** - SMAPI now supports some non-standard mod structures automatically, improves compatibility with - the Vortex mod manager, and improves various error/skip messages related to mod loading. - -* **Overhauled update checks.** - SMAPI update checks are now handled entirely on the web server and support community-defined - version mappings. In particular, false update alerts due to author mistakes can now be solved by - the community for all players. - -* **Fixed many bugs and edge cases.** - -For mod authors: -* **New event system.** - SMAPI 3.0 removes the deprecated static events in favor of the new `helper.Events` API. The event - engine is rewritten to make events more efficient, add events that weren't possible before, make - existing events more useful, and make event usage and behavior more consistent. When a mod makes - changes in an event handler, those changes are now also reflected in the next event raise. - -* **Improved mod build package.** - The [mod build package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig) now - includes the `assets` folder by default if present, supports the new `.csproj` project format, - enables mod `.pdb` files automatically (to provide line numbers in error messages), adds optional - Harmony support, and fixes some bugs and edge cases. This also adds compatibility with SMAPI 3.0 - and Stardew Valley 1.4, and drops support for older versions. - -* **Mods loaded earlier.** - SMAPI now loads mods much earlier, before the game is initialised. That lets mods do things that - were difficult before, like intercepting some core assets. - -* **Improved Android support.** - SMAPI now automatically detects when it's running on Android, and updates `Constants.TargetPlatform` - so mods can adjust their logic if needed. The Save Backup mod is also now Android-compatible. - -* **Improved asset propagation.** - SMAPI now automatically propagates asset changes for farm animal data, NPC default location data, - critter textures, and `DayTimeMoneyBox` buttons. Every loaded texture now also has a `Name` field - so mods can check which asset a texture was loaded for. - -* **Breaking changes:** - See _[migrate to SMAPI 3.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0)_ and - _[migrate to Stardew Valley 1.4](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.4)_ - for more info. - -### For players -* Changes: - * Updated for Stardew Valley 1.4. - * Improved performance. - * Reworked update checks and added community-defined version mapping, to reduce false update alerts due to author mistakes. - * SMAPI now removes invalid locations/NPCs when loading a save to prevent crashes. A warning is shown in-game when this happens. - * Added update checks for CurseForge mods. - * Added support for editing console colors via `smapi-internal/config.json` (for players with unusual consoles). - * Added support for setting SMAPI CLI arguments as environment variables for Linux/macOS compatibility. - * Improved mod scanning: - * Now ignores metadata files/folders (like `__MACOSX` and `__folder_managed_by_vortex`) and content files (like `.txt` or `.png`), which avoids missing-manifest errors in some cases. - * Now detects XNB mods more accurately, and consolidates multi-folder XNB mods in logged messages. - * Improved launch script compatibility on Linux (thanks to kurumushi and toastal!). - * Made error messages more user-friendly in some cases. - * Save Backup now works in the background, to avoid affecting startup time for players with a large number of saves. - * The installer now recognises custom game paths stored in [`stardewvalley.targets`](http://smapi.io/package/custom-game-path). - * Duplicate-mod errors now show the mod version in each folder. - * Update checks are now faster in some cases. - * Updated mod compatibility list. - * Updated SMAPI/game version map. - * Updated translations. Thanks to eren-kemer (added German)! -* Fixes: - * Fixed some assets not updated when you switch language to English. - * Fixed lag in some cases due to incorrect asset caching when playing in non-English. - * Fixed lag when a mod invalidates many NPC portraits/sprites at once. - * Fixed Console Commands not including upgraded tools in item commands. - * Fixed Console Commands' item commands failing if a mod adds invalid item data. - * Fixed Save Backup not pruning old backups if they're uncompressed. - * Fixed issues when a farmhand reconnects before the game notices they're disconnected. - * Fixed 'received message' logs shown in non-developer mode. - * Fixed various error messages and inconsistent spelling. - * Fixed update-check error if a Nexus mod is marked as adult content. - * Fixed update-check error if the Chucklefish page for an update key doesn't exist. - -### For the web UI -* Mod compatibility list: - * Added support for CurseForge mods. - * Added metadata links and dev notes (if any) to advanced info. - * Now loads faster (since data is fetched in a background service). - * Now continues working with cached data when the wiki is offline. - * Clicking a mod link now automatically adds it to the visible mods if the list is filtered. - -* JSON validator: - * Added JSON validator at [smapi.io/json](https://smapi.io/json), which lets you validate a JSON file against predefined mod formats. - * Added support for the `manifest.json` format. - * Added support for the Content Patcher format (thanks to TehPers!). - * Added support for referencing a schema in a JSON Schema-compatible text editor. - -* For the log parser: - * Added instructions for Android. - * The page now detects your OS and preselects the right instructions (thanks to danvolchek!). - -### For mod authors -* Breaking changes: - * Mods are now loaded much earlier in the game launch. This lets mods intercept any content asset, but the game is not fully initialized when `Entry` is called; use the `GameLaunched` event if you need to run code when the game is initialized. - * Removed all deprecated APIs. - * Removed unused APIs: `Monitor.ExitGameImmediately`, `Translation.ModName`, and `Translation.Assert`. - * Fixed `ICursorPosition.AbsolutePixels` not adjusted for zoom. - * `SemanticVersion` no longer omits `.0` patch numbers when formatting versions, for better [semver](https://semver.org/) conformity (e.g. `3.0` is now formatted as `3.0.0`). -* Changes: - * Added support for content pack translations. - * Added `IContentPack.HasFile`, `Context.IsGameLaunched`, and `SemanticVersion.TryParse`. - * Added separate `LogNetworkTraffic` option to make verbose logging less overwhelmingly verbose. - * Added asset propagation for `Data\FarmAnimals`, critter textures, and `DayTimeMoneyBox` buttons. - * Added `Texture2D.Name` values set to the asset key. - * Added trace logs for skipped loose files in the `Mods` folder and custom SMAPI settings so it's easier to troubleshoot player logs. - * `Constants.TargetPlatform` now returns `Android` when playing on an Android device. - * Trace logs for a broken mod now list all detected issues (instead of the first one). - * Trace logs when loading mods are now more clear. - * Clarified update-check errors for mods with multiple update keys. - * Updated dependencies (including Json.NET 11.0.2 → 12.0.3 and Mono.Cecil 0.10.1 → 0.11.1). -* Fixes: - * Fixed map reloads resetting tilesheet seasons. - * Fixed map reloads not updating door warps. - * Fixed outdoor tilesheets being seasonalised when added to an indoor location. - * Fixed mods needing to load custom `Map` assets before the game accesses them. SMAPI now does so automatically. - * Fixed custom maps loaded from `.xnb` files not having their tilesheet paths automatically adjusted. - * Fixed custom maps loaded from the mod folder with tilesheets in a subfolder not working crossplatform. All tilesheet paths are now normalized for the OS automatically. - * Fixed issue where mod changes weren't tracked correctly for raising events in some cases. Events now reflect a frozen snapshot of the game state, and any mod changes are reflected in the next event tick. - * Fixed issue where, when a mod's `IAssetEditor` uses `asset.ReplaceWith` on a texture asset while playing in non-English, any changes from that point forth wouldn't affect subsequent cached asset loads. - * Fixed asset propagation for NPC portraits resetting any unique portraits (e.g. Maru's hospital portrait) to the default. - * Fixed changes to `Data\NPCDispositions` not always propagated correctly to existing NPCs. - * Fixed `Rendering`/`Rendered` events not raised during minigames. - * Fixed `LoadStageChanged` event not raising correct flags in some cases when creating a new save. - * Fixed `GetApi` without an interface not checking if all mods are loaded. - -### For SMAPI maintainers -* Added support for core translation files. -* Migrated to new `.csproj` format. -* Internal refactoring. - -## 2.11.3 and earlier +## 3.18.6 and earlier See [older release notes](release-notes-archived.md). From d40cbdee1711e59b70b90c33b783dda5a6a2e27d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 18:25:58 -0500 Subject: [PATCH 24/26] only attest release builds --- .github/workflows/build-smapi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index 18b8d46d..c6f221d2 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -111,5 +111,6 @@ jobs: - name: Generate artifact attestations uses: actions/attest-build-provenance@v2 + if: github.ref_type == 'tag' with: subject-path: bin/SMAPI-${{env.VERSION}}-installer.zip From 93891cf9d0e89aeb401348c212dd1ee2ba0e262c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 20:08:43 -0500 Subject: [PATCH 25/26] move build zip rename into script --- .github/workflows/build-smapi.yml | 7 ++----- build/scripts/prepare-install-package.ps1 | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-smapi.yml b/.github/workflows/build-smapi.yml index c6f221d2..b026fcc8 100644 --- a/.github/workflows/build-smapi.yml +++ b/.github/workflows/build-smapi.yml @@ -87,9 +87,6 @@ jobs: Write-Host "Building SMAPI $env:VERSION." ./build/scripts/prepare-install-package.ps1 "$env:VERSION" - - name: Rename build zip - run: mv "bin/SMAPI ${{env.VERSION}} installer.zip" "bin/SMAPI-${{env.VERSION}}-installer.zip" - - name: Upload installer uses: actions/upload-artifact@v4 with: @@ -101,10 +98,10 @@ jobs: uses: ncipollo/release-action@v1 if: github.ref_type == 'tag' with: - artifacts: 'bin/SMAPI-${{env.VERSION}}*.zip' + artifacts: 'bin/SMAPI-${{env.VERSION}}-installer.zip' token: '${{secrets.GITHUB_TOKEN}}' tag: '${{github.ref_name}}' - name: "${{env.VERSION}}" + name: '${{env.VERSION}}' body: | Draft. draft: true diff --git a/build/scripts/prepare-install-package.ps1 b/build/scripts/prepare-install-package.ps1 index fc512714..45e27dec 100644 --- a/build/scripts/prepare-install-package.ps1 +++ b/build/scripts/prepare-install-package.ps1 @@ -283,7 +283,7 @@ foreach ($folder in $folders) { ########### Move-Item "$packagePath" "bin/SMAPI $version installer" -Compress-Archive -Path "bin/SMAPI $version installer" -DestinationPath "bin/SMAPI $version installer.zip" -CompressionLevel Optimal +Compress-Archive -Path "bin/SMAPI $version installer" -DestinationPath "bin/SMAPI-$version-installer.zip" -CompressionLevel Optimal Write-Output "" Write-Output "Done! Package created in ${pwd.Path}/bin." From 5244b663f4175c1853358b013b950f72bcdb3e43 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 25 Jan 2026 20:08:43 -0500 Subject: [PATCH 26/26] prepare for release --- build/common.targets | 2 +- docs/release-notes.md | 9 ++++++--- src/SMAPI.Mods.ConsoleCommands/manifest.json | 4 ++-- src/SMAPI.Mods.SaveBackup/manifest.json | 4 ++-- src/SMAPI/Constants.cs | 6 +++--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/build/common.targets b/build/common.targets index af24c8d7..05cfd8b5 100644 --- a/build/common.targets +++ b/build/common.targets @@ -7,7 +7,7 @@ repo. It imports the other MSBuild files as needed. - 4.4.0 + 4.5.0 SMAPI latest $(AssemblySearchPaths);{GAC} diff --git a/docs/release-notes.md b/docs/release-notes.md index c4331501..76f17c29 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,16 +1,19 @@ ← [README](README.md) # Release notes -## Upcoming release +## 4.5.0 +Released 25 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/149054246). + * For players: - * Added in-game config UI via [Generic Mod Config Menu](). + * Added in-game config UI for SMAPI via [Generic Mod Config Menu](https://www.nexusmods.com/stardewvalley/mods/5098). * SMAPI now uses [automated and attested builds](https://www.patreon.com/posts/automated-builds-148417912) (thanks to DecidedlyHuman)! _This improves the security and transparency of SMAPI builds. Every step to build SMAPI from the public source code is now public and verifiable, with file signatures to let players and tools confirm the build hasn't been tampered with._ * SMAPI can now detect known malicious loose files in the `Mods` folder. * Updated internal mod blacklist. * For mod authors: - * SMAPI no longer has a separate 'for developers' version. Instead, you can now use [Generic Mod Config Menu]() to enable 'developer mode' in the console window options. + * SMAPI no longer has a separate 'for developers' version. + _Instead, you can now use [Generic Mod Config Menu](https://www.nexusmods.com/stardewvalley/mods/5098) to enable 'developer mode' in the console window options._ ## 4.4.0 Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705). diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index 3f44f80c..9193514a 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": "4.4.0", + "Version": "4.5.0", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueId": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "4.4.0" + "MinimumApiVersion": "4.5.0" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index d3d054e6..aa5d53a9 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": "4.4.0", + "Version": "4.5.0", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueId": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "4.4.0" + "MinimumApiVersion": "4.5.0" } diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 06e066eb..21673fcc 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -49,7 +49,7 @@ internal static class EarlyConstants internal static int? LogScreenId { get; set; } /// SMAPI's current raw semantic version. - internal static string RawApiVersion = "4.4.0"; + internal static string RawApiVersion = "4.5.0"; } /// Contains SMAPI's constants and assumptions. @@ -108,7 +108,7 @@ public static class Constants #if DEBUG true; #else - false; + false; #endif /// The URL of the SMAPI home page. @@ -124,7 +124,7 @@ public static class Constants internal static string ApiUserConfigPath => Path.Combine(Constants.InternalFilesPath, "config.user.json"); /// The file path for the per-mods-folder override file, which is applied over it. - internal static string ApiModGroupConfigPath => Path.Combine(ModsPath, "SMAPI-config.json"); + internal static string ApiModGroupConfigPath => Path.Combine(Constants.ModsPath, "SMAPI-config.json"); /// The file path for the SMAPI metadata file. internal static string ApiMetadataPath => Path.Combine(Constants.InternalFilesPath, "metadata.json");