feat: multi-game adapter revamp, council protocol merge, and frontend design doc

- Revamp architecture for modular game server support (Arma 3 first, extensible)
- Merge ConfigSchema into ConfigGenerator per council decision (8→7 protocols)
- Add has_capability() method to GameAdapter protocol for explicit capability probing
- Add FRONTEND.md: production-grade dark neumorphism design with amber/orange palette
- Update all docs (ARCHITECTURE, MODULES, DATABASE, API, IMPLEMENTATION_PLAN, THREADING)
  to reflect protocol merge and multi-game adapter patterns
This commit is contained in:
Tran G. (Revernomad) Khoa
2026-04-16 17:05:04 +07:00
parent 2c72e45b5f
commit 624d7594e2
7 changed files with 3723 additions and 1466 deletions

355
API.md
View File

@@ -6,7 +6,7 @@ http://localhost:8000/api
```
## Authentication
- All endpoints except `POST /auth/login` require: `Authorization: Bearer <JWT>`
- All endpoints except `POST /auth/login` and `GET /system/health` require: `Authorization: Bearer <JWT>`
- WebSocket: pass token as query param: `ws://localhost:8000/ws/{server_id}?token=<JWT>`
- JWT payload: `{ "sub": "user_id", "username": "string", "role": "admin|viewer", "exp": timestamp }`
@@ -39,11 +39,26 @@ Error response:
| 400 | Validation error |
| 401 | Unauthenticated |
| 403 | Forbidden (insufficient role) |
| 404 | Not found |
| 404 | Not found (or capability not supported by adapter) |
| 409 | Conflict (already running, duplicate) |
| 422 | Unprocessable (Pydantic validation) |
| 500 | Internal server error |
## Capability-Based Routing
Some endpoints depend on the server's game adapter supporting a specific capability. If the adapter does not support the capability, the endpoint returns **404** with a clear message:
```json
{
"success": false,
"data": null,
"error": {
"code": "CAPABILITY_NOT_SUPPORTED",
"message": "Missions not supported for game type 'rust'"
}
}
```
---
## Auth Endpoints
@@ -101,12 +116,88 @@ Delete user. Admin only.
---
## Game Type Discovery Endpoints
### GET /games
List all registered game types and their capabilities.
**Response 200:**
```json
{
"success": true,
"data": [
{
"game_type": "arma3",
"display_name": "Arma 3",
"version": "1.0.0",
"capabilities": ["config_generator", "process_config",
"log_parser", "remote_admin", "mission_manager", "mod_manager", "ban_manager"]
}
]
}
```
### GET /games/{game_type}
Get details for a specific game type.
**Response 200:**
```json
{
"success": true,
"data": {
"game_type": "arma3",
"display_name": "Arma 3",
"version": "1.0.0",
"capabilities": ["config_generator", "process_config",
"log_parser", "remote_admin", "mission_manager", "mod_manager", "ban_manager"],
"config_sections": ["server", "basic", "profile", "launch", "rcon"],
"allowed_executables": ["arma3server_x64.exe", "arma3server.exe"]
}
}
```
### GET /games/{game_type}/config-schema
Returns JSON Schema for each config section defined by the adapter. Used by frontend to build dynamic config forms.
**Response 200:**
```json
{
"success": true,
"data": {
"server": { /* JSON Schema for server.cfg params */ },
"basic": { /* JSON Schema for basic.cfg params */ },
"profile": { /* JSON Schema for Arma3Profile params */ },
"launch": { /* JSON Schema for launch params */ },
"rcon": { /* JSON Schema for RCon params */ }
}
}
```
### GET /games/{game_type}/defaults
Default config values for new server creation.
**Response 200:**
```json
{
"success": true,
"data": {
"server": { "hostname": "My Arma 3 Server", "max_players": 40, ... },
"basic": { "min_bandwidth": 800000, ... },
"profile": { "reduced_damage": 0, ... },
"launch": { "world": "empty", "limit_fps": 50, ... },
"rcon": { "max_ping": 200, "enabled": 1 }
}
}
```
---
## Server Endpoints
### GET /servers
List all servers with current status. Supports pagination.
List all servers with current status. Supports filtering by game type.
**Query params:** `?limit=50&offset=0`
**Query params:** `?limit=50&offset=0&game_type=arma3`
**Response 200:**
```json
@@ -117,32 +208,30 @@ List all servers with current status. Supports pagination.
"id": 1,
"name": "Main Server",
"description": "Primary COOP server",
"game_type": "arma3",
"status": "running",
"pid": 12345,
"game_port": 2302,
"rcon_port": 2306,
"player_count": 15,
"max_players": 40,
"current_mission": "MyMission.Altis",
"uptime_seconds": 3600,
"cpu_percent": 34.2,
"ram_mb": 1850.5,
"started_at": "2026-04-16T10:00:00Z"
// current_mission: computed from RCon 'players' response or mission_rotation + server status
// uptime_seconds: computed as (now - started_at) in the service layer
}
]
}
```
### POST /servers
Create a new server. Admin only.
Create a new server. Admin only. `game_type` determines which adapter handles this server.
**Request:**
```json
{
"name": "Main Server",
"description": "Primary COOP server",
"game_type": "arma3",
"exe_path": "C:/Arma3Server/arma3server_x64.exe",
"game_port": 2302,
"rcon_port": 2306,
@@ -150,7 +239,9 @@ Create a new server. Admin only.
"max_restarts": 3
}
```
**Note:** `password_admin` is auto-generated if not provided in the request. The generated value is returned in the response (shown once — not stored in plaintext API responses after creation). `rcon_password` is also auto-generated if not provided.
The adapter provides default config values for all sections. Auto-generated credentials (e.g., `password_admin`, `rcon_password` for Arma 3) are returned in the response and not stored in plaintext.
**Response 201:** Returns full server object including auto-generated credentials.
### GET /servers/{server_id}
@@ -163,6 +254,7 @@ Get server detail with full status.
"data": {
"id": 1,
"name": "Main Server",
"game_type": "arma3",
"status": "running",
"pid": 12345,
"game_port": 2302,
@@ -174,8 +266,7 @@ Get server detail with full status.
"cpu_percent": 34.2,
"ram_mb": 1850.5,
"started_at": "2026-04-16T10:00:00Z",
"uptime_seconds": 3600,
"current_mission": "MyMission.Altis"
"uptime_seconds": 3600
}
}
```
@@ -187,7 +278,7 @@ Update server metadata (name, description, exe_path, ports). Admin only.
Delete server (must be stopped first). Admin only. Removes DB rows and `servers/{id}/` directory.
### POST /servers/{server_id}/start
Start the server. Admin only.
Start the server. Admin only. Core resolves the adapter and delegates config generation + launch arg building.
**Response 200:**
```json
@@ -196,7 +287,7 @@ Start the server. Admin only.
**Response 409:** Server already running.
### POST /servers/{server_id}/stop
Graceful stop (send `#shutdown` via RCon, then kill after 30s). Admin only.
Graceful stop (sends shutdown via adapter's RemoteAdmin, then force-kill after 30s). Admin only.
**Request (optional):**
```json
@@ -213,28 +304,46 @@ Force-kill the process immediately. Admin only. Emergency use only.
## Server Config Endpoints
Config sections are defined by the server's game adapter. The core `game_configs` table stores them as JSON. The adapter's Pydantic models validate input.
### GET /servers/{server_id}/config
Get all config sections combined.
Get all config sections combined. Each section includes its `config_version` for optimistic locking.
**Response 200:**
```json
{
"success": true,
"data": {
"server": { /* server_configs row */ },
"basic": { /* basic_configs row */ },
"profile": { /* server_profiles row */ },
"launch": { /* launch_params row */ },
"rcon": { "rcon_password": "***", "max_ping": 200, "enabled": true }
"server": { /* section JSON */, "_meta": {"config_version": 3, "schema_version": "1.0"} },
"basic": { /* section JSON */, "_meta": {"config_version": 1, "schema_version": "1.0"} },
"profile": { /* section JSON */, "_meta": {"config_version": 2, "schema_version": "1.0"} },
"launch": { /* section JSON */, "_meta": {"config_version": 1, "schema_version": "1.0"} },
"rcon": { "rcon_password": "***", "max_ping": 200, "enabled": 1, "_meta": {"config_version": 1, "schema_version": "1.0"} }
}
}
```
### PUT /servers/{server_id}/config/server
Update server.cfg settings. Admin only.
### GET /servers/{server_id}/config/{section}
Get a single config section. Section names are defined by the adapter (e.g., `server`, `basic`, `profile`, `launch`, `rcon` for Arma 3).
**Request:** Partial object matching `server_configs` columns (snake_case). Any omitted field keeps current value.
**Response 200:**
```json
{
"success": true,
"data": {
"hostname": "My Server",
"max_players": 40,
"_meta": { "config_version": 3, "schema_version": "1.0" }
}
}
```
### PUT /servers/{server_id}/config/{section}
Update a config section. Admin only. Validated against the adapter's Pydantic model for that section. Sensitive fields (passwords) are encrypted before storage. **Optimistic locking** — client must send `config_version` from their last read.
**Request:** Partial object matching the adapter's section schema. Any omitted field keeps current value. Must include `config_version` for conflict detection.
**Arma 3 `server` section example:**
```json
{
"hostname": "Updated Server Name",
@@ -242,13 +351,27 @@ Update server.cfg settings. Admin only.
"battleye": 1,
"verify_signatures": 2,
"motd_lines": ["Welcome!", "Have fun"],
"motd_interval": 5.0
"motd_interval": 5.0,
"config_version": 3
}
```
### PUT /servers/{server_id}/config/basic
Update basic.cfg (bandwidth) settings. Admin only.
**Response 409 (Conflict):** Another admin updated this section since you read it.
```json
{
"success": false,
"data": {
"current_config": { /* latest values */ },
"current_version": 5
},
"error": {
"code": "CONFIG_VERSION_CONFLICT",
"message": "Config section 'server' was modified by another user. Re-read and merge your changes."
}
}
```
**Arma 3 `basic` section example:**
```json
{
"max_bandwidth": 50000000,
@@ -256,9 +379,7 @@ Update basic.cfg (bandwidth) settings. Admin only.
}
```
### PUT /servers/{server_id}/config/profile
Update difficulty profile. Admin only.
**Arma 3 `profile` section example:**
```json
{
"third_person_view": 0,
@@ -269,28 +390,17 @@ Update difficulty profile. Admin only.
}
```
### PUT /servers/{server_id}/config/launch
Update launch parameters. Admin only.
**Arma 3 `launch` section example:**
```json
{
"world": "empty",
"limit_fps": 50,
"auto_init": false,
"load_mission_to_memory": true,
"bandwidth_alg": 2,
"enable_ht": true,
"huge_pages": false
"load_mission_to_memory": true
}
```
### PUT /servers/{server_id}/config/rcon
Update BattlEye RCon settings. Admin only.
Regenerates `battleye/beserver.cfg` immediately. **Note:** BattlEye reads beserver.cfg only at server startup — RCon config changes require a server restart to take effect. The updated config file is ready for the next start.
**Note on `rcon_port`:** This field is stored in the `servers` table (not `rcon_configs`).
The service layer updates both tables as needed. Include only fields you want to change.
**Arma 3 `rcon` section example:**
```json
{
"rcon_password": "newpassword",
@@ -300,20 +410,36 @@ The service layer updates both tables as needed. Include only fields you want to
}
```
### GET /servers/{server_id}/config/preview
Returns rendered `server.cfg` as plain text string (for preview in UI). **Admin only** — contains plaintext credentials.
**Note:** `rcon_port` is stored in the `servers` table, not in the config JSON. The service layer updates both tables as needed.
**Response 200:** `Content-Type: text/plain`
### GET /servers/{server_id}/config/preview
Returns rendered config for preview in UI. **Admin only** — may contain plaintext credentials. Returns a dict of `{label: rendered_content}` — labels are filenames for file-based configs, variable names for env-var configs, or argument names for CLI configs.
**Response 200:**
```json
{
"success": true,
"data": {
"server.cfg": "// Generated server.cfg\nhostname = \"My Server\";\n...",
"basic.cfg": "// Generated basic.cfg\n...",
"server.Arma3Profile": "// Generated profile\n..."
}
}
```
(Non-file games would return e.g. `{"SERVER_NAME": "My Server", "MAX_PLAYERS": "40"}` for env-var configs.)
### GET /servers/{server_id}/config/download/{filename}
Download generated config file. Filename must be one of: `server.cfg` | `basic.cfg` | `server.Arma3Profile` (whitelist-validated, no path traversal). **Admin only** — config files contain plaintext passwords.
Download generated config file. Filename must be in adapter's allowlist (whitelist-validated, no path traversal). **Admin only**.
---
## Mission Endpoints
## Mission Endpoints (Capability: `mission_manager`)
Returns **404** if adapter does not support `mission_manager`.
### GET /servers/{server_id}/missions
List all mission PBOs for a server.
List all mission/scenario files for a server.
**Response 200:**
```json
@@ -333,10 +459,10 @@ List all mission PBOs for a server.
```
### POST /servers/{server_id}/missions/upload
Upload a mission PBO. Admin only. `multipart/form-data`.
Upload a mission/scenario file. Admin only. `multipart/form-data`. File extension validated by adapter's `MissionManager.file_extension`.
**Form fields:**
- `file`: the `.pbo` file (filename is sanitized with `os.path.basename()` to prevent path traversal; only `.pbo` extension allowed)
- `file`: the mission file (filename sanitized; only adapter-allowed extensions accepted)
**Response 201:**
```json
@@ -353,10 +479,10 @@ Upload a mission PBO. Admin only. `multipart/form-data`.
```
### DELETE /servers/{server_id}/missions/{mission_id}
Delete a mission PBO (removes file from disk). Admin only.
Delete a mission file (removes file from disk). Admin only.
### GET /servers/{server_id}/missions/rotation
Get current mission rotation (ordered list).
Get current mission/scenario rotation (ordered list).
**Response 200:**
```json
@@ -368,7 +494,7 @@ Get current mission rotation (ordered list).
"sort_order": 0,
"mission": { "id": 1, "mission_name": "MyMission.Altis" },
"difficulty": "Regular",
"params": { "RespawnDelay": 15 }
"params_json": { "RespawnDelay": 15 }
}
]
}
@@ -388,16 +514,21 @@ Replace the entire mission rotation. Admin only.
---
## Mod Endpoints
## Mod Endpoints (Capability: `mod_manager`)
Returns **404** if adapter does not support `mod_manager`.
### GET /mods
List all registered mods (global list).
List all registered mods. Optionally filter by game type.
**Query params:** `?game_type=arma3`
### POST /mods
Register a mod folder. Admin only.
```json
{
"game_type": "arma3",
"name": "@CBA_A3",
"folder_path": "C:/Arma3Server/@CBA_A3",
"workshop_id": "450814997",
@@ -452,26 +583,26 @@ Get currently connected players.
"success": true,
"data": [
{
"player_num": 1,
"slot_id": "1",
"name": "PlayerOne",
"guid": "abc123...",
"ping": 45,
"verified": true,
"game_data": { "verified": true, "steam_uid": "76561198..." },
"joined_at": "2026-04-16T10:15:00Z"
}
]
}
```
### POST /servers/{server_id}/players/{player_num}/kick
Kick a player via RCon. Admin only.
### POST /servers/{server_id}/players/{slot_id}/kick
Kick a player. Admin only. Requires adapter `remote_admin` capability.
```json
{ "reason": "AFK" }
```
### POST /servers/{server_id}/players/{player_num}/ban
Ban a player via RCon. Admin only.
### POST /servers/{server_id}/players/{slot_id}/ban
Ban a player. Admin only. Requires adapter `remote_admin` capability.
```json
{
@@ -495,12 +626,11 @@ List all bans for a server.
**Query params:** `?active_only=true&limit=50&offset=0`
### POST /servers/{server_id}/bans
Add ban manually. Admin only.
Add ban manually. Admin only. If adapter has `ban_manager`, also syncs to the game's ban file.
```json
{
"guid": "abc123...",
"steam_uid": "76561198...",
"name": "PlayerName",
"reason": "Cheating",
"duration_minutes": 0
@@ -508,7 +638,37 @@ Add ban manually. Admin only.
```
### DELETE /servers/{server_id}/bans/{ban_id}
Remove a ban. Admin only.
Remove a ban. Admin only. If adapter has `ban_manager`, also removes from the game's ban file.
---
## Remote Admin Endpoints (Capability: `remote_admin`)
Returns **404** if adapter does not support `remote_admin`.
### POST /servers/{server_id}/remote-admin/command
Send raw remote admin command. Admin only.
```json
{ "command": "#restart" }
```
**Arma 3 available commands:**
- `#restart` — Restart mission
- `#reassign` — Restart with roles unassigned
- `#missions` — Open mission selection
- `#lock` / `#unlock` — Lock/unlock server
- `#mission NAME.TERRAIN [difficulty]` — Load specific mission
- `#shutdown` — Shut down server
- `#monitor N` — Toggle performance monitoring
- `say -1 MESSAGE` — Message all players
### POST /servers/{server_id}/remote-admin/say
Broadcast a message to all players. Admin only.
```json
{ "message": "Server restarting in 5 minutes!" }
```
---
@@ -566,34 +726,6 @@ Get time-series metrics.
---
## RCon Endpoints
### POST /servers/{server_id}/rcon/command
Send raw RCon/admin command. Admin only.
```json
{ "command": "#restart" }
```
**Available commands:**
- `#restart` — Restart mission
- `#reassign` — Restart with roles unassigned
- `#missions` — Open mission selection
- `#lock` / `#unlock` — Lock/unlock server
- `#mission NAME.TERRAIN [difficulty]` — Load specific mission
- `#shutdown` — Shut down server
- `#monitor N` — Toggle performance monitoring
- `say -1 MESSAGE` — Message all players
### POST /servers/{server_id}/rcon/say
Broadcast a message to all players. Admin only.
```json
{ "message": "Server restarting in 5 minutes!" }
```
---
## Event Log Endpoints
### GET /servers/{server_id}/events
@@ -615,6 +747,7 @@ Overall system status. **Requires authentication** (admin or viewer).
"version": "1.0.0",
"running_servers": 2,
"total_servers": 3,
"supported_games": ["arma3"],
"uptime_seconds": 86400
}
}
@@ -643,12 +776,9 @@ Use `server_id = "all"` to subscribe to events from all servers.
**Channel subscription**: The `ConnectionManager` tracks per-connection channel subscriptions. Only messages matching subscribed channels are delivered. Default subscriptions on connect: `["status"]`.
**Channel names match message types exactly:** `status`, `log`, `players`, `metrics`, `event`. Subscribe with channel names matching the `type` field in server→client messages.
### Server → Client Messages
#### Status Update
Sent when server status changes (starting → running → stopped, etc.)
```json
{
"type": "status",
@@ -662,7 +792,6 @@ Sent when server status changes (starting → running → stopped, etc.)
```
#### Log Line
Sent for each new RPT log line.
```json
{
"type": "log",
@@ -676,14 +805,13 @@ Sent for each new RPT log line.
```
#### Player List Update
Sent after each RCon poll (every 10s).
```json
{
"type": "players",
"server_id": 1,
"data": {
"players": [
{ "player_num": 1, "name": "PlayerOne", "ping": 45, "verified": true }
{ "slot_id": "1", "name": "PlayerOne", "ping": 45 }
],
"count": 1
}
@@ -691,7 +819,6 @@ Sent after each RCon poll (every 10s).
```
#### Metrics Update
Sent every 5 seconds.
```json
{
"type": "metrics",
@@ -706,7 +833,6 @@ Sent every 5 seconds.
```
#### Server Event
Sent for significant events (crash, restart, etc.)
```json
{
"type": "event",
@@ -726,6 +852,19 @@ Sent for significant events (crash, restart, etc.)
---
## Adapter-Specific Routes
Adapters may register additional routes under `/api/servers/{server_id}/game/{game_type}/...` for features that have no generic counterpart.
**Arma 3 example:**
```
POST /api/servers/{server_id}/game/arma3/battleye/reload
```
These routes are registered at app startup by iterating over all registered adapters.
---
## Rate Limiting
- `POST /auth/login`: 5 attempts per minute per IP. Exceeded returns `429 Too Many Requests`.
@@ -741,15 +880,19 @@ Sent for significant events (crash, restart, etc.)
| `UNAUTHORIZED` | Missing or invalid token |
| `FORBIDDEN` | Role insufficient |
| `NOT_FOUND` | Resource not found |
| `CAPABILITY_NOT_SUPPORTED` | Adapter lacks required capability for this endpoint |
| `SERVER_ALREADY_RUNNING` | Start called on running server |
| `SERVER_NOT_RUNNING` | Stop/command on stopped server |
| `RCON_UNAVAILABLE` | RCon connection failed |
| `INVALID_CONFIG` | Config validation failed |
| `EXE_NOT_FOUND` | arma3server.exe not at configured path |
| `REMOTE_ADMIN_UNAVAILABLE` | Remote admin connection failed |
| `INVALID_CONFIG` | Config validation failed (adapter-specific) |
| `CONFIG_WRITE_ERROR` | Config file write failed (disk, permissions) |
| `CONFIG_VERSION_CONFLICT` | Optimistic locking conflict on config update |
| `EXE_NOT_ALLOWED` | Executable not in adapter's allowlist |
| `PORT_IN_USE` | Game port already occupied |
| `UPLOAD_FAILED` | Mission file upload error |
| `UPLOAD_FAILED` | File upload error |
| `VALIDATION_ERROR` | Pydantic validation failure |
| `GAME_TYPE_NOT_FOUND` | No adapter registered for this game type |
| `INTERNAL_ERROR` | Unexpected server error |
| `MOD_IN_USE` | Cannot delete mod — enabled on one or more servers |
| `MISSION_IN_ROTATION` | Cannot delete mission — in active rotation |
| `RATE_LIMITED` | Too many requests |
| `RATE_LIMITED` | Too many requests |