From 5ca8b0bb645893b3b95811c2ddfb25ab9fffd3c9 Mon Sep 17 00:00:00 2001 From: Richard Higgins Date: Thu, 14 May 2026 13:04:03 -0700 Subject: [PATCH] chore: clean up among them roster checks --- among_them/server.nim | 54 ++++++++----------------------------------- among_them/sim.nim | 53 ++++++++---------------------------------- 2 files changed, 18 insertions(+), 89 deletions(-) diff --git a/among_them/server.nim b/among_them/server.nim index e1b2a876..08a234ad 100644 --- a/among_them/server.nim +++ b/among_them/server.nim @@ -753,14 +753,6 @@ proc identityIsKicked(identity: string): bool = identity in appState.kickedIdentities or rewardIdentity in appState.kickedIdentities -proc identityIsConnected(identity: string): bool = - ## Returns true when an identity already has a live websocket. - {.gcsafe.}: - withLock appState.lock: - for _, address in appState.playerAddresses.pairs: - if address == identity: - return true - proc respondKicked(request: Request) = ## Rejects a kicked player before upgrading to a WebSocket. var headers: HttpHeaders @@ -769,20 +761,6 @@ proc respondKicked(request: Request) = headers["Connection"] = "close" request.respond(409, headers, "player was kicked\n") -proc respondForbidden(request: Request, body: string) = - ## Rejects an unauthorized player request before WebSocket upgrade. - var headers: HttpHeaders - headers["Content-Type"] = "text/plain; charset=utf-8" - headers["Cache-Control"] = "no-cache" - headers["Connection"] = "close" - request.respond(403, headers, body) - -proc playerJoinAllowed(request: Request, identity: string, slot: int, token: string): bool = - ## Checks configured slot auth before upgrading the player WebSocket. - {.gcsafe.}: - withLock appState.lock: - result = appState.config.playerJoinAllowed(identity, slot, token) - proc httpHandler(request: Request) = if request.path == HealthPath and request.httpMethod == "GET": var headers: HttpHeaders @@ -798,12 +776,6 @@ proc httpHandler(request: Request) = if identity.identityIsKicked(): request.respondKicked() return - if identity.identityIsConnected(): - request.respondForbidden("player already connected\n") - return - if not request.playerJoinAllowed(identity, slot, token): - request.respondForbidden("player token rejected\n") - return let websocket = request.upgradeToWebSocket() {.gcsafe.}: withLock appState.lock: @@ -820,12 +792,6 @@ proc httpHandler(request: Request) = if identity.identityIsKicked(): request.respondKicked() return - if identity.identityIsConnected(): - request.respondForbidden("player already connected\n") - return - if not request.playerJoinAllowed(identity, slot, token): - request.respondForbidden("player token rejected\n") - return let websocket = request.upgradeToWebSocket() {.gcsafe.}: withLock appState.lock: @@ -1190,10 +1156,8 @@ proc runServerLoop*( identity in appState.kickedIdentities: sim.removePlayer(websocket) socketsToClose.add(websocket) - elif sim.playerAddressOccupied(address): - sim.removePlayer(websocket) - socketsToClose.add(websocket) - elif sim.phase == Lobby and sim.canAddPlayer(): + elif sim.phase == Lobby and + (sim.canAddPlayer() or slot >= 0 or token.len > 0): try: appState.playerIndices[websocket] = sim.addPlayer( address, @@ -1299,13 +1263,6 @@ proc runServerLoop*( reconnectSockets.add(websocket) appState.spectators = @[] for websocket in reconnectSockets: - if not sim.canAddPlayer(): - if websocket in appState.playerViewers: - appState.playerIndices[websocket] = -1 - else: - appState.spectators.add(websocket) - appState.playerIndices.del(websocket) - continue let address = appState.playerAddresses.getOrDefault( websocket, "unknown" @@ -1313,6 +1270,13 @@ proc runServerLoop*( let slot = appState.playerSlots.getOrDefault(websocket, -1) token = appState.playerTokens.getOrDefault(websocket, "") + if not sim.canAddPlayer() and slot < 0 and token.len == 0: + if websocket in appState.playerViewers: + appState.playerIndices[websocket] = -1 + else: + appState.spectators.add(websocket) + appState.playerIndices.del(websocket) + continue try: appState.playerIndices[websocket] = sim.addPlayer( address, diff --git a/among_them/sim.nim b/among_them/sim.nim index b6b29ad9..7208326a 100644 --- a/among_them/sim.nim +++ b/among_them/sim.nim @@ -1478,6 +1478,14 @@ proc canAddPlayer*(sim: SimServer): bool = ## Returns whether the game has room for another player. sim.players.len < sim.config.playerSlotLimit() +proc playerLimitError(config: GameConfig): string = + ## Returns a user-facing message for the current player cap. + if config.closedRoster: + let limit = config.playerSlotLimit() + return "Configured roster is full (" & $limit & + (if limit == 1: " player)." else: " players).") + "can't do more than " & $MaxPlayers & " players." + proc slotConfig(config: GameConfig, slotIndex: int): PlayerSlotConfig = ## Returns one slot config or an empty config for missing entries. if slotIndex >= 0 and slotIndex < config.slots.len: @@ -1537,49 +1545,6 @@ proc configuredPlayerName*(config: GameConfig, requestedSlot: int, token: string return slot.name "" -proc matchingConfiguredSlot( - config: GameConfig, - address, - token: string -): int = - ## Returns a configured slot matched by name or token without occupancy checks. - for i in 0 ..< config.slots.len: - let slot = config.slots[i] - let couldMatchName = slot.name.len > 0 and slot.name == address - let couldMatchToken = slot.token.len > 0 and slot.token == token - if (couldMatchName or couldMatchToken) and - config.slotAuthMatches(i, address, token): - return i - -1 - -proc conflictingConfiguredSlot( - config: GameConfig, - address, - token: string -): int = - ## Returns a configured slot matched by only one required credential. - for i in 0 ..< config.slots.len: - let slot = config.slots[i] - let matchedName = slot.name.len > 0 and slot.name == address - let matchedToken = - slot.token.len > 0 and token.len > 0 and slot.token == token - if (matchedName or matchedToken) and - not config.slotAuthMatches(i, address, token): - return i - -1 - -proc playerJoinAllowed*(config: GameConfig, address: string, requestedSlot: int, token: string): bool = - ## Returns whether a player websocket request can pass configured slot auth. - if requestedSlot >= config.playerSlotLimit(): - return false - if requestedSlot >= 0: - return config.slotAuthMatches(requestedSlot, address, token) - if config.matchingConfiguredSlot(address, token) >= 0: - return true - if config.conflictingConfiguredSlot(address, token) >= 0: - return false - not config.closedRoster - proc slotOccupied(sim: SimServer, slotIndex: int): bool = ## Returns true when a player already owns a slot. for player in sim.players: @@ -1793,7 +1758,7 @@ proc addPlayer*( ): int = ## Adds one player, optionally validating and using a requested slot. if not sim.canAddPlayer(): - raise newException(AmongThemError, "can't do more than 16 players.") + raise newException(AmongThemError, sim.config.playerLimitError()) if sim.playerAddressOccupied(address): raise newException( AmongThemError,