Skift spil i samme rum: "Vælg andet spil" efter podiet
Podiet har nu "Spil igen" og "Vælg andet spil". Det sidste går til menuen med rummets kode (/host/?code=ABCD); menuen viser rummet og hvor mange der venter, og Start sender koden med. Sømmen ligger i rooms.add_room: får et nyt rum en kode, der allerede kører, overtager det nye rum spillerne og forbindelserne (BaseRoom.take_over), og det gamle lukkes (timere og stemmekø). Spillerne joines i det nye spil i den rækkefølge de kom, med samme navn og token; dem over det nye spils max bliver publikum. Hver forbindelses room og member_id peges om, så ingen consumer hænger fast i det gamle rum, og telefonerne får det nye spils lobby med det samme uden at joine igen. En telefon, der genindlæser, kommer ind med sit gamle token. Test (WebSocket): et helt Fup og Fakta-spil til podiet, skift til Tegn og Gæt i samme rum, og et helt Tegn og Gæt-spil på de samme forbindelser; og 10 spillere til Fup og Fakta (8 spiller, resten er publikum). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -158,6 +158,28 @@ class BaseRoom:
|
||||
if self._voice_task:
|
||||
self._voice_task.cancel()
|
||||
|
||||
def take_over(self, old: "BaseRoom"):
|
||||
"""Skift af spil i samme rum: spillerne kommer med i det nye spils lobby med samme navn og token, så
|
||||
telefonerne følger med uden at joine igen. Er der flere end det nye spils max, bliver resten publikum
|
||||
(spillerne først, i den rækkefølge de kom). Forbindelserne flyttes, så ingen peger på det gamle rum."""
|
||||
g, ids = self.game, {}
|
||||
members = sorted(old.game.players.values(), key=lambda p: p.joined_order) + \
|
||||
sorted(old.game.audience.values(), key=lambda p: p.joined_order)
|
||||
for m in members:
|
||||
try:
|
||||
p = g.join(m.name)
|
||||
except engine.GameError: # navnet duer ikke i det nye spil: telefonen må joine igen
|
||||
log.warning("Rum %s: %s kom ikke med i det nye spil", self.code, m.name)
|
||||
continue
|
||||
p.token, p.connected = m.token, m.connected
|
||||
ids[m.id] = p.id
|
||||
g.drain_events() # ingen velkomst til hver enkelt: de var her i forvejen
|
||||
for conn in old.connections:
|
||||
conn.room = self
|
||||
if getattr(conn, "member_id", None):
|
||||
conn.member_id = ids.get(conn.member_id, "-") # "-": findes ikke, så telefonen beder om at joine
|
||||
self.connections, old.connections = old.connections, set()
|
||||
|
||||
def handle_events(self):
|
||||
self.game.drain_events()
|
||||
|
||||
@@ -732,6 +754,7 @@ def new_code() -> str:
|
||||
|
||||
def create_room(questions: list[dict], host_username: str, public_url: str, fest: bool = False,
|
||||
code: str | None = None) -> Room:
|
||||
"""code: et kørende rums kode = skift spil i det rum (se add_room)."""
|
||||
cleanup()
|
||||
code = code or new_code()
|
||||
game = Game(code, questions, join_url=f"{public_url}/?code={code}", fest=fest)
|
||||
@@ -742,7 +765,13 @@ def create_room(questions: list[dict], host_username: str, public_url: str, fest
|
||||
|
||||
|
||||
def add_room(room: BaseRoom):
|
||||
"""Læg rummet i kodelisten. Kører der allerede et rum med koden, er det et skift af spil: det nye rum
|
||||
overtager spillerne og forbindelserne, og det gamle lukkes."""
|
||||
old = _rooms.get(room.code)
|
||||
_rooms[room.code] = room
|
||||
if old is not None and old is not room:
|
||||
room.take_over(old)
|
||||
old.close()
|
||||
|
||||
|
||||
def get_room(code: str) -> Room | None:
|
||||
|
||||
@@ -414,7 +414,8 @@
|
||||
'<section class="fin-side" data-k="side">' +
|
||||
(aw ? '<h2>Priser</h2><ul class="awards">' + aw + '</ul>' : '') +
|
||||
(rest ? '<ol class="rest">' + rest + '</ol>' : '') +
|
||||
'<button class="btn btn-go btn-xl" data-act="play_again">Spil igen</button></section>';
|
||||
'<div class="fin-again"><button class="btn btn-go btn-xl" data-act="play_again">Spil igen</button>' +
|
||||
'<a class="btn fin-other" href="/host/?code=' + encodeURIComponent(state.code || '') + '">Vælg andet spil</a></div></section>';
|
||||
}
|
||||
|
||||
/* ---------- effekter: et lag over skærmen, som patch ikke rører ---------- */
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
return '<span class="mn-fact">👥 ' + players + ' spillere</span><span class="mn-fact">⏱ ca. ' + esc(g.minutes) + ' min</span>';
|
||||
}
|
||||
|
||||
function menuScreen(games) {
|
||||
return '<header class="mn-head" data-k="mh"><h1>Weirsøe Party Protocol</h1><p>Vælg et spil</p></header>' +
|
||||
function menuScreen(games, sw) {
|
||||
var sub = sw ? 'Rum <b>' + esc(sw.code) + '</b>: ' + (sw.players === 1 ? 'én venter' : sw.players + ' venter') + '. Vælg næste spil'
|
||||
: 'Vælg et spil';
|
||||
return '<header class="mn-head" data-k="mh:' + esc(sub) + '"><h1>Weirsøe Party Protocol</h1><p>' + sub + '</p></header>' +
|
||||
'<section class="mn-cards" data-k="mc">' + games.map(function (g, i) {
|
||||
return '<button type="button" class="mn-card" data-nav data-pick="' + esc(g.key) + '" style="--i:' + i + '">' +
|
||||
'<img src="' + esc(g.icon) + '" alt="" width="120" height="120">' +
|
||||
@@ -22,7 +24,7 @@
|
||||
}).join('') + '</section>';
|
||||
}
|
||||
|
||||
function introScreen(g, values) {
|
||||
function introScreen(g, values, sw) {
|
||||
var sets = (g.settings || []).map(function (s) {
|
||||
var cur = values[s.name] != null ? values[s.name] : s.default;
|
||||
var hint = s.options.filter(function (o) { return o.value === cur; })[0];
|
||||
@@ -37,19 +39,20 @@
|
||||
'<div class="mn-intro-top" data-k="top"><img src="' + esc(g.icon) + '" alt="" width="140" height="140">' +
|
||||
'<div><h1 class="mn-iname">' + WPP.brandHtml(g.name) + '</h1><p class="mn-tag">' + esc(g.tagline) + '</p>' +
|
||||
'<p class="mn-facts">' + facts(g) + '</p></div></div>' +
|
||||
(sw && sw.players > g.max_players ? '<p class="mn-note" data-k="note">' + g.max_players + ' kan spille med, resten er publikum.</p>' : '') +
|
||||
'<ol class="mn-steps" data-k="steps">' + g.steps.map(function (s, i) { return '<li style="--i:' + i + '">' + esc(s) + '</li>'; }).join('') + '</ol>' +
|
||||
(sets ? '<div class="mn-sets" data-k="sets" data-patch>' + sets + '</div>' : '') +
|
||||
'<div class="mn-actions" data-k="act"><button type="button" class="btn mn-back" data-nav data-back>Tilbage</button>' +
|
||||
'<button type="button" class="btn btn-go btn-xl mn-start" data-nav data-start>Start ' + esc(g.name) + '</button></div></section>';
|
||||
}
|
||||
|
||||
/* view = {screen: 'menu'} eller {screen: 'intro', key, values} */
|
||||
/* view = {screen: 'menu'} eller {screen: 'intro', key, values}; view.sw = {code, players} ved skift af spil */
|
||||
function render(root, view) {
|
||||
var games = WPP.games, g = view.screen === 'intro' && WPP.game(view.key);
|
||||
var sh = WPP.shell(root);
|
||||
sh.screen.className = 'screen mn mn-' + (g ? 'intro' : 'menu');
|
||||
WPP.patch(sh.screen, '<main class="mn-main" data-k="mm:' + (g ? g.key : '') + '" data-patch>' +
|
||||
(g ? introScreen(g, view.values || {}) : menuScreen(games)) + '</main>');
|
||||
(g ? introScreen(g, view.values || {}, view.sw) : menuScreen(games, view.sw)) + '</main>');
|
||||
}
|
||||
|
||||
/* pilene: nærmeste valgbare element i den retning (målt fra midten), så både kort i et gitter og knapper
|
||||
@@ -78,6 +81,7 @@
|
||||
|
||||
function mount(root) {
|
||||
var form = document.getElementById('mn-form');
|
||||
var sw = root.getAttribute('data-code') ? { code: root.getAttribute('data-code'), players: Number(root.getAttribute('data-players')) || 0 } : null;
|
||||
var view = { screen: 'menu' };
|
||||
|
||||
function focusFirst(sel) {
|
||||
@@ -95,6 +99,7 @@
|
||||
function show(next, push) {
|
||||
var from = view;
|
||||
view = next;
|
||||
view.sw = sw;
|
||||
render(root, view);
|
||||
if (view.screen === 'intro') {
|
||||
if (push) try { history.pushState({ intro: view.key }, '', '#' + view.key); } catch (e) { /* */ }
|
||||
|
||||
@@ -500,6 +500,9 @@ button, input { font-family: inherit; }
|
||||
.rest .rk { margin-right: .3em; }
|
||||
.rest b { margin-left: .4em; color: #fff; font-weight: 600; }
|
||||
.fin-side .btn { align-self: flex-start; margin-top: .3em; }
|
||||
.fin-again { display: flex; gap: .6em; flex-wrap: wrap; align-items: center; }
|
||||
.fin-again .btn { margin-top: .3em; }
|
||||
.fin-other { background: var(--card); color: var(--ink); box-shadow: inset 0 0 0 2px var(--card-b); text-decoration: none; font-size: 1.2em; }
|
||||
|
||||
/* host_new */
|
||||
.landing { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px; text-align: center; padding: 24px 16px; }
|
||||
@@ -1044,3 +1047,4 @@ button, input { font-family: inherit; }
|
||||
@keyframes mn-in { from { opacity: 0; transform: translateY(14px); } }
|
||||
@media (max-width: 640px) { .mn-who { position: static; padding-bottom: 12px; } .mn-intro-top { flex-direction: column; text-align: center; } .mn-intro .mn-tag { text-align: center; } .mn-intro .mn-facts { justify-content: center; } }
|
||||
@media (prefers-reduced-motion: reduce) { .mn-card, .mn-intro, .mn-steps li { animation: none; } }
|
||||
.mn-note { margin: 0; padding: 10px 18px; border-radius: 14px; background: rgba(255, 210, 63, .14); color: var(--yellow); font-weight: 600; }
|
||||
|
||||
@@ -271,6 +271,7 @@ def warm_texts(themes: list[dict] | None = None) -> list[str]:
|
||||
|
||||
def create_tegn_room(host_username: str, public_url: str, code: str | None = None,
|
||||
show_rules: bool = True) -> TegnRoom:
|
||||
"""code: et kørende rums kode = skift spil i det rum (se rooms.add_room)."""
|
||||
rooms.cleanup()
|
||||
code = code or rooms.new_code()
|
||||
game = Game(code, load_themes(), join_url=f"{public_url}/?code={code}")
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
{% load static %}
|
||||
{% block body_class %}page-menu{% endblock %}
|
||||
{% block content %}
|
||||
<div id="menu"></div>
|
||||
<div id="menu"{% if switch %} data-code="{{ switch.code }}" data-players="{{ switch.players }}"{% endif %}></div>
|
||||
{# menu.js sender denne formular med spillet og dets indstillinger #}
|
||||
<form id="mn-form" method="post" action="/host/new" hidden>{% csrf_token %}<input type="hidden" name="game"></form>
|
||||
<form id="mn-form" method="post" action="/host/new" hidden>{% csrf_token %}<input type="hidden" name="game">{% if switch %}<input type="hidden" name="code" value="{{ switch.code }}">{% endif %}</form>
|
||||
<noscript>
|
||||
<main class="landing">
|
||||
<h1>Weirsøe Party Protocol</h1>
|
||||
{# hvert spil er sin egen knap: Enter/OK på en fokuseret radio sender formularen med den afkrydsede, ikke den fokuserede #}
|
||||
<form method="post" action="/host/new" class="new-game games">{% csrf_token %}
|
||||
<form method="post" action="/host/new" class="new-game">{% csrf_token %}{% if switch %}<input type="hidden" name="code" value="{{ switch.code }}">{% endif %}
|
||||
{% for g in games %}<button type="submit" name="game" value="{{ g.key }}" class="btn btn-go">Start {{ g.name }}</button>
|
||||
{% endfor %}
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
"""Skift spil i samme rum: Fup og Fakta til podiet, "Vælg andet spil", Tegn og Gæt med de samme telefoner."""
|
||||
|
||||
import asyncio
|
||||
|
||||
from channels.testing.websocket import WebsocketCommunicator
|
||||
from django.test import TransactionTestCase, override_settings
|
||||
|
||||
from fupogfakta.engine import Game as FofGame
|
||||
from partyhub.asgi import application
|
||||
from party import rooms
|
||||
from party.rooms import Room
|
||||
from party.tegn import TegnRoom, create_tegn_room
|
||||
from party.tests.test_tegn import FAST, STROKE, recv_task
|
||||
from party.tests.test_ws import HOST_HEADERS, make_bank, recv_until
|
||||
from tegnoggaet import engine as tegn_engine
|
||||
from tegnoggaet.engine import Game as TegnGame
|
||||
|
||||
HOST = {"headers": {"x-authentik-username": "agw"}, "REMOTE_ADDR": "127.0.0.1"}
|
||||
FOF_WAIT = {"rules", "intro", "present", "reveal", "scores"} # faser, hvor kun tiden (eller værten) rykker
|
||||
|
||||
|
||||
async def drain(comm, wait=0.01):
|
||||
"""Tøm køen; den seneste tilstand gemmes i comm.last_state."""
|
||||
while not await comm.receive_nothing(timeout=wait):
|
||||
msg = await comm.receive_json_from()
|
||||
if msg["type"] == "state":
|
||||
comm.last_state = msg["state"]
|
||||
|
||||
|
||||
async def last_state(comm):
|
||||
await drain(comm, wait=0.1)
|
||||
return comm.last_state
|
||||
|
||||
|
||||
@override_settings(TRUSTED_PROXIES=["127.0.0.1"], DEV_HOST_USER=None)
|
||||
class SwitchGameTests(TransactionTestCase):
|
||||
def setUp(self):
|
||||
rooms.all_rooms().clear()
|
||||
for cls in (FofGame, TegnGame):
|
||||
self.addCleanup(setattr, cls, "show_rules", cls.show_rules)
|
||||
cls.show_rules = False
|
||||
for k, v in FAST.items():
|
||||
self.addCleanup(setattr, tegn_engine, k, getattr(tegn_engine, k))
|
||||
setattr(tegn_engine, k, v)
|
||||
|
||||
async def _host(self, code):
|
||||
comm = WebsocketCommunicator(application, f"/ws/host/{code}/", headers=HOST_HEADERS)
|
||||
comm.scope["client"] = ("127.0.0.1", 5000)
|
||||
self.assertTrue((await comm.connect())[0])
|
||||
return comm
|
||||
|
||||
async def _play_fof(self, code, host, phones):
|
||||
"""Telefonerne spiller hver handling over WebSocket; værten springer ventefaserne over."""
|
||||
acted = set()
|
||||
for _ in range(2000):
|
||||
room = rooms.get_room(code)
|
||||
g = room.game
|
||||
if g.phase == "final":
|
||||
return
|
||||
key = g.phase_key()
|
||||
if g.phase == "category":
|
||||
c, _ = next(p for p in phones if p[1]["player_id"] == g.chooser_id)
|
||||
if (key, "cat") not in acted:
|
||||
acted.add((key, "cat"))
|
||||
await c.send_json_to({"action": "choose_category", "category_id": g.category_options[0]["id"]})
|
||||
elif g.phase == "lie":
|
||||
for i, (c, j) in enumerate(phones):
|
||||
if (key, i) not in acted:
|
||||
acted.add((key, i))
|
||||
await c.send_json_to({"action": "submit_lie", "text": f"løgn {i} {key}"[:30]})
|
||||
elif g.phase == "guess":
|
||||
truth = next(a for a in g.answers if a.is_truth)
|
||||
for i, (c, j) in enumerate(phones):
|
||||
if (key, i) not in acted:
|
||||
acted.add((key, i))
|
||||
await c.send_json_to({"action": "guess", "answer_id": truth.id})
|
||||
elif g.phase in FOF_WAIT and (key, "skip") not in acted:
|
||||
acted.add((key, "skip"))
|
||||
await host.send_json_to({"action": "skip"})
|
||||
await asyncio.sleep(0.02)
|
||||
for c, _ in phones:
|
||||
await drain(c)
|
||||
await drain(host)
|
||||
self.fail(f"Fup og Fakta nåede aldrig podiet (fase {rooms.get_room(code).game.phase})")
|
||||
|
||||
async def test_fof_then_tegn_in_the_same_room(self):
|
||||
await asyncio.to_thread(make_bank)
|
||||
resp = await self.async_client.post("/host/new", {"game": "fof"}, **HOST)
|
||||
code = resp["Location"].rstrip("/").rsplit("/", 1)[-1]
|
||||
fof_room = rooms.get_room(code)
|
||||
self.assertIsInstance(fof_room, Room)
|
||||
host = await self._host(code)
|
||||
phones = []
|
||||
for name in ["Asger", "Sus", "Jørn"]:
|
||||
c = WebsocketCommunicator(application, f"/ws/play/{code}/")
|
||||
await c.connect()
|
||||
await c.send_json_to({"action": "join", "name": name})
|
||||
phones.append((c, await recv_until(c, "joined")))
|
||||
await phones[0][0].send_json_to({"action": "start"})
|
||||
await self._play_fof(code, host, phones)
|
||||
st = await last_state(phones[1][0])
|
||||
self.assertEqual((st["game"], st["phase"]), ("fof", "final"))
|
||||
self.assertEqual(len(st["final"]["ranking"]), 3)
|
||||
self.assertEqual(len(fof_room.game.used_questions), 7) # hele spillet: 3 + 3 + finalen
|
||||
self.assertTrue(all(p.score > 0 for p in fof_room.game.players.values())) # alle fandt sandheden
|
||||
|
||||
# Værten vælger et andet spil: menuen kender rummet, og Start sender koden med
|
||||
await host.disconnect() # storskærmen går til menuen
|
||||
menu = await self.async_client.get(f"/host/?code={code}", **HOST)
|
||||
self.assertEqual(menu.context["switch"], {"code": code, "players": 3})
|
||||
self.assertContains(menu, f'name="code" value="{code}"')
|
||||
resp = await self.async_client.post("/host/new", {"game": "tegn", "code": code, "rules": "0"}, **HOST)
|
||||
self.assertEqual(resp["Location"], f"/host/{code}/")
|
||||
room = rooms.get_room(code)
|
||||
self.assertIsInstance(room, TegnRoom)
|
||||
self.assertEqual(fof_room.connections, set()) # det gamle rum har ingen forbindelser tilbage
|
||||
self.assertIsNone(fof_room._timer)
|
||||
|
||||
# Telefonerne får det nye spils lobby uden at joine igen: samme navne, samme VIP, samme token
|
||||
ids = {}
|
||||
for c, j in phones:
|
||||
st = await recv_until(c, "state", "lobby")
|
||||
st = st["state"]
|
||||
self.assertEqual(st["game"], "tegn")
|
||||
self.assertEqual([p["name"] for p in st["players"]], ["Asger", "Sus", "Jørn"])
|
||||
self.assertEqual(room.game._member(st["me"]["id"]).token, j["token"])
|
||||
ids[j["player_id"]] = st["me"]["id"]
|
||||
self.assertEqual(room.game.vip_id(), ids[phones[0][1]["player_id"]])
|
||||
self.assertTrue(all(p.score == 0 for p in room.game.players.values()))
|
||||
|
||||
# ... og spiller Tegn og Gæt til podiet på de samme forbindelser
|
||||
host = await self._host(code)
|
||||
await phones[0][0].send_json_to({"action": "start"})
|
||||
st = (await recv_until(host, "state", "theme"))["state"]
|
||||
await host.send_json_to({"action": "choose_theme", "theme_id": st["theme"]["options"][0]["id"]})
|
||||
for i, (c, _) in enumerate(phones):
|
||||
await recv_task(c, "word")
|
||||
await c.send_json_to({"action": "submit_word", "text": f"ord {i}"})
|
||||
for c, _ in phones:
|
||||
await recv_task(c, "draw")
|
||||
await c.send_json_to({"action": "submit_drawing", "strokes": [STROKE]})
|
||||
for c, _ in phones:
|
||||
await recv_task(c, "guess")
|
||||
await c.send_json_to({"action": "submit_guess", "text": "en hest"})
|
||||
st = (await recv_until(host, "state", "final", limit=80))["state"]
|
||||
self.assertEqual((st["game"], len(st["final"]["ranking"])), ("tegn", 3))
|
||||
|
||||
# En telefon, der genindlæser, kommer ind med sit gamle token
|
||||
c0, j0 = phones[0]
|
||||
await c0.disconnect()
|
||||
again = WebsocketCommunicator(application, f"/ws/play/{code}/")
|
||||
await again.connect()
|
||||
await again.send_json_to({"action": "resume", "token": j0["token"]})
|
||||
self.assertEqual((await recv_until(again, "joined"))["player_id"], ids[j0["player_id"]])
|
||||
for c, _ in phones[1:]:
|
||||
await c.disconnect()
|
||||
await again.disconnect()
|
||||
await host.disconnect()
|
||||
room.close()
|
||||
|
||||
async def test_players_over_the_new_max_become_audience(self):
|
||||
tegn = create_tegn_room("agw", "http://testserver")
|
||||
names = [f"Spiller{i}" for i in range(10)]
|
||||
members = [tegn.game.join(n) for n in names] + [tegn.game.join("Tilskuer")]
|
||||
self.assertEqual((len(tegn.game.players), len(tegn.game.audience)), (10, 1))
|
||||
phone = WebsocketCommunicator(application, f"/ws/play/{tegn.code}/")
|
||||
await phone.connect()
|
||||
await phone.send_json_to({"action": "resume", "token": members[9].token}) # tiende spiller
|
||||
await recv_until(phone, "joined")
|
||||
await recv_until(phone, "state", "lobby")
|
||||
|
||||
room = rooms.create_room([{"category": "K", "prompt": "Hvad _____?", "answer": "x"}], "agw",
|
||||
"http://testserver", code=tegn.code)
|
||||
room.changed()
|
||||
g = room.game
|
||||
self.assertEqual([p.name for p in g.players.values()], names[:8]) # fof: højst 8, i den rækkefølge de kom
|
||||
self.assertEqual([p.name for p in g.audience.values()], names[8:] + ["Tilskuer"])
|
||||
self.assertEqual({p.token for p in list(g.players.values()) + list(g.audience.values())},
|
||||
{m.token for m in members})
|
||||
st = (await recv_until(phone, "state", "lobby"))["state"]
|
||||
self.assertEqual((st["game"], st["me"]["role"], st["me"]["name"]), ("fof", "audience", "Spiller9"))
|
||||
await phone.disconnect()
|
||||
room.close()
|
||||
+10
-2
@@ -37,7 +37,11 @@ def play(request):
|
||||
def host_new_page(request):
|
||||
if not request.host_identity:
|
||||
return _forbidden()
|
||||
return render(request, "party/host_new.html", {"username": request.host_identity.username, **_game_page(None)})
|
||||
# ?code=ABCD: "Vælg andet spil" fra podiet; det næste spil starter i samme rum med de samme spillere
|
||||
room = get_room(request.GET.get("code", ""))
|
||||
switch = {"code": room.code, "players": len(room.game.players) + len(room.game.audience)} if room else None
|
||||
return render(request, "party/host_new.html", {"username": request.host_identity.username, "switch": switch,
|
||||
**_game_page(None)})
|
||||
|
||||
|
||||
@require_GET
|
||||
@@ -59,10 +63,14 @@ async def host_new(request):
|
||||
game = games.GAMES.get(request.POST.get("game") or next(iter(games.GAMES)))
|
||||
if not game:
|
||||
return HttpResponse("Det spil findes ikke.", status=400)
|
||||
running = get_room(request.POST.get("code", "")) # skift spil i et kørende rum (se rooms.add_room)
|
||||
try:
|
||||
room = await game.create(ident.username, settings.PUBLIC_URL, game.clean_settings(request.POST))
|
||||
room = await game.create(ident.username, settings.PUBLIC_URL, game.clean_settings(request.POST),
|
||||
code=running.code if running else None)
|
||||
except games.Unavailable as e:
|
||||
return HttpResponse(str(e), status=503)
|
||||
if running:
|
||||
room.changed() # telefonerne får det nye spils lobby med det samme
|
||||
return redirect("party:host", code=room.code)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user