Namespace Valheim.Testing.Game
Classes
- AppliedPatch
One applied Harmony patch, as the adapter's census reports it. Method and Patch are
Type::Name(ParameterType,...); Kind is prefix, postfix, transpiler, finalizer or ilmanipulator.
- AreaObjectSnapshot
Optional saved-object and loaded-structure evidence for a small area. The server observes saved ZDOs and containers; the client observes instantiated prefabs and current piece support. Counts are completeness checks, not assertions about what a mod ought to place. Use terrain snapshots separately for height and paint.
- BepInExLoaderPackage
A reviewed, extracted BepInEx/UnityDoorstop package: identity and exact loader/core files, independent of a live Steam install. A prepared game may be copied into a disposable install, then this set is applied there before any plugin is staged. Static checks of the package do not claim it will start; the owned client still must reach its menu and write a fresh BepInEx log.
- Capability
A command discovered from the current extension instance. Rediscover it after a plugin reload.
- CensusCollision
A hash two different names share in a registry (
itemsorprefabs), and the name its index holds.
- CensusEntry
One line of a census report: on which Side, which Kind (
item,prefab,recipe, a recipe'srecipe-item,recipe-stationorrecipe-resource, and the later kinds) and Name (a dependency asrecipe/dependency), the game's hash for an item or prefab, the state and why.
- CensusOwner
The owner plugin as the observing process has it: loaded or not, its version and the MD5 of its file.
- CensusSideResult
How a side's observation was taken: whether it was supplied, which side and build it reported, and why it was refused (Problem; null when it was used).
- CensusTotals
The sizes of the observing process's whole registries and its in-scope build-table entries.
- CliBundle
The pinned ValheimCLI plugin bundle the toolkit ships: one zip of the default plugin, its capability manifest (
cli-manifest.json) and ValheimCLI's license, built once from the pinned commit and identified by its SHA-256. Valheim.Testing.GameSessions embeds it with its pin (cli-dependency.json), so a disposable client copy andvalheim-teststage the same set. Extract(Stream, string, string, string, string?) unpacks it once under ValheimTesting's own folder on this machine (cli/<commit>) and reuses that copy while every file still matches the manifest, so a run needs no network and no plugins in the game.
- CliBundleSource
A pinned default ValheimCLI plugin ready to stage: its capability manifest, containing folder, and origin.
- CliCapabilities
Checks, right after a client answers, that its ValheimCLI offers the capabilities a run will use, and says which build is missing: an older ValheimCLI may answer
cli_expectbut lackcli_extensionsor the required built-in command. Read-only: onecli_extensionscall through the strictly pinned actor.
- CliCapabilityManifest
What one pinned ValheimCLI build provides, tied to its plugin's exact SHA256: ValheimCLI is one plugin (PluginFile, declaring PluginGuid), and the manifest records its SHA256 and the extension commands it registers, with their result schema versions. An owned client's CliManifest names one, and Preflight() then refuses, before the game starts, an install whose ValheimCLI plugin is not that build (missing, another build of it, a second copy, a renamed file declaring its GUID) or lacks a capability the run uses, naming the capability. A file name, plugin GUID or version never stands for a capability: only a DLL with the manifest's hash does. ValheimCLI ships this file with its build; Generate(string, IEnumerable<string>, string?) writes one from a known build by reading the DLL's metadata and IL, never loading it. Read(string) is the one place that refuses anything but a one-plugin build, so every command that reads a manifest refuses it with the same error. Require(GameActor, params string[]) after launch stays the authoritative check: this one only fails earlier.
- CliManifestCheck
What Check(string, IEnumerable<string>) found: the manifest's build, its files where they are installed, and the capabilities checked.
- CliManifestFile
The one plugin of a CliCapabilityManifest.
- CliTransport
A TCP attachment to a running ValheimCLI. Disposing it closes the socket, not the game process.
- ClientRound
One round of ClientRounds, handed to the mod's measurement and after-restart check.
- ClientRounds
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:
- a client that joins an owned dedicated server (Run(GameActor, Func<ClientSession>, Action<ClientRound>, Action<ClientRound>?), with OwnedServer and WorldUid);
- a client that hosts a fixture world itself, as a listen server or locally (
HostedClientRounds.RuninValheim.Testing.GameSessions, whose journalled site places the world, with the plan's HostWorld section): it is both the server and the client of each round.
- waits until the server accepts game connections (WaitUntilJoinable(GameActor));
- 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;
- with an Arrival, has the player arrive there with Arrive(GameActor?, GameActor, HeightExpectation, TimeSpan, CancellationToken, bool, bool) (
{round}-arrival.jsonand{round}-teleport-trace.json); - runs the mod's measurement, which adds its own steps and evidence through ClientRound;
- 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 inhostWorldLeftInPlace. A failed teardown fails a passing run and never hides an earlier failure.
- ClientRunPlan
The game client a system test joins to its server, as a plan section.
owned: the runner launches it from Install and stops only that process (Launch(ClientRunPlan, string, CancellationToken)); it must run in the desktop session where Steam is running and signed in, with a display.attach: an operator launched and signed in the client; the runner only connects to its ValheimCLI port and never touches the process. The join password stays in the client's own process environment, named by PasswordVariable.
- ClientSession
A game client for a system test: one this session launched and owns, or one an operator runs that it only attaches to. Either way Actor is strictly pinned to the plan's menu expectations when the session is returned, and every command it sends is recorded to
client-commands.jsonlin the output directory (client-commands-2.jsonland so on for later sessions: evidence is never overwritten). Disposing closes the connection and, for an owned client, stops only the process this session started (never one found by name): it asks the client to quit and kills it only if it does not (Stopped), then keeps its logs beside the evidence. An attached client's process is never touched. The client is pinned at its menu. A session given a leased Steam account (a multi-machine session's client) starts or attaches only while that lease is held (and signed-in checked when the profile asks); the hold's owner releases it after disposing the session.
- ConfigValue
One plugin config entry as a process holds it now. Value and DefaultValue are written as BepInEx writes them to the config file (
true,5,1.5). Installed is false when the plugin is not loaded in that process, Found when it has no entry with that section and key.
- ConnectionStatusReading
One reading of
cli_connection_status: ValheimCLI repliesOK: connectionStatus=ErrorVersion, server=...(its console twin addsgameState=...beforeserver=). The command is a diagnostic, so ValheimCLI answers it even while its standing expectations fail.
- ContentCensus
Reads each side's registered content and reconciles it with what the mod declares. A registration double can show that a mod calls ObjectDB or ZNetScene; only the running game shows what each process ended up with, and a client's registries are its own: a server observation never stands for a client. Read(GameActor, string, ContentExpectations) takes one process's census through a strictly pinned actor; Reconcile(ContentExpectations, params SideObservation[]) judges all sides together.
- ContentCensusReport
The reconciliation of a mod's ContentExpectations with each side's observation. It passes only when every side with expectations was observed, by that side itself, with the pinned build of the owner, and every entry is Present; a side problem, and every other state, fails it.
- ContentExpectations
What a mod declares it registers, per side, for ContentCensus: the Owner plugin whose build must be the pinned one on each side, the Scope (name prefixes that are the mod's own content; every expected name starts with one, and only entries in scope can be Unexpected, so vanilla content never is) and the entries. Load it from a file (Load(string)) written as
{ "owner": "example.mymod", "scope": ["MyMod_", "Recipe_MyMod_"], "items": [{ "name": "MyMod_SurveyStake", "sides": ["server", "client"] }], "prefabs": [{ "name": "MyMod_SurveyStake", "sides": ["server", "client"] }], "recipes": [{ "name": "Recipe_MyMod_SurveyStake", "sides": ["server", "client"], "item": "MyMod_SurveyStake", "station": "piece_workbench", "resources": ["Wood"] }], "pieces": [{ "name": "MyMod_SurveyPost", "tool": "Hammer", "sides": ["server", "client"], "station": "none", "resources": ["Wood"] }], "statusEffects": [{ "name": "MyMod_SurveyBlessing", "sides": ["server", "client"] }] }Anything else in the file (another key, a side other than
serverorclient, a name twice, a name outside the scope) is refused. Derive it from the mod's design, never from a census: a census copied into its own expectations can only pass.
- ContentObservation
One process's content census (ValheimCLI's
valheim.observe/content-census): which side it reports being, its owner plugin's build, the whole registries' sizes, and the registered content in scope with every shared hash. An older adapter may omit StatusEffects or Pieces; those checks then report unsupported, never missing or present. Read with Read(GameActor, string, ContentExpectations).
- Coverage
Exact sample coverage: counts alone never establish that the intended samples were measured.
- CrossplayLobby
A crossplay server's lobby: RemotePlayerId is what a client joins (
cli_connect_playfab_user).
- CrossplayServer
A dedicated server started with
-crossplay(Crossplay). It uses the game's PlayFab backend: it logs in to PlayFab after it starts, then registers a lobby that clients join by the server's PlayFab entity id, not by address. The lobby is keyed by the server's public IP and game port, so two crossplay servers behind one public IP must use different game ports (-port), or the second takes over the first one's joins.
- CustomDataReading
The local player's custom data and profile, read through ValheimCLI's
valheim.observe/custom-data. Character is the profile's name, ProfileFile its file name without.fchand FileSource where the game saves it (LocalorCloud). Data is the reading as returned.
- DeclaredPatch
A patch the mod declares: its target,
Type::Namefor any overload orType::Name(ParameterType,...)for one; its kind; and optionally the patch method (Type::Name, with or without parameters). Types are written as .NET writes them:Terminal,MyMod.Plugin+RegisterCommands,System.Int32.
- DefaultSmokeCharacter
A clean, game-created Valheim 1.0.16 local character for an owned smoke run. It has completed its first spawn, has visited no worlds and contains no data copied from another character. Every run stages a copy from a DisposableCharacterStore; the packaged save is never played or rewritten.
- DefaultSmokeWorld
A small, game-created Valheim 1.0.16 world for an owned smoke run. The server that created it had only ValheimCLI default plugin and its built-in commands. It is a starting point, not a synthetic terrain replacement: the game still generates terrain and locations when the client enters it.
- DiskSpace
Free space and directory sizes for the copies a native run makes. A run that copies a game install needs room for the whole copy; refusing before the copy starts says which drive and how much, where a full drive part-way through a copy leaves a half-made runtime behind and a confusing I/O error.
- DisposableCharacter
A character taken from a DisposableCharacterStore. Only the store creates these; each use re-reads the store and refuses the handle if the registration was removed or the stored copy changed since it was taken.
- DisposableCharacterStore
The registered disposable test characters: a directory outside every game character folder holding one copy of each character and a manifest of their names and the game's player IDs. Registering is the one explicit step that declares a character disposable; staging a character for an owned client accepts only characters registered here, so pointing a test at a personal character file is refused rather than edited.
- DungeonLocation
The location instance the server keeps nearest the queried ground position (in the 3 x 3 zones around it). The game keys each location instance by the zone its own position stands in, so this is found from the query, not from the generator's zone, and a generator displaced into a neighbouring zone still pairs with its location.
- DungeonRoom
One saved dungeon room: its prefab-name hash (Of(string) of the room prefab's name), position and rotation as Euler angles in degrees, as the dungeon generator saved them. Index is its place in the save.
- DungeonRoomProblem
A reason a dungeon fails OutsideZone(SavedDungeon, IReadOnlyDictionary<string, RoomSize>?, float): a room (when Room is set) or the dungeon itself.
- DungeonRooms
Reads a dungeon's saved rooms through ValheimCLI's
valheim.observe/dungeon-roomson the server, and checks that every room lies inside its location's zone. In Valheim 1.0.16 the game gives a dungeon interior its environment with a 64 m wide box centred on the location's zone (5000 m above the location), so the part of a room outside that zone loses the interior environment; the generator itself keeps rooms inside the zone of its own position.
- EnvironmentInventory
Ordered, private operator inventory. Client identities are discovered from their signed-in Steam environments; a campaign supplies actors and its own dependency locks. Reading it has no host effects; placing actors on it (deterministic, no host effects either) and acting on its hosts are
EnvironmentRuns'.
- EnvironmentRecipe
One preferred launch recipe in a private operator inventory; it is never a running process.
- EvidenceReference
One piece of evidence linked from
result.json: what it is (Kind, for exampleterrain-site,area-objects,review-still,review-clip), the site or capture id, the world it was taken in, the file (relative to the report directory when it lies inside it) and that file's SHA-256.
- ExpectedContent
One piece of content a mod declares by stable identity: its Kind (
item,prefab,recipe,statusEffectorpiece), its Name (the prefab or recipe object's name; items and prefabs are identified by the game's stable hash of it) and the Sides that must have it. A recipe may also declare the Item it crafts, its Station (a crafting station's prefab name, ornonefor crafting by hand) and its Resources (item prefab names). A piece must name the Tool whose build table should contain it, and may also declare its station and resources. An optional dependency left null is not checked.
- ExtensionInstance
One live (not closing) extension registration as
cli_extensionslists it.
- ExtensionReload
The steps of a hot-reload smoke test of a game-side extension (see tools/reload-check): replace the extension's DLL in the reloader's watched directory without it ever seeing a half-written file, then wait until the replacement has registered as a new instance, or until a removed extension is gone. Waiting re-reads
cli_extensions(read-only) at an interval, because a registration announces itself nowhere else. Use a GameActor whose pins allow the reload (WaitForEnvironment(string, TimeSpan, CancellationToken) first) and rediscover capabilities afterwards: the new instance's are new.
- FileHash
The toolkit's one owner of hashing: every SHA256 and MD5 this package computes, as lower-case hex. Pins, manifests, listings and evidence checksums are SHA256; plugin pins are MD5 (Md5(string)). Two hashes are computed elsewhere on purpose: a host computes its own listing remotely (
sha256sum, or .NET in PowerShell), which Valheim.Testing.Game.FileHash.Listing(System.Collections.Generic.IEnumerable<(string Relative, string Sha256)>) then reads, and a character save's SHA512 is the game's own integrity check, read as the game writes it.
- FixtureLayout
Finds the one world a hosted fixture root holds, and when the root has another shape says which one and what to point at instead: the world folder itself, a
worlds_localwrapper, several worlds or none. Every refusal prints the expected tree and what the root holds. Preflight() then checks the UID.
- GameActor
An actor for ValheimCLI actions and observations. After strict VerifyEnvironment(string), it checks the expected world and plugins before every command.
- GameLaunch
One BepInEx game process's launch, as data: the executable, its working directory and arguments, the variables set for it, the entries put in front of a search list's existing value, and the inherited variables removed. It reproduces what BepInExPack_Valheim's start scripts export, without the script, so the started process ID is the game's own. ForServer(string, IEnumerable<string>, IReadOnlyDictionary<string, string>?, ServerPlatform?) builds one for a dedicated server and ForClient(string, IEnumerable<string>, IReadOnlyDictionary<string, string>?, ClientPlatform?, ClientArchitecture?, bool, IEnumerable<string>?) one for a game client. A launch is built for this machine (render it with ToStartInfo()) or, given a host platform, for a host of a multi-machine session, where the hosting layer's server or interactive-client start runs it. It also says what an install on this machine is: DetectServer(string) and DetectClient(string) read the platform from the files, RequireServerExecutable(string) and RequireClientExecutable(string) the executable, and ClientLaunchArchitectures(string) the slices a client can start as.
- GameReply
What the game and ValheimCLI answered one command: the transport's result, its lines, and whether the game refused it. Execute(string, bool) returns one and, by default, requires it Accepted: the transport completed and no line is a known refusal. A transport result alone is not acceptance: a refused console command still completes. Acceptance supplements a step's semantic check (read the line or state it changed); it never replaces one.
- GlobalKeyDifference
How a client's global keys differ from the server's: keys it lacks and keys only it has.
- GlobalKeyFixture
Puts a world into a known progression state: sets and removes global keys (boss progress, events, world modifiers) on the server and waits until a joined client reports the same set, through ValheimCLI's read-only
valheim.observe/globalkeyson both sides and the Adapter package's guardedGlobalKeyCommands.Changefixture command on the server. In Valheim 1.0.16 the server sends its whole list to every client after each change and the client replaces its own list with it, so the client's set must equal the server's exactly. Keys are lower case,nameorname value. Each change is issued once; only the read-only lists are re-read. ValheimCLI'scli_check_global_keyanswers for one key at a time, so it cannot show a key the client has and the server does not; the listing can.
- HarmonyCensus
The Harmony patches applied in the game, read from ValheimCLI's optional
valheim.observe/harmonycommand. On the Valheim 1.0.16 Windows dedicated server (BepInEx 5.4.23.5, HarmonyX 2.9.0) a patch whose target method is missing leaves anaccesstools-not-foundwarning in BepInEx's log, andPatchAllthrows, which only Unity's log records; the teardown log scan fails on both, but only after the run. A half-patched mod stays loaded and passes every test that does not ask; Check(string, IEnumerable<DeclaredPatch>) asks, by name, while the run goes on. The census is read-only and may be re-read.
- HarmonyPatchCheck
The result of Check(string, IEnumerable<DeclaredPatch>).
- HostProfile
One machine or container. Kind is
local,sshorcontainer.
- HostWorldPlan
The
hostWorldsection of a ClientRunPlan: the client hosts this fixture world from its menu (a listen server) instead of joining a dedicated server. The world is the client's own local world, so the runner copies the pinned fixture into the client's local worlds for the run (HostedWorld) and never uses a world already there. Unknown fields are refused with the rest of the plan.
- HostWorlds
Starts and restarts a hosted world on a game client at its menu, with
cli_start_host_world. The host is the server of its world (Server true, not dedicated) and has a local player.
- HostedWorld
A fixture world placed in a hosting client's local worlds for one hosted run. Its one placement owner is the hosting layer's journalled site (
HostedWorldOnHostinValheim.Testing.GameSessions), on a campaign client's host or on this machine: it refuses when anything named for the world (Valheim.Testing.Game.HostedWorld.IsNamedFor(string, string)) is already inworlds_local(a user's world is never overwritten or used, and a refused run copies nothing), copies and verifies the fixture into the output directory (that copy stays as the input evidence), journals the placement and places the fixture inworlds_local. Collect() (also Dispose()) moves the world, with whatever the game wrote for it (saves,.oldfiles, backups), out of the client's worlds into the run's folder on its host, and fetches it intohost-worldin the output directory. Collect only once the client no longer hosts it: after an owned client stopped, or after the host left to its menu.
- InstallPins
What a game install or server runtime holds that ValheimCLI cannot report in game, pinned on disk before launch. Each value is a full SHA256; Of(string) computes them.
- Game: the game's own code, every
assembly_*.dllin the Managed folder that holdsassembly_valheim.dll(<executable>_Data/Managed, in a macOS bundleValheim.app/Contents/Resources/Data/Managed):assembly_valheim,assembly_utils,assembly_guiutils,assembly_postprocessingand the rest. A game update or another branch (default_old,default_pre1_0) that changes any of them changes this pin; one that changes only assets or Unity's own assemblies does not. - Loader: BepInEx's loader as one set (Valheim.Testing.Game.InstallPins.IsLoaderFile(string, System.StringComparison)): the Windows Doorstop proxy
winhttp.dlland itsdoorstop_config.ini, a rootlibdoorstop.dylib,doorstop_libsandBepInEx/core. Another BepInEx, BepInExPack or Doorstop build changes it, and so does a proxy a mod manager replaced beside the pack's configuration. Loader is the same value for a reviewed loader package, so a plan, a package and every check of an install share one loader identity.BepInEx/config/BepInEx.cfgis configuration, not loader: BepInEx rewrites it when it binds its settings. - Patchers: the contents of
BepInEx/patchers, which rewrite game assemblies before any plugin loads: a clean runtime has none, so a patcher a removed mod left behind changes this pin, and a refusal names the folder's entries.
Each value is the SHA256 of a listing: one line per file,
<sha256> <relative path>\nwith/separators, ordered by path (ordinal), assha256sumprints it; Loader's paths are relative to the install's root, the others' to their folder. Finder's.DS_Storeand AppleDouble._*files are left out. An empty or absent folder hashes the empty listing.- Game: the game's own code, every
- JoinRefusal
A refused join, as JoinExpectingRefusal(string, string, GameConnectionStatus, string, TimeSpan, string?, bool, CancellationToken) confirmed it.
- LoadedAssemblyProvenance
The exact managed assembly observed in this process.
Sha256hashes its file at report time;Mvididentifies the module actually loaded, even if that file was replaced later. The version and commit are build metadata, not proof that the assembly came from a NuGet package.
- LogClassification
A run's own classification of a known pattern, with the written reason it differs from the default: another severity, named lines the run expects, or both. Under a name that is not built in, it is a pattern of the run's own: its Line (and optional Frame) regex and its Severity, for a mod's own known-bad line or another mod's known noise, counted like the built-in patterns at teardown.
- LogFileScan
One log's scan. An absent log has no counts (absent is not zero); Problem says why a required one is missing. Failed when a required log is missing or a failure pattern matched.
- LogLine
A matched log line and how long its wait took.
- LogPattern
A known log problem. Line matches one line; with Frame, the line counts only when a line of its stack trace (up to a blank line, the next log message or the next exception) matches the frame. A Startup pattern also ends an owned startup at once (RuntimeLoadFailures); startup matching reads its Line only, so a startup pattern has no Frame.
- LogPatternCount
How often a pattern matched in one log and where first; Reason is set when the run reclassified it. Lines the run named as expected are counted apart in Expected (the first in FirstExpected) and never fail. FirstFrame is the most useful stack frame under the first counted line, for UnityException: the first frame outside the runtime's own System, Mono and wrapper frames.
- LogScanner
Scans a whole run's logs after its processes stopped, for problems that only warn or appear long after startup: a Harmony patch whose target is gone, a global unpatch, missing members after a game update, errors while objects unload, RPCs without a handler, objects whose prefab is not registered, missing scripts and shaders a GPU cannot run; a dedicated server's own graphics errors are known too. Each known pattern is counted per log with its first occurrence and has a default severity a run may change with a written reason. BepInEx warning and error lines that match no pattern are counted as UnknownWarning and UnknownError, never ignored. Unknown errors fail by default; a run can name an exact expected header line with a written reason. Unity's Player.log has no levels; there an exception Unity printed (a line that starts with the exception's type name, outside a BepInEx warning or error record) that no known pattern names is counted as UnityException, which fails. The same Unity message may appear in both logs. Owned processes are asked to quit at teardown and killed only if they do not (StopCleanly(TimeSpan, TimeSpan)): after a clean stop the logs include what the game and its mods logged while shutting down, for example an UnpatchAll a mod calls when the game quits; after a kill they do not.
- LogoutCycle
A logout and the next login, observed: the character file is rewritten when the player leaves to the menu, and the mod's custom data comes back when the character joins again. Mods that keep state in the player profile (
Player.m_customData) lose it when the file is not written, for example when another mod removes every Harmony patch at quit or a server-character mod has no time to finish.In 1.0.16 the save happens inside the logout itself, before the menu loads:
Game.Logoutshuts the game session down, which saves the player into its profile and writes the profile (PlayerProfile.Save): a new file<name>.fch.new, the previous file moved to<name>.fch.oldand the new one renamed to<name>.fch. ValheimCLI's session leave issues that logout and replies once the menu is up, so the new file is normally there when the leave returns; the runner still waits up to WriteTimeout for its hash to change, woken by file events, because it may see the client's disk late. The game skips the save entirely when its save system blocks character saves (logged "Character save blocked") and when the disk is full.Run(ClientRound, ClientRunPlan, string, CancellationToken, string) reads the Keys (each must be set), hashes the profile file, has the client leave to its menu, waits for a different hash, joins again with the plan's character by address (JoinWorld(ClientRunPlan, string, CrossplayLobby?, bool, CancellationToken), the join ClientRounds uses: protected, and test access on an owned client) and requires every key back with its value; a crossplay or hosting plan is refused before anything is sent. Only a disposable local character: the game must report it saved
Local, and CharactersDirectory must be the game's local character folder,characters_local, never a cloud copy. The client is joined again when it returns.An unchanged hash is all the runner can see of a save that rewrote identical bytes; a moved player, a longer play time or a changed value makes the file differ. Another save in the window (a world save asks every client to save its profile) is indistinguishable from the logout's own; OldMatchesBefore is false then.
- LogoutResult
What LogoutCycle observed, in order.
- NativeDependencyFile
One selected DLL and the reason it belongs in the disposable game.
- NativeDependencyGap
A choice the resolver cannot make safely. Candidate paths are suggestions, never staged automatically.
- NativeDependencyLock
An editable, pinned result of Resolve(NativeDependencyRequest). Save a plan with gaps, supply or confirm the missing files in the request, and resolve again; once ready, ReadReady(string) checks the exact files without rediscovering them. Keep this lock private: it contains machine paths.
- NativeDependencyRequest
Inputs for discovering the files a native mod smoke needs. Search inputs are explicit local directories or DLLs; nothing is downloaded.
- NativeDependencyResolver
Discovers a mod's hard BepInEx dependencies and directly referenced managed libraries from explicit local roots, plus one pinned default ValheimCLI plugin providing the requested commands. It never guesses among candidates, omits a direct reference merely because a soft dependency exists, or downloads code. No game is started or install changed.
- NotKeptFile
A file a run made or changed that Retire(string, long, long) listed instead of keeping: its size and SHA256 (none for a link), and why.
- Observation
One read-only extension result and its declared source/completeness. An incomplete reading is not an assertion pass.
- ObservedCommand
One read-only ValheimCLI observation retained with the actor's role and its complete reply.
- ObservedContent
An item or prefab in scope: the game's hash of its name, how many times its registry's list holds it (0: only in the index) and the name of what the game's lookup by that hash returns (null: nothing).
- ObservedPiece
A prefab found through a build tool's PieceTable, with the dependencies of its Piece component.
- ObservedReference
A recipe's item, station or resource: its prefab name (null for no reference) and the game's lookup of it.
- ObservedWait
The toolkit's one polling wait: re-observes a read-only source until it matches, for state that announces its change nowhere the runner can wait on (an adapter's readiness flag, a client's global keys, a synced config value). When the change announces itself, wait for that instead: LogWait for a log line, StateWait for a ValheimCLI game state.
Each round observes once, then fails at once when
failsnames a reason (WaitFailedException), returns whenmatchesholds, and otherwise waits at mostinterval(less when the deadline is nearer) before observing again. The timeout is required and finite; expiry throws WaitTimeoutException withdescribeof the last observation. The cancellation token ends the wait at once, including during the pause.observemust be read-only: never put a gameplay action in it.
- PackageProvenance
One package of this toolkit in the running process: its NuGet id, the version it was built as, the source commit its build recorded (for the ValheimCLI transport, the valheimCLI commit a candidate version names; empty when none), its state and the SHA-256 of the assembly file at report time. These are package coordinates from assembly metadata, not the hash or provenance of a
.nupkg. AValheim.Testing*package isreleasedwhen the release workflow built it, acandidatewhen its version carries-candidate(a local build identity, never published), andunreleasedotherwise (a source build). The transport (Valheim.Testing.Cli, built from the valheimCLI fork) is acandidateorreleased: a version without-candidateis one the release refuses unless NuGet.org serves it, whose nuspec names its commit; its SHA-256 tells a local build of that version apart.
- PaintPlan
What a client paint check measures: loaded paint RGBA at declared points. Fixture preparation writes it; the ObserveCheck example's
paintprobe reads it.
- PinnedDirectory
A fixture directory pinned by the SHA256 of every file (see Manifest(string)).
- PinnedFile
A file a plan depends on, pinned by path and SHA256.
- PlacedObject
A placed world object as observed or expected: prefab hash, position (x, y, z) and rotation quaternion (x, y, z, w).
- PlayerPlacement
Puts a joined client's player where a check needs it and proves it stands there: protection, arrival arranged by the server, and stationary support read on the client. The server moves the player with the game's own teleport (
cli_teleport_peer), which runs on the player's client and needs nothing installed there, so this works on a client that is not an admin and has cheats off. Arrival is established only by the client's own observations; a teleport reply is not arrival.
- PlayerPlacement.TeleportArrival
One arrival: the client's supported landing, and the teleport's game-reported phase times and raw trace line. Target is the point the landing was judged against: the requested point, or with
loadedGroundthe same place at the loaded ground's height.
- PluginAssembly
What a DLL declares to BepInEx: its assembly name, the assemblies it references and its plugins (none for a library).
- PluginDeclaration
A BepInEx plugin as its assembly declares it:
[BepInPlugin]and the attributes BepInEx reads beside it.
- PluginDependency
A
[BepInDependency]: a hard one stops the plugin loading without it; a soft one only orders loading.
- PluginMetadata
Reads a DLL's BepInEx metadata without loading it, as BepInEx's chainloader decides what to load: the
[BepInPlugin],[BepInDependency],[BepInIncompatibility]and[BepInProcess]attributes on its types, matched by full type name as BepInEx matches them, and the assembly references the runtime will resolve. A file name says nothing here: a renamed DLL declares the same plugin, and a DLL named like a plugin may declare none.
- ProcessStop
What stopping an owned process did.
- ProfileFileState
A profile file as the runner sees it: whether it exists, its length, SHA256 and last write time.
- RegressionArm
One arm's build of the mod under test: the file, its SHA256 and the source commit it was built from.
- RegressionCli
The pinned default ValheimCLI plugin, staged in
BepInEx/plugins.
- RegressionClient
The owned client's disposable character and launch options; its install and port are the client environment's.
- RegressionFile
A file to stage, by path and its SHA256.
- RegressionFixture
The hosted fixture: a directory that holds exactly one world (ExpectedTree), and that world's UID.
- RegressionInputs
The inputs of a targeted native regression: one owned client hosting one disposable world, one mod under test in two or more arms (conventionally
parentandcandidate), its runtime dependencies and an optional game-side probe. Where it runs is not here: the client's install, port, disposable copy, save folders and loader come from the environment inventory's client environment (selected byTargetedRegressionin GameSessions), this machine when there is no file. The scenario source (the rounds and their assertions) stays free of both, so the same source can be shared. Read(string) resolves relative paths against the file's own directory and refuses unknown fields.
- RegressionMod
The mod under test: the file name it is installed as and its arms, which differ only in this DLL.
- RetiredCopy
What Retire(string, long, long) did: the removed copy, where the changes went, the files the run added or changed and those it removed (relative paths), what was listed and not kept, the bytes kept and the bytes the copy held.
- RoomSize
A room prefab's size (
Room.m_size) in metres: x across, y up, z along the room's own axes.
- RunLog
A log file a run's teardown scan reads.
Required: its absence is a failure (a BepInEx log); otherwise it is recorded as absent (a Unity log that was not redirected there).
- RunManifest
What one arm's run loads, for review and evidence: only the allowlist and the exact staged hashes; no machine path and no pin for any plugin outside the allowlist.
- RunManifestArm
One arm of the comparison: its name, commit, distinct artifact name and SHA256.
- SavedDungeon
A dungeon generator's saved state on the server. Format is
roomData(the byte array Valheim 1.0 writes),legacy(theroom<i>fields of older saves, which the game reads whenroomDatais absent) ornone. The generator stands at its location's position plus the location's rotation applied to the generator's offset in the location prefab (typically about 5000 m up); InteriorOffset is that difference.
- ScenarioReport
The result of one run: its steps, each in a StepPhase, the four states derived from them, provenance, log scans and linked evidence. Write(string) writes
result.json(schema Schema) andjunit.xml(one test suite per phase).
- ServerRunPlan
A plan for owned dedicated-server runs on pinned fixtures (see
PinnedServerRun). Derive a mod's plan from it and add the mod's scenario fields; Read<T>(string) refuses unknown fields.{runtime},{world}and{port}in arguments and environment values expand to the copies and the CLI port.
- SessionControl
Actions affect an attached game, never launch or terminate its process. Mod readiness is separate.
- SideObservation
The observation one side supplies, and the build its owner plugin is pinned to there (the plan's MD5 for that side, or
absent). A null Observation is a side that was not observed.
- SimulationRange
The client's simulation distance, as the game syncs it with the server: Near rings of zones round the player's zone are loaded with their terrain and every object, and distant objects (large trees, locations) stay up to Near + Far rings. Classic areas are squares; the others leave out the corner zones by distance. The graphics setting picks it (near 1 to 6 and beyond, far 2); the server lowers a client's to its own.
- SiteNotFoundException
Find(GameActor, string, IReadOnlyList<TerrainGridRequest>, Func<IReadOnlyList<TerrainSample>, bool>) read every grid and none was enough; Samples says how many were searched.
- SiteSearch
Finds test sites on a world the game has just created, from the world generator's heights read through ValheimCLI (
valheim.world/terrain-grid, TerrainCapture), never from the mod under test. Grids are read in the given order, each sample kept, until the mod's own rule (choose) finds what it needs; the rule decides what a site is, the search only decides where to look and in what order.
- StableHash
The game's stable string hash (
string.GetStableHashCode()), by which Valheim keys prefabs, dungeon rooms and ZDO fields. Two accumulators over alternate UTF-16 code units, stopping at the end or at a'\0', as Valheim 1.0.16 computes it. Use it to name the hashes an observation reports: a client that cannot resolve a hash cannot name it.
- StagedFile
One DLL in the staged allowlist, as the run manifest records it (install-relative path, no machine path).
- StandingPins
ValheimCLI's standing expectations file (its
[Expectations] Filesetting): onekey=valuepin per line, which the game checks before every command but its diagnostics. Format(IEnumerable<KeyValuePair<string, string>>) writes one, Parse(IEnumerable<string>, string) and Read(string) check one with ValheimCLI's own parser before a launch, so a malformed line fails here rather than as a refusal of every command in the game. Written by a script, eight pins once ended up on one space-separated line (a PowerShell array expanded into a string); that line is named as such.
- StartupEvents
What an owned server's startup waits on instead of retrying connections. The process exit is always watched. Without CliLog, connecting falls back to bounded retries at the session's poll interval.
- StepResult
One timed outcome in Steps.
Secondsis elapsed wall time;Erroris empty on success and contains the exception message on failure. The defaultPhaseis Scenario.
- StrictExpectations
Uses ValheimCLI's parser and always emits its strict expectation command.
- SupportException
A support check failed; Readings holds every observation taken, the failing one last.
- SurfacePlan
What a client surface check measures: loaded-ground heights at grid vertices and, optionally, a support point where a stationary player must stand grounded (on dry ground: a swimming player is not supported). Fixture preparation writes it; the ObserveCheck example's
surfaceprobe reads it. Expected values come from the fixture's declared inputs, never from the check.
- SyncedConfig
Reads a plugin's live config entry on a server or a client through an adapter's
ConfigEntryCommand(default ValheimCLI plugin), so a test can compare the value each side uses after a join or an admin change. The observation reads theConfigEntryitself on each side; how the mod syncs it (ServerSync embedded in the mod, Jötunn, its own RPC) is the mod's business, and a synced mod sets the client's entry. Values compare as the text BepInEx would write to the file, whatever the entry's type. Nothing here writes config: the change under test is the mod's own (an admin command, a server-side file edit).
- TeleportTrace
One ValheimCLI teleport trace, in milliseconds from arming the trace. A phase that did not occur is -1. The raw line is retained for audit; the numeric fields are suitable for comparisons and reports.
- TerrainCapture
Validated captured inputs. Replay is exact and is not an independent correctness assertion.
- TerrainSitePoint
A bounded, read-only observation of loaded terrain at an explicitly named site.
- TerrainSiteReading
One point's loaded layers, read through the same capabilities as the probes: the ground height (
valheim.world/terrain,loaded-ground, as TerrainProbe), the terrain vertex's own collider height (valheim.world/terrain-surface, as SurfaceProbe) and the raw paint texel (valheim.world/terrain-paint, as PaintProbe: R dirt, G cultivated, B paved, A vegetation: 1 where it may grow, 0 where cleared).
- TerrainSiteSnapshot
Captures the loaded client ground, terrain collider and paint. A dedicated server can also be supplied to verify the same world, but only a client with a local player can report site-area readiness. Every command and its exact reply is retained so a test report can be audited without trusting only the parsed values. A snapshot is the probes' readings without expectations.
- TestAccess
Establishes explicitly requested test access on owned disposable actors only.
- TestAccessState
Observed access facts, not a grant of permission or proof a scenario action succeeded.
- ToolkitProvenance
What ran, written into every
result.jsonasToolkit(schema 4): the package coordinates recorded by toolkit assemblies (PackageProvenance) and, separately, the loaded assemblies including the runner (LoadedAssemblies). Package coordinates are not evidence that a source-built runner used those package bytes. Also records the runner (the entry assembly) and its SHA-256, and, when the provenance itself could not be read, why (Error; the result is written regardless). Each actor's in-game plugin pins are Plugins.
- TransformMatch
One-to-one matching of placed objects by prefab, position and rotation. Counts must agree and every expected object needs its own observed object: stacked identical prefabs cannot swap by sharing a horizontal position, and a duplicate cannot stand in for a missing object. Rotation compares the quaternion angle, so q and -q are the same.
- UnresolvedPrefab
One prefab hash the observing process cannot resolve: how many saved objects carry it, and where the first is.
- UnresolvedPrefabScan
A census of the saved objects (ZDOs) within Radius metres of (X, Z) whose prefab hash the observing process's
ZNetScenecannot resolve. Complete once every zone the circle touches was loaded there; Scanned counts the objects read, leaving out the observing player's own object, and WithoutPrefab those without a prefab (which the game creates nothing for on purpose).
- UnresolvedPrefabs
Reads the census of prefab hashes a process cannot resolve, through an adapter's
UnresolvedPrefabs.Command()(default ValheimCLI plugin), around that process's reference position (the local player on a client). On a client without a server-side mod, an object whose prefab only the mod registers is never created there: Valheim 1.0.16 logs "Missing prefab hash" and skips it. VanillaClientCheck runs it as a scenario step.
- VanillaClientCheck
The client half of a "clients do not need it" check for a server-side mod: a client with BepInEx, ValheimCLI and the mod's test adapter but not the mod (pin the mod
absentin the client plan) goes to each place the mod touches and must see nothing it cannot handle. Use Measure(ClientRound) as, or inside, a ClientRounds measurement. For each of Points (or once where the player stands, when there are none) it arrives there (Arrive(GameActor?, GameActor, HeightExpectation, TimeSpan, CancellationToken, bool, bool),{round}-vanilla-client-{n}-arrival.json), waits for a complete census of the objects around the player and fails on any prefab hash the client cannot resolve, naming each ({round}-vanilla-client-{n}.json). Then, with ClientLogs, it scans the client's logs so far and fails on the three quiet failures of a client without the mod: objects whose prefab is missing (missing-prefab-hash), per-object RPCs with no handler (rpc-method-missing) and errors while objects unload (nre-remove-objects) ({round}-vanilla-client-logs.json). Pass LogClassifications to the run's teardown scan too, so the same lines fail the whole run wherever they appear.
- WorldFixture
Verified, disposable copy of a world fixture. Disposing removes only the new copy unless Preserve is set.
- WorldIdentity
The identity a world fixture's own metadata file states: its name, seed and UID, as the game reads them when it loads the world and as
cli_worldand the session state report the UID (m_uid). Reading it before a run tells a wrong fixture (a reused world name holding another snapshot) from a world that fails to load, without starting the game.
- ZoneCycle
Leave the area and come back: the lifecycle event in which the game destroys every object of a zone the player has left and recreates it from its saved data on return. A mod's object that keeps state only in a component field loses it here, and an object whose teardown throws shows up as
ZNetScene.RemoveObjectserrors in the client's log.Run(GameActor, GameActor, CancellationToken) reads the Zones through the default ValheimCLI plugin's zone observation and requires them loaded, then has the protected player arrive at Away (Arrive(GameActor?, GameActor, HeightExpectation, TimeSpan, CancellationToken, bool, bool): one teleport by the server, arrival read on the client, never flying) and waits until the client holds nothing of those zones: no terrain and no object instance. Then the player arrives at Back and it waits until the zones are loaded again, every saved object instantiated. Re-observe the mod's objects afterwards and compare them with what was there before; scan the client's log for teardown errors.
How far is far enough comes from the client's own simulation distance, read before leaving: in 1.0.16 a client keeps a zone's objects while the player is within near + far rings of it (the distant ones that far out, the rest within near rings) and its terrain within near rings, dropping the terrain once it has been out of that range for 4 s and its last near object has gone. So Away's zone must be at least near + far + 1 rings from every zone of interest; closer is refused before anything moves, and a player that settles closer fails. Each wait reads the client at Interval until StepTimeout; expiry names the zones and the last reading. The teleports are never repeated.
- ZoneCycleResult
What Run(GameActor, GameActor, CancellationToken) observed, in order.
- ZonePreparation
Fixture authoring: make zones exist in a world before a scenario relies on them. Never an acceptance result.
- ZoneReading
One reading of ValheimCLI's
valheim.observe/zonesobservation: the zone of the client's reference position (where its player is), its simulation distance, and the requested zones. Data is the reading as the adapter returned it, for evidence.
- ZoneState
One zone as the client holds it: its terrain, the object instances in it and the saved objects that have none.
Structs
- ZoneId
A zone as the game numbers it: 64 m square, zone (0, 0) centred on the world origin.
Interfaces
- IEvidence
Evidence that Write(string) serializes itself, to
evidence/<kind>-NNN.jsonbesideresult.json, and links with its SHA-256. The snapshots implement it.
- IGameThreadStatusTransport
A STATUS reading made on a separate socket, independent of a queued game-thread command.
- IGameTransport
Low-level transport. Use GameActor for strictly pinned test actions and observations.
- IOwnedProcess
A process this run started and must stop: an owned dedicated server (DirectServerProcess,
HostServerProcess), an owned client (ClientSession's launch,InteractiveClientProcess) or a helper such as aCliTunnel's ssh forward. The seam lets fast tests drive the real startup, restart and stop rules without Unity: use FakeOwnedProcess.
- IOwnedServer
The owned dedicated server a joining client's ClientRounds run against: the rounds wait until it accepts game connections before each join and restart only it between rounds. OwnedServerSession implements it; a no-game test implements it over scripted servers.
Enums
- CensusSide
The process a content observation comes from: a dedicated server or host, or a joined client.
- CensusState
What a content census found for one expected entry, or for one in-scope entry nobody expected. Only Present passes; Unsupported means the census cannot answer the check and is never a pass.
- GameConnectionStatus
The game's connection status, with the game's names and numbers (
ZNet.ConnectionStatusin 1.0.16). A server refuses a peer by sending one of the error numbers, and the client keeps that status until its next join, also once it is back at its main menu: 3 is a network or mod version mismatch, 6 a wrong password, 7 an id already connected, 8 the ban or permit list, 9 a full server.
- LogPhase
When a pattern applies: the teardown scan of a run's logs, an owned process's startup, or both.
- LogSeverity
A failure fails the run's log scan; a warning is counted and reported only.
- QuitRequest
How an owned game process was asked to quit. The game quits through Unity's
OnApplicationQuit: it saves the profile and, on a server, the world, then retires its PlayFab lobby.
- StepPhase
Which of the four reported states a step belongs to. Preflight: checks before anything is copied. Setup: copying, launching, joining and the barriers until the runtime is ready (a runner's
validatemode runs Preflight and the copying Setup steps, and launches nothing). Scenario: the mod's exercise and assertions, the default. Cleanup: stopping, retiring, restoring and the log scan.
- StopOutcome
How an owned process ended when it was stopped.
- TestActorRole
The owned process state in which test access is requested.