Connection is not readiness. Core RPG tracks transport, authentication, player ownership, replicated readiness, location restoration, and local presentation binding as separate gates.
Startup owner
NetworkBootstrap is the single persistent session entry point and implements INetworkSession. It owns start/stop for dedicated server, host, and client and installs the save-authority seam so pure clients cannot become a second writer.
Authentication to owned player
GameplayReadiness
public readonly struct GameplayReadiness
{
public readonly bool Connected;
public readonly bool Authenticated;
public readonly bool PlayerSpawnedOwned;
public readonly bool NetReady;
public readonly bool SceneRestored;
public readonly bool PlayerBound;
}
Use readiness as a diagnostic model rather than revealing gameplay merely because NGO connected. If a screen remains hidden, identify the specific missing gate instead of bypassing the gate.
Replication categories
| Category | Examples |
|---|---|
| Server-only | Credential repository, password material, character locks, authoritative inventory/combat/quest/bank state, occupancy and save capture. |
| Owner-only | Private inventory/equipment/skills/prayer/magic/location/session state. |
| Public replicated | Public identity, movement, health, combat target, readiness, animation/presentation events. |
| Client-only | Input, camera, HUD, interpolation, VFX, audio and transitions. |
RPC rule
An owner RPC should carry intent, not truth. Validate sender ownership and readiness, resolve IDs against server state, reject stale/invalid requests, then call shared authoritative logic. Never accept client-computed damage, inventory totals, quest state, paths, or save payloads as authoritative.
Disconnect/shutdown rule
Capture character state before the player is despawned/unregistered. Server shutdown captures all registered characters and waits for pending writes inside the bootstrap shutdown lifecycle. A save call after the source player has disappeared is already too late.
IAuthenticationService seam exists so a production backend can replace it.