* Projekt-Spezifikation für StarCraft-Kampagnen-MCP-Server in README festhalten Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NaZBZofC1on2gkSMb5UWZy * 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 --------- Co-authored-by: Claude <noreply@anthropic.com>
111 lines
3.9 KiB
Python
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]
|