fix rusenie hier ked vsetkych odpoji

This commit is contained in:
tim
2026-07-03 19:16:41 +02:00
parent c4800fca0e
commit 7886b3a6b8
3 changed files with 51 additions and 12 deletions
+29 -10
View File
@@ -1,3 +1,4 @@
import asyncio
import hmac
import json
import os
@@ -273,19 +274,37 @@ async def send_error(sid: str, message: str):
await sio.emit("error", {"error": message}, to=sid)
# Ako dlho prezije nezacata hra, ked su vsetci hraci naraz offline. Na mobile
# sa socket bezne strati uz pri zamknuti obrazovky, takze okamzite zmazanie
# hry rusilo lobby, v ktorom hraci len cakali so zhasnutym telefonom.
LOBBY_ABANDON_GRACE_SECONDS = 10 * 60
async def _cleanup_abandoned_lobby(gid: str):
"""Po grace periode zmaz nezacatu hru, ak sa medzitym nikto nevratil.
Podmienka sa overuje az po uplynuti casu, takze pri navrate hraca je
task neskodny no-op (netreba nic rusit)."""
await asyncio.sleep(LOBBY_ABANDON_GRACE_SECONDS)
game = games.get(gid)
if game is not None and not game.started and not any(p.connected for p in game.players):
del games[gid]
await broadcast_lobby()
async def _mark_player_offline(game: "Game", player: "Player"):
"""Mark player disconnected. An unstarted game with nobody left is cleaned
up; a started game is kept in memory so it stays in the lobby and can be
resumed (it's torn down only by end_game)."""
"""Mark player disconnected. An unstarted game with nobody left gets a
delayed cleanup (mobile sockets drop on screen lock, so an immediate
delete would kill lobbies where everyone is just waiting); a started game
is kept in memory so it stays in the lobby and can be resumed (it's torn
down only by end_game)."""
player.connected = False
if not any(p.connected for p in game.players) and not game.started:
del games[game.gid]
else:
await sio.emit(
"player_connection",
{"order": player.order, "connected": False},
room=game.gid,
)
asyncio.create_task(_cleanup_abandoned_lobby(game.gid))
await sio.emit(
"player_connection",
{"order": player.order, "connected": False},
room=game.gid,
)
def _active_game(sid: str) -> "tuple[Game, dict] | None":
+3
View File
@@ -132,6 +132,9 @@ export function setupSocketListeners() {
// Surface connection failures instead of silently buffering emits.
socket.on('connect_error', (err: Error) => {
// `active` = klient sa automaticky pokusi znova (typicky prebudeny mobil,
// kym sa siet zobudi) -- prechodne, netreba strasit toastom.
if (socket.active) return;
useGameStore.getState().setError(`Spojenie so serverom zlyhalo: ${err.message}`);
});
}
+19 -2
View File
@@ -1,3 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useGameStore } from '../store/gameStore';
import { emit } from '../lib/socket';
@@ -8,6 +9,8 @@ export default function Lobby() {
const navigate = useNavigate();
const myPlayer = useGameStore((s) => s.myPlayer);
const games = useGameStore((s) => s.games);
const [showCopied, setShowCopied] = useState(false);
const copiedTimeout = useRef<ReturnType<typeof setTimeout>>();
const game = games.find((g) => g.gid === gid);
const players = game?.players ?? [];
@@ -17,11 +20,25 @@ export default function Lobby() {
const handleLeave = () => leaveGame(navigate);
const handleCopyCode = () => {
if (gid) navigator.clipboard.writeText(`${window.location.origin}/lobby/${gid}`);
if (!gid) return;
navigator.clipboard.writeText(`${window.location.origin}/lobby/${gid}`);
setShowCopied(true);
clearTimeout(copiedTimeout.current);
copiedTimeout.current = setTimeout(() => setShowCopied(false), 1800);
};
useEffect(() => () => clearTimeout(copiedTimeout.current), []);
return (
<div className="max-w-md mx-auto p-4 pt-8 min-h-screen">
<div
className={`fixed top-4 left-1/2 -translate-x-1/2 px-4 py-2 rounded-lg bg-header border border-gold/30 text-gold text-sm shadow-lg transition-opacity duration-300 z-50 ${
showCopied ? 'opacity-100' : 'opacity-0 pointer-events-none'
}`}
>
Odkaz na hru skopírovaný
</div>
<div className="flex items-center justify-between mb-6">
<h1 className="font-serif text-2xl text-gold">{game?.name ?? 'Hra'}</h1>
<button onClick={handleLeave} className="text-sm text-green-dim hover:text-gold">
@@ -38,7 +55,7 @@ export default function Lobby() {
onClick={handleCopyCode}
className="ml-3 px-3 py-1 rounded-lg text-sm border border-gold/30 text-gold hover:bg-gold hover:text-table transition-colors"
>
Kopírovať
Kopírovať URL
</button>
</div>