- Python 87.9%
- Shell 4.9%
- TypeScript 2.7%
- Dockerfile 2.3%
- Makefile 1.8%
- Other 0.4%
So restarting after an mcp redeploy re-embeds only what's missing (Qdrant persists the rest). --force to re-embed all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .opencode | ||
| audio-server | ||
| comfyui | ||
| docs | ||
| graphify-out/cache | ||
| orchestrator | ||
| playtest | ||
| scripts | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| docker-compose.build.yml | ||
| docker-compose.yml | ||
| Makefile | ||
| opencode.jsonc | ||
| openhands.config.toml.example | ||
| pyproject.toml | ||
| README.md | ||
| requirements-dev.txt | ||
| ROADMAP.md | ||
Agentic Game Development Studio
A fully self-hosted, agent-driven game studio on one server.
What it is
A local game studio where an AI agent (driven by OpenCode) does the heavy lifting across planning, code, art, music, sound, and playtesting. Three principles shape the stack:
- The harness owns the loop. OpenCode drives; Trogdor just provides a model (the brain) and tools (the zoo, over MCP).
- Every generator has a critic. Images are judged by a vision model, music is scored against its brief, games are run and their screenshots reviewed.
- One compose stack. One
docker-compose.yml, deployed as a Portainer git stack from Forgejo. Git is the single source of truth.
flowchart TD
You["👤 You + OpenCode"]
Brain["🧠 Brain<br/>writes the game, sees its own screenshots"]
Zoo["🧰 The Zoo<br/>image · music · SFX · video · speech · playtest · CC0 library"]
Judge["🎬 Director<br/>the only official critic — verifies every artifact"]
You -->|"prompt + /new-game"| Brain
Brain -->|"MCP tool calls"| Zoo
Zoo -->|"artifacts + screenshots"| Judge
Judge -->|"pass / fail / fix"| Brain
Judge -->|"verdicts"| You
Build, deploy, operate
Everything is driven from a laptop; Docker commands run on Trogdor over SSH
(DOCKER_HOST=ssh://…). The stack is a Portainer git stack pulling this
repo from Forgejo. Config lives in .env (copy from .env.example).
make deploy # build custom images on Trogdor → push to Forgejo → webhook redeploy
make redeploy # config-only change: git push first, then poke the webhook
make warm # after a brain redeploy: pays the one-time ~2 min CUDA init
make health # gateway + all downstreams
make smoke # every endpoint + model state
make ps / logs # container status / tails (S=brain|director|comfyui|audio|nemo|playtest|mcp)
make test # 92 laptop tests — FastAPI wiring, no Docker/GPU/weights
Building, pushing, and running all happen on the real amd64 + CUDA hardware
over SSH — no repo or Docker needed on Trogdor. See the Makefile (and
make help) for the full list.
Set up OpenCode to drive the studio
The studio has no built-in agent loop — point OpenCode at the brain and the tool gateway:
- Model (brain):
http://trogdor:8090/v1(OpenAI-compatible) - Tools (zoo):
http://trogdor:8080/mcp/(trailing slash —/mcp307-redirects and some clients won't follow on POST)
Game projects live in their own folders, where repo-scoped config wouldn't
load. make opencode-setup symlinks the studio config into
~/.config/opencode/ — every OpenCode session on the machine gets the
studio, and a git pull here updates them all:
make opencode-setup # installs symlinks (AGENTS.md, agents/, plugins/, commands/) + verifies wiring
opencode # in ANY folder; /mcp lists the 15 zoo tools
The installer never touches your global opencode.json[c] (permissions and
theme are yours) — it verifies the trogdor provider/MCP blocks match the
repo and tells you what to paste if they drift.
Serve mode (live session the director can post producer notes into):
make studio-up DIR=~/games/my-game # opencode serve on :4096, session registered with gateway
opencode --hostname 127.0.0.1 --port 4096 # attach the TUI
make studio-down # kills ONLY the recorded pid
For OpenHands, use make openhands-setup (or copy the blocks manually from
openhands.config.toml.example).
Prompting OpenCode
The brain is text-only — but it never works blind: it delegates image judging to the director and calls the zoo's vision tools directly. Quick reference:
| Goal | How to prompt |
|---|---|
| Start a new game | /new-game <brief> — runs the full six-phase process (concept → storyboards → asset spec → asset QA → dev plan → build). Director authors design, then hands off to the coder at phase 6. |
| Resume a game | In the game dir: opencode (or make studio-up DIR=<dir>). The committed docs/ is the handoff state — the coder picks up from the next unfinished milestone. /playtest to boot + critique screenshots. |
| Consult the director | /director <question> — runs on the big vision model; looks at the actual artifacts before answering. Use mid-session for design questions or to audit a completion claim. |
| Playtest | /playtest [seconds] — commits + pushes, then run_love2d(git_url=…); critiques each screenshot against the storyboard. |
| Make an image | "Generate a 256px transparent sprite of a cloaked ranger, side view, idle pose, flat background." The agent calls generate_image(transparent=true, sprite_size=256), then critique_image to verify. Always describe: subject, style/medium, palette, perspective, intended use. |
| Make a spritesheet | "Generate a 4-frame walk-cycle spritesheet of a knight walking right, side view." → generate_spritesheet(frames=4) returns an alpha strip + per-frame PNGs + Love2D quads JSON in one pass. Never generate frames separately — they won't match. |
| Make music | "Generate a 30-second driving chiptune loop, action-battle mood, ~140 BPM." → generate_music (async: poll check_job ~every 30s), then analyze_music reports tempo and a CLAP intent score (>0.4 strong, <0.2 miss). |
| Make a sound effect | "Generate a 1-second sword-swing whoosh SFX." → generate_sfx (async, ≤ ~47 s per clip), then analyze_music to verify. |
| Make speech / narration | "Generate a TTS line: 'You shall not pass!', male, dramatic." → generate_tts (Chatterbox, default .ogg). |
| Transcribe audio | "Transcribe this clip." → transcribe_audio(url) (Parakeet default; mode=fast or provider=faster_whisper for the alt). Speech only — hallucinates on music/SFX. |
| Make a short video | "Generate a 4-second animated banner of a dragon breathing fire." → generate_video (LTX-Video, async, 2–6 s animated WEBP — experimental). |
| Read a reference image | "Look at this mood board and describe its style and palette." → read_image(url). For a local file, the upload-bridge plugin auto-uploads it for you. |
| Find a library asset | "Is there a wooden table texture in the library?" → search_local_assets("wooden table") (semantic — plain descriptions work). "Find library art that looks like this generated sprite." → find_similar_assets(url) (reverse-image). Results carry a license — credit CC-BY. |
Briefing rules (the generators have no memory — each prompt stands alone):
- Be specific: subject, style/medium, palette, perspective, intended use.
- Search the library first —
search_local_assets("knight sprite")is instant and license-free; restyle a near-miss withgenerate_image(init_url=…)written as an edit instruction ("Recolor this tileset to a lava theme."). - For sprites use the pixel pipeline:
style="pixel",transparent=true,sprite_size=256(high-res by default — small sizes are an opt-in retro aesthetic, not a space saving). - Slow generators (image, spritesheet, music, SFX, video) are async jobs:
the tool returns
{job_id}and you pollcheck_job~every 30s while doing other work. Never resubmit while one runs. - Close the loop:
verify:true(auto-rerolls up to 3× on FAIL) orcritique_imageafter — don't ship an asset you haven't checked.
The full operating manual lives in AGENTS.md — auto-loaded by
OpenCode into every session.
Services
One compose file, eight services. The brain/director use the stock
llama.cpp:server-cuda image and qdrant the stock qdrant/qdrant; the five
custom images are built on Trogdor and pushed to Forgejo. The asset gateway
(mcp) is the only one the agent talks to directly — it routes every other
service.
| Service | Container · port · GPU | Version / model | Capability | How to prompt from OpenCode |
|---|---|---|---|---|
| brain (coder) | trogdor-coder · 8090 · GPU 0+1 |
Qwen3.6-27B + vision tower (UD-Q4_K_XL, 131K ctx q8 KV) | Writes the game; sees its own screenshots while debugging. Self-checks never count as verification. | The default build agent — every normal turn. Provide a code/test/fix task; tools=true. |
| director | trogdor-director · 8094 · GPU 2 + NUMA-0 RAM |
Qwen3.5-122B-A10B MoE (UD-Q4_K_XL) | The studio's ONLY official judge: critiques, verifies, reviews playtest screenshots, audits claims. | /director <question> (mid-session consult) or the director primary agent (Tab). Default during /new-game design phases. |
| mcp (gateway) | trogdor-mcp · 8080 · CPU |
n/a (orchestrator) | 15 MCP tools + REST + /files + /library. Routes every other service; serializes GPU3 tenants; runs async jobs. |
Every generate_*, critique_image, read_image, search_local_assets, run_love2d, *audio* tool call goes here. |
| comfyui | gen-comfyui · 8188 · GPU 3 |
FLUX.2 klein 9B (Q6 GGUF) · LTX-Video 2B · BiRefNet matting | Image, spritesheet, and video generation. Style is a prompt phrase; transparent:true → real alpha. |
Indirect — generate_image, generate_spritesheet, generate_video route here. |
| audio | gen-audio · 8092 · GPU 3 |
ACE-Step · Stable Audio Open · CLAP + librosa · faster-whisper large-v3 · Chatterbox | Music, SFX, audio analysis (tempo/loop intent score), alt ASR, TTS. | generate_music, generate_sfx, analyze_music, generate_tts, transcribe_audio(mode=fast). |
| nemo | gen-nemo · 8096 · CPU |
Parakeet TDT 0.6B v3 · MarbleNet v2.0 VAD | Default ASR (multilingual) + speech-segment detection. Isolated image (NeMo pulls numpy≥2). | transcribe_audio (default), detect_speech. CPU-pinned so never contends on GPU3. |
| playtest | trogdor-playtest · 8093 · CPU |
LÖVE under Xvfb + ffmpeg | Headless game run + screenshots; one endpoint per engine (/love2d today). |
run_love2d(git_url=…, script=[…]) — interactive playtests with labeled expect screenshots the director critiques. |
library (open-license, mounted into mcp) |
/library |
Kenney + game-icons + ambientCG + OpenGameArt · ~90k files · Qdrant (semantic + reverse-image) | Game-ready sprites, tiles, UI, icons, textures, audio, fonts — CC0 / CC-BY. Search-first sourcing workflow. | search_local_assets("<query>") (semantic) · find_similar_assets(url) (reverse-image / "more like this"). |
| qdrant (vector store) | trogdor-qdrant · 6333 · CPU |
n/a | CLIP dense + BM25 sparse (hybrid) over the library; scalar-quantized. The gateway embeds via fastembed (ONNX). | (internal — the library tools query it) |
REST surface (curl / debugging + batch scripts)
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness + all downstream reachability |
GET |
/files/{path} |
Fetch any generated asset |
GET |
/library/{path} |
Fetch any CC0 library asset |
POST |
/assets/image | /music | /sfx | /tts |
One-shot generation → JSON record ("raw": true streams bytes; image also takes lora, init_url, denoise) |
POST |
/assets/upload |
Multipart file → /files URL (bridge local files to vision) |
POST |
/describe | /critique |
REST twins of read_image / critique_image — vision access for batch scripts |
POST |
/transcribe |
Multipart audio → ASR; provider/mode route to Parakeet (default) or faster-whisper |
Big batches (hundreds of similar assets) go through a repo-local script that
POSTs the REST API one item at a time — see the "Batch asset generation"
section in AGENTS.md.
Hardware (Trogdor)
| Component | Specification |
|---|---|
| Server | Dell T640 |
| CPU | 2× Intel Xeon Silver 4214 — 24 cores / 48 threads, 2 NUMA nodes |
| GPU | 4× NVIDIA Quadro RTX 5000 — 16 GB each (Turing, two NVLink pairs) |
| Memory | 384 GB DDR4-2400 — 192 GB per NUMA node |
| Storage | 4× 1 TB SAS SSD, RAID 10 (~2 TB usable) |