feat: support linux-arm64 - #260
Conversation
Anego publishes no linux-arm64 Vintage Story server archive, so Stratum could only ever run on x86_64 nodes. Almost nothing in the archive is actually architecture-specific, though: checking ELF headers across vs_server_linux-x64_1.22.6.tar.gz turns up exactly five x86-64 files, and a dedicated server only needs three of them: Lib/libe_sqlite3.so world save DB Lib/libSkiaSharp.so asserted by Stratum's own preflight Lib/libzstd.so Lib/libopenal.so.1 audio, never loaded by a server VintagestoryServer vanilla apphost, unused - Stratum ships its own Everything else is portable IL. Every library project already builds AnyCPU with no RID or native package references, and the launcher is published framework-dependent, so the only architecture-specific build artifact is the apphost - which the SDK cross-targets from an x64 runner. VanillaBootstrap now keeps using the official linux-x64 archive for assets and IL, then replaces the natives with the aarch64 builds from anegostudios/VintagestoryServerArm64. Only *.so files are taken from that overlay: it also ships VintagestoryServer host files, and it lags the base game (newest overlay is 1.22.0 against a 1.22.6 base), so copying its managed or host files would risk a version mismatch against Stratum's patched assemblies. The natives are self-contained third-party libraries whose ABI does not track Vintage Story patch releases. Overlays are resolved per minor version, the published sha256 digest is verified, and the asset name is recorded in .stratum-arm64-natives so reboots do no network I/O. StratumServer.csproj pinned PlatformTarget to x64 unconditionally, which fails NETSDK1032 against -r linux-arm64. It now applies only to RID-less builds, so existing behaviour is unchanged. Verified by booting the linux-arm64 build under QEMU aarch64 emulation on ghcr.io/parkervcp/yolks:dotnet_10: the launcher swapped in 3 aarch64 natives, applied the patched assemblies, and reached "Dedicated Server now running on Port 42420" with no exceptions, writing a 6.5 MB world DB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| private static void ApplyArm64NativeOverlay(string installDir, string cacheDir, string baseGameVersion, bool overwriteExisting) | ||
| { | ||
| string markerPath = Path.Combine(installDir, ".stratum-arm64-natives"); | ||
| if (!overwriteExisting && File.Exists(markerPath)) |
There was a problem hiding this comment.
This never fires? overwriteExisting is always true by the time this method runs, seems like it only gets called when EnsureVanillaAssets didn't do its early return at line 30, and that early return only skips when refresh is true or markerMatches is false. Either of which forces overwriteExisting = true at line 36.
So this check can't ever take the skip branch? also every refresh would redo the GitHub API and re-verifies even if we had the right overlay already installed. I would probably want to check the marker before deciding to touch the network at all, which is the same as the .stratum-base check up top
|
|
||
| private static bool VerifyArm64Digest(string path, string digest) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(digest)) |
There was a problem hiding this comment.
This seems like it would just skip verification instead of failing when GitHub's asset JSON has no digest field, which probably isn't guaranteed on every release. Your main archive path a few methods up treats a bad or missing checksum as fatal. And that means this path is doing the same job, downloading code from a third-party repo that gets loaded into the server process, so I'd want it at least as strict.
| private const string VersionManifestUrl = "https://api.vintagestory.at/stable-unstable.json"; | ||
|
|
||
| // Anego publishes the aarch64 natives as a standalone overlay; the main manifest is x64-only. | ||
| private const string Arm64ReleasesUrl = "https://api.github.com/repos/anegostudios/VintagestoryServerArm64/releases?per_page=50"; |
There was a problem hiding this comment.
Would rather see this pinned like the rest of our deps in forks.json than resolved dynamically
| } | ||
| if (fallback.Name != null) | ||
| { | ||
| Console.WriteLine($"Stratum: no arm64 overlay published for {MajorMinor(baseGameVersion)}.x; falling back to {fallback.Name}"); |
There was a problem hiding this comment.
Worth a second look regardless of my pin question above, this falls back to installing whatever overlay is newest even if it doesn't match the base game's minor version
Do you think we should be? |
Summary
Adds
linux-arm64support: a RID in the release pipeline, and an aarch64 native swap inVanillaBootstrap.Anego publishes no
linux-arm64server archive — the manifest keys arewindows,windowsupdate,linuxserver,windowsserver,mac-x64,mac-arm64,linux— so Stratum has been x86_64-only. On an arm64 host the launcher dies withcannot execute binary file: Exec format error(exit 126).Very little of the game is actually architecture-specific. Checking ELF headers across
vs_server_linux-x64_1.22.6.tar.gzturns up exactly five x86-64 files, and a dedicated server needs three:Lib/libe_sqlite3.soLib/libSkiaSharp.soStratumRuntimepreflightLib/libzstd.soLib/libopenal.so.1VintagestoryServerEverything else (
assets/,VintagestoryLib.dll,VintagestoryAPI.dll,Lib/*.dll,Mods/VS*.dll) is portable IL. Every library project already builds AnyCPU with no RID or native package references, and the launcher publishes framework-dependent, so the only architecture-specific build artifact is the apphost — which the SDK cross-targets from the existing x64 runner. No arm64 CI runner is needed.Changes
StratumServer.csproj—PlatformTargetwas pinned tox64unconditionally, which failsNETSDK1032against-r linux-arm64. Now applies only to RID-less builds, so existing behaviour is byte-for-byte unchanged; with a RID set the SDK infers the platform from it.release.yml—-Rids linux-x64,linux-arm64,win-x64.pack-release.ps1needed no changes:$Ridsis already a parameter, the loop already does-r $rid, and the$rid -like 'win-*'exe-name check is already correct forlinux-arm64.VanillaBootstrap.cs—GetArchiveForPlatformis untouched; the officiallinux-x64archive is still the right source for all assets and IL. A new post-extract step runs only onLinux && Architecture.Arm64, beforePatchedFileOverlay, and replaces the natives with the aarch64 builds from anegostudios/VintagestoryServerArm64. It reuses the existingExtractTarGz,FindContentRoot, andDownloadFilehelpers.Only
*.sofiles are taken from that overlay. It also shipsVintagestoryServerhost files built at 1.22.0 against a 1.22.6 base, and Stratum runs its own apphost, so copying its managed or host files would risk a version mismatch against the patched assemblies. The natives are self-contained third-party libraries whose ABI does not track Vintage Story patch releases, so taking those alone is both sufficient and safe.Overlays are resolved per minor version (1.22.6 → newest 1.22.x), the GitHub-published
sha256digest is verified, and the asset name is recorded in.stratum-arm64-nativesso later boots do no network I/O. A missing or empty overlay throws rather than silently leaving x86-64 natives in place, which would otherwise surface as a confusing crash inside SQLite or Skia.GITHUB_TOKENis honoured if present — the anonymous GitHub API limit is 60 requests/hour per IP.x86_64 behaviour is unchanged: the new branch never executes.
Type
Checklist
.\scripts\extract-patches.ps1ran clean. — n/a, nopatches/orsources/changes. This PR touches onlyStratumServer/, the release workflow, and docs, none of which are extractor inputs.dotnet build VintageStory.slnx -c Releaseis green.// Stratummarker. — n/a, no vanilla files touched.Testing
Built and published all three RIDs from this branch on
upstream/indev;readelf/ELFe_machineconfirms thelinux-arm64apphost isAArch64andlinux-x64is unchanged atx86-64.Booted on real aarch64 hardware (Debian 13, kernel 6.8, 4 cores, .NET 10.0.10) under a Pterodactyl panel:
A player connected and played; worldgen produced an 87-schematic dungeon, and the SQLite savegame was created and written — so all three swapped natives are exercised. No exceptions. Patched systems (
repulse-agents,collect-stride, parallel pathfinding) reported in, confirming the patchedVintagestoryLibis live rather than a silent vanilla fallback.Also boot-tested under QEMU aarch64 emulation on
ghcr.io/parkervcp/yolks:dotnet_10before the hardware run, with the same result.Two cosmetic arm64 gaps observed, both pre-existing and out of scope here:
CPU: Unknown— aarch64/proc/cpuinfohas nomodel namefield.[Stratum] metrics pipe ready: \\.\pipe\stratum-metrics-22— Windows pipe syntax printed on Linux. .NET maps it to a Unix socket, so it works; only the display string is wrong.Open questions for maintainers
Happy to adjust any of these:
stratum.json, or an env override?api.github.com. An alternative is resolving it at install time, but that does not fit the current single-binary bootstrap model.