StarCraft-Kampagnen-MCP-Server: vollstaendige Implementierung
FastMCP-Server (stdio + Streamable HTTP) mit 13 sc_-Tools fuer das Lesen und Schreiben von StarCraft-Brood-War-Karten (.scm/.scx) ueber RichChk: - sc_list_maps, sc_describe_map, sc_list_locations, sc_list_triggers (lesen) - sc_create_location, sc_rename_location, sc_set_player_setup - sc_add_trigger (Kern-Tool: 10 Condition- und 20 Action-Typen), sc_remove_trigger, sc_clear_triggers - sc_embed_wav (Voiceover/Sound-Einbettung), sc_save_map, sc_reset_map Tolerante Pydantic-Eingaben, hilfreiche Fehlermeldungen, readOnly/destructive-Hints. Karten-Sitzung im Speicher pro Basis-Karte; Basis-Karte bleibt unveraendert. Deployment: Dockerfile (python:3.12-slim), docker-compose, Caddyfile, run.sh. Beispiel-Basis-Karte in data/maps. Selbsttest beweist die ganze Pipeline. Verifiziert: Pipeline + alle Tools + WAV-Einbettung + Streamable-HTTP-Handshake (echter MCP-Client), unter Python 3.11 und 3.12. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NaZBZofC1on2gkSMb5UWZy
This commit is contained in:
parent
70d10c4554
commit
9604e26351
17 changed files with 1852 additions and 121 deletions
312
README.md
312
README.md
|
|
@ -1,155 +1,225 @@
|
|||
# Star-Edit — StarCraft-Kampagnen-MCP-Server ("Missions-Baumeister")
|
||||
|
||||
> **Status:** Planung / Spezifikation. Dieses Dokument hält den kompletten Auftrag
|
||||
> fest, bevor Code gebaut wird. Es dient als verbindliche Vorlage für die Umsetzung.
|
||||
Ein **MCP-Server**, mit dem Claude StarCraft-Brood-War-Karten (`.scm`/`.scx`) liest und
|
||||
schreibt. Du lieferst die Kreativ-Seite (Story, Dialoge, Voiceover, Sounds, Ablauf),
|
||||
Claude baut daraus echte, **spielbare Missionsdateien** — komplette Logik, Texte und
|
||||
Sound-Einbettung.
|
||||
|
||||
## Ziel in einem Satz
|
||||
> **Das Gelände wird NICHT generiert.** Jede Mission startet von einer vorhandenen
|
||||
> Basis-Karte als Leinwand; der Server bearbeitet nur deren Daten-/Logik-Sektionen
|
||||
> (Trigger, Locations, Player-Setup, Sounds).
|
||||
|
||||
Ein **MCP-Server**, der Claude in die Lage versetzt, aus der Kreativ-Seite einer
|
||||
StarCraft-Brood-War-Kampagne (Story, Dialoge, Voiceover, Sounds, Missionsablauf)
|
||||
echte, spielbare `.scm`/`.scx`-Missionsdateien zu bauen — also komplette
|
||||
Missions-Logik, Texte und Sound-Einbettung.
|
||||
Die ganze Karten-Manipulation läuft über die Python-Bibliothek
|
||||
[RichChk](https://github.com/sethmachine/richchk).
|
||||
|
||||
**Wichtig:** Das **Gelände wird nicht generiert**. Jede Mission startet von einer
|
||||
vorhandenen Basis-Karte als Leinwand; der Server bearbeitet nur die Daten-/Logik-
|
||||
Sektionen darin.
|
||||
---
|
||||
|
||||
## Rollenverteilung
|
||||
## In 3 Schritten loslegen
|
||||
|
||||
- **Ich (Auftraggeber):** liefere die Kreativ-Seite — Story, Dialoge, Voiceover,
|
||||
Sounds, Missionsablauf. Null Programmierkenntnisse. Will am Ende **einen Befehl**
|
||||
ausführen und eine **URL** in Claude eintragen.
|
||||
- **Claude + MCP-Server:** setzt die Kreativ-Vorlage in echte Missionsdateien um.
|
||||
### 1. Server starten (ein Befehl)
|
||||
|
||||
## Technische Basis
|
||||
Auf dem VPS, im Projektverzeichnis:
|
||||
|
||||
- **Karten-Manipulation:** Python-Bibliothek **RichChk** (PyPI: `richchk`).
|
||||
- `pip install richchk` funktioniert; bringt StormLib mit, läuft auf Linux
|
||||
(getestet — `StarCraftMpqIoHelper.create_mpq_io()` startet ohne Fehler).
|
||||
- **Bestätigte Einstiegspunkte** (Signaturen vor dem Coden per Introspektion final
|
||||
prüfen, da sie je nach Version leicht abweichen):
|
||||
```bash
|
||||
./run.sh
|
||||
```
|
||||
|
||||
Lesen/Schreiben von Karten (MPQ-Container):
|
||||
```python
|
||||
from richchk.io.mpq.starcraft_mpq_io_helper import StarCraftMpqIoHelper
|
||||
mpq_io = StarCraftMpqIoHelper.create_mpq_io()
|
||||
rich_chk = mpq_io.read_chk_from_mpq("/pfad/map.scx") # -> RichChk-Modell
|
||||
mpq_io.save_chk_to_mpq(rich_chk, "/pfad/basis.scx", "/pfad/out.scx")
|
||||
```
|
||||
Das baut das Docker-Image (beim ersten Mal) und startet den Container `sc-mcp`.
|
||||
Er lauscht intern auf Port **8000** mit Streamable-HTTP unter dem Pfad **`/mcp`**.
|
||||
|
||||
Bearbeiten:
|
||||
```python
|
||||
from richchk.editor.richchk.rich_chk_editor import RichChkEditor
|
||||
new_chk = RichChkEditor().replace_chk_section(neue_sektion, rich_chk)
|
||||
rich_chk.get_sections_by_name(...) # vorhandene Sektionen abfragen
|
||||
rich_chk.chk_sections # alle Sektionen
|
||||
```
|
||||
Weitere Befehle:
|
||||
|
||||
- **Fertige Editoren** unter `richchk.editor.richchk.*`:
|
||||
**trig** (Trigger), **mrgn** (Locations), **unis/unix** (Einheiten-Werte),
|
||||
**forc** (Forces/Fraktionen), **ownr** (Owner), **side** (Rassen),
|
||||
**swnm** (Switches), **uprp** (Unit Properties), **wav** (Sounds).
|
||||
- **Rich-Modelle** (Trigger, Conditions, Actions, Locations, …) unter
|
||||
`richchk.model.richchk.*`.
|
||||
```bash
|
||||
./run.sh logs # Live-Logs ansehen
|
||||
./run.sh selftest # den eingebauten Selbsttest laufen lassen
|
||||
./run.sh stop # Container stoppen
|
||||
```
|
||||
|
||||
**Herzstück = Trigger.** Fast die gesamte Kampagnen-Logik wird über Trigger
|
||||
abgebildet: Gegnerwellen, Verstärkungen, Einheiten spawnen, Story-Text einblenden,
|
||||
Funksprüche mit Portrait, WAV-Sounds abspielen, Missionsziele, Sieg/Niederlage,
|
||||
Timer, AI-Skripte starten. Das Trigger-Tool muss ausdrucksstark und gut
|
||||
dokumentiert sein (unterstützte Condition-/Action-Typen in der Tool-Beschreibung).
|
||||
### 2. Subdomain via Caddy (TLS)
|
||||
|
||||
### Erster Arbeitsschritt (vor dem Tool-Coding)
|
||||
Caddy terminiert TLS und leitet die Subdomain an den Container weiter. Trage den Block
|
||||
aus der [`Caddyfile`](./Caddyfile) in deine bestehende Caddy-Konfiguration ein:
|
||||
|
||||
1. `richchk` installieren.
|
||||
2. README/Quellen des GitHub-Repos "richchk" lesen.
|
||||
3. Installiertes Paket per Introspektion inspizieren, um die echten Klassen für
|
||||
Trigger (Conditions/Actions), Locations, Forces etc. zu lernen.
|
||||
4. **Keine erratenen Methodennamen** schreiben.
|
||||
```caddy
|
||||
sc-mcp.pixel-by-design.de {
|
||||
reverse_proxy sc-mcp:8000
|
||||
}
|
||||
```
|
||||
|
||||
## Stack & Architektur
|
||||
Voraussetzung: Caddy und der `sc-mcp`-Container hängen im selben Docker-Netzwerk (in
|
||||
`docker-compose.yml` als externes Netzwerk `caddy` eingetragen — passe den Namen an
|
||||
dein Setup an).
|
||||
|
||||
| Aspekt | Wahl |
|
||||
|---------------|------|
|
||||
| Sprache | Python 3.12 |
|
||||
| MCP-Framework | FastMCP (offizielles `mcp`-SDK, `pip install "mcp[cli]"`) |
|
||||
| Transport | stdio (lokaler Test) + **Streamable HTTP** (Dauerbetrieb VPS) |
|
||||
| Betrieb | Docker-Container, eingebunden ins bestehende docker-compose Setup |
|
||||
| Reverse-Proxy | Caddy (läuft bereits) → TLS-Subdomain, z.B. `sc-mcp.pixel-by-design.de` |
|
||||
| Karten-Volume | gemountetes Volume (z.B. `/data/maps`) für Basis-Karten, WAVs, Missionen |
|
||||
### 3. In Claude als Custom Connector eintragen
|
||||
|
||||
## Tools (Prefix `sc_`)
|
||||
Die URL, die du in Claude einträgst:
|
||||
|
||||
| Tool | Zweck | Hints |
|
||||
|------|-------|-------|
|
||||
| `sc_list_maps` | listet `.scm`/`.scx`-Dateien im Karten-Verzeichnis | readOnly |
|
||||
| `sc_describe_map` | menschenlesbare Übersicht: Tileset, Größe, Forces/Player-Setup, Rassen, Locations, Trigger-Zusammenfassung | readOnly |
|
||||
| `sc_list_locations` | Locations (MRGN) auflisten | readOnly |
|
||||
| `sc_create_location` | Location anlegen | — |
|
||||
| `sc_rename_location` | Location umbenennen | — |
|
||||
| `sc_set_player_setup` | Forces (forc), Owner (ownr), Rassen (side) setzen | — |
|
||||
| `sc_add_trigger` | **Kern-Tool.** Trigger mit Conditions + Actions + betroffenen Playern hinzufügen | — |
|
||||
| `sc_list_triggers` | Trigger auflisten | readOnly |
|
||||
| `sc_remove_trigger` | einzelnen Trigger entfernen | destructive |
|
||||
| `sc_clear_triggers` | alle Trigger entfernen | destructive |
|
||||
| `sc_embed_wav` | WAV-Datei einbetten und referenzierbar machen (wav-Editor) | — |
|
||||
| `sc_save_map` | bearbeitetes Modell als neue `.scm`/`.scx` schreiben (`save_chk_to_mpq`, Basis-Karte als Vorlage) | — |
|
||||
| `sc_set_briefing` | **optional / experimentell.** Mission-Briefing (MBRF). Falls kein High-Level-Editor: als TODO markieren, kein Blocker | — |
|
||||
```
|
||||
https://sc-mcp.pixel-by-design.de/mcp
|
||||
```
|
||||
|
||||
### `sc_add_trigger` — Mindest-Abdeckung
|
||||
In Claude (Web/Desktop): **Einstellungen → Connectors → Custom Connector hinzufügen** →
|
||||
obige URL als Streamable-HTTP-/Remote-MCP-Endpunkt eintragen. Danach stehen die
|
||||
`sc_`-Tools im Chat zur Verfügung.
|
||||
|
||||
Strukturierte Eingabe (Liste von Conditions, Liste von Actions, betroffene Player).
|
||||
Muss u.a. abdecken:
|
||||
- Einheiten erzeugen an Location
|
||||
- Text einblenden (Display Text Message)
|
||||
- Transmission mit Portrait + WAV
|
||||
- Play WAV
|
||||
- Sieg-/Niederlagebedingungen
|
||||
- Timer
|
||||
- AI-Skript ausführen
|
||||
---
|
||||
|
||||
### Anforderungen pro Tool
|
||||
## Wo liegen Karten, WAVs und Missionen?
|
||||
|
||||
- Klare Beschreibung.
|
||||
- Sinnvolle **Pydantic**-Eingabeschemas mit Feldbeschreibungen.
|
||||
- Hilfreiche Fehlermeldungen, z.B.
|
||||
`"Location 'Basis' existiert nicht – verfügbar: …"`.
|
||||
- `readOnlyHint`/`destructiveHint`-Annotationen passend gesetzt.
|
||||
Alles im Verzeichnis **`./data/maps`** (im Container als `/data/maps` gemountet):
|
||||
|
||||
## Definition of Done — Selbsttest
|
||||
- **Basis-Karten** (`.scx`/`.scm`) — die Leinwand für neue Missionen.
|
||||
- **WAV-Dateien** (`.wav`) — Voiceover/Sounds, die eingebettet werden.
|
||||
- **Fertige Missionen** — schreibt der Server hierhin.
|
||||
|
||||
Ein kurzer Selbsttest beweist die ganze Pipeline:
|
||||
Eine **Beispiel-Basis-Karte** liegt bereits unter
|
||||
[`data/maps/base-map.scx`](./data/maps/base-map.scx) (256×256, Jungle), damit du sofort
|
||||
die erste Mission bauen kannst.
|
||||
|
||||
1. Basis-Karte aus `/data/maps` laden.
|
||||
2. Eine Location anlegen.
|
||||
3. Per Trigger hinzufügen: bei Spielstart eine Texteinblendung
|
||||
`"Mission 1 – Start"` **und** das Erzeugen von ein paar Einheiten an der Location.
|
||||
4. Als neue `.scx` speichern.
|
||||
5. Neue Datei erneut laden und bestätigen, dass Trigger + Location wirklich drin sind.
|
||||
---
|
||||
|
||||
Läuft das durch, steht das Fundament.
|
||||
## Die Tools (Prefix `sc_`)
|
||||
|
||||
## Liefergegenstände
|
||||
| Tool | Zweck |
|
||||
|------|-------|
|
||||
| `sc_list_maps` | listet alle Karten/Missionen im Verzeichnis *(nur lesen)* |
|
||||
| `sc_describe_map` | Übersicht: Tileset, Größe, Player-Setup, Locations, Trigger-Zusammenfassung *(nur lesen)* |
|
||||
| `sc_list_locations` | Locations auflisten *(nur lesen)* |
|
||||
| `sc_create_location` | Location anlegen (Mittelpunkt + Größe in Pixeln) |
|
||||
| `sc_rename_location` | Location umbenennen |
|
||||
| `sc_set_player_setup` | Owner-Typ, Rasse und Force je Spieler setzen (+ Force-Namen) |
|
||||
| `sc_add_trigger` | **Kern-Tool:** Trigger mit Bedingungen + Aktionen hinzufügen |
|
||||
| `sc_list_triggers` | Trigger mit Klartext-Zusammenfassung auflisten *(nur lesen)* |
|
||||
| `sc_remove_trigger` | einzelnen Trigger entfernen *(destruktiv)* |
|
||||
| `sc_clear_triggers` | alle Trigger entfernen *(destruktiv)* |
|
||||
| `sc_embed_wav` | WAV einbetten und referenzierbar machen |
|
||||
| `sc_save_map` | aktuellen Stand als neue `.scx`/`.scm` schreiben |
|
||||
| `sc_reset_map` | nicht gespeicherte Änderungen verwerfen *(destruktiv)* |
|
||||
|
||||
- [ ] Server läuft als Docker-Service, erreichbar über die TLS-Subdomain.
|
||||
- [ ] **Ein** Start-/Neustart-Befehl.
|
||||
- [ ] Die Connector-URL für Claude (Custom Connector).
|
||||
- [ ] Beispiel-Basis-Karte liegt bereits in `/data/maps`.
|
||||
- [ ] `README.md` mit Startbefehl, Subdomain-URL und Anleitung für den Custom
|
||||
Connector in Claude.
|
||||
**Arbeitsmodell:** Die Bearbeitung läuft pro Basis-Karte als Sitzung im Speicher des
|
||||
Servers. Mehrere Tool-Aufrufe (Location anlegen, Trigger hinzufügen, WAV einbetten)
|
||||
sammeln sich an; erst `sc_save_map` schreibt eine neue Datei. Die Basis-Karte bleibt
|
||||
unverändert. `sc_reset_map` verwirft den Zwischenstand.
|
||||
|
||||
## Defaults / Entscheidungen
|
||||
### Das Kern-Tool `sc_add_trigger`
|
||||
|
||||
> Hier werden während der Umsetzung die getroffenen Default-Entscheidungen
|
||||
> dokumentiert, damit am Ende klar ist, was gewählt wurde (keine technischen
|
||||
> Rückfragen, wo selbst entscheidbar).
|
||||
Ein Trigger besteht aus **Bedingungen** (`conditions`, UND-verknüpft) und **Aktionen**
|
||||
(`actions`), plus den **Spielern** (`players`), für die er läuft. Werte dürfen tolerant
|
||||
angegeben werden: per Bezeichner (`TERRAN_MARINE`), Anzeigename (`Terran Marine`) oder
|
||||
Zahl (`0`).
|
||||
|
||||
- _(wird ergänzt)_
|
||||
**Unterstützte Bedingungen (`type`):** `always`, `never`, `elapsed_time`,
|
||||
`countdown_timer`, `bring`, `command`, `kill`, `deaths`, `accumulate`, `opponents`.
|
||||
|
||||
## Offene TODOs
|
||||
**Unterstützte Aktionen (`type`):** `display_text`, `set_mission_objectives`,
|
||||
`play_wav`, `create_unit`, `kill_unit_at_location`, `remove_unit_at_location`,
|
||||
`move_unit`, `give_units`, `set_resources`, `set_deaths`, `set_countdown_timer`,
|
||||
`pause_timer`, `unpause_timer`, `run_ai_script`, `run_ai_script_at_location`,
|
||||
`center_view`, `minimap_ping`, `set_alliance_status`, `victory`, `defeat`.
|
||||
|
||||
- [ ] `richchk` installieren & per Introspektion echte Klassen verifizieren.
|
||||
- [ ] Projektstruktur (FastMCP-Server, Pydantic-Schemas, Tool-Module) anlegen.
|
||||
- [ ] Self-Test-Skript schreiben.
|
||||
- [ ] Dockerfile + docker-compose-Service + Caddy-Reverse-Proxy-Eintrag.
|
||||
- [ ] Beispiel-Basis-Karte bereitstellen.
|
||||
- [ ] `sc_set_briefing` (MBRF) als best-effort prüfen.
|
||||
Beispiel (Spielstart: Text + 4 Marines an der Location "Basis"):
|
||||
|
||||
```json
|
||||
{
|
||||
"map": "base-map.scx",
|
||||
"players": ["PLAYER_1"],
|
||||
"conditions": [{ "type": "always" }],
|
||||
"actions": [
|
||||
{ "type": "display_text", "text": "Mission 1 – Start" },
|
||||
{ "type": "create_unit", "player": "PLAYER_1", "amount": 4,
|
||||
"unit": "TERRAN_MARINE", "location": "Basis" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Funksprüche / Voiceover:** Zuerst `sc_embed_wav` aufrufen (gibt einen `wav_path`
|
||||
zurück), dann im Trigger `play_wav` (mit diesem `wav_path`) zusammen mit `display_text`
|
||||
und optional `center_view` verwenden.
|
||||
|
||||
---
|
||||
|
||||
## Selbsttest (beweist die ganze Pipeline)
|
||||
|
||||
```bash
|
||||
./run.sh selftest # im Container
|
||||
# oder lokal:
|
||||
python selftest.py
|
||||
```
|
||||
|
||||
Der Test: Basis-Karte laden → Location anlegen → Trigger (Text + Einheiten bei Start) →
|
||||
als neue `.scx` speichern → neu laden → Trigger + Location bestätigt.
|
||||
|
||||
---
|
||||
|
||||
## Lokal testen (ohne Docker, stdio)
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv && . .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
SC_MAPS_DIR=./data/maps python -m starcraft_mcp.server # stdio-Transport
|
||||
```
|
||||
|
||||
Für HTTP lokal: `SC_TRANSPORT=http SC_PORT=8000 SC_MAPS_DIR=./data/maps python -m starcraft_mcp.server`
|
||||
→ Endpunkt `http://127.0.0.1:8000/mcp`.
|
||||
|
||||
### Konfiguration (Umgebungsvariablen)
|
||||
|
||||
| Variable | Standard | Bedeutung |
|
||||
|----------|----------|-----------|
|
||||
| `SC_TRANSPORT` | `stdio` | `http` für Streamable-HTTP-Betrieb |
|
||||
| `SC_HOST` | `0.0.0.0` (HTTP) | Bind-Adresse |
|
||||
| `SC_PORT` | `8000` | Port |
|
||||
| `SC_MAPS_DIR` | `/data/maps` | Karten-/WAV-/Missions-Verzeichnis |
|
||||
|
||||
---
|
||||
|
||||
## Gewählte Defaults
|
||||
|
||||
- **Beispiel-Basis-Karte:** die MIT-lizenzierte `base-map.scx` aus dem RichChk-Repo
|
||||
(256×256, Jungle). Liegt in `data/maps/`.
|
||||
- **Locations** werden als Box um einen Mittelpunkt angelegt (Standard 96×96 px;
|
||||
32 px = 1 Kachel).
|
||||
- **`preserve=true`** ist Standard bei `sc_add_trigger` (Trigger bleibt nach Auslösen
|
||||
bestehen — passt für fast alle Kampagnen-Trigger).
|
||||
- **RichChk-Logging** ist auf `CRITICAL` gesetzt, damit die Server-Ausgabe sauber bleibt.
|
||||
- **Netzwerk:** Der Container wird **nicht** direkt nach außen exponiert; Caddy spricht
|
||||
ihn intern über `sc-mcp:8000` an (sicherer). Zum reinen Lokaltest sind die `ports:`
|
||||
in der `docker-compose.yml` auskommentiert vorbereitet.
|
||||
|
||||
---
|
||||
|
||||
## Bekannte Einschränkungen / TODO
|
||||
|
||||
- **Transmission mit Portrait (`Transmission`/`TalkingPortrait`):** RichChk hat in dieser
|
||||
Version **kein** High-Level-Modell für diese Aktion. Funksprüche baust du deshalb aus
|
||||
`play_wav` + `display_text` (+ optional `center_view`) zusammen. → TODO, sobald RichChk
|
||||
es unterstützt.
|
||||
- **Mission-Briefing (`sc_set_briefing`, MBRF):** RichChk bietet (noch) keinen
|
||||
High-Level-Editor für die MBRF-Sektion. Für v1 bewusst weggelassen. → TODO.
|
||||
- **Schalter (Switches) in Bedingungen/Aktionen:** für v1 nicht exponiert. → TODO.
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
starcraft_mcp/
|
||||
server.py FastMCP-Server mit allen sc_-Tools
|
||||
workspace.py Karten-Sitzung im Speicher (Laden/Bearbeiten/Speichern, WAV-Einbettung)
|
||||
triggers.py Pydantic-Schemas + Builder für Conditions/Actions (Herzstück-Doku)
|
||||
enums.py tolerante Resolver (Einheiten, Spieler, Vergleiche, …)
|
||||
richchk_logging.yaml setzt RichChk-Logging auf CRITICAL
|
||||
selftest.py End-to-End-Selbsttest durch die echten Tools
|
||||
data/maps/ Karten-Volume (Basis-Karte, WAVs, fertige Missionen)
|
||||
Dockerfile, docker-compose.yml, Caddyfile, run.sh Betrieb
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hinweis zur Erstellung
|
||||
|
||||
Code und gesamte Pipeline wurden in einer Linux-Umgebung gebaut und **getestet**
|
||||
(RichChk-Lese-/Schreib-Pipeline, alle 13 Tools, WAV-Einbettung, sowie der
|
||||
Streamable-HTTP-Endpunkt per echtem MCP-Client-Handshake). Das tatsächliche Deployment
|
||||
auf deinem Hetzner-VPS (Docker-Build, Caddy-Subdomain, DNS) führst du mit `./run.sh`
|
||||
selbst aus — das konnte aus der Build-Umgebung heraus nicht für deinen VPS erfolgen.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue