Skip to main content
Beyond the SDK

Not translated yet — showing English.

Under the hood: transport & the hub

An architecture reference, not a surface you call. Nothing on this page appears in the API you write against: there is no socket, channel, envelope or retry for you to manage. Core Concepts names the stack; the mechanism lives only here, so that an architect can check what the SDK does with a dropped connection, a disabled module or a message that must arrive exactly once.

The layer stack

user spacethe SDK's own businessyour codemodules: rooms, matchmaking, inventory, leaderboards…primitives: events · RPC · data · groupsthe hub: DI and mounting · the user session · state recovery · message QoS · routingtransport adapters: WebSocket · PlayServ UDP · HTTP
The layer stack: your code and modules above the line, primitives, the hub and transports below it

Five layers, top down: user space, modules, primitives, the hub, and the transport adapters under it. The top two are user space; everything below is the SDK's own business.

  • User space is your code. It sees modules, and the vocabulary stops there.
  • Modules are the applied layer: rooms, matchmaking, inventory, leaderboards. They form a graph, not a tree, which is what the hub has to resolve when one of them is switched off (Inheritance & Composition is the shape itself).
  • Primitives are the first implementation everything references: data, events, RPC, groups. A module is a named assembly of primitives plus its own rules.
  • The hub is the controller: dependency injection, module mounting, the user session, state recovery, message quality of service, and routing each inbound message to the module mounted for it.
  • Transports are adapters onto a protocol. Several exist; the hub treats them alike.

Transports are adapters

There will be more than one transport, and they differ in ways that would otherwise leak into every module:

AxisRange
Shapemessage-driven or request-driven
Channelssingle-channel or multi-channel
Statewith connection state recovery, or without
ProtocolTCP or UDP

Today that means WebSocket, the PlayServ UDP transport, and plain HTTP. Each one is an adapter behind its own implementation details, and each exposes the same thing upward: a transport session. The hub holds a session, never a socket, so nothing above the adapter reasons about the network interface.

One transport session at a time

One transport channel and one transport session at a time. Running a backend transport for leaderboards while a master-client transport carries the live session is out of scope, and no API signature depends on several channels being open. Until a later version says otherwise, the single-session shape is the contract.

The hub hides the transport completely

Downward, the hub speaks the transport interface. Upward, it offers state, events and the user session. Module code and game code are equally unable to tell which transport is underneath, or how the hub batched a call, or what it did to get back to a consistent state after a gap.

  • The user session belongs to the hub, not to a module. Reconnect, resume and state recovery happen once, at the hub, for everything mounted on it.
  • Message QoS belongs to the hub, not to the data module. Envelopes, retries and packing are hub mechanics.

Message QoS: three levels

A module declares only the delivery guarantee it needs:

LevelMeaning
at least onceredelivered until acknowledged; the receiver tolerates duplicates
at most oncesent once, never retried; loss is acceptable
exactly oncededuplicated and acknowledged; the expensive one, used where it is required

How the guarantee is met is the hub's concern, not the module's and not yours.

DI, mounting, and disabled modules

The hub instantiates modules and mounts them — at the root or in a namespace — resolving each module's dependencies on the primitives and on other modules. A build that does not need a module does not mount it. Mounting is namespaced, and a second module claiming an already-occupied mount point is rejected at mount time — composition fails there, never at the first call into it.

Because modules form a graph, switching one off has consequences downstream, and the hub takes exactly one of two paths:

  1. Disable the dependent chain. Every module that needs the missing one is switched off too, and its interfaces are absent rather than failing.
  2. Declare degraded functionality. The dependents stay mounted and announce what they can no longer do.

There is no third path: a mounted module never quietly drops the operations it can no longer perform, so a disabled dependency is always observable.

What stays above the line

Every promise on the module pages is kept above it: an entity mutation is the network operation, a hook is a typed function, a join is one call. The stack's names may reach you — Core Concepts points here — but nothing below the line is a call your code makes. Those layers exist so the promises above survive a transport change.

What you will meet — the delivery context your handlers run on, when a handle ends, and the in-memory implementation you test against — is one page up: Threads, Lifetime and Testing.