From c3d7f57228749c991be6febdf874355337a720e2 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Mon, 9 Feb 2026 09:25:24 +0800 Subject: [PATCH 01/55] initial --- docs/Contributing.md | 24 +++++++++++++++--------- docs/General-Info.md | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/Contributing.md b/docs/Contributing.md index d2ace5e068..7600d05367 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -215,15 +215,21 @@ DEFINE_HOOK(0x48381D, CellClass_SpreadTiberium_CellSpread, 0x6) The styleguide is not exhaustive and may be adjusted in the future. ``` -### Git branching model - -Couple of notes regarding the Git practices. We use [git-flow](https://nvie.com/posts/a-successful-git-branching-model/)-like workflow: - - `master` is for stable releases, can have hotfixes pushed to it or branched off like a feature branch with the requirement of version increment and `master` being merged into `develop` after that; - - `develop` is the main development branch; - - `feature/`-prefixed branches (sometimes the prefix may be different if appropriate, like for big fixes or changes) are so called "feature branches" - those are branched off `develop` for every new feature to be introduced into it and then merged back. We use squash merge to merge them back in case of smaller branches and sometimes merge commit in case the branch is so big it would be viable to keep it as is. - - `hotfix/`-prefixed branches may be used in a same manner as `feature/`, but with `master` branch, with a requirement of `master` being merged into `develop` after `hotfix/` branch was squash merged into `master`. - - `release/`-prefixed branches are branched off `develop` when a new stable release is slated to allow working on features for a next release and stability improvements for this release. Those are merged with a merge commit into `master` and `develop` with a stable version number increase, after which the stable version is released. -- When you're working with your local & remote branches use **fast-forward** pulls to get the changes from remote branch to local, **don't merge remote branch into local and vice versa**, this creates junk commits and makes things unsquashable. +### Git branching model / Version lifecycle and release strategy + +Starting from version 0.5, Phobos adopts a new release strategy to enable faster and more frequent releases. The lifecycle of a version is as follows: + +1. **Development phase**: New features and changes are committed to the `develop` branch. +2. **Pre-release phase**: When enough features have accumulated on `develop`, a pre-release build (e.g., `v0.5-beta1`) is created. This build marks the start of a new *release branch* (e.g., `release/v0.5`) and signifies that active feature development for version 0.5 is complete. This branch will be used for all subsequent testing and the final stable release. + - During this phase, multiple pre-release builds (which can be called beta, alpha, or release candidate) may be published for wider testing. Between pre-releases on the same branch, there shall be no changes that warrant a changelog addition; in other words — only bug fixes, minor additions, and polish to the existing feature set are allowed. + - Documentation for the version (e.g., `v0.5`) is created at this point and is shared across all its pre-releases and the final stable release, rather than generating separate docs for each pre-release. +3. **Stable release**: When the pre-release builds are deemed stable enough, a stable release (e.g., `v0.5`) is published from the release branch. +4. **Maintenance phase**: After the stable release, the release branch enters maintenance mode, where only bug fixes are applied, resulting in patch releases (e.g., `v0.5.0.1`, `v0.5.0.2`). +5. **End of maintenance**: When a new stable release is published (e.g., `v0.6`), the previous minor version branch (e.g., `v0.5.0.x`) is officially deprecated and enters end-of-life, ceasing to receive any further updates, including bug fixes. Concurrently, the new stable release (e.g., `v0.6`) enters its own maintenance phase, and a new release branch for the next version (e.g., `release/v0.7`) may already have been created from the `develop` branch, initiating its pre-release cycle. + +```{important} +The `master` branch is deprecated; all development occurs in `develop`, and each version branches off from it. +``` These commands will do the following for all repositories on your PC: 1) remove the automatic merge upon pull and replace it with a rebase; diff --git a/docs/General-Info.md b/docs/General-Info.md index 5b1dc16cde..a5012c71b7 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -6,7 +6,7 @@ This page lists general info that should be known about the project. There are three main types of Phobos builds: - *stable builds* - those are numbered like your regular versions (something close to semantic versioning, e.g. version 1.2.3 for example) and ideally should contain no bugs, therefore are safe to use in mods; -- *development builds* - those are the builds which contain functionality that needs to be tested. They are numbered plainly starting from 0 and incrementing the number on each release. Mod authors still can include those versions with their mods if they want latest features, though we can't guarantee lack of bugs; +- *pre-release builds* - previously known as *development builds*, these builds mark the start of a new release branch and are used for testing new features before they are finalized. They are numbered with a version and a pre-release suffix (e.g., 0.5-beta1, 0.5-rc2). Mod authors can include these versions with their mods to access the latest features, but we cannot guarantee the absence of bugs; - *nightly builds* - bleeding edge versions which can include prototypes, proofs of concepts, scrapped features etc., in other words - we can't guarantee anything in those builds and they absolutely should NOT be used in mod releases and should only be used to help with development and testing. ```{hint} From 000160d4252e67c1239d3ffd3e66fdb127984055 Mon Sep 17 00:00:00 2001 From: Starkku Date: Fri, 10 Apr 2026 21:42:37 +0300 Subject: [PATCH 02/55] Update build configurations, metadata and workflows - Remove DevBuild configuration - Release configuration no longer sets IS_RELEASE_VER preprocessor directive, references across codebase changed to use RELEASE definition - Metadata is determined based on preprocessor definitions set based on compiler option BuildType (NIGHTLY, PRERELEASE & RELEASE) - Workflows updated to use the new compiler options and accomodate configuration changes --- .github/actions/build-phobos/action.yml | 6 +- .github/workflows/nightly.yml | 5 +- .github/workflows/pr-nightly.yml | 4 +- Phobos.sln | 7 +- Phobos.vcxproj | 89 +++++++------------------ src/Phobos.cpp | 8 +-- src/Phobos.version.h | 36 +++++----- src/version.rc | 4 +- 8 files changed, 61 insertions(+), 98 deletions(-) diff --git a/.github/actions/build-phobos/action.yml b/.github/actions/build-phobos/action.yml index d818a510a3..fee486c3c0 100644 --- a/.github/actions/build-phobos/action.yml +++ b/.github/actions/build-phobos/action.yml @@ -7,6 +7,10 @@ inputs: build-config: description: 'Configuration to build' required: true + build-type: + description: 'Build type e.g nightly, prerelease or release' + required: true + default: 'NIGHTLY' runs: using: "composite" steps: @@ -21,7 +25,7 @@ runs: working-directory: ${{env.GITHUB_WORKSPACE}} # Add additional options to the MSBuild command line here (like platform or verbosity level). # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Configuration=${{inputs.build-config}} /p:GitCommit=${{github.sha}} /p:GitBranch=${{github.ref}} ${{inputs.sln-path}} + run: msbuild /m /p:Configuration=${{inputs.build-config}} /p:GitCommit=${{github.sha}} /p:GitBranch=${{github.ref}} /p:BuildType=${{inputs.build-type}} ${{inputs.sln-path}} shell: cmd - name: Upload Artifact diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a72f06476a..0f84ef6a75 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -3,8 +3,6 @@ name: Nightly Build on: push: branches: - - master - - main - develop env: @@ -14,7 +12,7 @@ env: # Configuration type to build. # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - BUILD_CONFIGURATION: DevBuild + BUILD_CONFIGURATION: Release # GIT_COMMIT: $(git rev-parse --short "$GITHUB_SHA") # GIT_BRANCH: ${GITHUB_REF#refs/heads/} @@ -32,3 +30,4 @@ jobs: with: sln-path: ${{env.SOLUTION_FILE_PATH}} build-config: ${{env.BUILD_CONFIGURATION}} + build-type: NIGHTLY diff --git a/.github/workflows/pr-nightly.yml b/.github/workflows/pr-nightly.yml index ad7d13bd27..2f9b337c0b 100644 --- a/.github/workflows/pr-nightly.yml +++ b/.github/workflows/pr-nightly.yml @@ -10,7 +10,7 @@ env: # Configuration type to build. # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - BUILD_CONFIGURATION: DevBuild + BUILD_CONFIGURATION: Release # GIT_COMMIT: $(git rev-parse --short "$GITHUB_SHA") # GIT_BRANCH: ${GITHUB_REF#refs/heads/} @@ -29,3 +29,5 @@ jobs: with: sln-path: ${{env.SOLUTION_FILE_PATH}} build-config: ${{env.BUILD_CONFIGURATION}} + build-type: NIGHTLY + diff --git a/Phobos.sln b/Phobos.sln index cb404798b9..d5ed44ff6f 100644 --- a/Phobos.sln +++ b/Phobos.sln @@ -1,21 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29209.62 +# Visual Studio Version 18 +VisualStudioVersion = 18.4.11626.88 stable MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Phobos", "Phobos.vcxproj", "{3FAF7126-F38C-4D1E-9973-C21A37870F60}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 - DevBuild|x86 = DevBuild|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3FAF7126-F38C-4D1E-9973-C21A37870F60}.Debug|x86.ActiveCfg = Debug|Win32 {3FAF7126-F38C-4D1E-9973-C21A37870F60}.Debug|x86.Build.0 = Debug|Win32 - {3FAF7126-F38C-4D1E-9973-C21A37870F60}.DevBuild|x86.ActiveCfg = DevBuild|Win32 - {3FAF7126-F38C-4D1E-9973-C21A37870F60}.DevBuild|x86.Build.0 = DevBuild|Win32 {3FAF7126-F38C-4D1E-9973-C21A37870F60}.Release|x86.ActiveCfg = Release|Win32 {3FAF7126-F38C-4D1E-9973-C21A37870F60}.Release|x86.Build.0 = Release|Win32 EndGlobalSection diff --git a/Phobos.vcxproj b/Phobos.vcxproj index d45a6ac91f..552f1e461d 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -8,10 +8,6 @@ Debug Win32 - - DevBuild - Win32 - Release Win32 @@ -351,13 +347,6 @@ NotSet false - - DynamicLibrary - false - v143 - NotSet - false - DynamicLibrary false @@ -373,9 +362,6 @@ - - - @@ -387,13 +373,6 @@ $(Configuration)\ $(Configuration)\IntDir\ - - false - $(SolutionDir)src\;$(SolutionDir)src\ExtraHeaders;$(SolutionDir)yrpp;$(SolutionDir)lib;$(VC_IncludePath) - false - $(Configuration)\ - $(Configuration)\IntDir\ - false $(SolutionDir)src\;$(SolutionDir)src\ExtraHeaders;$(SolutionDir)yrpp;$(SolutionDir)lib;$(VC_IncludePath) @@ -410,7 +389,7 @@ true Caret true - IS_RELEASE_VER;SYR_VER=2;HAS_EXCEPTIONS=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions);%(AdditionalDefinitions);PHOBOS_DLL="$(ProjectName).dll" + NIGHTLY;SYR_VER=2;HAS_EXCEPTIONS=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions);%(AdditionalDefinitions);PHOBOS_DLL="$(ProjectName).dll" true false MultiThreaded @@ -446,48 +425,6 @@ IS_RELEASE_VER;%(PreprocessorDefinitions) - - - Level4 - MaxSpeed - true - - true - Caret - true - SYR_VER=2;HAS_EXCEPTIONS=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions);%(AdditionalDefinitions);PHOBOS_DLL="$(ProjectName).dll" - true - false - MultiThreaded - 8Bytes - false - false - StreamingSIMDExtensions2 - true - false - stdcpp20 - $(IntDir)\%(RelativeDir) - $(IntDir)\%(RelativeDir) - StdCall - %(AdditionalOptions) - 4100;4201;4530;4731;4740;4458;4819;5103;5105 - true - true - true - - - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dbghelp.lib;onecore.lib;%(AdditionalDependencies) - Windows - $(OutDir)$(TargetName).pdb - $(IntDir)$(TargetName).pgd - $(IntDir)$(TargetName).lib - - - - - Level4 @@ -548,6 +485,30 @@ /DGIT_BRANCH="$(GitBranch)" %(AdditionalOptions) + + + NIGHTLY;%(PreprocessorDefinitions) + + + NIGHTLY;%(PreprocessorDefinitions) + + + + + PRERELEASE;%(PreprocessorDefinitions) + + + PRERELEASE;%(PreprocessorDefinitions) + + + + + RELEASE;%(PreprocessorDefinitions) + + + RELEASE;%(PreprocessorDefinitions) + + \ No newline at end of file diff --git a/src/Phobos.cpp b/src/Phobos.cpp index c061b50170..f324f1af2d 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -10,7 +10,7 @@ #include "Utilities/AresHelper.h" #include "Utilities/Parser.h" -#ifndef IS_RELEASE_VER +#ifndef RELEASE bool HideWarning = false; #endif @@ -30,7 +30,7 @@ bool Phobos::Optimizations::DisableSyncLogging = false; #ifdef STR_GIT_COMMIT const wchar_t* Phobos::VersionDescription = L"Phobos nightly build (" STR_GIT_COMMIT L" @ " STR_GIT_BRANCH L"). DO NOT SHIP IN MODS!"; -#elif !defined(IS_RELEASE_VER) +#elif !defined(RELEASE) const wchar_t* Phobos::VersionDescription = L"Phobos development build #" _STR(BUILD_NUMBER) L". Please test the build before shipping."; #else //const wchar_t* Phobos::VersionDescription = L"Phobos release build v" FILE_VERSION_STR L"."; @@ -59,7 +59,7 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) { Phobos::AppIconPath = ppArgs[++i]; } -#ifndef IS_RELEASE_VER +#ifndef RELEASE if (_stricmp(pArg, "-b=" _STR(BUILD_NUMBER)) == 0) { HideWarning = true; @@ -232,7 +232,7 @@ DEFINE_HOOK(0x683E7F, ScenarioClass_Start_Optimizations, 0x7) return 0; } -#ifndef IS_RELEASE_VER +#ifndef RELEASE DEFINE_HOOK(0x4F4583, GScreenClass_DrawText, 0x6) { #ifndef STR_GIT_COMMIT diff --git a/src/Phobos.version.h b/src/Phobos.version.h index abd7d3237b..657e50c3ab 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -22,32 +22,32 @@ #pragma endregion -// Build number. Incremented on each released build. -#define BUILD_NUMBER 48 +// Pre-release build number. Incremented on each pre-release build. +#define PRERELEASE_NUM 1 // Nightly defines GIT_COMMIT and GIT_BRANCH in GH Actions +// NIGHTLY / PRERELEASE / RELEASE come from compiler option BuildType - used by GH Actions as well -#ifdef IS_RELEASE_VER // Release build metadata - #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) - #define FILE_DESCRIPTION "Phobos, Ares-compatible YR engine extension" - #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) - #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH - #define PRODUCT_VERSION "Release Build " FILE_VERSION_STR -#elif defined(GIT_COMMIT) // Nightly devbuild metadata +#if defined(NIGHTLY) #define STR_GIT_COMMIT _STR(GIT_COMMIT) #define STR_GIT_BRANCH _STR(GIT_BRANCH) - - #define SAVEGAME_ID ((BUILD_NUMBER << 24) | (BUILD_NUMBER << 12) | (BUILD_NUMBER)) - #define FILE_DESCRIPTION "Unstable nightly devbuild of Phobos engine extension" + #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) + #define FILE_DESCRIPTION "Unstable nightly build of Phobos engine extension" #define FILE_VERSION_STR "Commit " STR_GIT_COMMIT #define FILE_VERSION 0 #define PRODUCT_VERSION "Nightly Build " STR_GIT_COMMIT " @ " STR_GIT_BRANCH -#else // Regular devbuild metadata - #define SAVEGAME_ID ((BUILD_NUMBER << 24) | (BUILD_NUMBER << 12) | (BUILD_NUMBER)) - #define FILE_DESCRIPTION "Development build of Phobos engine extension" - #define FILE_VERSION_STR "Build #" _STR(BUILD_NUMBER) - #define FILE_VERSION 0,0,0,BUILD_NUMBER - #define PRODUCT_VERSION "Development Build #" _STR(BUILD_NUMBER) +#elif defined(PRERELEASE) + #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (PRERELEASE_NUM << 8)) + #define FILE_DESCRIPTION "Pre-release build of Phobos, Ares-compatible YR engine extension" + #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "-beta" _STR(PRERELEASE_NUM) + #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, 0, 0 + #define PRODUCT_VERSION "v." _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "-beta" _STR(PRERELEASE_NUM) +#else + #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) + #define FILE_DESCRIPTION "Phobos, Ares-compatible YR engine extension" + #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) + #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH + #define PRODUCT_VERSION "v." FILE_VERSION_STR #endif #endif // VERSION_H diff --git a/src/version.rc b/src/version.rc index 394625a99e..240a811ea9 100644 --- a/src/version.rc +++ b/src/version.rc @@ -9,7 +9,7 @@ ID_VERSION VERSIONINFO FILEVERSION FILE_VERSION PRODUCTVERSION FILE_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifndef IS_RELEASE_VER +#ifndef RELEASE FILEFLAGS VS_FF_PRERELEASE | VS_FF_SPECIALBUILD #endif @@ -29,7 +29,7 @@ BEGIN VALUE "InternalName", "Phobos.dll\0" VALUE "OriginalFilename", "Phobos.dll\0" -#ifndef IS_RELEASE_VER +#ifndef RELEASE VALUE "SpecialBuild", "Testing version. Use at your own risk.\0" #endif END From 9178a76ff280c394d3196c6ba9094409231ed281 Mon Sep 17 00:00:00 2001 From: Starkku Date: Fri, 10 Apr 2026 21:51:43 +0300 Subject: [PATCH 03/55] Fix minor error from testing --- Phobos.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 552f1e461d..18a895d600 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -389,7 +389,7 @@ true Caret true - NIGHTLY;SYR_VER=2;HAS_EXCEPTIONS=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions);%(AdditionalDefinitions);PHOBOS_DLL="$(ProjectName).dll" + SYR_VER=2;HAS_EXCEPTIONS=0;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions);%(AdditionalDefinitions);PHOBOS_DLL="$(ProjectName).dll" true false MultiThreaded From a918fd9d75b66c41d48df8b6a8c1acd3e9acd409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:38:00 +0800 Subject: [PATCH 04/55] auto release and changelog --- .github/workflows/release.yml | 63 ++++++++++++++++++++++++ scripts/extract_changelog.ps1 | 93 +++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/extract_changelog.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..6c3d40b5dc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,63 @@ +name: Release Build + +on: + release: + types: [published] + +env: + SOLUTION_FILE_PATH: . + BUILD_CONFIGURATION: Release + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Determine Build Type and Version Info + id: info + shell: pwsh + run: | + $tag = "${{ github.ref_name }}" + $version = $tag.TrimStart('v') + $isPrerelease = "${{ github.event.release.prerelease }}" + if ($isPrerelease -eq "true") { + $buildType = "PRERELEASE" + $releaseName = "Phobos $tag (Pre-release)" + } else { + $buildType = "RELEASE" + $releaseName = "Phobos $tag" + } + echo "build-type=$buildType" >> $env:GITHUB_OUTPUT + echo "version=$version" >> $env:GITHUB_OUTPUT + echo "release-name=$releaseName" >> $env:GITHUB_OUTPUT + + - name: Build Phobos + uses: ./.github/actions/build-phobos + with: + sln-path: ${{env.SOLUTION_FILE_PATH}} + build-config: ${{ env.BUILD_CONFIGURATION }} + build-type: ${{ steps.info.outputs.build-type }} + + - name: Extract Changelog + shell: pwsh + run: | + .\scripts\extract_changelog.ps1 ` + -Tag "${{ github.ref_name }}" ` + -WhatsNewPath docs\Whats-New.md ` + -OutputPath RELEASE_NOTES.md + + - name: Upload Release Assets + uses: softprops/action-gh-release@v2 + with: + name: ${{ steps.info.outputs.release-name }} + files: | + ${{ env.BUILD_CONFIGURATION }}/Phobos.dll + ${{ env.BUILD_CONFIGURATION }}/Phobos.pdb + body_path: RELEASE_NOTES.md diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 new file mode 100644 index 0000000000..2f565595d0 --- /dev/null +++ b/scripts/extract_changelog.ps1 @@ -0,0 +1,93 @@ +param( + [Parameter(Mandatory = $true)] + [string]$Tag, + + [Parameter(Mandatory = $true)] + [string]$WhatsNewPath, + + [Parameter(Mandatory = $true)] + [string]$OutputPath +) + +$version = $Tag.TrimStart('v') + +if (-not (Test-Path $WhatsNewPath)) { + Write-Error "File not found: $WhatsNewPath" + exit 1 +} + +$content = (Get-Content -Path $WhatsNewPath -Raw -Encoding utf8) -replace "`r`n", "`n" + +$changelogIdx = $content.IndexOf("## Changelog") +if ($changelogIdx -lt 0) { + Write-Error "Could not find '## Changelog' section in $WhatsNewPath" + exit 1 +} + +$afterChangelog = $content.Substring($changelogIdx + "## Changelog".Length + 1) + +$nextSectionIdx = $afterChangelog.IndexOf("`n## ") +$changelogBody = if ($nextSectionIdx -ge 0) { $afterChangelog.Substring(0, $nextSectionIdx) } else { $afterChangelog } + +# Try to find the section: exact version -> parent versions -> Version TBD +$sectionStart = -1 + +# 1) Exact version match +$sectionStart = $changelogBody.IndexOf("### $version`n") + +# 2) Strip trailing numeric components until found +if ($sectionStart -lt 0) { + $tryVersion = $version -replace '\.[0-9]+$', '' + while ($tryVersion -ne $version) { + $sectionStart = $changelogBody.IndexOf("### $tryVersion`n") + if ($sectionStart -ge 0) { break } + $version = $tryVersion + $tryVersion = $version -replace '\.[0-9]+$', '' + } + $version = $Tag.TrimStart('v') # restore original +} + +# 3) Fall back to Version TBD +if ($sectionStart -lt 0) { + $sectionStart = $changelogBody.IndexOf("### Version TBD") +} + +if ($sectionStart -lt 0) { + Write-Error "Could not find changelog section for version '$version' or 'Version TBD'" + exit 1 +} + +$sectionEnd = $changelogBody.IndexOf("`n### ", $sectionStart + 5) +if ($sectionEnd -ge 0) { + $sectionContent = $changelogBody.Substring($sectionStart, $sectionEnd - $sectionStart) +} else { + $sectionContent = $changelogBody.Substring($sectionStart) +} + +$firstNewline = $sectionContent.IndexOf("`n") +if ($firstNewline -ge 0) { + $sectionContent = $sectionContent.Substring($firstNewline + 1) +} else { + $sectionContent = "" +} + +$sectionContent = $sectionContent.Trim() + +$dropdownPattern = '(?ms)^```\{dropdown\}[^\n]*\n(.+?)^```' +$dropdownMatch = [regex]::Match($sectionContent, $dropdownPattern) + +if ($dropdownMatch.Success) { + $sectionContent = $dropdownMatch.Groups[1].Value.Trim() +} + +$sectionContent = $sectionContent -replace '(?m)^:open:\s*\n', '' +$sectionContent = $sectionContent.Trim() + +if (-not $sectionContent) { + Write-Error "Extracted changelog content is empty for version '$version'" + exit 1 +} + +$sectionContent | Out-File -FilePath $OutputPath -Encoding utf8 + +Write-Host "Extracted changelog for version '$version' to '$OutputPath'" From 770ebbd5a92464fb438c54bffa4b33632618b155 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Sat, 6 Jun 2026 17:59:00 +0300 Subject: [PATCH 05/55] missed fix --- Phobos.props | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/Phobos.props b/Phobos.props index bee83b6da5..d16dd4699b 100644 --- a/Phobos.props +++ b/Phobos.props @@ -5,10 +5,6 @@ Debug Win32 - - DevBuild - Win32 - Release Win32 @@ -70,21 +66,6 @@ - - IS_RELEASE_VER;%(PreprocessorDefinitions) - MaxSpeed - true - - - - - - - IS_RELEASE_VER;%(PreprocessorDefinitions) - - - - MaxSpeed true @@ -124,6 +105,31 @@ /DGIT_BRANCH="$(GitBranch)" %(AdditionalOptions) + + + + NIGHTLY;%(PreprocessorDefinitions) + + + NIGHTLY;%(PreprocessorDefinitions) + + + + + PRERELEASE;%(PreprocessorDefinitions) + + + PRERELEASE;%(PreprocessorDefinitions) + + + + + RELEASE;%(PreprocessorDefinitions) + + + RELEASE;%(PreprocessorDefinitions) + + From 578f8341318939d96fcdb7257b022c785f5a7782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Sat, 13 Jun 2026 16:20:43 +0800 Subject: [PATCH 06/55] rewrite extract_changelog.ps1 to use heading-tree parsing Adapt to the new Whats-New.md structure where version sections use ### headings and change categories use #### headings, and content may be wrapped in `{dropdown}` blocks. The script now: - Parses the full document heading tree to locate ### version sections under ## Changelog, avoiding confusion with duplicate headings in other sections (e.g. ## Breaking changes) - Matches version via exact match -> parent version fallback -> Version TBD fallback - Correctly strips outer `{dropdown}` wrappers and :open: directives - Preserves #### change category headings in the extracted output Suggested-by: Metadorius (Kerbiter) --- scripts/extract_changelog.ps1 | 95 ++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index 2f565595d0..bd0b5e0b7f 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -18,68 +18,93 @@ if (-not (Test-Path $WhatsNewPath)) { $content = (Get-Content -Path $WhatsNewPath -Raw -Encoding utf8) -replace "`r`n", "`n" -$changelogIdx = $content.IndexOf("## Changelog") -if ($changelogIdx -lt 0) { +# ---- Parse the document into a heading tree ---- +$lines = $content -split "`n" +$headings = @() +for ($i = 0; $i -lt $lines.Count; $i++) { + if ($lines[$i] -match '^(#{1,6})\s+(.+)$') { + $headings += @{ + Level = $matches[1].Length + Text = $matches[2] + Line = $i + } + } +} + +# ---- Find ## Changelog and its ### children ---- +$changelogNode = $headings | Where-Object { $_.Level -eq 2 -and $_.Text -eq 'Changelog' } | Select-Object -First 1 +if (-not $changelogNode) { Write-Error "Could not find '## Changelog' section in $WhatsNewPath" exit 1 } -$afterChangelog = $content.Substring($changelogIdx + "## Changelog".Length + 1) +# Next heading at level <= 2 marks the end of ## Changelog +$changelogEndNode = $headings | Where-Object { $_.Level -le 2 -and $_.Line -gt $changelogNode.Line } | Select-Object -First 1 +$changelogEnd = if ($changelogEndNode) { $changelogEndNode.Line } else { $lines.Count } -$nextSectionIdx = $afterChangelog.IndexOf("`n## ") -$changelogBody = if ($nextSectionIdx -ge 0) { $afterChangelog.Substring(0, $nextSectionIdx) } else { $afterChangelog } +# All ### headings within ## Changelog +$versionNodes = $headings | Where-Object { + $_.Level -eq 3 -and $_.Line -gt $changelogNode.Line -and $_.Line -lt $changelogEnd +} -# Try to find the section: exact version -> parent versions -> Version TBD -$sectionStart = -1 +if ($versionNodes.Count -eq 0) { + Write-Error "No version sections found within '## Changelog'" + exit 1 +} + +# ---- Extract the version string from a ### heading ---- +function Get-VersionText($headingText) { + if ($headingText -match '^Version\s') { + return $headingText # "Version TBD (develop branch nightly builds)" + } + # Take the first space-delimited token, e.g. "0.4.0.3" + return ($headingText -split '\s')[0] +} + +# ---- Match version ---- +$matchedNode = $null # 1) Exact version match -$sectionStart = $changelogBody.IndexOf("### $version`n") +$matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -eq $version } | Select-Object -First 1 # 2) Strip trailing numeric components until found -if ($sectionStart -lt 0) { - $tryVersion = $version -replace '\.[0-9]+$', '' - while ($tryVersion -ne $version) { - $sectionStart = $changelogBody.IndexOf("### $tryVersion`n") - if ($sectionStart -ge 0) { break } - $version = $tryVersion - $tryVersion = $version -replace '\.[0-9]+$', '' +if (-not $matchedNode) { + $tryVersion = $version + $stripped = $tryVersion -replace '\.[0-9]+$', '' + while ($stripped -ne $tryVersion) { + $tryVersion = $stripped + $matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -eq $tryVersion } | Select-Object -First 1 + if ($matchedNode) { break } + $stripped = $tryVersion -replace '\.[0-9]+$', '' } - $version = $Tag.TrimStart('v') # restore original } # 3) Fall back to Version TBD -if ($sectionStart -lt 0) { - $sectionStart = $changelogBody.IndexOf("### Version TBD") +if (-not $matchedNode) { + $matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -match '^Version TBD' } | Select-Object -First 1 } -if ($sectionStart -lt 0) { +if (-not $matchedNode) { Write-Error "Could not find changelog section for version '$version' or 'Version TBD'" exit 1 } -$sectionEnd = $changelogBody.IndexOf("`n### ", $sectionStart + 5) -if ($sectionEnd -ge 0) { - $sectionContent = $changelogBody.Substring($sectionStart, $sectionEnd - $sectionStart) -} else { - $sectionContent = $changelogBody.Substring($sectionStart) -} +# ---- Extract content between this ### and the next ### (or end of ## Changelog) ---- +$sectionStart = $matchedNode.Line + 1 # skip the heading line itself +$nextNode = $versionNodes | Where-Object { $_.Line -gt $matchedNode.Line } | Select-Object -First 1 +$sectionEnd = if ($nextNode) { $nextNode.Line } else { $changelogEnd } -$firstNewline = $sectionContent.IndexOf("`n") -if ($firstNewline -ge 0) { - $sectionContent = $sectionContent.Substring($firstNewline + 1) -} else { - $sectionContent = "" -} +$sectionLines = $lines[$sectionStart..($sectionEnd - 1)] +$sectionContent = ($sectionLines -join "`n").Trim() -$sectionContent = $sectionContent.Trim() - -$dropdownPattern = '(?ms)^```\{dropdown\}[^\n]*\n(.+?)^```' +# ---- Handle outer ```{dropdown} ... ``` wrapper ---- +$dropdownPattern = '(?ms)^```\{dropdown\}[^\n]*\n(.+?)^```$' $dropdownMatch = [regex]::Match($sectionContent, $dropdownPattern) - if ($dropdownMatch.Success) { $sectionContent = $dropdownMatch.Groups[1].Value.Trim() } +# Remove :open: Sphinx directive $sectionContent = $sectionContent -replace '(?m)^:open:\s*\n', '' $sectionContent = $sectionContent.Trim() From c5e0042dfe4da0f2aad32cc8eb711ba426eb8805 Mon Sep 17 00:00:00 2001 From: Kirill Andriiashin Date: Sat, 1 Aug 2026 21:50:04 +0300 Subject: [PATCH 07/55] NO LATEST@!!!!!!!@11!!11 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c3d40b5dc..e97bf0b63b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ permissions: jobs: build: - runs-on: windows-latest + runs-on: windows-2022 steps: - uses: actions/checkout@v6 From 4969b9efcabb5cc32d115117c08fa3ce5a783488 Mon Sep 17 00:00:00 2001 From: Kirill Andriiashin Date: Sat, 1 Aug 2026 23:28:59 +0300 Subject: [PATCH 08/55] Apply suggestions from code review Co-authored-by: Kirill Andriiashin --- docs/Project-guidelines-and-policies.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index 45cd421188..0ddcc59c46 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -235,9 +235,8 @@ Starting from version 0.5, Phobos adopts a new release strategy to enable faster 1. **Development phase**: New features and changes are committed to the `develop` branch. 2. **Pre-release phase**: When enough features have accumulated on `develop`, a pre-release build (e.g., `v0.5-beta1`) is created. This build marks the start of a new *release branch* (e.g., `release/v0.5`) and signifies that active feature development for version 0.5 is complete. This branch will be used for all subsequent testing and the final stable release. - - During this phase, multiple pre-release builds (which can be called beta, alpha, or release candidate) may be published for wider testing. Between pre-releases on the same branch, there shall be no changes that warrant a changelog addition; in other words — only bug fixes, minor additions, and polish to the existing feature set are allowed. - - Documentation for the version (e.g., `v0.5`) is created at this point and is shared across all its pre-releases and the final stable release, rather than generating separate docs for each pre-release. -3. **Stable release**: When the pre-release builds are deemed stable enough, a stable release (e.g., `v0.5`) is published from the release branch. + - During this phase, multiple pre-release builds (which can be called beta, alpha, or release candidate) may be published for wider testing. Between pre-releases on the same branch, there shall be no changes that warrant a stable version changelog addition; in other words — only bug fixes, minor additions, and polish to the existing feature set are allowed. +3. **Stable release**: When the pre-release builds are deemed stable enough, a stable release (e.g., `v0.5`) is published from the release's branch. 4. **Maintenance phase**: After the stable release, the release branch enters maintenance mode, where only bug fixes are applied, resulting in patch releases (e.g., `v0.5.0.1`, `v0.5.0.2`). 5. **End of maintenance**: When a new stable release is published (e.g., `v0.6`), the previous minor version branch (e.g., `v0.5.0.x`) is officially deprecated and enters end-of-life, ceasing to receive any further updates, including bug fixes. Concurrently, the new stable release (e.g., `v0.6`) enters its own maintenance phase, and a new release branch for the next version (e.g., `release/v0.7`) may already have been created from the `develop` branch, initiating its pre-release cycle. From 34d0f580c0a8e044dadcabc68441267033dfecba Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 1 Aug 2026 23:58:34 +0300 Subject: [PATCH 09/55] Use a semver pre-release suffix instead of a pre-release build number --- src/Phobos.version.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 657e50c3ab..03e446e51f 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -22,28 +22,30 @@ #pragma endregion -// Pre-release build number. Incremented on each pre-release build. -#define PRERELEASE_NUM 1 +// Pre-release identifier, appended to the version as a semantic versioning suffix. +// Any identifier is allowed, e.g. alpha1, beta2 or rc3 - bump it on each pre-release build. +#define PRERELEASE_SUFFIX beta1 // Nightly defines GIT_COMMIT and GIT_BRANCH in GH Actions // NIGHTLY / PRERELEASE / RELEASE come from compiler option BuildType - used by GH Actions as well +// Savegame compatibility is tied to the version alone: nightlies and pre-releases of a version +// are expected to stay save-compatible with the stable release they lead up to. +#define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) + #if defined(NIGHTLY) #define STR_GIT_COMMIT _STR(GIT_COMMIT) #define STR_GIT_BRANCH _STR(GIT_BRANCH) - #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) #define FILE_DESCRIPTION "Unstable nightly build of Phobos engine extension" #define FILE_VERSION_STR "Commit " STR_GIT_COMMIT #define FILE_VERSION 0 #define PRODUCT_VERSION "Nightly Build " STR_GIT_COMMIT " @ " STR_GIT_BRANCH #elif defined(PRERELEASE) - #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (PRERELEASE_NUM << 8)) #define FILE_DESCRIPTION "Pre-release build of Phobos, Ares-compatible YR engine extension" - #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "-beta" _STR(PRERELEASE_NUM) + #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "-" _STR(PRERELEASE_SUFFIX) #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, 0, 0 - #define PRODUCT_VERSION "v." _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "-beta" _STR(PRERELEASE_NUM) + #define PRODUCT_VERSION "v." FILE_VERSION_STR #else - #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) #define FILE_DESCRIPTION "Phobos, Ares-compatible YR engine extension" #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH From ca19e75c2afb8c9551ff03817a63263262150c93 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 1 Aug 2026 23:58:34 +0300 Subject: [PATCH 10/55] Reword the version warning and require the full version to hide it --- docs/General-Info.md | 6 +++--- src/Phobos.cpp | 14 +++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/General-Info.md b/docs/General-Info.md index c3b82019ab..879a31cae4 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -13,11 +13,11 @@ There are three main types of Phobos builds: You can find the downloads for these versions on the document's [main page](index.md#downloads). ``` -### Disabling development build warning +### Disabling pre-release build warning -**DISCLAIMER:** We understand that everyone wants to try and use the new features as soon as they're released, but we can't do all the testing ourselves, so we only test the functionality on a basic level. We ask everyone who uses the new development build first to **test the new changes in every possible way first before disabling the development build warning** and proceeding to include the build in your mod release. This would allow us to concentrate on implementing the actual features, which is the most complex task. Learn more on testing [here](Contributing.md#testing). +**DISCLAIMER:** We understand that everyone wants to try and use the new features as soon as they're released, but we can't do all the testing ourselves, so we only test the functionality on a basic level. We ask everyone who uses the new pre-release build first to **test the new changes in every possible way first before disabling the pre-release build warning** and proceeding to include the build in your mod release. This would allow us to concentrate on implementing the actual features, which is the most complex task. Learn more on testing [here](Contributing.md#testing). -You can hide the warning by specifying the build number after `-b=` as a command line argument (for example, `-b=1` would hide the warning for development build #1 of Phobos). +You can hide the warning by specifying the exact version of the build you use after `-HideVersionWarning=` as a command line argument (for example, `-HideVersionWarning=0.5-beta1` would hide the warning for the `v0.5-beta1` pre-release of Phobos). The version is shown in the warning itself and in the title of the respective release; a build only accepts its own version, so the switch has to be updated whenever you update Phobos. Nightly builds don't support hiding the warning at all. ## Saved games filtering diff --git a/src/Phobos.cpp b/src/Phobos.cpp index 456276b142..f50f9487e8 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -30,10 +30,10 @@ bool Phobos::Optimizations::DisableBalloonHoverPathingFix = false; bool Phobos::Optimizations::DisableRadDamageOnBuildings = true; bool Phobos::Optimizations::DisableSyncLogging = false; -#ifdef STR_GIT_COMMIT +#ifdef NIGHTLY const wchar_t* Phobos::VersionDescription = L"Phobos nightly build (" STR_GIT_COMMIT L" @ " STR_GIT_BRANCH L"). DO NOT SHIP IN MODS!"; #elif !defined(RELEASE) -const wchar_t* Phobos::VersionDescription = L"Phobos development build #" _STR(BUILD_NUMBER) L". Please test the build before shipping."; +const wchar_t* Phobos::VersionDescription = L"Phobos pre-release build v" FILE_VERSION_STR L". Please test the build before shipping."; #else //const wchar_t* Phobos::VersionDescription = L"Phobos release build v" FILE_VERSION_STR L"."; #endif @@ -59,7 +59,11 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) Phobos::AppIconPath = ppArgs[++i]; } #ifndef RELEASE - if (_stricmp(pArg, "-b=" _STR(BUILD_NUMBER)) == 0) + // Suppresses the "please test this build" warning drawn over the game screen. + // The exact version of this very build has to be spelled out (it is printed in + // the warning itself and in the release title), so that the switch can't be set + // once and then silently carried over into a mod release with a newer build. + if (_stricmp(pArg, "-HideVersionWarning=" FILE_VERSION_STR) == 0) { HideWarning = true; } @@ -303,9 +307,9 @@ DEFINE_HOOK(0x4F4583, GScreenClass_DrawText, 0x6) int coordY = 0; #ifndef RELEASE -#ifndef STR_GIT_COMMIT +#ifndef NIGHTLY if (!HideWarning) -#endif // !STR_GIT_COMMIT +#endif // !NIGHTLY { auto wanted = Drawing::GetTextDimensions(Phobos::VersionDescription, { 0, 0 }, 0, 2, 0); From 6a8b74def6a669726b30dc293195a56d76154618 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 1 Aug 2026 23:58:34 +0300 Subject: [PATCH 11/55] Ship the exception database and SyringeEx with releases --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e97bf0b63b..7b0675730f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: runs-on: windows-2022 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive @@ -60,4 +60,7 @@ jobs: files: | ${{ env.BUILD_CONFIGURATION }}/Phobos.dll ${{ env.BUILD_CONFIGURATION }}/Phobos.pdb + ${{ env.BUILD_CONFIGURATION }}/gamemd.edb + ${{ env.BUILD_CONFIGURATION }}/Syringe.exe + ${{ env.BUILD_CONFIGURATION }}/Syringe.LICENSE.txt body_path: RELEASE_NOTES.md From 3dc36c4230b715afb34a0e75fe592a80cf33704c Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 1 Aug 2026 23:58:35 +0300 Subject: [PATCH 12/55] Resolve pre-release tags to their version's changelog section --- scripts/extract_changelog.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index bd0b5e0b7f..b4ea11eb10 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -67,15 +67,16 @@ $matchedNode = $null # 1) Exact version match $matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -eq $version } | Select-Object -First 1 -# 2) Strip trailing numeric components until found +# 2) Drop the pre-release suffix (pre-releases share the section of the version they lead up to, +# e.g. 0.5-beta1 -> 0.5), then strip trailing numeric components until found (0.5.0.1 -> 0.5.0 -> 0.5) if (-not $matchedNode) { - $tryVersion = $version - $stripped = $tryVersion -replace '\.[0-9]+$', '' - while ($stripped -ne $tryVersion) { - $tryVersion = $stripped + $tryVersion = $version -replace '-.*$', '' + while ($true) { $matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -eq $tryVersion } | Select-Object -First 1 if ($matchedNode) { break } $stripped = $tryVersion -replace '\.[0-9]+$', '' + if ($stripped -eq $tryVersion) { break } + $tryVersion = $stripped } } From baaf7c8e98cfe3f25a4d343daf889924eb6e0041 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 1 Aug 2026 23:59:05 +0300 Subject: [PATCH 13/55] Retire the leftovers of the removed DevBuild configuration --- .github/copilot-instructions.md | 14 ++++++++------ .vscode/tasks.json | 3 --- scripts/build.bat | 5 +++-- scripts/build_debug.bat | 2 +- .../{build_devbuild.bat => build_prerelease.bat} | 4 ++-- scripts/clean.bat | 1 - 6 files changed, 14 insertions(+), 15 deletions(-) rename scripts/{build_devbuild.bat => build_prerelease.bat} (58%) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index be74b16347..ad21b48114 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -23,17 +23,19 @@ git submodule update --init --recursive | Config | Command | Output | |--------|---------|--------| | Debug (recommended for dev) | `scripts\build_debug.bat` | `Debug\Phobos.dll` + `.pdb` | -| DevBuild (CI nightly) | `scripts\build_devbuild.bat` | `DevBuild\Phobos.dll` + `.pdb` | | Release | `scripts\build_release.bat` | `Release\Phobos.dll` + `.pdb` | +| Release, stamped as a pre-release | `scripts\build_prerelease.bat` | `Release\Phobos.dll` + `.pdb` | + +There are only two build configurations, `Debug` and `Release`. What kind of build is produced (nightly, pre-release or stable release) is a separate axis, set by the `BuildType` MSBuild property (`NIGHTLY`, `PRERELEASE` or `RELEASE`), which defines the preprocessor macro of the same name; an unset `BuildType` means a plain local build. These scripts invoke `scripts\run_msbuild.bat`, which locates the VS Developer Command Prompt via `vswhere.exe` (bundled in `scripts/`), then runs `msbuild`. **VS 2022 or VS Build Tools 2022** with the components listed in `.vsconfig` must be installed: - `Microsoft.VisualStudio.Component.VC.Tools.x86.x64` - `Microsoft.VisualStudio.Component.Windows10SDK.20348` - `Microsoft.VisualStudio.Component.VC.ATL` -**In VS Code**, prefer the pre-configured build tasks over running scripts directly unless there are issues. The workspace defines a **"Build Phobos"** task (default build task) that prompts for Debug/DevBuild/Release. Run it via `Ctrl+Shift+B` or the `Tasks: Run Build Task` command. A **"Cleanup build folders"** task is also available. +**In VS Code**, prefer the pre-configured build tasks over running scripts directly unless there are issues. The workspace defines a **"Build Phobos"** task (default build task) that prompts for Debug/Release. Run it via `Ctrl+Shift+B` or the `Tasks: Run Build Task` command. A **"Cleanup build folders"** task is also available. -**In Visual Studio 2022**, most contributors build directly from the IDE using `Build > Build Solution` (`Ctrl+Shift+B`) with the solution configuration dropdown (Debug/DevBuild/Release). The batch scripts are not needed when building from VS. +**In Visual Studio 2022**, most contributors build directly from the IDE using `Build > Build Solution` (`Ctrl+Shift+B`) with the solution configuration dropdown (Debug/Release). The batch scripts are not needed when building from VS. The build takes roughly 1–3 minutes for a full rebuild. Incremental builds are much faster. To clean: ``` @@ -42,16 +44,16 @@ scripts\clean.bat ### CI build (GitHub Actions) -The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **DevBuild** config with MSBuild, passing `/p:GitCommit= /p:GitBranch=` for version stamping. The agent should replicate the CI as: +The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:GitCommit= /p:GitBranch=` for version stamping and `/p:BuildType=` for the build type. The agent should replicate the CI as: ``` -msbuild /m /p:Configuration=DevBuild Phobos.sln +msbuild /m /p:Configuration=Release /p:BuildType=NIGHTLY Phobos.sln ``` ## Tests & Validation There is **no automated test suite**. Validation is: 1. **Successful compilation** with zero errors (warning level 4, but not treated as errors). -2. **PR CI checks** - the `Pull Request Nightly Build` workflow must pass (builds DevBuild config). +2. **PR CI checks** - the `Pull Request Nightly Build` workflow must pass (builds the Release config as a nightly). 3. **PR doc checker** (`.github/workflows/pr-doc-checker.yml`) - unless the PR has the `No Documentation Needed` label, these files must be modified: - `docs/Whats-New.md` (changelog entry) - `CREDITS.md` (credit entry; skipped if the `Bugfix` label is set) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c441998e81..7a64122948 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -77,9 +77,6 @@ { "value": "Debug" }, - { - "value": "DevBuild" - }, { "value": "Release" } diff --git a/scripts/build.bat b/scripts/build.bat index ad24a6f110..e4aa7e5de9 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -1,8 +1,9 @@ @if not defined _echo echo off -rem Builds a provided build config. +rem Builds a provided build config, optionally with a provided build type +rem (NIGHTLY, PRERELEASE or RELEASE - none means a plain local build). rem Ensure we're in correct directory. cd /D "%~dp0" -call run_msbuild /maxCpuCount /consoleloggerparameters:NoSummary /property:Configuration=%1 +call run_msbuild /maxCpuCount /consoleloggerparameters:NoSummary /property:Configuration=%1 /property:BuildType=%2 diff --git a/scripts/build_debug.bat b/scripts/build_debug.bat index 5502ae892a..18bc79e806 100644 --- a/scripts/build_debug.bat +++ b/scripts/build_debug.bat @@ -1,6 +1,6 @@ @if not defined _echo echo off -rem Builds Phobos DevBuild. +rem Builds Phobos Debug. rem Ensure we're in correct directory. cd /D "%~dp0" diff --git a/scripts/build_devbuild.bat b/scripts/build_prerelease.bat similarity index 58% rename from scripts/build_devbuild.bat rename to scripts/build_prerelease.bat index 0cc72323e2..608c62aa5d 100644 --- a/scripts/build_devbuild.bat +++ b/scripts/build_prerelease.bat @@ -1,8 +1,8 @@ @if not defined _echo echo off -rem Builds Phobos DevBuild. +rem Builds Phobos pre-release. rem Ensure we're in correct directory. cd /D "%~dp0" -call build DevBuild +call build Release PRERELEASE diff --git a/scripts/clean.bat b/scripts/clean.bat index 16d98dfa24..07ebbbfefc 100644 --- a/scripts/clean.bat +++ b/scripts/clean.bat @@ -7,5 +7,4 @@ cd /D "%~dp0" cd .. if exist Debug\ rmdir /S /Q Debug\ -if exist DevBuild\ rmdir /S /Q DevBuild\ if exist Release\ rmdir /S /Q Release\ From 3f34bc7c367e467fa763d8175fe3b489d5080f22 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 1 Aug 2026 23:59:05 +0300 Subject: [PATCH 14/55] Update devbuild terminology in the README and docs --- README.md | 4 ++-- docs/Miscellanous.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6b3de732df..a67ee1bb99 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ Downloads You can choose one of the following: - [Latest stable branch build](https://github.com/Phobos-developers/Phobos/releases/latest) (most bug-free release but very slow on new features) -- [Latest development branch builds](https://github.com/Phobos-developers/Phobos/releases) (a bit less bug-free releases, devbuilds get new features when they are finished) -- [Latest development branch nightly](https://nightly.link/Phobos-developers/Phobos/blob/develop/.github/workflows/nightly.yml) (added unreleased features that will be in next devbuild) +- [Latest pre-release builds](https://github.com/Phobos-developers/Phobos/releases) (a bit less bug-free releases, pre-releases get new features when they are finished) +- [Latest development branch nightly](https://nightly.link/Phobos-developers/Phobos/blob/develop/.github/workflows/nightly.yml) (added unreleased features that will be in the next pre-release) - Individual new feature nightly builds for testing can be found in [pull requests](https://github.com/Phobos-developers/Phobos/pulls) ### Note on nightly builds diff --git a/docs/Miscellanous.md b/docs/Miscellanous.md index 4085f193ee..b135eb385a 100644 --- a/docs/Miscellanous.md +++ b/docs/Miscellanous.md @@ -278,7 +278,7 @@ When the exception handler is enabled, Phobos replaces the game's own crash hand Crash folders older than 5 days are cleaned up automatically. The `-FullCrashDump` command line arg makes the automatically written minidump a full memory dump (useful for unattended setups). Crash reports can be enriched with extra information: -- If a `gamemd.pdb` file is present in the game directory, it is used to resolve game addresses to symbol names in the call stacks. `Phobos.pdb` (shipped with devbuilds and nightlies) is picked up automatically for Phobos's own addresses. +- If a `gamemd.pdb` file is present in the game directory, it is used to resolve game addresses to symbol names in the call stacks. `Phobos.pdb` (shipped with every build) is picked up automatically for Phobos's own addresses. - If a `gamemd.edb` exception database file is present in the game directory, its description for the faulting address (if any) is included in the report under "Additional information". The format is shared with Vinifera: one entry per line, `;` starts a comment. ```text From 91d45e5e7c166776a36216b472a85f889d618db9 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sun, 2 Aug 2026 00:15:40 +0300 Subject: [PATCH 15/55] Build release notes from the heading tree, grouped by type of change --- .github/workflows/release.yml | 5 +- scripts/extract_changelog.ps1 | 301 +++++++++++++++++++++++++--------- 2 files changed, 227 insertions(+), 79 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b0675730f..12b1f1e072 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,13 +45,14 @@ jobs: build-config: ${{ env.BUILD_CONFIGURATION }} build-type: ${{ steps.info.outputs.build-type }} - - name: Extract Changelog + - name: Extract Release Notes shell: pwsh run: | .\scripts\extract_changelog.ps1 ` -Tag "${{ github.ref_name }}" ` -WhatsNewPath docs\Whats-New.md ` - -OutputPath RELEASE_NOTES.md + -OutputPath RELEASE_NOTES.md ` + -LinkBase "https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/docs" - name: Upload Release Assets uses: softprops/action-gh-release@v2 diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index b4ea11eb10..9169d65acf 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -1,3 +1,20 @@ +<# +.SYNOPSIS + Extracts the release notes for a tag out of the What's New document. + +.DESCRIPTION + Whats-New.md is laid out as "type of change -> version": each top level section + (`## Breaking changes`, `## Changelog`) holds one subsection per version. A release + is about a single version, so this script parses the document into a heading tree, + picks every subsection matching the released version wherever it sits in the tree, + and re-emits them as "version -> type of change" - the version is the release + itself, so the type of change becomes the top level heading. + + Markup that only means something to Sphinx is normalized on the way out, so the + result can be used as a release body as is: `{dropdown}` directives are unwrapped, + admonitions become GitHub alerts, directive options are dropped and relative + documentation links are resolved against -LinkBase. +#> param( [Parameter(Mandatory = $true)] [string]$Tag, @@ -6,114 +23,244 @@ param( [string]$WhatsNewPath, [Parameter(Mandatory = $true)] - [string]$OutputPath + [string]$OutputPath, + + # Base URL to resolve relative documentation links against, e.g. + # https://github.com/Phobos-developers/Phobos/blob/v0.5/docs + # When empty the links are emitted unchanged, which only works within the docs themselves. + [string]$LinkBase = '' ) -$version = $Tag.TrimStart('v') +$ErrorActionPreference = 'Stop' -if (-not (Test-Path $WhatsNewPath)) { - Write-Error "File not found: $WhatsNewPath" - exit 1 +# MyST admonitions that map onto GitHub alerts. Anything else is unwrapped silently. +$AlertKinds = @{ + 'note' = 'NOTE' + 'seealso' = 'NOTE' + 'admonition' = 'NOTE' + 'tip' = 'TIP' + 'hint' = 'TIP' + 'important' = 'IMPORTANT' + 'attention' = 'IMPORTANT' + 'warning' = 'WARNING' + 'caution' = 'CAUTION' + 'danger' = 'CAUTION' + 'error' = 'CAUTION' } -$content = (Get-Content -Path $WhatsNewPath -Raw -Encoding utf8) -replace "`r`n", "`n" - -# ---- Parse the document into a heading tree ---- -$lines = $content -split "`n" -$headings = @() -for ($i = 0; $i -lt $lines.Count; $i++) { - if ($lines[$i] -match '^(#{1,6})\s+(.+)$') { - $headings += @{ - Level = $matches[1].Length - Text = $matches[2] - Line = $i +$linkPrefix = $LinkBase.TrimEnd('/') + +function New-Section([int]$Level, [string]$Title, $Parent) { + [pscustomobject]@{ + Level = $Level + Title = $Title + Parent = $Parent + Lines = [System.Collections.Generic.List[string]]::new() + Children = [System.Collections.Generic.List[object]]::new() + } +} + +# Returns the fence descriptor for a line that opens or closes a fenced block, else $null. +function Get-Fence([string]$Line) { + if ($Line -match '^ {0,3}(?`{3,}|~{3,})[ \t]*(?.*?)[ \t]*$') { + return [pscustomobject]@{ + Char = $Matches.fence.Substring(0, 1) + Len = $Matches.fence.Length + Info = $Matches.info } } + return $null } -# ---- Find ## Changelog and its ### children ---- -$changelogNode = $headings | Where-Object { $_.Level -eq 2 -and $_.Text -eq 'Changelog' } | Select-Object -First 1 -if (-not $changelogNode) { - Write-Error "Could not find '## Changelog' section in $WhatsNewPath" - exit 1 +# A fence closes another one when it uses the same character, is at least as long +# and carries no info string. +function Test-FenceCloses($Fence, $Opener) { + return ($Fence.Char -eq $Opener.Char) -and ($Fence.Len -ge $Opener.Len) -and ($Fence.Info -eq '') } -# Next heading at level <= 2 marks the end of ## Changelog -$changelogEndNode = $headings | Where-Object { $_.Level -le 2 -and $_.Line -gt $changelogNode.Line } | Select-Object -First 1 -$changelogEnd = if ($changelogEndNode) { $changelogEndNode.Line } else { $lines.Count } +# Relative links into the docs are meaningless outside of them, so anchor them to -LinkBase. +function Convert-Link([string]$Text) { + if (-not $linkPrefix) { return $Text } -# All ### headings within ## Changelog -$versionNodes = $headings | Where-Object { - $_.Level -eq 3 -and $_.Line -gt $changelogNode.Line -and $_.Line -lt $changelogEnd + $evaluator = [System.Text.RegularExpressions.MatchEvaluator] { + param($m) + '](' + $linkPrefix + '/' + $m.Groups['path'].Value + $m.Groups['frag'].Value + ')' + } + return [regex]::Replace($Text, '\]\((?!\w+:|[#/])(?[^)\s#]+\.md)(?#[^)\s]*)?\)', $evaluator) } -if ($versionNodes.Count -eq 0) { - Write-Error "No version sections found within '## Changelog'" - exit 1 +# ---- Parse the document into a tree of sections carrying their own contents ---- + +function Read-SectionTree([string[]]$Lines) { + $root = New-Section 0 '' $null + $current = $root + + # Fenced code blocks are opaque - their contents are never markdown. + # MyST directive fences (```{dropdown} etc.) are transparent - they wrap markdown, + # headings included, so they only affect how the contents are emitted. + $code = $null + $directives = [System.Collections.Generic.List[object]]::new() + $inOptions = $false + + foreach ($line in $Lines) { + + if ($code) { + $current.Lines.Add($line) + $fence = Get-Fence $line + if ($fence -and (Test-FenceCloses $fence $code)) { $code = $null } + continue + } + + $fence = Get-Fence $line + if ($fence) { + # Innermost first: a bare fence closes the directive it is nested in. + if ($directives.Count -gt 0 -and (Test-FenceCloses $fence $directives[$directives.Count - 1])) { + $directives.RemoveAt($directives.Count - 1) + $inOptions = $false + continue + } + + if ($fence.Info.StartsWith('{')) { + $kind = ($fence.Info -replace '^\{([^}]*)\}.*$', '$1').Trim().ToLowerInvariant() + $alert = $null + if ($AlertKinds.ContainsKey($kind)) { + $alert = $AlertKinds[$kind] + $current.Lines.Add('') + $current.Lines.Add("> [!$alert]") + } + $directives.Add([pscustomobject]@{ Char = $fence.Char; Len = $fence.Len; Kind = $kind; Alert = $alert }) + $inOptions = $true + continue + } + + $code = $fence + $current.Lines.Add($line) + continue + } + + # `:open:` and friends configure the directive, they are not contents. + if ($inOptions) { + if ($line -match '^\s*:[A-Za-z][A-Za-z0-9_-]*:') { continue } + $inOptions = $false + } + + if ($line -match '^(?#{1,6})\s+(?.*?)\s*#*\s*$') { + $level = $Matches.hashes.Length + while ($current.Level -ge $level) { $current = $current.Parent } + + $node = New-Section $level (Convert-Link $Matches.title) $current + $current.Children.Add($node) + $current = $node + continue + } + + $text = Convert-Link $line + + # Contents of an admonition have to be quoted for the alert to hold them. + $alert = $null + for ($i = $directives.Count - 1; $i -ge 0; $i--) { + if ($directives[$i].Alert) { $alert = $directives[$i].Alert; break } + } + if ($alert) { $text = if ($text.Trim() -eq '') { '>' } else { "> $text" } } + + $current.Lines.Add($text) + } + + return $root } -# ---- Extract the version string from a ### heading ---- -function Get-VersionText($headingText) { - if ($headingText -match '^Version\s') { - return $headingText # "Version TBD (develop branch nightly builds)" +function Get-AllSections($Node) { + foreach ($child in $Node.Children) { + $child + Get-AllSections $child } - # Take the first space-delimited token, e.g. "0.4.0.3" - return ($headingText -split '\s')[0] } -# ---- Match version ---- -$matchedNode = $null - -# 1) Exact version match -$matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -eq $version } | Select-Object -First 1 - -# 2) Drop the pre-release suffix (pre-releases share the section of the version they lead up to, -# e.g. 0.5-beta1 -> 0.5), then strip trailing numeric components until found (0.5.0.1 -> 0.5.0 -> 0.5) -if (-not $matchedNode) { - $tryVersion = $version -replace '-.*$', '' - while ($true) { - $matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -eq $tryVersion } | Select-Object -First 1 - if ($matchedNode) { break } - $stripped = $tryVersion -replace '\.[0-9]+$', '' - if ($stripped -eq $tryVersion) { break } - $tryVersion = $stripped +# The version a section documents, or $null when it documents no particular version. +function Get-SectionVersion([string]$Title) { + if ($Title -match '^\s*Version\s+TBD\b') { return 'TBD' } + if ($Title -match '^\s*[vV]?(?<version>\d+(\.\d+)*(-[0-9A-Za-z.]+)?)\b') { return $Matches.version } + return $null +} + +# ---- Render a matched section as "type of change -> contents" ---- + +function Add-SectionContents($Node, [int]$Shift, $Sink) { + foreach ($line in $Node.Lines) { $Sink.Add($line) } + + foreach ($child in $Node.Children) { + $level = [Math]::Min(6, [Math]::Max(1, $child.Level + $Shift)) + $Sink.Add('') + $Sink.Add(('#' * $level) + ' ' + $child.Title) + Add-SectionContents $child $Shift $Sink } } -# 3) Fall back to Version TBD -if (-not $matchedNode) { - $matchedNode = $versionNodes | Where-Object { (Get-VersionText $_.Text) -match '^Version TBD' } | Select-Object -First 1 +# The path from the top level section down to the matched one names the type of change. +function Get-SectionKind($Node) { + $chain = @() + $parent = $Node.Parent + while ($parent -and $parent.Level -ge 2) { + $chain = , $parent.Title + $chain + $parent = $parent.Parent + } + return ($chain -join ' / ') } -if (-not $matchedNode) { - Write-Error "Could not find changelog section for version '$version' or 'Version TBD'" - exit 1 +# ---- Main ---- + +if (-not (Test-Path -LiteralPath $WhatsNewPath)) { + throw "File not found: $WhatsNewPath" } -# ---- Extract content between this ### and the next ### (or end of ## Changelog) ---- -$sectionStart = $matchedNode.Line + 1 # skip the heading line itself -$nextNode = $versionNodes | Where-Object { $_.Line -gt $matchedNode.Line } | Select-Object -First 1 -$sectionEnd = if ($nextNode) { $nextNode.Line } else { $changelogEnd } +$content = (Get-Content -LiteralPath $WhatsNewPath -Raw -Encoding utf8) -replace "`r`n", "`n" +$root = Read-SectionTree ($content -split "`n") +$sections = @(Get-AllSections $root) + +$version = $Tag -replace '^[vV]', '' +$baseVersion = $version -replace '-.*$', '' + +$matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $version }) + +# Only a pre-release may document itself under another name: it shares the sections of the +# version it leads up to (0.5-beta1 -> 0.5), which are still called "Version TBD" until the +# release branch is cut. A stable or patch release documenting nothing is an oversight, and +# publishing whatever else is at hand - the unreleased develop notes above all - would hide it. +if (-not $matched -and $baseVersion -ne $version) { + $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $baseVersion }) + if ($matched) { Write-Host "No sections for '$version', using the ones for '$baseVersion'" } -$sectionLines = $lines[$sectionStart..($sectionEnd - 1)] -$sectionContent = ($sectionLines -join "`n").Trim() + if (-not $matched) { + $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq 'TBD' }) + if ($matched) { Write-Warning "No sections for '$version', falling back to 'Version TBD' - rename them to '$baseVersion' in $WhatsNewPath" } + } +} -# ---- Handle outer ```{dropdown} ... ``` wrapper ---- -$dropdownPattern = '(?ms)^```\{dropdown\}[^\n]*\n(.+?)^```$' -$dropdownMatch = [regex]::Match($sectionContent, $dropdownPattern) -if ($dropdownMatch.Success) { - $sectionContent = $dropdownMatch.Groups[1].Value.Trim() +if (-not $matched) { + $known = @($sections | ForEach-Object { Get-SectionVersion $_.Title } | Where-Object { $_ } | Select-Object -Unique) -join ', ' + throw "No sections for version '$version' in $WhatsNewPath. Documented versions: $known" +} + +$out = [System.Collections.Generic.List[string]]::new() +foreach ($section in $matched) { + $kind = Get-SectionKind $section + if ($kind) { + $out.Add('') + $out.Add("## $kind") + } + # The matched section's own children start right below the type of change heading. + Add-SectionContents $section (2 - $section.Level) $out } -# Remove :open: Sphinx directive -$sectionContent = $sectionContent -replace '(?m)^:open:\s*\n', '' -$sectionContent = $sectionContent.Trim() +$text = ($out -join "`n") -replace '(?m)[ \t]+$', '' +$text = ($text -replace "\n{3,}", "`n`n").Trim() -if (-not $sectionContent) { - Write-Error "Extracted changelog content is empty for version '$version'" - exit 1 +if (-not $text) { + throw "Extracted release notes for version '$version' are empty" } -$sectionContent | Out-File -FilePath $OutputPath -Encoding utf8 +$outPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) { $OutputPath } else { Join-Path (Get-Location).Path $OutputPath } +[System.IO.File]::WriteAllText($outPath, $text + "`n", (New-Object System.Text.UTF8Encoding($false))) -Write-Host "Extracted changelog for version '$version' to '$OutputPath'" +$kinds = @($matched | ForEach-Object { Get-SectionKind $_ }) -join ', ' +Write-Host "Extracted release notes for version '$version' ($kinds) to '$OutputPath'" From 934c6d7b4afbe200845df87a60385ff087d47225 Mon Sep 17 00:00:00 2001 From: ZivDero <kirill.andriiashin@gmail.com> Date: Sun, 2 Aug 2026 00:32:38 +0300 Subject: [PATCH 16/55] Keep the patch number out of the savegame ID --- src/Phobos.version.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 03e446e51f..f5d2e1f0da 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -29,9 +29,10 @@ // Nightly defines GIT_COMMIT and GIT_BRANCH in GH Actions // NIGHTLY / PRERELEASE / RELEASE come from compiler option BuildType - used by GH Actions as well -// Savegame compatibility is tied to the version alone: nightlies and pre-releases of a version -// are expected to stay save-compatible with the stable release they lead up to. -#define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8) | VERSION_PATCH) +// Savegame compatibility is tied to the version with the patch number left out: patch releases +// only carry Phobos bugfixes and stay compatible with each other. Nightlies and pre-releases of +// a version share the ID with the stable release they lead up to. +#define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8)) #if defined(NIGHTLY) #define STR_GIT_COMMIT _STR(GIT_COMMIT) From 66cdb4644fea6653b79445c7bc5379e0b8e81b95 Mon Sep 17 00:00:00 2001 From: ZivDero <kirill.andriiashin@gmail.com> Date: Sun, 2 Aug 2026 00:32:38 +0300 Subject: [PATCH 17/55] Release pre-releases as their version plus the pre-release subsections --- scripts/extract_changelog.ps1 | 65 +++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index 9169d65acf..878be48a30 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -57,6 +57,7 @@ function New-Section([int]$Level, [string]$Title, $Parent) { Parent = $Parent Lines = [System.Collections.Generic.List[string]]::new() Children = [System.Collections.Generic.List[object]]::new() + Skip = $false } } @@ -177,18 +178,25 @@ function Get-AllSections($Node) { } # The version a section documents, or $null when it documents no particular version. +# The whole title has to be the version, so that a section like "3 new trajectories" is contents. function Get-SectionVersion([string]$Title) { if ($Title -match '^\s*Version\s+TBD\b') { return 'TBD' } - if ($Title -match '^\s*[vV]?(?<version>\d+(\.\d+)*(-[0-9A-Za-z.]+)?)\b') { return $Matches.version } + if ($Title -match '^\s*[vV]?(?<version>\d+(\.\d+)+(-[0-9A-Za-z.]+)?)(\s+\(.*\))?\s*$') { return $Matches.version } return $null } +function Test-HasContents($Node) { + return ($Node.Children.Count -gt 0) -or (@($Node.Lines | Where-Object { $_.Trim() }).Count -gt 0) +} + # ---- Render a matched section as "type of change -> contents" ---- function Add-SectionContents($Node, [int]$Shift, $Sink) { foreach ($line in $Node.Lines) { $Sink.Add($line) } foreach ($child in $Node.Children) { + if ($child.Skip) { continue } + $level = [Math]::Min(6, [Math]::Max(1, $child.Level + $Shift)) $Sink.Add('') $Sink.Add(('#' * $level) + ' ' + $child.Title) @@ -196,6 +204,28 @@ function Add-SectionContents($Node, [int]$Shift, $Sink) { } } +# A pre-release is documented as a subsection of the version it leads up to, holding what changed +# since the previous one, so those subsections are builds rather than kinds of change and are left +# out by default. A pre-release build carries the fixes of every pre-release before it as well and +# they are listed newest first, so the tagged subsection and everything below it are kept. Ones +# without contents are dropped - such a pre-release is just the parent version as it stands. +function Set-BuildSectionVisibility($Section, [string]$PreRelease) { + $builds = @($Section.Children | Where-Object { Get-SectionVersion $_.Title }) + foreach ($build in $builds) { $build.Skip = $true } + + if (-not $PreRelease) { return } + + $tagged = -1 + for ($i = 0; $i -lt $builds.Count; $i++) { + if ((Get-SectionVersion $builds[$i].Title) -eq $PreRelease) { $tagged = $i; break } + } + if ($tagged -lt 0) { return } + + for ($i = $tagged; $i -lt $builds.Count; $i++) { + if (Test-HasContents $builds[$i]) { $builds[$i].Skip = $false } + } +} + # The path from the top level section down to the matched one names the type of change. function Get-SectionKind($Node) { $chain = @() @@ -220,19 +250,30 @@ $sections = @(Get-AllSections $root) $version = $Tag -replace '^[vV]', '' $baseVersion = $version -replace '-.*$', '' -$matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $version }) +$isPreRelease = $baseVersion -ne $version +$preRelease = '' -# Only a pre-release may document itself under another name: it shares the sections of the -# version it leads up to (0.5-beta1 -> 0.5), which are still called "Version TBD" until the -# release branch is cut. A stable or patch release documenting nothing is an oversight, and -# publishing whatever else is at hand - the unreleased develop notes above all - would hide it. -if (-not $matched -and $baseVersion -ne $version) { +# A pre-release is released as its parent version plus whatever the pre-release subsections +# below it record, so it is the parent sections that get rendered. +$matched = @() +if ($isPreRelease) { $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $baseVersion }) - if ($matched) { Write-Host "No sections for '$version', using the ones for '$baseVersion'" } + if ($matched) { $preRelease = $version } +} - if (-not $matched) { - $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq 'TBD' }) - if ($matched) { Write-Warning "No sections for '$version', falling back to 'Version TBD' - rename them to '$baseVersion' in $WhatsNewPath" } +if (-not $matched) { + $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $version }) +} + +# Only a pre-release may document itself under another name: until the release branch is cut its +# sections are still called "Version TBD". A stable or patch release documenting nothing is an +# oversight, and publishing whatever else is at hand - the unreleased develop notes above all - +# would hide it. +if (-not $matched -and $isPreRelease) { + $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq 'TBD' }) + if ($matched) { + $preRelease = $version + Write-Warning "No sections for '$version', falling back to 'Version TBD' - rename them to '$baseVersion' in $WhatsNewPath" } } @@ -248,6 +289,8 @@ foreach ($section in $matched) { $out.Add('') $out.Add("## $kind") } + Set-BuildSectionVisibility $section $preRelease + # The matched section's own children start right below the type of change heading. Add-SectionContents $section (2 - $section.Level) $out } From b120288b6fce07960ccde2489d0f74148110cc21 Mon Sep 17 00:00:00 2001 From: ZivDero <kirill.andriiashin@gmail.com> Date: Sun, 2 Aug 2026 01:01:18 +0300 Subject: [PATCH 18/55] Derive all build metadata from a single set of product and version defines --- src/Phobos.cpp | 11 +++++---- src/Phobos.version.h | 58 +++++++++++++++++++++++++++++--------------- src/version.rc | 6 ++--- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/Phobos.cpp b/src/Phobos.cpp index f50f9487e8..1b8d571069 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -30,12 +30,12 @@ bool Phobos::Optimizations::DisableBalloonHoverPathingFix = false; bool Phobos::Optimizations::DisableRadDamageOnBuildings = true; bool Phobos::Optimizations::DisableSyncLogging = false; +// The leading L"" widens the narrow metadata literals it is concatenated with, so that the +// name and the version are taken from Phobos.version.h rather than spelled out again. #ifdef NIGHTLY -const wchar_t* Phobos::VersionDescription = L"Phobos nightly build (" STR_GIT_COMMIT L" @ " STR_GIT_BRANCH L"). DO NOT SHIP IN MODS!"; +const wchar_t* Phobos::VersionDescription = L"" PRODUCT_NAME " " PRODUCT_VERSION L". DO NOT SHIP IN MODS!"; #elif !defined(RELEASE) -const wchar_t* Phobos::VersionDescription = L"Phobos pre-release build v" FILE_VERSION_STR L". Please test the build before shipping."; -#else -//const wchar_t* Phobos::VersionDescription = L"Phobos release build v" FILE_VERSION_STR L"."; +const wchar_t* Phobos::VersionDescription = L"" PRODUCT_NAME " " PRODUCT_VERSION L". Please test the build before shipping."; #endif @@ -63,7 +63,8 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) // The exact version of this very build has to be spelled out (it is printed in // the warning itself and in the release title), so that the switch can't be set // once and then silently carried over into a mod release with a newer build. - if (_stricmp(pArg, "-HideVersionWarning=" FILE_VERSION_STR) == 0) + if (_stricmp(pArg, "-HideVersionWarning=" FILE_VERSION_STR) == 0 + || _stricmp(pArg, "-HideVersionWarning=v" FILE_VERSION_STR) == 0) // as shown in the warning { HideWarning = true; } diff --git a/src/Phobos.version.h b/src/Phobos.version.h index f5d2e1f0da..4ad00fb4b2 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -1,12 +1,10 @@ #ifndef VERSION_H #define VERSION_H -#define _WSTR(x) _WSTR_(x) -#define _WSTR_(x) L ## #x #define _STR(x) _STR_(x) #define _STR_(x) #x -#pragma region Release build version numbering +#pragma region Version numbering // Indicates project maturity and completeness #define VERSION_MAJOR 0 @@ -20,37 +18,59 @@ // Indicates Phobos-related bugfixes only #define VERSION_PATCH 1 -#pragma endregion +// Identifier of the pre-release being prepared, spelled out in full rather than as a number, +// so that it can be anything semantic versioning allows - "alpha1", "beta2", "rc3". +// Only ever used by PRERELEASE builds; bump it on each pre-release. +#define PRERELEASE_SUFFIX "beta1" -// Pre-release identifier, appended to the version as a semantic versioning suffix. -// Any identifier is allowed, e.g. alpha1, beta2 or rc3 - bump it on each pre-release build. -#define PRERELEASE_SUFFIX beta1 +#pragma endregion -// Nightly defines GIT_COMMIT and GIT_BRANCH in GH Actions -// NIGHTLY / PRERELEASE / RELEASE come from compiler option BuildType - used by GH Actions as well +#define VERSION_SHORT_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) +#define VERSION_LONG_STR VERSION_SHORT_STR "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) // Savegame compatibility is tied to the version with the patch number left out: patch releases // only carry Phobos bugfixes and stay compatible with each other. Nightlies and pre-releases of // a version share the ID with the stable release they lead up to. #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8)) +#pragma region Build metadata + +// NIGHTLY / PRERELEASE / RELEASE come from the BuildType compiler option - used by GH Actions as +// well - and none of them being set means a local build. Nightlies additionally get GIT_COMMIT +// and GIT_BRANCH. Everything below is derived from those, so that the product name, the version +// and the build type are each spelled out once and stay in sync everywhere they surface. + +#define PRODUCT_NAME "Phobos" +#define PRODUCT_FILE_NAME PRODUCT_NAME ".dll" +#define PRODUCT_SUMMARY "Ares-compatible YR engine extension" + #if defined(NIGHTLY) #define STR_GIT_COMMIT _STR(GIT_COMMIT) #define STR_GIT_BRANCH _STR(GIT_BRANCH) - #define FILE_DESCRIPTION "Unstable nightly build of Phobos engine extension" - #define FILE_VERSION_STR "Commit " STR_GIT_COMMIT - #define FILE_VERSION 0 - #define PRODUCT_VERSION "Nightly Build " STR_GIT_COMMIT " @ " STR_GIT_BRANCH + + #define BUILD_TYPE_NAME "nightly build" + #define FILE_VERSION_STR VERSION_SHORT_STR "-nightly+" STR_GIT_COMMIT + #define PRODUCT_VERSION "v" FILE_VERSION_STR " @ " STR_GIT_BRANCH + #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, 0, 0 #elif defined(PRERELEASE) - #define FILE_DESCRIPTION "Pre-release build of Phobos, Ares-compatible YR engine extension" - #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "-" _STR(PRERELEASE_SUFFIX) + #define BUILD_TYPE_NAME "pre-release build" + #define FILE_VERSION_STR VERSION_SHORT_STR "-" PRERELEASE_SUFFIX + #define PRODUCT_VERSION "v" FILE_VERSION_STR #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, 0, 0 - #define PRODUCT_VERSION "v." FILE_VERSION_STR +#elif defined(RELEASE) + #define BUILD_TYPE_NAME "release build" + #define FILE_VERSION_STR VERSION_LONG_STR + #define PRODUCT_VERSION "v" FILE_VERSION_STR + #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH #else - #define FILE_DESCRIPTION "Phobos, Ares-compatible YR engine extension" - #define FILE_VERSION_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) + #define BUILD_TYPE_NAME "development build" + #define FILE_VERSION_STR VERSION_LONG_STR "-dev" + #define PRODUCT_VERSION "v" FILE_VERSION_STR #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH - #define PRODUCT_VERSION "v." FILE_VERSION_STR #endif +#define FILE_DESCRIPTION PRODUCT_NAME ", " PRODUCT_SUMMARY " (" BUILD_TYPE_NAME ")" + +#pragma endregion + #endif // VERSION_H diff --git a/src/version.rc b/src/version.rc index 240a811ea9..e1f426c135 100644 --- a/src/version.rc +++ b/src/version.rc @@ -20,14 +20,14 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "ProductName", "Phobos\0" + VALUE "ProductName", PRODUCT_NAME "\0" VALUE "CompanyName", "The Phobos Contributors\0" VALUE "LegalCopyright", " The Phobos Contributors 2020-2026\0" VALUE "FileDescription", FILE_DESCRIPTION "\0" VALUE "FileVersion", FILE_VERSION_STR "\0" VALUE "ProductVersion", PRODUCT_VERSION "\0" - VALUE "InternalName", "Phobos.dll\0" - VALUE "OriginalFilename", "Phobos.dll\0" + VALUE "InternalName", PRODUCT_FILE_NAME "\0" + VALUE "OriginalFilename", PRODUCT_FILE_NAME "\0" #ifndef RELEASE VALUE "SpecialBuild", "Testing version. Use at your own risk.\0" From e3f3c5c861bebe6f82c58484b7959fa60057f9a1 Mon Sep 17 00:00:00 2001 From: ZivDero <kirill.andriiashin@gmail.com> Date: Sun, 2 Aug 2026 01:01:36 +0300 Subject: [PATCH 19/55] Bump the version on develop to 0.5 --- src/Phobos.version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 4ad00fb4b2..6f5a39726e 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -10,13 +10,13 @@ #define VERSION_MAJOR 0 // Indicates major changes and significant additions, like new logics -#define VERSION_MINOR 3 +#define VERSION_MINOR 5 // Indicates minor changes, like vanilla bugfixes, unhardcodings or hacks #define VERSION_REVISION 0 // Indicates Phobos-related bugfixes only -#define VERSION_PATCH 1 +#define VERSION_PATCH 0 // Identifier of the pre-release being prepared, spelled out in full rather than as a number, // so that it can be anything semantic versioning allows - "alpha1", "beta2", "rc3". From df6a7c9747a146881c6865650d9aa585b5a8c7db Mon Sep 17 00:00:00 2001 From: ZivDero <kirill.andriiashin@gmail.com> Date: Sun, 2 Aug 2026 01:01:36 +0300 Subject: [PATCH 20/55] Document when develop's version numbers are bumped --- docs/Project-guidelines-and-policies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index 2218d5b9c3..599757399e 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -234,7 +234,7 @@ The styleguide is not exhaustive and may be adjusted in the future. Starting from version 0.5, Phobos adopts a new release strategy to enable faster and more frequent releases. The lifecycle of a version is as follows: -1. **Development phase**: New features and changes are committed to the `develop` branch. +1. **Development phase**: New features and changes are committed to the `develop` branch. `develop` always carries the version it is working towards: as soon as a release branch is cut, `VERSION_MINOR` (or `VERSION_MAJOR`) in `src/Phobos.version.h` is bumped and `VERSION_REVISION`/`VERSION_PATCH` are reset to 0, so that nightlies are stamped with the version they lead up to instead of one that has already been released. 2. **Pre-release phase**: When enough features have accumulated on `develop`, a pre-release build (e.g., `v0.5-beta1`) is created. This build marks the start of a new *release branch* (e.g., `release/v0.5`) and signifies that active feature development for version 0.5 is complete. This branch will be used for all subsequent testing and the final stable release. - During this phase, multiple pre-release builds (which can be called beta, alpha, or release candidate) may be published for wider testing. Between pre-releases on the same branch, there shall be no changes that warrant a stable version changelog addition; in other words — only bug fixes, minor additions, and polish to the existing feature set are allowed. 3. **Stable release**: When the pre-release builds are deemed stable enough, a stable release (e.g., `v0.5`) is published from the release's branch. From 27ee94e969cdfa55d9cf49fdee644b933afaab6f Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 03:46:03 +0300 Subject: [PATCH 21/55] feat: unify build versioning and Git stamping Derive Git commit/branch/dirty in MSBuild so local and CI builds are stamped identically, and keep the nightly warning strict. Assisted-by: DeepSeek V4 Flash --- .github/actions/build-phobos/action.yml | 2 +- .github/copilot-instructions.md | 2 +- .github/workflows/nightly.yml | 2 - .github/workflows/pr-nightly.yml | 2 - Phobos.props | 69 ++++++++++++++++++------- src/Misc/ExceptionHandler.Report.cpp | 5 +- src/Phobos.cpp | 6 +++ src/Phobos.version.h | 67 +++++++++++++++++------- 8 files changed, 110 insertions(+), 45 deletions(-) diff --git a/.github/actions/build-phobos/action.yml b/.github/actions/build-phobos/action.yml index 1e4a448f55..223f728b47 100644 --- a/.github/actions/build-phobos/action.yml +++ b/.github/actions/build-phobos/action.yml @@ -30,7 +30,7 @@ runs: working-directory: ${{env.GITHUB_WORKSPACE}} # Add additional options to the MSBuild command line here (like platform or verbosity level). # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Configuration=${{inputs.build-config}} /p:GitCommit=${{github.sha}} /p:GitBranch=${{github.ref}} /p:BuildType=${{inputs.build-type}} ${{inputs.sln-path}} + run: msbuild /m /p:Configuration=${{inputs.build-config}} /p:BuildType=${{inputs.build-type}} ${{inputs.sln-path}} shell: cmd - name: Stage exception database diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ad21b48114..8c5dcd5937 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -44,7 +44,7 @@ scripts\clean.bat ### CI build (GitHub Actions) -The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:GitCommit=<sha> /p:GitBranch=<ref>` for version stamping and `/p:BuildType=<type>` for the build type. The agent should replicate the CI as: +The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=<type>` for the build type. Git commit/branch info is not passed from CI: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the branch (as a full ref, e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate the CI as: ``` msbuild /m /p:Configuration=Release /p:BuildType=NIGHTLY Phobos.sln ``` diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e24c2e2aa3..cb27d97e15 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -13,8 +13,6 @@ env: # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix BUILD_CONFIGURATION: Release - # GIT_COMMIT: $(git rev-parse --short "$GITHUB_SHA") - # GIT_BRANCH: ${GITHUB_REF#refs/heads/} jobs: build: diff --git a/.github/workflows/pr-nightly.yml b/.github/workflows/pr-nightly.yml index 63ff471e14..10354fe56f 100644 --- a/.github/workflows/pr-nightly.yml +++ b/.github/workflows/pr-nightly.yml @@ -11,8 +11,6 @@ env: # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix BUILD_CONFIGURATION: Release - # GIT_COMMIT: $(git rev-parse --short "$GITHUB_SHA") - # GIT_BRANCH: ${GITHUB_REF#refs/heads/} jobs: build: diff --git a/Phobos.props b/Phobos.props index d16dd4699b..468a19c697 100644 --- a/Phobos.props +++ b/Phobos.props @@ -87,24 +87,57 @@ <PreprocessorDefinitions>DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ResourceCompile> </ItemDefinitionGroup> - <!-- GitCommit --> - <ItemDefinitionGroup Condition="'$(GitCommit)' != ''"> - <ClCompile> - <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(AdditionalOptions)</AdditionalOptions> - </ClCompile> - <ResourceCompile> - <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(AdditionalOptions)</AdditionalOptions> - </ResourceCompile> - </ItemDefinitionGroup> - <!-- GitBranch --> - <ItemDefinitionGroup Condition="'$(GitBranch)' != ''"> - <ClCompile> - <AdditionalOptions>/DGIT_BRANCH="$(GitBranch)" %(AdditionalOptions)</AdditionalOptions> - </ClCompile> - <ResourceCompile> - <AdditionalOptions>/DGIT_BRANCH="$(GitBranch)" %(AdditionalOptions)</AdditionalOptions> - </ResourceCompile> - </ItemDefinitionGroup> + <!-- + Git stamping. GitCommit / GitBranch / GitDirty are derived from the local repository at + build time (the branch as the full ref, e.g. refs/heads/develop), so local and CI builds + are stamped alike with no external input. The properties can still be overridden from the + command line. The defines are applied in a target rather than an ItemDefinitionGroup + because the values are only known after the props are evaluated. stderr is redirected to + nul so a missing or failing git cannot fail the build nor leak error text into the version + - it only results in an unstamped build with an informational message. + --> + <Target Name="ComputeGitInfo" BeforeTargets="ClCompile;ResourceCompile"> + <Exec Command="git rev-parse --short HEAD 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" + ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" + Condition="'$(GitCommit)' == ''"> + <Output TaskParameter="ConsoleOutput" PropertyName="GitCommit" /> + </Exec> + <Exec Command="git symbolic-ref HEAD 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" + ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" + Condition="'$(GitBranch)' == ''"> + <Output TaskParameter="ConsoleOutput" PropertyName="GitBranch" /> + </Exec> + <Exec Command="git status --porcelain 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" + ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" + Condition="'$(GitDirty)' == '' and '$(GitCommit)' != ''"> + <Output TaskParameter="ConsoleOutput" PropertyName="GitStatusPorcelain" /> + </Exec> + <PropertyGroup> + <GitDirty Condition="'$(GitDirty)' == '' and '$(GitStatusPorcelain)' != ''">true</GitDirty> + </PropertyGroup> + <Message Condition="'$(GitCommit)' == '' and '$(GitBranch)' == ''" Importance="High" + Text="Could not determine the Git commit/branch - this build will not be stamped with Git info." /> + <ItemGroup> + <ClCompile Condition="'$(GitCommit)' != ''"> + <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(ClCompile.AdditionalOptions)</AdditionalOptions> + </ClCompile> + <ClCompile Condition="'$(GitBranch)' != ''"> + <AdditionalOptions>/DGIT_BRANCH="$(GitBranch)" %(ClCompile.AdditionalOptions)</AdditionalOptions> + </ClCompile> + <ClCompile Condition="'$(GitDirty)' == 'true'"> + <AdditionalOptions>/DGIT_DIRTY %(ClCompile.AdditionalOptions)</AdditionalOptions> + </ClCompile> + <ResourceCompile Condition="'$(GitCommit)' != ''"> + <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> + </ResourceCompile> + <ResourceCompile Condition="'$(GitBranch)' != ''"> + <AdditionalOptions>/DGIT_BRANCH="$(GitBranch)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> + </ResourceCompile> + <ResourceCompile Condition="'$(GitDirty)' == 'true'"> + <AdditionalOptions>/DGIT_DIRTY %(ResourceCompile.AdditionalOptions)</AdditionalOptions> + </ResourceCompile> + </ItemGroup> + </Target> <!-- BuildType --> <ItemDefinitionGroup Condition="'$(BuildType)' == 'NIGHTLY'"> <ClCompile> diff --git a/src/Misc/ExceptionHandler.Report.cpp b/src/Misc/ExceptionHandler.Report.cpp index 0b08c9ee26..8a4168908e 100644 --- a/src/Misc/ExceptionHandler.Report.cpp +++ b/src/Misc/ExceptionHandler.Report.cpp @@ -468,7 +468,10 @@ namespace ExceptionHandler::Append("Phobos version: " PRODUCT_VERSION "\r\n"); #ifdef STR_GIT_COMMIT - ExceptionHandler::Append("Git: " STR_GIT_COMMIT " @ " STR_GIT_BRANCH "\r\n"); + ExceptionHandler::Append("Git commit: " STR_GIT_COMMIT "\r\n"); +#endif +#ifdef STR_GIT_BRANCH + ExceptionHandler::Append("Git branch: " STR_GIT_BRANCH "\r\n"); #endif ExceptionHandler::Append("Phobos base address: 0x%08X\r\n", AresHelper::PhobosBaseAddress); diff --git a/src/Phobos.cpp b/src/Phobos.cpp index 1b8d571069..bd32c41e23 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -135,6 +135,12 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) ExceptionHandler::Init(); Debug::Log("Initialized version: " PRODUCT_VERSION "\n"); +#ifdef STR_GIT_COMMIT + Debug::Log("Git commit: " STR_GIT_COMMIT "\n"); +#endif +#ifdef STR_GIT_BRANCH + Debug::Log("Git branch: " STR_GIT_BRANCH "\n"); +#endif Debug::Log("ExceptionHandler is %s\n", dontSetExceptionHandler ? "not present" : "present"); } diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 6f5a39726e..a4f3385bc6 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -25,8 +25,7 @@ #pragma endregion -#define VERSION_SHORT_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) -#define VERSION_LONG_STR VERSION_SHORT_STR "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) +#define VERSION_LONG_STR _STR(VERSION_MAJOR) "." _STR(VERSION_MINOR) "." _STR(VERSION_REVISION) "." _STR(VERSION_PATCH) // Savegame compatibility is tied to the version with the patch number left out: patch releases // only carry Phobos bugfixes and stay compatible with each other. Nightlies and pre-releases of @@ -36,37 +35,65 @@ #pragma region Build metadata // NIGHTLY / PRERELEASE / RELEASE come from the BuildType compiler option - used by GH Actions as -// well - and none of them being set means a local build. Nightlies additionally get GIT_COMMIT -// and GIT_BRANCH. Everything below is derived from those, so that the product name, the version -// and the build type are each spelled out once and stay in sync everywhere they surface. +// well - and none of them being set means a local build. GIT_COMMIT / GIT_BRANCH / GIT_DIRTY are +// defined by Phobos.props whenever Git info is available (derived from the repository at build +// time; the branch is the full ref, e.g. refs/heads/develop). Pre-releases and releases embed +// that info as auxiliary metadata (crash reports, logs, file resources) while keeping a clean +// version string; nightly and local builds carry the Git info in the version itself and differ +// only in the build type name. Everything below is derived from those inputs so that the product +// name, the version and the build type are each spelled out once and stay in sync everywhere they +// surface. #define PRODUCT_NAME "Phobos" #define PRODUCT_FILE_NAME PRODUCT_NAME ".dll" #define PRODUCT_SUMMARY "Ares-compatible YR engine extension" -#if defined(NIGHTLY) +// The "v" prefix is a human-facing convention for version numbers and is only ever applied in +// front of a numeric version - never in front of a branch or commit. It is applied exactly once +// here in PRODUCT_VERSION and no other macro in this header prepends it. +#define VERSION_PREFIX "v" + +#ifdef GIT_COMMIT #define STR_GIT_COMMIT _STR(GIT_COMMIT) +#endif +#ifdef GIT_BRANCH #define STR_GIT_BRANCH _STR(GIT_BRANCH) +#endif +#ifdef GIT_DIRTY + #define STR_GIT_DIRTY "-dirty" +#else + #define STR_GIT_DIRTY "" +#endif + +// The numeric file version is the full four-part version for every build type. +#define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH - #define BUILD_TYPE_NAME "nightly build" - #define FILE_VERSION_STR VERSION_SHORT_STR "-nightly+" STR_GIT_COMMIT - #define PRODUCT_VERSION "v" FILE_VERSION_STR " @ " STR_GIT_BRANCH - #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, 0, 0 -#elif defined(PRERELEASE) +#if defined(PRERELEASE) #define BUILD_TYPE_NAME "pre-release build" - #define FILE_VERSION_STR VERSION_SHORT_STR "-" PRERELEASE_SUFFIX - #define PRODUCT_VERSION "v" FILE_VERSION_STR - #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, 0, 0 + #define FILE_VERSION_STR VERSION_LONG_STR "-" PRERELEASE_SUFFIX + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR #elif defined(RELEASE) #define BUILD_TYPE_NAME "release build" #define FILE_VERSION_STR VERSION_LONG_STR - #define PRODUCT_VERSION "v" FILE_VERSION_STR - #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR #else - #define BUILD_TYPE_NAME "development build" - #define FILE_VERSION_STR VERSION_LONG_STR "-dev" - #define PRODUCT_VERSION "v" FILE_VERSION_STR - #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH + // Nightly (CI) and local builds are one unified development build differing only in the + // build type name; the version string carries the Git commit and branch when available. + #ifdef NIGHTLY + #define BUILD_TYPE_NAME "nightly build" + #else + #define BUILD_TYPE_NAME "development build" + #endif + #ifdef GIT_COMMIT + #define FILE_VERSION_STR VERSION_LONG_STR "+" STR_GIT_COMMIT STR_GIT_DIRTY + #else + #define FILE_VERSION_STR VERSION_LONG_STR + #endif + #ifdef GIT_BRANCH + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR " @ " STR_GIT_BRANCH + #else + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR + #endif #endif #define FILE_DESCRIPTION PRODUCT_NAME ", " PRODUCT_SUMMARY " (" BUILD_TYPE_NAME ")" From a5dad450bed963f9fa37517c9c3cd04fb6ca1d47 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 15:18:09 +0300 Subject: [PATCH 22/55] feat: make PRERELEASE_SUFFIX the pre-release knob The presence of PRERELEASE_SUFFIX in version.h decides pre-release vs release instead of a separate PRERELEASE BuildType; pre-releases keep their testing warning and SpecialBuild flags. Assisted-by: DeepSeek V4 Flash --- .github/actions/build-phobos/action.yml | 2 +- .github/copilot-instructions.md | 4 +-- .github/workflows/release.yml | 6 ++-- Phobos.props | 8 ----- scripts/build.bat | 3 +- scripts/build_prerelease.bat | 8 ----- src/Phobos.cpp | 8 ++--- src/Phobos.version.h | 42 ++++++++++++++++--------- src/version.rc | 6 ++-- 9 files changed, 42 insertions(+), 45 deletions(-) delete mode 100644 scripts/build_prerelease.bat diff --git a/.github/actions/build-phobos/action.yml b/.github/actions/build-phobos/action.yml index 223f728b47..44c78314db 100644 --- a/.github/actions/build-phobos/action.yml +++ b/.github/actions/build-phobos/action.yml @@ -9,7 +9,7 @@ inputs: description: 'Configuration to build' required: true build-type: - description: 'Build type e.g nightly, prerelease or release' + description: 'Build type: NIGHTLY or RELEASE' required: true default: 'NIGHTLY' syringeex-tag: diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8c5dcd5937..dba989b921 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -26,7 +26,7 @@ git submodule update --init --recursive | Release | `scripts\build_release.bat` | `Release\Phobos.dll` + `.pdb` | | Release, stamped as a pre-release | `scripts\build_prerelease.bat` | `Release\Phobos.dll` + `.pdb` | -There are only two build configurations, `Debug` and `Release`. What kind of build is produced (nightly, pre-release or stable release) is a separate axis, set by the `BuildType` MSBuild property (`NIGHTLY`, `PRERELEASE` or `RELEASE`), which defines the preprocessor macro of the same name; an unset `BuildType` means a plain local build. +There are only two build configurations, `Debug` and `Release`. What kind of build is produced (nightly or stable release) is a separate axis, set by the `BuildType` MSBuild property (`NIGHTLY` or `RELEASE`), which defines the preprocessor macro of the same name; an unset `BuildType` means a plain local build. A pre-release is a `RELEASE` build whose `PRERELEASE_SUFFIX` (hardcoded in `src/Phobos.version.h`) is still defined; remove the suffix there to build a stable release. These scripts invoke `scripts\run_msbuild.bat`, which locates the VS Developer Command Prompt via `vswhere.exe` (bundled in `scripts/`), then runs `msbuild`. **VS 2022 or VS Build Tools 2022** with the components listed in `.vsconfig` must be installed: - `Microsoft.VisualStudio.Component.VC.Tools.x86.x64` @@ -44,7 +44,7 @@ scripts\clean.bat ### CI build (GitHub Actions) -The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=<type>` for the build type. Git commit/branch info is not passed from CI: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the branch (as a full ref, e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate the CI as: +The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=RELEASE`. Whether that build is a pre-release is decided by `PRERELEASE_SUFFIX` in `src/Phobos.version.h`, not by the workflow. Git commit/branch info is not passed from CI: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the branch (as a full ref, e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate the CI as: ``` msbuild /m /p:Configuration=Release /p:BuildType=NIGHTLY Phobos.sln ``` diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12b1f1e072..5f1c4e0f06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,14 +27,14 @@ jobs: $tag = "${{ github.ref_name }}" $version = $tag.TrimStart('v') $isPrerelease = "${{ github.event.release.prerelease }}" + # Whether the build is a pre-release is decided by Phobos.version.h's PRERELEASE_SUFFIX; + # here we only build a release-family build and keep the display name distinct. if ($isPrerelease -eq "true") { - $buildType = "PRERELEASE" $releaseName = "Phobos $tag (Pre-release)" } else { - $buildType = "RELEASE" $releaseName = "Phobos $tag" } - echo "build-type=$buildType" >> $env:GITHUB_OUTPUT + echo "build-type=RELEASE" >> $env:GITHUB_OUTPUT echo "version=$version" >> $env:GITHUB_OUTPUT echo "release-name=$releaseName" >> $env:GITHUB_OUTPUT diff --git a/Phobos.props b/Phobos.props index 468a19c697..39094708ea 100644 --- a/Phobos.props +++ b/Phobos.props @@ -147,14 +147,6 @@ <PreprocessorDefinitions>NIGHTLY;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ResourceCompile> </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(BuildType)' == 'PRERELEASE'"> - <ClCompile> - <PreprocessorDefinitions>PRERELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>PRERELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ResourceCompile> - </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(BuildType)' == 'RELEASE'"> <ClCompile> <PreprocessorDefinitions>RELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions> diff --git a/scripts/build.bat b/scripts/build.bat index e4aa7e5de9..cc6a27fad0 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -1,7 +1,8 @@ @if not defined _echo echo off rem Builds a provided build config, optionally with a provided build type -rem (NIGHTLY, PRERELEASE or RELEASE - none means a plain local build). +rem (NIGHTLY or RELEASE - none means a plain local build; a pre-release is +rem a RELEASE build with PRERELEASE_SUFFIX set in src/Phobos.version.h). rem Ensure we're in correct directory. cd /D "%~dp0" diff --git a/scripts/build_prerelease.bat b/scripts/build_prerelease.bat deleted file mode 100644 index 608c62aa5d..0000000000 --- a/scripts/build_prerelease.bat +++ /dev/null @@ -1,8 +0,0 @@ -@if not defined _echo echo off - -rem Builds Phobos pre-release. - -rem Ensure we're in correct directory. -cd /D "%~dp0" - -call build Release PRERELEASE diff --git a/src/Phobos.cpp b/src/Phobos.cpp index bd32c41e23..7024e308cd 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -11,7 +11,7 @@ #include "Utilities/GeneralUtils.h" #include "Utilities/Parser.h" -#ifndef RELEASE +#ifdef TESTING_BUILD bool HideWarning = false; #endif @@ -34,7 +34,7 @@ bool Phobos::Optimizations::DisableSyncLogging = false; // name and the version are taken from Phobos.version.h rather than spelled out again. #ifdef NIGHTLY const wchar_t* Phobos::VersionDescription = L"" PRODUCT_NAME " " PRODUCT_VERSION L". DO NOT SHIP IN MODS!"; -#elif !defined(RELEASE) +#elif defined(TESTING_BUILD) const wchar_t* Phobos::VersionDescription = L"" PRODUCT_NAME " " PRODUCT_VERSION L". Please test the build before shipping."; #endif @@ -58,7 +58,7 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) { Phobos::AppIconPath = ppArgs[++i]; } -#ifndef RELEASE +#ifdef TESTING_BUILD // Suppresses the "please test this build" warning drawn over the game screen. // The exact version of this very build has to be spelled out (it is printed in // the warning itself and in the release title), so that the switch can't be set @@ -313,7 +313,7 @@ DEFINE_HOOK(0x4F4583, GScreenClass_DrawText, 0x6) const int marginX = Phobos::Config::MessageDisplayInCenter ? 28 : 10; int coordY = 0; -#ifndef RELEASE +#ifdef TESTING_BUILD #ifndef NIGHTLY if (!HideWarning) #endif // !NIGHTLY diff --git a/src/Phobos.version.h b/src/Phobos.version.h index a4f3385bc6..5c9dd5a47c 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -18,9 +18,9 @@ // Indicates Phobos-related bugfixes only #define VERSION_PATCH 0 -// Identifier of the pre-release being prepared, spelled out in full rather than as a number, -// so that it can be anything semantic versioning allows - "alpha1", "beta2", "rc3". -// Only ever used by PRERELEASE builds; bump it on each pre-release. +// The current pre-release being prepared, spelled out in full rather than as a number, so that +// it can be anything semantic versioning allows - "alpha1", "beta2", "rc3". Its presence is +// what makes a release build a pre-release; comment it out it for a stable release. #define PRERELEASE_SUFFIX "beta1" #pragma endregion @@ -34,10 +34,11 @@ #pragma region Build metadata -// NIGHTLY / PRERELEASE / RELEASE come from the BuildType compiler option - used by GH Actions as -// well - and none of them being set means a local build. GIT_COMMIT / GIT_BRANCH / GIT_DIRTY are -// defined by Phobos.props whenever Git info is available (derived from the repository at build -// time; the branch is the full ref, e.g. refs/heads/develop). Pre-releases and releases embed +// NIGHTLY / RELEASE come from the BuildType compiler option - used by GH Actions as well - and +// none of them being set means a local build. A pre-release is a RELEASE build that still has +// the PRERELEASE_SUFFIX defined above. GIT_COMMIT / GIT_BRANCH / GIT_DIRTY are defined by +// Phobos.props whenever Git info is available (derived from the repository at build time; the +// branch is the full ref, e.g. refs/heads/develop). Pre-releases and releases embed // that info as auxiliary metadata (crash reports, logs, file resources) while keeping a clean // version string; nightly and local builds carry the Git info in the version itself and differ // only in the build type name. Everything below is derived from those inputs so that the product @@ -68,14 +69,18 @@ // The numeric file version is the full four-part version for every build type. #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH -#if defined(PRERELEASE) - #define BUILD_TYPE_NAME "pre-release build" - #define FILE_VERSION_STR VERSION_LONG_STR "-" PRERELEASE_SUFFIX - #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR -#elif defined(RELEASE) - #define BUILD_TYPE_NAME "release build" - #define FILE_VERSION_STR VERSION_LONG_STR - #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR +#if defined(RELEASE) + // A RELEASE build whose PRERELEASE_SUFFIX is still defined is a pre-release; without it, a + // stable release. + #ifdef PRERELEASE_SUFFIX + #define BUILD_TYPE_NAME "pre-release build" + #define FILE_VERSION_STR VERSION_LONG_STR "-" PRERELEASE_SUFFIX + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR + #else + #define BUILD_TYPE_NAME "release build" + #define FILE_VERSION_STR VERSION_LONG_STR + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR + #endif #else // Nightly (CI) and local builds are one unified development build differing only in the // build type name; the version string carries the Git commit and branch when available. @@ -96,6 +101,13 @@ #endif #endif +// A testing build carries the on-screen "please test the build before shipping" warning and the +// SpecialBuild file flags: everything that is not a stable release - local dev, nightly and +// pre-release builds. A pre-release is a RELEASE build that still has PRERELEASE_SUFFIX defined. +#if !defined(RELEASE) || defined(PRERELEASE_SUFFIX) + #define TESTING_BUILD +#endif + #define FILE_DESCRIPTION PRODUCT_NAME ", " PRODUCT_SUMMARY " (" BUILD_TYPE_NAME ")" #pragma endregion diff --git a/src/version.rc b/src/version.rc index e1f426c135..e7a9e50287 100644 --- a/src/version.rc +++ b/src/version.rc @@ -9,7 +9,7 @@ ID_VERSION VERSIONINFO FILEVERSION FILE_VERSION PRODUCTVERSION FILE_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifndef RELEASE +#ifdef TESTING_BUILD FILEFLAGS VS_FF_PRERELEASE | VS_FF_SPECIALBUILD #endif @@ -22,14 +22,14 @@ BEGIN BEGIN VALUE "ProductName", PRODUCT_NAME "\0" VALUE "CompanyName", "The Phobos Contributors\0" - VALUE "LegalCopyright", " The Phobos Contributors 2020-2026\0" + VALUE "LegalCopyright", "� The Phobos Contributors 2020-2026\0" VALUE "FileDescription", FILE_DESCRIPTION "\0" VALUE "FileVersion", FILE_VERSION_STR "\0" VALUE "ProductVersion", PRODUCT_VERSION "\0" VALUE "InternalName", PRODUCT_FILE_NAME "\0" VALUE "OriginalFilename", PRODUCT_FILE_NAME "\0" -#ifndef RELEASE +#ifdef TESTING_BUILD VALUE "SpecialBuild", "Testing version. Use at your own risk.\0" #endif END From b4e7d7e5d170bab4b9e489d0ed7bb752ed4fe761 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 18:17:54 +0300 Subject: [PATCH 23/55] docs: document release process, adjust local build naming and release notes generation behavior - Add release lifecycle and how-to-publish-a-release sections - Rename development builds to local builds - Tweak the release workflow so the changelog is appended to the release text you wrote, instead of replacing it Assisted-by: DeepSeek V4 Flash --- .github/copilot-instructions.md | 1 - .github/workflows/release.yml | 16 ++++++++++ docs/General-Info.md | 4 ++- docs/Project-guidelines-and-policies.md | 42 +++++++++++++++++++++++-- src/Phobos.version.h | 14 ++++----- 5 files changed, 65 insertions(+), 12 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index dba989b921..f5119dcf1a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -24,7 +24,6 @@ git submodule update --init --recursive |--------|---------|--------| | Debug (recommended for dev) | `scripts\build_debug.bat` | `Debug\Phobos.dll` + `.pdb` | | Release | `scripts\build_release.bat` | `Release\Phobos.dll` + `.pdb` | -| Release, stamped as a pre-release | `scripts\build_prerelease.bat` | `Release\Phobos.dll` + `.pdb` | There are only two build configurations, `Debug` and `Release`. What kind of build is produced (nightly or stable release) is a separate axis, set by the `BuildType` MSBuild property (`NIGHTLY` or `RELEASE`), which defines the preprocessor macro of the same name; an unset `BuildType` means a plain local build. A pre-release is a `RELEASE` build whose `PRERELEASE_SUFFIX` (hardcoded in `src/Phobos.version.h`) is still defined; remove the suffix there to build a stable release. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f1c4e0f06..abef787d0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,10 +54,26 @@ jobs: -OutputPath RELEASE_NOTES.md ` -LinkBase "https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/docs" + - name: Append Changelog to the Release Notes + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + $tag = "${{ github.ref_name }}" + # Fetch the text the release author wrote, so the changelog can be appended to it + # instead of replacing it. + $existing = (gh api "repos/${{ github.repository }}/releases/tags/$tag" --jq '.body // ""' 2>$null) -join "`n" + if ($LASTEXITCODE -ne 0) { $existing = '' } + $changelog = (Get-Content -Raw -Path RELEASE_NOTES.md).TrimStart("`r", "`n") + $existing = $existing.TrimEnd("`r", "`n") + $combined = if ([string]::IsNullOrWhiteSpace($existing)) { $changelog } else { "$existing`n`n$changelog" } + Set-Content -Path RELEASE_NOTES.md -Value $combined -Encoding utf8 + - name: Upload Release Assets uses: softprops/action-gh-release@v2 with: name: ${{ steps.info.outputs.release-name }} + update_release_body: true files: | ${{ env.BUILD_CONFIGURATION }}/Phobos.dll ${{ env.BUILD_CONFIGURATION }}/Phobos.pdb diff --git a/docs/General-Info.md b/docs/General-Info.md index 879a31cae4..fefd958b85 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -9,6 +9,8 @@ There are three main types of Phobos builds: - *pre-release builds* - previously known as *development builds*, these builds mark the start of a new release branch and are used for testing new features before they are finalized. They are numbered with a version and a pre-release suffix (e.g., 0.5-beta1, 0.5-rc2). Mod authors can include these versions with their mods to access the latest features, but we cannot guarantee the absence of bugs; - *nightly builds* - bleeding edge versions which can include prototypes, proofs of concepts, scrapped features etc., in other words - we can't guarantee anything in those builds and they absolutely should NOT be used in mod releases and should only be used to help with development and testing. +Besides the published builds above, any build you make locally (from Visual Studio, VS Code or the build scripts) is marked as a *local build*: like nightly, it is stamped with the git commit and branch it was built from (plus a `-dirty` marker when the working tree has uncommitted changes) and shows a hideable "please test" warning. + ```{hint} You can find the downloads for these versions on the document's [main page](index.md#downloads). ``` @@ -17,7 +19,7 @@ You can find the downloads for these versions on the document's [main page](inde **DISCLAIMER:** We understand that everyone wants to try and use the new features as soon as they're released, but we can't do all the testing ourselves, so we only test the functionality on a basic level. We ask everyone who uses the new pre-release build first to **test the new changes in every possible way first before disabling the pre-release build warning** and proceeding to include the build in your mod release. This would allow us to concentrate on implementing the actual features, which is the most complex task. Learn more on testing [here](Contributing.md#testing). -You can hide the warning by specifying the exact version of the build you use after `-HideVersionWarning=` as a command line argument (for example, `-HideVersionWarning=0.5-beta1` would hide the warning for the `v0.5-beta1` pre-release of Phobos). The version is shown in the warning itself and in the title of the respective release; a build only accepts its own version, so the switch has to be updated whenever you update Phobos. Nightly builds don't support hiding the warning at all. +You can hide the warning by specifying the exact version of the build you use after `-HideVersionWarning=` as a command line argument (for example, `-HideVersionWarning=0.5.0.0-beta1` would hide the warning for the `v0.5.0.0-beta1` pre-release of Phobos). The version is shown in the warning itself and in the title of the respective release; a build only accepts its own version, so the switch has to be updated whenever you update Phobos. Nightly builds don't support hiding the warning at all. ## Saved games filtering diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index 599757399e..fd4ad9e575 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -232,19 +232,55 @@ The styleguide is not exhaustive and may be adjusted in the future. ## Git branching model / Version lifecycle and release strategy -Starting from version 0.5, Phobos adopts a new release strategy to enable faster and more frequent releases. The lifecycle of a version is as follows: +Starting from version 0.5, Phobos adopts a new release strategy to enable faster and more frequent releases. + +```{hint} +A brief summary compared to old style: +- devbuilds are now called pre-releases (alpha, beta, RC etc.) and are almost a proper version with docs, all changes tracked in a special changelog subsection, released on the same cadence; +- each pre-release (new devbuild) materializes a version branch, bugfix followups committed to develop get ported to it; +- when enough bugs are fixed - a stable version is created; +- if some critical change that warrants a version bump needs to be applied (e.g. forgot to serialize a field, or fixed a critical bug from a feature released before upcoming version) - we introduce a new version (with docs, version change etc.) on the same branch; +- the new stable release is not the old stable: after 0.5 a stable version is "a devbuild with enough bug fixes", released on a faster cadence with less time spent per version. +``` + +The lifecycle of a version is as follows: 1. **Development phase**: New features and changes are committed to the `develop` branch. `develop` always carries the version it is working towards: as soon as a release branch is cut, `VERSION_MINOR` (or `VERSION_MAJOR`) in `src/Phobos.version.h` is bumped and `VERSION_REVISION`/`VERSION_PATCH` are reset to 0, so that nightlies are stamped with the version they lead up to instead of one that has already been released. 2. **Pre-release phase**: When enough features have accumulated on `develop`, a pre-release build (e.g., `v0.5-beta1`) is created. This build marks the start of a new *release branch* (e.g., `release/v0.5`) and signifies that active feature development for version 0.5 is complete. This branch will be used for all subsequent testing and the final stable release. - - During this phase, multiple pre-release builds (which can be called beta, alpha, or release candidate) may be published for wider testing. Between pre-releases on the same branch, there shall be no changes that warrant a stable version changelog addition; in other words — only bug fixes, minor additions, and polish to the existing feature set are allowed. + - During this phase, multiple pre-release builds (which can be called beta, alpha, or release candidate) may be published for wider testing. Between pre-releases on the same version number, there shall be no changes that warrant a stable version changelog addition; in other words — only bug fixes, minor additions, and polish to the existing new version feature set are allowed. + - If there's an urgent need to introduce a feature that would warrant new changelog addition on the same branch - it is allowed to **reset the pre-release prefix and increment the appropriate version number**, while also creating the corresponding doc changelog section. 3. **Stable release**: When the pre-release builds are deemed stable enough, a stable release (e.g., `v0.5`) is published from the release's branch. 4. **Maintenance phase**: After the stable release, the release branch enters maintenance mode, where only bug fixes are applied, resulting in patch releases (e.g., `v0.5.0.1`, `v0.5.0.2`). -5. **End of maintenance**: When a new stable release is published (e.g., `v0.6`), the previous minor version branch (e.g., `v0.5.0.x`) is officially deprecated and enters end-of-life, ceasing to receive any further updates, including bug fixes. Concurrently, the new stable release (e.g., `v0.6`) enters its own maintenance phase, and a new release branch for the next version (e.g., `release/v0.7`) may already have been created from the `develop` branch, initiating its pre-release cycle. +5. **End of maintenance**: When a new stable release is published (e.g., `v0.6`), the previous minor version branch (e.g., `v0.5.x.y`) is officially deprecated and enters end-of-life, ceasing to receive any further updates, including bug fixes. Concurrently, the new stable release (e.g., `v0.6`) enters its own maintenance phase, and a new release branch for the next version (e.g., `release/v0.7`) may already have been created from the `develop` branch, initiating its pre-release cycle. + +```{hint} +If needed, a new release branch may be started even before the previous one has had a stable release. Doing so will temporarily increase the burden of upkeeping multiple branches, so do it only when there's a valid reason for such. +``` ```{important} The `master` branch is deprecated; all development occurs in `develop`, and each version branches off from it. + +**`develop` is the source of truth! Always apply your changes to `develop` first, then cherry-pick them onto the correct branch!** ``` +### How to publish a release + +Publishing a release is done from a release branch (see the lifecycle above). The steps are: + +1. **Set the version** in `src/Phobos.version.h`. When a release branch is cut, bump `VERSION_MINOR` (or `VERSION_MAJOR`) and reset `VERSION_REVISION` and `VERSION_PATCH` to 0; patch releases only bump `VERSION_PATCH`. +2. **Decide whether it is a pre-release or a stable release.** The pre-release suffix is the knob: as long as `PRERELEASE_SUFFIX` is defined (e.g. `#define PRERELEASE_SUFFIX "beta1"`), a release build is a pre-release; remove the define entirely for a stable release. The suffix can be anything semantic versioning allows (e.g. `alpha5`, `beta1`, `rc3`). +3. **Create a GitHub release and tag** using the short user-facing version you've set in steps 1 and 2 (e.g. `v0.5-alpha1`). The `release.yml` workflow builds the DLL with `BuildType=RELEASE`; + - The changelog is extracted from `docs/Whats-New.md` automatically. **Do not write the changelog yourself!** It will be appended to the text you wrote after you publish the release. Also **do not use GitHub's "Generate release notes" button!** It can't be configured to provide correct output. + - The build is built and attached automatically. **Do not build Phobos releases manually!** + - **GitHub "pre-release" checkbox doesn't affect the produced build type**, it only affects the release's display status for GitHub. +4. **Verify the artifacts** once the build finishes and the artifacts are attached, then announce the release. + +The release tag and name use the short user-facing version (e.g. `v0.5-alpha1`); the DLL reports the full version with trailing zeros (e.g. `0.5.0.0-alpha1`) internally, so that is what appears in the file properties and what the `-HideVersionWarning` switch expects. + +If you want to build a pre-release locally for testing, run `scripts\build.bat Release RELEASE` with the suffix still defined in `version.h`. A plain `scripts\build_debug.bat` or `scripts\build_release.bat` always produces a local build instead. + +### Useful Git config + These commands will do the following for all repositories on your PC: 1) remove the automatic merge upon pull and replace it with a rebase; 2) highlight changes consisting of moving existing lines to another location with a different color. diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 5c9dd5a47c..885b66017b 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -19,9 +19,9 @@ #define VERSION_PATCH 0 // The current pre-release being prepared, spelled out in full rather than as a number, so that -// it can be anything semantic versioning allows - "alpha1", "beta2", "rc3". Its presence is -// what makes a release build a pre-release; comment it out it for a stable release. -#define PRERELEASE_SUFFIX "beta1" +// it can be anything semantic versioning allows - "alpha5", "beta1", "rc3", etc. Its presence is +// what makes a release build a pre-release; comment it out for a stable release. +#define PRERELEASE_SUFFIX "alpha1" #pragma endregion @@ -70,8 +70,8 @@ #define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH #if defined(RELEASE) - // A RELEASE build whose PRERELEASE_SUFFIX is still defined is a pre-release; without it, a - // stable release. + // A RELEASE build whose PRERELEASE_SUFFIX is still defined is a pre-release; + // without it, a stable release. #ifdef PRERELEASE_SUFFIX #define BUILD_TYPE_NAME "pre-release build" #define FILE_VERSION_STR VERSION_LONG_STR "-" PRERELEASE_SUFFIX @@ -82,12 +82,12 @@ #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR #endif #else - // Nightly (CI) and local builds are one unified development build differing only in the + // Nightly (CI) and local builds are one mostly unified build differing in warning and in // build type name; the version string carries the Git commit and branch when available. #ifdef NIGHTLY #define BUILD_TYPE_NAME "nightly build" #else - #define BUILD_TYPE_NAME "development build" + #define BUILD_TYPE_NAME "local build" #endif #ifdef GIT_COMMIT #define FILE_VERSION_STR VERSION_LONG_STR "+" STR_GIT_COMMIT STR_GIT_DIRTY From ab6de1512a1f7c7dc620fde91ba55746444b07b4 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 18:30:42 +0300 Subject: [PATCH 24/55] fix discrepancy in terminology (git branch -> git ref) --- .github/copilot-instructions.md | 2 +- Phobos.props | 20 ++++++++++---------- docs/General-Info.md | 2 +- src/Misc/ExceptionHandler.Report.cpp | 4 ++-- src/Phobos.cpp | 4 ++-- src/Phobos.version.h | 18 +++++++++--------- src/version.rc | 4 ++-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f5119dcf1a..8e3fa0a4d7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -43,7 +43,7 @@ scripts\clean.bat ### CI build (GitHub Actions) -The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=RELEASE`. Whether that build is a pre-release is decided by `PRERELEASE_SUFFIX` in `src/Phobos.version.h`, not by the workflow. Git commit/branch info is not passed from CI: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the branch (as a full ref, e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate the CI as: +The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=RELEASE`. Whether that build is a pre-release is decided by `PRERELEASE_SUFFIX` in `src/Phobos.version.h`, not by the workflow. Git commit/ref info is not passed from CI: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the ref (e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate the CI as: ``` msbuild /m /p:Configuration=Release /p:BuildType=NIGHTLY Phobos.sln ``` diff --git a/Phobos.props b/Phobos.props index 39094708ea..62a1c5afe8 100644 --- a/Phobos.props +++ b/Phobos.props @@ -88,8 +88,8 @@ </ResourceCompile> </ItemDefinitionGroup> <!-- - Git stamping. GitCommit / GitBranch / GitDirty are derived from the local repository at - build time (the branch as the full ref, e.g. refs/heads/develop), so local and CI builds + Git stamping. GitCommit / GitRef / GitDirty are derived from the local repository at + build time (the ref, e.g. refs/heads/develop), so local and CI builds are stamped alike with no external input. The properties can still be overridden from the command line. The defines are applied in a target rather than an ItemDefinitionGroup because the values are only known after the props are evaluated. stderr is redirected to @@ -104,8 +104,8 @@ </Exec> <Exec Command="git symbolic-ref HEAD 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" - Condition="'$(GitBranch)' == ''"> - <Output TaskParameter="ConsoleOutput" PropertyName="GitBranch" /> + Condition="'$(GitRef)' == ''"> + <Output TaskParameter="ConsoleOutput" PropertyName="GitRef" /> </Exec> <Exec Command="git status --porcelain 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" @@ -115,14 +115,14 @@ <PropertyGroup> <GitDirty Condition="'$(GitDirty)' == '' and '$(GitStatusPorcelain)' != ''">true</GitDirty> </PropertyGroup> - <Message Condition="'$(GitCommit)' == '' and '$(GitBranch)' == ''" Importance="High" - Text="Could not determine the Git commit/branch - this build will not be stamped with Git info." /> + <Message Condition="'$(GitCommit)' == '' and '$(GitRef)' == ''" Importance="High" + Text="Could not determine the Git commit/ref - this build will not be stamped with Git info." /> <ItemGroup> <ClCompile Condition="'$(GitCommit)' != ''"> <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(ClCompile.AdditionalOptions)</AdditionalOptions> </ClCompile> - <ClCompile Condition="'$(GitBranch)' != ''"> - <AdditionalOptions>/DGIT_BRANCH="$(GitBranch)" %(ClCompile.AdditionalOptions)</AdditionalOptions> + <ClCompile Condition="'$(GitRef)' != ''"> + <AdditionalOptions>/DGIT_REF="$(GitRef)" %(ClCompile.AdditionalOptions)</AdditionalOptions> </ClCompile> <ClCompile Condition="'$(GitDirty)' == 'true'"> <AdditionalOptions>/DGIT_DIRTY %(ClCompile.AdditionalOptions)</AdditionalOptions> @@ -130,8 +130,8 @@ <ResourceCompile Condition="'$(GitCommit)' != ''"> <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> </ResourceCompile> - <ResourceCompile Condition="'$(GitBranch)' != ''"> - <AdditionalOptions>/DGIT_BRANCH="$(GitBranch)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> + <ResourceCompile Condition="'$(GitRef)' != ''"> + <AdditionalOptions>/DGIT_REF="$(GitRef)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> </ResourceCompile> <ResourceCompile Condition="'$(GitDirty)' == 'true'"> <AdditionalOptions>/DGIT_DIRTY %(ResourceCompile.AdditionalOptions)</AdditionalOptions> diff --git a/docs/General-Info.md b/docs/General-Info.md index fefd958b85..0735c569d8 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -9,7 +9,7 @@ There are three main types of Phobos builds: - *pre-release builds* - previously known as *development builds*, these builds mark the start of a new release branch and are used for testing new features before they are finalized. They are numbered with a version and a pre-release suffix (e.g., 0.5-beta1, 0.5-rc2). Mod authors can include these versions with their mods to access the latest features, but we cannot guarantee the absence of bugs; - *nightly builds* - bleeding edge versions which can include prototypes, proofs of concepts, scrapped features etc., in other words - we can't guarantee anything in those builds and they absolutely should NOT be used in mod releases and should only be used to help with development and testing. -Besides the published builds above, any build you make locally (from Visual Studio, VS Code or the build scripts) is marked as a *local build*: like nightly, it is stamped with the git commit and branch it was built from (plus a `-dirty` marker when the working tree has uncommitted changes) and shows a hideable "please test" warning. +Besides the published builds above, any build you make locally (from Visual Studio, VS Code or the build scripts) is marked as a *local build*: like nightly, it is stamped with the git commit and ref it was built from (plus a `-dirty` marker when the working tree has uncommitted changes) and shows a hideable "please test" warning. ```{hint} You can find the downloads for these versions on the document's [main page](index.md#downloads). diff --git a/src/Misc/ExceptionHandler.Report.cpp b/src/Misc/ExceptionHandler.Report.cpp index 8a4168908e..842e3002fd 100644 --- a/src/Misc/ExceptionHandler.Report.cpp +++ b/src/Misc/ExceptionHandler.Report.cpp @@ -470,8 +470,8 @@ namespace #ifdef STR_GIT_COMMIT ExceptionHandler::Append("Git commit: " STR_GIT_COMMIT "\r\n"); #endif -#ifdef STR_GIT_BRANCH - ExceptionHandler::Append("Git branch: " STR_GIT_BRANCH "\r\n"); +#ifdef STR_GIT_REF + ExceptionHandler::Append("Git ref: " STR_GIT_REF "\r\n"); #endif ExceptionHandler::Append("Phobos base address: 0x%08X\r\n", AresHelper::PhobosBaseAddress); diff --git a/src/Phobos.cpp b/src/Phobos.cpp index 7024e308cd..83f38e0d67 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -138,8 +138,8 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) #ifdef STR_GIT_COMMIT Debug::Log("Git commit: " STR_GIT_COMMIT "\n"); #endif -#ifdef STR_GIT_BRANCH - Debug::Log("Git branch: " STR_GIT_BRANCH "\n"); +#ifdef STR_GIT_REF + Debug::Log("Git ref: " STR_GIT_REF "\n"); #endif Debug::Log("ExceptionHandler is %s\n", dontSetExceptionHandler ? "not present" : "present"); } diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 885b66017b..cf1c373d63 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -36,9 +36,9 @@ // NIGHTLY / RELEASE come from the BuildType compiler option - used by GH Actions as well - and // none of them being set means a local build. A pre-release is a RELEASE build that still has -// the PRERELEASE_SUFFIX defined above. GIT_COMMIT / GIT_BRANCH / GIT_DIRTY are defined by -// Phobos.props whenever Git info is available (derived from the repository at build time; the -// branch is the full ref, e.g. refs/heads/develop). Pre-releases and releases embed +// the PRERELEASE_SUFFIX defined above. GIT_COMMIT / GIT_REF / GIT_DIRTY are defined by +// Phobos.props whenever Git info is available (derived from the repository at build time as +// the full ref, e.g. refs/heads/develop). Pre-releases and releases embed // that info as auxiliary metadata (crash reports, logs, file resources) while keeping a clean // version string; nightly and local builds carry the Git info in the version itself and differ // only in the build type name. Everything below is derived from those inputs so that the product @@ -50,15 +50,15 @@ #define PRODUCT_SUMMARY "Ares-compatible YR engine extension" // The "v" prefix is a human-facing convention for version numbers and is only ever applied in -// front of a numeric version - never in front of a branch or commit. It is applied exactly once +// front of a numeric version - never in front of a ref or commit. It is applied exactly once // here in PRODUCT_VERSION and no other macro in this header prepends it. #define VERSION_PREFIX "v" #ifdef GIT_COMMIT #define STR_GIT_COMMIT _STR(GIT_COMMIT) #endif -#ifdef GIT_BRANCH - #define STR_GIT_BRANCH _STR(GIT_BRANCH) +#ifdef GIT_REF + #define STR_GIT_REF _STR(GIT_REF) #endif #ifdef GIT_DIRTY #define STR_GIT_DIRTY "-dirty" @@ -83,7 +83,7 @@ #endif #else // Nightly (CI) and local builds are one mostly unified build differing in warning and in - // build type name; the version string carries the Git commit and branch when available. + // build type name; the version string carries the Git commit and ref when available. #ifdef NIGHTLY #define BUILD_TYPE_NAME "nightly build" #else @@ -94,8 +94,8 @@ #else #define FILE_VERSION_STR VERSION_LONG_STR #endif - #ifdef GIT_BRANCH - #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR " @ " STR_GIT_BRANCH + #ifdef GIT_REF + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR " @ " STR_GIT_REF #else #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR #endif diff --git a/src/version.rc b/src/version.rc index e7a9e50287..efab59fab9 100644 --- a/src/version.rc +++ b/src/version.rc @@ -41,8 +41,8 @@ BEGIN #ifdef STR_GIT_COMMIT VALUE "GitCommit", STR_GIT_COMMIT #endif -#ifdef STR_GIT_BRANCH - VALUE "GitBranch", STR_GIT_BRANCH +#ifdef STR_GIT_REF + VALUE "GitRef", STR_GIT_REF #endif END END From 1a6dce699f14a06875cb50411540464413a04910 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 20:16:31 +0300 Subject: [PATCH 25/55] ci: rework PR doc-checker skip labels Replace the Minor / No Documentation Needed labels with three per-check Skip labels (Skip Changelog, Skip Docs, Skip Credits), drop the auto-labeler and the Interop gate, and document the requirements per change type in the contributing guide and PR template. Assisted-by: DeepSeek V4 Flash --- .github/copilot-instructions.md | 8 +++----- .github/labeler.yml | 26 -------------------------- .github/pull_request_template.md | 15 +++++++++++++-- .github/workflows/labeler-action.yml | 16 ---------------- .github/workflows/pr-doc-checker.yml | 20 +++++++++----------- docs/Contributing.md | 17 ++++++++++++++++- 6 files changed, 41 insertions(+), 61 deletions(-) delete mode 100644 .github/labeler.yml delete mode 100644 .github/workflows/labeler-action.yml diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8e3fa0a4d7..c60bc61fea 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -53,9 +53,7 @@ msbuild /m /p:Configuration=Release /p:BuildType=NIGHTLY Phobos.sln There is **no automated test suite**. Validation is: 1. **Successful compilation** with zero errors (warning level 4, but not treated as errors). 2. **PR CI checks** - the `Pull Request Nightly Build` workflow must pass (builds the Release config as a nightly). -3. **PR doc checker** (`.github/workflows/pr-doc-checker.yml`) - unless the PR has the `No Documentation Needed` label, these files must be modified: - - `docs/Whats-New.md` (changelog entry) - - `CREDITS.md` (credit entry; skipped if the `Bugfix` label is set) +3. **PR doc checker** (`.github/workflows/pr-doc-checker.yml`) - its three checks (changelog, credits, docs) each pass when the corresponding file is modified, and are skipped individually when the matching label is applied (`Skip Changelog`, `Skip Docs`, `Skip Credits`). Labels are applied by maintainers; see the table in `docs/Contributing.md` for what each kind of change is expected to cover. Always verify your changes compile by running `scripts\build_debug.bat` before committing. @@ -402,12 +400,12 @@ Many contributors are non-native English speakers, so the existing documentation ## PR Checklist -For non-trivial changes (unless labeled `No Documentation Needed`): +For non-trivial changes: 1. Update `docs/Whats-New.md` with a changelog entry. 2. Update `CREDITS.md` with your contribution. 3. Update relevant documentation pages in `docs/`. -Use `[Minor]` in the PR title for small changes that don't need documentation updates. +If one of these doesn't apply to your change, ask a maintainer to apply the matching label (`Skip Changelog`, `Skip Docs` or `Skip Credits`) so the corresponding check is skipped. See the table in `docs/Contributing.md` for what each kind of change is expected to cover. ## Trust These Instructions diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index 3d1afafe8e..0000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,26 +0,0 @@ -# https://github.com/marketplace/actions/auto-labeler -# labeler "full" schema - -# enable labeler on issues, prs, or both. -enable: - issues: false - prs: true -# comments object allows you to specify a different message for issues and prs - -# Labels is an object where: -# - keys are labels -# - values are objects of { include: [ pattern ], exclude: [ pattern ] } -# - pattern must be a valid regex, and is applied globally to -# title + description of issues and/or prs (see enabled config above) -# - 'include' patterns will associate a label if any of these patterns match -# - 'exclude' patterns will ignore this label if any of these patterns match -labels: - 'Minor': - include: - - '(?i)\[minor\]' - exclude: [] - - 'No Documentation Needed': - include: - - '(?i)\[no documentation needed\]' - exclude: [] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fe85615ca1..3c2d1718ec 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,2 +1,13 @@ -<!-- Please manually add a label named [No Documentation Needed] if this change should not be mentioned in documentation, what's new and no credit needs to be given. -If the changes are solely made to the documentation, please set the target branch to the pull request named "Weekly Regular Documentation Revisions", rather than the repository's existing branches. --> +<!-- +If the changes are solely made to the documentation, please set the target branch to the pull request named "Weekly Regular Documentation Revisions", rather than the repository's existing branches. +--> + +## What kind of change is this? + +Tick the row that matches your change. It tells the maintainers which of the changelog, docs and credits checks should run, and which `Skip` labels to apply for the ones that don't. + +- [ ] **New feature, vanilla bugfix or enhancement of a released feature** - changelog, docs and credits entries are needed. +- [ ] **Improvement to a new (unreleased) feature** - docs and credits entries are needed; no changelog entry (`Skip Changelog`). +- [ ] **Bugfix to a new (unreleased) feature** - credits entry is needed; no changelog or docs entries (`Skip Changelog`, `Skip Docs`). +- [ ] **Bugfix to an old (released) feature** - changelog and credits entries are needed; no docs entry (`Skip Docs`). +- [ ] **Completely minor change (e.g. a typo fix)** - no entries are needed (`Skip Changelog`, `Skip Docs`, `Skip Credits`). diff --git a/.github/workflows/labeler-action.yml b/.github/workflows/labeler-action.yml deleted file mode 100644 index 2448ff159c..0000000000 --- a/.github/workflows/labeler-action.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Labeler Action -on: - pull_request_target: - types: [opened, edited] - -jobs: - - labeler: - runs-on: ubuntu-latest - - steps: - - name: Label - id: labeler - uses: jimschubert/labeler-action@v2 - with: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/pr-doc-checker.yml b/.github/workflows/pr-doc-checker.yml index 1795c07167..b452e67d58 100644 --- a/.github/workflows/pr-doc-checker.yml +++ b/.github/workflows/pr-doc-checker.yml @@ -14,8 +14,8 @@ env: jobs: Changelog-Check: name: Changelog Mention - # If the No Documentation Needed label is set, then workflow will not be executed - if: ${{ !contains(github.event.pull_request.labels.*.name, 'No Documentation Needed') }} + # If the Skip Changelog label is set, then workflow will not be executed + if: ${{ !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') }} runs-on: ubuntu-latest steps: @@ -31,14 +31,14 @@ jobs: exit 0 else echo "It looks like you forgot to update the Changelog! 🧐" - echo "Please, mention your changes in 'docs/Whats-New.md' or use [No Documentation Needed] label for your Pull Request." + echo "Please, mention your changes in 'docs/Whats-New.md' or ask a maintainer to apply the Skip Changelog label for your Pull Request." exit 1 fi Credits-Check: name: Credits List Mention - # If the No Documentation Needed or Bugfix label is set, then workflow will not be executed - if: ${{ !contains(github.event.pull_request.labels.*.name, 'No Documentation Needed') && !contains(github.event.pull_request.labels.*.name, 'Bugfix') }} + # If the Skip Credits label is set, then workflow will not be executed + if: ${{ !contains(github.event.pull_request.labels.*.name, 'Skip Credits') }} runs-on: ubuntu-latest steps: @@ -54,14 +54,14 @@ jobs: exit 0 else echo "It looks like you forgot to update the Credits List! 🧐" - echo "Please, mention your contribution in 'CREDITS.md' or use [No Documentation Needed] label for your Pull Request." + echo "Please, mention your contribution in 'CREDITS.md' or ask a maintainer to apply the Skip Credits label for your Pull Request." exit 1 fi Documentation-Check: name: Documentation for Changes - # If the No Documentation Needed or Bugfix label is set, then workflow will not be executed - if: ${{ !contains(github.event.pull_request.labels.*.name, 'No Documentation Needed') && !contains(github.event.pull_request.labels.*.name, 'Bugfix') }} + # If the Skip Docs label is set, then workflow will not be executed + if: ${{ !contains(github.event.pull_request.labels.*.name, 'Skip Docs') }} runs-on: ubuntu-latest steps: @@ -85,14 +85,12 @@ jobs: exit 0 else echo "It looks like you forgot to add your changes to the docs! 🧐" - echo "Please, document your changes or use [No Documentation Needed] label for your Pull Request." + echo "Please, document your changes or ask a maintainer to apply the Skip Docs label for your Pull Request." exit 1 fi Interop-Version-Check: name: Interop API Version Updated - # If the No Documentation Needed label is set, then workflow will not be executed - if: ${{ !contains(github.event.pull_request.labels.*.name, 'No Documentation Needed') }} runs-on: ubuntu-latest steps: diff --git a/docs/Contributing.md b/docs/Contributing.md index 9fc7527616..8db6b46dd6 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -41,7 +41,22 @@ If you contribute something, please make sure: - you mention the change in the changelog and migration sections in the [what's new page](Whats-New.md); - you mention your contribution in the [credits page](CREDITS.md). -If your change does not fit in standard criteria or too small that it doesn't need the above - add `[Minor]` to your pull request's title, so the CI won't yell at you for no reason. +Not every change needs all of the above. The table below shows what each kind of change is expected to cover (a checkmark means that kind of entry is required): + +| Change type | Changelog | Docs | Credits | +|---|---|---|---| +| New feature, vanilla bugfix, enhancement of a released feature | ✓ | ✓ | ✓ | +| Improvement to a new (unreleased) feature | ✗ | ✓ | ✓ | +| Bugfix to a new (unreleased) feature | ✗ | ✗ | ✓ | +| Bugfix to an old (released) feature | ✓ | ✗ | ✓ | +| Completely minor change (e.g. a typo fix) | ✗ | ✗ | ✗ | + +```{warning} +If editing this table - also edit the PR template. +``` + +The checks for changelog, docs and credits are skipped individually when a maintainer applies the matching label - `Skip Changelog`, `Skip Docs` or `Skip Credits`. If one of the requirements does not apply to your change, say so in the pull request and ask for the corresponding label. + ```{hint} Every pull request push trigger a nightly build for the latest pushed commit, so you can check the build status at the bottom of PR page, press `Show all checks`, go to details of a build run and get the zip containing built DLL and PDB (for your testers, f. ex.), or download a build from an automatically posted comment. From 14817e4495f1872f41e7f91414d1ba9474ae0122 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 21:09:23 +0300 Subject: [PATCH 26/55] build: recover the git ref on detached HEAD checkouts git symbolic-ref fails on a detached HEAD (CI pull request merge and tag checkouts), so fall back to the single ref pointing at HEAD via for-each-ref. PR nightlies and release builds stay stamped with their ref without needing it passed in from the workflows. Assisted-by: DeepSeek V4 Flash --- Phobos.props | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Phobos.props b/Phobos.props index 62a1c5afe8..fcdee8df50 100644 --- a/Phobos.props +++ b/Phobos.props @@ -107,6 +107,13 @@ Condition="'$(GitRef)' == ''"> <Output TaskParameter="ConsoleOutput" PropertyName="GitRef" /> </Exec> + <!-- On a detached HEAD (e.g. CI checkouts of a pull request merge or a tag), symbolic-ref + yields nothing, so fall back to the one ref pointing at HEAD instead. --> + <Exec Command="git for-each-ref --points-at HEAD --format=%25(refname) 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" + ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" + Condition="'$(GitRef)' == ''"> + <Output TaskParameter="ConsoleOutput" PropertyName="GitRef" /> + </Exec> <Exec Command="git status --porcelain 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" ConsoleToMsBuild="true" IgnoreExitCode="true" StandardOutputImportance="Low" Condition="'$(GitDirty)' == '' and '$(GitCommit)' != ''"> From bf9d92b1fc3225657c7486d956d793defee30ad7 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 22:25:53 +0300 Subject: [PATCH 27/55] fix: restore the copyright symbol encoding in version.rc The copyright symbol was corrupted to U+FFFD when the file was re-saved as UTF-8 without a BOM. Restore it and encode the file as UTF-8 with a BOM so rc.exe decodes it correctly (it assumes ANSI without one); document the exception in .editorconfig. Assisted-by: DeepSeek V4 Flash --- .editorconfig | 4 ++++ src/version.rc | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 21516d51c5..8719cf43c5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,10 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true +[*.rc] +# rc.exe assumes the system ANSI codepage without a BOM, which would mangle non-ASCII strings like the copyright symbol. +charset = utf-8-bom + [.gitattributes] charset = utf-8 end_of_line = unset diff --git a/src/version.rc b/src/version.rc index efab59fab9..166256e7b9 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,4 +1,4 @@ -#include <windows.h> +#include <windows.h> #include "Phobos.version.h" @@ -22,7 +22,7 @@ BEGIN BEGIN VALUE "ProductName", PRODUCT_NAME "\0" VALUE "CompanyName", "The Phobos Contributors\0" - VALUE "LegalCopyright", "� The Phobos Contributors 2020-2026\0" + VALUE "LegalCopyright", "© The Phobos Contributors 2020-2026\0" VALUE "FileDescription", FILE_DESCRIPTION "\0" VALUE "FileVersion", FILE_VERSION_STR "\0" VALUE "ProductVersion", PRODUCT_VERSION "\0" From 5d8be21084eaf5bc845ff2c9e15587f4fe88a621 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 22:25:53 +0300 Subject: [PATCH 28/55] ci: make the release changelog append idempotent Re-running the release workflow appended a second copy of the changelog. Wrap it in a marker block that is stripped before re-appending, and drop the non-existent update_release_body input. Assisted-by: DeepSeek V4 Flash --- .github/workflows/release.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index abef787d0c..64d7467385 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,19 +61,23 @@ jobs: run: | $tag = "${{ github.ref_name }}" # Fetch the text the release author wrote, so the changelog can be appended to it - # instead of replacing it. + # instead of replacing it. Re-running the workflow must not append a second copy, + # so the changelog is wrapped in a marker block that is stripped before re-appending. $existing = (gh api "repos/${{ github.repository }}/releases/tags/$tag" --jq '.body // ""' 2>$null) -join "`n" if ($LASTEXITCODE -ne 0) { $existing = '' } - $changelog = (Get-Content -Raw -Path RELEASE_NOTES.md).TrimStart("`r", "`n") - $existing = $existing.TrimEnd("`r", "`n") - $combined = if ([string]::IsNullOrWhiteSpace($existing)) { $changelog } else { "$existing`n`n$changelog" } + $changelog = (Get-Content -Raw -Path RELEASE_NOTES.md).Trim() + $markerStart = '<!-- Phobos changelog start -->' + $markerEnd = '<!-- Phobos changelog end -->' + $block = "$markerStart`n$changelog`n$markerEnd" + # Drop any previously appended changelog block so re-runs replace rather than duplicate + $existing = [regex]::Replace($existing, "(?s)$markerStart.*$markerEnd", '').TrimEnd("`r", "`n") + $combined = if ([string]::IsNullOrWhiteSpace($existing)) { $block } else { "$existing`n`n$block" } Set-Content -Path RELEASE_NOTES.md -Value $combined -Encoding utf8 - name: Upload Release Assets uses: softprops/action-gh-release@v2 with: name: ${{ steps.info.outputs.release-name }} - update_release_body: true files: | ${{ env.BUILD_CONFIGURATION }}/Phobos.dll ${{ env.BUILD_CONFIGURATION }}/Phobos.pdb From d43c814074dafa35a6ae10efbca4fbdda170c473 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sun, 2 Aug 2026 22:25:54 +0300 Subject: [PATCH 29/55] docs: fix version naming and stale savegame note General-Info referenced a 'v0.5.0.0-beta1' release that doesn't exist (releases use the short form) and claimed nightlies use a development build number that no longer applies (they share the version's savegame ID). Clarify the release vs nightly build type in copilot-instructions. Assisted-by: DeepSeek V4 Flash --- .github/copilot-instructions.md | 2 +- docs/General-Info.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c60bc61fea..8e1fe0506b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -43,7 +43,7 @@ scripts\clean.bat ### CI build (GitHub Actions) -The CI workflow (`.github/actions/build-phobos/action.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=RELEASE`. Whether that build is a pre-release is decided by `PRERELEASE_SUFFIX` in `src/Phobos.version.h`, not by the workflow. Git commit/ref info is not passed from CI: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the ref (e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate the CI as: +The release workflow (`.github/workflows/release.yml`) builds the **Release** config with MSBuild, passing `/p:BuildType=RELEASE`; whether that build is a pre-release is decided by `PRERELEASE_SUFFIX` in `src/Phobos.version.h`, not by the workflow. Nightly builds (`.github/workflows/nightly.yml` and the PR nightly) pass `/p:BuildType=NIGHTLY`. Git commit/ref info is not passed from CI in either case: `Phobos.props` runs a `ComputeGitInfo` target that derives the short commit SHA, the ref (e.g. `refs/heads/develop`) and a dirty marker straight from the repository, so local builds are stamped identically. The agent should replicate a nightly CI build as: ``` msbuild /m /p:Configuration=Release /p:BuildType=NIGHTLY Phobos.sln ``` diff --git a/docs/General-Info.md b/docs/General-Info.md index 0735c569d8..e70dfe46c9 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -19,11 +19,11 @@ You can find the downloads for these versions on the document's [main page](inde **DISCLAIMER:** We understand that everyone wants to try and use the new features as soon as they're released, but we can't do all the testing ourselves, so we only test the functionality on a basic level. We ask everyone who uses the new pre-release build first to **test the new changes in every possible way first before disabling the pre-release build warning** and proceeding to include the build in your mod release. This would allow us to concentrate on implementing the actual features, which is the most complex task. Learn more on testing [here](Contributing.md#testing). -You can hide the warning by specifying the exact version of the build you use after `-HideVersionWarning=` as a command line argument (for example, `-HideVersionWarning=0.5.0.0-beta1` would hide the warning for the `v0.5.0.0-beta1` pre-release of Phobos). The version is shown in the warning itself and in the title of the respective release; a build only accepts its own version, so the switch has to be updated whenever you update Phobos. Nightly builds don't support hiding the warning at all. +You can hide the warning by specifying the exact version of the build you use after `-HideVersionWarning=` as a command line argument (for example, `-HideVersionWarning=0.5.0.0-beta1` would hide the warning for the `v0.5-beta1` pre-release of Phobos). The version is shown in the warning itself and in the title of the respective release; a build only accepts its own version, so the switch has to be updated whenever you update Phobos. Nightly builds don't support hiding the warning at all. ## Saved games filtering -Phobos fully supports saving and loading thanks to prototype code from publicly released Ares 0.A source and it implements it's own filtering which shouldn't conflict with Ares save filtering. Save games between different versions are incompatible due to changes to Phobos extension classes which are present in almost every build release. The filtering mechanism, hovewer, doesn't apply to nightly versions - those use latest development build number on which this nightly is based on. While different nightly version saves may be listed, they are most likely incompatible in case there were changes to extension class fields. +Phobos fully supports saving and loading thanks to prototype code from publicly released Ares 0.A source and it implements it's own filtering which shouldn't conflict with Ares save filtering. Save games between different versions are incompatible due to changes to Phobos extension classes which are present in almost every build release. Nightlies and pre-releases of a version share the savegame ID of the stable release they lead up to, so their saves are filtered as a single version. They may still be incompatible with each other if there were changes to extension class fields. ## Compatibility From ab2b51615c7e38df85a6967a523f0bf3734ac80d Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 00:23:03 +0300 Subject: [PATCH 30/55] add missed template changes --- .github/pull_request_template.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3c2d1718ec..969e5e7158 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,10 +4,14 @@ If the changes are solely made to the documentation, please set the target branc ## What kind of change is this? -Tick the row that matches your change. It tells the maintainers which of the changelog, docs and credits checks should run, and which `Skip` labels to apply for the ones that don't. +<!-- Tick the row that matches your change. It tells the maintainers which of the changelog, docs and credits checks should run, and which `Skip` labels to apply for the ones that don't. --> - [ ] **New feature, vanilla bugfix or enhancement of a released feature** - changelog, docs and credits entries are needed. - [ ] **Improvement to a new (unreleased) feature** - docs and credits entries are needed; no changelog entry (`Skip Changelog`). - [ ] **Bugfix to a new (unreleased) feature** - credits entry is needed; no changelog or docs entries (`Skip Changelog`, `Skip Docs`). - [ ] **Bugfix to an old (released) feature** - changelog and credits entries are needed; no docs entry (`Skip Docs`). - [ ] **Completely minor change (e.g. a typo fix)** - no entries are needed (`Skip Changelog`, `Skip Docs`, `Skip Credits`). + +## Description + +<!-- Write your description below. Usually this is your improvement documentation copy, but you may add extra notes or sections or whatever you feel is important for reviewing. --> From 8aa514c4e87a704d42d67978912760a5843930f4 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 02:33:19 +0300 Subject: [PATCH 31/55] build: generate git info into a header instead of per-TU defines Write STR_GIT_COMMIT / STR_GIT_REF / STR_GIT_DIRTY into a generated $(IntDir)Generated\Phobos.Git.h that Phobos.version.h includes, instead of passing /D defines to every translation unit. A changed commit now only rebuilds the few units that include the header (WriteOnlyWhenDifferent keeps no-op builds from churning), instead of all 223. The STR_* values are spelled out directly because rc.exe cannot stringize a file-defined string literal. Assisted-by: DeepSeek V4 Flash --- Phobos.props | 40 ++++++++++++++++------------------------ src/Phobos.version.h | 26 ++++++++++++-------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/Phobos.props b/Phobos.props index fcdee8df50..9cf1e017d8 100644 --- a/Phobos.props +++ b/Phobos.props @@ -21,10 +21,10 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <PropertyGroup> <GenerateManifest>false</GenerateManifest> - <IncludePath>$(ExpandedIncludePath);$(MSBuildThisFileDirectory)src\;$(YRppDir);$(VC_IncludePath)</IncludePath> + <IntDir>$(Configuration)\IntDir\</IntDir> + <IncludePath>$(ExpandedIncludePath);$(MSBuildThisFileDirectory)src\;$(YRppDir);$(IntDir)Generated;$(VC_IncludePath)</IncludePath> <LinkIncremental>false</LinkIncremental> <OutDir>$(Configuration)\</OutDir> - <IntDir>$(Configuration)\IntDir\</IntDir> <CoreLibraryDependencies></CoreLibraryDependencies> <PhobosExtraLibs>dbghelp.lib;onecore.lib</PhobosExtraLibs> </PropertyGroup> @@ -91,10 +91,10 @@ Git stamping. GitCommit / GitRef / GitDirty are derived from the local repository at build time (the ref, e.g. refs/heads/develop), so local and CI builds are stamped alike with no external input. The properties can still be overridden from the - command line. The defines are applied in a target rather than an ItemDefinitionGroup - because the values are only known after the props are evaluated. stderr is redirected to - nul so a missing or failing git cannot fail the build nor leak error text into the version - - it only results in an unstamped build with an informational message. + command line. The values are written into a generated header in a target rather than baked + into an ItemDefinitionGroup because they are only known after the props are evaluated. + stderr is redirected to nul so a missing or failing git cannot fail the build nor leak error + text into the version - it only results in an unstamped build with an informational message. --> <Target Name="ComputeGitInfo" BeforeTargets="ClCompile;ResourceCompile"> <Exec Command="git rev-parse --short HEAD 2>nul" WorkingDirectory="$(MSBuildProjectDirectory)" @@ -124,26 +124,18 @@ </PropertyGroup> <Message Condition="'$(GitCommit)' == '' and '$(GitRef)' == ''" Importance="High" Text="Could not determine the Git commit/ref - this build will not be stamped with Git info." /> + <!-- Write the git info into a generated header included by Phobos.version.h instead of + passing it as defines on every translation unit's command line. This keeps the git info + off the command lines, so a changed commit only rebuilds the few units that include the + header; WriteOnlyWhenDifferent keeps the header timestamp stable when nothing changed. --> <ItemGroup> - <ClCompile Condition="'$(GitCommit)' != ''"> - <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(ClCompile.AdditionalOptions)</AdditionalOptions> - </ClCompile> - <ClCompile Condition="'$(GitRef)' != ''"> - <AdditionalOptions>/DGIT_REF="$(GitRef)" %(ClCompile.AdditionalOptions)</AdditionalOptions> - </ClCompile> - <ClCompile Condition="'$(GitDirty)' == 'true'"> - <AdditionalOptions>/DGIT_DIRTY %(ClCompile.AdditionalOptions)</AdditionalOptions> - </ClCompile> - <ResourceCompile Condition="'$(GitCommit)' != ''"> - <AdditionalOptions>/DGIT_COMMIT="$(GitCommit)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> - </ResourceCompile> - <ResourceCompile Condition="'$(GitRef)' != ''"> - <AdditionalOptions>/DGIT_REF="$(GitRef)" %(ResourceCompile.AdditionalOptions)</AdditionalOptions> - </ResourceCompile> - <ResourceCompile Condition="'$(GitDirty)' == 'true'"> - <AdditionalOptions>/DGIT_DIRTY %(ResourceCompile.AdditionalOptions)</AdditionalOptions> - </ResourceCompile> + <GitHeader Include="// Generated by ComputeGitInfo - do not edit." /> + <GitHeader Include="#define STR_GIT_COMMIT "$(GitCommit)"" Condition="'$(GitCommit)' != ''" /> + <GitHeader Include="#define STR_GIT_REF "$(GitRef)"" Condition="'$(GitRef)' != ''" /> + <GitHeader Include="#define STR_GIT_DIRTY "-dirty"" Condition="'$(GitDirty)' == 'true'" /> </ItemGroup> + <MakeDir Directories="$(IntDir)Generated" /> + <WriteLinesToFile File="$(IntDir)Generated\Phobos.Git.h" Lines="@(GitHeader)" Overwrite="true" WriteOnlyWhenDifferent="true" /> </Target> <!-- BuildType --> <ItemDefinitionGroup Condition="'$(BuildType)' == 'NIGHTLY'"> diff --git a/src/Phobos.version.h b/src/Phobos.version.h index cf1c373d63..32a63432e5 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -32,13 +32,17 @@ // a version share the ID with the stable release they lead up to. #define SAVEGAME_ID ((VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | (VERSION_REVISION << 8)) +// Git info (commit / ref / dirty), generated by Phobos.props' ComputeGitInfo target into $(IntDir)Generated. +#include "Phobos.Git.h" + #pragma region Build metadata // NIGHTLY / RELEASE come from the BuildType compiler option - used by GH Actions as well - and // none of them being set means a local build. A pre-release is a RELEASE build that still has -// the PRERELEASE_SUFFIX defined above. GIT_COMMIT / GIT_REF / GIT_DIRTY are defined by -// Phobos.props whenever Git info is available (derived from the repository at build time as -// the full ref, e.g. refs/heads/develop). Pre-releases and releases embed +// the PRERELEASE_SUFFIX defined above. STR_GIT_COMMIT / STR_GIT_REF / STR_GIT_DIRTY are +// defined by the generated Phobos.Git.h whenever Git info is available (derived from the +// repository at build time as the full ref, e.g. refs/heads/develop). Pre-releases and +// releases embed // that info as auxiliary metadata (crash reports, logs, file resources) while keeping a clean // version string; nightly and local builds carry the Git info in the version itself and differ // only in the build type name. Everything below is derived from those inputs so that the product @@ -54,15 +58,9 @@ // here in PRODUCT_VERSION and no other macro in this header prepends it. #define VERSION_PREFIX "v" -#ifdef GIT_COMMIT - #define STR_GIT_COMMIT _STR(GIT_COMMIT) -#endif -#ifdef GIT_REF - #define STR_GIT_REF _STR(GIT_REF) -#endif -#ifdef GIT_DIRTY - #define STR_GIT_DIRTY "-dirty" -#else +// STR_GIT_COMMIT / STR_GIT_REF are defined by the generated Phobos.Git.h when Git info is +// available; STR_GIT_DIRTY falls back to empty when the working tree is clean. +#ifndef STR_GIT_DIRTY #define STR_GIT_DIRTY "" #endif @@ -89,12 +87,12 @@ #else #define BUILD_TYPE_NAME "local build" #endif - #ifdef GIT_COMMIT + #ifdef STR_GIT_COMMIT #define FILE_VERSION_STR VERSION_LONG_STR "+" STR_GIT_COMMIT STR_GIT_DIRTY #else #define FILE_VERSION_STR VERSION_LONG_STR #endif - #ifdef GIT_REF + #ifdef STR_GIT_REF #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR " @ " STR_GIT_REF #else #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR From e5707b0198abad5e81abf9682f1f85392ac867fc Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 02:37:19 +0300 Subject: [PATCH 32/55] docs: illustrate the version lifecycle with a branch diagram Add the versioning-scheme diagram (from 0.5 onward) to the branching model section, right after the intro, with a footnote pointing to the editable Excalidraw source. Assisted-by: DeepSeek V4 Flash --- docs/Project-guidelines-and-policies.md | 4 ++++ .../images/versioning-past-0.5.excalidraw.png | Bin 0 -> 176953 bytes 2 files changed, 4 insertions(+) create mode 100644 docs/_static/images/versioning-past-0.5.excalidraw.png diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index fd4ad9e575..cf773eaa0b 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -234,6 +234,10 @@ The styleguide is not exhaustive and may be adjusted in the future. Starting from version 0.5, Phobos adopts a new release strategy to enable faster and more frequent releases. +![Versioning scheme from 0.5 onward](_static/images/versioning-past-0.5.excalidraw.png) + +*Image editable in [Excalidraw](https://excalidraw.com)* + ```{hint} A brief summary compared to old style: - devbuilds are now called pre-releases (alpha, beta, RC etc.) and are almost a proper version with docs, all changes tracked in a special changelog subsection, released on the same cadence; diff --git a/docs/_static/images/versioning-past-0.5.excalidraw.png b/docs/_static/images/versioning-past-0.5.excalidraw.png new file mode 100644 index 0000000000000000000000000000000000000000..1439aaa0b3a365326f0fac6d70d6480c0eede36f GIT binary patch literal 176953 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 ny~eT7s-ND*|~1#v?LQ` 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 literal 0 HcmV?d00001 From 2484dcd2fc0e8894ee0743abc4e269f8d37b2899 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 02:42:24 +0300 Subject: [PATCH 33/55] missed whitespace removal --- .github/workflows/pr-doc-checker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-doc-checker.yml b/.github/workflows/pr-doc-checker.yml index b452e67d58..58ad0778d0 100644 --- a/.github/workflows/pr-doc-checker.yml +++ b/.github/workflows/pr-doc-checker.yml @@ -102,12 +102,12 @@ jobs: # Check if any files in src/Interop/ were modified (excluding Version.h itself for initial checks) CHANGED_FILES=$(git diff --name-only $(git merge-base origin/$BASE_BRANCH HEAD)) INTEROP_MODIFIED=$(echo "$CHANGED_FILES" | grep -E "^src/Interop/.*\.(h|cpp)$" | grep -v "^src/Interop/Version\." || true) - + if [ -z "$INTEROP_MODIFIED" ]; then echo "No Interop files modified. Skipping version check." exit 0 fi - + # If Interop files were modified, check if Version.h was also modified if echo "$CHANGED_FILES" | grep -q "^src/Interop/Version\.h$"; then echo "Thank you for updating the Interop API version! 😋" From 8a63b43b65ba0447ca8e93fd004518922af8672a Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 02:44:31 +0300 Subject: [PATCH 34/55] build: align IntelliSense config with the generated header and build defines Include both Debug and Release Generated folders (Phobos.Git.h lands in whichever config is built; cpptools has no predefined Debug/Release variable, so list both). Add the always-present defines from Phobos.props that were missing (_WIN32_WINNT, NTDDI_VERSION, PHOBOS_DLL); keep _WINDLL since MSBuild adds it for the DLL config. DEBUG/NIGHTLY/RELEASE are config-specific and stay out. Assisted-by: DeepSeek V4 Flash --- .vscode/c_cpp_properties.example.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.vscode/c_cpp_properties.example.json b/.vscode/c_cpp_properties.example.json index cbea9e2a8e..dcd5039162 100644 --- a/.vscode/c_cpp_properties.example.json +++ b/.vscode/c_cpp_properties.example.json @@ -20,12 +20,19 @@ "${workspaceFolder}/YRpp/**", "${workspaceFolder}/lib", "${workspaceFolder}/lib/**", + "${workspaceFolder}/Debug/IntDir/Generated", + "${workspaceFolder}/Debug/IntDir/Generated/**", + "${workspaceFolder}/Release/IntDir/Generated", + "${workspaceFolder}/Release/IntDir/Generated/**", ], "defines": [ "SYR_VER=2", "HAS_EXCEPTIONS=0", "NOMINMAX", "_CRT_SECURE_NO_WARNINGS", + "_WIN32_WINNT=0x0601", + "NTDDI_VERSION=0x06010000", + "PHOBOS_DLL=\"Phobos.dll\"", "_WINDLL" ], "compilerPath": "${vsPath}/VC/Tools/MSVC/14.29.30133/bin/HostX86/x86/cl.exe", From f3378705e937443ff7588274601ce11221369e35 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 03:12:37 +0300 Subject: [PATCH 35/55] version the vanilla migration doc --- docs/Whats-New.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 5562162d1a..1707c477c4 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -10,25 +10,33 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] ### From vanilla +#### Version TBD (develop branch nightly builds) + - Vehicles paradropped by AI players now default to `Hunt` mission instead of `Guard`, matching what infantry do. This can be customized by setting `AIParadropMission` on the VehicleType, defaults to `[General] -> AIParadropMission`. - `IsSimpleDeployer` units now obey deploying facing constraint even without deploying animation if `DeployDir` is explicitly set on the unit. - `Vertical=true` projectiles now default to completely downwards initial trajectory/facing regardless of if their projectile image has `Voxel=true` or not. This behavior can be reverted by setting `VerticalInitialFacing=false` on projectile in `rulesmd.ini`. - `Vertical=true` projectiles no longer move horizontally if fired by aircraft by default. To re-enable this behaviour set `Vertical.AircraftFix=false` on the projectile. - Weapons with `Airstrike=true` on Warhead will now check target eligibility for airstrikes regardless of weapon slot. Use `AirstrikeTargets=all` on `Primary` airstrike weapon Warhead to restore previous behaviour. +- The default direction for aircraft landing on an airfield will use the direction specified by `[AudioVisual] -> PoseDir=` instead of the building's direction, which can be reverted by setting `AircraftDockingDir.DefaultToPoseDir=false`. + +#### 0.4 + - `PowerUpNAnim` is now used instead of the upgrade building's image file for upgrade animation if set. Note that displaying a damaged version will still require setting `PowerUpNDamagedAnim` explicitly in all cases, as the fallback to upgrade building image does not extend to it, nor would it be safe to add. `PowersUpToLevel=-1` upgrades still do not work correctly `PowerUpNAnim` and such buildings should forgo using explicit upgrade animations. - Elite technos no longer scatter by default, behaviour can be restored by including `SCATTER` in their `EliteAbilities`. - `[CrateRules] -> FreeMCV` now controls whether or not player is forced to receive unit from `[General] -> BaseUnit` from goodie crate if they own no buildings or any existing `[General] -> BaseUnit` vehicles and own more than `[CrateRules] -> FreeMCV.CreditsThreshold` (defaults to 1500) credits. -- Translucent RLE SHPs will now be drawn using a more precise and performant algorithm that has no green tint and banding. Can be disabled with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`. -- Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`. - The obsolete `[General] -> WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `[General] -> WarpOut`. - Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both default to `TiltsWhenCrushes`. -- The default direction for aircraft landing on an airfield will use the direction specified by `[AudioVisual] -> PoseDir=` instead of the building's direction, which can be reverted by setting `AircraftDockingDir.DefaultToPoseDir=false`. + +#### 0.3 + +- Translucent RLE SHPs will now be drawn using a more precise and performant algorithm that has no green tint and banding. Can be disabled with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`. +- Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`. ## Breaking changes This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping, so you can simply read up on this log of breaking changes and apply whatever you might need to apply. -### Version TBD (develop branch nightly builds) +### 0.5 - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. From 69d8d0268eb3119fe69745b024198d0cb99b98ed Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 04:13:22 +0300 Subject: [PATCH 36/55] scripts: rework pre-release handling in changelog extraction A pre-release whose own heading carries notes is now released as just those notes; an empty or missing heading falls back to the base version it leads up to, instead of appending the tagged subsection and everything below it to the base sections. Also drop the now-unused Version TBD fallback and recognize MyST colon fences alongside backtick and tilde ones. Assisted-by: DeepSeek V4 Flash --- scripts/extract_changelog.ps1 | 63 ++++++++++++----------------------- 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index 878be48a30..800562095e 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -62,8 +62,10 @@ function New-Section([int]$Level, [string]$Title, $Parent) { } # Returns the fence descriptor for a line that opens or closes a fenced block, else $null. +# Backtick, tilde and MyST colon fences are recognized, of any length at or above three, which +# allows nesting directives by increasing the run length (e.g. `:::{note}` inside `:::{hint}`). function Get-Fence([string]$Line) { - if ($Line -match '^ {0,3}(?<fence>`{3,}|~{3,})[ \t]*(?<info>.*?)[ \t]*$') { + if ($Line -match '^ {0,3}(?<fence>`{3,}|~{3,}|:{3,})[ \t]*(?<info>.*?)[ \t]*$') { return [pscustomobject]@{ Char = $Matches.fence.Substring(0, 1) Len = $Matches.fence.Length @@ -180,7 +182,6 @@ function Get-AllSections($Node) { # The version a section documents, or $null when it documents no particular version. # The whole title has to be the version, so that a section like "3 new trajectories" is contents. function Get-SectionVersion([string]$Title) { - if ($Title -match '^\s*Version\s+TBD\b') { return 'TBD' } if ($Title -match '^\s*[vV]?(?<version>\d+(\.\d+)+(-[0-9A-Za-z.]+)?)(\s+\(.*\))?\s*$') { return $Matches.version } return $null } @@ -204,34 +205,27 @@ function Add-SectionContents($Node, [int]$Shift, $Sink) { } } -# A pre-release is documented as a subsection of the version it leads up to, holding what changed -# since the previous one, so those subsections are builds rather than kinds of change and are left -# out by default. A pre-release build carries the fixes of every pre-release before it as well and -# they are listed newest first, so the tagged subsection and everything below it are kept. Ones -# without contents are dropped - such a pre-release is just the parent version as it stands. -function Set-BuildSectionVisibility($Section, [string]$PreRelease) { - $builds = @($Section.Children | Where-Object { Get-SectionVersion $_.Title }) - foreach ($build in $builds) { $build.Skip = $true } - - if (-not $PreRelease) { return } - - $tagged = -1 - for ($i = 0; $i -lt $builds.Count; $i++) { - if ((Get-SectionVersion $builds[$i].Title) -eq $PreRelease) { $tagged = $i; break } - } - if ($tagged -lt 0) { return } - - for ($i = $tagged; $i -lt $builds.Count; $i++) { - if (Test-HasContents $builds[$i]) { $builds[$i].Skip = $false } +# Pre-release notes live in version-numbered subsections of the section they lead up to (e.g. +# `#### 0.5-alpha2` under `### 0.5`). A release of the base version never carries them, so those +# subsections are always left out of the base sections. A pre-release that documents its own +# changes is matched and rendered on its own instead (see the main flow), so it never reaches +# this point as a base section. +function Set-BuildSectionVisibility($Section) { + foreach ($child in $Section.Children) { + if (Get-SectionVersion $child.Title) { $child.Skip = $true } } } # The path from the top level section down to the matched one names the type of change. +# Version-numbered sections are not a kind of change: the base version a pre-release leads up to +# (e.g. the `### 0.5` holding a `#### 0.5-alpha2`) is left out of the path. function Get-SectionKind($Node) { $chain = @() $parent = $Node.Parent while ($parent -and $parent.Level -ge 2) { - $chain = , $parent.Title + $chain + if (-not (Get-SectionVersion $parent.Title)) { + $chain = , $parent.Title + $chain + } $parent = $parent.Parent } return ($chain -join ' / ') @@ -251,30 +245,17 @@ $version = $Tag -replace '^[vV]', '' $baseVersion = $version -replace '-.*$', '' $isPreRelease = $baseVersion -ne $version -$preRelease = '' -# A pre-release is released as its parent version plus whatever the pre-release subsections -# below it record, so it is the parent sections that get rendered. +# A pre-release is documented as a version-numbered subsection of the version it leads up to. +# When that subsection carries its own notes the pre-release is released as just those notes; an +# empty (or missing) heading falls back to the base version it leads up to. $matched = @() if ($isPreRelease) { - $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $baseVersion }) - if ($matched) { $preRelease = $version } + $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $version -and (Test-HasContents $_) }) } if (-not $matched) { - $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $version }) -} - -# Only a pre-release may document itself under another name: until the release branch is cut its -# sections are still called "Version TBD". A stable or patch release documenting nothing is an -# oversight, and publishing whatever else is at hand - the unreleased develop notes above all - -# would hide it. -if (-not $matched -and $isPreRelease) { - $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq 'TBD' }) - if ($matched) { - $preRelease = $version - Write-Warning "No sections for '$version', falling back to 'Version TBD' - rename them to '$baseVersion' in $WhatsNewPath" - } + $matched = @($sections | Where-Object { (Get-SectionVersion $_.Title) -eq $baseVersion }) } if (-not $matched) { @@ -289,7 +270,7 @@ foreach ($section in $matched) { $out.Add('') $out.Add("## $kind") } - Set-BuildSectionVisibility $section $preRelease + Set-BuildSectionVisibility $section # The matched section's own children start right below the type of change heading. Add-SectionContents $section (2 - $section.Level) $out From 4c5685d381bd2f8d98073414fd2009a6056bd0ae Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 04:17:37 +0300 Subject: [PATCH 37/55] draft doc fixup --- docs/Whats-New.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 1707c477c4..b45559250f 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -2,15 +2,15 @@ This page lists the history of changes across stable Phobos releases and also all the stuff that requires modders to change something in their mods to accommodate. -## Migrating +## Migrating from vanilla + +This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping (be sure to read Phobos migration one below too), so you can simply read up on this log of breaking changes and apply whatever you might need to apply. ```{hint} You can use the migration utility (can be found on [Phobos supplementaries repo](https://github.com/Phobos-developers/PhobosSupplementaries)) to apply most of the changes automatically using a corresponding sed script file. ``` -### From vanilla - -#### Version TBD (develop branch nightly builds) +### 0.5 - Vehicles paradropped by AI players now default to `Hunt` mission instead of `Guard`, matching what infantry do. This can be customized by setting `AIParadropMission` on the VehicleType, defaults to `[General] -> AIParadropMission`. - `IsSimpleDeployer` units now obey deploying facing constraint even without deploying animation if `DeployDir` is explicitly set on the unit. @@ -19,7 +19,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - Weapons with `Airstrike=true` on Warhead will now check target eligibility for airstrikes regardless of weapon slot. Use `AirstrikeTargets=all` on `Primary` airstrike weapon Warhead to restore previous behaviour. - The default direction for aircraft landing on an airfield will use the direction specified by `[AudioVisual] -> PoseDir=` instead of the building's direction, which can be reverted by setting `AircraftDockingDir.DefaultToPoseDir=false`. -#### 0.4 +### 0.4 - `PowerUpNAnim` is now used instead of the upgrade building's image file for upgrade animation if set. Note that displaying a damaged version will still require setting `PowerUpNDamagedAnim` explicitly in all cases, as the fallback to upgrade building image does not extend to it, nor would it be safe to add. `PowersUpToLevel=-1` upgrades still do not work correctly `PowerUpNAnim` and such buildings should forgo using explicit upgrade animations. - Elite technos no longer scatter by default, behaviour can be restored by including `SCATTER` in their `EliteAbilities`. @@ -27,14 +27,18 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - The obsolete `[General] -> WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `[General] -> WarpOut`. - Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both default to `TiltsWhenCrushes`. -#### 0.3 +### 0.3 - Translucent RLE SHPs will now be drawn using a more precise and performant algorithm that has no green tint and banding. Can be disabled with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`. - Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`. -## Breaking changes +## Breaking Phobos changes -This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping, so you can simply read up on this log of breaking changes and apply whatever you might need to apply. +This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping (be sure to read vanilla migration one above too), so you can simply read up on this log of breaking changes and apply whatever you might need to apply. + +```{hint} +You can use the migration utility (can be found on [Phobos supplementaries repo](https://github.com/Phobos-developers/PhobosSupplementaries)) to apply most of the changes automatically using a corresponding sed script file. +``` ### 0.5 @@ -403,7 +407,7 @@ HideShakeEffects=false ; boolean ## Changelog -### Version TBD (develop branch nightly builds) +### 0.5 ```{dropdown} Click to show :open: @@ -813,6 +817,13 @@ HideShakeEffects=false ; boolean - Fixed a bug where passengers created by the InitialPayload logic or TeamType with `Full=true` would fail to execute the auto death logic (by Noble_Fish) - Fixed the issue of Ares' EMP not suspending the production of AI factories (by CrimRecya) - Removed the restriction that prohibits InfantryTypes from using the InitialPayload logic (by Noble_Fish) + +``` + +```{dropdown} Pre-release changes + +#### 0.5-alpha1 + ``` ### 0.4.0.3 From be7fe37e1ba223204f6b41c4aea17f0cf517262b Mon Sep 17 00:00:00 2001 From: ZivDero <kirill.andriiashin@gmail.com> Date: Mon, 3 Aug 2026 04:58:05 +0300 Subject: [PATCH 38/55] Fix the documented name of the crushing tilt keys --- docs/Fixed-or-Improved-Logics.md | 2 +- docs/Whats-New.md | 4 ++-- docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po | 4 ++-- docs/locale/zh_CN/LC_MESSAGES/Whats-New.po | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 4e66ba1d63..95b3851404 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2572,7 +2572,7 @@ TypeSelectUseIFVMode=true ; boolean ### Customizing crushing tilt and slowdown -- Vehicles with `Crusher=true` and `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both default to `TiltsWhenCrushes`. +- Vehicles with `Crusher=true` and `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both default to `TiltsWhenCrushes`. - `CrushForwardTiltPerFrame` determines how much the forward tilt is adjusted per frame when crushing overlays or vehicles. Defaults to -0.02 for vehicles using Ship locomotor crushing overlays, -0.050000001 for all other cases. - `CrushOverlayExtraForwardTilt` is additional forward tilt applied after an overlay has been crushed by the vehicle. - It is possible to customize the movement speed slowdown when `MovementZone=CrusherAll` vehicle crushes walls by setting `CrushSlowdownMultiplier`. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index b45559250f..89115ecd5c 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -21,11 +21,11 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] ### 0.4 -- `PowerUpNAnim` is now used instead of the upgrade building's image file for upgrade animation if set. Note that displaying a damaged version will still require setting `PowerUpNDamagedAnim` explicitly in all cases, as the fallback to upgrade building image does not extend to it, nor would it be safe to add. `PowersUpToLevel=-1` upgrades still do not work correctly `PowerUpNAnim` and such buildings should forgo using explicit upgrade animations. +- `PowerUpNAnim` is now used instead of the upgrade building's image file for upgrade animation if set. Note that displaying a damaged version will still require setting `PowerUpNDamagedAnim` explicitly in all cases, as the fallback to upgrade building image does not extend to it, nor would it be safe to add. `PowersUpToLevel=-1` upgrades still do not work correctly with `PowerUpNAnim` and such buildings should forgo using explicit upgrade animations. - Elite technos no longer scatter by default, behaviour can be restored by including `SCATTER` in their `EliteAbilities`. - `[CrateRules] -> FreeMCV` now controls whether or not player is forced to receive unit from `[General] -> BaseUnit` from goodie crate if they own no buildings or any existing `[General] -> BaseUnit` vehicles and own more than `[CrateRules] -> FreeMCV.CreditsThreshold` (defaults to 1500) credits. - The obsolete `[General] -> WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `[General] -> WarpOut`. -- Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both default to `TiltsWhenCrushes`. +- Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both default to `TiltsWhenCrushes`. ### 0.3 diff --git a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po index dcc9bacda6..a2d3e7aa59 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po @@ -5302,12 +5302,12 @@ msgid "" "`MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles /" " walls respectively. This now obeys `TiltsWhenCrushes` but can be " "customized individually for these two scenarios using " -"`TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both " +"`TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both " "default to `TiltsWhenCrushes`." msgstr "" "拥有 `Crusher=true` 和 `OmniCrusher=true` / `MovementZone=CrusherAll` " "的载具在碾压载具/围墙时原版被硬编码进行倾斜。现在这遵守 `TiltsWhenCrushes`,并且可以对 " -"`TiltsWhenCrusher.Vehicles` 和 `TiltsWhenCrusher.Overlays` " +"`TiltsWhenCrushes.Vehicles` 和 `TiltsWhenCrushes.Overlays` " "对两种情况分别进行定义,它们都默认为 `TiltsWhenCrushes`。" msgid "" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index a88e1fc702..5b9a801b32 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -141,12 +141,12 @@ msgid "" "`MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles /" " walls respectively. This now obeys `TiltsWhenCrushes` but can be " "customized individually for these two scenarios using " -"`TiltsWhenCrusher.Vehicles` and `TiltsWhenCrusher.Overlays`, which both " +"`TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both " "default to `TiltsWhenCrushes`." msgstr "" "拥有 `Crusher=true` 和 `OmniCrusher=true`/`MovementZone=CrusherAll` " "的载具类型硬编码在其碾压载具/围墙时会掀起而倾斜。现在此行为遵循 `TiltsWhenCrushes` 设置,但可以使用 " -"`TiltsWhenCrusher.Vehicles` 和 `TiltsWhenCrusher.Overlays` " +"`TiltsWhenCrushes.Vehicles` 和 `TiltsWhenCrushes.Overlays` " "分别定义这两种情况,它们都默认为 `TiltsWhenCrushes`。" msgid "" From 2e99b20b1193ab6319b4d0f3c65f2fe4bfb95807 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 19:07:29 +0300 Subject: [PATCH 39/55] better migration doc structure --- docs/Whats-New.md | 56 ++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 89115ecd5c..df6e9b7330 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -2,9 +2,13 @@ This page lists the history of changes across stable Phobos releases and also all the stuff that requires modders to change something in their mods to accommodate. -## Migrating from vanilla +## Migration (breaking changes) -This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping (be sure to read Phobos migration one below too), so you can simply read up on this log of breaking changes and apply whatever you might need to apply. +```{important} +New Phobos user? Read the "Changes to vanilla behavior" subsections in every version below to know what you might need to adjust when installing Phobos +``` + +This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping, so you can simply read up on this log of breaking changes and apply whatever you might need to apply. ```{hint} You can use the migration utility (can be found on [Phobos supplementaries repo](https://github.com/Phobos-developers/PhobosSupplementaries)) to apply most of the changes automatically using a corresponding sed script file. @@ -12,6 +16,8 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] ### 0.5 +#### Changes to vanilla behavior + - Vehicles paradropped by AI players now default to `Hunt` mission instead of `Guard`, matching what infantry do. This can be customized by setting `AIParadropMission` on the VehicleType, defaults to `[General] -> AIParadropMission`. - `IsSimpleDeployer` units now obey deploying facing constraint even without deploying animation if `DeployDir` is explicitly set on the unit. - `Vertical=true` projectiles now default to completely downwards initial trajectory/facing regardless of if their projectile image has `Voxel=true` or not. This behavior can be reverted by setting `VerticalInitialFacing=false` on projectile in `rulesmd.ini`. @@ -19,28 +25,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - Weapons with `Airstrike=true` on Warhead will now check target eligibility for airstrikes regardless of weapon slot. Use `AirstrikeTargets=all` on `Primary` airstrike weapon Warhead to restore previous behaviour. - The default direction for aircraft landing on an airfield will use the direction specified by `[AudioVisual] -> PoseDir=` instead of the building's direction, which can be reverted by setting `AircraftDockingDir.DefaultToPoseDir=false`. -### 0.4 - -- `PowerUpNAnim` is now used instead of the upgrade building's image file for upgrade animation if set. Note that displaying a damaged version will still require setting `PowerUpNDamagedAnim` explicitly in all cases, as the fallback to upgrade building image does not extend to it, nor would it be safe to add. `PowersUpToLevel=-1` upgrades still do not work correctly with `PowerUpNAnim` and such buildings should forgo using explicit upgrade animations. -- Elite technos no longer scatter by default, behaviour can be restored by including `SCATTER` in their `EliteAbilities`. -- `[CrateRules] -> FreeMCV` now controls whether or not player is forced to receive unit from `[General] -> BaseUnit` from goodie crate if they own no buildings or any existing `[General] -> BaseUnit` vehicles and own more than `[CrateRules] -> FreeMCV.CreditsThreshold` (defaults to 1500) credits. -- The obsolete `[General] -> WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `[General] -> WarpOut`. -- Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both default to `TiltsWhenCrushes`. - -### 0.3 - -- Translucent RLE SHPs will now be drawn using a more precise and performant algorithm that has no green tint and banding. Can be disabled with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`. -- Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`. - -## Breaking Phobos changes - -This serves as a changelog for when you just need to drop the new version in without reading into every version you're skipping (be sure to read vanilla migration one above too), so you can simply read up on this log of breaking changes and apply whatever you might need to apply. - -```{hint} -You can use the migration utility (can be found on [Phobos supplementaries repo](https://github.com/Phobos-developers/PhobosSupplementaries)) to apply most of the changes automatically using a corresponding sed script file. -``` - -### 0.5 +#### Changes to Phobos behavior - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. @@ -84,7 +69,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - `PassengerDeletion.SoylentAllowedHouses`: `none` -> `enemies` - `PassengerDeletion.DisplaySoylentOffset`: `none` -> `all` -#### Changes compared to inter-version builds / pre-releases +##### Changes compared to inter-version builds / pre-releases ```{dropdown} Click to show - Due to the format issue with `select.shp` in vanilla Yuri's Revenge that prevents the [Select box logic](User-Interface.md#select-box) from rendering correctly, `select.shp` no longer serves as the default value for `[SelectBoxType] -> Shape=`, and you need to manually specify a value for this flag. @@ -94,6 +79,16 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] ### 0.4 +#### Changes to vanilla behavior + +- `PowerUpNAnim` is now used instead of the upgrade building's image file for upgrade animation if set. Note that displaying a damaged version will still require setting `PowerUpNDamagedAnim` explicitly in all cases, as the fallback to upgrade building image does not extend to it, nor would it be safe to add. `PowersUpToLevel=-1` upgrades still do not work correctly with `PowerUpNAnim` and such buildings should forgo using explicit upgrade animations. +- Elite technos no longer scatter by default, behaviour can be restored by including `SCATTER` in their `EliteAbilities`. +- `[CrateRules] -> FreeMCV` now controls whether or not player is forced to receive unit from `[General] -> BaseUnit` from goodie crate if they own no buildings or any existing `[General] -> BaseUnit` vehicles and own more than `[CrateRules] -> FreeMCV.CreditsThreshold` (defaults to 1500) credits. +- The obsolete `[General] -> WarpIn` has been enabled for the default anim type when technos are warping in. If you want to restore the vanilla behavior, use the same anim type as `[General] -> WarpOut`. +- Vehicles with `Crusher=true` + `OmniCrusher=true` / `MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles / walls respectively. This now obeys `TiltsWhenCrushes` but can be customized individually for these two scenarios using `TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both default to `TiltsWhenCrushes`. + +#### Changes to Phobos behavior + - Phobos-introduced Warhead effects like shield modifiers, critical hits, disguise & mind control removal now require Warhead `Verses` to affect target to apply unless `EffectsRequireVerses` is set to false. Shield armor type is used if target has an active shield that cannot be penetrated by the Warhead. - `Trajectory=Straight` projectiles can now snap on targets within 0.5 cells from their detonation point, this distance can be customized via `Trajectory.Straight.TargetSnapDistance`. - `LaunchSW.RealLaunch=false` now checks if firing house has enough credits to satisfy SW's `Money.Amount` in order to be fired. @@ -107,7 +102,7 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] - Buildings delivered by trigger action 125 will now **always** play buildup anim as long as it exists. `[ParamTypes] -> 53` is deprecated. - `Shadow` for debris & meteor animations is changed to `ExtraShadow`. -#### Changes compared to inter-version builds / pre-releases +##### Changes compared to inter-version builds / pre-releases ```{dropdown} Click to show - Ivan bombs no longer automatically center on building when attached. Set `[CombatDamage] -> IvanBombAttachToCenter` to true to restore this behaviour. Due to technical constraints this cannot be customized per WeaponType. @@ -133,12 +128,19 @@ You can use the migration utility (can be found on [Phobos supplementaries repo] ### 0.3 +#### Changes to vanilla behavior + +- Translucent RLE SHPs will now be drawn using a more precise and performant algorithm that has no green tint and banding. Can be disabled with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`. +- Iron Curtain status is now preserved by default when converting between TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be turned off per-TechnoType and global basis using `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`. + +#### Changes to Phobos behavior + - Keys `rulesmd.ini -> [WarheadType] -> PenetratesShield` and `rulesmd.ini -> [WarheadType] -> BreaksShield` have been changed to `Shield.Penetrate` and `Shield.Break`, respectively. - `Rad.NoOwner` on weapons is deprecated. This has been replaced by `RadHasOwner` key on radiation types itself. It also defaults to no, so radiation once again has no owner house by default. - `RadApplicationDelay` and `RadApplicationDelay.Building` on custom radiation types are now only used if `[Radiation] -> UseGlobalRadApplicationDelay` is explicitly set to false, otherwise values from `[Radiation]` are used. - Existing script actions were renumbered, please use the migration utility to change the numbers to the correct ones. -#### Changes compared to inter-version builds / pre-releases +##### Changes compared to inter-version builds / pre-releases ```{dropdown} Click to show - `Trajectory.Speed` is now defined on projectile instead of weapon. From f22ee78ca74b01509a4d4255dfab018086db1cff Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Mon, 3 Aug 2026 19:29:52 +0300 Subject: [PATCH 40/55] link to relevant sections --- README.md | 4 ++++ docs/Project-guidelines-and-policies.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index a67ee1bb99..bd605077ba 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ You can choose one of the following: - [Latest development branch nightly](https://nightly.link/Phobos-developers/Phobos/blob/develop/.github/workflows/nightly.yml) (added unreleased features that will be in the next pre-release) - Individual new feature nightly builds for testing can be found in [pull requests](https://github.com/Phobos-developers/Phobos/pulls) +To learn how these build types relate to each other and how versioning works, see the [release model and version lifecycle](docs/Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy). + ### Note on nightly builds Last two listed versions are bleeding edge (don't redistribute them outside of testing!) and have build information (commit and branch/tag) in them which is displayed ingame and can't be turned off. You can get a build for development branch (link above) any up-to-date pull request via an automatic bot comment that would appear in it and would contain the most recent successfully compiled version of Phobos for that feature branch. Please note that the build is produced *only if the PR has no merge conflicts*. Alternatively, you can get an artifact manually from GitHub Actions runs. You can get an artifact for a specific commit which is built automatically with a GitHub Actions workflow, just press on a green tick, open the workflow, find and download the build artifact. This is limited to authorized users only. @@ -46,6 +48,8 @@ Installation and Usage 2. Place those files in the game folder (where your `gamemd.exe` is located), replacing any existing `Syringe.exe` (for example the one shipped with Ares). 3. To launch the game with Phobos (and all other installed Syringe-compatible engine extensions including Ares) you need to execute `Syringe.exe "gamemd.exe" [command line arguments for gamemd.exe]` in command line (omit arguments if you don't need any). `RunAres.bat` from Ares package does the same so you may use that as well. +Be sure to read [migration and breaking changes](docs/Whats-New.md#migration-breaking-changes) to know if you need to adjust something in your mod after Phobos installation (or update). + If you already use Ares in your mod, you just need to drop Phobos files mentioned above in your game folder, Syringe will load Phobos automatically. This also applies to mods using XNA client with Syringe; if your mod doesn't use Syringe and Ares (or you just haven't set up the client) yet we recommend to use [CnCNet client mod base by Starkku](https://github.com/Starkku/cncnet-client-mod-base) which is compatible with Ares and Phobos out of the box. Additional files and tools that you may need are located at [Phobos supplementaries repo](https://github.com/Phobos-developers/PhobosSupplementaries). diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index cf773eaa0b..c8e05f0362 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -238,6 +238,10 @@ Starting from version 0.5, Phobos adopts a new release strategy to enable faster *Image editable in [Excalidraw](https://excalidraw.com)* +```{important} +All changes are to be made **exclusively** to `develop` as the source of truth, and then cherry-picked to other branches! +``` + ```{hint} A brief summary compared to old style: - devbuilds are now called pre-releases (alpha, beta, RC etc.) and are almost a proper version with docs, all changes tracked in a special changelog subsection, released on the same cadence; From 364e62cf16a2c989251da44ac60acb4f182e6649 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Tue, 4 Aug 2026 09:01:14 +0800 Subject: [PATCH 41/55] update doc --- docs/Contributing.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/Contributing.md b/docs/Contributing.md index 8db6b46dd6..60dcec2858 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -43,13 +43,13 @@ If you contribute something, please make sure: Not every change needs all of the above. The table below shows what each kind of change is expected to cover (a checkmark means that kind of entry is required): -| Change type | Changelog | Docs | Credits | -|---|---|---|---| -| New feature, vanilla bugfix, enhancement of a released feature | ✓ | ✓ | ✓ | -| Improvement to a new (unreleased) feature | ✗ | ✓ | ✓ | -| Bugfix to a new (unreleased) feature | ✗ | ✗ | ✓ | -| Bugfix to an old (released) feature | ✓ | ✗ | ✓ | -| Completely minor change (e.g. a typo fix) | ✗ | ✗ | ✗ | +| Change type | Changelog | Docs | Credits | +|----------------------------------------------------------------|-----------|------|---------| +| New feature, vanilla bugfix, enhancement of a released feature | ✓ | ✓ | ✓ | +| Improvement to a new (unreleased) feature | ✗ | ✓ | ✓ | +| Bugfix to a new (unreleased) feature | ✗ | ✗ | ✓ | +| Bugfix to an old (released) feature | ✓ | ✗ | ✓ | +| Completely minor change (e.g. a typo fix) | ✗ | ✗ | ✗ | ```{warning} If editing this table - also edit the PR template. From 151cc4c2c0e6c1a2615ecdef7bc2abd1f605defd Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Tue, 4 Aug 2026 09:16:34 +0800 Subject: [PATCH 42/55] Attempt to improve the script to work in scenarios without adding a title to the dropdown --- docs/Whats-New.md | 70 +++++++++++++++++------------------ scripts/extract_changelog.ps1 | 38 ++++++++++++++++++- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 82f9eb6e93..60fa6e3f4f 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -415,7 +415,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show :open: -#### New: +New: - [Allow using waypoints, area guard and attack move with aircraft](Fixed-or-Improved-Logics.md#extended-aircraft-missions) (by CrimRecya) - [Enhanced Straight trajectory](New-or-Enhanced-Logics.md#straight-trajectory) (by CrimRecya) - [Enable building production queue](User-Interface.md#building-production-queue) (by CrimRecya) @@ -669,7 +669,7 @@ HideShakeEffects=false ; boolean - `OmniFire` supports buildings with `Turret=yes` (by FlyStar) - [Automatic conversion based on heallth](New-or-Enhanced-Logics.md#automatic-conversion-based-on-heallth) (by obsidianus) -#### Vanilla fixes: +Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) - Buildings with foundation bigger than 1x1 can now recycle spawned correctly (by TaranDahl) @@ -758,7 +758,7 @@ HideShakeEffects=false ; boolean - Fixed the bug that technos do not reset their link with the linked building when deactivated (by NetsuNegi) - Fixed an issue where setting a production building as `Primary` could cause it to enter an unload state (by FlyStar) -#### Phobos fixes: +Phobos fixes: - Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi) - Fixed an issue that `FireAngle` was not taken into account when drawing barrel in `TurretShadow` (by CrimRecya) - Fixed a bug that sometimes caused weapon/warhead detonations from features such as `ExtraWarheads`, animation damage or `Crit.Warhead` to unintentionally move from its intended position (by Starkku) @@ -799,7 +799,7 @@ HideShakeEffects=false ; boolean - Fixed the bug where landing direction cannot be correctly converted when set to a value exceeding 256 (by Noble_Fish) - Fixed a bug with some keys relating to `Interceptor` and `PassengerDeletion` not having correct default values (by Starkku) -#### Fixes / interactions with other extensions: +Fixes / interactions with other extensions: - Taking over Ares' AlphaImage respawn logic to reduce lags from it (by NetsuNegi) - Fixed an issue that Ares' Type Conversion not resetting barrel's direction by `FireAngle` (by TaranDahl) - Fixed the issue where Ares' `Flash.Duration` cannot override the weapon's repair flash effect (by Sovietianqi, based on knowledge of Noble_Fish) @@ -842,11 +842,11 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Vanilla fixes: +Vanilla fixes: - Vehicles overlapping `Wall=true` OverlayTypes no longer display sell cursor and cannot be sold (by CnCRAZER, Starkku, Noble_Fish) - Fixed a desync due to an inconsistent shroud state caused by `GapGenerator` and `SpySat` interaction (by Starkku) -#### Phobos fixes: +Phobos fixes: - Fixed vehicles disguised as trees incorrectly displaying veterancy insignia when they shouldn't (by Starkku) - Fixed the issue that power output of building on tooltip won't consider power enhancer (by NetsuNegi) - `RealTimeTimers` now support independent gamespeed index values for Multiplayer and Skirmish (by RAZER) @@ -871,7 +871,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Phobos fixes: +Phobos fixes: - Fixed `AircraftDockingDirs` being reset if the BuildingType section is redefined in map file (by Starkku) - Fixed harvester counter not accounting for type converting harvesters (by Ollerus) - Fixed the bug that weapon cannot used to intercept on gound bullet if it's projectile has `AG=no` (by NetsuNegi) @@ -887,10 +887,10 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Vanilla fixes: +Vanilla fixes: - Fixed preplaced aircraft outside visible map being incorrectly flagged as crashing under certain conditions (by Starkku) -#### Phobos fixes: +Phobos fixes: - AttachEffect `DisableWeapons` no longer interferes with 'can this unit/building fire weapons?' checks for units/buildings with no weapons (by Starkku) - Fixed starting infantry being scattered randomly on game start instead of being grouped (by 11EJDE11, Belonit, Ollerus) - Fixed an issue with certain Warhead detonation features (f.ex `Crit.Warhead` without `Crit.Warhead.FullDetonation=true` not snapping on the intended target without `CellSpread`) (by Starkku) @@ -905,7 +905,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - `Crit.AffectsHouses` for critical hit system (by Starkku) - Warhead or weapon detonation at superweapon target cell (by Starkku) - Super Weapons launching other Super Weapons (by Morton) @@ -1073,7 +1073,7 @@ HideShakeEffects=false ; boolean - Allow retint fix to be disabled with `[AudioVisual] -> UseRetintFix=no` in `rulesmd.ini` due to performance considerations (by Kerbiter) - Elite technos no longer scatter by default, behaviour is controlled by `SCATTER` veterancy ability now (by NetsuNegi & Starkku) -#### Vanilla fixes: +Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) - Allow setting whether `AlternateFLH` applies to vehicle passengers in the transport unit (by Trsdy & NetsuNegi) - Improved the statistic distribution of the spawned crates over the visible area of the map. (by Trsdy, based on TwinkleStar's work) @@ -1219,7 +1219,7 @@ HideShakeEffects=false ; boolean - Fixed the fact that when the selected unit is in a rearmed state, it can unconditionally use attack mouse on the target (by FlyStar) - Fixed pathfinding crashes (EIP 0x42A525, 0x42C507, 0x42C554) that happened on bigger maps due to too small pathfinding node buffer (by CrimRecya) -#### Phobos fixes: +Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) - Add `ImmuneToCrit` for shields (by Trsdy) - Reimplemented the bugfix for jumpjet units' facing when firing, discard the inappropriate `JumpjetTurnToTarget` tag (by Trsdy) @@ -1274,7 +1274,7 @@ HideShakeEffects=false ; boolean - Fixed a read bug when setting the SHP file name in INI (By Noble_Fish) - Fixed map trigger action `125 Build At...` not always playing buildups correctly (by Starkku) -#### Fixes / interactions with other extensions: +Fixes / interactions with other extensions: - Weapons fired by EMPulse superweapons *(Ares feature)* now fully respect the firing building's FLH (by Starkku) - Weapons fired by EMPulse superweapons *(Ares feature)* now respect `Floater` and Phobos-added `Gravity` setting (by Starkku) - `IsSimpleDeployer` units with Hover locomotor and `DeployToLand` no longer get stuck after deploying or play their move sound indefinitely (by Starkku) @@ -1301,10 +1301,10 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - Additional sync logging in case of desync errors occuring (by Starkku) -#### Phobos fixes: +Phobos fixes: - `AutoDeath` support for objects in limbo (by Trsdy) - Buildings sold by `AutoDeath` no longer play a click sound effect (by Trsdy) - Fixed shield animation being hidden while underground or in tunnels fix not working correctly (by Starkku) @@ -1332,7 +1332,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - LaserTrails initial implementation (by Kerbiter & ChrisLv_CN) - Anim-to-Unit logic and ability to randomize `DestroyAnim` (by Otamaa) - Shield modification warheads (by Starkku) @@ -1432,7 +1432,7 @@ HideShakeEffects=false ; boolean - New condition for automatic self-destruction logic when TechnoTypes exist/don't exist (by FlyStar) - For developers: now you can use command line arg to control main exception handler (by Multfinite) -#### Vanilla fixes: +Vanilla fixes: - Fixed laser drawing code to allow for thicker lasers in house color draw mode (by Kerbiter & ChrisLv_CN) - Fixed `DeathWeapon` not detonating properly (by Uranusian) - Fixed lasers & other effects drawing from wrong offset with weapons that use `Burst` (by Starkku) @@ -1466,7 +1466,7 @@ HideShakeEffects=false ; boolean - Fixed building `TargetCoordOffset` not being taken into accord for several things like fire angle calculations and target lines (by Starkku) - Allowed observers to see a selected building's radial indicator (by Trsdy) -#### Phobos fixes: +Phobos fixes: - Fixed shields being able to take damage when the parent TechnoType was under effects of a `Temporal` Warhead (by Starkku) - Improved shield behavior for forced damage (by Uranusian) - Fixed `SplashList` animations playing when a unit is hit on a bridge over water (by Uranusian) @@ -1495,11 +1495,11 @@ HideShakeEffects=false ; boolean - Corrected the misinterpretation in the definition of `DiskLaser.Radius` (by Trsdy) - Fixed GlobalVariables failed working among scenarios (by Trsdy) -#### Fixes / interactions with other extensions: +Fixes / interactions with other extensions: - Fixed AI Aircraft docks bug when Ares tag `[GlobalControls] -> AllowParallelAIQueues=no` is set (by FS-21) - Weapons fired by EMPulse superweapons *(Ares feature)* without `EMPulse.TargetSelf=true` can now create radiation (by Starkku) -#### Non-DLL: +Non-DLL: - Implemented a tool (sed wrapper) to semi-automatically upgrade INIs to use latest Phobos tags (by Kerbiter) ``` @@ -1508,7 +1508,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Phobos fixes: +Phobos fixes: - Fixed shield type info not saving properly (by Uranusian) - Fixed extended building upgrades logic not properly interacting with Ares' BuildLimit check (by Uranusian) - Fixed more random crashes for `CameoPriority` (by Uranusian) @@ -1520,7 +1520,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Phobos fixes: +Phobos fixes: - Fixed random crashes about `CameoPriority` (by Uranusian) - Fixed trigger action 125 not functioning properly (by Uranusian) - Fixed area warhead detonation not falling back to firer house (by Otamaa) @@ -1534,16 +1534,16 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - Customizable producing progress "bars" like CnC:Remastered did (by Uranusian) - Customizable cameo sorting priority (by Uranusian) - Customizable harvester ore gathering animation (by secsome & Uranusian) - Allow making technos unable to be issued with movement order (by Uranusian) -#### Vanilla fixes: +Vanilla fixes: - Fixed non-IME keyboard input to be working correctly for languages / keyboard layouts that use character ranges other than Basic Latin and Latin-1 Supplement (by Belonit) -#### Phobos fixes: +Phobos fixes: - Fixed the critical damage logic not functioning properly (by Uranusian) - Fixed the bug when executing the stop command game crashes (by Uranusian) @@ -1553,7 +1553,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Phobos fixes: +Phobos fixes: - Fixed occasional crashes introduced by `Speed=0` stationary vehicles code (by Starkku) ``` @@ -1562,14 +1562,14 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - Setting VehicleType `Speed` to 0 now makes game treat them as stationary (by Starkku) -#### Vanilla fixes: +Vanilla fixes: - Fixed the bug when after a failed placement the building/defence tab hotkeys won't trigger placement mode again (by Uranusian) - Fixed the bug when building with `UndeployInto` plays `EVA_NewRallypointEstablished` while undeploying (by secsome) -#### Phobos fixes: +Phobos fixes: - Fixed the bug when trigger action `125 Build At...` wasn't actually producing a building when the target cells were occupied (by secsome) ``` @@ -1579,7 +1579,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - Shield logic for TechnoTypes (by Uranusian, secsome, Belonit) with warhead additions (by Starkku) - Custom Radiation Types (by AlexB, Otamaa, Belonit, Uranusian) - New ScriptType actions `71 Timed Area Guard`, `72 Load Onto Transports`, `73 Wait until ammo is full` (by FS-21) @@ -1604,7 +1604,7 @@ HideShakeEffects=false ; boolean - Maximum waypoints amount increased from 702 to 2147483647 (by secsome) - Customizeable Missing Cameo file (by Uranusian) -#### Vanilla fixes: +Vanilla fixes: - Map previews with zero size won't crash the game anymore (by Kerbiter & Belonit) - Tileset 255+ bridge fix (by E1 Elite) - Fixed fatal errors when `Blowfish.dll` couldn't be registered in the system properly due to missing admin rights (by Belonit) @@ -1617,7 +1617,7 @@ HideShakeEffects=false ; boolean - Fixed `DebrisMaximums` (spawned debris type amounts cannot go beyond specified maximums anymore) (by Otamaa) - Fixes to `DeployFire` logic (`DeployFireWeapon`, `FireOnce`, stop command now work properly) (by Starkku) -#### Phobos fixes: +Phobos fixes: - Properly rewritten a fix for mind-controlled vehicles deploying into buildings (by FS-21) - Properly rewritten `DeployToFire` fix, tag `Deployed.RememberTarget` is deprecated, now always on (by Kerbiter) - New warheads now work with Ares' `GenericWarhead` superweapon (by Belonit) @@ -1628,7 +1628,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### Phobos fixes: +Phobos fixes: - Fixed an occasional crash when selecting units with a selection box (by Kerbiter) ``` @@ -1637,7 +1637,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show -#### New: +New: - Full-color PCX graphics support (by Belonit) - Support for PCX loading screens of any size (by Belonit) - Extended sidebar tooltips with descriptions, recharge time and power consumption/generation (by Kerbiter & Belonit) @@ -1649,7 +1649,7 @@ HideShakeEffects=false ; boolean - Customizable disk laser radius (by Belonit & Kerbiter) - Ability to switch to GDI sidebar layout for any side (by Belonit) -#### Vanilla fixes: +Vanilla fixes: - Deploying mind-controlled TechnoTypes won't make them permanently mind-controlled anymore (unfinished fix by DCoder) - SHP debris hardcoded shadows now respect `Shadow=no` tag value (by Kerbiter) - `DeployToFire` vehicles won't lose target on deploy anymore (unfinished fix by DCoder) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index 800562095e..b6700a767d 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -238,6 +238,42 @@ if (-not (Test-Path -LiteralPath $WhatsNewPath)) { } $content = (Get-Content -LiteralPath $WhatsNewPath -Raw -Encoding utf8) -replace "`r`n", "`n" + +$lines = $content -split "`n" +$resultLines = [System.Collections.Generic.List[string]]::new() +$inDropdown = $false + +foreach ($line in $lines) { + # dropdown start + if ($line -match '^\s*```\{dropdown\}') { + $inDropdown = $true + $resultLines.Add($line) + continue + } + + # dropdown end + if ($inDropdown -and $line -match '^\s*```\s*$') { + $inDropdown = $false + $resultLines.Add($line) + continue + } + + # in dropdown + if ($inDropdown) { + $trimmed = $line.Trim() + # !empty, !list, !title, :$ + if ($trimmed -ne '' -and $trimmed -notmatch '^[-*+]' -and $trimmed -notmatch '^#' -and $trimmed -match ':$') { + # transform 4-level title + $resultLines.Add("#### " + $trimmed) + continue + } + } + + $resultLines.Add($line) +} + +$content = $resultLines -join "`n" + $root = Read-SectionTree ($content -split "`n") $sections = @(Get-AllSections $root) @@ -287,4 +323,4 @@ $outPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) { $OutputPath } else [System.IO.File]::WriteAllText($outPath, $text + "`n", (New-Object System.Text.UTF8Encoding($false))) $kinds = @($matched | ForEach-Object { Get-SectionKind $_ }) -join ', ' -Write-Host "Extracted release notes for version '$version' ($kinds) to '$OutputPath'" +Write-Host "Extracted release notes for version '$version' ($kinds) to '$OutputPath'" \ No newline at end of file From 494a24dbff5de22f2495d9d891594b9d30991647 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Tue, 4 Aug 2026 09:38:17 +0800 Subject: [PATCH 43/55] ignore `:open:` --- scripts/extract_changelog.ps1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index b6700a767d..d671d2b105 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -261,9 +261,7 @@ foreach ($line in $lines) { # in dropdown if ($inDropdown) { $trimmed = $line.Trim() - # !empty, !list, !title, :$ - if ($trimmed -ne '' -and $trimmed -notmatch '^[-*+]' -and $trimmed -notmatch '^#' -and $trimmed -match ':$') { - # transform 4-level title +if ($trimmed -ne '' -and $trimmed -notmatch '^[-*+]' -and $trimmed -notmatch '^#' -and $trimmed -notmatch '^:[A-Za-z]' -and $trimmed -match ':$') { $resultLines.Add("#### " + $trimmed) continue } From 1790e86443fa93ed55e2a9c630995067dfb48492 Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Tue, 4 Aug 2026 10:20:09 +0800 Subject: [PATCH 44/55] update tr. --- docs/locale/zh_CN/LC_MESSAGES/Contributing.po | 59 +++- docs/locale/zh_CN/LC_MESSAGES/General-Info.po | 68 +++-- docs/locale/zh_CN/LC_MESSAGES/Miscellanous.po | 36 ++- .../Project-guidelines-and-policies.po | 266 ++++++++++++++---- docs/locale/zh_CN/LC_MESSAGES/Whats-New.po | 219 +++++++------- docs/locale/zh_CN/LC_MESSAGES/index.po | 46 ++- 6 files changed, 475 insertions(+), 219 deletions(-) diff --git a/docs/locale/zh_CN/LC_MESSAGES/Contributing.po b/docs/locale/zh_CN/LC_MESSAGES/Contributing.po index ff74924b06..2bad098dd4 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Contributing.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Contributing.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-02 01:28+0800\n" +"POT-Creation-Date: 2026-08-04 10:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -89,7 +89,9 @@ msgid "" "agents, but is also a useful read for any new contributor looking to " "understand the codebase quickly." msgstr "" -"仓库中包含一份 [Copilot 指令文件](https://github.com/Phobos-developers/Phobos/blob/develop/.github/copilot-instructions.md),作为项目的快速入门指南——包含构建、项目结构、钩子模式、补丁宏、YRpp 使用方法等内容。该文件可被 " +"仓库中包含一份 [Copilot 指令文件](https://github.com/Phobos-" +"developers/Phobos/blob/develop/.github/copilot-" +"instructions.md),作为项目的快速入门指南——包含构建、项目结构、钩子模式、补丁宏、YRpp 使用方法等内容。该文件可被 " "GitHub Copilot 和类似的 AI 代码智能体自动识别,同时也是新贡献者快速了解代码库的实用参考。" msgid "" @@ -152,10 +154,55 @@ msgid "you mention your contribution in the [credits page](CREDITS.md)." msgstr "你在 [鸣谢](CREDITS.md) 页面列出了你的贡献。" msgid "" -"If your change does not fit in standard criteria or too small that it " -"doesn't need the above - add `[Minor]` to your pull request's title, so " -"the CI won't yell at you for no reason." -msgstr "若变更不符合标准流程或规模过小无需上述步骤,请在拉取请求标题添加 `[Minor]`,以避免 CI 无故报错。" +"Not every change needs all of the above. The table below shows what each " +"kind of change is expected to cover (a checkmark means that kind of entry" +" is required):" +msgstr "" +"并非每项更改都需要上述所有内容。下表给出了每种更改类型应当覆盖的内容(勾号代表该条目为必需):" + +msgid "Change type" +msgstr "更改类型" + +msgid "Changelog" +msgstr "版本更新说明" + +msgid "Docs" +msgstr "文档" + +msgid "Credits" +msgstr "鸣谢" + +msgid "New feature, vanilla bugfix, enhancement of a released feature" +msgstr "新功能、原版 Bug 修复、增强已发布功能" + +msgid "✓" +msgstr "✓" + +msgid "Improvement to a new (unreleased) feature" +msgstr "改进新(未发布)功能" + +msgid "✗" +msgstr "✗" + +msgid "Bugfix to a new (unreleased) feature" +msgstr "新(未发布)功能的 Bug 修复" + +msgid "Bugfix to an old (released) feature" +msgstr "旧(已发布)功能的 Bug 修复" + +msgid "Completely minor change (e.g. a typo fix)" +msgstr "完全微小的更改(例如拼写错误)" + +msgid "If editing this table - also edit the PR template." +msgstr "编辑此表格时一并编辑 PR 模版。" + +msgid "" +"The checks for changelog, docs and credits are skipped individually when " +"a maintainer applies the matching label - `Skip Changelog`, `Skip Docs` " +"or `Skip Credits`. If one of the requirements does not apply to your " +"change, say so in the pull request and ask for the corresponding label." +msgstr "" +"当维护者使用对应标签 `Skip Changelog`、`Skip Docs` 或 `Skip Credits` 时版本更新说明、文档和鸣谢各自的检查将被跳过。如果某项要求不适用于你的更改,请在拉取请求中说明并请求对应的标签。" msgid "" "Every pull request push trigger a nightly build for the latest pushed " diff --git a/docs/locale/zh_CN/LC_MESSAGES/General-Info.po b/docs/locale/zh_CN/LC_MESSAGES/General-Info.po index 58cb6ef004..95d60ca018 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/General-Info.po +++ b/docs/locale/zh_CN/LC_MESSAGES/General-Info.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-25 14:06+0800\n" +"POT-Creation-Date: 2026-08-04 10:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -33,17 +33,17 @@ msgid "" "*stable builds* - those are numbered like your regular versions " "(something close to semantic versioning, e.g. version 1.2.3 for example) " "and ideally should contain no bugs, therefore are safe to use in mods;" -msgstr "**稳定版**:采用常规版本号(例如 v1.2.3),并且理论上不包含任何此前已知的 Bug,可以安全地用于 Mod。" +msgstr "**稳定版本**:采用常规版本号(例如 v1.2.3),并且理论上不包含任何此前已知的 Bug,可以安全地用于 Mod。" msgid "" -"*development builds* - those are the builds which contain functionality " -"that needs to be tested. They are numbered plainly starting from 0 and " -"incrementing the number on each release. Mod authors still can include " -"those versions with their mods if they want latest features, though we " -"can't guarantee lack of bugs;" +"*pre-release builds* - previously known as *development builds*, these " +"builds mark the start of a new release branch and are used for testing " +"new features before they are finalized. They are numbered with a version " +"and a pre-release suffix (e.g., 0.5-beta1, 0.5-rc2). Mod authors can " +"include these versions with their mods to access the latest features, but" +" we cannot guarantee the absence of bugs;" msgstr "" -"**开发版**:包含待测试功能的版本,采用 `build-` 加上从 `0` 开始且每次发布后递增的简单数字编号作为版本号。Mod 作者仍可在 " -"Mod 中使用这类版本以获得最新功能,但我们无法保证它们没有 Bug。" +"**预发布版本**:即之前的 *开发版*,此类构建标志着新发布分支的开始,用于在新功能最终确定前进行测试。它们的版本号由主版本号加上预发布后缀组成(例如 `0.5-beta1`、`0.5-rc2`)。Mod 作者可以在 Mod 中包含这些版本以获取最新功能,但我们无法保证它们没有 Bug。" msgid "" "*nightly builds* - bleeding edge versions which can include prototypes, " @@ -55,33 +55,47 @@ msgstr "" "**自动构建版本**:最前沿版本,可能包含原型机、理论验证、废弃功能等。换而言之我们无法保证这些版本的稳定性,它们仅应当用于协助 Phobos " "开发和测试而绝不应该用于 Mod 的发布版本。" +msgid "" +"Besides the published builds above, any build you make locally (from " +"Visual Studio, VS Code or the build scripts) is marked as a *local " +"build*: like nightly, it is stamped with the git commit and ref it was " +"built from (plus a `-dirty` marker when the working tree has uncommitted " +"changes) and shows a hideable \"please test\" warning." +msgstr "" +"除了上述发布的构建版本,你在本地(通过 Visual Studio、VS Code 或构建脚本)生成的任何构建都会标记为 *本地构建*:与自动构建版本类似,它会标记有构建所基于的 git 提交和引用(当工作树有未提交更改时还会添加 `-dirty` 标记),并显示一个可隐藏的「please test」警告。" + msgid "" "You can find the downloads for these versions on the document's [main " "page](index.md#downloads)." msgstr "你可以在文档 [主页面](index.md#downloads) 找到这些版本的下载。" -msgid "Disabling development build warning" -msgstr "关闭开发版本警告" +msgid "Disabling pre-release build warning" +msgstr "关闭预发布版本警告" msgid "" "**DISCLAIMER:** We understand that everyone wants to try and use the new " "features as soon as they're released, but we can't do all the testing " "ourselves, so we only test the functionality on a basic level. We ask " -"everyone who uses the new development build first to **test the new " -"changes in every possible way first before disabling the development " +"everyone who uses the new pre-release build first to **test the new " +"changes in every possible way first before disabling the pre-release " "build warning** and proceeding to include the build in your mod release. " "This would allow us to concentrate on implementing the actual features, " "which is the most complex task. Learn more on testing " "[here](Contributing.md#testing)." msgstr "" -"**免责声明**:我们理解每个人都希望尽快体验新功能的愿望,但我们仅可进行基础的功能验证而无法完成全面的测试。我们要求所有用户在关闭开发版本警告前务必通过各种方式全面测试变更内容,然后再将开发版本使用于" +"**免责声明**:我们理解每个人都希望尽快体验新功能的愿望,但我们仅可进行基础的功能验证而无法完成全面的测试。我们要求所有用户在关闭预发布版本警告前务必通过各种方式全面测试变更内容,然后再将预发布版本使用于" " Mod 的发布版本中。这将使我们能够专注于实现最复杂的核心功能。测试指南见 [此处](Contributing.md#testing)。" msgid "" -"You can hide the warning by specifying the build number after `-b=` as a " -"command line argument (for example, `-b=1` would hide the warning for " -"development build #1 of Phobos)." -msgstr "你可以通过在命令行参数 `-b=` 后面指定开发版本的版本号来隐藏开发版本警告(例如 `-b=1` 可以隐藏 Build#1 开发版本的警告)。" +"You can hide the warning by specifying the exact version of the build you" +" use after `-HideVersionWarning=` as a command line argument (for " +"example, `-HideVersionWarning=0.5.0.0-beta1` would hide the warning for " +"the `v0.5-beta1` pre-release of Phobos). The version is shown in the " +"warning itself and in the title of the respective release; a build only " +"accepts its own version, so the switch has to be updated whenever you " +"update Phobos. Nightly builds don't support hiding the warning at all." +msgstr "" +"你可以通过在命令行参数 `-HideVersionWarning=` 后面指定所使用的构建的确切版本号来隐藏该警告(例如 `-HideVersionWarning=0.5.0.0-beta1` 将隐藏 Phobos `v0.5-beta1` 预发布版的警告)。该版本号会显示在警告信息以及对应发布的标题中;每个构建只接受其自身的版本号,因此每当更新 Phobos 时都需要同步更新该开关参数。自动构建版完全不支持隐藏警告。" msgid "Saved games filtering" msgstr "存档过滤" @@ -91,15 +105,13 @@ msgid "" "publicly released Ares 0.A source and it implements it's own filtering " "which shouldn't conflict with Ares save filtering. Save games between " "different versions are incompatible due to changes to Phobos extension " -"classes which are present in almost every build release. The filtering " -"mechanism, hovewer, doesn't apply to nightly versions - those use latest " -"development build number on which this nightly is based on. While " -"different nightly version saves may be listed, they are most likely " -"incompatible in case there were changes to extension class fields." +"classes which are present in almost every build release. Nightlies and " +"pre-releases of a version share the savegame ID of the stable release " +"they lead up to, so their saves are filtered as a single version. They " +"may still be incompatible with each other if there were changes to " +"extension class fields." msgstr "" -"得益于 Ares0.A 公开的源码范例,Phobos 完全支持存读档功能,并且实现了独立的过滤机制,这与 Ares 的存档过滤机制没有冲突。由于 " -"Phobos " -"扩展类几乎在每个版本都有所变动,所以不同版本间的存档互不兼容。但过滤机制不适用于自动构建版本——这些版本使用其所基于的最新开发版本的编号。虽然不同自动构建版本的存档可能显示在存读档列表中,但如果扩展类字段发生了变动,那么这些存档很可能实际上并不兼容。" +"得益于 Ares0.A 公开的源码范例,Phobos 完全支持存读档功能,并且实现了独立的过滤机制,这与 Ares 的存档过滤机制没有冲突。由于 Phobos 扩展类几乎在每个版本都有所变动,所以不同版本间的存档互不兼容。某个版本的自动构建版本和预发布版本共享其所对应稳定版本的存档 ID,因此它们的存档会被视为同一版本进行过滤。但如果扩展类字段发生了更改,它们之间仍然可能互不兼容。" msgid "Compatibility" msgstr "兼容性" @@ -111,7 +123,9 @@ msgid "" "versions do not provide, and will show an error and exit on startup " "without them." msgstr "" -"Phobos 需要 [SyringeEx](https://github.com/Phobos-developers/SyringeEx)(v0.1.0.2 及以上版本)作为启动器——它依赖于旧版 Syringe 所不具备的功能(通过 SyringeEx 特性标志发出信号),如果没有这些功能,它将在启动时显示错误并退出。" +"Phobos 需要 [SyringeEx](https://github.com/Phobos-" +"developers/SyringeEx)(v0.1.0.2 及以上版本)作为启动器——它依赖于旧版 Syringe 所不具备的功能(通过 " +"SyringeEx 特性标志发出信号),如果没有这些功能,它将在启动时显示错误并退出。" msgid "" "While Phobos is standalone, it is designed to be used alongside " diff --git a/docs/locale/zh_CN/LC_MESSAGES/Miscellanous.po b/docs/locale/zh_CN/LC_MESSAGES/Miscellanous.po index c6e2bfec39..755923568d 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Miscellanous.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Miscellanous.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-25 14:06+0800\n" +"POT-Creation-Date: 2026-08-04 10:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -476,7 +476,9 @@ msgid "" "ported from [Vinifera](https://github.com/Vinifera-Developers/Vinifera). " "When the game crashes, it will:" msgstr "" -"移植自 [Vinifera](https://github.com/Vinifera-Developers/Vinifera):当异常处理程序启用时,Phobos 会使用自身的异常处理程序取代游戏(及 Ares)自带的崩溃处理。当游戏崩溃时,它会:" +"移植自 [Vinifera](https://github.com/Vinifera-" +"Developers/Vinifera):当异常处理程序启用时,Phobos 会使用自身的异常处理程序取代游戏(及 " +"Ares)自带的崩溃处理。当游戏崩溃时,它会:" msgid "" "create a per-crash folder `debug\\snapshot-<timestamp>\\` and write into " @@ -486,7 +488,10 @@ msgid "" "layout Ares uses, so the CnCNet client will also fold `debug.log` and " "`syringe.log` into it;" msgstr "" -"创建一个按崩溃时间命名的目录 `debug\\snapshot-<时间戳>\\` 并在里面写入一份包含寄存器、调用栈、栈转储和已加载模块列表的详细崩溃报告(`except.txt`)、一个小型转储文件(`crashdump.dmp`)以及一份 `debug.log` 副本(如果找得到)。该目录布局与 Ares 的相同,因此 CnCNet 客户端也会将 `debug.log` 和 `syringe.log` 置入其中。" +"创建一个按崩溃时间命名的目录 `debug\\snapshot-<时间戳>\\` " +"并在里面写入一份包含寄存器、调用栈、栈转储和已加载模块列表的详细崩溃报告(`except.txt`)、一个小型转储文件(`crashdump.dmp`)以及一份" +" `debug.log` 副本(如果找得到)。该目录布局与 Ares 的相同,因此 CnCNet 客户端也会将 `debug.log` 和 " +"`syringe.log` 置入其中。" msgid "" "show a dialog displaying the crash report, with buttons to quit, break " @@ -494,26 +499,28 @@ msgid "" "(`fulldump.dmp`, into the same folder) - large, but the most useful to " "developers." msgstr "" -"显示一个包含崩溃报告的对话框,提供退出、附加调试器中断或额外保存完整内存转储(同目录下名为 `fulldump.dmp`)的按钮。`fulldump.dmp` 文件较大,但对开发人员来说是最有用的。" +"显示一个包含崩溃报告的对话框,提供退出、附加调试器中断或额外保存完整内存转储(同目录下名为 " +"`fulldump.dmp`)的按钮。`fulldump.dmp` 文件较大,但对开发人员来说是最有用的。" msgid "" "Crash folders older than 5 days are cleaned up automatically. The " "`-FullCrashDump` command line arg makes the automatically written " "minidump a full memory dump (useful for unattended setups)." msgstr "" -"超过 5 天的崩溃目录会自动清理。命令函参数 `-FullCrashDump` 可自动将完整内存转储写入小型转储中(适用于无需人工操作的自动运行环境)。" +"超过 5 天的崩溃目录会自动清理。命令函参数 `-FullCrashDump` " +"可自动将完整内存转储写入小型转储中(适用于无需人工操作的自动运行环境)。" msgid "Crash reports can be enriched with extra information:" -msgstr "" -"崩溃报告可以附加额外信息:" +msgstr "崩溃报告可以附加额外信息:" msgid "" "If a `gamemd.pdb` file is present in the game directory, it is used to " "resolve game addresses to symbol names in the call stacks. `Phobos.pdb` " -"(shipped with devbuilds and nightlies) is picked up automatically for " -"Phobos's own addresses." +"(shipped with every build) is picked up automatically for Phobos's own " +"addresses." msgstr "" -"如果游戏目录存在 `gamemd.pdb` 文件,它会被用于游戏地址解析调用栈中的符号名称。`Phobos.pdb`(开发构建和自动构建自带)会自动用于解析 Phobos 自身的地址。" +"如果游戏目录存在 `gamemd.pdb` " +"文件,它会被用于游戏地址解析调用栈中的符号名称。`Phobos.pdb`(每个构建自带)会自动用于解析 Phobos 自身的地址。" msgid "" "If a `gamemd.edb` exception database file is present in the game " @@ -521,7 +528,8 @@ msgid "" "in the report under \"Additional information\". The format is shared with" " Vinifera: one entry per line, `;` starts a comment." msgstr "" -"如果游戏目录存在 `gamemd.edb` 异常数据库文件,其中对故障地址的描述(若存在)会包含在报告的“Additional information”下。该格式与 Vinifera 共享:每行一个条目,`;` 代表注释开始。" +"如果游戏目录存在 `gamemd.edb` 异常数据库文件,其中对故障地址的描述(若存在)会包含在报告的“Additional " +"information”下。该格式与 Vinifera 共享:每行一个条目,`;` 代表注释开始。" msgid "" "The exception handler is enabled by default in all builds, including " @@ -529,7 +537,8 @@ msgid "" "this does not interfere with debugging; pass `-ExceptionHandler=false` if" " you want crashes to bypass the handler entirely." msgstr "" -"异常处理程序在包括 Debug 版本的所有构建版本中默认启用。附加的调试器仍然优先接收异常,因此这不会干扰调试;你可以通过 `-ExceptionHandler=false` 使崩溃完全绕过该处理程序" +"异常处理程序在包括 Debug 版本的所有构建版本中默认启用。附加的调试器仍然优先接收异常,因此这不会干扰调试;你可以通过 " +"`-ExceptionHandler=false` 使崩溃完全绕过该处理程序" msgid "" "The CnCNet 5 spawner uses the main loop exception handler for fixes. If " @@ -547,7 +556,8 @@ msgid "" "the process, so windows created by the game and Phobos - dialogs, message" " boxes and the crash dialog - render with modern visual styles." msgstr "" -"gamemd.exe 在发布时没有应用程序清单,因此其窗口绑定旧的通用控件 v5 并以 Windows 9x 样式渲染。现在 Phobos 在进程生命周期内激活其内嵌的通用控件 v6 清单,以使游戏和 Phobos 创建的对话框、消息框和崩溃对话框使用现代视觉样式渲染。" +"gamemd.exe 在发布时没有应用程序清单,因此其窗口绑定旧的通用控件 v5 并以 Windows 9x 样式渲染。现在 Phobos " +"在进程生命周期内激活其内嵌的通用控件 v6 清单,以使游戏和 Phobos 创建的对话框、消息框和崩溃对话框使用现代视觉样式渲染。" msgid "Player colors" msgstr "玩家颜色" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Project-guidelines-and-policies.po b/docs/locale/zh_CN/LC_MESSAGES/Project-guidelines-and-policies.po index 630bbe4238..ec6e6275ab 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Project-guidelines-and-policies.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Project-guidelines-and-policies.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-19 04:52+0800\n" +"POT-Creation-Date: 2026-08-04 10:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -314,10 +314,10 @@ msgid "" "a separate folder named after vanilla engine class name and contains the " "following:" msgstr "" -"`Ext/` - 原版引擎类的扩展类源码。扩展类形成了一个镜像了游戏自身类树的并行继承层次结构" -"(`Container.h` 中的 `AbstractExt` 为根;例如`BuildingExt : TechnoExt : RadioExt : MissionExt : ObjectExt : AbstractExt`)," -"并且每个游戏对象都恰好携带一个最派生匹配类型的扩展实例,该实例缓存在对象内部统一的 `AbstractClass` `0x18` 槽位中。" -"每个类扩展都保存在一个以原版引擎类名称命名的单独文件夹中,并包含以下内容:" +"`Ext/` - 原版引擎类的扩展类源码。扩展类形成了一个镜像了游戏自身类树的并行继承层次结构(`Container.h` 中的 " +"`AbstractExt` 为根;例如`BuildingExt : TechnoExt : RadioExt : MissionExt : " +"ObjectExt : AbstractExt`),并且每个游戏对象都恰好携带一个最派生匹配类型的扩展实例,该实例缓存在对象内部统一的 " +"`AbstractClass` `0x18` 槽位中。每个类扩展都保存在一个以原版引擎类名称命名的单独文件夹中,并包含以下内容:" msgid "" "`Body.h` and `Body.cpp` contain class and method definitions/declarations" @@ -330,7 +330,8 @@ msgid "" "`LoadFromINIFile`/`SaveToStream`/`LoadFromStream` overrides, plus static " "helper methods;" msgstr "" -"新的数据成员,以及(对于适当的类)`LoadFromINIFile`/`SaveToStream`/`LoadFromStream` 的重写,外加静态辅助方法;" +"新的数据成员,以及(对于适当的类)`LoadFromINIFile`/`SaveToStream`/`LoadFromStream` " +"的重写,外加静态辅助方法;" msgid "" "`ExtContainer`/`ExtMap` - the per-class container (inherits " @@ -346,7 +347,12 @@ msgid "" "itself so code written against the old nested data classes keeps " "compiling;" msgstr "" -"`ExtContainer`/`ExtMap` —— 每个类的容器(继承自 `Container.h` 中的 `Container<T>`),用于跟踪一个具体扩展类的所有活动实例,以进行批量操作(分配/移除、集中式存档流式处理、加载后重链接、场景清除);只有具体的叶子类(例如 `BuildingExt`)才有容器,而层次结构中的每一级都提供了类型化的 `Fetch`/`TryFetch`。容器自身的 `Find`/`TryFind` 查找已弃用,仅为了向前兼容(`TechnoExt`/`TechnoTypeExt`,其容器被拆分为每个叶子类一个,出于同样的原因保留了一个仅用于查找的 `ExtMap` 占位符),并且每个重做前的类都带有一个已弃用的 `ExtData` 别名,这样针对旧嵌套数据类编写的代码仍可编译;" +"`ExtContainer`/`ExtMap` —— 每个类的容器(继承自 `Container.h` 中的 " +"`Container<T>`),用于跟踪一个具体扩展类的所有活动实例,以进行批量操作(分配/移除、集中式存档流式处理、加载后重链接、场景清除);只有具体的叶子类(例如" +" `BuildingExt`)才有容器,而层次结构中的每一级都提供了类型化的 `Fetch`/`TryFetch`。容器自身的 " +"`Find`/`TryFind` " +"查找已弃用,仅为了向前兼容(`TechnoExt`/`TechnoTypeExt`,其容器被拆分为每个叶子类一个,出于同样的原因保留了一个仅用于查找的" +" `ExtMap` 占位符),并且每个重做前的类都带有一个已弃用的 `ExtData` 别名,这样针对旧嵌套数据类编写的代码仍可编译;" msgid "" "`Fetch`/`TryFetch` statics - the O(1) lookup used at call sites " @@ -354,7 +360,9 @@ msgid "" "extension, `TryFetch` returns null instead (also the right choice while a" " savegame is loading);" msgstr "" -"`Fetch`/`TryFetch` 静态方法 —— 在调用点使用的 O(1) 查找(`TechnoExt::Fetch(pThis)`);如果对象没有携带扩展,`Fetch` 会触发致命错误,而 `TryFetch` 则返回空(在存档加载期间也是正确的选择);" +"`Fetch`/`TryFetch` 静态方法 —— 在调用点使用的 O(1) " +"查找(`TechnoExt::Fetch(pThis)`);如果对象没有携带扩展,`Fetch` 会触发致命错误,而 `TryFetch` " +"则返回空(在存档加载期间也是正确的选择);" msgid "" "constructor/destructor and (for appropriate classes) INI reading hooks. " @@ -365,14 +373,18 @@ msgid "" "inline within each cell's own savegame block instead (see " "`Ext/Cell/Body.cpp`)." msgstr "" -"构造函数/析构函数,以及(对于适当的类)INI 读取钩子。序列化集中在 `Phobos.Ext.cpp` 中 —— 没有每个类的存档钩子。唯一的例外是 `CellExt`:游戏将单元格视为值对象,其身份不稳定(它会就地重新初始化它们并进行整体复制),因此单元格扩展被内联地持久化在每个单元格自身的存档块中(参见 `Ext/Cell/Body.cpp`)。" +"构造函数/析构函数,以及(对于适当的类)INI 读取钩子。序列化集中在 `Phobos.Ext.cpp` 中 —— " +"没有每个类的存档钩子。唯一的例外是 " +"`CellExt`:游戏将单元格视为值对象,其身份不稳定(它会就地重新初始化它们并进行整体复制),因此单元格扩展被内联地持久化在每个单元格自身的存档块中(参见" +" `Ext/Cell/Body.cpp`)。" msgid "" "Extensions subscribe to pointer invalidation by inheriting " "`Detach::Listener<T>` from `Utilities/Detach.h` and overriding `OnDetach`" " (see `HouseExt` for an example)." msgstr "" -"扩展通过从 `Utilities/Detach.h` 继承 `Detach::Listener<T>` 并重写 `OnDetach` 来订阅指针失效通知(示例见 `HouseExt`)。" +"扩展通过从 `Utilities/Detach.h` 继承 `Detach::Listener<T>` 并重写 `OnDetach` " +"来订阅指针失效通知(示例见 `HouseExt`)。" msgid "" "`Hooks.cpp` and `Hooks.*.cpp` contain non-common hooks to correctly patch" @@ -578,67 +590,205 @@ msgstr "若能使用等效的 `constexpr` 或 `__forceinline` 函数替代就不 msgid "The styleguide is not exhaustive and may be adjusted in the future." msgstr "样式指南并不详尽,未来可能会进行调整。" -msgid "Git branching model" -msgstr "Git 分支模型" +msgid "Git branching model / Version lifecycle and release strategy" +msgstr "Git 分支模型/版本生命周期与发布策略" msgid "" -"Couple of notes regarding the Git practices. We use [git-" -"flow](https://nvie.com/posts/a-successful-git-branching-model/)-like " -"workflow:" -msgstr "" -"我们使用 [git-flow](https://nvie.com/posts/a-successful-git-branching-model/)" -" 之类的工作流:" +"Starting from version 0.5, Phobos adopts a new release strategy to enable" +" faster and more frequent releases." +msgstr "从 0.5 版本开始,Phobos 采用新的发布策略以实现更快速、更频繁的版本发布。" msgid "" -"`master` is for stable releases, can have hotfixes pushed to it or " -"branched off like a feature branch with the requirement of version " -"increment and `master` being merged into `develop` after that;" -msgstr "" -"`master` 用于稳定版本发布,允许直接推送热修复补丁或像功能分支一样分离分支,但需遵循版本号递增要求,并在之后将 `master` " -"分支合并回 `develop` 分支;" +"![Versioning scheme from 0.5 onward](_static/images/versioning-" +"past-0.5.excalidraw.png)" +msgstr "![0.5 之后的版本管理方案](_static/images/versioning-past-0.5.excalidraw.png)" -msgid "`develop` is the main development branch;" -msgstr "`develop` 是主要开发分支;" +msgid "Versioning scheme from 0.5 onward" +msgstr "0.5 之后的版本管理方案" + +msgid "*Image editable in [Excalidraw](https://excalidraw.com)*" +msgstr "*图片可在 [Excalidraw](https://excalidraw.com) 中编辑*" msgid "" -"`feature/`-prefixed branches (sometimes the prefix may be different if " -"appropriate, like for big fixes or changes) are so called \"feature " -"branches\" - those are branched off `develop` for every new feature to be" -" introduced into it and then merged back. We use squash merge to merge " -"them back in case of smaller branches and sometimes merge commit in case " -"the branch is so big it would be viable to keep it as is." -msgstr "" -"带有 `feature/` 前缀的分支(有时根据实际情况可能使用不同的前缀,例如大的修复或改动)即所谓的「功能分支」——这些分支从 " -"`develop` 分离出来用于引入新的功能,完成后合并回 " -"`develop`。对于小型分支,我们使用压缩合并;若分支规模较大,则可能使用合并提交来维持提交记录的完整性;" +"All changes are to be made **exclusively** to `develop` as the source of " +"truth, and then cherry-picked to other branches!" +msgstr "所有更改 **必须** 首先提交到作为唯一真实源的 `develop` 分支,然后再拣选到其他分支!" + +msgid "A brief summary compared to old style:" +msgstr "与旧方式的简要对比:" msgid "" -"`hotfix/`-prefixed branches may be used in a same manner as `feature/`, " -"but with `master` branch, with a requirement of `master` being merged " -"into `develop` after `hotfix/` branch was squash merged into `master`." -msgstr "" -"带有 `hotfix/` 前缀的分支使用方式类似于 `feature/`,但基于 `master` 分支创建,要求将 `hotfix/` " -"分支压缩合并到 `master` 后必须将 `master` 合并回 `develop`;" +"devbuilds are now called pre-releases (alpha, beta, RC etc.) and are " +"almost a proper version with docs, all changes tracked in a special " +"changelog subsection, released on the same cadence;" +msgstr "开发版(devbuilds)现在称为预发布版本(alpha、beta、RC 等),几乎是一个完整的版本,包含文档,所有变更记录在专门的更新日志小节中,以相同的节奏发布;" msgid "" -"`release/`-prefixed branches are branched off `develop` when a new stable" -" release is slated to allow working on features for a next release and " -"stability improvements for this release. Those are merged with a merge " -"commit into `master` and `develop` with a stable version number increase," -" after which the stable version is released." -msgstr "" -"带有 `release/` 前缀的分支在计划发布新的稳定版本时从 `develop` " -"分离,允许同时开发下个版本的功能和改进当前版本的稳定性。这些分支通过合并提交合并到 `master` 和 " -"`develop`,并递增稳定版本的版本号,随后发布稳定版本。" +"each pre-release (new devbuild) materializes a version branch, bugfix " +"followups committed to develop get ported to it;" +msgstr "每个预发布版本(新开发版)都会生成一个版本分支,提交到 `develop` 分支的后续修复会被移植到该分支;" + +msgid "when enough bugs are fixed - a stable version is created;" +msgstr "当足够多的 Bug 被修复后,就会创建稳定版本;" msgid "" -"When you're working with your local & remote branches use **fast-" -"forward** pulls to get the changes from remote branch to local, **don't " -"merge remote branch into local and vice versa**, this creates junk " -"commits and makes things unsquashable." -msgstr "" -"当你在处理本地与远程分支时应当使用 **拉取(快进)** " -"将远程分支的变更同步到本地,**不要将远程分支合并到本地分支**,反之亦然,否则会产生垃圾提交并使代码无法压缩整理。" +"if some critical change that warrants a version bump needs to be applied " +"(e.g. forgot to serialize a field, or fixed a critical bug from a feature" +" released before upcoming version) - we introduce a new version (with " +"docs, version change etc.) on the same branch;" +msgstr "如果需要应用某些需要版本号递增的关键更改(例如忘记序列化某个字段,或修复了即将发布的版本之前某个功能中的关键 Bug),我们会在同一分支上发布一个新版本(包含文档、版本号变更等);" + +msgid "" +"the new stable release is not the old stable: after 0.5 a stable version " +"is \"a devbuild with enough bug fixes\", released on a faster cadence " +"with less time spent per version." +msgstr "新的稳定版本不再是旧式稳定版本:0.5 之后,稳定版本就是“修复了足够多 Bug 的开发版”,以更快的节奏发布,每个版本花费的时间更少。" + +msgid "The lifecycle of a version is as follows:" +msgstr "一个版本的生命周期如下:" + +msgid "" +"**Development phase**: New features and changes are committed to the " +"`develop` branch. `develop` always carries the version it is working " +"towards: as soon as a release branch is cut, `VERSION_MINOR` (or " +"`VERSION_MAJOR`) in `src/Phobos.version.h` is bumped and " +"`VERSION_REVISION`/`VERSION_PATCH` are reset to 0, so that nightlies are " +"stamped with the version they lead up to instead of one that has already " +"been released." +msgstr "**开发阶段**:新功能和变更提交到 `develop` 分支。`develop` 始终带有其正在开发的目标版本号:一旦创建发布分支,`src/Phobos.version.h` 中的 `VERSION_MINOR`(或 `VERSION_MAJOR`)就会递增,而 `VERSION_REVISION`/`VERSION_PATCH` 则重置为 0,这样自动构建版本所标记的版本号就是它们所对应的目标版本,而不是已经发布的版本。" + +msgid "" +"**Pre-release phase**: When enough features have accumulated on " +"`develop`, a pre-release build (e.g., `v0.5-beta1`) is created. This " +"build marks the start of a new *release branch* (e.g., `release/v0.5`) " +"and signifies that active feature development for version 0.5 is " +"complete. This branch will be used for all subsequent testing and the " +"final stable release." +msgstr "**预发布阶段**:当 `develop` 上积累了足够多的功能后,会创建一个预发布构建(例如 `v0.5-beta1`)。该构建标志着新 *发布分支*(例如 `release/v0.5`)的起始,并表明 0.5 版本的功能开发已告完成。该分支将用于后续所有测试以及最终的稳定版本发布。" + +msgid "" +"During this phase, multiple pre-release builds (which can be called beta," +" alpha, or release candidate) may be published for wider testing. Between" +" pre-releases on the same version number, there shall be no changes that " +"warrant a stable version changelog addition; in other words — only bug " +"fixes, minor additions, and polish to the existing new version feature " +"set are allowed." +msgstr "在此阶段,可能会发布多个预发布构建(可称为 beta、alpha 或发布候选版本)以供广泛测试。在同一版本号的预发布之间,不应存在需要在稳定版本更新日志中记录的变更;换言之——只允许进行 Bug 修复、小幅增补和对现有新功能集的打磨。" + +msgid "" +"If there's an urgent need to introduce a feature that would warrant new " +"changelog addition on the same branch - it is allowed to **reset the pre-" +"release prefix and increment the appropriate version number**, while also" +" creating the corresponding doc changelog section." +msgstr "如果有紧急需求需要引入一个需要在同一分支上新增更新日志记录的功能——允许 **重置预发布前缀并递增相应的版本号**,同时还需要创建对应的文档更新日志章节。" + +msgid "" +"**Stable release**: When the pre-release builds are deemed stable enough," +" a stable release (e.g., `v0.5`) is published from the release's branch." +msgstr "**稳定发布**:当预发布构建被认为足够稳定时,会从该发布分支发布稳定版本(例如 `v0.5`)。" + +msgid "" +"**Maintenance phase**: After the stable release, the release branch " +"enters maintenance mode, where only bug fixes are applied, resulting in " +"patch releases (e.g., `v0.5.0.1`, `v0.5.0.2`)." +msgstr "**维护阶段**:稳定版本发布后,该发布分支进入维护模式,只进行 Bug 修复,并产生补丁版本(例如 `v0.5.0.1`、`v0.5.0.2`)。" + +msgid "" +"**End of maintenance**: When a new stable release is published (e.g., " +"`v0.6`), the previous minor version branch (e.g., `v0.5.x.y`) is " +"officially deprecated and enters end-of-life, ceasing to receive any " +"further updates, including bug fixes. Concurrently, the new stable " +"release (e.g., `v0.6`) enters its own maintenance phase, and a new " +"release branch for the next version (e.g., `release/v0.7`) may already " +"have been created from the `develop` branch, initiating its pre-release " +"cycle." +msgstr "**维护结束**:当发布新的稳定版本(例如 `v0.6`)时,之前的次版本分支(例如 `v0.5.x.y`)将被正式弃用并进入生命周期终止状态,不再接收任何进一步更新(包括 Bug 修复)。与此同时,新的稳定版本(例如 `v0.6`)进入其自身的维护阶段,而下一个版本的新发布分支(例如 `release/v0.7`)可能已经从 `develop` 分支创建,并开始其预发布周期。" + +msgid "" +"If needed, a new release branch may be started even before the previous " +"one has had a stable release. Doing so will temporarily increase the " +"burden of upkeeping multiple branches, so do it only when there's a valid" +" reason for such." +msgstr "如果需要,即使上一个发布分支尚未发布稳定版本,也可以启动新的发布分支。但这样做会暂时增加维护多个分支的负担,因此仅在有充分理由时才应如此操作。" + +msgid "" +"The `master` branch is deprecated; all development occurs in `develop`, " +"and each version branches off from it." +msgstr "`master` 分支已弃用;所有开发均在 `develop` 中进行,每个版本都从该分支分离。" + +msgid "" +"**`develop` is the source of truth! Always apply your changes to " +"`develop` first, then cherry-pick them onto the correct branch!**" +msgstr "**`develop` 是唯一真实源!请始终先将更改应用到 `develop`,然后再拣选到正确的分支!**" + +msgid "How to publish a release" +msgstr "如何发布版本" + +msgid "" +"Publishing a release is done from a release branch (see the lifecycle " +"above). The steps are:" +msgstr "版本发布需从发布分支进行(参见上述生命周期)。步骤如下:" + +msgid "" +"**Set the version** in `src/Phobos.version.h`. When a release branch is " +"cut, bump `VERSION_MINOR` (or `VERSION_MAJOR`) and reset " +"`VERSION_REVISION` and `VERSION_PATCH` to 0; patch releases only bump " +"`VERSION_PATCH`." +msgstr "**设置版本号**:在 `src/Phobos.version.h` 中设置版本号。创建发布分支时,递增 `VERSION_MINOR`(或 `VERSION_MAJOR`),并将 `VERSION_REVISION` 和 `VERSION_PATCH` 重置为 0;补丁版本仅递增 `VERSION_PATCH`。" + +msgid "" +"**Decide whether it is a pre-release or a stable release.** The pre-" +"release suffix is the knob: as long as `PRERELEASE_SUFFIX` is defined " +"(e.g. `#define PRERELEASE_SUFFIX \"beta1\"`), a release build is a pre-" +"release; remove the define entirely for a stable release. The suffix can " +"be anything semantic versioning allows (e.g. `alpha5`, `beta1`, `rc3`)." +msgstr "**决定是预发布还是稳定版本**:预发布后缀即控制开关:只要定义了 `PRERELEASE_SUFFIX`(例如 `#define PRERELEASE_SUFFIX \"beta1\"`),该发布构建即为预发布版本;若要发布稳定版本,则完全移除该定义。后缀可以是语义化版本允许的任何内容(例如 `alpha5`、`beta1`、`rc3`)。" + +msgid "" +"**Create a GitHub release and tag** using the short user-facing version " +"you've set in steps 1 and 2 (e.g. `v0.5-alpha1`). The `release.yml` " +"workflow builds the DLL with `BuildType=RELEASE`;" +msgstr "**创建 GitHub 发布和标签**:使用你在步骤 1 和 2 中设置的面向用户的简短版本号(例如 `v0.5-alpha1`)。`release.yml` 工作流会以 `BuildType=RELEASE` 构建 DLL;" + +msgid "" +"The changelog is extracted from `docs/Whats-New.md` automatically. **Do " +"not write the changelog yourself!** It will be appended to the text you " +"wrote after you publish the release. Also **do not use GitHub's " +"\"Generate release notes\" button!** It can't be configured to provide " +"correct output." +msgstr "更新日志会自动从 `docs/Whats-New.md` 提取。**请勿手动编写更新日志!** 它会在你发布版本后附加到你编写的文本之后。另外 **不要使用 GitHub 的「生成发布说明」按钮!** 它无法配置以提供正确的输出。" + +msgid "" +"The build is built and attached automatically. **Do not build Phobos " +"releases manually!**" +msgstr "构建会自动编译并附加。**请勿手动构建 Phobos 发布版本!**" + +msgid "" +"**GitHub \"pre-release\" checkbox doesn't affect the produced build " +"type**, it only affects the release's display status for GitHub." +msgstr "**GitHub 的“预发布”复选框不会影响生成的构建类型**,它仅影响该发布在 GitHub 上的显示状态。" + +msgid "" +"**Verify the artifacts** once the build finishes and the artifacts are " +"attached, then announce the release." +msgstr "**验证构建产物**:构建完成且产物附加后,再宣布发布。" + +msgid "" +"The release tag and name use the short user-facing version (e.g. " +"`v0.5-alpha1`); the DLL reports the full version with trailing zeros " +"(e.g. `0.5.0.0-alpha1`) internally, so that is what appears in the file " +"properties and what the `-HideVersionWarning` switch expects." +msgstr "发布标签和名称使用面向用户的简短版本号(例如 `v0.5-alpha1`);而 DLL 内部报告的是包含尾随零的完整版本号(例如 `0.5.0.0-alpha1`),因此文件属性中显示的是此完整版本号,也是 `-HideVersionWarning` 参数所期望的值。" + +msgid "" +"If you want to build a pre-release locally for testing, run " +"`scripts\\build.bat Release RELEASE` with the suffix still defined in " +"`version.h`. A plain `scripts\\build_debug.bat` or " +"`scripts\\build_release.bat` always produces a local build instead." +msgstr "如果你想在本地构建预发布版本进行测试,请在 `version.h` 中仍定义后缀的情况下运行 `scripts\\build.bat Release RELEASE`。而直接运行 `scripts\\build_debug.bat` 或 `scripts\\build_release.bat` 则始终生成本地构建。" + +msgid "Useful Git config" +msgstr "有用的 Git 配置" msgid "These commands will do the following for all repositories on your PC:" msgstr "这些命令对你电脑上的所有代码仓库执行以下操作:" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index d7a2517584..c4f4bd058a 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-02 22:22+0800\n" +"POT-Creation-Date: 2026-08-04 10:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -26,8 +26,22 @@ msgid "" "mods to accommodate." msgstr "此页面列出了 Phobos 稳定版本的历史更改以及 modder 们为适应当前版本对其 mod 所需要做的必要改动。" -msgid "Migrating" -msgstr "迁移" +msgid "Migration (breaking changes)" +msgstr "迁移(破坏性更改)" + +msgid "" +"New Phobos user? Read the \"Changes to vanilla behavior\" subsections in " +"every version below to know what you might need to adjust when installing" +" Phobos" +msgstr "" +"Phobos 新用户?请阅读以下每个版本中的“原版行为更改”小节,以了解安装 Phobos 时可能需要调整的内容。" + +msgid "" +"This serves as a changelog for when you just need to drop the new version" +" in without reading into every version you're skipping, so you can simply" +" read up on this log of breaking changes and apply whatever you might " +"need to apply." +msgstr "这是一份用于让你只需替换新版本而无需了解你所跳过的每个版本时使用的更新日志,以便你可以简单查阅这份破坏性变更日志并应用你可能需要的任何更改。" msgid "" "You can use the migration utility (can be found on [Phobos " @@ -38,8 +52,11 @@ msgstr "" "你可以使用迁移工具(可以在 [Phobos 补充资源库](https://github.com/Phobos-" "developers/PhobosSupplementaries) 中找到)通过相应的 sed 脚本文件自动应用大多数更改。" -msgid "From vanilla" -msgstr "由原版" +msgid "0.5" +msgstr "0.5" + +msgid "Changes to vanilla behavior" +msgstr "原版行为更改" msgid "" "Vehicles paradropped by AI players now default to `Hunt` mission instead " @@ -81,74 +98,6 @@ msgstr "" "使用 `Airstrike=true` 弹头的武器现在无论在哪个武器槽位都将检查目标的类别是否可被空袭。需要在作为主武器的空袭武器弹头上使用 " "`AirstrikeTargets=all` 来恢复旧有行为。" -msgid "" -"`PowerUpNAnim` is now used instead of the upgrade building's image file " -"for upgrade animation if set. Note that displaying a damaged version will" -" still require setting `PowerUpNDamagedAnim` explicitly in all cases, as " -"the fallback to upgrade building image does not extend to it, nor would " -"it be safe to add. `PowersUpToLevel=-1` upgrades still do not work " -"correctly `PowerUpNAnim` and such buildings should forgo using explicit " -"upgrade animations." -msgstr "" -"现在如果通过 `PowerUpNAnim` 设置了加载物动画那么它们将会使用替代加载物建筑自身的图像来显示。注意你仍需显式设置 " -"`PowerUpNDamagedAnim` " -"来启用伤残版本的加载物动画,因为并没有添加一个它们不存在时回退到未伤残版本加载物图像的逻辑,并且那也不是一个安全的做法。`PowersUpToLevel=-1`" -" 的加载物在使用 `PowerUpNAnim` 时仍然无法正常工作,因此这类建筑应当避免使用显式的加载物动画。" - -msgid "" -"Elite technos no longer scatter by default, behaviour can be restored by " -"including `SCATTER` in their `EliteAbilities`." -msgstr "精英单位不再默认获得分散能力,可以通过在他们的 `EliteAbilities` 中添加 `SCATTER` 来复原旧有行为。" - -msgid "" -"`[CrateRules] -> FreeMCV` now controls whether or not player is forced to" -" receive unit from `[General] -> BaseUnit` from goodie crate if they own " -"no buildings or any existing `[General] -> BaseUnit` vehicles and own " -"more than `[CrateRules] -> FreeMCV.CreditsThreshold` (defaults to 1500) " -"credits." -msgstr "" -"现在 `[CrateRules] -> FreeMCV` 可以控制玩家是否会在他们没有任何建筑物或任何 `BaseUnit` 中的载具且资金超过 " -"`[CrateRules] -> FreeMCV.CreditsThreshold`(默认为 1500)时强制获得一个 `[General] ->" -" BaseUnit` 中的单位。" - -msgid "" -"Translucent RLE SHPs will now be drawn using a more precise and " -"performant algorithm that has no green tint and banding. Can be disabled " -"with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`." -msgstr "" -"现在半透明的 RLE SHP 将使用更精确且性能更高的算法进行绘制,不会出现绿色色调和条纹。可以通过 `rulesmd.ini -> " -"[General] -> FixTransparencyBlitters=no` 来禁用。" - -msgid "" -"Iron Curtain status is now preserved by default when converting between " -"TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be " -"turned off per-TechnoType and global basis using " -"`[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`." -msgstr "" -"现在铁幕效果会默认在科技类型通过 `DeploysInto`/`UndeploysInto` 转换时保留。这一行为可以通过每个科技类型和全局设置的" -" `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no` 来关闭。" - -msgid "" -"The obsolete `[General] -> WarpIn` has been enabled for the default anim " -"type when technos are warping in. If you want to restore the vanilla " -"behavior, use the same anim type as `[General] -> WarpOut`." -msgstr "" -"原本废弃的 `[General] -> WarpIn` 已启用于科技类型传送来时的默认动画类型。如果想要恢复原版行为,请为其设置一个与 " -"`[General] -> WarpOut` 相同的动画类型。" - -msgid "" -"Vehicles with `Crusher=true` + `OmniCrusher=true` / " -"`MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles /" -" walls respectively. This now obeys `TiltsWhenCrushes` but can be " -"customized individually for these two scenarios using " -"`TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both " -"default to `TiltsWhenCrushes`." -msgstr "" -"拥有 `Crusher=true` 和 `OmniCrusher=true`/`MovementZone=CrusherAll` " -"的载具类型硬编码在其碾压载具/围墙时会掀起而倾斜。现在此行为遵循 `TiltsWhenCrushes` 设置,但可以使用 " -"`TiltsWhenCrushes.Vehicles` 和 `TiltsWhenCrushes.Overlays` " -"分别定义这两种情况,它们都默认为 `TiltsWhenCrushes`。" - msgid "" "The default direction for aircraft landing on an airfield will use the " "direction specified by `[AudioVisual] -> PoseDir=` instead of the " @@ -158,18 +107,8 @@ msgstr "" "战机降落到机场的默认方向将使用 `[AudioVisual] -> PoseDir=` 指定的方向而不再是建筑的方向,这可以通过 " "`AircraftDockingDir.DefaultToPoseDir=false` 来复原。" -msgid "Breaking changes" -msgstr "破坏性更改" - -msgid "" -"This serves as a changelog for when you just need to drop the new version" -" in without reading into every version you're skipping, so you can simply" -" read up on this log of breaking changes and apply whatever you might " -"need to apply." -msgstr "这是一份用于让你只需替换新版本而无需了解你所跳过的每个版本时使用的更新日志,以便你可以简单查阅这份破坏性变更日志并应用你可能需要的任何更改。" - -msgid "Version TBD (develop branch nightly builds)" -msgstr "待定版本(开发分支自动构建版本)" +msgid "Changes to Phobos behavior" +msgstr "Phobos 行为更改" msgid "" "`Splits.TargetCellRange` < 0 now applies special behaviour where the " @@ -384,35 +323,29 @@ msgid "" "correctly set default values. The ones where fixing this issue changes " "behaviour are (old wrong default -> new fixed default):" msgstr "" -"某些 `Interceptor` 和 `PassengerDeletion` 相关语句没有正确默认值。修复该问题导致的行为变更如下(旧有错误默认值→修复后的新默认值):" +"某些 `Interceptor` 和 `PassengerDeletion` " +"相关语句没有正确默认值。修复该问题导致的行为变更如下(旧有错误默认值→修复后的新默认值):" msgid "`Interceptor.CanTargetHouses`: `none` -> `enemies`" -msgstr "" -"`Interceptor.CanTargetHouses`:`none`→`enemies`" +msgstr "`Interceptor.CanTargetHouses`:`none`→`enemies`" msgid "`PassengerDeletion.Rate.SizeMultiply`: `false` -> `true`" -msgstr "" -"`PassengerDeletion.Rate.SizeMultiply`:`false`→`true`" +msgstr "`PassengerDeletion.Rate.SizeMultiply`:`false`→`true`" msgid "`PassengerDeletion.CostMultiplier`: `0.0` -> `1.0`" -msgstr "" -"`PassengerDeletion.CostMultiplier`:`0.0`→`1.0`" +msgstr "`PassengerDeletion.CostMultiplier`:`0.0`→`1.0`" msgid "`PassengerDeletion.AllowedHouses`: `none` -> `all`" -msgstr "" -"`PassengerDeletion.AllowedHouses`:`none`→`all`" +msgstr "`PassengerDeletion.AllowedHouses`:`none`→`all`" msgid "`PassengerDeletion.SoylentMultiplier`: `0.0` -> `1.0`" -msgstr "" -"`PassengerDeletion.SoylentMultiplier`:`0.0`→`1.0`" +msgstr "`PassengerDeletion.SoylentMultiplier`:`0.0`→`1.0`" msgid "`PassengerDeletion.SoylentAllowedHouses`: `none` -> `enemies`" -msgstr "" -"`PassengerDeletion.SoylentAllowedHouses`:`none`→`enemies`" +msgstr "`PassengerDeletion.SoylentAllowedHouses`:`none`→`enemies`" msgid "`PassengerDeletion.DisplaySoylentOffset`: `none` -> `all`" -msgstr "" -"`PassengerDeletion.DisplaySoylentOffset`:`none`→`all`" +msgstr "`PassengerDeletion.DisplaySoylentOffset`:`none`→`all`" msgid "Changes compared to inter-version builds / pre-releases" msgstr "相较于版本间构建/预发布版本的更改" @@ -444,6 +377,57 @@ msgstr "" msgid "0.4" msgstr "0.4" +msgid "" +"`PowerUpNAnim` is now used instead of the upgrade building's image file " +"for upgrade animation if set. Note that displaying a damaged version will" +" still require setting `PowerUpNDamagedAnim` explicitly in all cases, as " +"the fallback to upgrade building image does not extend to it, nor would " +"it be safe to add. `PowersUpToLevel=-1` upgrades still do not work " +"correctly with `PowerUpNAnim` and such buildings should forgo using " +"explicit upgrade animations." +msgstr "" +"现在如果通过 `PowerUpNAnim` 设置了加载物动画那么它们将会使用替代加载物建筑自身的图像来显示。注意你仍需显式设置 " +"`PowerUpNDamagedAnim` " +"来启用伤残版本的加载物动画,因为并没有添加一个它们不存在时回退到未伤残版本加载物图像的逻辑,并且那也不是一个安全的做法。`PowersUpToLevel=-1`" +" 的加载物在使用 `PowerUpNAnim` 时仍然无法正常工作,因此这类建筑应当避免使用显式的加载物动画。" + +msgid "" +"Elite technos no longer scatter by default, behaviour can be restored by " +"including `SCATTER` in their `EliteAbilities`." +msgstr "精英单位不再默认获得分散能力,可以通过在他们的 `EliteAbilities` 中添加 `SCATTER` 来复原旧有行为。" + +msgid "" +"`[CrateRules] -> FreeMCV` now controls whether or not player is forced to" +" receive unit from `[General] -> BaseUnit` from goodie crate if they own " +"no buildings or any existing `[General] -> BaseUnit` vehicles and own " +"more than `[CrateRules] -> FreeMCV.CreditsThreshold` (defaults to 1500) " +"credits." +msgstr "" +"现在 `[CrateRules] -> FreeMCV` 可以控制玩家是否会在他们没有任何建筑物或任何 `BaseUnit` 中的载具且资金超过 " +"`[CrateRules] -> FreeMCV.CreditsThreshold`(默认为 1500)时强制获得一个 `[General] ->" +" BaseUnit` 中的单位。" + +msgid "" +"The obsolete `[General] -> WarpIn` has been enabled for the default anim " +"type when technos are warping in. If you want to restore the vanilla " +"behavior, use the same anim type as `[General] -> WarpOut`." +msgstr "" +"原本废弃的 `[General] -> WarpIn` 已启用于科技类型传送来时的默认动画类型。如果想要恢复原版行为,请为其设置一个与 " +"`[General] -> WarpOut` 相同的动画类型。" + +msgid "" +"Vehicles with `Crusher=true` + `OmniCrusher=true` / " +"`MovementZone=CrusherAll` were hardcoded to tilt when crushing vehicles /" +" walls respectively. This now obeys `TiltsWhenCrushes` but can be " +"customized individually for these two scenarios using " +"`TiltsWhenCrushes.Vehicles` and `TiltsWhenCrushes.Overlays`, which both " +"default to `TiltsWhenCrushes`." +msgstr "" +"拥有 `Crusher=true` 和 `OmniCrusher=true`/`MovementZone=CrusherAll` " +"的载具类型硬编码在其碾压载具/围墙时会掀起而倾斜。现在此行为遵循 `TiltsWhenCrushes` 设置,但可以使用 " +"`TiltsWhenCrushes.Vehicles` 和 `TiltsWhenCrushes.Overlays` " +"分别定义这两种情况,它们都默认为 `TiltsWhenCrushes`。" + msgid "" "Phobos-introduced Warhead effects like shield modifiers, critical hits, " "disguise & mind control removal now require Warhead `Verses` to affect " @@ -696,6 +680,23 @@ msgstr "" msgid "0.3" msgstr "0.3" +msgid "" +"Translucent RLE SHPs will now be drawn using a more precise and " +"performant algorithm that has no green tint and banding. Can be disabled " +"with `rulesmd.ini -> [General] -> FixTransparencyBlitters=no`." +msgstr "" +"现在半透明的 RLE SHP 将使用更精确且性能更高的算法进行绘制,不会出现绿色色调和条纹。可以通过 `rulesmd.ini -> " +"[General] -> FixTransparencyBlitters=no` 来禁用。" + +msgid "" +"Iron Curtain status is now preserved by default when converting between " +"TechnoTypes via `DeploysInto` / `UndeploysInto`. This behavior can be " +"turned off per-TechnoType and global basis using " +"`[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no`." +msgstr "" +"现在铁幕效果会默认在科技类型通过 `DeploysInto`/`UndeploysInto` 转换时保留。这一行为可以通过每个科技类型和全局设置的" +" `[TechnoType]/[CombatDamage] -> IronCurtain.KeptOnDeploy=no` 来关闭。" + msgid "" "Keys `rulesmd.ini -> [WarheadType] -> PenetratesShield` and `rulesmd.ini " "-> [WarheadType] -> BreaksShield` have been changed to `Shield.Penetrate`" @@ -3048,8 +3049,7 @@ msgstr "修复降落方向设为超出 256 的值时不能正确换算的 Bug( msgid "" "Fixed a bug with some keys relating to `Interceptor` and " "`PassengerDeletion` not having correct default values (by Starkku)" -msgstr "" -"修复了某些 `Interceptor` 和 `PassengerDeletion` 相关语句没有正确默认值的 Bug(by Starkku)" +msgstr "修复了某些 `Interceptor` 和 `PassengerDeletion` 相关语句没有正确默认值的 Bug(by Starkku)" msgid "Fixes / interactions with other extensions:" msgstr "其他扩展引擎相关的修复/交互:" @@ -3228,6 +3228,12 @@ msgid "" "InitialPayload logic (by Noble_Fish)" msgstr "移除了禁止步兵使用初始载员逻辑的限制(by Noble_Fish)" +msgid "Pre-release changes" +msgstr "预发布版本更改" + +msgid "0.5-alpha1" +msgstr "0.5-alpha1" + msgid "0.4.0.3" msgstr "0.4.0.3" @@ -6366,3 +6372,12 @@ msgstr "现在侧边栏拓展工具条可以超出侧边栏边界(by Belonit msgid "Lifted stupidly small limit for tooltip character amount (by Belonit)" msgstr "解除了拓展工具条字符数量小得愚蠢的上限(by Belonit)" +#~ msgid "Migrating" +#~ msgstr "迁移" + +#~ msgid "From vanilla" +#~ msgstr "由原版" + +#~ msgid "Version TBD (develop branch nightly builds)" +#~ msgstr "待定版本(开发分支自动构建版本)" + diff --git a/docs/locale/zh_CN/LC_MESSAGES/index.po b/docs/locale/zh_CN/LC_MESSAGES/index.po index 069a258ced..e4fc991043 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/index.po +++ b/docs/locale/zh_CN/LC_MESSAGES/index.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-25 14:06+0800\n" +"POT-Creation-Date: 2026-08-04 10:03+0800\n" "PO-Revision-Date: 2025-02-07 13:02+0800\n" "Last-Translator: \n" "Language: zh\n" @@ -181,21 +181,21 @@ msgstr "" "(Bug 最少的版本,但功能更新上非常缓慢)" msgid "" -"[Latest development branch builds](https://github.com/Phobos-" -"developers/Phobos/releases) (a bit less bug-free releases, devbuilds get " -"new features when they are finished)" +"[Latest pre-release builds](https://github.com/Phobos-" +"developers/Phobos/releases) (a bit less bug-free releases, pre-releases " +"get new features when they are finished)" msgstr "" -"[最新的开发分支版本](https://github.com/Phobos-developers/Phobos/releases) (Bug " -"相对较少的版本,开发构建会在新功能**完成后**立即更新)" +"[最新的预发布版本](https://github.com/Phobos-developers/Phobos/releases) (Bug " +"相对较少的版本,预发布版本会在新功能 **完成后** 立即更新)" msgid "" "[Latest development branch nightly](https://nightly.link/Phobos-" "developers/Phobos/blob/develop/.github/workflows/nightly.yml) (added " -"unreleased features that will be in next devbuild)" +"unreleased features that will be in the next pre-release)" msgstr "" "[最新的自动构建版本](https://nightly.link/Phobos-" "developers/Phobos/blob/develop/.github/workflows/nightly.yml) " -"(添加了尚未发布的功能,这些功能将在下一个开发版本中推出)" +"(添加了尚未发布的功能,这些功能将在下一个预发布版本中推出)" msgid "" "Individual new feature nightly builds for testing can be found in [pull " @@ -204,6 +204,14 @@ msgstr "" "用于测试单个新功能的自动构建版本可在 [拉取请求](https://github.com/Phobos-" "developers/Phobos/pulls) 中找到" +msgid "" +"To learn how these build types relate to each other and how versioning " +"works, see the [release model and version lifecycle](docs/Project-" +"guidelines-and-policies.md#git-branching-model-version-lifecycle-and-" +"release-strategy)." +msgstr "" +"要了解这些构建类型之间的关系以及版本管理如何工作,请参阅 [发布模型与版本生命周期](docs/Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy)。" + msgid "Note on nightly builds" msgstr "关于自动构建版本的说明" @@ -240,9 +248,13 @@ msgid "" "feature set, just drop all the files from the archive except " "documentation folder and `Syringe.exe` into your game folder." msgstr "" -"Phobos 需要 [SyringeEx](https://github.com/Phobos-developers/SyringeEx)(v0.1.0.2 及以上版本)以运行,这是一款扩展的开源版本 Syringe;" -"游戏会在旧版 Syringe 下启动时显示错误并退出。Phobos 发布包和自动构建版本已附带捆绑了 SyringeEx 的 `Syringe.exe`;它也可以在 [SyringeEx 发布页面](https://github.com/Phobos-" -"developers/SyringeEx/releases) 单独下载。强烈建议同时 **安装 [Ares](https://launchpad.net/ares/+download)** 以获得完整的 Phobos 功能体验,只需将压缩包中除 Documentation 目录和 `Syringe.exe` 之外的所有文件放入游戏根目录即可。" +"Phobos 需要 [SyringeEx](https://github.com/Phobos-" +"developers/SyringeEx)(v0.1.0.2 及以上版本)以运行,这是一款扩展的开源版本 Syringe;游戏会在旧版 " +"Syringe 下启动时显示错误并退出。Phobos 发布包和自动构建版本已附带捆绑了 SyringeEx 的 " +"`Syringe.exe`;它也可以在 [SyringeEx 发布页面](https://github.com/Phobos-" +"developers/SyringeEx/releases) 单独下载。强烈建议同时 **安装 " +"[Ares](https://launchpad.net/ares/+download)** 以获得完整的 Phobos " +"功能体验,只需将压缩包中除 Documentation 目录和 `Syringe.exe` 之外的所有文件放入游戏根目录即可。" msgid "" "Obtain a Phobos \"package\" (official builds can be found on [releases " @@ -252,7 +264,8 @@ msgid "" msgstr "" "获取 Phobos 「软件包」(可在 [发布页面](https://github.com/Phobos-" "developers/Phobos/releases) " -"找到官方构建版;阅读下文了解如何获取自动构建版本)。最终得到:`Phobos.dll`、`Phobos.pdb` 以及捆绑的 SyringeEx 版 `Syringe.exe`。" +"找到官方构建版;阅读下文了解如何获取自动构建版本)。最终得到:`Phobos.dll`、`Phobos.pdb` 以及捆绑的 SyringeEx " +"版 `Syringe.exe`。" msgid "" "Place those files in the game folder (where your `gamemd.exe` is " @@ -271,6 +284,13 @@ msgstr "" "`Syringe.exe \"gamemd.exe\" [gamemd.exe 的命令行参数]`(如果不需要参数,请省略)。Ares 软件包中的 " "`RunAres.bat` 也有相同的功能,因此你也同样可以使用它来启动。" +msgid "" +"Be sure to read [migration and breaking changes](docs/Whats-New.md" +"#migration-breaking-changes) to know if you need to adjust something in " +"your mod after Phobos installation (or update)." +msgstr "" +"请务必阅读 [迁移与破坏性更改](docs/Whats-New.md#migration-breaking-changes),以了解在安装(或更新)Phobos 后是否需要对 Mod 进行调整。" + msgid "" "If you already use Ares in your mod, you just need to drop Phobos files " "mentioned above in your game folder, Syringe will load Phobos " @@ -390,7 +410,7 @@ msgid "" msgstr "" "本项目由 [@Belonit](https://github.com/Belonit)(Gluk-v48)和 " "[@Metadorius](https://github.com/Metadorius)(Kerbiter)于 2020 年创立,2021 " -"年首次发布稳定版。此后它已发展成为一个由众多贡献者和维护者组成的大型社区项目。" +"年首次发布稳定版本。此后它已发展成为一个由众多贡献者和维护者组成的大型社区项目。" msgid "" "Phobos has opened the external interfaces of some key components. If you " From b0ab5813102364a9deb7ed1ef0d445f322340987 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 16:46:43 +0300 Subject: [PATCH 45/55] Revert "ignore `:open:`" This reverts commit 494a24dbff5de22f2495d9d891594b9d30991647. --- scripts/extract_changelog.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index d671d2b105..b6700a767d 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -261,7 +261,9 @@ foreach ($line in $lines) { # in dropdown if ($inDropdown) { $trimmed = $line.Trim() -if ($trimmed -ne '' -and $trimmed -notmatch '^[-*+]' -and $trimmed -notmatch '^#' -and $trimmed -notmatch '^:[A-Za-z]' -and $trimmed -match ':$') { + # !empty, !list, !title, :$ + if ($trimmed -ne '' -and $trimmed -notmatch '^[-*+]' -and $trimmed -notmatch '^#' -and $trimmed -match ':$') { + # transform 4-level title $resultLines.Add("#### " + $trimmed) continue } From ee5b719df7ca1e51168e91d5622568c751121b71 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 16:46:57 +0300 Subject: [PATCH 46/55] Revert "Attempt to improve the script to work in scenarios without adding a title to the dropdown" This reverts commit 151cc4c2c0e6c1a2615ecdef7bc2abd1f605defd. --- docs/Whats-New.md | 70 +++++++++++++++++------------------ scripts/extract_changelog.ps1 | 38 +------------------ 2 files changed, 36 insertions(+), 72 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 60fa6e3f4f..82f9eb6e93 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -415,7 +415,7 @@ HideShakeEffects=false ; boolean ```{dropdown} Click to show :open: -New: +#### New: - [Allow using waypoints, area guard and attack move with aircraft](Fixed-or-Improved-Logics.md#extended-aircraft-missions) (by CrimRecya) - [Enhanced Straight trajectory](New-or-Enhanced-Logics.md#straight-trajectory) (by CrimRecya) - [Enable building production queue](User-Interface.md#building-production-queue) (by CrimRecya) @@ -669,7 +669,7 @@ New: - `OmniFire` supports buildings with `Turret=yes` (by FlyStar) - [Automatic conversion based on heallth](New-or-Enhanced-Logics.md#automatic-conversion-based-on-heallth) (by obsidianus) -Vanilla fixes: +#### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) - Buildings with foundation bigger than 1x1 can now recycle spawned correctly (by TaranDahl) @@ -758,7 +758,7 @@ Vanilla fixes: - Fixed the bug that technos do not reset their link with the linked building when deactivated (by NetsuNegi) - Fixed an issue where setting a production building as `Primary` could cause it to enter an unload state (by FlyStar) -Phobos fixes: +#### Phobos fixes: - Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi) - Fixed an issue that `FireAngle` was not taken into account when drawing barrel in `TurretShadow` (by CrimRecya) - Fixed a bug that sometimes caused weapon/warhead detonations from features such as `ExtraWarheads`, animation damage or `Crit.Warhead` to unintentionally move from its intended position (by Starkku) @@ -799,7 +799,7 @@ Phobos fixes: - Fixed the bug where landing direction cannot be correctly converted when set to a value exceeding 256 (by Noble_Fish) - Fixed a bug with some keys relating to `Interceptor` and `PassengerDeletion` not having correct default values (by Starkku) -Fixes / interactions with other extensions: +#### Fixes / interactions with other extensions: - Taking over Ares' AlphaImage respawn logic to reduce lags from it (by NetsuNegi) - Fixed an issue that Ares' Type Conversion not resetting barrel's direction by `FireAngle` (by TaranDahl) - Fixed the issue where Ares' `Flash.Duration` cannot override the weapon's repair flash effect (by Sovietianqi, based on knowledge of Noble_Fish) @@ -842,11 +842,11 @@ Fixes / interactions with other extensions: ```{dropdown} Click to show -Vanilla fixes: +#### Vanilla fixes: - Vehicles overlapping `Wall=true` OverlayTypes no longer display sell cursor and cannot be sold (by CnCRAZER, Starkku, Noble_Fish) - Fixed a desync due to an inconsistent shroud state caused by `GapGenerator` and `SpySat` interaction (by Starkku) -Phobos fixes: +#### Phobos fixes: - Fixed vehicles disguised as trees incorrectly displaying veterancy insignia when they shouldn't (by Starkku) - Fixed the issue that power output of building on tooltip won't consider power enhancer (by NetsuNegi) - `RealTimeTimers` now support independent gamespeed index values for Multiplayer and Skirmish (by RAZER) @@ -871,7 +871,7 @@ Phobos fixes: ```{dropdown} Click to show -Phobos fixes: +#### Phobos fixes: - Fixed `AircraftDockingDirs` being reset if the BuildingType section is redefined in map file (by Starkku) - Fixed harvester counter not accounting for type converting harvesters (by Ollerus) - Fixed the bug that weapon cannot used to intercept on gound bullet if it's projectile has `AG=no` (by NetsuNegi) @@ -887,10 +887,10 @@ Phobos fixes: ```{dropdown} Click to show -Vanilla fixes: +#### Vanilla fixes: - Fixed preplaced aircraft outside visible map being incorrectly flagged as crashing under certain conditions (by Starkku) -Phobos fixes: +#### Phobos fixes: - AttachEffect `DisableWeapons` no longer interferes with 'can this unit/building fire weapons?' checks for units/buildings with no weapons (by Starkku) - Fixed starting infantry being scattered randomly on game start instead of being grouped (by 11EJDE11, Belonit, Ollerus) - Fixed an issue with certain Warhead detonation features (f.ex `Crit.Warhead` without `Crit.Warhead.FullDetonation=true` not snapping on the intended target without `CellSpread`) (by Starkku) @@ -905,7 +905,7 @@ Phobos fixes: ```{dropdown} Click to show -New: +#### New: - `Crit.AffectsHouses` for critical hit system (by Starkku) - Warhead or weapon detonation at superweapon target cell (by Starkku) - Super Weapons launching other Super Weapons (by Morton) @@ -1073,7 +1073,7 @@ New: - Allow retint fix to be disabled with `[AudioVisual] -> UseRetintFix=no` in `rulesmd.ini` due to performance considerations (by Kerbiter) - Elite technos no longer scatter by default, behaviour is controlled by `SCATTER` veterancy ability now (by NetsuNegi & Starkku) -Vanilla fixes: +#### Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) - Allow setting whether `AlternateFLH` applies to vehicle passengers in the transport unit (by Trsdy & NetsuNegi) - Improved the statistic distribution of the spawned crates over the visible area of the map. (by Trsdy, based on TwinkleStar's work) @@ -1219,7 +1219,7 @@ Vanilla fixes: - Fixed the fact that when the selected unit is in a rearmed state, it can unconditionally use attack mouse on the target (by FlyStar) - Fixed pathfinding crashes (EIP 0x42A525, 0x42C507, 0x42C554) that happened on bigger maps due to too small pathfinding node buffer (by CrimRecya) -Phobos fixes: +#### Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) - Add `ImmuneToCrit` for shields (by Trsdy) - Reimplemented the bugfix for jumpjet units' facing when firing, discard the inappropriate `JumpjetTurnToTarget` tag (by Trsdy) @@ -1274,7 +1274,7 @@ Phobos fixes: - Fixed a read bug when setting the SHP file name in INI (By Noble_Fish) - Fixed map trigger action `125 Build At...` not always playing buildups correctly (by Starkku) -Fixes / interactions with other extensions: +#### Fixes / interactions with other extensions: - Weapons fired by EMPulse superweapons *(Ares feature)* now fully respect the firing building's FLH (by Starkku) - Weapons fired by EMPulse superweapons *(Ares feature)* now respect `Floater` and Phobos-added `Gravity` setting (by Starkku) - `IsSimpleDeployer` units with Hover locomotor and `DeployToLand` no longer get stuck after deploying or play their move sound indefinitely (by Starkku) @@ -1301,10 +1301,10 @@ Fixes / interactions with other extensions: ```{dropdown} Click to show -New: +#### New: - Additional sync logging in case of desync errors occuring (by Starkku) -Phobos fixes: +#### Phobos fixes: - `AutoDeath` support for objects in limbo (by Trsdy) - Buildings sold by `AutoDeath` no longer play a click sound effect (by Trsdy) - Fixed shield animation being hidden while underground or in tunnels fix not working correctly (by Starkku) @@ -1332,7 +1332,7 @@ Phobos fixes: ```{dropdown} Click to show -New: +#### New: - LaserTrails initial implementation (by Kerbiter & ChrisLv_CN) - Anim-to-Unit logic and ability to randomize `DestroyAnim` (by Otamaa) - Shield modification warheads (by Starkku) @@ -1432,7 +1432,7 @@ New: - New condition for automatic self-destruction logic when TechnoTypes exist/don't exist (by FlyStar) - For developers: now you can use command line arg to control main exception handler (by Multfinite) -Vanilla fixes: +#### Vanilla fixes: - Fixed laser drawing code to allow for thicker lasers in house color draw mode (by Kerbiter & ChrisLv_CN) - Fixed `DeathWeapon` not detonating properly (by Uranusian) - Fixed lasers & other effects drawing from wrong offset with weapons that use `Burst` (by Starkku) @@ -1466,7 +1466,7 @@ Vanilla fixes: - Fixed building `TargetCoordOffset` not being taken into accord for several things like fire angle calculations and target lines (by Starkku) - Allowed observers to see a selected building's radial indicator (by Trsdy) -Phobos fixes: +#### Phobos fixes: - Fixed shields being able to take damage when the parent TechnoType was under effects of a `Temporal` Warhead (by Starkku) - Improved shield behavior for forced damage (by Uranusian) - Fixed `SplashList` animations playing when a unit is hit on a bridge over water (by Uranusian) @@ -1495,11 +1495,11 @@ Phobos fixes: - Corrected the misinterpretation in the definition of `DiskLaser.Radius` (by Trsdy) - Fixed GlobalVariables failed working among scenarios (by Trsdy) -Fixes / interactions with other extensions: +#### Fixes / interactions with other extensions: - Fixed AI Aircraft docks bug when Ares tag `[GlobalControls] -> AllowParallelAIQueues=no` is set (by FS-21) - Weapons fired by EMPulse superweapons *(Ares feature)* without `EMPulse.TargetSelf=true` can now create radiation (by Starkku) -Non-DLL: +#### Non-DLL: - Implemented a tool (sed wrapper) to semi-automatically upgrade INIs to use latest Phobos tags (by Kerbiter) ``` @@ -1508,7 +1508,7 @@ Non-DLL: ```{dropdown} Click to show -Phobos fixes: +#### Phobos fixes: - Fixed shield type info not saving properly (by Uranusian) - Fixed extended building upgrades logic not properly interacting with Ares' BuildLimit check (by Uranusian) - Fixed more random crashes for `CameoPriority` (by Uranusian) @@ -1520,7 +1520,7 @@ Phobos fixes: ```{dropdown} Click to show -Phobos fixes: +#### Phobos fixes: - Fixed random crashes about `CameoPriority` (by Uranusian) - Fixed trigger action 125 not functioning properly (by Uranusian) - Fixed area warhead detonation not falling back to firer house (by Otamaa) @@ -1534,16 +1534,16 @@ Phobos fixes: ```{dropdown} Click to show -New: +#### New: - Customizable producing progress "bars" like CnC:Remastered did (by Uranusian) - Customizable cameo sorting priority (by Uranusian) - Customizable harvester ore gathering animation (by secsome & Uranusian) - Allow making technos unable to be issued with movement order (by Uranusian) -Vanilla fixes: +#### Vanilla fixes: - Fixed non-IME keyboard input to be working correctly for languages / keyboard layouts that use character ranges other than Basic Latin and Latin-1 Supplement (by Belonit) -Phobos fixes: +#### Phobos fixes: - Fixed the critical damage logic not functioning properly (by Uranusian) - Fixed the bug when executing the stop command game crashes (by Uranusian) @@ -1553,7 +1553,7 @@ Phobos fixes: ```{dropdown} Click to show -Phobos fixes: +#### Phobos fixes: - Fixed occasional crashes introduced by `Speed=0` stationary vehicles code (by Starkku) ``` @@ -1562,14 +1562,14 @@ Phobos fixes: ```{dropdown} Click to show -New: +#### New: - Setting VehicleType `Speed` to 0 now makes game treat them as stationary (by Starkku) -Vanilla fixes: +#### Vanilla fixes: - Fixed the bug when after a failed placement the building/defence tab hotkeys won't trigger placement mode again (by Uranusian) - Fixed the bug when building with `UndeployInto` plays `EVA_NewRallypointEstablished` while undeploying (by secsome) -Phobos fixes: +#### Phobos fixes: - Fixed the bug when trigger action `125 Build At...` wasn't actually producing a building when the target cells were occupied (by secsome) ``` @@ -1579,7 +1579,7 @@ Phobos fixes: ```{dropdown} Click to show -New: +#### New: - Shield logic for TechnoTypes (by Uranusian, secsome, Belonit) with warhead additions (by Starkku) - Custom Radiation Types (by AlexB, Otamaa, Belonit, Uranusian) - New ScriptType actions `71 Timed Area Guard`, `72 Load Onto Transports`, `73 Wait until ammo is full` (by FS-21) @@ -1604,7 +1604,7 @@ New: - Maximum waypoints amount increased from 702 to 2147483647 (by secsome) - Customizeable Missing Cameo file (by Uranusian) -Vanilla fixes: +#### Vanilla fixes: - Map previews with zero size won't crash the game anymore (by Kerbiter & Belonit) - Tileset 255+ bridge fix (by E1 Elite) - Fixed fatal errors when `Blowfish.dll` couldn't be registered in the system properly due to missing admin rights (by Belonit) @@ -1617,7 +1617,7 @@ Vanilla fixes: - Fixed `DebrisMaximums` (spawned debris type amounts cannot go beyond specified maximums anymore) (by Otamaa) - Fixes to `DeployFire` logic (`DeployFireWeapon`, `FireOnce`, stop command now work properly) (by Starkku) -Phobos fixes: +#### Phobos fixes: - Properly rewritten a fix for mind-controlled vehicles deploying into buildings (by FS-21) - Properly rewritten `DeployToFire` fix, tag `Deployed.RememberTarget` is deprecated, now always on (by Kerbiter) - New warheads now work with Ares' `GenericWarhead` superweapon (by Belonit) @@ -1628,7 +1628,7 @@ Phobos fixes: ```{dropdown} Click to show -Phobos fixes: +#### Phobos fixes: - Fixed an occasional crash when selecting units with a selection box (by Kerbiter) ``` @@ -1637,7 +1637,7 @@ Phobos fixes: ```{dropdown} Click to show -New: +#### New: - Full-color PCX graphics support (by Belonit) - Support for PCX loading screens of any size (by Belonit) - Extended sidebar tooltips with descriptions, recharge time and power consumption/generation (by Kerbiter & Belonit) @@ -1649,7 +1649,7 @@ New: - Customizable disk laser radius (by Belonit & Kerbiter) - Ability to switch to GDI sidebar layout for any side (by Belonit) -Vanilla fixes: +#### Vanilla fixes: - Deploying mind-controlled TechnoTypes won't make them permanently mind-controlled anymore (unfinished fix by DCoder) - SHP debris hardcoded shadows now respect `Shadow=no` tag value (by Kerbiter) - `DeployToFire` vehicles won't lose target on deploy anymore (unfinished fix by DCoder) diff --git a/scripts/extract_changelog.ps1 b/scripts/extract_changelog.ps1 index b6700a767d..800562095e 100644 --- a/scripts/extract_changelog.ps1 +++ b/scripts/extract_changelog.ps1 @@ -238,42 +238,6 @@ if (-not (Test-Path -LiteralPath $WhatsNewPath)) { } $content = (Get-Content -LiteralPath $WhatsNewPath -Raw -Encoding utf8) -replace "`r`n", "`n" - -$lines = $content -split "`n" -$resultLines = [System.Collections.Generic.List[string]]::new() -$inDropdown = $false - -foreach ($line in $lines) { - # dropdown start - if ($line -match '^\s*```\{dropdown\}') { - $inDropdown = $true - $resultLines.Add($line) - continue - } - - # dropdown end - if ($inDropdown -and $line -match '^\s*```\s*$') { - $inDropdown = $false - $resultLines.Add($line) - continue - } - - # in dropdown - if ($inDropdown) { - $trimmed = $line.Trim() - # !empty, !list, !title, :$ - if ($trimmed -ne '' -and $trimmed -notmatch '^[-*+]' -and $trimmed -notmatch '^#' -and $trimmed -match ':$') { - # transform 4-level title - $resultLines.Add("#### " + $trimmed) - continue - } - } - - $resultLines.Add($line) -} - -$content = $resultLines -join "`n" - $root = Read-SectionTree ($content -split "`n") $sections = @(Get-AllSections $root) @@ -323,4 +287,4 @@ $outPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) { $OutputPath } else [System.IO.File]::WriteAllText($outPath, $text + "`n", (New-Object System.Text.UTF8Encoding($false))) $kinds = @($matched | ForEach-Object { Get-SectionKind $_ }) -join ', ' -Write-Host "Extracted release notes for version '$version' ($kinds) to '$OutputPath'" \ No newline at end of file +Write-Host "Extracted release notes for version '$version' ($kinds) to '$OutputPath'" From e378af2d7236b8f40049278ef254cd78aa17ed22 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 16:50:52 +0300 Subject: [PATCH 47/55] fix locale doc build script --- scripts/build_docs_locale.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build_docs_locale.bat b/scripts/build_docs_locale.bat index d8ca69b16e..693c5c74d0 100644 --- a/scripts/build_docs_locale.bat +++ b/scripts/build_docs_locale.bat @@ -6,5 +6,5 @@ rem Ensure we're in correct directory. cd /D "%~dp0" cd ..\docs -sphinx-build -b gettext ./ ./locale -sphinx-intl update -p ./locale -l zh_CN +sphinx-build -b gettext . _build/gettext +sphinx-intl update -p _build/gettext -l zh_CN From d59faab7c9ee65bb29a2cb72933117cb0a65795a Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 17:54:15 +0300 Subject: [PATCH 48/55] docs: keep docutils system messages out of gettext catalogs Repeated sub-headings inside {dropdown}s (e.g. the many "#### Vanilla fixes:" blocks in Whats-New.md) produce "Duplicate implicit target name" INFO/1 system messages. Sphinx's ApplySourceWorkaround folds them into the rubric rawsource, and the gettext builder extracts that rawsource as a bogus translatable msgid into the .pot/.po files. Add a local extension docs/_ext/sanitize_system_messages.py that drops system_message nodes and repairs the polluted rawsource at doctree-read, register it in conf.py, and regenerate the zh_CN catalogs. Assisted-by: DeepSeek V4 Flash --- .github/copilot-instructions.md | 2 + .gitignore | 4 ++ docs/_ext/__init__.py | 3 + docs/_ext/sanitize_system_messages.py | 69 +++++++++++++++++++ docs/conf.py | 9 ++- docs/locale/zh_CN/LC_MESSAGES/CREDITS.po | 28 ++++++-- .../LC_MESSAGES/Fixed-or-Improved-Logics.po | 19 +++-- .../zh_CN/LC_MESSAGES/Interoperability.po | 26 ++++--- .../LC_MESSAGES/New-or-Enhanced-Logics.po | 49 ++++++++++--- docs/locale/zh_CN/LC_MESSAGES/Whats-New.po | 42 +++++++++-- 10 files changed, 210 insertions(+), 41 deletions(-) create mode 100644 docs/_ext/__init__.py create mode 100644 docs/_ext/sanitize_system_messages.py diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8e1fe0506b..4e8f02ed5f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -372,6 +372,8 @@ scripts\build_docs.bat Output goes to `docs/_build/html/`. Pull requests are automatically built and served by Read the Docs - check the PR status checks for a preview link. +**Local extension `docs/_ext/`**: a small local Sphinx extension (`sanitize_system_messages`, registered as `_ext.sanitize_system_messages` in `docs/conf.py`) strips docutils `system_message` nodes and repairs the `rawsource` that Sphinx's `ApplySourceWorkaround` pollutes with them. Without it, the "Duplicate implicit target name" warnings caused by repeated sub-headings (e.g. the many `#### Vanilla fixes:` blocks inside `{dropdown}`s in `Whats-New.md`) leak into the gettext `.pot`/`.po` files as bogus translatable strings. Do not remove it or drop `sys.path.insert(0, os.path.abspath('.'))` from `conf.py` - the extension is only importable because of that path insert. A standalone reproduction of the underlying Sphinx/MyST bug is in this [gist](https://gist.github.com/Metadorius/ee435861903ba132cd70563c2bdffec1). + ### Translations The project uses Sphinx internationalization with `.po` files. Currently only **zh_CN** (Chinese) is maintained. The translation workflow: diff --git a/.gitignore b/.gitignore index 8591b8d397..2f2b9c2dde 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,7 @@ out # Python virtual environment for docs .venv/ + +# Python bytecode caches +__pycache__/ +*.py[cod] diff --git a/docs/_ext/__init__.py b/docs/_ext/__init__.py new file mode 100644 index 0000000000..c62c2fb55f --- /dev/null +++ b/docs/_ext/__init__.py @@ -0,0 +1,3 @@ +"""Local Sphinx extensions for the Phobos documentation.""" + +from __future__ import annotations diff --git a/docs/_ext/sanitize_system_messages.py b/docs/_ext/sanitize_system_messages.py new file mode 100644 index 0000000000..4d0bac0593 --- /dev/null +++ b/docs/_ext/sanitize_system_messages.py @@ -0,0 +1,69 @@ +"""Keep docutils system messages out of extracted content. + +Background +---------- + +Sphinx's ``ApplySourceWorkaround`` transform (``sphinx.util.nodes. +apply_source_workaround``) sets ``rawsource = node.astext()`` on any +``TextElement``/``image``/``topic`` whose ``rawsource`` is empty. + +MyST renders headings that live inside a directive (such as a sphinx-design +``{dropdown}``) as ``rubric`` nodes whose ``rawsource`` is left empty. Repeated +headings (e.g. the many ``#### Vanilla fixes:`` blocks in ``Whats-New.md``) +produce duplicate implicit targets, and docutils appends an ``INFO/1 +system_message`` child to the rubric (via ``myst_parser``'s +``generate_heading_target`` -> ``document.note_implicit_target``). The +``astext()`` then includes that message, so the rubric's ``rawsource`` ends up +as:: + + Vanilla fixes:D:\\Repos\\Phobos\\docs\\Whats-New.md:845: (INFO/1) Duplicate + implicit target name: "vanilla fixes:". + +The gettext builder later extracts ``rawsource`` as a translatable message, so +these warnings leak into the ``.pot``/``.po`` files. + +This extension removes the system-message children from the doctree (Sphinx's +``FilterSystemMessages`` would remove them at write time anyway) and restores +the ``rawsource`` that ``ApplySourceWorkaround`` polluted with the message text. +""" + +from __future__ import annotations + +import re + +from docutils import nodes + +__version__ = '1.0.0' + +# Matches the leading part of the message that docutils appends to ``rawsource`` +# via ``system_message.astext()``: "<source>:<line>: (LEVEL/N) <message>". +_MESSAGE_START = re.compile( + r'(?:[A-Za-z]:)?[\\/][^\r\n:]+:\d+: \((?:INFO|WARNING|ERROR|SEVERE)/\d+\) ' +) + + +def _repair(app, doctree) -> None: + # 1. Drop system-message children from content nodes. + for node in list(doctree.findall(nodes.system_message)): + parent = node.parent + if parent is not None: + parent.remove(node) + + # 2. Restore rawsource that ApplySourceWorkaround polluted with the + # system-message text. + for node in doctree.findall(nodes.TextElement): + raw = node.rawsource + if not raw: + continue + match = _MESSAGE_START.search(raw) + if match: + node.rawsource = raw[:match.start()].rstrip() + + +def setup(app): + app.connect('doctree-read', _repair) + return { + 'version': __version__, + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/docs/conf.py b/docs/conf.py index 253443e356..363204aeab 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,10 +9,9 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) +import os +import sys +sys.path.insert(0, os.path.abspath('.')) # -- Project information ----------------------------------------------------- @@ -26,7 +25,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx_rtd_theme', 'myst_parser', 'sphinx.ext.mathjax', 'sphinx_design'] +extensions = ['sphinx_rtd_theme', 'myst_parser', 'sphinx.ext.mathjax', 'sphinx_design', '_ext.sanitize_system_messages'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po index 751fddc91b..ec2ed3df51 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po +++ b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-01 18:48+0800\n" +"POT-Creation-Date: 2026-08-04 17:34+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.16.0\n" +"Generated-By: Babel 2.18.0\n" msgid "Credits" msgstr "鸣谢" @@ -459,12 +459,10 @@ msgid "`Pips.HideIfNoStrength` and `SelfHealing.EnabledBy` additions for shields msgstr "护盾新增功能 `Pips.HideIfNoStrength` 与 `SelfHealing.EnabledBy`" msgid "Map Action 600 for overwriting DropCrate behaviour in attached Technos" -msgstr "" -"用于覆盖附着单位 `DropCrate` 行为的触发结果 600" +msgstr "用于覆盖附着单位 `DropCrate` 行为的触发结果 600" msgid "`DropCrate` for setting what crate drop when Techno is destroyed" -msgstr "" -"用于设置单位被摧毁时掉落哪种箱子的 `DropCrate`" +msgstr "用于设置单位被摧毁时掉落哪种箱子的 `DropCrate`" msgid "Warhead activation target health thresholds enhancements" msgstr "目标血量作为弹头生效条件的进一步改进" @@ -907,6 +905,9 @@ msgstr "动画使用气候/地块色盘的开关" msgid "Tank Bunker improvements" msgstr "坦克碉堡改进" +msgid "`ProjectileRange` weapon range modifiers interaction fix" +msgstr "" + msgid "**Morton (MortonPL)**:" msgstr "**Morton (MortonPL)**:" @@ -1346,6 +1347,18 @@ msgid "" "`EVAIndex` is no longer reset after load game" msgstr "`EVA.Tag` 已经支持在国家层面设置并且 `EVAIndex` 不再在读档后重置" +#, fuzzy +msgid "DeployFire supports buildings" +msgstr "低优先级部署" + +msgid "`OmniFire` supports buildings with `Turret=yes`" +msgstr "" + +msgid "" +"Fixed an issue where setting a production building as `Primary` could " +"cause it to enter an unload state" +msgstr "" + msgid "**NetsuNegi**:" msgstr "**NetsuNegi**:" @@ -3134,3 +3147,6 @@ msgid "" "position is clamped to the visible area" msgstr "为 `BannerType` 添加了 `ClampToScreen` 标签以控制横幅位置是否被限制在可视化区域内" +msgid "**obsidianus** - Automatic conversion based on health" +msgstr "" + diff --git a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po index 61d73f6455..d924630045 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-31 10:01+0800\n" +"POT-Creation-Date: 2026-08-04 17:34+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.16.0\n" +"Generated-By: Babel 2.18.0\n" msgid "Fixed / Improved Logics" msgstr "修复或改进的逻辑" @@ -1669,6 +1669,11 @@ msgid "" "building when deactivated." msgstr "修复了单位失灵时不会重置其与相链接建筑链接状态的 Bug。" +msgid "" +"Fixed an issue where setting a production building as `Primary` could " +"cause it to enter an unload state." +msgstr "" + msgid "Fixes / interactions with other extensions" msgstr "修复或与其他扩展的交互" @@ -1922,8 +1927,13 @@ msgstr "修复了 Ares 的 EMP 不会使 AI 工厂暂停生产的问题。" msgid "" "Removed the restriction that prohibits InfantryTypes from using the " "InitialPayload logic." +msgstr "移除了禁止步兵使用初始载员逻辑的限制。" + +msgid "" +"`ProjectileRange` now has weapon range modifiers applied to it if greater" +" than 0 and unless `ProjectileRange.ApplyModifiers` is set to false on " +"the WeaponType" msgstr "" -"移除了禁止步兵使用初始载员逻辑的限制。" msgid "Newly added global settings" msgstr "新增的全局设定" @@ -3688,7 +3698,8 @@ msgid "" "map file/editor support, see [Increased Overlay Limit](AI-Scripting-and-" "Mapping.md#increased-overlay-limit)." msgstr "" -"游戏现在支持超过 255 种不同的覆盖物,最多可达 65535 种。有关地图文件/编辑器的支持,见 [更高的覆盖物数量上限](AI-Scripting-and-Mapping.md#increased-overlay-limit)。" +"游戏现在支持超过 255 种不同的覆盖物,最多可达 65535 种。有关地图文件/编辑器的支持,见 [更高的覆盖物数量上限](AI-" +"Scripting-and-Mapping.md#increased-overlay-limit)。" msgid "`ZAdjust` for OverlayTypes" msgstr "覆盖物的 Z 深校正值" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po b/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po index 1daef224c2..5588531a17 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-06-13 01:11+0800\n" +"POT-Creation-Date: 2026-08-04 17:34+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language: zh_CN\n" @@ -17,7 +17,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.16.0\n" +"Generated-By: Babel 2.18.0\n" msgid "Interoperability" msgstr "互操作性" @@ -94,27 +94,30 @@ msgstr "API 版本跟踪" msgid "Semantic Versioning Rules" msgstr "语义化版本规则" +#, fuzzy msgid "" "**Major (X)**: Increment for breaking changes (backward incompatible API " -"modifications). Example: Deleting an API, changing function parameters, " -"modifying data structures that break existing code. When bumped, reset " -"Minor and Patch to 0 (e.g., 1.2.3 → 2.0.0)." +"modifications).\\ Example: Deleting an API, changing function parameters," +" modifying data structures that break existing code.\\ When bumped, reset" +" Minor and Patch to 0 (e.g., 1.2.3 → 2.0.0)." msgstr "" "**主版本号 (X)**:递增表示发生了破坏性变更(向后不兼容的 API 修改)。<br>示例:删除 " "API、更改函数参数、修改数据结构导致现有代码损坏。<br>当此版本号递增时,次版本号和修订号重置为 0(例如 1.2.3 → 2.0.0)。" +#, fuzzy msgid "" -"**Minor (Y)**: Increment for backward-compatible new features. Example: " -"Adding a new API function, new parameters that don't break existing code." -" When bumped, reset Patch to 0 (e.g., 1.2.3 → 1.3.0)." +"**Minor (Y)**: Increment for backward-compatible new features.\\ Example:" +" Adding a new API function, new parameters that don't break existing " +"code.\\ When bumped, reset Patch to 0 (e.g., 1.2.3 → 1.3.0)." msgstr "" "**次版本号 (Y)**:递增表示增加了向后兼容的新功能。<br>示例:添加新的 API " "函数、增加不破坏现有代码的新参数。<br>当此版本号递增时,修订号重置为 0(例如 1.2.3 → 1.3.0)。" +#, fuzzy msgid "" -"**Patch (Z)**: Increment for backward-compatible miscs (no API changes). " -"Example: Fixing crashes, correcting calculations, optimizing internals " -"without changing interface (e.g., 1.2.3 → 1.2.4)." +"**Patch (Z)**: Increment for backward-compatible miscs (no API " +"changes).\\ Example: Fixing crashes, correcting calculations, optimizing " +"internals without changing interface (e.g., 1.2.3 → 1.2.4)." msgstr "" "**修订号 (Z)**:递增表示向后兼容的杂项改进(无 API 变更)。<br>示例:修复崩溃、更正计算、优化内部实现而不改变接口(例如 " "1.2.3 → 1.2.4)。" @@ -128,6 +131,7 @@ msgstr "通过 `pVersion` 输出参数返回当前的 Interop API 版本。" msgid "Parameters:" msgstr "参数:" +#, python-brace-format msgid "`pVersion`: Receives the version structure `{ major, minor, patch }`." msgstr "`pVersion`:接收版本结构体 `{ major, minor, patch }`。" diff --git a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po index 65d2b46054..0b7290d4a6 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-02 22:22+0800\n" +"POT-Creation-Date: 2026-08-04 17:34+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.16.0\n" +"Generated-By: Babel 2.18.0\n" msgid "New / Enhanced Logics" msgstr "新增或增强的逻辑" @@ -348,14 +348,12 @@ msgstr "" msgid "" "`ArmorMultiplier.Chance` can be used to set the chance of whether the " "armor multiplier will take effect or not when taking damage." -msgstr "" -"`ArmorMultiplier.Chance` 可用于设置在受伤时护甲倍率生效的概率。" +msgstr "`ArmorMultiplier.Chance` 可用于设置在受伤时护甲倍率生效的概率。" msgid "" "`ArmorMultiplier.AffectsHouse` can be used to set which houses the armor " "multiplier will take effect." -msgstr "" -"`ArmorMultiplier.AffectsHouse` 可用于设置护甲倍率生效概率对哪些所属方生效。" +msgstr "`ArmorMultiplier.AffectsHouse` 可用于设置护甲倍率生效概率对哪些所属方生效。" msgid "" "`ArmorMultiplier.HitAnim` can be used to set the animation that'll be " @@ -364,7 +362,9 @@ msgid "" "`ArmorMultiplier.Chance` or `ArmorMultiplier.AffectsHouse` settings. If " "more than one animation is listed, a random one is selected." msgstr "" -"`ArmorMultiplier.HitAnim` 可用于设置在受伤时播放的动画。如果由于 `ArmorMultiplier.Allow/DisallowWarheads`、`ArmorMultiplier.Chance` 或 `ArmorMultiplier.AffectsHouse` 设置导致护甲倍率未生效则不显示该动画。如果列出了多个动画,则将随机选择一个。" +"`ArmorMultiplier.HitAnim` 可用于设置在受伤时播放的动画。如果由于 " +"`ArmorMultiplier.Allow/DisallowWarheads`、`ArmorMultiplier.Chance` 或 " +"`ArmorMultiplier.AffectsHouse` 设置导致护甲倍率未生效则不显示该动画。如果列出了多个动画,则将随机选择一个。" msgid "" "If `ROFMultiplier.ApplyOnCurrentTimer` is set to true, `ROFMultiplier` is" @@ -1718,6 +1718,24 @@ msgid "" "`Sight`. Now you can customize it." msgstr "在原版中,`RevealToAll` 硬编码揭示半径为 `Sight` 的区域。现在你可以自定义了。" +msgid "DeployFire supports" +msgstr "" + +msgid "Building types now also support using `DeployFire` and `DeployFireWeapon`." +msgstr "" + +msgid "" +"If a building has other configurations such as `Factory`, `GapGenerator`," +" or `Passengers`, it will prioritize executing those deployment actions " +"instead." +msgstr "" + +msgid "" +"`DeployFireDelay` specifies the frame interval at which deployed weapons " +"attempt to fire. If the weapon is unavailable due to `ROF`, `CanTarget`, " +"or other reasons, the deployed weapon will not fire." +msgstr "" + msgid "Destroyable pathfinding obstacles" msgstr "可摧毁的寻路障碍" @@ -3632,16 +3650,25 @@ msgstr "自动根据血量变形" msgid "Units can now be converted into another unit by health percentage." msgstr "单位可以现在可以基于HP变为其他单位" -msgid "`Convert.Health.AbovePercent` determines the minimal health percentage at which a unit converts automatically." +msgid "" +"`Convert.Health.AbovePercent` determines the minimal health percentage at" +" which a unit converts automatically." msgstr "`Convert.Health.AbovePercent`决定了触发单位自动变身的最小HP值" -msgid "`Convert.Health.BelowPercent` determines the maximum health percentage at which a unit converts automatically." +msgid "" +"`Convert.Health.BelowPercent` determines the maximum health percentage at" +" which a unit converts automatically." msgstr "`Convert.Health.BelowPercent`决定了触发单位自动变身的最大HP值" -msgid "`Convert.Health` specify the new techno after the conversion. This unit must be of the same type of the original (infantry -> infantry, vehicle -> vehicle or aircraft -> aircraft)." +msgid "" +"`Convert.Health` specify the new techno after the conversion. This unit " +"must be of the same type of the original (infantry -> infantry, vehicle " +"-> vehicle or aircraft -> aircraft)." msgstr "`Convert.Health` 指定变身后的单位. 类型需要和原单位相同 (步兵 → 步兵, 载具 → 载具 或 飞机 → 飞机)" -msgid "Setting a negative number will disable the HP check, and when both checks are disabled, conversion will not occur." +msgid "" +"Setting a negative number will disable the HP check, and when both checks" +" are disabled, conversion will not occur." msgstr "设置为负值会关闭对应的HP检查,当两项检查都被关闭则不会触发变身." msgid "Automatic passenger deletion" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index c4f4bd058a..91387cbfe6 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-04 10:03+0800\n" +"POT-Creation-Date: 2026-08-04 17:34+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.16.0\n" +"Generated-By: Babel 2.18.0\n" msgid "What's New" msgstr "版本更新说明" @@ -33,8 +33,7 @@ msgid "" "New Phobos user? Read the \"Changes to vanilla behavior\" subsections in " "every version below to know what you might need to adjust when installing" " Phobos" -msgstr "" -"Phobos 新用户?请阅读以下每个版本中的“原版行为更改”小节,以了解安装 Phobos 时可能需要调整的内容。" +msgstr "Phobos 新用户?请阅读以下每个版本中的“原版行为更改”小节,以了解安装 Phobos 时可能需要调整的内容。" msgid "" "This serves as a changelog for when you just need to drop the new version" @@ -110,6 +109,12 @@ msgstr "" msgid "Changes to Phobos behavior" msgstr "Phobos 行为更改" +msgid "" +"`ProjectileRange` (Ares feature) now has weapon range modifiers applied " +"to it if greater than 0 and unless `ProjectileRange.ApplyModifiers` is " +"set to false on the WeaponType." +msgstr "" + msgid "" "`Splits.TargetCellRange` < 0 now applies special behaviour where the " "projectile does not consider nearby cells as additional targets if there " @@ -2384,6 +2389,24 @@ msgstr "" "[`600` 配置掉落箱子行为](AI-Scripting-and-Mapping.md#configure-drop-crate)(by " "FS-21)" +#, fuzzy +msgid "" +"[DeployFire supports buildings](New-or-Enhanced-Logics.md#deployfire-" +"supports) (By FlyStar)" +msgstr "[延迟开火](New-or-Enhanced-Logics.md#delayed-firing)(by Starkku)" + +#, fuzzy +msgid "`OmniFire` supports buildings with `Turret=yes` (by FlyStar)" +msgstr "步兵类现已支持使用 `IsGattling=yes` 开启盖特逻辑(by FlyStar)" + +#, fuzzy +msgid "" +"[Automatic conversion based on heallth](New-or-Enhanced-Logics.md" +"#automatic-conversion-based-on-heallth) (by obsidianus)" +msgstr "" +"[基于弹药量的自动变形](New-or-Enhanced-Logics.md#automatic-conversion-based-on-" +"ammo)(by FS-21)" + msgid "Vanilla fixes:" msgstr "原版问题修复:" @@ -2854,6 +2877,11 @@ msgid "" "building when deactivated (by NetsuNegi)" msgstr "修复了单位失灵时不会重置其与相链接建筑链接状态的 Bug(by NetsuNegi)" +msgid "" +"Fixed an issue where setting a production building as `Primary` could " +"cause it to enter an unload state (by FlyStar)" +msgstr "" + msgid "Phobos fixes:" msgstr "Phobos 过往版本问题修复:" @@ -3228,6 +3256,12 @@ msgid "" "InitialPayload logic (by Noble_Fish)" msgstr "移除了禁止步兵使用初始载员逻辑的限制(by Noble_Fish)" +msgid "" +"`ProjectileRange` now has weapon range modifiers applied to it if greater" +" than 0 and unless `ProjectileRange.ApplyModifiers` is set to false on " +"the WeaponType (by Starkku)" +msgstr "" + msgid "Pre-release changes" msgstr "预发布版本更改" From d563f2dda3cc12d93beb4a0f10d4d508a0962d57 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 18:26:44 +0300 Subject: [PATCH 49/55] docs: harden sanitize_system_messages against docutils message format Replace the regex that matched docutils' system-message render format with a format-agnostic repair: docutils prepends the node's own source path when it formats the attached message, so strip everything from that path onwards. This survives smart-quote transforms and wording changes to the message. Note: the source must be read with getattr(node, 'source') - MyST stores it as a plain attribute, not in the docutils attribute map. A structural alternative (rawsource == astext) does not work because smart quotes already curl the message's inner quotes by doctree-read. Assisted-by: DeepSeek V4 Flash --- docs/_ext/sanitize_system_messages.py | 28 ++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/_ext/sanitize_system_messages.py b/docs/_ext/sanitize_system_messages.py index 4d0bac0593..3ec6765b7b 100644 --- a/docs/_ext/sanitize_system_messages.py +++ b/docs/_ext/sanitize_system_messages.py @@ -29,17 +29,9 @@ from __future__ import annotations -import re - from docutils import nodes -__version__ = '1.0.0' - -# Matches the leading part of the message that docutils appends to ``rawsource`` -# via ``system_message.astext()``: "<source>:<line>: (LEVEL/N) <message>". -_MESSAGE_START = re.compile( - r'(?:[A-Za-z]:)?[\\/][^\r\n:]+:\d+: \((?:INFO|WARNING|ERROR|SEVERE)/\d+\) ' -) +__version__ = '1.1.0' def _repair(app, doctree) -> None: @@ -51,13 +43,23 @@ def _repair(app, doctree) -> None: # 2. Restore rawsource that ApplySourceWorkaround polluted with the # system-message text. + # + # docutils prepends the node's own source path when it formats the + # system message, so the appended segment always starts with that path. + # Strip everything from the path onwards instead of matching the message + # format - this survives smart-quote transforms and changes to how + # docutils words the message. + # + # Reading the source with getattr() is deliberate: MyST stores it as a + # plain attribute, so the docutils attribute-map lookup would return None. for node in doctree.findall(nodes.TextElement): raw = node.rawsource - if not raw: + src = getattr(node, 'source', None) + if not raw or not src or src not in raw: continue - match = _MESSAGE_START.search(raw) - if match: - node.rawsource = raw[:match.start()].rstrip() + idx = raw.find(src) + if idx > 0: + node.rawsource = raw[:idx].rstrip() def setup(app): From 2229d489ddf6bc64f5e6a37a5c4e2f7cc46f83c6 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 18:28:45 +0300 Subject: [PATCH 50/55] docs: Deepusheenk-translated stuff --- docs/locale/zh_CN/LC_MESSAGES/CREDITS.po | 11 +++++------ .../LC_MESSAGES/Fixed-or-Improved-Logics.po | 4 +++- .../zh_CN/LC_MESSAGES/Interoperability.po | 3 --- .../zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po | 8 ++++++-- docs/locale/zh_CN/LC_MESSAGES/Whats-New.po | 17 ++++++++--------- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po index ec2ed3df51..cd22b5de2e 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po +++ b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po @@ -906,7 +906,7 @@ msgid "Tank Bunker improvements" msgstr "坦克碉堡改进" msgid "`ProjectileRange` weapon range modifiers interaction fix" -msgstr "" +msgstr "修复了 `ProjectileRange` 应用武器射程修正时的交互问题" msgid "**Morton (MortonPL)**:" msgstr "**Morton (MortonPL)**:" @@ -1347,17 +1347,16 @@ msgid "" "`EVAIndex` is no longer reset after load game" msgstr "`EVA.Tag` 已经支持在国家层面设置并且 `EVAIndex` 不再在读档后重置" -#, fuzzy msgid "DeployFire supports buildings" -msgstr "低优先级部署" +msgstr "`DeployFire` 支持建筑" msgid "`OmniFire` supports buildings with `Turret=yes`" -msgstr "" +msgstr "`OmniFire` 支持拥有 `Turret=yes` 的建筑" msgid "" "Fixed an issue where setting a production building as `Primary` could " "cause it to enter an unload state" -msgstr "" +msgstr "修复了将生产建筑设为 `Primary` 时可能使其进入卸载状态的问题" msgid "**NetsuNegi**:" msgstr "**NetsuNegi**:" @@ -3148,5 +3147,5 @@ msgid "" msgstr "为 `BannerType` 添加了 `ClampToScreen` 标签以控制横幅位置是否被限制在可视化区域内" msgid "**obsidianus** - Automatic conversion based on health" -msgstr "" +msgstr "**obsidianus** - 自动根据血量变形" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po index d924630045..a0c38a2a1e 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po @@ -1672,7 +1672,7 @@ msgstr "修复了单位失灵时不会重置其与相链接建筑链接状态的 msgid "" "Fixed an issue where setting a production building as `Primary` could " "cause it to enter an unload state." -msgstr "" +msgstr "修复了将生产建筑设为 `Primary` 时可能使其进入卸载状态的问题。" msgid "Fixes / interactions with other extensions" msgstr "修复或与其他扩展的交互" @@ -1934,6 +1934,8 @@ msgid "" " than 0 and unless `ProjectileRange.ApplyModifiers` is set to false on " "the WeaponType" msgstr "" +"`ProjectileRange` 现在在大于 0 时会应用武器射程修正,除非在 WeaponType 上将 " +"`ProjectileRange.ApplyModifiers` 设置为 false" msgid "Newly added global settings" msgstr "新增的全局设定" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po b/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po index 5588531a17..9306f051ad 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Interoperability.po @@ -94,7 +94,6 @@ msgstr "API 版本跟踪" msgid "Semantic Versioning Rules" msgstr "语义化版本规则" -#, fuzzy msgid "" "**Major (X)**: Increment for breaking changes (backward incompatible API " "modifications).\\ Example: Deleting an API, changing function parameters," @@ -104,7 +103,6 @@ msgstr "" "**主版本号 (X)**:递增表示发生了破坏性变更(向后不兼容的 API 修改)。<br>示例:删除 " "API、更改函数参数、修改数据结构导致现有代码损坏。<br>当此版本号递增时,次版本号和修订号重置为 0(例如 1.2.3 → 2.0.0)。" -#, fuzzy msgid "" "**Minor (Y)**: Increment for backward-compatible new features.\\ Example:" " Adding a new API function, new parameters that don't break existing " @@ -113,7 +111,6 @@ msgstr "" "**次版本号 (Y)**:递增表示增加了向后兼容的新功能。<br>示例:添加新的 API " "函数、增加不破坏现有代码的新参数。<br>当此版本号递增时,修订号重置为 0(例如 1.2.3 → 1.3.0)。" -#, fuzzy msgid "" "**Patch (Z)**: Increment for backward-compatible miscs (no API " "changes).\\ Example: Fixing crashes, correcting calculations, optimizing " diff --git a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po index 0b7290d4a6..5cffb3b17a 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/New-or-Enhanced-Logics.po @@ -1719,22 +1719,26 @@ msgid "" msgstr "在原版中,`RevealToAll` 硬编码揭示半径为 `Sight` 的区域。现在你可以自定义了。" msgid "DeployFire supports" -msgstr "" +msgstr "`DeployFire` 支持建筑" msgid "Building types now also support using `DeployFire` and `DeployFireWeapon`." -msgstr "" +msgstr "建筑类型现在也支持使用 `DeployFire` 和 `DeployFireWeapon`。" msgid "" "If a building has other configurations such as `Factory`, `GapGenerator`," " or `Passengers`, it will prioritize executing those deployment actions " "instead." msgstr "" +"如果建筑有其他诸如 `Factory`、`GapGenerator` 或 `Passengers` 等配置,则将优" +"先执行这些部署动作。" msgid "" "`DeployFireDelay` specifies the frame interval at which deployed weapons " "attempt to fire. If the weapon is unavailable due to `ROF`, `CanTarget`, " "or other reasons, the deployed weapon will not fire." msgstr "" +"`DeployFireDelay` 指定部署武器尝试开火的帧间隔。如果武器由于 `ROF`、`CanTarget` " +"或其他原因无法开火,则部署武器不会开火。" msgid "Destroyable pathfinding obstacles" msgstr "可摧毁的寻路障碍" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index 91387cbfe6..d78601f689 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -114,6 +114,8 @@ msgid "" "to it if greater than 0 and unless `ProjectileRange.ApplyModifiers` is " "set to false on the WeaponType." msgstr "" +"`ProjectileRange`(Ares 功能)现在在大于 0 时会应用武器射程修正,除非在 " +"WeaponType 上将 `ProjectileRange.ApplyModifiers` 设置为 false。" msgid "" "`Splits.TargetCellRange` < 0 now applies special behaviour where the " @@ -2389,23 +2391,18 @@ msgstr "" "[`600` 配置掉落箱子行为](AI-Scripting-and-Mapping.md#configure-drop-crate)(by " "FS-21)" -#, fuzzy msgid "" "[DeployFire supports buildings](New-or-Enhanced-Logics.md#deployfire-" "supports) (By FlyStar)" -msgstr "[延迟开火](New-or-Enhanced-Logics.md#delayed-firing)(by Starkku)" +msgstr "[`DeployFire` 支持建筑](New-or-Enhanced-Logics.md#deployfire-supports)(by FlyStar)" -#, fuzzy msgid "`OmniFire` supports buildings with `Turret=yes` (by FlyStar)" -msgstr "步兵类现已支持使用 `IsGattling=yes` 开启盖特逻辑(by FlyStar)" +msgstr "`OmniFire` 支持拥有 `Turret=yes` 的建筑(by FlyStar)" -#, fuzzy msgid "" "[Automatic conversion based on heallth](New-or-Enhanced-Logics.md" "#automatic-conversion-based-on-heallth) (by obsidianus)" -msgstr "" -"[基于弹药量的自动变形](New-or-Enhanced-Logics.md#automatic-conversion-based-on-" -"ammo)(by FS-21)" +msgstr "[自动根据血量变形](New-or-Enhanced-Logics.md#automatic-conversion-based-on-heallth)(by obsidianus)" msgid "Vanilla fixes:" msgstr "原版问题修复:" @@ -2880,7 +2877,7 @@ msgstr "修复了单位失灵时不会重置其与相链接建筑链接状态的 msgid "" "Fixed an issue where setting a production building as `Primary` could " "cause it to enter an unload state (by FlyStar)" -msgstr "" +msgstr "修复了将生产建筑设为 `Primary` 时可能使其进入卸载状态的问题(by FlyStar)" msgid "Phobos fixes:" msgstr "Phobos 过往版本问题修复:" @@ -3261,6 +3258,8 @@ msgid "" " than 0 and unless `ProjectileRange.ApplyModifiers` is set to false on " "the WeaponType (by Starkku)" msgstr "" +"`ProjectileRange` 现在在大于 0 时会应用武器射程修正,除非在 WeaponType 上将 " +"`ProjectileRange.ApplyModifiers` 设置为 false(by Starkku)" msgid "Pre-release changes" msgstr "预发布版本更改" From 0c41a38f04aa6482b69c6a1ca2ec69ebdd00d4d5 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Tue, 4 Aug 2026 18:49:30 +0300 Subject: [PATCH 51/55] docs: run system-message sanitization before translation The doctree-read hook (priority 880) fixed gettext extraction but ran after the Locale transform (priority 20), which matches rawsource against the translation catalog - so duplicate headings inside {dropdown}s never got translated (only the first occurrence did). Register the repair as a transform at priority 15, after ApplySourceWorkaround (10) folds the message into rawsource and before Locale (20). Assisted-by: DeepSeek V4 Flash --- docs/_ext/sanitize_system_messages.py | 79 ++++++++++++++++----------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/docs/_ext/sanitize_system_messages.py b/docs/_ext/sanitize_system_messages.py index 3ec6765b7b..772570f3d8 100644 --- a/docs/_ext/sanitize_system_messages.py +++ b/docs/_ext/sanitize_system_messages.py @@ -20,7 +20,10 @@ implicit target name: "vanilla fixes:". The gettext builder later extracts ``rawsource`` as a translatable message, so -these warnings leak into the ``.pot``/``.po`` files. +these warnings leak into the ``.pot``/``.po`` files. And because the ``Locale`` +transform matches ``rawsource`` against the translation catalog, the polluted +``rawsource`` also stops the duplicate headings from being translated - only the +first (non-duplicate) occurrence ever gets a translation. This extension removes the system-message children from the doctree (Sphinx's ``FilterSystemMessages`` would remove them at write time anyway) and restores @@ -30,40 +33,54 @@ from __future__ import annotations from docutils import nodes +from sphinx.transforms import SphinxTransform -__version__ = '1.1.0' - - -def _repair(app, doctree) -> None: - # 1. Drop system-message children from content nodes. - for node in list(doctree.findall(nodes.system_message)): - parent = node.parent - if parent is not None: - parent.remove(node) - - # 2. Restore rawsource that ApplySourceWorkaround polluted with the - # system-message text. - # - # docutils prepends the node's own source path when it formats the - # system message, so the appended segment always starts with that path. - # Strip everything from the path onwards instead of matching the message - # format - this survives smart-quote transforms and changes to how - # docutils words the message. - # - # Reading the source with getattr() is deliberate: MyST stores it as a - # plain attribute, so the docutils attribute-map lookup would return None. - for node in doctree.findall(nodes.TextElement): - raw = node.rawsource - src = getattr(node, 'source', None) - if not raw or not src or src not in raw: - continue - idx = raw.find(src) - if idx > 0: - node.rawsource = raw[:idx].rstrip() +__version__ = '1.2.0' + + +class SanitizeSystemMessages(SphinxTransform): + """Remove docutils system messages and repair the polluted rawsource. + + Must run after ``ApplySourceWorkaround`` (priority 10), which folds the + attached system message into ``rawsource``, but before the ``Locale`` + transform (priority 20), which matches ``rawsource`` against the translation + catalog. A ``doctree-read`` hook (priority 880) is too late: translations + would already have failed to apply. + """ + + default_priority = 15 + + def apply(self, **kwargs) -> None: + # 1. Drop system-message children from content nodes. + for node in list(self.document.findall(nodes.system_message)): + parent = node.parent + if parent is not None: + parent.remove(node) + + # 2. Restore rawsource that ApplySourceWorkaround polluted with the + # system-message text. + # + # docutils prepends the node's own source path when it formats the + # system message, so the appended segment always starts with that + # path. Strip everything from the path onwards instead of matching + # the message format - this survives smart-quote transforms and + # changes to how docutils words the message. + # + # Reading the source with getattr() is deliberate: MyST stores it as + # a plain attribute, so the docutils attribute-map lookup would + # return None. + for node in self.document.findall(nodes.TextElement): + raw = node.rawsource + src = getattr(node, 'source', None) + if not raw or not src or src not in raw: + continue + idx = raw.find(src) + if idx > 0: + node.rawsource = raw[:idx].rstrip() def setup(app): - app.connect('doctree-read', _repair) + app.add_transform(SanitizeSystemMessages) return { 'version': __version__, 'parallel_read_safe': True, From 4fce17b67560d0cadf0038c4e858067005d938fa Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Wed, 5 Aug 2026 00:37:28 +0800 Subject: [PATCH 52/55] [doc] fix the links pointing to "release model and version lifecycle" and "Migration (breaking changes)". The current format cannot be navigated correctly on ReadTheDocs. --- README.md | 4 ++-- docs/locale/zh_CN/LC_MESSAGES/index.po | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bd605077ba..f9b03a34ff 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ You can choose one of the following: - [Latest development branch nightly](https://nightly.link/Phobos-developers/Phobos/blob/develop/.github/workflows/nightly.yml) (added unreleased features that will be in the next pre-release) - Individual new feature nightly builds for testing can be found in [pull requests](https://github.com/Phobos-developers/Phobos/pulls) -To learn how these build types relate to each other and how versioning works, see the [release model and version lifecycle](docs/Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy). +To learn how these build types relate to each other and how versioning works, see the [release model and version lifecycle](Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy). ### Note on nightly builds @@ -48,7 +48,7 @@ Installation and Usage 2. Place those files in the game folder (where your `gamemd.exe` is located), replacing any existing `Syringe.exe` (for example the one shipped with Ares). 3. To launch the game with Phobos (and all other installed Syringe-compatible engine extensions including Ares) you need to execute `Syringe.exe "gamemd.exe" [command line arguments for gamemd.exe]` in command line (omit arguments if you don't need any). `RunAres.bat` from Ares package does the same so you may use that as well. -Be sure to read [migration and breaking changes](docs/Whats-New.md#migration-breaking-changes) to know if you need to adjust something in your mod after Phobos installation (or update). +Be sure to read [migration and breaking changes](Whats-New.md#migration-breaking-changes) to know if you need to adjust something in your mod after Phobos installation (or update). If you already use Ares in your mod, you just need to drop Phobos files mentioned above in your game folder, Syringe will load Phobos automatically. This also applies to mods using XNA client with Syringe; if your mod doesn't use Syringe and Ares (or you just haven't set up the client) yet we recommend to use [CnCNet client mod base by Starkku](https://github.com/Starkku/cncnet-client-mod-base) which is compatible with Ares and Phobos out of the box. diff --git a/docs/locale/zh_CN/LC_MESSAGES/index.po b/docs/locale/zh_CN/LC_MESSAGES/index.po index e4fc991043..db75fbf844 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/index.po +++ b/docs/locale/zh_CN/LC_MESSAGES/index.po @@ -206,11 +206,11 @@ msgstr "" msgid "" "To learn how these build types relate to each other and how versioning " -"works, see the [release model and version lifecycle](docs/Project-" +"works, see the [release model and version lifecycle](Project-" "guidelines-and-policies.md#git-branching-model-version-lifecycle-and-" "release-strategy)." msgstr "" -"要了解这些构建类型之间的关系以及版本管理如何工作,请参阅 [发布模型与版本生命周期](docs/Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy)。" +"要了解这些构建类型之间的关系以及版本管理如何工作,请参阅 [发布模型与版本生命周期](Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy)。" msgid "Note on nightly builds" msgstr "关于自动构建版本的说明" @@ -285,11 +285,11 @@ msgstr "" "`RunAres.bat` 也有相同的功能,因此你也同样可以使用它来启动。" msgid "" -"Be sure to read [migration and breaking changes](docs/Whats-New.md" +"Be sure to read [migration and breaking changes](Whats-New.md" "#migration-breaking-changes) to know if you need to adjust something in " "your mod after Phobos installation (or update)." msgstr "" -"请务必阅读 [迁移与破坏性更改](docs/Whats-New.md#migration-breaking-changes),以了解在安装(或更新)Phobos 后是否需要对 Mod 进行调整。" +"请务必阅读 [迁移与破坏性更改](Whats-New.md#migration-breaking-changes),以了解在安装(或更新)Phobos 后是否需要对 Mod 进行调整。" msgid "" "If you already use Ares in your mod, you just need to drop Phobos files " From 7bef844f2423a1cba1dac69df338367b0066bb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:15:16 +0800 Subject: [PATCH 53/55] Revert AttackMove.StopWhenTargetAcquired and AttackMove.PursuitTarget The implementations of these attack move enhancements from #1730 are problematic, so remove them before the upcoming release. Keeps AttackMove.Follow and AttackMove.IgnoreWeaponCheck, drops the dependent mega mission fix hook (#1759) that only served the removed feature, and updates the Whats-New entry to point to the remaining attack move enhancements. --- docs/New-or-Enhanced-Logics.md | 24 ---------------- docs/Whats-New.md | 2 +- src/Ext/Rules/Body.cpp | 2 -- src/Ext/Rules/Body.h | 2 -- src/Ext/Techno/Hooks.cpp | 50 ---------------------------------- src/Ext/TechnoType/Body.cpp | 4 --- src/Ext/TechnoType/Body.h | 4 --- 7 files changed, 1 insertion(+), 87 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 00e3c041e4..7059ee7500 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1420,30 +1420,6 @@ AttackMove.Aggressive= ; boolean, default to [General] -> AttackMove.Agg AttackMove.UpdateTarget= ; boolean, default to [General] -> AttackMove.UpdateTarget ``` -### Attack move - behavior when target acquired - -- Now you can make attack-moving units stop moving when they spot an enemy using `AttackMove.StopWhenTargetAcquired`. This is more like the attack move behavior in Starcraft and Warcraft. - - This feature is used to prevent units from charging forward and taking more damage during an attack move command. -- You can also make them keep chasing on the spotted target using `AttackMove.PursuitTarget`. - - This feature should be useful for close range units like ZEP. - - -In `rulesmd.ini`: -```ini -[General] -AttackMove.StopWhenTargetAcquired= ; boolean - -[SOMETECHNO] ; TechnoType -AttackMove.StopWhenTargetAcquired= ; boolean, default to [General] -> AttackMove.StopWhenTargetAcquired if set, inverse of OpportunityFire otherwise. -AttackMove.PursuitTarget= ; boolean -``` - -```{note} -1. Many units would have stopped when they found an enemy during an attack move command already. This behavior is independent from `AttackMove.StopWhenTargetAcquired`. -2. Some units (f.ex. jumpjets) will not fire correctly under the vanilla attack move command. The exact reason is not clear, but this feature can fix this problem. -3. Jumpjets with `AttackMove.StopWhenTargetAcquired=true` will stop immediatly and not scatter to a cell. This is designed for practical reason. -``` - ### Attack move - follow - Now you can have some units following surrounding units when executing an attack move command. The follow behavior is equivalent to the behavior of follow command (`[Ctrl]+[Alt]`). diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 82f9eb6e93..c382fd8e45 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -500,7 +500,7 @@ HideShakeEffects=false ; boolean - [Allows refineries to use multiple ActiveAnim simultaneously](Fixed-or-Improved-Logics.md#allows-refineries-to-use-multiple-activeanim-simultaneously) (by TaranDahl) - Electric/RadBeam trail for laser tails (by NetsuNegi) - Add `DebrisMinimums` to keep the count of debris within a certain range (by CrimRecya) -- [Several attackmove related enhancement](New-or-Enhanced-Logics.md#attack-move-behavior-when-target-acquired) (by TaranDahl) +- [Attack move - follow](New-or-Enhanced-Logics.md#attack-move-follow) and [attack move - without weapon](New-or-Enhanced-Logics.md#attack-move-without-weapon) (by TaranDahl) - Ground line for select box (by NetsuNegi) - [Support for more optional weapons](New-or-Enhanced-Logics.md#multi-weapon) (by FlyStar) - [Task subtitles display in the middle of the screen](User-Interface.md#task-subtitles-display-in-the-middle-of-the-screen) (by CrimRecya) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 5cf6099f5f..9975e67b78 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -383,7 +383,6 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->AllowBerzerkOnAllies.Read(exINI, GameStrings::CombatDamage, "AllowBerzerkOnAllies"); this->AttackMove_IgnoreWeaponCheck.Read(exINI, GameStrings::General, "AttackMove.IgnoreWeaponCheck"); - this->AttackMove_StopWhenTargetAcquired.Read(exINI, GameStrings::General, "AttackMove.StopWhenTargetAcquired"); this->Parasite_GrappleAnim.Read(exINI, GameStrings::AudioVisual, "Parasite.GrappleAnim"); this->Parasite_AllowWaterExit.Read(exINI, GameStrings::General, "Parasite.AllowWaterExit"); @@ -923,7 +922,6 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->TintColorForceShield) .Process(this->TintColorBerserk) .Process(this->AttackMove_IgnoreWeaponCheck) - .Process(this->AttackMove_StopWhenTargetAcquired) .Process(this->Parasite_GrappleAnim) .Process(this->Parasite_AllowWaterExit) .Process(this->InfantryAutoDeploy) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 136cf87f17..a81cb4e0c3 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -334,7 +334,6 @@ class RulesExt Valueable<bool> AllowBerzerkOnAllies; Valueable<bool> AttackMove_IgnoreWeaponCheck; - Nullable<bool> AttackMove_StopWhenTargetAcquired; NullableIdx<AnimTypeClass> Parasite_GrappleAnim; Nullable<bool> Parasite_AllowWaterExit; @@ -805,7 +804,6 @@ class RulesExt , TintColorBerserk { 0 } , AttackMove_IgnoreWeaponCheck { false } - , AttackMove_StopWhenTargetAcquired { } , Parasite_GrappleAnim {} , Parasite_AllowWaterExit {} diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 52c290d1cf..fdb073d74d 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -1,5 +1,4 @@ #include <TunnelLocomotionClass.h> -#include <JumpjetLocomotionClass.h> #include <Ext/Aircraft/Body.h> #include <Ext/Anim/Body.h> @@ -1222,55 +1221,6 @@ DEFINE_HOOK(0x519FEC, InfantryClass_UpdatePosition_EngineerRepair, 0xA) #pragma region AttackMove -DEFINE_HOOK(0x4DF410, FootClass_UpdateAttackMove_TargetAcquired, 0x6) -{ - GET(FootClass* const, pThis, ESI); - - auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; - - if (pThis->IsCloseEnoughToAttack(pThis->Target) - && pTypeExt->AttackMove_StopWhenTargetAcquired.Get(RulesExt::Global()->AttackMove_StopWhenTargetAcquired.Get(!pTypeExt->OwnerObject()->OpportunityFire))) - { - if (auto const pJumpjetLoco = locomotion_cast<JumpjetLocomotionClass*>(pThis->Locomotor)) - { - auto const crd = pThis->GetCoords(); - pJumpjetLoco->DestinationCoords.X = crd.X; - pJumpjetLoco->DestinationCoords.Y = crd.Y; - pJumpjetLoco->CurrentSpeed = 0; - pJumpjetLoco->MaxSpeed = 0; - pJumpjetLoco->State = JumpjetLocomotionClass::State::Hovering; - pThis->AbortMotion(); - } - else - { - pThis->StopMoving(); - pThis->AbortMotion(); - } - } - - if (pTypeExt->AttackMove_PursuitTarget) - pThis->SetDestination(pThis->Target, true); - - return 0; -} - -DEFINE_HOOK(0x4DF4DB, TechnoClass_RefreshMegaMission_CheckMissionFix, 0xA) -{ - enum { ClearMegaMission = 0x4DF4F9, ContinueMegaMission = 0x4DF4CF }; - - GET(FootClass* const, pThis, ESI); - - auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; - auto const mission = pThis->GetCurrentMission(); - const bool stopWhenTargetAcquired = pTypeExt->AttackMove_StopWhenTargetAcquired.Get(RulesExt::Global()->AttackMove_StopWhenTargetAcquired.Get(!pTypeExt->OwnerObject()->OpportunityFire)); - bool clearMegaMission = mission != Mission::Guard; - - if (stopWhenTargetAcquired && clearMegaMission) - clearMegaMission = !(mission == Mission::Move && pThis->MegaDestination && pThis->DistanceFrom(pThis->MegaDestination) > 256); - - return clearMegaMission ? ClearMegaMission : ContinueMegaMission; -} - DEFINE_HOOK(0x711E90, TechnoTypeClass_CanAttackMove_IgnoreWeapon, 0x6) { enum { SkipGameCode = 0x711E9A }; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index ceedf8c89d..fdfd642ec0 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1081,8 +1081,6 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->AttackMove_Follow.Read(exINI, pSection, "AttackMove.Follow"); this->AttackMove_Follow_IncludeAir.Read(exINI, pSection, "AttackMove.Follow.IncludeAir"); this->AttackMove_Follow_IfMindControlIsFull.Read(exINI, pSection, "AttackMove.Follow.IfMindControlIsFull"); - this->AttackMove_StopWhenTargetAcquired.Read(exINI, pSection, "AttackMove.StopWhenTargetAcquired"); - this->AttackMove_PursuitTarget.Read(exINI, pSection, "AttackMove.PursuitTarget"); this->Ammo_AutoConvertMinimumAmount.Read(exINI, pSection, "Ammo.AutoConvertMinimumAmount"); this->Ammo_AutoConvertMaximumAmount.Read(exINI, pSection, "Ammo.AutoConvertMaximumAmount"); @@ -1707,8 +1705,6 @@ void TechnoTypeExt::Serialize(T& Stm) .Process(this->AttackMove_Follow) .Process(this->AttackMove_Follow_IncludeAir) .Process(this->AttackMove_Follow_IfMindControlIsFull) - .Process(this->AttackMove_StopWhenTargetAcquired) - .Process(this->AttackMove_PursuitTarget) .Process(this->MultiWeapon) .Process(this->MultiWeapon_IsSecondary) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 63f1afdbd7..ee12e3b29e 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -379,8 +379,6 @@ class TechnoTypeExt : public ObjectTypeExt Valueable<bool> AttackMove_Follow; Valueable<bool> AttackMove_Follow_IncludeAir; Valueable<bool> AttackMove_Follow_IfMindControlIsFull; - Nullable<bool> AttackMove_StopWhenTargetAcquired; - Valueable<bool> AttackMove_PursuitTarget; Valueable<bool> MultiWeapon; ValueableVector<bool> MultiWeapon_IsSecondary; @@ -764,8 +762,6 @@ class TechnoTypeExt : public ObjectTypeExt , AttackMove_Follow { false } , AttackMove_Follow_IncludeAir { false } , AttackMove_Follow_IfMindControlIsFull { false } - , AttackMove_StopWhenTargetAcquired { } - , AttackMove_PursuitTarget { false } , MultiWeapon { false } , MultiWeapon_IsSecondary {} From 24e80283dcdb8f8acb3dc29a3016513bbec9dfbb Mon Sep 17 00:00:00 2001 From: Noble_Fish <1065703286@qq.com> Date: Wed, 5 Aug 2026 22:54:09 +0800 Subject: [PATCH 54/55] Resolve the conflict between VSCode navigation and HTML navigation caused by includes Previously, we often omitted paths in README.md to ensure proper HTML navigation, but this also caused the navigation links on the GitHub project homepage and in VSCode to become incorrect. This issue is now resolved by introducing this extension. Assisted-by: DeepSeek V4 Flash --- README.md | 6 +-- docs/_ext/fix_included_readme_links.py | 55 ++++++++++++++++++++++++++ docs/conf.py | 2 +- docs/locale/zh_CN/LC_MESSAGES/index.po | 6 +-- 4 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 docs/_ext/fix_included_readme_links.py diff --git a/README.md b/README.md index f9b03a34ff..37c16cdc20 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ You can choose one of the following: - [Latest development branch nightly](https://nightly.link/Phobos-developers/Phobos/blob/develop/.github/workflows/nightly.yml) (added unreleased features that will be in the next pre-release) - Individual new feature nightly builds for testing can be found in [pull requests](https://github.com/Phobos-developers/Phobos/pulls) -To learn how these build types relate to each other and how versioning works, see the [release model and version lifecycle](Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy). +To learn how these build types relate to each other and how versioning works, see the [release model and version lifecycle](docs/Project-guidelines-and-policies.md#git-branching-model-version-lifecycle-and-release-strategy). ### Note on nightly builds @@ -48,7 +48,7 @@ Installation and Usage 2. Place those files in the game folder (where your `gamemd.exe` is located), replacing any existing `Syringe.exe` (for example the one shipped with Ares). 3. To launch the game with Phobos (and all other installed Syringe-compatible engine extensions including Ares) you need to execute `Syringe.exe "gamemd.exe" [command line arguments for gamemd.exe]` in command line (omit arguments if you don't need any). `RunAres.bat` from Ares package does the same so you may use that as well. -Be sure to read [migration and breaking changes](Whats-New.md#migration-breaking-changes) to know if you need to adjust something in your mod after Phobos installation (or update). +Be sure to read [migration and breaking changes](docs/Whats-New.md#migration-breaking-changes) to know if you need to adjust something in your mod after Phobos installation (or update). If you already use Ares in your mod, you just need to drop Phobos files mentioned above in your game folder, Syringe will load Phobos automatically. This also applies to mods using XNA client with Syringe; if your mod doesn't use Syringe and Ares (or you just haven't set up the client) yet we recommend to use [CnCNet client mod base by Starkku](https://github.com/Starkku/cncnet-client-mod-base) which is compatible with Ares and Phobos out of the box. @@ -101,7 +101,7 @@ This project was founded by [@Belonit](https://github.com/Belonit) (Gluk-v48) an ### Interoperability -Phobos has opened the external interfaces of some key components. If you are also developing your own engine extension and wish to use Phobos at the same time, please check out [Interoperability](Interoperability.md). +Phobos has opened the external interfaces of some key components. If you are also developing your own engine extension and wish to use Phobos at the same time, please check out [Interoperability](docs/Interoperability.md). ### Maintenance crew diff --git a/docs/_ext/fix_included_readme_links.py b/docs/_ext/fix_included_readme_links.py new file mode 100644 index 0000000000..8426e0d5de --- /dev/null +++ b/docs/_ext/fix_included_readme_links.py @@ -0,0 +1,55 @@ +""" +Sphinx extension to fix relative links in included README.md. + +When README.md is included into index.md via {include}, the links inside +README.md are relative to the project root (e.g. [x](docs/xxx.md)). +But after inclusion, they should be relative to the docs/ directory +(e.g. [x](xxx.md)). This extension converts them at build time. +""" + +import re +from sphinx.application import Sphinx +from sphinx.util.logging import getLogger + +logger = getLogger(__name__) + +def fix_readme_links(app, docname, source): + """ + Modify the source content of docname if it contains an include of README.md. + Replace markdown link patterns that start with 'docs/' with the plain filename. + """ + # Only process the document that includes README.md (assume it's 'index') + if docname != 'index': + return + + content = source[0] + + # Check if this document includes README.md via MyST's {include} + # MyST converts {include} to `.. include::` directive with appropriate path. + # We check both common forms: ../README.md (from docs/index.md) and README.md (if in same dir) + if '.. include:: ../README.md' not in content and '.. include:: README.md' not in content: + return + + # Fix links: [text](docs/xxx.md) -> [text](xxx.md) + # Correctly capture optional anchor: #section + def fix_link(m): + # m.group(1): filename with .md, m.group(2): anchor including #, or None + anchor = m.group(2) or '' + return f']({m.group(1)}{anchor})' + + new_content = re.sub( + r'\]\(docs/([^)]+\.md)(#[^)]*)?\)', + fix_link, + content + ) + + if new_content != content: + source[0] = new_content + +def setup(app): + app.connect('source-read', fix_readme_links) + return { + 'version': '1.0.2', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 363204aeab..e20326a0df 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx_rtd_theme', 'myst_parser', 'sphinx.ext.mathjax', 'sphinx_design', '_ext.sanitize_system_messages'] +extensions = ['sphinx_rtd_theme', 'myst_parser', 'sphinx.ext.mathjax', 'sphinx_design', '_ext.sanitize_system_messages', '_ext.fix_included_readme_links'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/locale/zh_CN/LC_MESSAGES/index.po b/docs/locale/zh_CN/LC_MESSAGES/index.po index db75fbf844..434daa6554 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/index.po +++ b/docs/locale/zh_CN/LC_MESSAGES/index.po @@ -206,7 +206,7 @@ msgstr "" msgid "" "To learn how these build types relate to each other and how versioning " -"works, see the [release model and version lifecycle](Project-" +"works, see the [release model and version lifecycle](docs/Project-" "guidelines-and-policies.md#git-branching-model-version-lifecycle-and-" "release-strategy)." msgstr "" @@ -285,7 +285,7 @@ msgstr "" "`RunAres.bat` 也有相同的功能,因此你也同样可以使用它来启动。" msgid "" -"Be sure to read [migration and breaking changes](Whats-New.md" +"Be sure to read [migration and breaking changes](docs/Whats-New.md" "#migration-breaking-changes) to know if you need to adjust something in " "your mod after Phobos installation (or update)." msgstr "" @@ -415,7 +415,7 @@ msgstr "" msgid "" "Phobos has opened the external interfaces of some key components. If you " "are also developing your own engine extension and wish to use Phobos at " -"the same time, please check out [Interoperability](Interoperability.md)." +"the same time, please check out [Interoperability](docs/Interoperability.md)." msgstr "" "Phobos为一些关键组件打开了外部接口。如果你也在开发自己的引擎扩展并且希望同时使用Phobos,请查阅 " "[互操作性](Interoperability.md)。" From f4c3563433a845d1b0d80924d138af204d662a80 Mon Sep 17 00:00:00 2001 From: Metadorius <crabiter@vivaldi.net> Date: Sat, 15 Aug 2026 21:41:12 +0300 Subject: [PATCH 55/55] feat: label bleeding-edge builds with a clean version and explicit git metadata Local and nightly builds now report the plain four-part version (0.5.0.0) as FileVersion and append the git commit and ref as clearly labeled attributes in ProductVersion (e.g. v0.5.0.0 @ abc1234-dirty @ refs/heads/develop), replacing the semver '+' build-metadata style that implied a tagged release. The numeric version is never replaced by git info. Add an explicit GitDirty yes/no field alongside GitCommit/GitRef in the file metadata, the debug log and the crash report. Bleeding-edge builds (local and nightly) no longer apply the SAVEGAME_ID version offset when saving/loading, so their saves are never filtered by version - each snapshot is unique. Update the docs and the zh_CN translation accordingly. Assisted-by: DeepSeek V4 Flash --- docs/General-Info.md | 4 +- docs/locale/zh_CN/LC_MESSAGES/General-Info.po | 38 ++++++++++++------- src/Misc/ExceptionHandler.Report.cpp | 1 + src/Phobos.Ext.cpp | 9 +++++ src/Phobos.cpp | 1 + src/Phobos.version.h | 25 +++++++++--- src/version.rc | 1 + 7 files changed, 57 insertions(+), 22 deletions(-) diff --git a/docs/General-Info.md b/docs/General-Info.md index e70dfe46c9..d6ab97543b 100644 --- a/docs/General-Info.md +++ b/docs/General-Info.md @@ -9,7 +9,7 @@ There are three main types of Phobos builds: - *pre-release builds* - previously known as *development builds*, these builds mark the start of a new release branch and are used for testing new features before they are finalized. They are numbered with a version and a pre-release suffix (e.g., 0.5-beta1, 0.5-rc2). Mod authors can include these versions with their mods to access the latest features, but we cannot guarantee the absence of bugs; - *nightly builds* - bleeding edge versions which can include prototypes, proofs of concepts, scrapped features etc., in other words - we can't guarantee anything in those builds and they absolutely should NOT be used in mod releases and should only be used to help with development and testing. -Besides the published builds above, any build you make locally (from Visual Studio, VS Code or the build scripts) is marked as a *local build*: like nightly, it is stamped with the git commit and ref it was built from (plus a `-dirty` marker when the working tree has uncommitted changes) and shows a hideable "please test" warning. +Besides the published builds above, any build you make locally (from Visual Studio, VS Code or the build scripts) is marked as a *local build*: like nightly, it is stamped with the git commit and ref it was built from (e.g. `v0.5.0.0 @ abc1234-dirty @ refs/heads/develop`) and shows a hideable "please test" warning. ```{hint} You can find the downloads for these versions on the document's [main page](index.md#downloads). @@ -23,7 +23,7 @@ You can hide the warning by specifying the exact version of the build you use af ## Saved games filtering -Phobos fully supports saving and loading thanks to prototype code from publicly released Ares 0.A source and it implements it's own filtering which shouldn't conflict with Ares save filtering. Save games between different versions are incompatible due to changes to Phobos extension classes which are present in almost every build release. Nightlies and pre-releases of a version share the savegame ID of the stable release they lead up to, so their saves are filtered as a single version. They may still be incompatible with each other if there were changes to extension class fields. +Phobos fully supports saving and loading thanks to prototype code from publicly released Ares 0.A source and it implements its own filtering which shouldn't conflict with Ares save filtering. Save games between different versions are incompatible due to changes to Phobos extension classes which are present in almost every build release. Pre-releases of a version share the savegame ID of the stable release they lead up to, so their saves are filtered as a single version. Nightly and local builds are bleeding-edge snapshots that skip this version-based filtering entirely - each one is a unique snapshot, so their saves are never filtered by version. ## Compatibility diff --git a/docs/locale/zh_CN/LC_MESSAGES/General-Info.po b/docs/locale/zh_CN/LC_MESSAGES/General-Info.po index 95d60ca018..4cd3f34d24 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/General-Info.po +++ b/docs/locale/zh_CN/LC_MESSAGES/General-Info.po @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: Phobos \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-04 10:03+0800\n" +"POT-Creation-Date: 2026-08-15 21:00+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.16.0\n" +"Generated-By: Babel 2.18.0\n" msgid "General Info" msgstr "概况" @@ -43,7 +43,9 @@ msgid "" "include these versions with their mods to access the latest features, but" " we cannot guarantee the absence of bugs;" msgstr "" -"**预发布版本**:即之前的 *开发版*,此类构建标志着新发布分支的开始,用于在新功能最终确定前进行测试。它们的版本号由主版本号加上预发布后缀组成(例如 `0.5-beta1`、`0.5-rc2`)。Mod 作者可以在 Mod 中包含这些版本以获取最新功能,但我们无法保证它们没有 Bug。" +"**预发布版本**:即之前的 " +"*开发版*,此类构建标志着新发布分支的开始,用于在新功能最终确定前进行测试。它们的版本号由主版本号加上预发布后缀组成(例如 " +"`0.5-beta1`、`0.5-rc2`)。Mod 作者可以在 Mod 中包含这些版本以获取最新功能,但我们无法保证它们没有 Bug。" msgid "" "*nightly builds* - bleeding edge versions which can include prototypes, " @@ -59,10 +61,12 @@ msgid "" "Besides the published builds above, any build you make locally (from " "Visual Studio, VS Code or the build scripts) is marked as a *local " "build*: like nightly, it is stamped with the git commit and ref it was " -"built from (plus a `-dirty` marker when the working tree has uncommitted " -"changes) and shows a hideable \"please test\" warning." +"built from (e.g. `v0.5.0.0 @ abc1234-dirty @ refs/heads/develop`) and " +"shows a hideable \"please test\" warning." msgstr "" -"除了上述发布的构建版本,你在本地(通过 Visual Studio、VS Code 或构建脚本)生成的任何构建都会标记为 *本地构建*:与自动构建版本类似,它会标记有构建所基于的 git 提交和引用(当工作树有未提交更改时还会添加 `-dirty` 标记),并显示一个可隐藏的「please test」警告。" +"除了上述发布的构建版本,你在本地(通过 Visual Studio、VS Code 或构建脚本)生成的任何构建都会标记为 " +"*本地构建*:与自动构建版本类似,它会标记有构建所基于的 git 提交和引用(例如 `v0.5.0.0 @ abc1234-dirty @ " +"refs/heads/develop`),并显示一个可隐藏的「please test」警告。" msgid "" "You can find the downloads for these versions on the document's [main " @@ -95,23 +99,29 @@ msgid "" "accepts its own version, so the switch has to be updated whenever you " "update Phobos. Nightly builds don't support hiding the warning at all." msgstr "" -"你可以通过在命令行参数 `-HideVersionWarning=` 后面指定所使用的构建的确切版本号来隐藏该警告(例如 `-HideVersionWarning=0.5.0.0-beta1` 将隐藏 Phobos `v0.5-beta1` 预发布版的警告)。该版本号会显示在警告信息以及对应发布的标题中;每个构建只接受其自身的版本号,因此每当更新 Phobos 时都需要同步更新该开关参数。自动构建版完全不支持隐藏警告。" +"你可以通过在命令行参数 `-HideVersionWarning=` 后面指定所使用的构建的确切版本号来隐藏该警告(例如 " +"`-HideVersionWarning=0.5.0.0-beta1` 将隐藏 Phobos `v0.5-beta1` " +"预发布版的警告)。该版本号会显示在警告信息以及对应发布的标题中;每个构建只接受其自身的版本号,因此每当更新 Phobos " +"时都需要同步更新该开关参数。自动构建版完全不支持隐藏警告。" msgid "Saved games filtering" msgstr "存档过滤" msgid "" "Phobos fully supports saving and loading thanks to prototype code from " -"publicly released Ares 0.A source and it implements it's own filtering " +"publicly released Ares 0.A source and it implements its own filtering " "which shouldn't conflict with Ares save filtering. Save games between " "different versions are incompatible due to changes to Phobos extension " -"classes which are present in almost every build release. Nightlies and " -"pre-releases of a version share the savegame ID of the stable release " -"they lead up to, so their saves are filtered as a single version. They " -"may still be incompatible with each other if there were changes to " -"extension class fields." +"classes which are present in almost every build release. Pre-releases of " +"a version share the savegame ID of the stable release they lead up to, so" +" their saves are filtered as a single version. Nightly and local builds " +"are bleeding-edge snapshots that skip this version-based filtering " +"entirely - each one is a unique snapshot, so their saves are never " +"filtered by version." msgstr "" -"得益于 Ares0.A 公开的源码范例,Phobos 完全支持存读档功能,并且实现了独立的过滤机制,这与 Ares 的存档过滤机制没有冲突。由于 Phobos 扩展类几乎在每个版本都有所变动,所以不同版本间的存档互不兼容。某个版本的自动构建版本和预发布版本共享其所对应稳定版本的存档 ID,因此它们的存档会被视为同一版本进行过滤。但如果扩展类字段发生了更改,它们之间仍然可能互不兼容。" +"得益于 Ares0.A 公开的源码范例,Phobos 完全支持存读档功能,并且实现了独立的过滤机制,这与 Ares 的存档过滤机制没有冲突。由于 " +"Phobos 扩展类几乎在每个版本都有所变动,所以不同版本间的存档互不兼容。某个版本的预发布版与其所对应的稳定版共享存档 " +"ID,因此它们的存档会被视为同一版本进行过滤。而自动构建版和本地构建版属于前沿快照,会完全跳过这种基于版本的过滤 - 每个快照都是独一无二的,因此它们的存档永远不会按版本进行过滤。" msgid "Compatibility" msgstr "兼容性" diff --git a/src/Misc/ExceptionHandler.Report.cpp b/src/Misc/ExceptionHandler.Report.cpp index 112de4ca78..df3711da2d 100644 --- a/src/Misc/ExceptionHandler.Report.cpp +++ b/src/Misc/ExceptionHandler.Report.cpp @@ -469,6 +469,7 @@ namespace ExceptionHandler::Append("Phobos version: " PRODUCT_VERSION "\r\n"); #ifdef STR_GIT_COMMIT ExceptionHandler::Append("Git commit: " STR_GIT_COMMIT "\r\n"); + ExceptionHandler::Append("Git dirty: " GIT_DIRTY_FLAG "\r\n"); #endif #ifdef STR_GIT_REF ExceptionHandler::Append("Git ref: " STR_GIT_REF "\r\n"); diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 58e30fc369..bfd410084f 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -407,7 +407,14 @@ DEFINE_HOOK(0x67D04E, GameSave_SavegameInformation, 0x7) { REF_STACK(SavegameInformation, Info, STACK_OFFSET(0x4A4, -0x3F4)); + // Bleeding-edge builds (local and nightly) skip the version offset entirely: each one is a + // unique snapshot, so version-based filtering would both falsely claim compatibility between + // same-version nightlies and hide saves from other bleeding-edge snapshots. Only release and + // pre-release builds get the offset, which makes saves from different Phobos versions filter + // out of each other's load lists. +#if defined(RELEASE) Info.InternalVersion = Info.InternalVersion + SAVEGAME_ID; +#endif strncat(Info.ExecutableName.data(), " + Phobos " FILE_VERSION_STR, Info.ExecutableName.Size - sizeof(" + Phobos " FILE_VERSION_STR) @@ -419,8 +426,10 @@ DEFINE_HOOK(0x67D04E, GameSave_SavegameInformation, 0x7) DEFINE_HOOK_AGAIN(0x67FD9D, LoadOptionsClass_GetFileInfo, 0x7) DEFINE_HOOK(0x67FDB1, LoadOptionsClass_GetFileInfo, 0x7) { +#if defined(RELEASE) GET(SavegameInformation*, Info, ESI); Info->InternalVersion = Info->InternalVersion - SAVEGAME_ID; +#endif return 0; } diff --git a/src/Phobos.cpp b/src/Phobos.cpp index 2ba8f626e1..ee211ae571 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -138,6 +138,7 @@ void Phobos::CmdLineParse(char** ppArgs, int nNumArgs) Debug::Log("Initialized version: " PRODUCT_VERSION "\n"); #ifdef STR_GIT_COMMIT Debug::Log("Git commit: " STR_GIT_COMMIT "\n"); + Debug::Log("Git dirty: " GIT_DIRTY_FLAG "\n"); #endif #ifdef STR_GIT_REF Debug::Log("Git ref: " STR_GIT_REF "\n"); diff --git a/src/Phobos.version.h b/src/Phobos.version.h index 32a63432e5..9953e9e978 100644 --- a/src/Phobos.version.h +++ b/src/Phobos.version.h @@ -59,7 +59,14 @@ #define VERSION_PREFIX "v" // STR_GIT_COMMIT / STR_GIT_REF are defined by the generated Phobos.Git.h when Git info is -// available; STR_GIT_DIRTY falls back to empty when the working tree is clean. +// available. STR_GIT_DIRTY is defined only when the working tree was dirty; expose that as an +// explicit yes/no flag (for the "GitDirty" metadata field and the git log lines) before the +// empty fallback below makes STR_GIT_DIRTY unconditional. +#ifdef STR_GIT_DIRTY + #define GIT_DIRTY_FLAG "yes" +#else + #define GIT_DIRTY_FLAG "no" +#endif #ifndef STR_GIT_DIRTY #define STR_GIT_DIRTY "" #endif @@ -81,22 +88,28 @@ #endif #else // Nightly (CI) and local builds are one mostly unified build differing in warning and in - // build type name; the version string carries the Git commit and ref when available. + // build type name; the version string appends the Git commit and ref when available. #ifdef NIGHTLY #define BUILD_TYPE_NAME "nightly build" #else #define BUILD_TYPE_NAME "local build" #endif + // Bleeding-edge builds append git info as clearly labeled attributes separated by " @ ", + // never replacing the numeric version: e.g. v0.5.0.0 @ ab2b51615-dirty @ refs/heads/develop. + // The suffixes build on the raw STR_GIT_COMMIT / STR_GIT_REF / STR_GIT_DIRTY values from the + // generated Phobos.Git.h. #ifdef STR_GIT_COMMIT - #define FILE_VERSION_STR VERSION_LONG_STR "+" STR_GIT_COMMIT STR_GIT_DIRTY + #define GIT_COMMIT_SUFFIX " @ " STR_GIT_COMMIT STR_GIT_DIRTY #else - #define FILE_VERSION_STR VERSION_LONG_STR + #define GIT_COMMIT_SUFFIX "" #endif #ifdef STR_GIT_REF - #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR " @ " STR_GIT_REF + #define GIT_REF_SUFFIX " @ " STR_GIT_REF #else - #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR + #define GIT_REF_SUFFIX "" #endif + #define FILE_VERSION_STR VERSION_LONG_STR + #define PRODUCT_VERSION VERSION_PREFIX FILE_VERSION_STR GIT_COMMIT_SUFFIX GIT_REF_SUFFIX #endif // A testing build carries the on-screen "please test the build before shipping" warning and the diff --git a/src/version.rc b/src/version.rc index 166256e7b9..3669055ce9 100644 --- a/src/version.rc +++ b/src/version.rc @@ -40,6 +40,7 @@ BEGIN VALUE "Translation", 0x0409, 1200 #ifdef STR_GIT_COMMIT VALUE "GitCommit", STR_GIT_COMMIT + VALUE "GitDirty", GIT_DIRTY_FLAG #endif #ifdef STR_GIT_REF VALUE "GitRef", STR_GIT_REF