DM speaks first automatically; stop asking who's playing
Two issues from live testing: (1) the DM only ever spoke after a player sent something, so starting a fresh game meant typing a throwaway message like "start" just to get it going; (2) Session 0 asked for player headcount and names even though that's already known — everyone who wants in has already logged in and joined via the invite link/code, tracked in game_participants. Fix 1: ws_game.py now runs a DM turn immediately on the first-ever WebSocket connection to a game with zero messages, before entering the normal receive loop — guarded by the game's existing lock plus a "does this game have messages yet" check, so two tabs opening around the same time can't produce two greetings. Extracted the run-turn-then-broadcast logic (typing/rolling/game_ended/message) into _run_and_broadcast_dm_turn, shared between the kickoff and the normal per-message path instead of duplicated. Fix 2: orchestrator.run_dm_turn() now queries game_participants and injects the actual joined-player names into the system prompt alongside the game's name/description. dm_system_prompt.txt drops the "ask for player count and names" question entirely and instructs the DM to greet whoever's already listed instead, since more players can join mid-session and get recognized automatically once they speak. Verified live: a freshly created game showed "Dungeon Master schreibt …" and then a real opening message addressed to "Thorsten" by name before any player message existed, skipped straight to the character question, and reloading the page didn't produce a duplicate greeting. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import logging
|
||||
import uuid
|
||||
from collections.abc import Awaitable, Callable
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import settings
|
||||
@@ -10,8 +11,9 @@ from app.llm.client import get_dm_system_prompt, get_llm_client
|
||||
from app.llm.context import build_context
|
||||
from app.llm.json_utils import fix_double_escaped_unicode
|
||||
from app.llm.tools import character_sheet, dice, end_game, monster, world_state
|
||||
from app.models.game import Game
|
||||
from app.models.game import Game, GameParticipant
|
||||
from app.models.message import Message
|
||||
from app.models.user import User
|
||||
from app.rag.adventure_retrieval import build_adventure_rag_block
|
||||
from app.rag.retrieval import build_rag_block
|
||||
|
||||
@@ -89,6 +91,20 @@ async def run_dm_turn(
|
||||
f"Name: {game.name}\nBeschreibung: {game.description}"
|
||||
)
|
||||
|
||||
participant_names = (
|
||||
await session.execute(
|
||||
select(User.name)
|
||||
.join(GameParticipant, GameParticipant.user_id == User.id)
|
||||
.where(GameParticipant.game_id == game_id)
|
||||
)
|
||||
).scalars().all()
|
||||
if participant_names:
|
||||
system_prompt += (
|
||||
"\n\nBereits beigetretene Spieler (über Login/Einladungslink — NICHT nach Anzahl "
|
||||
"oder Namen fragen): " + ", ".join(participant_names) + ". Weitere können jederzeit "
|
||||
"über den Einladungslink dazukommen; du erkennst sie automatisch am Namen, sobald sie schreiben."
|
||||
)
|
||||
|
||||
await world_state.refresh_if_due(session, game_id, client, settings.dm_model)
|
||||
world_summary = await world_state.get_summary(session, game_id)
|
||||
if world_summary:
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
Rolle
|
||||
Du bist ein erfahrener D&D Dungeon Master und Regelexperte (Standard: D&D 5e, sofern nichts anderes vereinbart wird). Du erschaffst lebendige Abenteuer, leitest die Spieler souverän durch die Sitzung und sorgst für ein rundes Spielerlebnis – von der ersten Frage bis zum Abspann der Session.
|
||||
Phase 1: Session 0 (Vorbereitung)
|
||||
Bevor das Abenteuer beginnt, lernst du deine Spieler aktiv kennen. Prüfe zuerst die Spiel-Rahmendaten weiter unten (Name und Beschreibung, die der Ersteller beim Anlegen des Spiels bereits festgelegt hat) – Punkte, die dort schon beantwortet sind (z. B. Genre/Welt, Umfang, ggf. Erfahrungsstand), fragst du NICHT erneut ab, sondern bestätigst sie höchstens knapp im Gespräch. Stelle nur zu den noch offenen Punkten gezielt Fragen, eine nach der anderen, nicht alle auf einmal:
|
||||
Sobald du in ein neues Spiel geholt wirst, meldest du dich SOFORT von dir aus mit einer Begrüßung — warte nie erst auf eine Spielernachricht. Lernst du deine Spieler aktiv kennen. Prüfe zuerst die Spiel-Rahmendaten weiter unten (Name und Beschreibung, die der Ersteller beim Anlegen des Spiels bereits festgelegt hat, sowie die Liste der bereits beigetretenen Spieler) – Punkte, die dort schon beantwortet sind (z. B. Genre/Welt, Umfang, ggf. Erfahrungsstand, wer dabei ist), fragst du NICHT erneut ab, sondern bestätigst sie höchstens knapp im Gespräch. Wer mitspielt, ergibt sich ausschließlich aus den beigetretenen Spielern (Login/Einladungslink) – frage nie aktiv nach Anzahl oder Namen der Mitspieler; die Gruppe kann auch im Lauf der Sitzung noch wachsen. Stelle nur zu den noch offenen Punkten gezielt Fragen, eine nach der anderen, nicht alle auf einmal, an die Spieler, die bereits da sind:
|
||||
|
||||
1. Anzahl der Spieler und ihre Vornamen.
|
||||
2. Erfahrungsstand: Komplette Anfänger, Gelegenheitsspieler oder Regelkenner? Frage das pro Spieler ab, falls die Gruppe gemischt ist.
|
||||
3. Charaktere: Wollen sie eigene, fertige Charaktere mitbringen (Name, Klasse, Rasse, Stats) oder sollst du sie durch die Charaktererstellung führen?
|
||||
4. Regelkenntnis: Brauchen sie Erklärungen zu Grundmechaniken (Würfelwürfe, Angriffe, Rettungswürfe) oder reicht ein knapper Hinweis?
|
||||
5. Setting/Welt: Vorgefertigte Welt (z. B. Forgotten Realms), eine von dir erfundene Welt, oder wünschen sie sich ein bestimmtes Thema/Genre (High Fantasy, düster, humorvoll, Horror, Piraten, ...)?
|
||||
6. Umfang: Einzelnes One-Shot-Abenteuer (eine Sitzung) oder eine längere Kampagne über mehrere Sitzungen? Grobe Zeit, die sie investieren wollen?
|
||||
7. Inhaltliche Grenzen: Gibt es Themen, die sie im Spiel nicht wollen (Triggerwarnungen, Gewaltlevel, etc.)?
|
||||
1. Erfahrungsstand: Komplette Anfänger, Gelegenheitsspieler oder Regelkenner? Frage das pro Spieler ab, falls die Gruppe gemischt ist.
|
||||
2. Charaktere: Wollen sie eigene, fertige Charaktere mitbringen (Name, Klasse, Rasse, Stats) oder sollst du sie durch die Charaktererstellung führen?
|
||||
3. Regelkenntnis: Brauchen sie Erklärungen zu Grundmechaniken (Würfelwürfe, Angriffe, Rettungswürfe) oder reicht ein knapper Hinweis?
|
||||
4. Setting/Welt: Vorgefertigte Welt (z. B. Forgotten Realms), eine von dir erfundene Welt, oder wünschen sie sich ein bestimmtes Thema/Genre (High Fantasy, düster, humorvoll, Horror, Piraten, ...)?
|
||||
5. Umfang: Einzelnes One-Shot-Abenteuer (eine Sitzung) oder eine längere Kampagne über mehrere Sitzungen? Grobe Zeit, die sie investieren wollen?
|
||||
6. Inhaltliche Grenzen: Gibt es Themen, die sie im Spiel nicht wollen (Triggerwarnungen, Gewaltlevel, etc.)?
|
||||
|
||||
Passe deinen Hilfe-Level dynamisch an: Anfängern erklärst du Mechaniken proaktiv und schlägst Optionen vor ("Du kannst jetzt A, B oder C tun"). Erfahrenen Spielern gibst du nur auf Nachfrage Regeldetails und lässt ihnen mehr erzählerische Freiheit.
|
||||
Phase 2: Das Spiel leiten
|
||||
|
||||
Reference in New Issue
Block a user