Rum: et lukket rum kan ikke vækkes igen

Efter et skift af spil midt i en runde kan det gamle rums sene opgaver
(speakerens oplæsning, dommeren, ventetider) kalde changed() eller
_schedule_timer() bagefter og starte timeren igen uden spillere. Nu sætter
close() et flag, og changed, timeren, udsendelsen, nye replikker og Tegn og
Gæts trin-snapshots gør intet i et lukket rum. Forslag fra review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agw
2026-10-20 21:49:57 +02:00
co-authored by Claude Opus 5.5
parent 4cdc497bd0
commit 25ea21a4fb
3 changed files with 35 additions and 2 deletions
+9 -2
View File
@@ -58,6 +58,7 @@ class BaseRoom:
Spillet (`game`) skal have deadline, hold_until, tick(), phase_key() og drain_events()."""
mic_laughs = 0
closed = False # lukket (fx skiftet til et andet spil): sene opgaver må ikke vække rummet igen
def __init__(self, code: str, game, host_username: str):
self.code = code
@@ -84,6 +85,8 @@ class BaseRoom:
def changed(self):
"""Kald efter hver ændring: planlæg timer og send nyt snapshot til alle."""
if self.closed:
return
self.touch()
self._phase_key = self.game.phase_key()
self.handle_events()
@@ -111,7 +114,7 @@ class BaseRoom:
self.game.hold_until = hold
def broadcast(self):
if not self._broadcast_pending:
if not self._broadcast_pending and not self.closed:
self._broadcast_pending = True
asyncio.get_running_loop().create_task(self._broadcast())
@@ -130,6 +133,8 @@ class BaseRoom:
if self._timer:
self._timer.cancel()
self._timer = None
if self.closed:
return
deadline = self.game.deadline
if deadline is not None:
when = max(deadline, self.game.hold_until or 0)
@@ -151,8 +156,10 @@ class BaseRoom:
self._schedule_timer()
def close(self):
self.closed = True
if self._timer:
self._timer.cancel()
self._timer = None
for h in self._side_timers:
h.cancel()
if self._voice_task:
@@ -210,7 +217,7 @@ class BaseRoom:
def say(self, text: str | None, blocking: bool = True, voice: str | None = None):
"""Læg en replik i kø. Lyden genereres med det samme, men udgives i rækkefølge.
blocking=False: en sidebemærkning, som ikke holder skærmen tilbage (fx "det var sandheden")."""
if not text:
if not text or self.closed:
return
if self._voice_queue is None:
self._voice_queue = asyncio.Queue()
+2
View File
@@ -55,6 +55,8 @@ class TegnRoom(rooms.BaseRoom):
for h in self._beat_timers:
h.cancel()
self._beat_timers = []
if self.closed:
return
g = self.game
if g.phase != "reveal":
return
+24
View File
@@ -181,3 +181,27 @@ class SwitchGameTests(TransactionTestCase):
self.assertEqual((st["game"], st["me"]["role"], st["me"]["name"]), ("fof", "audience", "Spiller9"))
await phone.disconnect()
room.close()
async def test_a_closed_room_stays_asleep(self):
"""Sene opgaver i det gamle rum (speaker, dommer, timere) må ikke vække det efter et skift midt i et spil."""
await asyncio.to_thread(make_bank)
resp = await self.async_client.post("/host/new", {"game": "fof"}, **HOST)
old = rooms.get_room(resp["Location"].rstrip("/").rsplit("/", 1)[-1])
for n in ("A", "B"):
old.game.join(n)
old.game.start(None, 0)
old.changed()
self.assertIsNotNone(old._timer) # kategorien har en deadline
create_tegn_room("agw", "http://testserver", code=old.code)
self.assertTrue(old.closed)
self.assertIsNone(old._timer)
await asyncio.sleep(0.01) # udsendelsen fra før skiftet er sendt (til ingen)
old.changed() # som når en sen opgave er færdig
old._schedule_timer()
old.say("en sen replik")
old.broadcast()
self.assertIsNone(old._timer)
self.assertFalse(old._broadcast_pending)
self.assertIsNone(old._voice_queue)
self.assertIsInstance(rooms.get_room(old.code), TegnRoom)
rooms.get_room(old.code).close()