# Pluggable Room/WebRTC Architecture

This document describes the layered architecture that separates reusable Room/WebRTC infrastructure from application-specific logic.

## Overview

The system is designed with three distinct layers:

1. **Infrastructure Layer** - Reusable Room/Session/WebRTC management
2. **Metadata Layer** - Application-specific data attached to infrastructure
3. **Adapter Layer** - Backward compatibility and migration support

This separation allows MediaControl and Room management to be reused across different applications without modification.

## Architecture Diagram

```
┌─────────────────────────────────────────────────────────────┐
│                     Application Layer                        │
│         (Game Logic - Settlers of Catan specific)           │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  Game Rules, Turns, Resources, Trading, etc.           │ │
│  └────────────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────────┘
                     │ uses metadata
┌────────────────────▼────────────────────────────────────────┐
│                    Metadata Layer                            │
│         (Application-specific data structures)              │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  Session.metadata { color, player, resources }         │ │
│  │  Room.metadata { game state, board, cards, etc. }     │ │
│  └────────────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────────┘
                     │ attached to
┌────────────────────▼────────────────────────────────────────┐
│                Infrastructure Layer                          │
│            (Reusable across applications)                   │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  BaseSession: id, name, ws, live, has_media           │ │
│  │  BaseRoom: id, sessions, state                        │ │
│  │  MediaControl: WebRTC signaling, peer management      │ │
│  │  Room Helpers: getParticipants, session management    │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```

## Type Hierarchy

### Infrastructure Types (Reusable)

Located in: `server/routes/room/types.ts`

```typescript
// Core session - no application logic
interface BaseSession {
  id: string;
  name: string | null;
  ws?: WebSocket;
  live: boolean;
  has_media: boolean;
  // ... infrastructure fields only
}

// Core room - no application logic
interface BaseRoom {
  id: string;
  sessions: Record<string, BaseSession>;
  state: string;
  // ... infrastructure fields only
}

// Generic with metadata support
interface Session<TMetadata = any> extends BaseSession {
  metadata?: TMetadata; // Application data goes here
}

interface Room<TMetadata = any> extends BaseRoom {
  metadata?: TMetadata; // Application data goes here
}
```

### Application-Specific Types

Located in: `server/routes/games/gameMetadata.ts`

```typescript
// Game-specific session data
interface GameSessionMetadata {
  color?: string;        // Player color selection
  player?: Player;       // Reference to game player
  resources?: number;    // Temporary resource count
}

// Game-specific room data
interface GameRoomMetadata {
  players: Record<string, Player>;  // Game players
  turn: Turn;                       // Current turn
  placements: Placements;           // Board state
  chat: any[];                      // Chat messages
  // ... all game-specific fields
}

// Convenience types
type GameSession = Session<GameSessionMetadata>;
type GameRoom = Room<GameRoomMetadata>;
```

## Key Principles

### 1. Clear Separation of Concerns

**Infrastructure Layer Responsibilities:**
- WebSocket connection management
- Session lifecycle (connect, disconnect, timeout)
- WebRTC signaling (join, peer, ICE, SDP)
- Participant listing
- Room state management

**Metadata Layer Responsibilities:**
- Application-specific data structures
- Business logic
- Game rules and validation
- Application state

### 2. Metadata Pattern

All application-specific data is stored in `metadata` fields:

```typescript
// ❌ Old approach - mixed concerns
interface Session {
  id: string;          // Infrastructure
  name: string;        // Infrastructure
  color: string;       // APPLICATION SPECIFIC! ❌
  player: Player;      // APPLICATION SPECIFIC! ❌
}

// ✅ New approach - separated concerns
interface Session<TMetadata> {
  id: string;          // Infrastructure
  name: string;        // Infrastructure
  metadata?: TMetadata; // Application-specific data
}

// Usage
const session: Session<GameSessionMetadata> = {
  id: "abc123",
  name: "Alice",
  metadata: {
    color: "red",      // Game-specific
    player: {...}      // Game-specific
  }
};
```

### 3. Extensibility

Any application can use the infrastructure by defining their own metadata types:

```typescript
// Example: Chat application
interface ChatSessionMetadata {
  displayColor: string;
  status: "available" | "away" | "busy";
  lastTyping?: number;
}

type ChatSession = Session<ChatSessionMetadata>;

// Example: Whiteboard application
interface WhiteboardSessionMetadata {
  tool: "pen" | "eraser" | "shape";
  color: string;
  brushSize: number;
}

type WhiteboardSession = Session<WhiteboardSessionMetadata>;
```

## Reusable Components

### MediaControl (Client)

Located in: `client/src/MediaControl.tsx`

**Reusable:** ✅ No application-specific logic

```typescript
// MediaControl only needs infrastructure data
<MediaControl
  peer={peer}              // Has: session_id, peer_name, srcObject
  isSelf={isLocal}
  sendJsonMessage={send}
/>

// MediaAgent only needs infrastructure
<MediaAgent
  session={session}        // Has: id, name, has_media
  socketUrl={url}
  peers={peers}
  setPeers={setPeers}
/>
```

### Room Helpers (Server)

Located in: `server/routes/room/helpers.ts`

**Reusable:** ✅ Generic functions work with any metadata

