Table of Contents

Class ScriptedGameHost

Namespace
Valheim.Testing.GameSessions
Assembly
Valheim.Testing.GameSessions.dll

The shared implementation: every operation is a fixed script run through the host's shell, so local, SSH and container hosts behave alike and differ only in how the shell is started and how the ValheimCLI port is reached.

A script travels on standard input, never on a command line: a small fixed wrapper on the host reads it, writes it to a temporary file, runs it and reports its exit code on its own line of stderr. That report, not the transport's exit status, is the script's exit code, so a Windows host whose login shell is PowerShell (which reports only 0 or 1) still returns the real one. Without the report the outcome is a transport failure or unknown.

public abstract class ScriptedGameHost : IGameHost
Inheritance
object
ScriptedGameHost
Implements
Derived

Properties

Kind

public abstract GameHostKind Kind { get; }

Property Value

GameHostKind

Name

public string Name { get; }

Property Value

string

Shell

public HostShell Shell { get; }

Property Value

HostShell

Methods

AcquireLockAsync(string, string, TimeSpan, CancellationToken)

Takes an advisory lock at lockPath, a directory on the host, for owner (a run id). The claim is the exclusive creation of an owner file inside it (O_EXCL, or CreateNew on Windows), which exactly one claimer can win; the directory itself may already exist. Each acquisition claims as owner plus a unique id (Owner), so two runs that pass the same owner never both hold the lock, and only this handle can release it. Another claimant's lock, or an unproven claim, throws HostLockException; nothing is waited for or retried. An unproven claim's message names the claimant it tried: check the lock with it before deciding what to do. A lock left by a crashed run stays until a person removes it.

public Task<HostLock> AcquireLockAsync(string lockPath, string owner, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

lockPath string
owner string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<HostLock>

CheckLockAsync(string, string, TimeSpan, CancellationToken)

Reads the lock: Free, Yours or HeldByOther. Changes nothing.

public Task<HostLockResult> CheckLockAsync(string lockPath, string owner, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

lockPath string
owner string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<HostLockResult>

FetchDirectoryAsync(string, string, TimeSpan, CancellationToken)

Copies hostDirectory into localDirectory, which must not exist yet: the host packs it with tar (no macOS AppleDouble files), the archive's SHA-256 and size are checked on both ends, and it is extracted here (entries that would land outside it are refused). A local directory this call created is removed again if anything fails.

public Task<FetchedDirectory> FetchDirectoryAsync(string hostDirectory, string localDirectory, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

hostDirectory string
localDirectory string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<FetchedDirectory>

LogOffsetAsync(string, TimeSpan, CancellationToken)

The log's current length in bytes (0 if it does not exist), for a later WaitForLogAsync(string, long, Regex, IReadOnlyList<Regex>?, TimeSpan, CancellationToken).

public Task<long> LogOffsetAsync(string logPath, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

logPath string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<long>

OpenCliTunnelAsync(int, TimeSpan, int, CancellationToken)

A local loopback endpoint for the ValheimCLI listening on the host's 127.0.0.1:hostPort.

public abstract Task<CliTunnel> OpenCliTunnelAsync(int hostPort, TimeSpan readyTimeout, int localPort = 0, CancellationToken cancellation = default)

Parameters

hostPort int
readyTimeout TimeSpan
localPort int
cancellation CancellationToken

Returns

Task<CliTunnel>

ReleaseLockAsync(string, string, TimeSpan, CancellationToken)

Removes the lock only if it names exactly owner; another owner's lock is left alone.

public Task<HostLockResult> ReleaseLockAsync(string lockPath, string owner, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

lockPath string
owner string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<HostLockResult>

RunAsync(string, IReadOnlyDictionary<string, string>?, TimeSpan, CancellationToken)

Runs script on the host. Each variable arrives as a literal assignment before the script, never parsed by any shell: bash name='value', PowerShell $name = 'value'. A PowerShell script runs with $ErrorActionPreference = 'Stop', from its own temporary .ps1 file, and ends with its exit N, 0 when it falls off its end, or 1 for an uncaught error. A bash script's CRLF line endings become LF; its exit status is bash's. Its standard input is empty. The working directory is the ssh user's home, the container's working directory, or this process's. A Windows host without PowerShell 7 ends as TransportFailed, its error naming the install command.

public Task<HostResult> RunAsync(string script, IReadOnlyDictionary<string, string>? variables, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

script string
variables IReadOnlyDictionary<string, string>
timeout TimeSpan
cancellation CancellationToken

Returns

Task<HostResult>

ShipFilesAsync(string, string, TimeSpan, CancellationToken)

Ships the files under localDirectory (as a tar archive, checked by SHA-256 on the host) into hostDirectory, which must not exist yet. SOURCE.txt there records the hash.

public Task<Shipment> ShipFilesAsync(string localDirectory, string hostDirectory, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

localDirectory string
hostDirectory string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<Shipment>

ShipRevisionAsync(string, string, string, TimeSpan, CancellationToken)

Ships one committed revision: git archive of revision (tracked files only, never the working tree) is sent to the host, its SHA-256 checked there, and extracted into hostDirectory, which must not exist yet. SOURCE.txt there records the commit, tree and hash. A directory this call created is removed again if extraction fails.

public Task<Shipment> ShipRevisionAsync(string repository, string revision, string hostDirectory, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

repository string
revision string
hostDirectory string
timeout TimeSpan
cancellation CancellationToken

Returns

Task<Shipment>

WaitForLogAsync(string, long, Regex, IReadOnlyList<Regex>?, TimeSpan, CancellationToken)

Follows logPath on the host from byte fromOffset, event driven there (tail -F, or a file watcher under PowerShell), and matches each complete line here with .NET regular expressions, so a pattern means the same on every host. A line matching one of failures (checked first) ends the wait as FailureMatched. The log may not exist yet. Take the offset with LogOffsetAsync(string, TimeSpan, CancellationToken) before the action whose line you await; a log the game recreates for each boot is best given a fresh path (a disposable runtime) and offset 0, because a replacement longer than the offset cannot be told apart. The host stops following on its own a little after the deadline. A transport failure or a follower that ends early throws HostOperationException.

public Task<HostLogResult> WaitForLogAsync(string logPath, long fromOffset, Regex success, IReadOnlyList<Regex>? failures, TimeSpan timeout, CancellationToken cancellation = default)

Parameters

logPath string
fromOffset long
success Regex
failures IReadOnlyList<Regex>
timeout TimeSpan
cancellation CancellationToken

Returns

Task<HostLogResult>