feat: add publisher and browser client SDK (#9)
quality-gate / quality (push) Successful in 1m6s
quality-gate / quality (push) Successful in 1m6s
Closes #9
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# ADR 0007: caller-owned .NET publisher and browser SDK
|
||||
|
||||
- Status: Accepted
|
||||
- Date: 2026-07-16
|
||||
- Tracking: #9
|
||||
|
||||
## Decision
|
||||
|
||||
The .NET client package exposes separate publisher and browser interfaces plus
|
||||
concrete clients over a caller-supplied `HttpClient`. The caller owns that client,
|
||||
its handler, base address, connection pool, proxy, and lifetime. SDK operations
|
||||
dispose every request, response, and response body they create, but never dispose
|
||||
the supplied client. The package targets `netstandard2.1`, depends only on the
|
||||
wire-contract package and LiteNetLib, and contains no Godot types, global client,
|
||||
service URL, publisher secret, or embedded game credential.
|
||||
|
||||
Every operation returns `RendezvousClientResult<T>` with a stable error code,
|
||||
message, and optional retry guidance. Cancellation remains exceptional through
|
||||
the caller's `CancellationToken`; transport failures become `ServiceUnavailable`.
|
||||
Response bodies are streamed under the contract's 256 KiB browser ceiling before
|
||||
deserialization. Invalid or oversized success bodies become `InternalError` and
|
||||
never escape as partially trusted contract objects.
|
||||
|
||||
The SDK retries only operations whose duplicate execution is safe: scoped reads,
|
||||
idempotency-keyed registration, lease renewal with the same lease token, complete
|
||||
resource update, and lease-token deregistration. It honors bounded server retry
|
||||
guidance and otherwise uses capped exponential backoff with jitter. Each retry
|
||||
creates a fresh HTTP request while preserving the caller's registration
|
||||
idempotency key. Configuration is copied on construction so later option mutation
|
||||
cannot change an in-flight client's behavior.
|
||||
|
||||
`PublishedSession` holds the server-issued lease and presence capabilities needed
|
||||
by the host. Its string representation always redacts them. Update requests are
|
||||
copied before the lease token is attached, so the SDK never mutates caller-owned
|
||||
DTOs. The browser exposes one-page calls and bounded cursor traversal; cursor
|
||||
values remain opaque and caller requests remain unchanged.
|
||||
|
||||
Lease maintenance is explicit. Creating a `SessionLeaseMaintainer` starts no task;
|
||||
the game chooses when to call `RunAsync`, owns cancellation, and awaits
|
||||
`DisposeAsync`. The loop uses the latest server-provided renewal interval and
|
||||
returns a distinct cancelled, disposed, lost-lease, or failed result. Terminal
|
||||
authorization, expiry, and missing-lease responses also raise `LeaseLost` so the
|
||||
host can stop advertising or re-register deliberately.
|
||||
|
||||
## Consequences
|
||||
|
||||
- SpaceGame and Unscouted can inject the publisher/browser interfaces in tests
|
||||
without an engine runtime or real network.
|
||||
- Games must configure an absolute `HttpClient.BaseAddress` (or equivalent
|
||||
handler routing), obtain publisher credentials from their deployment boundary,
|
||||
and explicitly run and dispose lease maintenance.
|
||||
- The versioned client public-API snapshot and live-server integration tests fail
|
||||
together when SDK and HTTP contracts drift.
|
||||
Reference in New Issue
Block a user