Server Overview
RabiRiichi.Server is an ASP.NET Core (net9.0) app that hosts the
Core engine as a multiplayer service. It adds everything
the headless engine deliberately leaves out: networking, rooms and seats,
authentication, reconnection, AI substitution, and replays.
Architecture at a glance
The key ideas:
- One transport, gRPC-shaped. All traffic is Protobuf messages over a single
/wsWebSocket. AWebSocketAdapterimplements the gRPC streaming interfaces, so the connection/streaming code is transport-agnostic (a dormant gRPC endpoint exists but isn't mapped). See Transport & protocol. - Single-writer concurrency. Nearly all room/user mutations run through one
RoomTaskQueue(a bounded single-reader channel), giving lock-free-by- serialization semantics. Overload surfaces as aResourceExhaustederror. - Durable connections. Each
Userowns aConnectionthat survives socket drops; a reconnect within a grace window resumes the same session, replays missed messages, and re-syncs game state. - The engine bridge is
ServerActionCenter, the server'sIActionCenter. It serializes each event per seat (privacy-filtered), presents inquiries, and — for replays — captures a fully-revealed god-view stream.
The main pieces
| Area | Files | Docs |
|---|---|---|
| Bootstrap / DI | Startup.cs | Running the server |
| Transport | WebSockets/, Connections/ | Transport & protocol |
| Rooms & users | Models/ | Rooms & users |
| Engine bridge | Connections/ServerActionCenter.cs | Transport, Replays |
| Replays | Services/ReplayStore.cs, ReplayCleanupService.cs | Replays |
| Auth | Auth/ | Authentication |
| AI | Agents/ | AI agents |
Request lifecycle
- A client opens
/ws/public(anonymous) or/ws/connect(after a token handshake). - It sends a
ClientMessageDtocontaining aClientRequest(with a monotonicid). - The
WebSocketControllerdispatches it on theRoomTaskQueue(HandlePublic/HandlePrivate). - The handler mutates room/user state or reads a replay, and returns a payload
wrapped as a
ServerMessageDtowithrespond_to = idso the client can correlate. - Server-initiated pushes (events, inquiries, room state, chat, heartbeats) flow
as separate
ServerMsg/EventMsgmessages.
Read on: Transport & protocol.