Star-Edit/starcraft_mcp/enums.py
Claude 9604e26351
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
2026-06-22 07:35:13 +00:00

111 lines
3.9 KiB
Python

"""Flexible Resolver fuer RichChk-Enums.
Claude (und Menschen) sollen Einheiten, Spieler, Vergleiche usw. tolerant angeben
koennen: per Bezeichner ("TERRAN_MARINE"), per Anzeigename ("Terran Marine") oder per
Zahl ("0"). Diese Helfer uebersetzen solche Eingaben in die echten RichChk-Enum-Member
und werfen bei Fehlern eine *hilfreiche* Fehlermeldung mit Beispielen.
"""
from __future__ import annotations
from typing import Type, TypeVar
from richchk.model.richchk.forc.force_id import ForceId
from richchk.model.richchk.ownr.player_type import PlayerType
from richchk.model.richchk.richchk_enum import RichChkEnum
from richchk.model.richchk.side.player_race import PlayerRace
from richchk.model.richchk.trig.conditions.comparators.numeric_comparator import (
NumericComparator,
)
from richchk.model.richchk.trig.enums.alliance_status import AllianceStatus
from richchk.model.richchk.trig.enums.amount_modifier import AmountModifier
from richchk.model.richchk.trig.enums.resource_type import ResourceType
from richchk.model.richchk.trig.enums.switch_state import SwitchState
from richchk.model.richchk.trig.player_id import PlayerId
from richchk.model.richchk.unis.unit_id import UnitId
_E = TypeVar("_E", bound=RichChkEnum)
def _identifier(member: RichChkEnum) -> str:
"""Der Python-Bezeichner eines Members, z.B. 'TERRAN_MARINE'."""
return member._name_ # type: ignore[attr-defined]
def _build_index(enum_cls: Type[_E]) -> dict[str, _E]:
index: dict[str, _E] = {}
for member in enum_cls: # type: ignore[attr-defined]
index[_identifier(member).upper()] = member
index[member.name.upper()] = member # Anzeigename ("Terran Marine")
index[str(member.id)] = member # numerische ID
return index
def _examples(enum_cls: Type[_E], limit: int = 20) -> str:
names = sorted({_identifier(m) for m in enum_cls}) # type: ignore[attr-defined]
shown = ", ".join(names[:limit])
if len(names) > limit:
shown += f", … ({len(names)} insgesamt)"
return shown
def resolve(enum_cls: Type[_E], value: str | int, what: str) -> _E:
"""Loese eine tolerante Eingabe in ein RichChk-Enum-Member auf.
Akzeptiert Bezeichner ('TERRAN_MARINE'), Anzeigename ('Terran Marine') oder ID (0).
"""
index = _build_index(enum_cls)
key = str(value).strip().upper()
if key in index:
return index[key]
raise ValueError(
f"Unbekannter Wert fuer {what}: {value!r}. "
f"Verfuegbar (Bezeichner): {_examples(enum_cls)}"
)
# --- Convenience-Wrapper mit sprechenden Namen --------------------------------
def resolve_unit(value: str | int) -> UnitId:
return resolve(UnitId, value, "Einheit (unit)")
def resolve_player(value: str | int) -> PlayerId:
return resolve(PlayerId, value, "Spieler/Gruppe (player)")
def resolve_comparator(value: str | int) -> NumericComparator:
return resolve(NumericComparator, value, "Vergleich (comparator)")
def resolve_modifier(value: str | int) -> AmountModifier:
return resolve(AmountModifier, value, "Mengen-Modifikator (modifier)")
def resolve_resource(value: str | int) -> ResourceType:
return resolve(ResourceType, value, "Ressource (resource)")
def resolve_alliance(value: str | int) -> AllianceStatus:
return resolve(AllianceStatus, value, "Allianz-Status (alliance_status)")
def resolve_switch_state(value: str | int) -> SwitchState:
return resolve(SwitchState, value, "Schalter-Zustand (switch_state)")
def resolve_player_type(value: str | int) -> PlayerType:
return resolve(PlayerType, value, "Spieler-Typ (type)")
def resolve_race(value: str | int) -> PlayerRace:
return resolve(PlayerRace, value, "Rasse (race)")
def resolve_force(value: str | int) -> ForceId:
return resolve(ForceId, value, "Force/Team (force)")
def list_identifiers(enum_cls: Type[_E]) -> list[str]:
return sorted({_identifier(m) for m in enum_cls}) # type: ignore[attr-defined]