Skip to content

feat: support linux-arm64 - #260

Open
HanosThePlacid wants to merge 1 commit into
StratumServer:indevfrom
HanosThePlacid:feat/linux-arm64
Open

feat: support linux-arm64#260
HanosThePlacid wants to merge 1 commit into
StratumServer:indevfrom
HanosThePlacid:feat/linux-arm64

Conversation

@HanosThePlacid

Copy link
Copy Markdown

Summary

Adds linux-arm64 support: a RID in the release pipeline, and an aarch64 native swap in VanillaBootstrap.

Anego publishes no linux-arm64 server archive — the manifest keys are windows, windowsupdate, linuxserver, windowsserver, mac-x64, mac-arm64, linux — so Stratum has been x86_64-only. On an arm64 host the launcher dies with cannot 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.gz turns up exactly five x86-64 files, and a dedicated server needs three:

file needed by a server?
Lib/libe_sqlite3.so yes — world save DB
Lib/libSkiaSharp.so yes — asserted byStratumRuntime preflight
Lib/libzstd.so yes
Lib/libopenal.so.1 no — audio
VintagestoryServer no — vanilla apphost; Stratum ships its own

Everything 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.csprojPlatformTarget was pinned to x64 unconditionally, which fails NETSDK1032 against -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.ps1 needed no changes: $Rids is already a parameter, the loop already does -r $rid, and the $rid -like 'win-*' exe-name check is already correct for linux-arm64.

VanillaBootstrap.csGetArchiveForPlatform is untouched; the official linux-x64 archive is still the right source for all assets and IL. A new post-extract step runs only on Linux && Architecture.Arm64, before PatchedFileOverlay, and replaces the natives with the aarch64 builds from anegostudios/VintagestoryServerArm64. It reuses the existing ExtractTarGz, FindContentRoot, and DownloadFile helpers.

Only *.so files are taken from that overlay. It also ships VintagestoryServer host 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 sha256 digest is verified, and the asset name is recorded in .stratum-arm64-natives so 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_TOKEN is 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

  • Bug fix
  • Performance
  • New feature
  • Refactor or cleanup
  • Docs or build

Checklist

  • .\scripts\extract-patches.ps1 ran clean. — n/a, no patches/ or sources/ changes. This PR touches only StratumServer/, the release workflow, and docs, none of which are extractor inputs.
  • dotnet build VintageStory.slnx -c Release is green.
  • Every vanilla edit has a // Stratum marker. — n/a, no vanilla files touched.
  • No vanilla source committed.
  • Tested on a real server start, not just compilation.

Testing

Built and published all three RIDs from this branch on upstream/indev; readelf/ELF e_machine confirms the linux-arm64 apphost is AArch64 and linux-x64 is unchanged at x86-64.

Booted on real aarch64 hardware (Debian 13, kernel 6.8, 4 cores, .NET 10.0.10) under a Pterodactyl panel:

Stratum: installed 9637 vanilla file(s) (existing files were refreshed)
Stratum: arm64 node detected; fetching native overlay vs_server_linux-arm64_1.22.0.tar.gz
Stratum: replaced 3 native librar(ies) with aarch64 builds from vs_server_linux-arm64_1.22.0.tar.gz
Stratum: applied patched files (10 file(s))
...
[Server Notification] Zstd Version: 1.5.7
[Server Notification] [Stratum] preflight passed
[Server Notification] [Stratum] runtime ready.
[Server Event] Dedicated Server now running on Port 26000 and all ips!

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 patched VintagestoryLib is live rather than a silent vanilla fallback.

Also boot-tested under QEMU aarch64 emulation on ghcr.io/parkervcp/yolks:dotnet_10 before the hardware run, with the same result.

Two cosmetic arm64 gaps observed, both pre-existing and out of scope here:

  • CPU: Unknown — aarch64 /proc/cpuinfo has no model name field.
  • [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:

  1. The overlay repo is a hardcoded const. Would you prefer it in stratum.json, or an env override?
  2. The overlay is fetched at first boot, adding a startup dependency on api.github.com. An alternative is resolving it at install time, but that does not fit the current single-binary bootstrap model.

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>
@trevorftp trevorftp added the status: needs-review Ready for maintainer review. label Aug 16, 2026
@trevorftp trevorftp moved this from Inbox to Review / Testing in Stratum Roadmap Aug 16, 2026
@trevorftp trevorftp moved this from Review / Testing to Blocked in Stratum Roadmap Aug 17, 2026
@trevorftp trevorftp added status: blocked Waiting on dependency, decision, or external fix. and removed status: needs-review Ready for maintainer review. labels Aug 17, 2026
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))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@pizza2004

Copy link
Copy Markdown

Anego publishes no linux-arm64 server archive

Do you think we should be?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: blocked Waiting on dependency, decision, or external fix.

Projects

Status: Blocked

Development

Successfully merging this pull request may close these issues.

3 participants