```typescript
import { getParticipants } from './room/helpers';

// Works with any Room<T>
const participants = getParticipants(room.sessions);

// Returns base participant data (no app-specific fields)
// [{ session_id, name, live, has_media, ... }]
```

### Extending Participants

Applications can extend the base participant list:

```typescript
// Get base participants (reusable)
const baseParticipants = getParticipants(room.sessions);

// Add application-specific data
const gameParticipants = baseParticipants.map(p => ({
  ...p,
  color: room.sessions[p.session_id].metadata?.color || null
}));
```

## Migration Strategy

### Phase 1: Adapter Layer (Current)

Use the adapter layer to maintain backward compatibility:

```typescript
import { wrapGame, wrapSession } from './games/gameAdapter';

// Wrap existing game objects
const game = wrapGame(loadedGame);

// Code still works with old syntax
session.color = "red";  // Proxied to session.metadata.color
game.turn = {...};      // Proxied to game.metadata.turn
```

### Phase 2: Gradual Migration

Migrate functions one at a time:

```typescript
// Old code
function doSomething(game: any) {
  const color = session.color;  // Direct access
}

// New code
function doSomething(game: GameRoom) {
  const color = session.metadata?.color;  // Metadata access
}
```

### Phase 3: Pure Architecture

Eventually remove adapters and use pure metadata architecture:

```typescript
// All application data in metadata
const session: GameSession = {
  id: "123",
  name: "Alice",
  has_media: true,
  live: true,
  // ... base fields
  metadata: {
    color: "red",
    player: {...},
    resources: 5
  }
};
```

## Usage Examples

### Example 1: Creating a New Room Type

```typescript
// Define your metadata
interface MyAppSessionMetadata {
  customField: string;
}

interface MyAppRoomMetadata {
  appState: any;
}

// Use the base types
type MySession = Session<MyAppSessionMetadata>;
type MyRoom = Room<MyAppRoomMetadata>;

// Reuse infrastructure
import { getParticipants } from './room/helpers';

function listUsers(room: MyRoom) {
  return getParticipants(room.sessions);
}
```

### Example 2: Extending Participant Data

```typescript
// Start with base participants
const participants = getParticipants(room.sessions);

// Add app-specific fields
const extendedParticipants = participants.map(p => {
  const session = room.sessions[p.session_id];
  return {
    ...p,
    // Add your custom fields
    customField: session.metadata?.customField,
  };
});
```

### Example 3: MediaControl Integration

```typescript
// MediaControl is fully reusable
import { MediaAgent, MediaControl } from './MediaControl';

function MyApp() {
  const [peers, setPeers] = useState<Record<string, Peer>>({});

  return (
    <>
      <MediaAgent
        session={session}      // Base session data
        socketUrl={socketUrl}
        peers={peers}
        setPeers={setPeers}
      />

      {participants.map(p => (
        <MediaControl
          peer={peers[p.session_id]}
          isSelf={p.session_id === session.id}
        />
      ))}
    </>
  );
}
```

## Benefits

### 1. Reusability
- MediaControl can be used in any WebRTC application
- Room management works for any multi-user application
- WebSocket handling is application-agnostic

### 2. Maintainability
- Clear boundaries between infrastructure and application
- Easy to test each layer independently
- Simpler upgrade paths

### 3. Type Safety
- Generic types ensure type safety across layers
- TypeScript enforces proper metadata usage
- IntelliSense works correctly

### 4. Flexibility
- Easy to add new applications using same infrastructure
- Can run multiple different applications on same server
- Minimal code changes to adapt to new use cases

## File Organization

```
server/
├── routes/
│   ├── room/                    # Reusable infrastructure
│   │   ├── types.ts            # Base types (BaseSession, BaseRoom, etc.)
│   │   └── helpers.ts          # Reusable functions
│   │
│   └── games/                   # Game-specific code
│       ├── types.ts            # Game domain types (Player, Turn, etc.)
│       ├── gameMetadata.ts     # Metadata type definitions
│       ├── gameAdapter.ts      # Backward compatibility
│       └── helpers.ts          # Game-specific logic

client/
└── src/
    ├── MediaControl.tsx         # Reusable WebRTC component
    └── PlayerList.tsx          # Uses MediaControl + game metadata
```

## Best Practices

### DO:
✅ Keep infrastructure layer generic and reusable
✅ Put all application logic in metadata
✅ Use type parameters for extensibility
✅ Document what's reusable vs. application-specific
✅ Test infrastructure independently

### DON'T:
❌ Mix application logic into infrastructure types
❌ Hard-code application fields in base types
❌ Duplicate infrastructure code per application
❌ Bypass metadata layer in new code
❌ Create circular dependencies between layers

## Next Steps

1. **Immediate**: Use adapter layer for backward compatibility
2. **Short-term**: Migrate high-traffic functions to use metadata
3. **Long-term**: Remove adapters, use pure metadata architecture
4. **Future**: Extract infrastructure into separate npm package

## See Also

- [MEDIACONTROL_API.md](./MEDIACONTROL_API.md) - WebRTC signaling protocol
- [server/routes/room/types.ts](./server/routes/room/types.ts) - Infrastructure types
- [server/routes/games/gameMetadata.ts](./server/routes/games/gameMetadata.ts) - Game metadata types
- [server/routes/games/gameAdapter.ts](./server/routes/games/gameAdapter.ts) - Compatibility adapter
