diff --git a/docs/planning/creator_studio.md b/docs/planning/creator_studio.md index 9468c40..bbedfc0 100644 --- a/docs/planning/creator_studio.md +++ b/docs/planning/creator_studio.md @@ -32,26 +32,26 @@ _As a creator, I can see how many plays and average session length my experience ### US-1: Asset Import Pipeline -#### T1.1: Backend — glTF Asset Model & Upload Endpoint +#### T1.1: Backend — glTF Asset Upload with Metadata Extraction - **Type:** implementation -- **Files:** `backend/app/models/asset.py` (add glTF fields), `backend/app/routers/assets.py` (new endpoint) -- **Scope:** Extend Asset model with glTF-specific metadata fields (mesh_count, vertex_count, has_animation, texture_refs) stored in `metadata_json`. Add upload endpoint that accepts `.glb`/`.gltf` files and stores them in the upload directory with metadata extraction (parse glTF JSON for mesh/texture counts). -- **Acceptance:** POST `/api/assets/{id}/upload` with glTF file returns asset with extracted metadata; GET lists glTF assets; file stored on disk. +- **Files:** `backend/app/routers/assets.py` (extend upload endpoint), `backend/app/models/asset.py` (add glTF-specific metadata fields) +- **Scope:** Extend the existing `upload_asset_file` endpoint in `assets.py` to detect `.glb`/`.gltf` files and extract glTF metadata (mesh_count, vertex_count, has_animation, texture_refs, bounding_box) from the embedded JSON. Store extracted metadata in `metadata_json`. Add a `has_gltf_metadata` computed property. The Asset model already has `asset_type = MODEL_3D` and `metadata_json` — this task adds the extraction logic. +- **Acceptance:** POST `/api/assets/{id}/upload` with a `.glb` file returns asset with `metadata_json` containing `mesh_count`, `vertex_count`, `has_animation`, `texture_refs`. GET lists include these fields. File stored on disk. Non-glTF files are unaffected. - **Dependencies:** None (builds on existing asset infrastructure) -#### T1.2: Backend — Asset Catalog API for Client Lookup +#### T1.2: Backend — Public Asset Catalog Endpoint - **Type:** implementation -- **Files:** `backend/app/routers/assets.py` (new endpoints), `backend/app/models/asset.py` (minor additions) -- **Scope:** Add `GET /api/assets/catalog` endpoint that returns all public glTF assets with their metadata (name, thumbnail_url, mesh_count, vertex_count, tags). Add filtering by tags and pagination. This is what the editor's asset palette queries. -- **Acceptance:** Catalog endpoint returns paginated list of public assets; filtering by tag works; pagination params respected. +- **Files:** `backend/app/routers/assets.py` (new endpoint) +- **Scope:** Add `GET /api/assets/catalog` endpoint that returns all public glTF assets with their metadata (name, thumbnail_url, mesh_count, vertex_count, tags). Add filtering by tags and pagination. This is what the editor's asset palette queries. Uses the existing `Asset.is_public` field. +- **Acceptance:** Catalog endpoint returns paginated list of public assets; filtering by tag works; pagination params respected; returns 401 without auth. - **Dependencies:** T1.1 #### T1.3: Godot — glTF Import Utility (Editor-Side) - **Type:** implementation - **Files:** `scripts/gltf_importer.gd` (new), `scripts/asset_catalog.gd` (new) -- **Scope:** Godot-side utility that connects to the catalog API, downloads glTF files, and converts them to Godot-compatible resources (`.res` files with embedded mesh data). Provides `import_gltf(url: String) -> Resource` function. Handles texture extraction and mesh merging. +- **Scope:** Godot-side utility that connects to the catalog API, downloads glTF files, and converts them to Godot-compatible resources (`.res` files with embedded mesh data). Provides `import_gltf(url: String) -> Resource` function. Handles texture extraction and mesh merging. Uses the ApiClient autoload (from client-server-wiring plan) for HTTP. - **Acceptance:** `import_gltf()` downloads a glTF from the catalog, converts mesh to `Mesh` resource, returns a ready-to-use Godot resource. Texture files stored in `user://assets/`. -- **Dependencies:** T1.2 +- **Dependencies:** T1.2, client-server-wiring ApiClient (task 1 from that plan) ### US-2: Script Editor with Sandboxed API @@ -60,15 +60,15 @@ _As a creator, I can see how many plays and average session length my experience #### T2.1: Backend — Script Model & CRUD Endpoints - **Type:** architecture - **Files:** `backend/app/models/script.py` (new), `backend/app/routers/scripts.py` (new), `backend/app/main.py` (register router), `backend/app/models/__init__.py` (export) -- **Scope:** Create `Script` model (id, name, world_id, owner_id, source_code, version, created_at, updated_at). Add full CRUD endpoints: `POST /api/scripts/` (create), `GET /api/scripts/` (list), `GET /api/scripts/{id}` (get), `PUT /api/scripts/{id}` (update), `DELETE /api/scripts/{id}` (delete). Scripts are owned by users and associated with a world. -- **Acceptance:** All CRUD endpoints work; scripts are owned by creator; list endpoint filters by owner/world; update increments version. +- **Scope:** Create `Script` model (id, name, world_id, owner_id, source_code TEXT, version INT, script_type ENUM[gdscript, dsl], dsl_payload JSON, created_at, updated_at). Add full CRUD endpoints: `POST /api/scripts/` (create), `GET /api/scripts/` (list), `GET /api/scripts/{id}` (get), `PUT /api/scripts/{id}` (update), `DELETE /api/scripts/{id}` (delete). Scripts are owned by users and associated with a world. `dsl_payload` stores the JSON/Resource data for the DSL approach; `source_code` stores raw GDScript if used. +- **Acceptance:** All CRUD endpoints work; scripts are owned by creator; list endpoint filters by owner/world; update increments version; DSL and gdscript types supported. - **Dependencies:** None (new model + router) #### T2.2: Godot — Script API Surface Definition - **Type:** architecture -- **Files:** `scripts/editor_script_api.gd` (new), `docs/architecture/scripting_sandbox.md` (update) -- **Scope:** Define the public API surface available to creator scripts. This is a `RefCounted` class that wraps the Godot engine's capabilities into a safe, documented interface. Includes: `WorldAPI` (get_tile, get_object, set_property), `PlayerAPI` (get_position, get_health), `AudioAPI` (play_sound, play_music), `NetworkAPI` (send_event, broadcast). Each method has documented parameters and return types. -- **Acceptance:** `EditorScriptAPI` class exposes all documented methods; methods are callable from user scripts; API surface is self-documenting (each method has `##` doc comments). +- **Files:** `scripts/editor_script_api.gd` (new) +- **Scope:** Define the public API surface available to creator scripts. This is a `RefCounted` class that wraps the Godot engine's capabilities into a safe, documented interface. Includes: `WorldAPI` (get_tile, get_object, set_property), `PlayerAPI` (get_position, get_health), `AudioAPI` (play_sound, play_music), `NetworkAPI` (send_event, broadcast). Each method has documented parameters and return types. Self-documenting via `##` doc comments for autocomplete. +- **Acceptance:** `EditorScriptAPI` class exposes all documented methods; methods are callable from user scripts; API surface is self-documenting. - **Dependencies:** None (standalone definition) #### T2.3: Godot — In-Game Script Editor UI @@ -83,13 +83,13 @@ _As a creator, I can see how many plays and average session length my experience #### T3.1: Backend — Experience Model & Status Transitions - **Type:** architecture - **Files:** `backend/app/models/experience.py` (new), `backend/app/routers/experiences.py` (new), `backend/app/main.py` (register router), `backend/app/models/__init__.py` (export) -- **Scope:** Create `Experience` model (id, world_id, title, description, status, published_at, review_notes, reviewer_id). Status enum: `DRAFT`, `SUBMITTED`, `IN_REVIEW`, `APPROVED`, `REJECTED`, `LIVE`. Add endpoints: `POST /api/experiences/` (create draft), `POST /api/experiences/{id}/submit` (submit for review), `POST /api/experiences/{id}/approve` (approve), `POST /api/experiences/{id}/reject` (reject), `GET /api/experiences/` (list with status filter). -- **Acceptance:** All CRUD + transition endpoints work; status transitions enforced (DRAFT→SUBMITTED→IN_REVIEW→APPROVED→LIVE); rejected can be resubmitted; list filters by status. +- **Scope:** Create `Experience` model (id, world_id FK→worlds, title, description, status ENUM[DRAFT,SUBMITTED,IN_REVIEW,APPROVED,REJECTED,LIVE], published_at, review_notes, reviewer_id FK→users). Status transitions enforced: DRAFT→SUBMITTED→IN_REVIEW→APPROVED→LIVE; REJECTED→DRAFT (resubmit). Add endpoints: `POST /api/experiences/` (create draft), `POST /api/experiences/{id}/submit` (submit for review), `POST /api/experiences/{id}/approve` (approve), `POST /api/experiences/{id}/reject` (reject), `GET /api/experiences/` (list with status filter). An experience is a **publishing wrapper** around a World — the world's visibility is set to PUBLIC when experience goes LIVE. +- **Acceptance:** All CRUD + transition endpoints work; status transitions enforced; rejected can be resubmitted; list filters by status; approve sets world visibility to PUBLIC; only world owner can submit; only moderators can approve/reject. - **Dependencies:** None (new model + router) #### T3.2: Backend — Review Queue & Moderation Integration - **Type:** implementation -- **Files:** `backend/app/routers/experiences.py` (add endpoints), `backend/app/models/experience.py` (add fields) +- **Files:** `backend/app/routers/experiences.py` (add endpoints) - **Scope:** Add review queue endpoint `GET /api/experiences/review-queue` (returns experiences in IN_REVIEW status). Add `POST /api/experiences/{id}/review` endpoint for moderators to add review notes and change status. Integrates with content moderation: when an experience is submitted, its world's content flags are computed and attached to the review. - **Acceptance:** Review queue returns IN_REVIEW experiences; review endpoint accepts notes and status change; content flags attached on submit; only moderators can review. - **Dependencies:** T3.1 @@ -110,10 +110,10 @@ _As a creator, I can see how many plays and average session length my experience ### US-4: Creator Analytics -#### T4.1: Backend — Session & Play Tracking Model +#### T4.1: Backend — Session & Play Tracking Models - **Type:** architecture - **Files:** `backend/app/models/analytics.py` (new), `backend/app/routers/analytics.py` (new), `backend/app/main.py` (register router), `backend/app/models/__init__.py` (export) -- **Scope:** Create `PlaySession` model for analytics (id, experience_id, player_id, started_at, ended_at, duration_seconds, chunk_loads, interactions_count). Create `PlayEvent` model for granular events (id, experience_id, player_id, event_type, payload, created_at). Add endpoints: `POST /api/analytics/session/start` (start session), `POST /api/analytics/session/end` (end session), `POST /api/analytics/event` (log event). +- **Scope:** Create `PlaySession` model (id, experience_id FK→experiences, player_id FK→users, started_at, ended_at, duration_seconds, chunk_loads INT, interactions_count INT). Create `PlayEvent` model (id, experience_id FK→experiences, player_id FK→users, event_type, payload JSON, created_at). Add endpoints: `POST /api/analytics/session/start` (start session), `POST /api/analytics/session/end` (end session), `POST /api/analytics/event` (log event). Note: `sessions` table is taken by auth tokens; this uses `play_sessions` table name. - **Acceptance:** Session start/end endpoints work; events logged; duration calculated correctly; endpoints accept valid experience/player IDs. - **Dependencies:** None (new models + routers) @@ -144,9 +144,9 @@ _As a creator, I can see how many plays and average session length my experience New tables needed (in order): 1. `scripts` — script storage -2. `experiences` — publishing state -3. `analytics_sessions` — session tracking (note: `sessions` table already exists for auth tokens) -4. `analytics_events` — granular events +2. `experiences` — publishing state (wraps worlds) +3. `play_sessions` — session tracking (note: `sessions` table already exists for auth tokens) +4. `play_events` — granular events Migration file: `backend/alembic/versions/0003_creator_studio.py` @@ -156,25 +156,26 @@ Migration file: `backend/alembic/versions/0003_creator_studio.py` ``` T1.1 (glTF upload) ──→ T1.2 (catalog API) ──→ T1.3 (Godot importer) -T2.1 (script model) ──→ T2.3 (API surface) ──→ T2.4 (editor UI) +T2.1 (script model) ──→ T2.3 (editor UI) +T2.2 (API surface) ──→ T2.3 (editor UI) T3.1 (experience model) ──→ T3.2 (review queue) ──→ T3.3 (publish UI) T3.1 ──→ T3.4 (discovery list) T4.1 (tracking model) ──→ T4.2 (dashboard endpoint) ──→ T4.4 (analytics UI) -T4.1 ──→ T4.3 (game tracker) ──→ T4.4 ──┘ +T4.1 ──→ T4.3 (game tracker) ──→ T4.4 (analytics UI) ``` ## Implementation Order Recommendation -1. **T1.1** — Foundation: asset upload with glTF support +1. **T1.1** — Foundation: asset upload with glTF metadata extraction 2. **T2.1** — Foundation: script model + CRUD 3. **T3.1** — Foundation: experience model + status transitions 4. **T4.1** — Foundation: analytics tracking model 5. **T1.2** — Asset catalog for editor -6. **T2.3** — Script API surface definition +6. **T2.2** — Script API surface definition 7. **T3.2** — Review queue integration 8. **T4.2** — Analytics dashboard endpoint -9. **T1.3** — Godot glTF importer -10. **T2.4** — Script editor UI +9. **T1.3** — Godot glTF importer (depends on ApiClient from client-server-wiring) +10. **T2.3** — Script editor UI 11. **T3.3** — Publishing UI panel 12. **T3.4** — Experience discovery list 13. **T4.3** — Analytics tracker in game