Summary
Expose a read-only REST API so external client applications (e.g. a world viewer or mapping tool) can query live game state without modifying it.
Background
Roam has no external API surface today. All game state lives in-memory (rooms, entities, player) and is only accessible via the pygame UI or JSON save files on disk. A lightweight read-only HTTP server embedded in the game process would allow external tools to consume live world data without coupling them to Roam's internals. Note: if PR #344 (structured logging via structlog) is merged before this, new log calls in this feature should use getLogger from src/gameLogging/logger.py.
Requirements
Server
- Embed a lightweight HTTP server in the Roam process using Python's stdlib
http.server module (no additional dependencies)
- Run the server on a background
ThreadPoolExecutor thread (consistent with the pattern used in MapImageUpdater and RoomPreloader) so it does not block the game loop
- Configure the port via
config.yml (e.g. restPort: 8080); if the port is already in use, log a warning and disable the REST server rather than crashing
- Add a
restEnabled boolean config option (default false) so the server is opt-in
- Register the REST server as a singleton in the DI container via
src/bootstrap.py and shut it down in WorldScreen.shutdown()
Endpoints
All endpoints return Content-Type: application/json and are read-only (GET only). No authentication is required for MVP.
| Endpoint |
Description |
GET /api/v1/world |
Current room coordinates, room type, tick count, and player location |
GET /api/v1/rooms |
List of all loaded rooms with coordinates, type, and entity counts |
GET /api/v1/rooms/{x}/{y} |
Full entity listing for a specific room by grid coordinates |
GET /api/v1/player |
Player energy, direction, inventory item count, and current room coordinates |
GET /api/v1/entities |
All entities across all loaded rooms with type, location, and room coordinates |
Serialization
- Responses should be plain Python dicts serialized via
json.dumps — no third-party serialization library
- Entity data should include at minimum:
entityClass, name, locationId, roomX, roomY
- All endpoints should return
404 with a JSON error body if the requested resource does not exist, and 503 if the game world is not yet initialized
Thread Safety
- All reads from
Map, Room, and Player must acquire Map._lock (already used by Map.getRoom() and Map.addRoom()) before reading shared state, consistent with the existing locking pattern
Acceptance Criteria
Summary
Expose a read-only REST API so external client applications (e.g. a world viewer or mapping tool) can query live game state without modifying it.
Background
Roam has no external API surface today. All game state lives in-memory (rooms, entities, player) and is only accessible via the pygame UI or JSON save files on disk. A lightweight read-only HTTP server embedded in the game process would allow external tools to consume live world data without coupling them to Roam's internals. Note: if PR #344 (structured logging via
structlog) is merged before this, new log calls in this feature should usegetLoggerfromsrc/gameLogging/logger.py.Requirements
Server
http.servermodule (no additional dependencies)ThreadPoolExecutorthread (consistent with the pattern used inMapImageUpdaterandRoomPreloader) so it does not block the game loopconfig.yml(e.g.restPort: 8080); if the port is already in use, log a warning and disable the REST server rather than crashingrestEnabledboolean config option (defaultfalse) so the server is opt-insrc/bootstrap.pyand shut it down inWorldScreen.shutdown()Endpoints
All endpoints return
Content-Type: application/jsonand are read-only (GETonly). No authentication is required for MVP.GET /api/v1/worldGET /api/v1/roomsGET /api/v1/rooms/{x}/{y}GET /api/v1/playerGET /api/v1/entitiesSerialization
json.dumps— no third-party serialization libraryentityClass,name,locationId,roomX,roomY404with a JSON error body if the requested resource does not exist, and503if the game world is not yet initializedThread Safety
Map,Room, andPlayermust acquireMap._lock(already used byMap.getRoom()andMap.addRoom()) before reading shared state, consistent with the existing locking patternAcceptance Criteria
restEnabled: trueinconfig.ymlstarts the HTTP server on the configured port when the world screen is activegameLoggingand disables the server without crashingWorldScreen.shutdown()is called