pridanie pocty online/available hracov
This commit is contained in:
+101
-7
@@ -253,8 +253,52 @@ def public_games() -> list:
|
||||
|
||||
# --- emit helpers ---------------------------------------------------------
|
||||
|
||||
def _online_player_ids() -> set[int]:
|
||||
"""Distinct logged-in players currently connected.
|
||||
|
||||
Counting *players* (by account id), not raw sockets, is what makes the
|
||||
lobby counters stable and meaningful:
|
||||
* one person with several tabs/devices counts once;
|
||||
* a not-yet-logged-in visitor (still on the auth screen) is not a player
|
||||
and is not counted;
|
||||
* reconnect churn and phantom sockets lingering until the engine.io ping
|
||||
timeout no longer inflate the number, because the identity is counted,
|
||||
not the transient connection.
|
||||
"""
|
||||
return {acc["player_id"] for acc in accounts.values()}
|
||||
|
||||
|
||||
def _in_game_player_ids() -> set[int]:
|
||||
"""Players who currently occupy a seat in a game (any of their sockets is
|
||||
seated) -- 'busy', i.e. not available to start/join another game."""
|
||||
ids: set[int] = set()
|
||||
for sid in sessions:
|
||||
acc = accounts.get(sid)
|
||||
if acc is not None:
|
||||
ids.add(acc["player_id"])
|
||||
return ids
|
||||
|
||||
|
||||
def online_count() -> int:
|
||||
"""Number of distinct logged-in players online (in the lobby or in a game)."""
|
||||
return len(_online_player_ids())
|
||||
|
||||
|
||||
def available_count() -> int:
|
||||
"""Online players not currently seated in any game (free to join/create)."""
|
||||
return len(_online_player_ids() - _in_game_player_ids())
|
||||
|
||||
|
||||
async def broadcast_lobby():
|
||||
await sio.emit("get_games", {"games": public_games()}, room=LOBBY)
|
||||
await sio.emit(
|
||||
"get_games",
|
||||
{
|
||||
"games": public_games(),
|
||||
"online_count": online_count(),
|
||||
"available_count": available_count(),
|
||||
},
|
||||
room=LOBBY,
|
||||
)
|
||||
|
||||
|
||||
async def send_game_status(gid: str):
|
||||
@@ -455,6 +499,13 @@ async def send_error_room(gid: str, message: str):
|
||||
await sio.emit("error", {"error": message}, room=gid)
|
||||
|
||||
|
||||
def _public_identity(account: dict) -> dict:
|
||||
"""The client only needs `player_id` to compare against game rosters (see
|
||||
`isMember` in frontend/src/pages/GameList.tsx) -- `username` stays purely
|
||||
server-side (used to name the seat on register_player/rejoin_game etc.)."""
|
||||
return {"player_id": account["player_id"]}
|
||||
|
||||
|
||||
# --- connection lifecycle -------------------------------------------------
|
||||
|
||||
@sio.event
|
||||
@@ -465,8 +516,9 @@ async def connect(sid, environ, auth=None):
|
||||
identity = await auth_module.player_by_token(token)
|
||||
if identity is not None:
|
||||
accounts[sid] = identity
|
||||
await sio.emit("login", {"player": identity}, to=sid)
|
||||
await sio.emit("get_games", {"games": public_games()}, to=sid)
|
||||
await sio.emit("login", {"player": _public_identity(identity)}, to=sid)
|
||||
# Broadcast (not just emit to sid) so everyone's online-players count stays live.
|
||||
await broadcast_lobby()
|
||||
|
||||
|
||||
@sio.event
|
||||
@@ -478,6 +530,7 @@ async def disconnect(sid):
|
||||
player = game.player_by_sid(sid)
|
||||
if player is not None:
|
||||
await _mark_player_offline(game, player)
|
||||
# accounts/sessions already updated above, so the lobby counters are correct.
|
||||
await broadcast_lobby()
|
||||
|
||||
|
||||
@@ -513,7 +566,11 @@ async def confirm_account(sid, username, code):
|
||||
return await send_error(sid, str(exc))
|
||||
accounts[sid] = {"player_id": identity["player_id"], "username": identity["username"]}
|
||||
await _record_login_event(sid, identity["player_id"])
|
||||
await sio.emit("login", {"player": accounts[sid], "token": identity["token"]}, to=sid)
|
||||
await sio.emit(
|
||||
"login", {"player": _public_identity(accounts[sid]), "token": identity["token"]}, to=sid
|
||||
)
|
||||
# A new player just came online -> refresh everyone's lobby counters.
|
||||
await broadcast_lobby()
|
||||
|
||||
|
||||
@sio.on("login")
|
||||
@@ -533,7 +590,28 @@ async def login(sid, username, code):
|
||||
return await send_error(sid, str(exc))
|
||||
accounts[sid] = {"player_id": identity["player_id"], "username": identity["username"]}
|
||||
await _record_login_event(sid, identity["player_id"])
|
||||
await sio.emit("login", {"player": accounts[sid], "token": identity["token"]}, to=sid)
|
||||
await sio.emit(
|
||||
"login", {"player": _public_identity(accounts[sid]), "token": identity["token"]}, to=sid
|
||||
)
|
||||
# A new player just came online -> refresh everyone's lobby counters.
|
||||
await broadcast_lobby()
|
||||
|
||||
|
||||
@sio.on("logout")
|
||||
async def logout(sid):
|
||||
"""De-authenticate this connection without touching the transport.
|
||||
|
||||
The previous approach (client calling `socket.disconnect()` then
|
||||
immediately `socket.connect()`) was racy: python-socketio's disconnect
|
||||
isn't instantaneous (the old sid can briefly linger past its ping-timeout
|
||||
while a brand new sid is already connecting), so for that window BOTH
|
||||
sids were counted in the lobby online/available totals -- exactly the
|
||||
transient over-count reported. Simply dropping the account association
|
||||
server-side avoids any connect/disconnect churn entirely.
|
||||
"""
|
||||
accounts.pop(sid, None)
|
||||
# A player just went offline -> refresh everyone's lobby counters.
|
||||
await broadcast_lobby()
|
||||
|
||||
|
||||
# --- lobby ----------------------------------------------------------------
|
||||
@@ -550,7 +628,15 @@ async def create_game(sid, name):
|
||||
|
||||
@sio.on("get_games")
|
||||
async def get_games(sid, *args):
|
||||
await sio.emit("get_games", {"games": public_games()}, to=sid)
|
||||
await sio.emit(
|
||||
"get_games",
|
||||
{
|
||||
"games": public_games(),
|
||||
"online_count": online_count(),
|
||||
"available_count": available_count(),
|
||||
},
|
||||
to=sid,
|
||||
)
|
||||
|
||||
|
||||
@sio.on("register_player")
|
||||
@@ -800,7 +886,15 @@ async def restore_game(sid, gid):
|
||||
if gid in games:
|
||||
# Uz je v pamati (lobby) -- staci obnovit zoznam hier u klienta.
|
||||
await sio.emit("game_restored", {"gid": gid}, to=sid)
|
||||
return await sio.emit("get_games", {"games": public_games()}, to=sid)
|
||||
return await sio.emit(
|
||||
"get_games",
|
||||
{
|
||||
"games": public_games(),
|
||||
"online_count": online_count(),
|
||||
"available_count": available_count(),
|
||||
},
|
||||
to=sid,
|
||||
)
|
||||
|
||||
info = await history.reopen_game(gid, account["player_id"])
|
||||
if info is None:
|
||||
|
||||
Reference in New Issue
Block a user