feat(client): standardize connection outcomes (#13)
quality-gate / quality (push) Successful in 59s
quality-gate / quality (push) Successful in 59s
This commit is contained in:
@@ -29,6 +29,12 @@ RendezvousClientResult<PublishedSession> registered = await publisher.RegisterAs
|
||||
DisplayName = "My server",
|
||||
Visibility = ListingVisibility.Public,
|
||||
Capacity = new() { CurrentPlayers = 1, MaximumPlayers = 8 },
|
||||
DedicatedFallback = new()
|
||||
{
|
||||
AddressFamily = AddressFamilyKind.Ipv4,
|
||||
Address = "203.0.113.40",
|
||||
Port = 7777,
|
||||
},
|
||||
},
|
||||
publisherCredential,
|
||||
cancellationToken);
|
||||
@@ -97,14 +103,22 @@ the accepted peer as connected. Register ordinary gameplay callbacks on
|
||||
`networkEvents.GameplayEvents`; the routing listener reserves Rendezvous direct
|
||||
requests for ticket validation and forwards every other callback normally.
|
||||
|
||||
The joining game first creates the HTTP attempt, then uses its own already-started
|
||||
gameplay manager in the same frame loop:
|
||||
The joining game first requests an attempt through the typed start API. It returns
|
||||
exactly one issued attempt or one terminal service outcome, so service authority
|
||||
is not confused with a later locally observed traversal failure:
|
||||
|
||||
```csharp
|
||||
CreateJoinAttemptResponse attempt = (await joins.CreateAsync(
|
||||
RendezvousConnectionStartResult start = await joins.CreateConnectionAttemptAsync(
|
||||
createJoinRequest,
|
||||
cancellationToken)).Value
|
||||
?? throw new InvalidOperationException("Join issuance failed.");
|
||||
cancellationToken: cancellationToken);
|
||||
if (start.Outcome is { } serviceOutcome)
|
||||
{
|
||||
ShowConnectionFailure(serviceOutcome.Kind, serviceOutcome.Category);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateJoinAttemptResponse attempt = start.Attempt
|
||||
?? throw new InvalidOperationException("The typed start result was invalid.");
|
||||
using RendezvousClientCoordinator client = new(
|
||||
gameplayNetManager,
|
||||
networkEvents,
|
||||
@@ -116,12 +130,35 @@ client.Poll();
|
||||
```
|
||||
|
||||
NAT introduction changes the client state to `Connecting`; it is not success.
|
||||
Only `Connected` supplies `ConnectedPeer`. Call `Cancel()` and then `Poll()` for
|
||||
local cancellation, or `CancelAsync(joins, cancellationToken)` to also revoke the
|
||||
service attempt. Terminal client paths release all event subscriptions. Disposing
|
||||
a coordinator never stops or disposes the caller-owned manager and does not touch
|
||||
an in-flight peer; call `Cancel()` followed by `Poll()` first when that peer must
|
||||
also be disconnected.
|
||||
Only a `Connected` outcome supplies `Peer`. Completion exposes a stable kind,
|
||||
source, category, phase, and elapsed duration. The default HTTP silence, punch,
|
||||
and direct-connect budgets are five, ten, and five seconds respectively; configure
|
||||
them through `RendezvousClientOptions` and `RendezvousCoordinatorOptions` when a
|
||||
game has measured reasons to do so. The signed attempt expiry is always the
|
||||
absolute upper bound.
|
||||
|
||||
Call `Cancel()` and then `Poll()` for local cancellation, or
|
||||
`CancelAsync(joins, cancellationToken)` to also revoke the service attempt.
|
||||
Terminal client paths complete exactly once and release all event subscriptions,
|
||||
so late packets and callbacks are inert. Disposing a coordinator never stops or
|
||||
disposes the caller-owned manager and does not touch an in-flight peer; call
|
||||
`Cancel()` followed by `Poll()` first when that peer must also be disconnected.
|
||||
|
||||
After terminal completion, reporting is explicit and safe to retry. It sends only
|
||||
the authenticated outcome enum and a coarse elapsed bucket—never the endpoint,
|
||||
exact duration, diagnostic text, metadata, or player identity:
|
||||
|
||||
```csharp
|
||||
RendezvousClientResult<ReportConnectionOutcomeResponse> report =
|
||||
await client.ReportOutcomeAsync(joins, cancellationToken);
|
||||
```
|
||||
|
||||
An optional `DedicatedFallback` is copied from the authoritative listing into the
|
||||
issued attempt and terminal outcome. A local deployment may replace it with
|
||||
`RendezvousCoordinatorOptions.DedicatedFallbackOverride`. The SDK only returns
|
||||
the endpoint; it never connects automatically. The game must explicitly decide
|
||||
whether to use it and then connect and authenticate through its own gameplay
|
||||
transport. If the outcome has no fallback, v1 offers no relay.
|
||||
|
||||
Lease renewal is explicit and caller-controlled:
|
||||
|
||||
@@ -155,5 +192,6 @@ apply its own player identity, capacity, ban, and gameplay admission rules. Revo
|
||||
the attempt on cancellation and dispose the validator during host shutdown so its
|
||||
keyed ticket digests are zeroed.
|
||||
|
||||
See the repository's ADR 0007 for HTTP ownership/retry semantics and ADR 0008 for
|
||||
join-capability and connection-ticket security semantics.
|
||||
See the repository's ADR 0007 for HTTP ownership/retry semantics, ADR 0008 for
|
||||
join-capability and connection-ticket security semantics, and ADR 0010 for typed
|
||||
outcomes, deadlines, reporting, and caller-owned fallback.
|
||||
|
||||
Reference in New Issue
Block a user