Worldshaper/docs/runtime-api-proposal.md

312 lines
7.3 KiB
Markdown

# Worldshaper Runtime API Proposal
## Goal
Provide a stable HTTP API that a game engine can call to load Worldshaper-authored data without depending directly on editor-only JSON structure.
This is different from the current editor API.
The editor API is optimized for:
- editing
- saving
- validation
- editor UI state
- internal content storage
The runtime API should be optimized for:
- loading the world into a game engine
- streaming chunks as the player moves
- fetching only the catalog data the engine actually needs
- staying stable even if the editor changes internally
## Current State
Worldshaper already exposes useful HTTP routes in [server.js](/D:/Programming/Worldshaper/server.js:2004), including:
- `GET /api/world-default`
- `GET /api/world/:worldId`
- `GET /api/world/:worldId/chunks`
- `GET /api/content/tiles`
- `GET /api/content/sprites`
- `GET /api/content/dialogues`
- `GET /api/content/npc_templates`
- `GET /api/images/:filename`
That means the project already has the foundations for a runtime API.
## Why Not Use The Editor API Directly
Some current payloads include editor concerns that a game engine should not be forced to understand.
Examples:
- world definitions contain `editorUi`
- images are stored in editor-authored form in `images.json`
- some content files may not always exist locally
- route naming and response shape are editor-first, not engine-first
Using those routes directly would tightly couple the engine to the editor's internal storage format.
## Recommendation
Add a separate runtime-facing API namespace:
- `GET /api/runtime/worlds`
- `GET /api/runtime/worlds/:worldId`
- `GET /api/runtime/worlds/:worldId/chunks`
- `GET /api/runtime/catalog/tiles`
- `GET /api/runtime/catalog/sprites`
- `GET /api/runtime/catalog/npc-templates`
- `GET /api/runtime/catalog/dialogues`
- `GET /api/runtime/bundle/:worldId`
This lets Worldshaper keep evolving internally while the engine targets a cleaner contract.
## Recommended V1
V1 should be small and practical.
### Startup Flow
The engine should be able to:
1. fetch the default or selected world
2. fetch shared runtime catalogs once
3. fetch chunks on demand as the player moves
### Best First Endpoints
#### `GET /api/runtime/worlds`
Returns a lightweight world list.
Example:
```json
{
"worlds": [
{
"id": "overworld",
"name": "Overworld Mock"
}
]
}
```
#### `GET /api/runtime/worlds/:worldId`
Returns runtime-safe world metadata only.
Example:
```json
{
"world": {
"id": "overworld",
"name": "Overworld Mock",
"chunkWidth": 32,
"chunkHeight": 32,
"tileSize": 32,
"backgroundColor": "#060A14",
"defaultBackgroundTileId": "tile_5b6206b849",
"spawn": { "x": 80, "y": 80 }
}
}
```
Do not include:
- `editorUi`
- editor layout metadata
- editor-only presentation settings unless the engine truly uses them
#### `GET /api/runtime/worlds/:worldId/chunks?chunkX=0&chunkY=0&radius=1`
Returns chunk payloads around a center point.
Example:
```json
{
"worldId": "overworld",
"center": { "chunkX": 0, "chunkY": 0 },
"radius": 1,
"chunks": [
{
"chunkX": 0,
"chunkY": 0,
"width": 32,
"height": 32,
"backgroundTileId": "tile_5b6206b849",
"roomLayers": [
{ "layer": 0, "rows": ["................................"] },
{ "layer": 1, "rows": [" "] }
],
"heightLayers": [],
"instances": []
}
]
}
```
This is the most important runtime route.
#### `GET /api/runtime/catalog/tiles`
Returns tile definitions needed for world rendering.
Example:
```json
{
"tiles": [
{
"id": "tile_5b6206b849",
"symbol": ".",
"name": "Grass",
"width": 16,
"height": 16,
"pixelScale": 2,
"rows": ["................"]
}
]
}
```
#### `GET /api/runtime/catalog/sprites`
Returns sprite definitions needed for entity rendering.
Example:
```json
{
"sprites": [
{
"id": "npc_human_style_01",
"name": "Human Style 01 - Wide Hat Coat",
"width": 16,
"height": 16,
"pixelScale": 2,
"rows": ["................"]
}
]
}
```
#### `GET /api/runtime/catalog/npc-templates`
Returns entity templates the engine can use to resolve placed instances.
Example:
```json
{
"npcTemplates": [
{
"id": "npc_gatekeeper_bubbles",
"name": "Bubbles",
"faction": "dangerous_gatekeeper",
"spriteId": "npc_human_style_13",
"defaultDialogueId": "dlg_npc_gatekeeper_bubbles"
}
]
}
```
#### `GET /api/runtime/catalog/dialogues`
Returns runtime dialogue definitions.
This may be enough for V1 if the engine can interpret the current dialogue node structure.
If not, Worldshaper should add a runtime dialogue normalization step later.
#### `GET /api/runtime/bundle/:worldId`
Optional convenience endpoint.
Returns:
- runtime world metadata
- tiles
- sprites
- npc templates
- factions
- dialogues
This is useful for prototypes and early engine work.
It may be too large for long-term world streaming, but it is a very good first integration endpoint.
## Suggested Runtime Data Rules
The runtime API should follow these rules:
- never expose editor layout/state unless the engine truly needs it
- prefer explicit IDs over editor-specific structures
- keep references stable: `tileId`, `spriteId`, `dialogueId`, `templateId`
- return only what the engine needs to simulate and render
- keep route names descriptive and runtime-oriented
## What The Engine Likely Needs
Minimum likely needs:
- world metadata
- chunk geometry and layer rows
- tile catalog
- sprite catalog
- instance template catalog
- placed instances per chunk
- dialogue definitions
- factions or other lookup catalogs used by templates
Maybe later:
- items
- abilities
- monsters
- loot tables
- triggers
- transitions
- quest hooks
## Open Questions For The Engine Developer
These should be answered before implementation:
1. Does the engine want raw JSON data from Worldshaper, or a normalized runtime contract?
2. Should the engine load the full world at startup, or stream chunks around the player?
3. Does the engine want graphics as row-based pixel data, or should Worldshaper also expose rendered asset URLs or atlas data?
4. Will the engine consume dialogues in the current node format, or does it need a simpler compiled dialogue format?
5. Does the engine need polling or version info so it can hot-reload changed data?
## Recommended Answer
Unless the engine developer strongly objects, the best answer is:
- normalized runtime contract
- chunk streaming
- row-based tile/sprite data first
- existing dialogue format first
- optional bundle endpoint for fast early integration
## Practical Next Step
Before implementation, send your friend this exact question:
> I can expose Worldshaper as a runtime API. Do you want a clean engine-facing contract with world metadata, chunk streaming, tiles, sprites, NPC templates, and dialogues, or do you want direct access to the raw editor JSON?
If he says "clean engine-facing contract", V1 should target:
- `GET /api/runtime/worlds/:worldId`
- `GET /api/runtime/worlds/:worldId/chunks`
- `GET /api/runtime/catalog/tiles`
- `GET /api/runtime/catalog/sprites`
- `GET /api/runtime/catalog/npc-templates`
- `GET /api/runtime/catalog/dialogues`
- optional `GET /api/runtime/bundle/:worldId`