Class PinnedServerRun
- Namespace
- Valheim.Testing.GameSessions
- Assembly
- Valheim.Testing.GameSessions.dll
The lifecycle of a mod's owned dedicated-server test runner, so the mod supplies only its plan fields, modes and
scenarios. Usage: <runner> [--inventory <environments.json>] validate|run <plan.json> <new-output-directory>.
- Refuses an existing output directory (evidence is never overwritten) and one inside a pinned source.
- Reads the plan and places its server: on this machine, from the install the plan's runtime names, or with
--inventoryon a host (below); checks the host before writing anything there. - Records provenance: plan, runner and toolkit hashes, mode, platform,
crossplay, the profile, the world copy and their input hashes. - Takes the host's lock, checks every runtime or plugin pin the plan supplies, stages the server's disposable loader
profile from the install (which is never changed), and binds omitted pins from that profile. It copies the world, checking
a supplied manifest or recording its hashes as found, reads a world UID from the fixture only when the plan supplied neither
worlduidnorworld, places the copy beside the profile, and checks the profile's executable and its game build, loader, patchers, required plugins and staged plugin set before launch. validatestops there. Otherwise: checks the CLI port is free, starts the GameSession's owned server (ServerActor) from the profile with per-boot logs (boot-N.*) and recorded commands (connection-N.jsonl), waits on the dedicated startup events, establishes test access and runs the scenario with the session and the plan.- Always disposes the session (clients the scenario left open, then only the owned server), records the server's PIDs,
scans the session's logs (Logs: every boot's and client's; ScanLogs(IEnumerable<RunLog>, IReadOnlyDictionary<string, LogClassification>?),
with the plan's LogScan), writes
result.jsonandjunit.xml, and prints PASS (only forrun), VALIDATED or FAIL. Ctrl+C and SIGTERM cancel the run.
A standalone plan with omitted pins enforces the values it observed from the staged profile and fixture and records their
PinOrigins as observed.
Without --inventory the server's host is this machine (a local host whose one environment is the plan's install);
with --inventory <environments.json> it is a host of the private environment inventory instead:
the first server environment in inventory order whose host (Linux with bash over SSH, in a container or this machine, or
Windows with PowerShell) and ports fit the plan. Either way its choice and reason are recorded as serverEnvironment. The runner takes that host's lock for the run, checks
the host's install against the plan's runtime manifest and RuntimePins, stages a disposable loader
profile of it in a new run directory there (the install's own mods on the pinned loader and ValheimCLI set, the install never
changed) and binds the plan to that profile, ships the verified world copy and verifies it there, makes the executable and pin
checks on the profile,
checks the CLI port on the host, reaches ValheimCLI only through a loopback tunnel (OpenCliTunnelAsync(int, TimeSpan, int, CancellationToken)),
starts each boot with HostServer and waits for its listening line in the host's log, and at teardown stops only
the process it started, fetches each boot's logs (boot-N/) and the world copy (host-world/), removes the profile,
closes the tunnel and releases the lock. Clients open on this machine; remote clients and several actors are a campaign's
(RunCampaignAsync<TPlan>(string, TPlan, Func<TPlan, IReadOnlyDictionary<string, ClientRunPlan>>, string, PinnedServerRunOptions<TPlan>)), where OpenClient(ClientRunPlan, string?, string?) starts each in its host's
desktop session on its leased, observed Steam identity.
A host operation whose outcome is unknown (a lost reply, a transport failure, an unproven lock or lease release), when nothing else
failed for certain, prints UNKNOWN and returns 3: neither a pass nor a failure.
Returns the process exit code: 0 when every step passed, 1 on failure, 2 on bad usage, 3 when the outcome is unknown.
public static class PinnedServerRun
- Inheritance
-
objectPinnedServerRun
Examples
A mod supplies its validated plan, adapter session capability and scenario. The toolkit owns fixture copies, startup, teardown and the report:
return await PinnedServerRun.MainAsync(args, new PinnedServerRunOptions<LifecyclePlan>
{
Name = "mymod-system-test",
ReadPlan = LifecyclePlan.ReadValidated,
Mod = new ModDeclaration("mymod.testing/session", LifecyclePlan.SessionTokenVariable),
Scenario = (session, plan) =>
{
DrySiteServerScenario.Run(plan, session.Server!.Game, session.Server.Restart, session.Report);
return Task.CompletedTask;
},
});
validate checks the pinned inputs without launching the game; run executes the scenario on a disposable
copy. For client rounds, restarts and failure-safe cleanup, see the compiling
full lifecycle example.
Fields
HostMode
The mode that runs a hosted plan's scenario; ValidateHostMode only checks it.
public const string HostMode = "host"
Field Value
- string
InPlaceOption
The option, before the mode, that runs every owned client of the run from its install as it is instead of a disposable copy: each client plan the run reads takes InPlace (the plan field of the same name). Nothing is written into the install; it must already hold BepInEx and ValheimCLI.
public const string InPlaceOption = "--in-place"
Field Value
- string
InventoryOption
The option that names the private environment inventory the dedicated server is placed from; it comes before the mode.
public const string InventoryOption = "--inventory"
Field Value
- string
KeepRuntimeVariable
Set to 1 to keep every actor's whole profile after the run, for hands-on debugging in it, with any runner.
By default a run that stopped its server keeps only what the run added or changed in the profile (runtime-changes/)
and removes the rest, which is the install's loader and mods.
public const string KeepRuntimeVariable = "VALHEIM_TESTING_KEEP_RUNTIME"
Field Value
- string
ValidateHostMode
The mode that runs a hosted plan's scenario; ValidateHostMode only checks it.
public const string ValidateHostMode = "validate-host"
Field Value
- string
Methods
MainAsync<TPlan>(string[], HostedRunOptions<TPlan>)
The lifecycle of a hosted run, where one client hosts a pinned fixture world from its menu and there is no dedicated server
to pin (#258 step 8). Usage: <runner> validate-host|host <plan.json> <new-output-directory>.
validate-host checks the plan and runs the run's own preflight (Preflight(IEnumerable<string>)
with HostedRounds: the fixture world's hashes and own world UID, and an owned client's
install), and copies or launches nothing. host runs the scenario on a GameSession whose
Host (HostingClientActor, on this machine) was prepared, opened and hosts its
world; then disposes the session (the host leaves, is stopped or detached, and its world moves into the evidence once no
client can host it), scans the host's logs with the plan's LogScan, writes
result.json and junit.xml and prints PASS, VALIDATED or FAIL. Ctrl+C and SIGTERM cancel the run.
Returns 0 when every step passed, 1 on failure, 2 on bad usage.
public static Task<int> MainAsync<TPlan>(string[] args, HostedRunOptions<TPlan> options) where TPlan : class
Parameters
argsstring[]optionsHostedRunOptions<TPlan>
Returns
- Task<int>
Type Parameters
TPlan
MainAsync<TPlan>(string[], PinnedServerRunOptions<TPlan>)
Runs the pinned dedicated-server plan named by args and returns a process exit code after owned cleanup.
public static Task<int> MainAsync<TPlan>(string[] args, PinnedServerRunOptions<TPlan> options) where TPlan : ServerRunPlan
Parameters
argsstring[]optionsPinnedServerRunOptions<TPlan>
Returns
- Task<int>
Type Parameters
TPlan
RunCampaignAsync<TPlan>(string, TPlan, Func<TPlan, IReadOnlyDictionary<string, ClientRunPlan>>, string, HostedRunOptions<TPlan>)
Runs a hosted plan on a campaign without a dedicated server (#258 step 8b): one of the named clients hosts the campaign's
fixture world (its plan section's hostWorld) and the others are its peers (joinsHost), each on the host the
inventory assigns it. As RunCampaignAsync<TPlan>(string, TPlan, Func<TPlan, IReadOnlyDictionary<string, ClientRunPlan>>, string, PinnedServerRunOptions<TPlan>):
the campaign's preflight and the plan's agreement with it (CheckHostedPlan(string, IReadOnlyDictionary<string, ClientRunPlan>))
are Preflight steps; preparing the disposable installs and characters and binding them to the plan are Setup steps. The
scenario runs on a GameSession whose Host is the hosting client and whose
Clients are its peers, each opened on its host on its leased Steam identity. Teardown: the session
(peers, then the host, then its world), the leases, the prepared installs and characters (one retire owner, under the locks
held), then the client hosts' locks. clients names the plan's client sections by the campaign's client
names. Exit codes as MainAsync<TPlan>(string[], PinnedServerRunOptions<TPlan>).
public static Task<int> RunCampaignAsync<TPlan>(string manifestFile, TPlan plan, Func<TPlan, IReadOnlyDictionary<string, ClientRunPlan>> clients, string output, HostedRunOptions<TPlan> options) where TPlan : class
Parameters
manifestFilestringplanTPlanclientsFunc<TPlan, IReadOnlyDictionary<string, ClientRunPlan>>outputstringoptionsHostedRunOptions<TPlan>
Returns
- Task<int>
Type Parameters
TPlan
RunCampaignAsync<TPlan>(string, TPlan, Func<TPlan, IReadOnlyDictionary<string, ClientRunPlan>>, string, PinnedServerRunOptions<TPlan>)
Runs plan on a campaign's actors (HostedCampaignManifest): the dedicated server and each
named client on the environment the inventory assigns it. The campaign's static preflight and the plan's agreement with
it (every plugin the plan pins is one a role selects; no angle-bracket placeholder left in the server's arguments) are
Preflight steps; preparing the disposable installs and characters (PrepareAsync(string, string, TimeSpan, Func<string, IGameHost>?, CancellationToken),
with its read-only host checks first) and binding them to the plan (ApplyTo(ServerRunPlan, HostedCampaignManifest, IReadOnlyDictionary<string, ClientRunPlan>, string)) are
Setup steps; retiring them after the processes stopped is a Cleanup step. clients names the plan's
client sections by the campaign's client names. The evidence, result.json and junit.xml are written to
output (new), the prepared inputs to its prepared/. Exit codes as MainAsync<TPlan>(string[], PinnedServerRunOptions<TPlan>).
public static Task<int> RunCampaignAsync<TPlan>(string manifestFile, TPlan plan, Func<TPlan, IReadOnlyDictionary<string, ClientRunPlan>> clients, string output, PinnedServerRunOptions<TPlan> options) where TPlan : ServerRunPlan
Parameters
manifestFilestringplanTPlanclientsFunc<TPlan, IReadOnlyDictionary<string, ClientRunPlan>>outputstringoptionsPinnedServerRunOptions<TPlan>
Returns
- Task<int>
Type Parameters
TPlan