354 lines
19 KiB
Markdown
354 lines
19 KiB
Markdown
# Architecture Diagram
|
|
|
|
## System Overview
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ CLIENT APPLICATIONS │
|
|
│ │
|
|
│ ┌───────────────────┐ ┌───────────────────┐ │
|
|
│ │ Settlers Game │ │ Chat Room App │ │
|
|
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
|
|
│ │ │ Game UI │ │ │ │ Chat UI │ │ │
|
|
│ │ │ - Board │ │ │ │ - Messages │ │ │
|
|
│ │ │ - Actions │ │ │ │ - Input │ │ │
|
|
│ │ └─────────────┘ │ │ └─────────────┘ │ │
|
|
│ │ │ │ │ │
|
|
│ │ Uses: │ │ Uses: │ │
|
|
│ │ ┌──────────────────────────────────────────┐ │ │
|
|
│ │ │ REUSABLE COMPONENTS │ │ │
|
|
│ │ │ • MediaControl (Video/Audio UI) │ │ │
|
|
│ │ │ • MediaAgent (WebRTC Signaling) │ │ │
|
|
│ │ │ • PlayerList (Participant Display) │ │ │
|
|
│ │ └──────────────────────────────────────────┘ │ │
|
|
│ └───────────────────┘ └───────────────────┘ │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ WebSocket
|
|
↓
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ SERVER LAYERS │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ APPLICATION LAYER (App-Specific) │ │
|
|
│ │ │ │
|
|
│ │ Settlers Game │ Chat App │ │
|
|
│ │ ┌────────────────────┐ │ ┌───────────────────┐ │ │
|
|
│ │ │ GameSessionMeta │ │ │ ChatSessionMeta │ │ │
|
|
│ │ │ - color │ │ │ - status │ │ │
|
|
│ │ │ - player │ │ │ - messageCount │ │ │
|
|
│ │ │ - resources │ │ │ - customStatus │ │ │
|
|
│ │ └────────────────────┘ │ └───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ┌────────────────────┐ │ ┌───────────────────┐ │ │
|
|
│ │ │ GameRoomMeta │ │ │ ChatRoomMeta │ │ │
|
|
│ │ │ - players │ │ │ - messages │ │ │
|
|
│ │ │ - board setup │ │ │ - topic │ │ │
|
|
│ │ │ - game rules │ │ │ - pinnedMessages │ │ │
|
|
│ │ └────────────────────┘ │ └───────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ Message Handlers: │ Message Handlers: │ │
|
|
│ │ • set color │ • send message │ │
|
|
│ │ • place settlement │ • set status │ │
|
|
│ │ • trade resources │ • pin message │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ │ uses metadata │
|
|
│ ↓ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ ADAPTER LAYER (Optional Compatibility) │ │
|
|
│ │ │ │
|
|
│ │ Proxy Handlers: │ │
|
|
│ │ • session.color ─────→ session.metadata.color │ │
|
|
│ │ • session.player ────→ session.metadata.player │ │
|
|
│ │ • game.players ──────→ game.metadata.players │ │
|
|
│ │ │ │
|
|
│ │ Enables backward compatibility without code changes │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ │ wraps │
|
|
│ ↓ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ INFRASTRUCTURE LAYER (Reusable Framework) │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Session Management │ │ │
|
|
│ │ │ • BaseSession (id, name, ws, live, has_media) │ │ │
|
|
│ │ │ • Session<TMeta> (+ metadata field) │ │ │
|
|
│ │ │ • createBaseSession() │ │ │
|
|
│ │ │ • updateSessionActivity() │ │ │
|
|
│ │ │ • isSessionActive() │ │ │
|
|
│ │ │ • getSessionName() │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Room Management │ │ │
|
|
│ │ │ • BaseRoom (id, name, sessions, state) │ │ │
|
|
│ │ │ • Room<TMeta> (+ metadata field) │ │ │
|
|
│ │ │ • getParticipants() │ │ │
|
|
│ │ │ • getActiveSessions() │ │ │
|
|
│ │ │ • cleanupInactiveSessions() │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ WebRTC Signaling │ │ │
|
|
│ │ │ • join() - Add peer to registry │ │ │
|
|
│ │ │ • part() - Remove peer from registry │ │ │
|
|
│ │ │ • relayICECandidate() - Forward ICE │ │ │
|
|
│ │ │ • relaySessionDescription() - Forward SDP │ │ │
|
|
│ │ │ • PeerRegistry - Track peer connections │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ WebSocket Handling │ │ │
|
|
│ │ │ • Connection management │ │ │
|
|
│ │ │ • Automatic reconnection │ │ │
|
|
│ │ │ • Message routing │ │ │
|
|
│ │ │ • Keep-alive pings │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Data Flow: Setting User Name
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ CLIENT │
|
|
└─────────────┘
|
|
│
|
|
│ 1. User types name "Alice"
|
|
│
|
|
↓
|
|
sendJsonMessage({
|
|
type: "set",
|
|
field: "name",
|
|
value: "Alice"
|
|
})
|
|
│
|
|
│ WebSocket
|
|
↓
|
|
┌─────────────────────────────────────────┐
|
|
│ SERVER │
|
|
│ │
|
|
│ Infrastructure Layer: │
|
|
│ ├─ Receive message │
|
|
│ ├─ Find session by WebSocket │
|
|
│ └─ Route to handler │
|
|
│ │
|
|
│ Application Layer: │
|
|
│ ├─ session.name = "Alice" │
|
|
│ └─ Broadcast to all sessions │
|
|
│ │
|
|
│ Infrastructure Layer: │
|
|
│ └─ getParticipants(room.sessions) │
|
|
│ └─ Returns: [ │
|
|
│ { session_id, name: "Alice", │
|
|
│ live: true, has_media: true } │
|
|
│ ] │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
│ Broadcast update
|
|
↓
|
|
┌─────────────┐
|
|
│ ALL CLIENTS│
|
|
└─────────────┘
|
|
│
|
|
│ Receive participants update
|
|
↓
|
|
PlayerList rerenders
|
|
with "Alice" shown
|
|
```
|
|
|
|
## Data Flow: Joining Video Call
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ CLIENT │
|
|
└─────────────┘
|
|
│
|
|
│ 1. MediaAgent mounts
|
|
│ 2. Gets user media (camera/mic)
|
|
│
|
|
↓
|
|
sendJsonMessage({
|
|
type: "join",
|
|
data: { has_media: true }
|
|
})
|
|
│
|
|
│ WebSocket
|
|
↓
|
|
┌──────────────────────────────────────────┐
|
|
│ SERVER │
|
|
│ │
|
|
│ Infrastructure Layer (WebRTC): │
|
|
│ ├─ join(peers, session, { has_media }) │
|
|
│ │ │
|
|
│ │ For each existing peer: │
|
|
│ │ ├─ Send addPeer to existing peer │
|
|
│ │ │ { peer_id: "Alice", │
|
|
│ │ │ peer_name: "Alice", │
|
|
│ │ │ has_media: true, │
|
|
│ │ │ should_create_offer: false } │
|
|
│ │ │ │
|
|
│ │ └─ Send addPeer to new peer │
|
|
│ │ { peer_id: "Bob", │
|
|
│ │ peer_name: "Bob", │
|
|
│ │ has_media: true, │
|
|
│ │ should_create_offer: true } │
|
|
│ │ │
|
|
│ └─ Add to peer registry: │
|
|
│ peers["Alice"] = { │
|
|
│ ws, has_media: true │
|
|
│ } │
|
|
│ │
|
|
│ └─ Send join_status: │
|
|
│ { type: "join_status", │
|
|
│ status: "Joined" } │
|
|
└──────────────────────────────────────────┘
|
|
│
|
|
│ Relay messages
|
|
↓
|
|
┌─────────────┐
|
|
│ ALL CLIENTS│
|
|
└─────────────┘
|
|
│
|
|
│ Each client:
|
|
├─ Creates RTCPeerConnection
|
|
├─ Adds local media tracks
|
|
├─ Creates offer/answer
|
|
└─ Exchanges ICE candidates
|
|
│
|
|
↓
|
|
Peer-to-peer video/audio flows
|
|
```
|
|
|
|
## Data Flow: Game-Specific Action
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ GAME CLIENT│
|
|
└─────────────┘
|
|
│
|
|
│ Player clicks "Blue" color
|
|
│
|
|
↓
|
|
sendJsonMessage({
|
|
type: "set",
|
|
field: "color",
|
|
value: "blue"
|
|
})
|
|
│
|
|
│ WebSocket
|
|
↓
|
|
┌───────────────────────────────────────────┐
|
|
│ SERVER │
|
|
│ │
|
|
│ Infrastructure Layer: │
|
|
│ ├─ Route message to app handler │
|
|
│ └─ session object available │
|
|
│ │
|
|
│ Adapter Layer (optional): │
|
|
│ └─ Wraps session with proxy │
|
|
│ │
|
|
│ Application Layer (Game): │
|
|
│ ├─ setPlayerColor(game, session, "blue")│
|
|
│ │ ├─ Validate color available │
|
|
│ │ ├─ session.color = "blue" │ ← Direct access
|
|
│ │ │ (or session.metadata.color) │ ← New arch
|
|
│ │ ├─ session.player = gamePlayer │
|
|
│ │ └─ Update game.metadata.players │
|
|
│ │ │
|
|
│ └─ Broadcast update: │
|
|
│ participants: [ │
|
|
│ { │
|
|
│ ...baseParticipant, │ ← Infrastructure
|
|
│ color: "blue" │ ← App-specific
|
|
│ } │
|
|
│ ] │
|
|
└───────────────────────────────────────────┘
|
|
│
|
|
│ Broadcast
|
|
↓
|
|
┌─────────────┐
|
|
│ ALL CLIENTS│
|
|
└─────────────┘
|
|
│
|
|
│ Update UI
|
|
↓
|
|
PlayerList shows
|
|
"Alice" with blue color
|
|
```
|
|
|
|
## Metadata Access Patterns
|
|
|
|
### Without Adapter (New Code)
|
|
|
|
```typescript
|
|
// Explicit metadata access
|
|
const color = session.metadata?.color;
|
|
session.metadata = { ...session.metadata, color: 'blue' };
|
|
|
|
const players = room.metadata.players;
|
|
room.metadata.players['blue'] = newPlayer;
|
|
```
|
|
|
|
### With Adapter (Backward Compatible)
|
|
|
|
```typescript
|
|
// Same as before - adapter proxies to metadata
|
|
const color = session.color;
|
|
session.color = 'blue';
|
|
|
|
const players = room.players;
|
|
room.players['blue'] = newPlayer;
|
|
```
|
|
|
|
Both work! Adapter allows gradual migration.
|
|
|
|
## Reusability Layers
|
|
|
|
```
|
|
Application A (Settlers) Application B (Chat) Application C (Whiteboard)
|
|
│ │ │
|
|
├── GameSessionMeta ├── ChatSessionMeta ├── WhiteboardSessionMeta
|
|
│ - color │ - status │ - cursorColor
|
|
│ - player │ - messageCount │ - selectedTool
|
|
│ │ │
|
|
└─────────────┬────────────┴────────────────────────┘
|
|
│
|
|
│ All apps extend base types
|
|
↓
|
|
┌─────────────────────────────────┐
|
|
│ INFRASTRUCTURE (Shared) │
|
|
│ • Session<TMeta> │
|
|
│ • Room<TMeta> │
|
|
│ • MediaControl (WebRTC) │
|
|
│ • getParticipants() │
|
|
│ • Session management │
|
|
└─────────────────────────────────┘
|
|
↑
|
|
Single implementation,
|
|
works for all apps!
|
|
```
|
|
|
|
## Summary
|
|
|
|
This architecture provides:
|
|
|
|
✅ **Clean separation** between infrastructure and application
|
|
✅ **Type-safe metadata** for app-specific data
|
|
✅ **Reusable components** that work across applications
|
|
✅ **Backward compatibility** via adapter layer
|
|
✅ **Clear data flows** from client to server to all clients
|
|
✅ **Proven patterns** ready for production use
|
|
|
|
Any new application can plug into the infrastructure and get:
|
|
- Multi-user rooms
|
|
- WebSocket connection management
|
|
- WebRTC video/audio signaling
|
|
- Participant tracking
|
|
- UI components (MediaControl)
|
|
|
|
All for free! Just define your metadata types and business logic.
|