Checkpoint 2: integrate native Editor, MCP, gameplay builds and standalone export

This commit is contained in:
Emil
2026-09-18 03:40:15 +03:00
parent 5c6b24d34d
commit 999686a896
125 changed files with 16086 additions and 1714 deletions
+58
View File
@@ -0,0 +1,58 @@
# Native Editor extensions
Editor extensions are startup-loaded `.so`/`.dll` modules. They are trusted native
code inside the Editor process. Gameplay remains statically linked into the separate
Player; an Editor extension is never required by the exported game.
The initial SDK registers commands and small action panels. Inspector fields for
runtime components come from the separate gameplay SchemaExporter. Rich custom
widgets and a general marketplace/package manager are later work.
## Example: Beacon
`examples/extensions/beacon` contains:
- `Beacon.hpp`: a runtime component schema and rotating-object behavior.
- `Editor.cpp`: an Editor command and a panel that creates a Beacon in one transaction.
Include `Beacon.hpp` from your project's `Scripts/Gameplay.cpp`. Call
`beacon::register_behavior(runtime)` from `registerGameplay`, and append
`beacon::schema()` to the array returned by `schema()`. Build gameplay so the Editor
can load the new metadata. Read [the first behavior tutorial](../scripting/first-behavior.md)
for the complete gameplay registration convention.
The normal engine build produces `example-plugin/` inside its build directory.
Copy its `beacon.faset-plugin.json` and native library into your project's
`Plugins/` directory, then restart the Editor. The **Beacon tools** panel offers
**Add Beacon**. Its command is also discoverable through MCP as
`plugin_example_beacon_create` and requires a document ID.
Disabling the Editor extension removes its panel and command after restart. It does
not erase its saved component data. Removing the runtime registration leaves an
unknown component preserved by authoring; exporting that scene fails until the
runtime dependency is restored or the component is deliberately removed.
## Compatibility and ownership
A manifest includes module ID/version, `kind: editor`, API version, native library,
build fingerprint and dependencies with exact versions. The loader validates the
complete graph for missing dependencies and cycles before calling entry points.
A failed dependency prevents loading its dependents.
The fingerprint includes the SDK sources, dependency lock, platform, architecture,
compiler version, configuration and CRT settings. Rebuild a plugin for the exact
Editor SDK. Compatibility across arbitrary C++ builds is not promised. Reloading an
updated native module requires restarting the Editor.
`include/faset/editor/plugin_api.h` defines a small C interface. Borrowed UTF-8 JSON
strings are valid during a call; responses are copied through a receiving callback.
Each side frees its own allocations. Register only during startup and invoke the SDK
on the Editor thread. Commands must use their owning module's name prefix. Panels
can invoke owned commands; a `$document` argument resolves to the active authoring
document. No EnTT registry, live runtime world, or arbitrary C++ object pointer is
exposed as a scripting API.
The native plugin test loads the actual example module, creates a component,
checks Undo, runs its separate runtime behavior, unloads command registrations,
opens the saved scene without the package, and rejects incompatible/cyclic/missing
plugin dependencies.
+85
View File
@@ -0,0 +1,85 @@
# Use the Editor through MCP
Faset exposes the same authoring commands to its native interface and to MCP. MCP
runs in the **Editor**. It never provides access to a running game's entities, and
is not linked into the Player or SchemaExporter.
Start a headless server after building Faset:
```sh
build/linux-debug/faset_editor --project /absolute/path/to/game --mcp
```
Configure your MCP client to launch that executable with those arguments using a
stdio transport. On Windows select `build/windows-debug/faset_editor.exe`. Use
absolute paths, including the project path. Add `--gui` when the same process
should display the native Editor; a graphical session and supported GPU are then
required. A separately launched Editor process owns a separate in-memory session.
The transport uses newline-delimited JSON-RPC and the MCP `2025-06-18` lifecycle.
Initialize, send `notifications/initialized`, then discover commands with
`tools/list`. Standard output is reserved for protocol messages. See the official
[MCP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
and [tool contracts](https://modelcontextprotocol.io/specification/2025-06-18/server/tools).
## Authoring workflow
1. Use `faset_documents` or create/open a document.
2. Query `faset_schema` for stable type and field IDs and their constraints.
3. Query the document for its persistent ID and current revision.
4. Submit one `faset_scene_edit` batch with that revision.
5. Save with `faset_document_save`. Use Undo/Redo for authoring changes.
For example, the arguments to `faset_scene_edit` can be:
```json
{
"document": "REPLACE_WITH_DOCUMENT_ID",
"revision": 0,
"idempotency_key": "create-first-object",
"operations": [{"op": "entity.create", "name": "Player"}]
}
```
The command returns the new revision and scene with generated IDs. Repeating the
same batch and retry key in the same session returns its previous result. Reusing
the key for a different payload is an error. An outdated revision produces
`revision.conflict`; query the new state and decide how to apply the intended change.
Do not blindly retry a write using a fresh revision.
Each successful batch is one Undo step. A failed operation rejects the whole batch.
Manual Inspector edits use this same service, including revision checks.
## Jobs and capabilities
`faset_import`, `faset_build`, `faset_export`, and `faset_play` return job IDs.
Use `faset_job` or `faset_jobs` for progress, diagnostics and results.
`faset_job_cancel` requests cancellation; cancelling a build is separate from
undoing a document change. Failed compilation/import preserves the last successful
published generation.
`faset_capabilities` reports available services. `faset_editor_capture` is present
only in a graphical Editor. It returns an MCP image and can save a project-relative
PNG. A headless server has no screenshot tool. `faset_play_control` provides pause,
resume and single-step process controls; it cannot read or modify game entities.
`faset_recovery_list` and `faset_recovery_restore` expose crash recovery, including
scenes that have never been saved. Restoring an already open document requires its
current revision. Recovery refuses to overwrite an externally changed scene file.
## Single-command CLI
The CLI is useful for scripts that do not need an MCP session:
```sh
build/linux-debug/faset_editor --project /absolute/path/to/game \
--command '{"name":"faset_import","arguments":{"path":"Assets/door/manifest.json"}}' --wait
```
`--wait` follows a returned job until completion. Failed, cancelled and conflicting
jobs produce a nonzero exit code. This shell quoting example targets Bash; use the
appropriate argument quoting for your Windows shell.
Validation: unit tests cover protocol errors and shared authoring semantics.
`editor_mcp_stdio` launches the real Editor and verifies initialization, clean JSON
stdout, conflicts, retry behavior, Undo/Redo, saving, reopening, and orderly EOF.