Decides where chunk/object content lives at rest and who may write it, now that there are two server-side processes per world (the always-on FastAPI backend and the per-join, reaper-killed Godot dedicated server). Decisions: chunk data lives in the existing Postgres DB (not world.md's per-world SQLite file); the FastAPI backend is the sole writer and the editor saves to it over HTTP; the dedicated server pulls content read-only at startup over a loopback /api/internal route with a per-spawn token; full-chunk PUT with base_revision instead of JSON-Patch deltas; the client streams chunks over HTTP through the existing ChunkStreamManager.set_server_rpc() seam. Adds docs/architecture/world_persistence_impl.md and marks the superseded sections of world.md (SS4, SS5) and editor.md (the request_chunk_edit path). Design only -- no implementation code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2FESCaMXeJpC9gSctiEeD
17 KiB
Editor Architecture: Godot Editor Plugin vs. In-Game Editor
Status: Proposed Owner: Platform team Related: World chunk data format & persistence, server authority + client prediction, content moderation & age verification, avatar component system Note: the recommendation here stands, but edits do not ride the
request_chunk_editENet RPC (§4.2, §7.4). Perworld_persistence_impl.mdthe editor saves chunks over HTTP to the FastAPI backend, which is the sole writer; the validation/moderation boundary moves with it. Godot version: 4.6.2 stable
1. Overview
This document evaluates the two primary ways to build the world/level authoring tool for agp-game:
- (a) Godot editor plugin — extend the Godot editor itself with custom panels, dock widgets, and tools that operate on the project's scenes and resources.
- (b) In-game editor — a fully custom editor built as game UI (Godot
Controlnodes) that runs inside the running game, editing the game's own world data.
The goal is to pick the approach that best fits the platform's constraints:
a server-authoritative multiplayer game where the world is stored as
versioned chunks (see world.md), content is moderation-flagged (see
content_moderation.md), and the client is a shipped binary that players
run (not a dev tool).
This document is a decision record: it lays out the trade-offs, gives a clear recommendation, and defines the resulting architecture boundaries.
2. Context & Constraints
Before comparing the two approaches, we must fix the constraints that drive the decision. These come from the existing architecture docs.
2.1 The World Is Data, Not Scenes
world.md defines the world as chunk data (JSON for authoring, binary for
runtime/network), persisted in SQLite on the server. A chunk is the atomic unit
of load, save, and stream. The server is the only writer to the store;
clients submit deltas against a base_revision and the server validates and
applies them.
Implication: The editor's output is chunk payloads, not .tscn scene
files. Whatever editor we build must serialize to the chunk contract, not to
Godot scene resources.
2.2 Server-Authoritative & Moderation-Aware
- The server validates every edit (schema, bounds, moderation) before
persisting (
world.md§5.4). - Content carries content flags aggregated from referenced assets/scripts
(
content_moderation.md§3). Mature content is gated behind age verification. - The client receives a content manifest and never loads content not in it.
Implication: An editor that runs on a player's machine cannot be the authority on what gets published. Editing must go through the server's validation pipeline, and the editor must surface moderation flags.
2.3 The Client Is a Shipped Binary
The game is a multiplayer product. Players run a release build. The Godot editor is a development tool that players do not have.
Implication: If authoring is a first-class player feature (user-generated content), the editor must ship inside the game binary. If authoring is a developer/platform feature, it can live in the editor.
2.4 Two Distinct Audiences
The evaluation must separate two audiences, because they have opposite needs:
| Audience | Who | Where they run | What they build |
|---|---|---|---|
| Platform/dev | The team building the game | Godot editor (dev machines) | Base worlds, rooms, templates, test levels |
| Players (UGC) | End users | The shipped game | Their own rooms/worlds, published to the server |
The two approaches map cleanly onto these audiences, and the right answer is likely both — but with a clear primary recommendation.
3. Approach (a): Godot Editor Plugin
3.1 What It Is
A Godot editor plugin is a set of scripts (and optionally scenes) that extend
the editor via the EditorPlugin API. It can add:
- Dock panels (
add_control_to_dock) — e.g., a chunk/tile palette panel. - Toolbar buttons (
add_control_to_container). - Inspector plugins (
EditorInspectorPlugin) — custom property editors. - Import plugins (
EditorImportPlugin) — custom asset importers. - Scene/Node plugins (
EditorNode3DGizmoPlugin) — custom gizmos. - Menu entries (
add_tool_menu_item).
Plugins are enabled per-project via project.godot ([editor_plugins]).
3.2 Pros
- Full editor power for free: You get the scene tree, inspector, viewport navigation, undo/redo, gizmos, and asset pipeline — all battle-tested.
- Native Godot editing: Editing
.tscnscenes, materials, and nodes is exactly what the editor is built for. Great for authoring base content (rooms, templates, avatar base bodies). - Fast iteration for devs: The platform team can build and test levels directly in the tool they already use.
- Undo/redo, selection, snapping come from the editor's
EditorUndoRedoManager. - No runtime footprint: Editor-only code is stripped from export builds
(
@toolscripts and editor plugins are not shipped to players).
3.3 Cons
- Not available to players. Players don't run the Godot editor. Any player-facing UGC authoring cannot use this approach.
- Tied to the editor's UX. You inherit the editor's look and interaction model; deep customization is limited by what the editor API exposes.
- Editor API churn.
EditorPluginAPIs change between Godot versions; plugins are more brittle across upgrades than game code. - Scene-centric, not data-centric. The editor is optimized for scenes and
resources. Our world is chunk data (
world.md). We'd have to build a translation layer (scene ↔ chunk) and keep it in sync — a real source of drift. - Single-user, local. The editor edits local files. It does not natively talk to our server-authoritative chunk store or the moderation pipeline.
- Headless/CI friction. Validating plugin behavior in CI requires running the editor headless, which is heavier than testing game code.
3.4 Fit for Our Constraints
- Platform/dev authoring: Excellent. This is the natural home for building base worlds, rooms, and templates.
- Player UGC: Not applicable. Players cannot run the editor.
- Server-authoritative chunk store: Poor fit natively. The editor edits local scenes; we'd bolt on a chunk export/import and a server sync layer.
4. Approach (b): In-Game Editor
4.1 What It Is
A fully custom editor built as game UI — Godot Control nodes (panels,
buttons, toolbars) that run inside the running game. It edits the game's own
world data model (chunks) and submits edits through the normal server pipeline.
4.2 Pros
- Ships to players. The editor is part of the game binary, so player UGC authoring is possible — the core requirement for a user-generated-content platform.
- Data-native. The editor operates directly on the chunk model
(
world.md), so there is no scene↔chunk translation layer and no drift. - Server-authoritative by construction. Edits go through the same
request_chunk_editRPCs as gameplay, so validation, revisioning, and moderation apply uniformly. The editor cannot bypass the server. - Moderation-aware. Because edits flow through the server, content flags are
computed and enforced at the boundary (
content_moderation.md§4). The editor can surface flags and gate mature content. - One codebase, one UX. The editor uses the same UI toolkit and art style as the game; no separate editor theme to maintain.
- Testable in CI. The editor is ordinary game code; it can be exercised headlessly and unit-tested like any other system.
4.3 Cons
- We build everything. No free scene tree, inspector, gizmos, or undo/redo. We must implement selection, snapping, undo/redo, viewport navigation, and property editing ourselves.
- Runtime footprint. Editor UI ships in the binary (though it can be gated behind a feature flag / separate scene that is only instantiated on demand).
- More upfront work. A usable in-game editor is a large feature; it needs its own architecture (tool modes, gizmos, palette, inspector).
- Not ideal for deep asset authoring. Modeling a mesh or authoring a material is better done in the Godot editor / DCC tools. The in-game editor should focus on world composition (placing tiles and objects), not asset creation.
4.4 Fit for Our Constraints
- Platform/dev authoring: Workable but not ideal for deep asset work.
- Player UGC: The only viable approach. Players can only author through the shipped game.
- Server-authoritative chunk store: Excellent fit. The editor is a client that submits validated deltas.
5. Comparison Matrix
| Criterion | (a) Editor plugin | (b) In-game editor |
|---|---|---|
| Ships to players | ❌ No | ✅ Yes |
| Player UGC authoring | ❌ Impossible | ✅ Core capability |
| World data model fit | ⚠️ Scene-centric; needs translation layer | ✅ Chunk-native |
| Server-authoritative edits | ⚠️ Bolted on | ✅ Native (same RPCs) |
| Moderation enforcement | ⚠️ Manual | ✅ At the boundary |
| Free editor features (undo, gizmos, inspector) | ✅ Rich | ❌ Build ourselves |
| Runtime footprint | ✅ None (stripped from export) | ⚠️ Ships in binary (gated) |
| Dev iteration speed | ✅ Fast | ⚠️ Slower to build |
| Deep asset authoring (mesh/material) | ✅ Native | ❌ Out of scope |
| CI testability | ⚠️ Heavier (headless editor) | ✅ Ordinary game code |
| Editor API churn risk | ⚠️ Higher | ✅ None (game code) |
6. Recommendation
Primary recommendation: build the in-game editor (b) as the platform's authoring surface, and use a lightweight Godot editor plugin (a) only as an internal dev convenience.
6.1 Rationale
-
Player UGC is the deciding factor. The platform's value proposition is user-generated content (rooms, worlds, avatars). Players cannot run the Godot editor, so only an in-game editor can deliver player authoring. This is a hard requirement that the editor-plugin approach cannot meet.
-
The world is data, and the in-game editor is data-native.
world.mddefines chunks as the atomic unit. An in-game editor edits chunks directly and submits them through the server pipeline. An editor plugin would force a scene↔chunk translation layer that is a constant source of drift and bugs. -
Server authority and moderation fall out naturally. Because in-game edits use the same
request_chunk_editRPCs as gameplay, validation, revisioning, optimistic concurrency, and content-flag enforcement (content_moderation.md) apply uniformly. The editor cannot bypass the server — which is exactly what a moderation-aware UGC platform needs. -
The editor-plugin's strengths are not what we need. Its free features (scene tree, inspector, gizmos) are for editing Godot scenes and resources. Our authoring is world composition against a chunk contract, not scene editing. The plugin's main win — deep asset authoring — is better served by DCC tools and the Godot editor for the platform team, not by a plugin we must maintain.
6.2 The Role of the Editor Plugin (Dev Convenience Only)
We still recommend a small, optional editor plugin for the platform team to author base content (base rooms, templates, avatar base bodies, test levels) directly in the Godot editor. This is a dev-time convenience, not a product feature:
- It can export a scene/resource as a chunk (reusing the same encoder as the in-game editor) so dev-authored content flows into the same pipeline.
- It is not shipped to players and is not the authoring surface for UGC.
- It should be kept thin to minimize editor-API churn risk.
6.3 Scope Boundary
To keep the in-game editor tractable, define its scope clearly:
- In scope (world composition): placing tiles, placing static/dynamic objects, setting transforms, editing object properties, saving/loading chunks, submitting edits to the server, showing moderation flags.
- Out of scope (asset creation): modeling meshes, authoring materials,
rigging avatars. These are done in DCC tools / the Godot editor and imported
as catalog assets (see
avatar.md§7). The in-game editor references catalog assets by id; it does not create them.
7. Resulting Architecture
7.1 Shared Editor Core
Both the in-game editor and the (optional) dev plugin should share a single editor core that operates on the chunk model. This avoids two divergent implementations.
EditorCore (shared, data-native)
├── ChunkEditorModel # in-memory chunk being edited (tiles, objects, flags)
├── ChunkEncoder # JSON/binary encode/decode (world.md §3)
├── EditSession # base_revision tracking, delta generation
├── EditSubmitter # request_chunk_edit RPC wrapper + conflict handling
├── UndoRedoStack # command pattern over chunk mutations
├── SelectionManager # selected tiles/objects
├── ToolRegistry # brush, object-place, select, erase, etc.
└── ModerationView # surfaces content flags; gates mature content
7.2 In-Game Editor (Primary)
InGameEditor (Control-based UI, shipped in binary, gated by feature flag)
├── EditorViewport # custom 2D/3D viewport with camera navigation
├── Toolbar # tool mode switcher
├── PalettePanel # tile/object palette (from catalog)
├── InspectorPanel # property editor for selected object
├── ChunkBrowser # load/save chunks, list rooms
└── EditorCore # shared core (above)
- Instantiated only when the player enters "build mode" (a separate scene / feature flag), so the runtime footprint is negligible during normal play.
- Submits edits via
request_chunk_edit; handleschunk_edit_resultconflicts by re-fetching and re-applying (world.md§5.4).
7.3 Dev Editor Plugin (Optional, Thin)
DevChunkPlugin (EditorPlugin, dev-only, not shipped)
├── Dock panel: "Chunk Export"
├── Reads the current scene / selected nodes
├── Maps scene content → chunk model (via EditorCore.ChunkEncoder)
└── Exports a .chunk.json / submits to a dev server
Kept thin to minimize editor-API churn. Its only job is to let the platform team author base content in the editor and push it into the same chunk pipeline.
7.4 Data Flow
[In-game editor] --request_chunk_edit(delta, base_revision)--> [Server]
^ | validate
| | apply
+--chunk_edit_result(ok/new_revision | conflict)-----------| persist
v
SQLite (world.db)
[Dev plugin] --export chunk JSON--> [EditorCore.ChunkEncoder] --> [Server]
Both paths converge on the same server validation pipeline, so moderation and revisioning are uniform regardless of authoring surface.
8. Risks & Mitigations
| Risk | Mitigation |
|---|---|
| In-game editor is a large build | Scope it to world composition (§6.3); reuse EditorCore; ship behind a feature flag |
| Custom viewport/gizmos are hard | Start with a 2D tile editor (matches CHUNK_SIZE_Z=1 default); add 3D later |
| Undo/redo complexity | Use a command pattern over chunk mutations; test headlessly |
| Editor-plugin API churn | Keep the plugin thin; treat it as dev-only convenience, not a product surface |
| Drift between dev plugin and in-game editor | Both use the same EditorCore/ChunkEncoder |
| Players bypass moderation | Impossible by construction: edits go through server validation |
9. Open Questions / Future Work
- 3D editing: The default world is flat (
CHUNK_SIZE_Z=1). A 3D in-game editor (volumetric chunks) is future work (world.md§8). - Collaborative editing: Multiple players editing the same chunk concurrently — the optimistic-concurrency model already supports it, but the UX (live cursors, conflict resolution) is unspecified.
- Template library: A curated set of room templates players can start from.
- Publish/review flow: How a player's authored room enters the moderation
review queue (
content_moderation.md§6) and becomes publicly joinable.
10. Summary
| Concern | Decision |
|---|---|
| Primary authoring surface | In-game editor (ships to players, enables UGC) |
| Dev authoring convenience | Thin, optional editor plugin (not shipped) |
| World data model | Both operate on the chunk model via a shared EditorCore |
| Server authority | Edits go through request_chunk_edit; server validates |
| Moderation | Enforced at the server boundary for both paths |
| In-game editor scope | World composition only; asset creation is out of scope |
| Runtime footprint | Editor gated behind a feature flag / separate scene |