-
Notifications
You must be signed in to change notification settings - Fork 1
Compatibility
Empirical results from the version compatibility sweep (.github/workflows/compat.yml),
which builds Atlas against each game version's own dlls and runs the full E2E suite on a real
embedded server:
| Version | Status | Notes |
|---|---|---|
| 1.22.3 | Compatible | All E2E scenarios green, tested on every push |
| 1.22.2 | Compatible | All E2E scenarios green, tested on every push |
| 1.22.1 | Compatible | All E2E scenarios green, tested on every push |
| 1.22.0 | Compatible | All E2E scenarios green, tested on every push |
| 1.21.7 | Compatible | All E2E scenarios green, tested on every push (the CI-verified 1.21 version) |
| 1.20.12 | Best effort | Full E2E suite green when rebuilt against its dlls; verified on the weekly sweep, not per push |
| 1.19.8 | Not supported | Rejected at boot: ServerMain.PreLaunch and the ServerProgramArgs boot signature differ beyond what the shim bridges |
| 1.18.15 | Not supported | Rejected at boot: same as above, plus Vintagestory.API.Common.Func collides with System.Func
|
Each row is the latest patch of its minor available on the stable CDN; 1.18.0 through 1.18.7
predate the game's .NET migration and ship under a different server archive entirely. The
supported floor is Vintage Story 1.21.0 since Atlas 0.9.0 (issue #15): the entire
compile-level gap below 1.22 was the server exit lifecycle, now owned by the runtime shim
EngineCompat (exit-state field, Stop signature, network version read from the loaded
engine's metadata), boot-validated fail-fast on engines whose layout drifted.
For 1.19.x and older, the boot API changes shape beyond what reflection can reasonably
bridge (ServerMain.PreLaunch, the ServerProgramArgs signature), so Atlas rejects those
versions up front with an AtlasSetupException citing the floor. Running a prebuilt Atlas
binary on an engine other than the one it was built against used to carry a caveat here
("designed for but no CI lane yet"); since 0.10.0 that path is auto-staged at launch and
proven by a per-push CI lane. See Multi-install runs below.
Repointing VINTAGE_STORY at a different install (another game version, or a server fork
like Stratum) does not require rebuilding the test project. Since 0.10.0 (issue #49) Atlas
auto-stages the engine assembly at launch, so a suite built once runs against whichever
install the variable points at.
The trap it removes. The test output carries its own VintagestoryAPI.dll copy, and
that copy wins assembly probing, so it mixes with the target install's VintagestoryLib: on
a same-version fork the boot dies with a cryptic MissingFieldException, and across versions
the game thread's own JIT loads VintagestoryLib against the stale copy and kills the whole
testhost with a raw TypeLoadException before a single Atlas frame runs. In-process
redirection cannot fix it (the default binder prefers the app-path copy for a colliding
simple name), so Atlas rewrites the copy itself.
How auto-staging works. A module initializer runs before anything can bind the copy
(Atlas.XUnit's fires at xUnit discovery, Atlas's own covers direct ServerHost flows) and
rewrites the test-output VintagestoryAPI.dll and its .pdb as a unit from the target
install, through same-directory temp files and atomic renames, with a one-line stderr notice.
When staging is genuinely impossible (the stale copy already bound because engine types were
JITted before any Atlas code ran, a read-only test output, or an install shipping no
VintagestoryAPI.pdb), the boot fails fast with an AtlasSetupException naming both file
identities and the remedies; an already-bound copy is still re-staged on disk, so a plain
re-run recovers without a rebuild. When the copies are already identical (the documented
rebuild flow) staging is a no-op and behavior is byte-for-byte unchanged.
The supported pattern: build newest, run older. Direction matters, because of the second
file the game provides. The test output's Newtonsoft.Json.dll is the BUILD-time install's
game build (13.0.4 on 1.22.x, 13.0.3 on 1.21.x/1.20.x, same 13.0.0.0 assembly identity), and
the VSTest host binds it at process start for its own protocol, before any staging trigger
could run. So:
- Build against the newest engine you target and run everywhere older. The newer Newtonsoft build is a superset for the older engine, and the whole matrix runs green: measured, the full engine E2E suite built once on 1.22.3 runs 105/105 on 1.21.7 AND 1.20.12, plus the samples, without a rebuild.
-
The reverse is refused up front. A floor-built output on a newer engine dies because
the newer engine binds Newtonsoft members its own build added (
JToken.WriteTo(JsonWriter)on 1.22.3 killed every boot in measurement), so the boot preflight fails fast with both file versions and the remedies named, and still re-stages the copy on disk so a plain re-run (or a rebuild) recovers. This is the honest limitation: auto-staging carries you down the version range from one build, not up it.
The per-push prebuilt-cross-install CI lane proves the supported path from one build: the
samples built once against 1.22.3 run unmodified (--no-build) on 1.21.7, again on 1.21.7
(idempotence), and back on 1.22.3, with byte-identity asserts on the staged copy. This is
what closes the old "no CI lane yet" caveat, and it removes the per-install rebuild that a
single-VINTAGE_STORY differential CI used to pay (StratumParity's vanilla-plus-fork CI had
doubled its time on it).
The one-shot pattern: atlas stage before a --no-build run. Auto-staging's
fail-then-rerun still needs a rerun: on the first run in a process after repointing
VINTAGE_STORY, the module initializer re-stages the copy on disk correctly, but if engine
types were already JITted before any Atlas code ran, THAT run still fails; only the rerun
goes green. A script that runs each install exactly once (a one-shot cross-install script,
StratumParity's run-parity.sh case) cannot absorb that cycle. Since 0.11.0, atlas stage <dir-or-dll> (see CLI) runs the identical staging decision explicitly, as its own
process, ahead of time:
atlas stage out/ && VINTAGE_STORY=... dotnet test out/ --no-buildBy the time dotnet test starts, the copy is already staged, so its module initializer
finds a no-op and nothing ever fails: this removes the fail-then-rerun on the first switch to
a new install, with no rebuild either.
One boundary still holds: auto-staging swaps the engine assembly, it does not invent engine
surface. A scenario body that calls an API member only the newer engine has still fails on
the older one with a MissingMethodException, and enum values or field/property shapes that
shifted across versions must go through EngineCompat (see Writing Scenarios for the
cross-version authoring rules).
.github/workflows/compat.yml runs on a schedule (cron: "0 6 * * 1", every Monday) across a
matrix of Vintage Story versions. For each version:
- Download and cache that version's Linux server archive from the Vintage Story CDN.
-
dotnet build Atlas.slnx -c Releaseagainst that version's dlls (continue-on-error: true, so a failure here does not abort the workflow, only that version's leg). - If the build succeeded, run
Atlas.Engine.TestsandSample.Scenarios, both filtered toCategory=E2E(alsocontinue-on-error: true). - Compute a verdict per version:
compatible(build and both E2E suites passed),incompatible (API)(build failed), orpartial(build succeeded but at least one E2E suite failed), with a short note naming which suite failed forpartial. - An
aggregatejob collects every version's verdict artifact and renders a consolidated table into the workflow's step summary.
Trigger a sweep on demand from the Actions tab (workflow_dispatch), or from the CLI:
gh workflow run compat.yml --repo Pixnop/Atlasworkflow_dispatch accepts an optional versions input (space- or comma-separated) to sweep
a custom set of versions instead of the default matrix
(1.18.15, 1.19.8, 1.20.12, 1.21.7, 1.22.0, 1.22.1, 1.22.2, 1.22.3):
gh workflow run compat.yml --repo Pixnop/Atlas -f versions="1.22.0,1.22.3"