mirror of
https://github.com/Pathoschild/SMAPI.git
synced 2026-10-12 02:08:51 +00:00
Merge branch 'develop' into stable
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
name: Build SMAPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+" # stable version tag
|
||||
branches:
|
||||
- develop
|
||||
- build-test
|
||||
|
||||
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 SMAPI assembly version
|
||||
id: smapi-version
|
||||
shell: pwsh
|
||||
run: |
|
||||
[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 "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
|
||||
}
|
||||
|
||||
"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 assembly version matches tag
|
||||
if: github.ref_type == 'tag'
|
||||
shell: pwsh
|
||||
run: |
|
||||
if ($env:VERSION -ne '${{github.ref_name}}') {
|
||||
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"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Update SMAPI assembly version
|
||||
if: github.ref_type != 'tag'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$updatedPatch = [int]${{steps.smapi-version.outputs.smapi_patch}} + 1
|
||||
$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
|
||||
|
||||
Write-Host "This is a dev version. Building as version: $updatedVersion"
|
||||
|
||||
./build/scripts/set-smapi-version.ps1 "$updatedVersion"
|
||||
Write-Host 'Version updated.'
|
||||
|
||||
- name: Checkout game reference assemblies
|
||||
run: |
|
||||
git clone --depth 1 https://github.com/StardewModders/mod-reference-assemblies.git "$HOME/StardewValley"
|
||||
|
||||
- name: Build SMAPI
|
||||
shell: pwsh
|
||||
run: |
|
||||
Write-Host "Building SMAPI $env:VERSION."
|
||||
./build/scripts/prepare-install-package.ps1 "$env:VERSION"
|
||||
|
||||
- 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: Create GitHub release
|
||||
uses: ncipollo/release-action@v1
|
||||
if: github.ref_type == 'tag'
|
||||
with:
|
||||
artifacts: 'bin/SMAPI-${{env.VERSION}}-installer.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
|
||||
if: github.ref_type == 'tag'
|
||||
with:
|
||||
subject-path: bin/SMAPI-${{env.VERSION}}-installer.zip
|
||||
@@ -7,7 +7,7 @@ repo. It imports the other MSBuild files as needed.
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<!--set general build properties -->
|
||||
<Version>4.4.0</Version>
|
||||
<Version>4.5.0</Version>
|
||||
<Product>SMAPI</Product>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##########
|
||||
## Read config
|
||||
##########
|
||||
# get SMAPI version
|
||||
version="$1"
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "SMAPI release version (like '4.0.0'):"
|
||||
read version
|
||||
fi
|
||||
|
||||
# get Windows bin path
|
||||
windowsBinPath="$2"
|
||||
if [ $# -le 1 ]; then
|
||||
echo "Windows compiled bin path:"
|
||||
read windowsBinPath
|
||||
fi
|
||||
|
||||
# installer internal folders
|
||||
buildFolders=("linux" "macOS" "windows")
|
||||
|
||||
|
||||
##########
|
||||
## Finalize release package
|
||||
##########
|
||||
# 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 {} \;
|
||||
|
||||
# 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 ""
|
||||
|
||||
echo "Done!"
|
||||
@@ -1,4 +1,4 @@
|
||||
function In-Place-Regex {
|
||||
function In-Place-Regex {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$Path,
|
||||
[Parameter(Mandatory)][string]$Search,
|
||||
@@ -0,0 +1,289 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
#
|
||||
#
|
||||
# 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
|
||||
##########
|
||||
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"
|
||||
)
|
||||
}
|
||||
|
||||
$gamePath = ""
|
||||
foreach ($possibleGamePath in $possibleGamePaths) {
|
||||
if (Test-Path $possibleGamePath -PathType Container) {
|
||||
$gamePath = $possibleGamePath
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########
|
||||
## Preset values
|
||||
##########
|
||||
# paths
|
||||
$bundleModNames = "ConsoleCommands", "SaveBackup"
|
||||
|
||||
# build configuration
|
||||
$buildConfig = "Release"
|
||||
$framework = "net6.0"
|
||||
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]
|
||||
if (!$version) {
|
||||
$version = Read-Host "SMAPI release version (like '4.0.0')"
|
||||
}
|
||||
|
||||
|
||||
##########
|
||||
## Move to SMAPI root
|
||||
##########
|
||||
Set-Location "$PSScriptRoot/../.."
|
||||
|
||||
|
||||
##########
|
||||
## Clear old build files
|
||||
##########
|
||||
Write-Output "Clearing old builds..."
|
||||
Write-Output "-------------------------------------------------"
|
||||
|
||||
foreach ($path in (Get-ChildItem -Recurse -Include ('bin', 'obj'))) {
|
||||
Write-Output "$path"
|
||||
Remove-Item -Recurse -Force "$path"
|
||||
}
|
||||
Write-Output ""
|
||||
|
||||
|
||||
##########
|
||||
## Compile files
|
||||
##########
|
||||
. "$PSScriptRoot/set-smapi-version.ps1" "$version"
|
||||
foreach ($folder in $folders) {
|
||||
$runtime = $runtimes[$folder]
|
||||
$msbuildPlatformName = $msBuildPlatformNames[$folder]
|
||||
|
||||
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
|
||||
Write-Output ""
|
||||
Write-Output ""
|
||||
|
||||
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
|
||||
Write-Output ""
|
||||
Write-Output ""
|
||||
|
||||
foreach ($modName in $bundleModNames) {
|
||||
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
|
||||
Write-Output ""
|
||||
Write-Output ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########
|
||||
## Prepare install package
|
||||
##########
|
||||
Write-Output "Preparing install package..."
|
||||
Write-Output "----------------------------"
|
||||
|
||||
# init paths
|
||||
$installAssets = "src/SMAPI.Installer/assets"
|
||||
$packagePath = "bin/SMAPI installer"
|
||||
|
||||
# init structure
|
||||
foreach ($folder in $folders) {
|
||||
$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
|
||||
foreach ($name in @("install on Linux.sh", "install on macOS.command", "install on Windows.bat", "README.txt")) {
|
||||
if ($windowsOnly -and ($name -eq "install on Linux.sh" -or $name -eq "install on macOS.command")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Copy-Item "$installAssets/$name" "$packagePath"
|
||||
}
|
||||
|
||||
# copy per-platform files
|
||||
foreach ($folder in $folders) {
|
||||
$runtime = $runtimes[$folder]
|
||||
|
||||
# get paths
|
||||
$smapiBin = "src/SMAPI/bin/$buildConfig/$runtime/publish"
|
||||
$internalPath = "$packagePath/internal/$folder"
|
||||
$bundlePath = "$internalPath/bundle"
|
||||
|
||||
# installer files
|
||||
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 6.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of
|
||||
# minor.
|
||||
Copy-Item "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json"
|
||||
|
||||
# installer DLL config
|
||||
if ($folder -eq "windows") {
|
||||
Copy-Item "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config"
|
||||
}
|
||||
|
||||
# bundle root files
|
||||
foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.xml", "steam_appid.txt")) {
|
||||
if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") {
|
||||
$name = "$name.exe"
|
||||
}
|
||||
|
||||
Copy-Item "$smapiBin/$name" "$bundlePath"
|
||||
}
|
||||
|
||||
# bundle i18n
|
||||
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")) {
|
||||
Copy-Item "$smapiBin/$name" "$bundlePath/smapi-internal"
|
||||
}
|
||||
|
||||
if ($folder -eq "windows") {
|
||||
Copy-Item "$smapiBin/VdfConverter.dll" "$bundlePath/smapi-internal"
|
||||
}
|
||||
|
||||
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") {
|
||||
Copy-Item "$installAssets/unix-launcher.sh" "$bundlePath"
|
||||
}
|
||||
else {
|
||||
Copy-Item "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
|
||||
}
|
||||
|
||||
# copy bundled mods
|
||||
foreach ($modName in $bundleModNames) {
|
||||
$fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
|
||||
$targetPath = "$bundlePath/Mods/$modName"
|
||||
|
||||
if ($IsWindows) {
|
||||
# On Windows, mkdir creates parent directories automatically and the --parents argument isn't recognized.
|
||||
mkdir "$targetPath" > $null
|
||||
}
|
||||
else
|
||||
{
|
||||
mkdir "$targetPath" --parents
|
||||
}
|
||||
|
||||
Copy-Item "$fromPath/$modName.dll" "$targetPath"
|
||||
Copy-Item "$fromPath/manifest.json" "$targetPath"
|
||||
if (Test-Path "$fromPath/i18n" -PathType Container) {
|
||||
Copy-Item -Recurse "$fromPath/i18n" "$targetPath"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# mark scripts executable
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# convert bundle folder into final 'install.dat' files
|
||||
foreach ($folder in $folders) {
|
||||
$path = "$packagePath/internal/$folder"
|
||||
|
||||
Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.dat"
|
||||
if (!$skipBundleDeletion) {
|
||||
Remove-Item -Recurse -Force "$path/bundle"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
###########
|
||||
### Create release zips
|
||||
###########
|
||||
Move-Item "$packagePath" "bin/SMAPI $version installer"
|
||||
|
||||
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."
|
||||
Regular → Executable
+2
-8
@@ -1,10 +1,4 @@
|
||||
#
|
||||
#
|
||||
# This is the PowerShell equivalent of ../unix/set-smapi-version.sh.
|
||||
# When making changes, both scripts should be updated.
|
||||
#
|
||||
#
|
||||
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
. "$PSScriptRoot\lib\in-place-regex.ps1"
|
||||
|
||||
@@ -15,7 +9,7 @@ if (!$version) {
|
||||
}
|
||||
|
||||
# move to SMAPI root
|
||||
cd "$PSScriptRoot/../.."
|
||||
Set-Location "$PSScriptRoot/../.."
|
||||
|
||||
# apply changes
|
||||
In-Place-Regex -Path "build/common.targets" -Search "<Version>.+</Version>" -Replace "<Version>$version</Version>"
|
||||
@@ -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"
|
||||
@@ -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>$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
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##########
|
||||
## Read config
|
||||
##########
|
||||
# get SMAPI version
|
||||
version="$1"
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "SMAPI release version (like '4.0.0'):"
|
||||
read version
|
||||
fi
|
||||
|
||||
# get Windows bin path
|
||||
windowsBinPath="$2"
|
||||
if [ $# -le 1 ]; then
|
||||
echo "Windows compiled bin path:"
|
||||
read windowsBinPath
|
||||
fi
|
||||
|
||||
# installer internal folders
|
||||
buildFolders=("linux" "macOS" "windows")
|
||||
|
||||
|
||||
##########
|
||||
## Finalize release package
|
||||
##########
|
||||
for folderName in "SMAPI $version installer" "SMAPI $version installer for developers"; do
|
||||
# move files to Linux filesystem
|
||||
echo "Preparing $folderName.zip..."
|
||||
echo "-------------------------------------------------"
|
||||
echo "copying '$windowsBinPath/$folderName' to Linux filesystem..."
|
||||
cp -r "$windowsBinPath/$folderName" .
|
||||
|
||||
# fix permissions
|
||||
echo "fixing permissions..."
|
||||
find "$folderName" -type d -exec chmod 755 {} \;
|
||||
find "$folderName" -type f -exec chmod 644 {} \;
|
||||
find "$folderName" -name "*.sh" -exec chmod 755 {} \;
|
||||
find "$folderName" -name "*.command" -exec chmod 755 {} \;
|
||||
find "$folderName" -name "SMAPI.Installer" -exec chmod 755 {} \;
|
||||
find "$folderName" -name "StardewModdingAPI" -exec chmod 755 {} \;
|
||||
|
||||
# convert bundle folder into final 'install.dat' files
|
||||
for build in ${buildFolders[@]}; do
|
||||
echo "packaging $folderName/internal/$build/install.dat..."
|
||||
pushd "$folderName/internal/$build/bundle" > /dev/null
|
||||
zip "install.dat" * --recurse-paths --quiet
|
||||
mv install.dat ../
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf "$folderName/internal/$build/bundle"
|
||||
done
|
||||
|
||||
# zip installer
|
||||
echo "packaging installer..."
|
||||
zip -9 "$folderName.zip" "$folderName" --recurse-paths --quiet
|
||||
|
||||
# move zip back to Windows bin path
|
||||
echo "moving release zip to $windowsBinPath/$folderName.zip..."
|
||||
mv "$folderName.zip" "$windowsBinPath"
|
||||
rm -rf "$folderName"
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "Done!"
|
||||
@@ -1,257 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
|
||||
. "$PSScriptRoot/lib/in-place-regex.ps1"
|
||||
|
||||
|
||||
##########
|
||||
## 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",
|
||||
|
||||
# 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) {
|
||||
$gamePath = $possibleGamePath
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########
|
||||
## Preset values
|
||||
##########
|
||||
# paths
|
||||
$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" }
|
||||
|
||||
# version number
|
||||
$version = $args[0]
|
||||
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/../.."
|
||||
|
||||
|
||||
##########
|
||||
## Clear old build files
|
||||
##########
|
||||
echo "Clearing old builds..."
|
||||
echo "-------------------------------------------------"
|
||||
|
||||
foreach ($path in (dir -Recurse -Include ('bin', 'obj'))) {
|
||||
echo "$path"
|
||||
rm -Recurse -Force "$path"
|
||||
}
|
||||
echo ""
|
||||
|
||||
|
||||
##########
|
||||
## Compile files
|
||||
##########
|
||||
. "$PSScriptRoot/set-smapi-version.ps1" "$version"
|
||||
foreach ($folder in $folders) {
|
||||
$runtime = $runtimes[$folder]
|
||||
$msbuildPlatformName = $msBuildPlatformNames[$folder]
|
||||
|
||||
echo "Compiling SMAPI for $folder..."
|
||||
echo "-------------------------------------------------"
|
||||
dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" --framework "$framework" -p:OS="$msbuildPlatformName" -p:TargetFrameworks="$framework" -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" --framework "$framework" -p:OS="$msbuildPlatformName" -p:TargetFrameworks="$framework" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
foreach ($modName in $bundleModNames) {
|
||||
echo "Compiling $modName for $folder..."
|
||||
echo "-------------------------------------------------"
|
||||
dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" --framework "$framework" -p:OS="$msbuildPlatformName" -p:TargetFrameworks="$framework" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained false
|
||||
echo ""
|
||||
echo ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########
|
||||
## Prepare install package
|
||||
##########
|
||||
echo "Preparing install package..."
|
||||
echo "----------------------------"
|
||||
|
||||
# init paths
|
||||
$installAssets = "src/SMAPI.Installer/assets"
|
||||
$packagePath = "bin/SMAPI installer"
|
||||
$packageDevPath = "bin/SMAPI installer for developers"
|
||||
|
||||
# init structure
|
||||
foreach ($folder in $folders) {
|
||||
mkdir "$packagePath/internal/$folder/bundle/smapi-internal" > $null
|
||||
}
|
||||
|
||||
# copy base installer files
|
||||
foreach ($name in @("install on Linux.sh", "install on macOS.command", "install on Windows.bat", "README.txt")) {
|
||||
if ($windowsOnly -and ($name -eq "install on Linux.sh" -or $name -eq "install on macOS.command")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cp "$installAssets/$name" "$packagePath"
|
||||
}
|
||||
|
||||
# copy per-platform files
|
||||
foreach ($folder in $folders) {
|
||||
$runtime = $runtimes[$folder]
|
||||
|
||||
# get paths
|
||||
$smapiBin = "src/SMAPI/bin/$buildConfig/$runtime/publish"
|
||||
$internalPath = "$packagePath/internal/$folder"
|
||||
$bundlePath = "$internalPath/bundle"
|
||||
|
||||
# installer files
|
||||
cp "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish/*" "$internalPath" -Recurse
|
||||
rm -Recurse -Force "$internalPath/assets"
|
||||
|
||||
# runtime config for SMAPI
|
||||
# This is identical to the one generated by the build, except that the min runtime version is
|
||||
# set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of
|
||||
# minor.
|
||||
cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json"
|
||||
|
||||
# installer DLL config
|
||||
if ($folder -eq "windows") {
|
||||
cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config"
|
||||
}
|
||||
|
||||
# bundle root files
|
||||
foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.xml", "steam_appid.txt")) {
|
||||
if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") {
|
||||
$name = "$name.exe"
|
||||
}
|
||||
|
||||
cp "$smapiBin/$name" "$bundlePath"
|
||||
}
|
||||
|
||||
# bundle i18n
|
||||
cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal"
|
||||
|
||||
# bundle smapi-internal
|
||||
foreach ($name in @("0Harmony.dll", "0Harmony.xml", "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"
|
||||
}
|
||||
|
||||
if ($folder -eq "windows") {
|
||||
cp "$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"
|
||||
if ($folder -eq "linux" -or $folder -eq "macOS") {
|
||||
cp "$installAssets/unix-launcher.sh" "$bundlePath"
|
||||
}
|
||||
else {
|
||||
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
|
||||
}
|
||||
|
||||
# copy bundled mods
|
||||
foreach ($modName in $bundleModNames) {
|
||||
$fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
|
||||
$targetPath = "$bundlePath/Mods/$modName"
|
||||
|
||||
mkdir "$targetPath" > $null
|
||||
|
||||
cp "$fromPath/$modName.dll" "$targetPath"
|
||||
cp "$fromPath/manifest.json" "$targetPath"
|
||||
if (Test-Path "$fromPath/i18n" -PathType Container) {
|
||||
cp -Recurse "$fromPath/i18n" "$targetPath"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# DISABLED: will be handled by Linux script
|
||||
# mark scripts executable
|
||||
#ForEach ($path in @("install on Linux.sh", "install on macOS.command", "bundle/unix-launcher.sh")) {
|
||||
# if (Test-Path "$packagePath/$path" -PathType Leaf) {
|
||||
# chmod 755 "$packagePath/$path"
|
||||
# }
|
||||
#}
|
||||
|
||||
# split into main + for-dev folders
|
||||
cp -Recurse "$packagePath" "$packageDevPath"
|
||||
foreach ($folder in $folders) {
|
||||
# disable developer mode in main package
|
||||
In-Place-Regex -Path "$packagePath/internal/$folder/bundle/smapi-internal/config.json" -Search "`"DeveloperMode`": true" -Replace "`"DeveloperMode`": false"
|
||||
|
||||
# convert bundle folder into final 'install.dat' files
|
||||
if ($windowsOnly)
|
||||
{
|
||||
foreach ($path in @("$packagePath/internal/$folder", "$packageDevPath/internal/$folder"))
|
||||
{
|
||||
Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.zip"
|
||||
mv "$path/install.zip" "$path/install.dat"
|
||||
rm -Recurse -Force "$path/bundle"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
###########
|
||||
### Create release zips
|
||||
###########
|
||||
# rename folders
|
||||
mv "$packagePath" "bin/SMAPI $version installer"
|
||||
mv "$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
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Done! See docs/technical/smapi.md to create the release zips."
|
||||
+16
-16
@@ -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
|
||||
|
||||
+1373
-29
File diff suppressed because it is too large
Load Diff
+19
-1349
File diff suppressed because it is too large
Load Diff
+77
-71
@@ -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 | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
|
||||
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
|
||||
release | `<version>` | `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,88 +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. Run `sudo apt update` in WSL to update the package list.
|
||||
3. The rest of the instructions below should be run in WSL.
|
||||
2. Install the required software:
|
||||
1. Install the [.NET 5 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu).
|
||||
_For Ubuntu-based systems, you can run `lsb_release -a` to get the Ubuntu version number._
|
||||
2. [Install Steam](https://linuxconfig.org/how-to-install-steam-on-ubuntu-20-04-focal-fossa-linux).
|
||||
3. Launch `steam` and install the game like usual.
|
||||
4. Download and install your preferred IDE. For the [latest standalone Rider
|
||||
version](https://www.jetbrains.com/help/rider/Installation_guide.html#prerequisites):
|
||||
```sh
|
||||
wget "<download url here>" -O rider-install.tar.gz
|
||||
sudo tar -xzvf rider-install.tar.gz -C /opt
|
||||
ln -s "/opt/JetBrains Rider-<version>/bin/rider.sh"
|
||||
./rider.sh
|
||||
```
|
||||
3. Clone the SMAPI repo:
|
||||
2. Follow the rest of the instructions inside WSL.
|
||||
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.
|
||||
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. Make sure you use a [semantic version](#version-format).
|
||||
|
||||
### Prepare the release
|
||||
1. Run `build/unix/prepare-install-package.sh VERSION_HERE` to create the release package in the
|
||||
root `bin` folder.
|
||||
### 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.
|
||||
|
||||
Make sure you use a [semantic version](https://semver.org). Recommended format:
|
||||
> [!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`.
|
||||
|
||||
build type | format | example
|
||||
:--------- | :----------------------- | :------
|
||||
dev build | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
|
||||
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
|
||||
release | `<version>` | `4.0.0`
|
||||
|
||||
### 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
|
||||
1. Run `build/windows/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:
|
||||
|
||||
build type | format | example
|
||||
:--------- | :----------------------- | :------
|
||||
dev build | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
|
||||
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
|
||||
release | `<version>` | `4.0.0`
|
||||
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](#version-format).
|
||||
|
||||
2. Launch WSL and run this script:
|
||||
```bash
|
||||
@@ -169,11 +178,8 @@ 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"
|
||||
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).
|
||||
|
||||
@@ -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?");
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StardewModdingAPI.Internal.ConsoleWriting;
|
||||
|
||||
/// <summary>The console color scheme options.</summary>
|
||||
internal class ColorSchemeConfig
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The default color scheme ID to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</summary>
|
||||
public MonitorColorScheme UseScheme { get; set; }
|
||||
|
||||
/// <summary>The available console color schemes.</summary>
|
||||
public IDictionary<MonitorColorScheme, IDictionary<ConsoleLogLevel, ConsoleColor>> Schemes { get; set; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="useScheme">The default color scheme ID to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <param name="schemes">The available console color schemes.</param>
|
||||
public ColorSchemeConfig(MonitorColorScheme useScheme, IDictionary<MonitorColorScheme, IDictionary<ConsoleLogLevel, ConsoleColor>> schemes)
|
||||
{
|
||||
this.UseScheme = useScheme;
|
||||
this.Schemes = schemes;
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,15 @@ internal class ColorfulConsoleWriter : IConsoleWriter
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The target platform.</summary>
|
||||
private readonly Platform Platform;
|
||||
|
||||
/// <summary>The console text color for each log level.</summary>
|
||||
private readonly IDictionary<ConsoleLogLevel, ConsoleColor>? Colors;
|
||||
private IDictionary<ConsoleLogLevel, ConsoleColor>? Colors;
|
||||
|
||||
/// <summary>Whether the current console supports color formatting.</summary>
|
||||
[MemberNotNullWhen(true, nameof(ColorfulConsoleWriter.Colors))]
|
||||
private bool SupportsColor { get; }
|
||||
private bool SupportsColor { get; set; }
|
||||
|
||||
|
||||
/*********
|
||||
@@ -25,14 +28,25 @@ internal class ColorfulConsoleWriter : IConsoleWriter
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="platform">The target platform.</param>
|
||||
public ColorfulConsoleWriter(Platform platform)
|
||||
: this(platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.AutoDetect)) { }
|
||||
: this(platform, MonitorColorScheme.AutoDetect, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.AutoDetect)) { }
|
||||
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="platform">The target platform.</param>
|
||||
/// <param name="colorSchemeId">The color scheme ID in <paramref name="colorConfig"/> to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <param name="colorConfig">The colors to use for text written to the SMAPI console.</param>
|
||||
public ColorfulConsoleWriter(Platform platform, ColorSchemeConfig colorConfig)
|
||||
public ColorfulConsoleWriter(Platform platform, MonitorColorScheme colorSchemeId, Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> colorConfig)
|
||||
{
|
||||
if (colorConfig.UseScheme == MonitorColorScheme.None)
|
||||
this.Platform = platform;
|
||||
|
||||
this.SetColors(colorSchemeId, colorConfig);
|
||||
}
|
||||
|
||||
/// <summary>Set the color scheme to apply.</summary>
|
||||
/// <param name="colorSchemeId">The color scheme ID in <paramref name="colorSchemes"/> to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <param name="colorSchemes">The colors to use for text written to the SMAPI console.</param>
|
||||
public void SetColors(MonitorColorScheme colorSchemeId, Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> colorSchemes)
|
||||
{
|
||||
if (colorSchemeId == MonitorColorScheme.None)
|
||||
{
|
||||
this.SupportsColor = false;
|
||||
this.Colors = null;
|
||||
@@ -40,7 +54,7 @@ internal class ColorfulConsoleWriter : IConsoleWriter
|
||||
else
|
||||
{
|
||||
this.SupportsColor = this.TestColorSupport();
|
||||
this.Colors = this.GetConsoleColorScheme(platform, colorConfig);
|
||||
this.Colors = ColorfulConsoleWriter.GetConsoleColorScheme(this.Platform, colorSchemeId, colorSchemes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,34 +87,31 @@ internal class ColorfulConsoleWriter : IConsoleWriter
|
||||
/// <summary>Get the default color scheme config for cases where it's not configurable (e.g. the installer).</summary>
|
||||
/// <param name="useScheme">The default color scheme ID to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <remarks>The colors here should be kept in sync with the SMAPI config file.</remarks>
|
||||
public static ColorSchemeConfig GetDefaultColorSchemeConfig(MonitorColorScheme useScheme)
|
||||
public static Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> GetDefaultColorSchemeConfig(MonitorColorScheme useScheme)
|
||||
{
|
||||
return new ColorSchemeConfig(
|
||||
useScheme: useScheme,
|
||||
schemes: new Dictionary<MonitorColorScheme, IDictionary<ConsoleLogLevel, ConsoleColor>>
|
||||
return new Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>>
|
||||
{
|
||||
[MonitorColorScheme.DarkBackground] = new()
|
||||
{
|
||||
[MonitorColorScheme.DarkBackground] = new Dictionary<ConsoleLogLevel, ConsoleColor>
|
||||
{
|
||||
[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, ConsoleColor>
|
||||
{
|
||||
[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 +134,22 @@ internal class ColorfulConsoleWriter : IConsoleWriter
|
||||
|
||||
/// <summary>Get the color scheme to use for the current console.</summary>
|
||||
/// <param name="platform">The target platform.</param>
|
||||
/// <param name="colorSchemeId">The color scheme ID in <paramref name="colorConfig"/> to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <param name="colorConfig">The colors to use for text written to the SMAPI console.</param>
|
||||
private IDictionary<ConsoleLogLevel, ConsoleColor> GetConsoleColorScheme(Platform platform, ColorSchemeConfig colorConfig)
|
||||
private static IDictionary<ConsoleLogLevel, ConsoleColor> GetConsoleColorScheme(Platform platform, MonitorColorScheme colorSchemeId, Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> 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<ConsoleLogLevel, ConsoleColor>? scheme)
|
||||
return colorConfig.TryGetValue(colorSchemeId, out Dictionary<ConsoleLogLevel, ConsoleColor>? scheme)
|
||||
? scheme
|
||||
: throw new NotSupportedException($"Unknown color scheme '{schemeId}'.");
|
||||
: throw new NotSupportedException($"Unknown color scheme '{colorSchemeId}'.");
|
||||
}
|
||||
|
||||
/// <summary>Get whether a console color should be considered dark, which is subjectively defined as 'white looks better than black on this text'.</summary>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\ColorfulConsoleWriter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\ColorSchemeConfig.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\ConsoleLogLevel.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\IConsoleWriter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\MonitorColorScheme.cs" />
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace StardewModdingAPI.Toolkit.Framework.ModBlacklistData;
|
||||
|
||||
/// <summary>A loose file entry in the <see cref="ModBlacklistModel"/>.</summary>
|
||||
public class LooseFileBlacklistEntryModel
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The file name to block (if any).</summary>
|
||||
public string? Name { get; }
|
||||
|
||||
/// <summary>The file extension to block (if any), including the dot.</summary>
|
||||
public string? Extension { get; }
|
||||
|
||||
/// <summary>The MD5 hash to block (if any).</summary>
|
||||
public string? Hash { get; }
|
||||
|
||||
/// <summary>A player-friendly explanation of why the mod is blocked and what they should do next.</summary>
|
||||
public string? Message { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="name"><inheritdoc cref="Name" path="/summary"/></param>
|
||||
/// <param name="extension"><inheritdoc cref="Extension" path="/summary"/></param>
|
||||
/// <param name="hash"><inheritdoc cref="Hash" path="/summary"/></param>
|
||||
/// <param name="message"><inheritdoc cref="Message" path="/summary"/></param>
|
||||
public LooseFileBlacklistEntryModel(string? name, string? extension, string? hash, string message)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Extension = extension;
|
||||
this.Hash = hash;
|
||||
this.Message = message;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*********/
|
||||
/// <summary>Construct an empty instance.</summary>
|
||||
public ModBlacklist()
|
||||
: this(new ModBlacklistModel([])) { }
|
||||
: this(new ModBlacklistModel([], [])) { }
|
||||
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="data">The underlying mod blacklist data.</param>
|
||||
@@ -30,7 +32,7 @@ public class ModBlacklist
|
||||
/// <summary>Get the blacklist entry for a mod, if any.</summary>
|
||||
/// <param name="modId">The unique mod ID.</param>
|
||||
/// <param name="entryDllPath">The absolute path to the entry DLL, if this is a C# mod.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Scan a folder to detect any files which match the list of malicious files.</summary>
|
||||
/// <param name="rootPath">The root path to scan.</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get the blacklist entry for a loose file, if any.</summary>
|
||||
/// <param name="fullPath">The absolute path to the file to check.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,17 @@ namespace StardewModdingAPI.Toolkit.Framework.ModBlacklistData;
|
||||
/// <summary>A mod entry in the <see cref="ModBlacklistModel"/>.</summary>
|
||||
public class ModBlacklistEntryModel
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The manifest IDs to block (if any).</summary>
|
||||
public string? Id { get; }
|
||||
|
||||
/// <summary>The MD5 hashes of the entry DLL to block (if any).</summary>
|
||||
/// <remarks>Due to the chance of MD5 collisions, this should only be used in addition to the <see cref="Id"/>.</remarks>
|
||||
/// <summary>The MD5 hash of the entry DLL to block (if any).</summary>
|
||||
public string? EntryDllHash { get; }
|
||||
|
||||
/// <summary>A player-friendly explanation of why the mod is blocked and what they should do next.</summary>
|
||||
public string Message { get; }
|
||||
public string? Message { get; }
|
||||
|
||||
|
||||
/*********
|
||||
|
||||
@@ -9,14 +9,19 @@ public class ModBlacklistModel
|
||||
/// <summary>Metadata about malicious or harmful SMAPI mods which are disabled by default.</summary>
|
||||
public ModBlacklistEntryModel[] Blacklist { get; }
|
||||
|
||||
/// <summary>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.</summary>
|
||||
public LooseFileBlacklistEntryModel[] LooseFileBlacklist { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="blacklist"><inheritdoc cref="Blacklist" path="/summary"/></param>
|
||||
public ModBlacklistModel(ModBlacklistEntryModel[]? blacklist)
|
||||
/// <param name="looseFileBlacklist"><inheritdoc cref="LooseFileBlacklist" path="/summary"/></param>
|
||||
public ModBlacklistModel(ModBlacklistEntryModel[]? blacklist, LooseFileBlacklistEntryModel[]? looseFileBlacklist)
|
||||
{
|
||||
this.Blacklist = blacklist ?? [];
|
||||
this.LooseFileBlacklist = looseFileBlacklist ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-(?<version>[\d\.]+(?:-.+)?)-installer(?<forDevs>-for-developers)?.zip");
|
||||
Match match = Regex.Match(asset.FileName, @"SMAPI-(?<version>[\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
|
||||
/// <summary>The SMAPI version.</summary>
|
||||
public ISemanticVersion Version { get; }
|
||||
|
||||
/// <summary>Whether this is a 'for developers' download.</summary>
|
||||
public bool IsForDevs { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
@@ -158,13 +154,11 @@ internal class IndexController : Controller
|
||||
/// <param name="release">The underlying GitHub release.</param>
|
||||
/// <param name="asset">The underlying download asset.</param>
|
||||
/// <param name="version">The SMAPI version.</param>
|
||||
/// <param name="isForDevs">Whether this is a 'for developers' download.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,6 @@
|
||||
|
||||
<h2 id="modcreators">For mod creators</h2>
|
||||
<ul>
|
||||
<li><a href="@Model.StableVersion.DevDownloadUrl">SMAPI @Model.StableVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li>
|
||||
<li><a href="https://stardewvalleywiki.com/Modding:Index">Modding documentation</a></li>
|
||||
<li><a href="https://github.com/Pathoschild/SMAPI">Source code</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -7,12 +7,25 @@
|
||||
"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": [
|
||||
// 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."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
|
||||
+7
-10
@@ -9,6 +9,7 @@
|
||||
<Folder Name="/.root/.github/">
|
||||
<File Path="../.github/CONTRIBUTING.md" />
|
||||
<File Path="../.github/SUPPORT.md" />
|
||||
<File Path="../.github/workflows/build-smapi.yml" />
|
||||
</Folder>
|
||||
<Folder Name="/.root/.github/ISSUE_TEMPLATE/">
|
||||
<File Path="../.github/ISSUE_TEMPLATE/config.yml" />
|
||||
@@ -19,17 +20,13 @@
|
||||
<File Path="../build/deploy-local-smapi.targets" />
|
||||
<File Path="../build/find-game-folder.targets" />
|
||||
</Folder>
|
||||
<Folder Name="/.root/build/unix/">
|
||||
<File Path="../build/unix/prepare-install-package.sh" />
|
||||
<File Path="../build/unix/set-smapi-version.sh" />
|
||||
<Folder Name="/.root/build/scripts/">
|
||||
<File Path="../build/scripts/finalize-install-package.sh" />
|
||||
<File Path="../build/scripts/prepare-install-package.ps1" />
|
||||
<File Path="../build/scripts/set-smapi-version.ps1" />
|
||||
</Folder>
|
||||
<Folder Name="/.root/build/windows/">
|
||||
<File Path="../build/windows/finalize-install-package.sh" />
|
||||
<File Path="../build/windows/prepare-install-package.ps1" />
|
||||
<File Path="../build/windows/set-smapi-version.ps1" />
|
||||
</Folder>
|
||||
<Folder Name="/.root/build/windows/lib/">
|
||||
<File Path="../build/windows/lib/in-place-regex.ps1" />
|
||||
<Folder Name="/.root/build/scripts/lib/">
|
||||
<File Path="../build/scripts/lib/in-place-regex.ps1" />
|
||||
</Folder>
|
||||
<Folder Name="/.root/docs/">
|
||||
<File Path="../docs/mod-build-config.md" />
|
||||
|
||||
@@ -49,7 +49,7 @@ internal static class EarlyConstants
|
||||
internal static int? LogScreenId { get; set; }
|
||||
|
||||
/// <summary>SMAPI's current raw semantic version.</summary>
|
||||
internal static string RawApiVersion = "4.4.0";
|
||||
internal static string RawApiVersion = "4.5.0";
|
||||
}
|
||||
|
||||
/// <summary>Contains SMAPI's constants and assumptions.</summary>
|
||||
@@ -108,7 +108,7 @@ public static class Constants
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
false;
|
||||
#endif
|
||||
|
||||
/// <summary>The URL of the SMAPI home page.</summary>
|
||||
@@ -124,7 +124,7 @@ public static class Constants
|
||||
internal static string ApiUserConfigPath => Path.Combine(Constants.InternalFilesPath, "config.user.json");
|
||||
|
||||
/// <summary>The file path for the per-mods-folder <see cref="ApiConfigPath"/> override file, which is applied over it.</summary>
|
||||
internal static string ApiModGroupConfigPath => Path.Combine(ModsPath, "SMAPI-config.json");
|
||||
internal static string ApiModGroupConfigPath => Path.Combine(Constants.ModsPath, "SMAPI-config.json");
|
||||
|
||||
/// <summary>The file path for the SMAPI metadata file.</summary>
|
||||
internal static string ApiMetadataPath => Path.Combine(Constants.InternalFilesPath, "metadata.json");
|
||||
|
||||
@@ -28,6 +28,12 @@ internal class LogManager : IDisposable
|
||||
/// <summary>Create a monitor instance given the ID and name.</summary>
|
||||
private readonly Func<string, string, Monitor> GetMonitorImpl;
|
||||
|
||||
/// <summary>The console writer which sends color-coded text to the console.</summary>
|
||||
private readonly ColorfulConsoleWriter ConsoleWriter;
|
||||
|
||||
/// <summary>The monitors managed by SMAPI.</summary>
|
||||
private readonly List<Monitor> Monitors = [];
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
@@ -47,23 +53,21 @@ internal class LogManager : IDisposable
|
||||
****/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="logPath">The log file path to write.</param>
|
||||
/// <param name="colorSchemeId">The color scheme ID in <paramref name="colorConfig"/> to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <param name="colorConfig">The colors to use for text written to the SMAPI console.</param>
|
||||
/// <param name="writeToConsole">Whether to output log messages to the console.</param>
|
||||
/// <param name="verboseLogging">The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting.</param>
|
||||
/// <param name="isDeveloperMode">Whether to enable full console output for developers.</param>
|
||||
/// <param name="getScreenIdForLog">Get the screen ID that should be logged to distinguish between players in split-screen mode, if any.</param>
|
||||
public LogManager(string logPath, ColorSchemeConfig colorConfig, bool writeToConsole, HashSet<string> verboseLogging, bool isDeveloperMode, Func<int?> getScreenIdForLog)
|
||||
public LogManager(string logPath, MonitorColorScheme colorSchemeId, Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> colorConfig, bool writeToConsole, HashSet<string> verboseLogging, bool isDeveloperMode, Func<int?> 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)
|
||||
{
|
||||
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");
|
||||
|
||||
@@ -90,6 +94,19 @@ internal class LogManager : IDisposable
|
||||
Console.Title = title;
|
||||
}
|
||||
|
||||
/// <summary>Apply the SMAPI settings to the log manager and its managed monitors.</summary>
|
||||
/// <param name="colorSchemeId">The color scheme ID in <paramref name="colorSchemes"/> to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</param>
|
||||
/// <param name="colorSchemes">The colors to use for text written to the SMAPI console.</param>
|
||||
/// <param name="verboseLogging">The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting.</param>
|
||||
/// <param name="isDeveloperMode">Whether to enable full console output for developers.</param>
|
||||
public void ApplySettings(MonitorColorScheme colorSchemeId, Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> colorSchemes, HashSet<string> verboseLogging, bool isDeveloperMode)
|
||||
{
|
||||
foreach (Monitor monitor in this.Monitors)
|
||||
this.ApplySettings(monitor, verboseLogging, isDeveloperMode);
|
||||
|
||||
this.ConsoleWriter.SetColors(colorSchemeId, colorSchemes);
|
||||
}
|
||||
|
||||
/****
|
||||
** Console input
|
||||
****/
|
||||
@@ -332,6 +349,38 @@ internal class LogManager : IDisposable
|
||||
/*********
|
||||
** Protected methods
|
||||
*********/
|
||||
/// <summary>Create and register a monitor instance.</summary>
|
||||
/// <param name="modId">The mod ID, if applicable.</param>
|
||||
/// <param name="source">The name of the module which logs messages using this instance.</param>
|
||||
/// <param name="verboseLogging">The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting.</param>
|
||||
/// <param name="getScreenIdForLog">Get the screen ID that should be logged to distinguish between players in split-screen mode, if any.</param>
|
||||
/// <param name="writeToConsole">Whether to write anything to the console. This should be disabled if no console is available.</param>
|
||||
/// <param name="isDeveloperMode">Whether to enable full console output for developers.</param>
|
||||
private Monitor CreateAndRegisterMonitor(string modId, string source, HashSet<string> verboseLogging, Func<int?> 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;
|
||||
}
|
||||
|
||||
/// <summary>Apply the SMAPI settings to a managed monitor.</summary>
|
||||
/// <param name="monitor">The monitor to update.</param>
|
||||
/// <param name="verboseLogging">The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting.</param>
|
||||
/// <param name="isDeveloperMode">Whether to enable full console output for developers.</param>
|
||||
private void ApplySettings(Monitor monitor, HashSet<string> verboseLogging, bool isDeveloperMode)
|
||||
{
|
||||
monitor.IsVerbose = verboseLogging.Contains("*") || verboseLogging.Contains(monitor.ModId);
|
||||
monitor.ShowTraceInConsole = isDeveloperMode;
|
||||
monitor.ShowFullStampInConsole = isDeveloperMode;
|
||||
}
|
||||
|
||||
/// <summary>Write a summary of mod warnings to the console and log.</summary>
|
||||
/// <param name="mods">The loaded mods.</param>
|
||||
/// <param name="skippedMods">The mods which could not be loaded.</param>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -15,20 +15,21 @@ internal class SConfig
|
||||
/// <summary>The default config values, for fields that should be logged if different.</summary>
|
||||
private static readonly IDictionary<string, object> DefaultValues = new Dictionary<string, object>
|
||||
{
|
||||
[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
|
||||
};
|
||||
|
||||
/// <summary>The default values for <see cref="SuppressUpdateChecks"/>, to log changes if different.</summary>
|
||||
@@ -46,7 +47,7 @@ internal class SConfig
|
||||
// Note: properties must be writable to support merging config.user.json into it.
|
||||
//
|
||||
|
||||
/// <summary>Whether to enable development features.</summary>
|
||||
/// <summary>Whether to show much more info in the SMAPI consoler window, intended for mod developers. Not recommended for most players.</summary>
|
||||
public bool DeveloperMode { get; set; }
|
||||
|
||||
/// <summary>Whether to check for newer versions of SMAPI and mods on startup.</summary>
|
||||
@@ -61,7 +62,7 @@ internal class SConfig
|
||||
/// <summary>Whether SMAPI should listen for console input to support console commands.</summary>
|
||||
public bool ListenForConsoleInput { get; set; }
|
||||
|
||||
/// <summary>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.</summary>
|
||||
/// <summary>Whether to add a section to the 'mod issues' list for mods which directly use potentially sensitive .NET APIs like file or shell access.</summary>
|
||||
public bool ParanoidWarnings { get; set; }
|
||||
|
||||
/// <summary>Whether to show beta versions as valid updates.</summary>
|
||||
@@ -83,6 +84,9 @@ internal class SConfig
|
||||
/// <summary>Whether SMAPI should rewrite mods for compatibility.</summary>
|
||||
public bool RewriteMods { get; set; }
|
||||
|
||||
/// <summary>Whether SMAPI should add an in-game config menu through Generic Mod Config Menu.</summary>
|
||||
public bool EnableConfigMenu { get; set; }
|
||||
|
||||
/// <summary>Whether to apply fixes to Harmony so it works with Stardew Valley.</summary>
|
||||
public bool FixHarmony { get; set; }
|
||||
|
||||
@@ -95,8 +99,11 @@ internal class SConfig
|
||||
/// <summary>Whether to include more technical details about broken mods in the TRACE logs. This is mainly useful for creating compatibility rewriters.</summary>
|
||||
public bool LogTechnicalDetailsForBrokenMods { get; set; }
|
||||
|
||||
/// <summary>The color scheme ID in <see cref="ConsoleColorSchemes"/> to use, or <see cref="MonitorColorScheme.AutoDetect"/> to select one automatically.</summary>
|
||||
public MonitorColorScheme ConsoleColorScheme { get; set; }
|
||||
|
||||
/// <summary>The colors to use for text written to the SMAPI console.</summary>
|
||||
public ColorSchemeConfig ConsoleColors { get; set; }
|
||||
public Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>> ConsoleColorSchemes { get; set; }
|
||||
|
||||
/// <summary>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.</summary>
|
||||
public bool SuppressHarmonyDebugMode { get; set; }
|
||||
@@ -124,18 +131,45 @@ internal class SConfig
|
||||
/// <param name="useBetaChannel"><inheritdoc cref="UseBetaChannel" path="/summary" /></param>
|
||||
/// <param name="gitHubProjectName"><inheritdoc cref="GitHubProjectName" path="/summary" /></param>
|
||||
/// <param name="webApiBaseUrl"><inheritdoc cref="WebApiBaseUrl" path="/summary" /></param>
|
||||
/// <param name="blacklistUrl"><inheritdoc cref="BlacklistUrl" path="/summary" /></param>
|
||||
/// <param name="verboseLogging"><inheritdoc cref="VerboseLogging" path="/summary" /></param>
|
||||
/// <param name="rewriteMods"><inheritdoc cref="RewriteMods" path="/summary" /></param>
|
||||
/// <param name="enableConfigMenu"><inheritdoc cref="EnableConfigMenu" path="/summary" /></param>
|
||||
/// <param name="fixHarmony"><inheritdoc cref="FixHarmony" path="/summary" /></param>
|
||||
/// <param name="useCaseInsensitivePaths"><inheritdoc cref="UseCaseInsensitivePaths" path="/summary" /></param>
|
||||
/// <param name="logNetworkTraffic"><inheritdoc cref="LogNetworkTraffic" path="/summary" /></param>
|
||||
/// <param name="logTechnicalDetailsForBrokenMods"><inheritdoc cref="LogTechnicalDetailsForBrokenMods" path="/summary" /></param>
|
||||
/// <param name="consoleColors"><inheritdoc cref="ConsoleColors" path="/summary" /></param>
|
||||
/// <param name="consoleColorScheme"><inheritdoc cref="ConsoleColorScheme" path="/summary" /></param>
|
||||
/// <param name="consoleColorSchemes"><inheritdoc cref="ConsoleColorSchemes" path="/summary" /></param>
|
||||
/// <param name="suppressHarmonyDebugMode"><inheritdoc cref="SuppressHarmonyDebugMode" path="/summary" /></param>
|
||||
/// <param name="suppressUpdateChecks"><inheritdoc cref="SuppressUpdateChecks" path="/summary" /></param>
|
||||
/// <param name="modsToLoadEarly"><inheritdoc cref="ModsToLoadEarly" path="/summary" /></param>
|
||||
/// <param name="modsToLoadLate"><inheritdoc cref="ModsToLoadLate" path="/summary" /></param>
|
||||
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? enableConfigMenu,
|
||||
bool? fixHarmony,
|
||||
bool? useCaseInsensitivePaths,
|
||||
bool? logNetworkTraffic,
|
||||
bool? logTechnicalDetailsForBrokenMods,
|
||||
MonitorColorScheme consoleColorScheme,
|
||||
Dictionary<MonitorColorScheme, Dictionary<ConsoleLogLevel, ConsoleColor>>? consoleColorSchemes,
|
||||
bool? suppressHarmonyDebugMode,
|
||||
string[]? suppressUpdateChecks,
|
||||
string[]? modsToLoadEarly,
|
||||
string[]? modsToLoadLate
|
||||
)
|
||||
{
|
||||
this.DeveloperMode = developerMode;
|
||||
this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)];
|
||||
@@ -146,13 +180,16 @@ 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<string>(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)];
|
||||
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<string>(suppressUpdateChecks ?? [], StringComparer.OrdinalIgnoreCase);
|
||||
this.ModsToLoadEarly = new HashSet<string>(modsToLoadEarly ?? [], StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
@@ -13,9 +13,6 @@ internal class Monitor : IMonitor
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The mod ID, if applicable.</summary>
|
||||
private readonly string ModId;
|
||||
|
||||
/// <summary>The name of the module which logs messages using this instance.</summary>
|
||||
private readonly string Source;
|
||||
|
||||
@@ -41,6 +38,9 @@ internal class Monitor : IMonitor
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The mod ID, if applicable.</summary>
|
||||
public string ModId { get; }
|
||||
|
||||
/// <summary>Whether to log basic contextual info (like buttons pressed and menus opened) even if <see cref="IsVerbose"/> is disabled.</summary>
|
||||
public static bool ForceLogContext { get; set; }
|
||||
|
||||
@@ -54,7 +54,11 @@ internal class Monitor : IMonitor
|
||||
public static LogLevel ContextLogLevel => Monitor.ForceLogContext ? LogLevel.Info : LogLevel.Trace;
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Whether to show the full log stamps (with time/level/logger) in the console. If false, shows a simplified stamp with only the logger.</summary>
|
||||
internal bool ShowFullStampInConsole { get; set; }
|
||||
@@ -73,10 +77,9 @@ internal class Monitor : IMonitor
|
||||
/// <param name="modId">The mod ID, if applicable.</param>
|
||||
/// <param name="source">The name of the module which logs messages using this instance.</param>
|
||||
/// <param name="logFile">The log file to which to write messages.</param>
|
||||
/// <param name="colorConfig">The colors to use for text written to the SMAPI console.</param>
|
||||
/// <param name="isVerbose">Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed.</param>
|
||||
/// <param name="consoleWriter">Handles writing text to the console.</param>
|
||||
/// <param name="getScreenIdForLog">Get the screen ID that should be logged to distinguish between players in split-screen mode, if any.</param>
|
||||
public Monitor(string modId, string source, LogFileManager logFile, ColorSchemeConfig colorConfig, bool isVerbose, Func<int?> getScreenIdForLog)
|
||||
public Monitor(string modId, string source, LogFileManager logFile, IConsoleWriter consoleWriter, Func<int?> getScreenIdForLog)
|
||||
{
|
||||
// validate
|
||||
if (string.IsNullOrWhiteSpace(source))
|
||||
@@ -86,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, colorConfig);
|
||||
this.IsVerbose = isVerbose;
|
||||
this.ConsoleWriter = consoleWriter ?? throw new ArgumentNullException(nameof(consoleWriter), "The console writer cannot be null.");
|
||||
this.GetScreenIdForLog = getScreenIdForLog;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
/// <summary>Encapsulates access to SMAPI core translations.</summary>
|
||||
private readonly Translator Translator = new();
|
||||
|
||||
/// <summary>The SMAPI configuration settings.</summary>
|
||||
private readonly SConfig Settings;
|
||||
|
||||
/// <summary>The mod toolkit used for generic mod interactions.</summary>
|
||||
private readonly ModToolkit Toolkit = new();
|
||||
|
||||
/// <summary>The SMAPI configuration settings.</summary>
|
||||
private SConfig Settings;
|
||||
|
||||
/****
|
||||
** Higher-level components
|
||||
****/
|
||||
@@ -113,13 +114,15 @@ internal class SCore : IDisposable
|
||||
/// <summary>Manages SMAPI events for mods.</summary>
|
||||
private readonly EventManager EventManager;
|
||||
|
||||
|
||||
/****
|
||||
** State
|
||||
****/
|
||||
/// <summary>The path to search for mods.</summary>
|
||||
private string ModsPath => Constants.ModsPath;
|
||||
|
||||
/// <summary>The override to apply for <see cref="SConfig.DeveloperMode"/>, or <c>null</c> to use the value from the settings file.</summary>
|
||||
private readonly bool? OverrideDeveloperMode;
|
||||
|
||||
/// <summary>Whether the game is currently running.</summary>
|
||||
private bool IsGameRunning;
|
||||
|
||||
@@ -178,8 +181,8 @@ internal class SCore : IDisposable
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="modsPath">The path to search for mods.</param>
|
||||
/// <param name="writeToConsole">Whether to output log messages to the console.</param>
|
||||
/// <param name="developerMode">Whether to enable development features, or <c>null</c> to use the value from the settings file.</param>
|
||||
public SCore(string modsPath, bool writeToConsole, bool? developerMode)
|
||||
/// <param name="overrideDeveloperMode">The override to apply for <see cref="SConfig.DeveloperMode"/>, or <c>null</c> to use the value from the settings file.</param>
|
||||
public SCore(string modsPath, bool writeToConsole, bool? overrideDeveloperMode)
|
||||
{
|
||||
SCore.Instance = this;
|
||||
|
||||
@@ -193,20 +196,11 @@ internal class SCore : IDisposable
|
||||
string logPath = this.GetLogPath();
|
||||
|
||||
// init settings
|
||||
{
|
||||
var deserializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
|
||||
|
||||
this.Settings = JsonConvert.DeserializeObject<SConfig>(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, 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);
|
||||
@@ -444,6 +438,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);
|
||||
@@ -502,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
|
||||
@@ -743,7 +760,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.
|
||||
@@ -1387,6 +1404,40 @@ internal class SCore : IDisposable
|
||||
this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames, assetNames.Select(p => p.GetBaseAssetName())));
|
||||
}
|
||||
|
||||
/// <summary>Reload the SMAPI settings.</summary>
|
||||
/// <exception cref="InvalidOperationException">The intern</exception>
|
||||
[MemberNotNull(nameof(SCore.Settings))]
|
||||
private void ReloadSettings()
|
||||
{
|
||||
JsonSerializerSettings deserializerSettings = new() { NullValueHandling = NullValueHandling.Ignore };
|
||||
|
||||
// load default settings
|
||||
SConfig? settings = JsonConvert.DeserializeObject<SConfig>(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);
|
||||
}
|
||||
|
||||
/// <summary>Get the load/edit operations to apply to an asset by querying registered <see cref="IContentEvents.AssetRequested"/> event handlers.</summary>
|
||||
/// <param name="asset">The asset info being requested.</param>
|
||||
private AssetOperationGroup? RequestAssetOperations(IAssetInfo asset)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>Registers the SMAPI configuration with Generic Mod Config Menu.</summary>
|
||||
internal class GenericModConfigMenuIntegration
|
||||
{
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The unique mod ID for Generic Mod Config Menu.</summary>
|
||||
private const string GenericModConfigMenuModId = "spacechase0.GenericModConfigMenu";
|
||||
|
||||
/// <summary>The minimum version of Generic Mod Config Menu supported by this integration.</summary>
|
||||
private const string MinVersion = "1.9.6";
|
||||
|
||||
/// <summary>Encapsulates monitoring and logging.</summary>
|
||||
private readonly IMonitor Monitor;
|
||||
|
||||
/// <summary>The core SMAPI translations.</summary>
|
||||
private readonly Translator Translations;
|
||||
|
||||
/// <summary>Get the current SMAPI settings.</summary>
|
||||
private readonly Func<SConfig> GetConfig;
|
||||
|
||||
/// <summary>Reload the SMAPI settings when they're changed through the config menu.</summary>
|
||||
private readonly Action ReloadConfig;
|
||||
|
||||
/// <summary>The custom setting values which haven't been saved yet.</summary>
|
||||
private readonly Dictionary<string, object> StagedSettings = [];
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="monitor"><inheritdoc cref="Monitor" path="/summary" /></param>
|
||||
/// <param name="translations"><inheritdoc cref="Translations" path="/summary" /></param>
|
||||
/// <param name="getConfig"><inheritdoc cref="GetConfig" path="/summary" /></param>
|
||||
/// <param name="reloadConfig"><inheritdoc cref="ReloadConfig" path="/summary" /></param>
|
||||
public GenericModConfigMenuIntegration(IMonitor monitor, Translator translations, Func<SConfig> getConfig, Action reloadConfig)
|
||||
{
|
||||
this.Monitor = monitor;
|
||||
this.Translations = translations;
|
||||
this.GetConfig = getConfig;
|
||||
this.ReloadConfig = reloadConfig;
|
||||
}
|
||||
|
||||
/// <summary>Register the mod config.</summary>
|
||||
/// <param name="internalModRegistry">An API for fetching metadata about loaded mods.</param>
|
||||
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<MonitorColorScheme>().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
|
||||
****/
|
||||
/// <summary>Reset the SMAPI configuration to the default values.</summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Save the mod configuration.</summary>
|
||||
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
|
||||
****/
|
||||
/// <summary>Store a value set by the player before it's saved to the config files.</summary>
|
||||
/// <param name="fieldName">The name of the <see cref="SConfig"/> field to change.</param>
|
||||
/// <param name="value">The value to stage.</param>
|
||||
private void StageOption(string fieldName, object value)
|
||||
{
|
||||
this.StagedSettings[fieldName] = value;
|
||||
}
|
||||
|
||||
/// <summary>Store a custom <see cref="SConfig.VerboseLogging"/> option before it's saved to the config files.</summary>
|
||||
/// <param name="id">The mod ID, 'SMAPI', or '*' (all) for which to toggle verbose logging.</param>
|
||||
/// <param name="enable">Whether verbose logging is enabled.</param>
|
||||
private void StageVerboseLoggingOption(string id, bool enable)
|
||||
{
|
||||
const string fieldName = nameof(SConfig.VerboseLogging);
|
||||
|
||||
if (this.StagedSettings.TryGetValue(fieldName, out object? rawValue) && rawValue is HashSet<string> set)
|
||||
set.Toggle(id, enable);
|
||||
else
|
||||
{
|
||||
HashSet<string> newSet = new HashSet<string>(this.GetConfig().VerboseLogging, StringComparer.OrdinalIgnoreCase);
|
||||
newSet.Toggle(id, enable);
|
||||
this.StagedSettings[fieldName] = newSet;
|
||||
}
|
||||
}
|
||||
|
||||
/****
|
||||
** Initialization
|
||||
****/
|
||||
/// <summary>Get the public API for Generic Mod Config Menu, if it's available.</summary>
|
||||
/// <param name="modRegistry">The mod registry to query.</param>
|
||||
/// <param name="monitor">The monitor with which to log issues loading the API.</param>
|
||||
/// <returns>Returns the API if it's available, else <c>null</c>.</returns>
|
||||
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<IGenericModConfigMenuApi>(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;
|
||||
}
|
||||
|
||||
/// <summary>Create a fake mod entry to represent SMAPI itself.</summary>
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Create a mod registry helper.</summary>
|
||||
/// <param name="internalRegistry">The internal mod registry.</param>
|
||||
/// <param name="monitor">Encapsulates monitoring and logging.</param>
|
||||
/// <param name="smapiMod">The fake mod entry representing SMAPI itself.</param>
|
||||
private IModRegistry CreateModRegistryHelper(ModRegistry internalRegistry, IMonitor monitor, ModMetadata smapiMod)
|
||||
{
|
||||
return new ModRegistryHelper(
|
||||
mod: smapiMod,
|
||||
registry: internalRegistry,
|
||||
proxyFactory: new InterfaceProxyFactory(),
|
||||
monitor: monitor
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
|
||||
namespace StardewModdingAPI.Integrations.GenericModConfigMenu;
|
||||
|
||||
/// <summary>The API provided by the Generic Mod Config Menu mod.</summary>
|
||||
public interface IGenericModConfigMenuApi
|
||||
{
|
||||
/*********
|
||||
** Methods
|
||||
*********/
|
||||
/****
|
||||
** Must be called first
|
||||
****/
|
||||
/// <summary>Register a mod whose config can be edited through the UI.</summary>
|
||||
/// <param name="mod">The mod's manifest.</param>
|
||||
/// <param name="reset">Reset the mod's config to its default values.</param>
|
||||
/// <param name="save">Save the mod's current config to the <c>config.json</c> file.</param>
|
||||
/// <param name="titleScreenOnly">Whether the options can only be edited from the title screen.</param>
|
||||
void Register(IManifest mod, Action reset, Action save, bool titleScreenOnly = false);
|
||||
|
||||
|
||||
/****
|
||||
** Basic options
|
||||
****/
|
||||
/// <summary>Add a section title at the current position in the form.</summary>
|
||||
/// <param name="mod">The mod's manifest.</param>
|
||||
/// <param name="text">The title text shown in the form.</param>
|
||||
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the title, or <c>null</c> to disable the tooltip.</param>
|
||||
void AddSectionTitle(IManifest mod, Func<string> text, Func<string>? tooltip = null);
|
||||
|
||||
/// <summary>Add a paragraph of text at the current position in the form.</summary>
|
||||
/// <param name="mod">The mod's manifest.</param>
|
||||
/// <param name="text">The paragraph text to display.</param>
|
||||
void AddParagraph(IManifest mod, Func<string> text);
|
||||
|
||||
/// <summary>Add a boolean option at the current position in the form.</summary>
|
||||
/// <param name="mod">The mod's manifest.</param>
|
||||
/// <param name="getValue">Get the current value from the mod config.</param>
|
||||
/// <param name="setValue">Set a new value in the mod config.</param>
|
||||
/// <param name="name">The label text to show in the form.</param>
|
||||
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the field, or <c>null</c> to disable the tooltip.</param>
|
||||
/// <param name="fieldId">The unique field ID for use with <c>OnFieldChanged</c>, or <c>null</c> to auto-generate a randomized ID.</param>
|
||||
void AddBoolOption(IManifest mod, Func<bool> getValue, Action<bool> setValue, Func<string> name, Func<string>? tooltip = null, string? fieldId = null);
|
||||
|
||||
/// <summary>Add a string option at the current position in the form.</summary>
|
||||
/// <param name="mod">The mod's manifest.</param>
|
||||
/// <param name="getValue">Get the current value from the mod config.</param>
|
||||
/// <param name="setValue">Set a new value in the mod config.</param>
|
||||
/// <param name="name">The label text to show in the form.</param>
|
||||
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the field, or <c>null</c> to disable the tooltip.</param>
|
||||
/// <param name="allowedValues">The values that can be selected, or <c>null</c> to allow any.</param>
|
||||
/// <param name="formatAllowedValue">Get the display text to show for a value from <paramref name="allowedValues"/>, or <c>null</c> to show the values as-is.</param>
|
||||
/// <param name="fieldId">The unique field ID for use with <c>OnFieldChanged</c>, or <c>null</c> to auto-generate a randomized ID.</param>
|
||||
void AddTextOption(IManifest mod, Func<string> getValue, Action<string> setValue, Func<string> name, Func<string>? tooltip = null, string[]? allowedValues = null, Func<string, string>? formatAllowedValue = null, string? fieldId = null);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>Wraps access to a JSON file which stores <see cref="SConfig"/> fields.</summary>
|
||||
internal class JsonSettingsWrapper
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The full path to the underlying file.</summary>
|
||||
public string FilePath { get; }
|
||||
|
||||
/// <summary>The loaded option properties.</summary>
|
||||
public Dictionary<string, JToken> Properties { get; }
|
||||
|
||||
/// <summary>Whether the saved values have changed.</summary>
|
||||
public bool Changed { get; private set; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="filePath"><inheritdoc cref="FilePath" path="/summary" /></param>
|
||||
public JsonSettingsWrapper(string filePath)
|
||||
: this(filePath, new Dictionary<string, JToken>(StringComparer.OrdinalIgnoreCase)) { }
|
||||
|
||||
/// <summary>Set the value for a user settings field.</summary>
|
||||
/// <param name="fieldName">The name of the field to set.</param>
|
||||
/// <param name="newValue">The new value to set.</param>
|
||||
/// <param name="defaultSettings">The default settings.</param>
|
||||
/// <param name="overwriteOnly">Whether to only set the value if it's already set in this file.</param>
|
||||
/// <returns>Returns whether the value changed.</returns>
|
||||
public bool SetUserOption(string fieldName, object newValue, JsonSettingsWrapper defaultSettings, bool overwriteOnly)
|
||||
{
|
||||
return fieldName == nameof(SConfig.VerboseLogging)
|
||||
? this.SetVerboseLoggingUserOption(fieldName, (HashSet<string>)newValue, defaultSettings, overwriteOnly)
|
||||
: this.SetScalarUserOption(fieldName, newValue, defaultSettings, overwriteOnly);
|
||||
}
|
||||
|
||||
/// <summary>Remove a user settings field.</summary>
|
||||
/// <param name="fieldName">The name of the field to set.</param>
|
||||
/// <returns>Returns whether the value was found and removed.</returns>
|
||||
public bool RemoveUserOption(string fieldName)
|
||||
{
|
||||
bool changed = this.Properties.Remove(fieldName);
|
||||
this.Changed = this.Changed || changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>Save the user settings to the file on disk, or delete the files if it's empty.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Read a raw JSON settings file.</summary>
|
||||
/// <param name="path">The full path for the file to read.</param>
|
||||
/// <param name="monitor">The monitor with which to log errors.</param>
|
||||
/// <returns>Returns the loaded JSON wrapper, or <c>null</c> if the file doesn't exist or couldn't be loaded.</returns>
|
||||
public static JsonSettingsWrapper? TryLoadFile(string path, IMonitor monitor)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
|
||||
string rawJson = File.ReadAllText(path);
|
||||
JObject? json = JsonConvert.DeserializeObject<JObject>(rawJson);
|
||||
if (json is null)
|
||||
return null;
|
||||
|
||||
Dictionary<string, JToken> 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
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="filePath"><inheritdoc cref="FilePath" path="/summary" /></param>
|
||||
/// <param name="properties"><inheritdoc cref="Properties" path="/summary" /></param>
|
||||
private JsonSettingsWrapper(string filePath, Dictionary<string, JToken> properties)
|
||||
{
|
||||
this.FilePath = filePath;
|
||||
this.Properties = properties;
|
||||
}
|
||||
|
||||
/// <summary>Set the value for a scalar user settings field.</summary>
|
||||
/// <param name="fieldName">The name of the field to set.</param>
|
||||
/// <param name="newValue">The new value to set.</param>
|
||||
/// <param name="defaultSettings">The default settings.</param>
|
||||
/// <param name="overwriteOnly">Whether to only set the value if it's already set in this file.</param>
|
||||
/// <returns>Returns whether the value changed.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Set the value for the verbose logging field.</summary>
|
||||
/// <param name="fieldName">The name of the field to set.</param>
|
||||
/// <param name="newValue">The new value to set.</param>
|
||||
/// <param name="defaultSettings">The default settings.</param>
|
||||
/// <param name="overwriteOnly">Whether to only set the value if it's already set in this file.</param>
|
||||
/// <returns>Returns whether the value changed.</returns>
|
||||
public bool SetVerboseLoggingUserOption(string fieldName, HashSet<string> newValue, JsonSettingsWrapper defaultSettings, bool overwriteOnly)
|
||||
{
|
||||
// remove default
|
||||
if (newValue.Count == 0)
|
||||
return this.RemoveUserOption(fieldName);
|
||||
|
||||
// else overwrite value
|
||||
if (this.Properties.GetValueOrDefault(fieldName)?.ToObject<HashSet<string>>() is { } oldSet)
|
||||
{
|
||||
oldSet = new HashSet<string>(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;
|
||||
}
|
||||
|
||||
/// <summary>Get whether a token has the expected value.</summary>
|
||||
/// <param name="token">The token whose value to check.</param>
|
||||
/// <param name="expectedValue">The value to match.</param>
|
||||
private bool TokenHasValue(JToken token, object expectedValue)
|
||||
{
|
||||
object? actualValue = token.Value<JValue>()?.Value;
|
||||
return expectedValue.Equals(actualValue);
|
||||
}
|
||||
}
|
||||
+35
-31
@@ -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:
|
||||
@@ -45,10 +45,11 @@ 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,
|
||||
"DeveloperMode": false,
|
||||
|
||||
/**
|
||||
* Whether SMAPI should listen for console input. Disabling this will prevent you from using
|
||||
@@ -57,8 +58,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 +123,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"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
+50
-1
@@ -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."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user