Headless Client (.NET tests & bots)
A Crossplay client without Unity, for a game's automated end-to-end tests and console bots: connect over LiteNet UDP (plaintext, or with the server's pre-shared key), log in with an account or as a guest (the identity can be kept in a file so the next run is the same player), resume and sign out, typed requests matched by id exactly like the Unity client's CallAsync, and pushes through OnMessage / NextAsync. It knows no piece: it speaks any piece's wire, or the game's own, through their Contracts types. Many independent clients run in one process. Reference Crossplay.HeadlessClient.Server.dll plus the Contracts DLLs from the server's _crossplay/lib, and the LiteNetLib and MemoryPack packages. Adds nothing to the server.
Services you call 1
Crossplay implements these. Resolve them from DI and call them from your own systems.
IHeadlessClient
A Crossplay client without Unity, for a game's automated end-to-end tests and console bots. It speaks any piece's wire - or the game's own - through that piece's Contracts types, so it knows no piece itself.
-
long? AccountId { get; }The signed-in account, or null.
-
Task<TReply> CallAsync<TRequest, TReply>(TRequest request, TimeSpan? timeout = null, CancellationToken ct = null)Sends a typed request and waits for its reply, matched by request id - the Unity IRequestClient.CallAsync contract. timeout null = the options' request timeout.
-
Task ConnectAsync(CancellationToken ct = null)Connects (following shard redirects). Throws TimeoutException or InvalidOperationException when the server cannot be reached or refuses.
-
void Disconnect()Closes the link on purpose (no Disconnected). ConnectAsync works again after.
-
event Action<string> DisconnectedRaised when an established link drops for any reason other than Disconnect or Dispose; the argument is the transport's reason.
-
event Action<uint> FlagsReceivedRaised with a reply's piggyback flags when they are not 0.
-
GuestIdentity Guest { get; }The guest identity the last successful LoginAsGuestAsync used, or null.
-
event Action<Exception> HandlerFailedRaised when an OnMessage``1 handler throws (the client keeps running).
-
bool IsConnected { get; }True while the link to the server is up.
-
Task<LoginResponse> LoginAsGuestAsync(string identityPath = null, CancellationToken ct = null)Logs in as a guest. With identityPath the identity is read from that file (created on first use) and saved after a successful login, so the next run is the same player; without it the identity lives as long as this client. If the server rejects a saved identity's credentials (e.g. a wiped dev database), one fresh identity is tried. Needs Auth:AllowSignUpOnFirstLogin on the server.
-
Task<LoginResponse> LoginAsync(string username, string password, CancellationToken ct = null)Logs in with a username and password. Waits through a login queue.
-
Task<T> NextAsync<T>(Func<T, bool> match = null, TimeSpan? timeout = null, CancellationToken ct = null)Waits for the next T that satisfies match (null = any). It is subscribed when called, so call it BEFORE sending what triggers the message. Throws TimeoutException when none arrives in time.
-
IDisposable OnMessage<T>(Action<T> handler)Runs handler for every T the server sends until the returned subscription is disposed. Any number of handlers may watch one type.
-
Task<ResumeResponse> ResumeAsync(string token = null, CancellationToken ct = null)Resumes a session on a fresh connection with token (null = SessionToken).
-
void Send<T>(T message, bool reliable = true)Sends any Contracts message (its id comes from its [MessageId]).
-
string SessionToken { get; }The token ResumeAsync resumes with, or null.
-
Task<SignOutResponse> SignOutAsync(CancellationToken ct = null)Signs out on purpose: the server revokes the resume token, then the client clears its identity and disconnects, whatever the server answered.
Configuration 1
Every tunable lives in an options object — there are no magic numbers to hunt for.
HeadlessClientOptions
Where and how a HeadlessClient connects. Mirror the server's Crossplay:Transport.
-
TimeSpan ConnectTimeout { get; set; }How long ConnectAsync waits for the server to accept.
-
string ConnectionKey { get; set; }LiteNet connection key; must match the server's Transport:ConnectionKey.
-
TimeSpan DisconnectTimeout { get; set; }How long without traffic before LiteNet declares the link dead.
-
string EncryptionKey { get; set; }Pre-shared packet key (the server's Transport:EncryptionKey). Empty = plaintext wire, for a dev server that has no key set.
-
string GuestNamePrefix { get; set; }Start of a generated guest name (Create).
-
string Host { get; set; }Server host name or address.
-
int MaxGuestNameLength { get; set; }Longest generated guest name; keep it within the server's name limit.
-
int MaxShardRedirects { get; set; }How many shard redirects a connect follows before giving up.
-
int Port { get; set; }Server UDP port (the server's Transport:Port).
-
int ProtocolVersion { get; set; }Sent with login and resume; the server checks it only when it expects a version.
-
TimeSpan RequestTimeout { get; set; }Default wait for a reply (requests, login, resume, sign-out) and for NextAsync``1. A login-queue update restarts the login wait.