Table of Contents

Class ClientRounds

Namespace
Valheim.Testing.Game
Assembly
Valheim.Testing.Game.dll

The client half of a persistence scenario, in rounds: the same entry into the world and measurement before and after a restart, with the evidence written in the same shape each time. One loop serves both kinds of client:

Run(GameActor, Func<ClientSession>, Action<ClientRound>, Action<ClientRound>?) opens the client, then for each of Rounds:
  1. waits until the server accepts game connections (WaitUntilJoinable(GameActor));
  2. joins with the plan's disposable character (JoinWorld(ClientRunPlan, string, CrossplayLobby?, bool, CancellationToken): devcommands first, the join exactly once, the client's world pins verified, the world awaited, which protects the player, and test access on an owned client's character), as one step; a Crossplay client first reads the server's lobby (Lobby) and joins it;
  3. with an Arrival, has the player arrive there with Arrive(GameActor?, GameActor, HeightExpectation, TimeSpan, CancellationToken, bool, bool) ({round}-arrival.json and {round}-teleport-trace.json);
  4. runs the mod's measurement, which adds its own steps and evidence through ClientRound;
  5. between rounds: a confirmed world save, the client leaves to its menu, only the owned server restarts (Restart()), and the optional after-restart check runs; after the last round the client leaves.

A hosting client's rounds first run the plan's static preflight (Preflight(IEnumerable<string>): the fixture's pinned files and own world UID, and an owned client's install, whose ValheimCLI set must also provide HostedRounds when the plan names a CliManifest), check the native client's save directory, and place the fixture world through the run's journalled site (HostedWorld), so a wrong fixture, install or ValheimCLI set stops the run before the fixture is copied or the game started. After opening the client they require the session commands the rounds use (HostedRounds); each round then hosts the world (Start(GameActor, ClientRunPlan, string, TimeSpan, CancellationToken, bool); from the second round on, this is the hosted world's restart), measures, saves with confirmation between rounds (Save(string, TimeSpan)) and leaves to the menu, which saves again.

Every step is recorded in Report, those of a round prefixed with its name (first: join ...). The first failure stops the rounds and is rethrown: nothing after a failed save, restart or measurement runs. The teardown is the same for both kinds: the client is closed in every outcome (an owned client's process is stopped, an attached client is detached and left running), then a hosted world is moved into the evidence (hostWorldEvidence) once no client can still host it; after a failure with an attached client, or an owned client that did not stop, the world is left in place and named in hostWorldLeftInPlace. A failed teardown fails a passing run and never hides an earlier failure.

public sealed class ClientRounds
Inheritance
object
ClientRounds
Extension Methods

Properties

Arrival

Where the player stands to measure; null leaves the player where it joined. A joining client only.

public HeightExpectation? Arrival { get; init; }

Property Value

HeightExpectation

ArriveStep

The arrival step's name within a round, saying where the player goes. A joining client only.

public string ArriveStep { get; init; }

Property Value

string

Cancellation

public CancellationToken Cancellation { get; init; }

Property Value

CancellationToken

Client

The client plan: join address or HostWorld section, character, password variable, pins and timeouts.

public required ClientRunPlan Client { get; init; }

Property Value

ClientRunPlan

Lobby

For a crossplay client (Crossplay), required: the server's lobby, read before each round's join because a restarted server opens a new one, for example server => CrossplayServer.WaitForLobby(server, CrossplayServer.BepInExLog(runtime), timeout).

public Func<GameActor, CrossplayLobby>? Lobby { get; init; }

Property Value

Func<GameActor, CrossplayLobby>

OpenStep

The client's opening step's name; the default says whether it is launched or attached, with plugins pinned.

public string? OpenStep { get; init; }

Property Value

string

Output

The run's output directory, where each round's evidence is written.

public required string Output { get; init; }

Property Value

string

OwnedServer

For a joining client, required: the owned server the rounds wait on before each join and restart between rounds, for example the runner's ServerActor. A hosting client is its own server: leave this out.

public IOwnedServer? OwnedServer { get; init; }

Property Value

IOwnedServer

ProtectPlayer

Whether to enable god, ghost and debug protection once in the world. The default protects a character before terrain or movement checks. Set false only for a short load/join smoke or a scenario that needs ordinary gameplay and intentionally avoids those modes; server admin privileges may be unavailable to a clean client.

public bool ProtectPlayer { get; init; }

Property Value

bool

Report

public required ScenarioReport Report { get; init; }

Property Value

ScenarioReport

Rounds

The rounds' names, which prefix their steps and evidence files: letters, digits, - and _, all different.

public IReadOnlyList<string> Rounds { get; init; }

Property Value

IReadOnlyList<string>

WorldUid

The owned server's world UID, which the joined client must report; required for a joining client. A hosting client's world UID is its plan's WorldUid: leave this out or give the same.

public string? WorldUid { get; init; }

Property Value

string

Methods

Run(GameActor, Func<ClientSession>, Action<ClientRound>, Action<ClientRound>?)

Runs a joining client's rounds against server, the owned server's current actor, opening the client with openClient (for example Open(ClientRunPlan, string, CancellationToken)). measure runs in each round once the player is in place; afterRestart, if given, runs after each restart before the next round's join, for server-side checks of what survived. Returns the server's actor after the last restart.

public GameActor Run(GameActor server, Func<ClientSession> openClient, Action<ClientRound> measure, Action<ClientRound>? afterRestart = null)

Parameters

server GameActor
openClient Func<ClientSession>
measure Action<ClientRound>
afterRestart Action<ClientRound>

Returns

GameActor