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:
|
||||
|
||||
Reference in New Issue
Block a user