async def lobby_socket_handler(request): users = request.app["users"] sockets = request.app["lobbysockets"] seeks = request.app["seeks"] ws = WebSocketResponse(heartbeat=3.0, receive_timeout=10.0) ws_ready = ws.can_prepare(request) if not ws_ready.ok: raise web.HTTPFound("/") await ws.prepare(request) session = await aiohttp_session.get_session(request) session_user = session.get("user_name") user = users[ session_user] if session_user is not None and session_user in users else None if (user is not None) and (not user.enabled): await ws.close() session.invalidate() raise web.HTTPFound("/") log.debug("-------------------------- NEW lobby WEBSOCKET by %s" % user) try: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: if msg.data == "close": log.debug("Got 'close' msg.") break else: data = json.loads(msg.data) if not data["type"] == "pong": log.debug("Websocket (%s) message: %s" % (id(ws), msg)) if data["type"] == "get_seeks": response = get_seeks(seeks) await ws.send_json(response) elif data["type"] == "create_ai_challenge": no = await is_playing(request, user, ws) if no: continue variant = data["variant"] engine = users.get("Fairy-Stockfish") if engine is None or not engine.online(): # TODO: message that engine is offline, but capture BOT will play instead engine = users.get("Random-Mover") seek = Seek(user, variant, fen=data["fen"], color=data["color"], base=data["minutes"], inc=data["increment"], byoyomi_period=data["byoyomiPeriod"], level=data["level"], rated=data["rated"], chess960=data["chess960"], alternate_start=data["alternateStart"]) # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"]) seeks[seek.id] = seek response = await new_game(request.app, engine, seek.id) await ws.send_json(response) if response["type"] != "error": gameId = response["gameId"] engine.game_queues[gameId] = asyncio.Queue() await engine.event_queue.put( challenge(seek, response)) elif data["type"] == "create_seek": no = await is_playing(request, user, ws) if no: continue print("create_seek", data) create_seek(seeks, user, data, ws) await lobby_broadcast(sockets, get_seeks(seeks)) if data.get("target"): queue = users[data["target"]].notify_queue if queue is not None: await queue.put( json.dumps({"notify": "new_challenge"})) elif data["type"] == "delete_seek": del seeks[data["seekID"]] del user.seeks[data["seekID"]] await lobby_broadcast(sockets, get_seeks(seeks)) elif data["type"] == "accept_seek": no = await is_playing(request, user, ws) if no: continue if data["seekID"] not in seeks: continue seek = seeks[data["seekID"]] # print("accept_seek", seek.as_json) response = await new_game(request.app, user, data["seekID"]) await ws.send_json(response) if seek.user.bot: gameId = response["gameId"] seek.user.game_queues[gameId] = asyncio.Queue() await seek.user.event_queue.put( challenge(seek, response)) else: await seek.ws.send_json(response) # Inform others, new_game() deleted accepted seek allready. await lobby_broadcast(sockets, get_seeks(seeks)) elif data["type"] == "lobby_user_connected": if session_user is not None: if data["username"] and data[ "username"] != session_user: log.info( "+++ Existing lobby_user %s socket connected as %s." % (session_user, data["username"])) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user response = { "type": "lobbychat", "user": "", "message": "%s joined the lobby" % session_user } else: if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user response = { "type": "lobbychat", "user": "", "message": "%s joined the lobby" % session_user } else: log.info( "+++ Existing lobby_user %s socket reconnected." % data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith("Anon-")) users[user.username] = user response = { "type": "lobbychat", "user": "", "message": "%s rejoined the lobby" % session_user } await lobby_broadcast(sockets, response) # update websocket user.lobby_sockets.add(ws) sockets[user.username] = user.lobby_sockets response = { "type": "lobby_user_connected", "username": user.username } await ws.send_json(response) response = { "type": "fullchat", "lines": list(request.app["chat"]) } await ws.send_json(response) # send game count response = { "type": "g_cnt", "cnt": request.app["g_cnt"] } await ws.send_json(response) # send user count response = { "type": "u_cnt", "cnt": online_count(users) } if len(user.game_sockets) == 0: await lobby_broadcast(sockets, response) else: await ws.send_json(response) elif data["type"] == "lobbychat": response = { "type": "lobbychat", "user": user.username, "message": data["message"] } await lobby_broadcast(sockets, response) request.app["chat"].append(response) elif data["type"] == "logout": await ws.close() elif data["type"] == "disconnect": # Used only to test socket disconnection... await ws.close(code=1009) elif msg.type == aiohttp.WSMsgType.CLOSED: log.debug( "--- Lobby websocket %s msg.type == aiohttp.WSMsgType.CLOSED" % id(ws)) break elif msg.type == aiohttp.WSMsgType.ERROR: log.error( "--- Lobby ws %s msg.type == aiohttp.WSMsgType.ERROR" % id(ws)) break else: log.debug("--- Lobby ws other msg.type %s %s" % (msg.type, msg)) except concurrent.futures._base.CancelledError: # client disconnected pass except Exception as e: log.error("!!! Lobby ws exception occured: %s" % type(e)) finally: log.debug("---fianlly: await ws.close()") await ws.close() if user is not None: if ws in user.lobby_sockets: user.lobby_sockets.remove(ws) # online user counter will be updated in quit_lobby also! if len(user.lobby_sockets) == 0: if user.username in sockets: del sockets[user.username] # not connected to lobby socket and not connected to game socket if len(user.game_sockets) == 0: response = {"type": "u_cnt", "cnt": online_count(users)} await lobby_broadcast(sockets, response) response = { "type": "lobbychat", "user": "", "message": "%s left the lobby" % user.username } await lobby_broadcast(sockets, response) await user.clear_seeks(sockets, seeks) return ws
async def round_socket_handler(request): users = request.app["users"] sockets = request.app["lobbysockets"] seeks = request.app["seeks"] games = request.app["games"] db = request.app["db"] ws = WebSocketResponse(heartbeat=3.0, receive_timeout=10.0) ws_ready = ws.can_prepare(request) if not ws_ready.ok: return web.HTTPFound("/") await ws.prepare(request) session = await aiohttp_session.get_session(request) session_user = session.get("user_name") user = users[ session_user] if session_user is not None and session_user in users else None game = None opp_ws = None log.debug("-------------------------- NEW round WEBSOCKET by %s", user) try: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: if msg.data == "close": log.debug("Got 'close' msg.") break else: data = json.loads(msg.data) # log.debug("Websocket (%s) message: %s" % (id(ws), msg)) if data["type"] == "move": # log.info("Got USER move %s %s %s" % (user.username, data["gameId"], data["move"])) game = await load_game(request.app, data["gameId"]) move = data["move"] await play_move(request.app, user, game, move, data["clocks"], data["ply"]) elif data["type"] == "analysis_move": game = await load_game(request.app, data["gameId"]) await analysis_move(request.app, user, game, data["move"], data["fen"], data["ply"]) elif data["type"] == "ready": game = await load_game(request.app, data["gameId"]) opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] if opp_player.bot: # Janggi game start have to wait for human player setup! if game.variant != "janggi" or not (game.bsetup or game.wsetup): await opp_player.event_queue.put( game.game_start) response = { "type": "gameStart", "gameId": data["gameId"] } await ws.send_json(response) else: response = { "type": "gameStart", "gameId": data["gameId"] } await ws.send_json(response) response = { "type": "user_present", "username": user.username } await round_broadcast(game, users, game.spectator_list, full=True) elif data["type"] == "board": game = await load_game(request.app, data["gameId"]) if game.variant == "janggi": if (game.bsetup or game.wsetup) and game.status <= STARTED: if game.bsetup: await ws.send_json({ "type": "setup", "color": "black", "fen": game.board.initial_fen }) elif game.wsetup: await ws.send_json({ "type": "setup", "color": "white", "fen": game.board.initial_fen }) else: board_response = game.get_board(full=True) await ws.send_json(board_response) else: board_response = game.get_board(full=True) await ws.send_json(board_response) elif data["type"] == "setup": # Janggi game starts with a prelude phase to set up horses and elephants # First the second player (Red) choses his setup! Then the first player (Blue) game = await load_game(request.app, data["gameId"]) game.board.initial_fen = data["fen"] game.initial_fen = game.board.initial_fen game.board.fen = game.board.initial_fen # print("--- Got FEN from %s %s" % (data["color"], data["fen"])) opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] game.steps[0]["fen"] = data["fen"] game.set_dests() if data["color"] == "black": game.bsetup = False response = { "type": "setup", "color": "white", "fen": data["fen"] } await ws.send_json(response) if opp_player.bot: game.board.janggi_setup("w") game.steps[0]["fen"] = game.board.initial_fen game.set_dests() else: opp_ws = users[opp_name].game_sockets[ data["gameId"]] await opp_ws.send_json(response) else: game.wsetup = False response = game.get_board(full=True) # log.info("User %s asked board. Server sent: %s" % (user.username, board_response["fen"])) await ws.send_json(response) if not opp_player.bot: opp_ws = users[opp_name].game_sockets[ data["gameId"]] await opp_ws.send_json(response) if opp_player.bot: await opp_player.event_queue.put(game.game_start) elif data["type"] == "analysis": game = await load_game(request.app, data["gameId"]) # If there is any fishnet client, use it. if len(request.app["workers"]) > 0: work_id = "".join( random.choice(string.ascii_letters + string.digits) for x in range(6)) work = { "work": { "type": "analysis", "id": work_id, }, # or: # "work": { # "type": "move", # "id": "work_id", # "level": 5 // 1 to 8 # }, "username": data["username"], "game_id": data["gameId"], # optional "position": game.board. initial_fen, # start position (X-FEN) "variant": game.variant, "chess960": game.chess960, "moves": " ".join(game.board.move_stack ), # moves of the game (UCI) "nodes": 500000, # optional limit # "skipPositions": [1, 4, 5] # 0 is the first position } request.app["works"][work_id] = work request.app["fishnet"].put_nowait( (ANALYSIS, work_id)) else: engine = users.get("Fairy-Stockfish") if (engine is not None) and engine.online: engine.game_queues[ data["gameId"]] = asyncio.Queue() await engine.event_queue.put( game.analysis_start(data["username"])) response = { "type": "roundchat", "user": "", "room": "spectator", "message": "Analysis request sent..." } await ws.send_json(response) elif data["type"] == "rematch": game = await load_game(request.app, data["gameId"]) if game is None: log.debug("Requested game %s not found!") response = { "type": "game_not_found", "username": user.username, "gameId": data["gameId"] } await ws.send_json(response) continue opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] handicap = data["handicap"] fen = "" if game.variant == "janggi" else game.initial_fen if opp_player.bot: if opp_player.username == "Random-Mover": engine = users.get("Random-Mover") else: engine = users.get("Fairy-Stockfish") if engine is None or not engine.online: # TODO: message that engine is offline, but capture BOT will play instead engine = users.get("Random-Mover") color = "w" if game.wplayer.username == opp_name else "b" if handicap: color = "w" if color == "b" else "b" seek = Seek(user, game.variant, fen=fen, color=color, base=game.base, inc=game.inc, byoyomi_period=game.byoyomi_period, level=game.level, rated=game.rated, chess960=game.chess960) seeks[seek.id] = seek response = await new_game(request.app, engine, seek.id) await ws.send_json(response) await engine.event_queue.put( challenge(seek, response)) gameId = response["gameId"] engine.game_queues[gameId] = asyncio.Queue() else: try: opp_ws = users[opp_name].game_sockets[ data["gameId"]] except KeyError: # opp disconnected pass if opp_name in game.rematch_offers: color = "w" if game.wplayer.username == opp_name else "b" if handicap: color = "w" if color == "b" else "b" seek = Seek(user, game.variant, fen=fen, color=color, base=game.base, inc=game.inc, byoyomi_period=game.byoyomi_period, level=game.level, rated=game.rated, chess960=game.chess960) seeks[seek.id] = seek response = await new_game( request.app, opp_player, seek.id) await ws.send_json(response) await opp_ws.send_json(response) else: game.rematch_offers.add(user.username) response = { "type": "offer", "message": "Rematch offer sent", "room": "player", "user": "" } game.messages.append(response) await ws.send_json(response) await opp_ws.send_json(response) elif data["type"] == "draw": game = await load_game(request.app, data["gameId"]) opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] response = await draw(games, data, agreement=opp_name in game.draw_offers) await ws.send_json(response) if opp_player.bot: if game.status > STARTED: await opp_player.game_queues[ data["gameId"]].put(game.game_end) else: try: opp_ws = users[opp_name].game_sockets[ data["gameId"]] await opp_ws.send_json(response) except KeyError: # opp disconnected pass if opp_name not in game.draw_offers: game.draw_offers.add(user.username) await round_broadcast(game, users, response) elif data["type"] == "logout": await ws.close() elif data["type"] == "byoyomi": game = await load_game(request.app, data["gameId"]) game.byo_correction += game.inc * 1000 game.byoyomi_periods[0 if data["color"] == "white" else 1] = data["period"] # print("BYOYOMI:", data) elif data["type"] in ("abort", "resign", "abandone", "flag"): game = await load_game(request.app, data["gameId"]) if data["type"] == "abort" and ( game is not None) and game.board.ply > 2: continue response = await game.game_ended(user, data["type"]) await ws.send_json(response) opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] if opp_player.bot: await opp_player.game_queues[data["gameId"] ].put(game.game_end) else: if data["gameId"] in users[opp_name].game_sockets: opp_ws = users[opp_name].game_sockets[ data["gameId"]] await opp_ws.send_json(response) await round_broadcast(game, users, response) elif data["type"] == "embed_user_connected": game = await load_game(request.app, data["gameId"]) if game is None: log.debug("Requested game %s not found!") response = { "type": "game_not_found", "gameId": data["gameId"] } await ws.send_json(response) continue else: response = {"type": "embed_user_connected"} await ws.send_json(response) elif data["type"] == "game_user_connected": game = await load_game(request.app, data["gameId"]) if session_user is not None: if data["username"] and data[ "username"] != session_user: log.info( "+++ Existing game_user %s socket connected as %s.", session_user, data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user # Update logged in users as spactators if user.username != game.wplayer.username and user.username != game.bplayer.username and game is not None: game.spectators.add(user) else: if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user else: log.info( "+++ Existing game_user %s socket reconnected.", data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith("Anon-")) users[user.username] = user # update websocket if data["gameId"] in user.game_sockets: await user.game_sockets[data["gameId"]].close() user.game_sockets[data["gameId"]] = ws user.update_online() # remove user seeks if len(user.lobby_sockets) == 0 or ( game.status <= STARTED and (user.username == game.wplayer.username or user.username == game.bplayer.username)): await user.clear_seeks(sockets, seeks) if game is None: log.debug("Requested game %s not found!") response = { "type": "game_not_found", "username": user.username, "gameId": data["gameId"] } await ws.send_json(response) continue else: if user.username != game.wplayer.username and user.username != game.bplayer.username: game.spectators.add(user) await round_broadcast(game, users, game.spectator_list, full=True) response = { "type": "game_user_connected", "username": user.username, "gameId": data["gameId"], "ply": game.board.ply } await ws.send_json(response) response = { "type": "crosstable", "ct": game.crosstable } await ws.send_json(response) response = { "type": "fullchat", "lines": list(game.messages) } await ws.send_json(response) response = { "type": "user_present", "username": user.username } await round_broadcast(game, users, response, full=True) # not connected to lobby socket but connected to game socket if len(user.game_sockets ) == 1 and user.username not in sockets: response = { "type": "u_cnt", "cnt": online_count(users) } await lobby_broadcast(sockets, response) elif data["type"] == "is_user_present": player_name = data["username"] player = users.get(player_name) await asyncio.sleep(1) if player is not None and data["gameId"] in ( player.game_queues if player.bot else player.game_sockets): response = { "type": "user_present", "username": player_name } else: response = { "type": "user_disconnected", "username": player_name } await ws.send_json(response) elif data["type"] == "moretime": # TODO: stop and update game stopwatch time with updated secs game = await load_game(request.app, data["gameId"]) opp_color = WHITE if user.username == game.bplayer.username else BLACK if opp_color == game.stopwatch.color: opp_time = game.stopwatch.stop() game.stopwatch.restart(opp_time + MORE_TIME) opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] if not opp_player.bot: opp_ws = users[opp_name].game_sockets[ data["gameId"]] response = { "type": "moretime", "username": opp_name } await opp_ws.send_json(response) await round_broadcast(game, users, response) elif data["type"] == "roundchat": gameId = data["gameId"] game = await load_game(request.app, gameId) # Users running a fishnet worker can ask server side analysis with chat message: !analysis if data["message"] == "!analysis" and user.username in request.app[ "fishnet_versions"]: for step in game.steps: if "analysis" in step: del step["analysis"] await ws.send_json({"type": "request_analysis"}) continue response = { "type": "roundchat", "user": user.username, "message": data["message"], "room": data["room"] } game.messages.append(response) for name in (game.wplayer.username, game.bplayer.username): player = users[name] if player.bot: if gameId in player.game_queues: await player.game_queues[gameId].put( '{"type": "chatLine", "username": "******", "room": "spectator", "text": "%s"}\n' % (user.username, data["message"])) else: if gameId in player.game_sockets: player_ws = player.game_sockets[gameId] await player_ws.send_json(response) await round_broadcast(game, users, response) elif data["type"] == "leave": response = { "type": "roundchat", "user": "", "message": "%s left the game" % user.username, "room": "player" } gameId = data["gameId"] game = await load_game(request.app, gameId) game.messages.append(response) opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] if not opp_player.bot and gameId in opp_player.game_sockets: opp_player_ws = opp_player.game_sockets[gameId] await opp_player_ws.send_json(response) response = { "type": "user_disconnected", "username": user.username } await opp_player_ws.send_json(response) await round_broadcast(game, users, response) elif data["type"] == "updateTV": if "profileId" in data and data["profileId"] != "": gameId = await tv_game_user( db, users, data["profileId"]) else: gameId = await tv_game(db, request.app) if gameId != data["gameId"] and gameId is not None: response = {"type": "updateTV", "gameId": gameId} await ws.send_json(response) elif data["type"] == "count": game = await load_game(request.app, data["gameId"]) cur_player = game.bplayer if game.board.color == BLACK else game.wplayer opp_name = game.wplayer.username if user.username == game.bplayer.username else game.bplayer.username opp_player = users[opp_name] opp_ws = users[opp_name].game_sockets[data["gameId"]] if user.username == cur_player.username: if data["mode"] == "start": game.start_manual_count() response = { "type": "count", "message": "Board's honor counting started", "room": "player", "user": "" } await ws.send_json(response) await opp_ws.send_json(response) await round_broadcast(game, users, response) elif data["mode"] == "stop": game.stop_manual_count() response = { "type": "count", "message": "Board's honor counting stopped", "room": "player", "user": "" } await ws.send_json(response) await opp_ws.send_json(response) await round_broadcast(game, users, response) else: response = { "type": "count", "message": "You can only start/stop board's honor counting on your own turn!", "room": "player", "user": "" } await ws.send_json(response) elif msg.type == aiohttp.WSMsgType.CLOSED: log.debug( "--- Round websocket %s msg.type == aiohttp.WSMsgType.CLOSED", id(ws)) break elif msg.type == aiohttp.WSMsgType.ERROR: log.error( "--- Round ws %s msg.type == aiohttp.WSMsgType.ERROR", id(ws)) break else: log.debug("--- Round ws other msg.type %s %s", msg.type, msg) except OSError: # disconnected pass except Exception: log.exception( "ERROR: Exception in round_socket_handler() owned by %s ", session_user) finally: log.debug("---fianlly: await ws.close()") await ws.close() if game is not None and not user.bot: if game.id in user.game_sockets: del user.game_sockets[game.id] user.update_online() if user.username != game.wplayer.username and user.username != game.bplayer.username: game.spectators.discard(user) await round_broadcast(game, users, game.spectator_list, full=True) # not connected to lobby socket and not connected to game socket if len(user.game_sockets) == 0 and user.username not in sockets: response = {"type": "u_cnt", "cnt": online_count(users)} await lobby_broadcast(sockets, response) if game is not None: response = {"type": "user_disconnected", "username": user.username} await round_broadcast(game, users, response, full=True) return ws
async def tournament_socket_handler(request): users = request.app["users"] sockets = request.app["tourneysockets"] lobby_sockets = request.app["lobbysockets"] tourneychat = request.app["tourneychat"] ws = MyWebSocketResponse(heartbeat=3.0, receive_timeout=10.0) ws_ready = ws.can_prepare(request) if not ws_ready.ok: return web.HTTPFound("/") await ws.prepare(request) session = await aiohttp_session.get_session(request) session_user = session.get("user_name") user = users[ session_user] if session_user is not None and session_user in users else None if (user is not None) and (not user.enabled): await ws.close() session.invalidate() return web.HTTPFound("/") log.debug("-------------------------- NEW tournament WEBSOCKET by %s", user) try: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: if msg.data == "close": log.debug("Got 'close' msg.") break else: data = json.loads(msg.data) if not data["type"] == "pong": log.debug("Websocket (%s) message: %s", id(ws), msg) if data["type"] == "get_players": tournament = await load_tournament( request.app, data["tournamentId"]) if tournament is not None: page = data["page"] if user in tournament.players and tournament.players[ user].page != page: tournament.players[user].page = page response = tournament.players_json(page=page) await ws.send_json(response) elif data["type"] == "my_page": tournament = await load_tournament( request.app, data["tournamentId"]) if tournament is not None: if user in tournament.players: # force to get users current page by leaderbord status tournament.players[user].page = -1 response = tournament.players_json(user=user) await ws.send_json(response) elif data["type"] == "get_games": tournament = await load_tournament( request.app, data["tournamentId"]) if tournament is not None: response = tournament.games_json(data["player"]) await ws.send_json(response) elif data["type"] == "join": tournament = await load_tournament( request.app, data["tournamentId"]) if tournament is not None: await tournament.join(user) response = { "type": "ustatus", "username": user.username, "ustatus": tournament.user_status(user) } await ws.send_json(response) elif data["type"] == "pause": tournament = await load_tournament( request.app, data["tournamentId"]) if tournament is not None: await tournament.pause(user) response = { "type": "ustatus", "username": user.username, "ustatus": tournament.user_status(user) } await ws.send_json(response) elif data["type"] == "withdraw": tournament = await load_tournament( request.app, data["tournamentId"]) if tournament is not None: await tournament.withdraw(user) response = { "type": "ustatus", "username": user.username, "ustatus": tournament.user_status(user) } await ws.send_json(response) elif data["type"] == "tournament_user_connected": tournamentId = data["tournamentId"] tournament = await load_tournament( request.app, tournamentId) if tournament is None: continue if session_user is not None: if data["username"] and data[ "username"] != session_user: log.info( "+++ Existing tournament_user %s socket connected as %s.", session_user, data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user else: if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user else: log.info( "+++ Existing lobby_user %s socket reconnected.", data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith("Anon-")) users[user.username] = user # update websocket if tournamentId not in user.tournament_sockets: user.tournament_sockets[tournamentId] = set() user.tournament_sockets[tournamentId].add(ws) user.update_online() sockets[tournamentId][ user. username] = user.tournament_sockets[tournamentId] now = datetime.now(timezone.utc) response = { "type": "tournament_user_connected", "username": user.username, "ustatus": tournament.user_status(user), "urating": tournament.user_rating(user), "tstatus": tournament.status, "tsystem": tournament.system, "tminutes": tournament.minutes, "startsAt": tournament.starts_at.isoformat(), "startFen": tournament.fen, "description": tournament.description, "frequency": tournament.frequency, "secondsToStart": (tournament.starts_at - now).total_seconds() if tournament.starts_at > now else 0, "secondsToFinish": (tournament.ends_at - now).total_seconds() if tournament.starts_at < now else 0, } if tournament.frequency == SHIELD: variant_name = tournament.variant + ( "960" if tournament.chess960 else "") defender = users[request.app["shield_owners"] [variant_name]] response["defender_title"] = defender.title response["defender_name"] = defender.username await ws.send_json(response) if (tournament.top_game is not None) and ( tournament.top_game.status <= STARTED): await ws.send_json(tournament.top_game_json) if tournament.status > T_STARTED: await ws.send_json(tournament.summary) response = { "type": "fullchat", "lines": list(tourneychat[tournamentId]) } await ws.send_json(response) if user.username not in tournament.spectators: tournament.spactator_join(user) await tournament.broadcast( tournament.spectator_list) if len(user.game_sockets) == 0 and len( user.lobby_sockets) == 0: response = { "type": "u_cnt", "cnt": online_count(users) } await lobby_broadcast(lobby_sockets, response) elif data["type"] == "lobbychat": tournamentId = data["tournamentId"] tournament = await load_tournament( request.app, tournamentId) message = data["message"] response = None if user.username in ADMINS: if message.startswith("/silence"): response = silence(message, tourneychat[tournamentId], users) elif message.startswith("/abort"): if tournament.status in (T_CREATED, T_STARTED): await tournament.abort() else: response = chat_response( "lobbychat", user.username, data["message"]) else: if user.silence == 0: response = chat_response( "lobbychat", user.username, data["message"]) if response is not None: await tournament.broadcast(response) tourneychat[tournamentId].append(response) elif data["type"] == "logout": await ws.close() elif data["type"] == "disconnect": # Used only to test socket disconnection... await ws.close(code=1009) elif msg.type == aiohttp.WSMsgType.CLOSED: log.debug( "--- Tournament websocket %s msg.type == aiohttp.WSMsgType.CLOSED", id(ws)) break elif msg.type == aiohttp.WSMsgType.ERROR: log.error( "--- Tournament ws %s msg.type == aiohttp.WSMsgType.ERROR", id(ws)) break else: log.debug("--- Tournament ws other msg.type %s %s", msg.type, msg) except OSError: # disconnected pass except Exception: log.exception( "ERROR: Exception in tournament_socket_handler() owned by %s ", session_user) finally: log.debug("--- wsl.py fianlly: await ws.close() %s", session_user) await ws.close() if user is not None: for tournamentId in user.tournament_sockets: if ws in user.tournament_sockets[tournamentId]: user.tournament_sockets[tournamentId].remove(ws) if len(user.tournament_sockets[tournamentId]) == 0: del user.tournament_sockets[tournamentId] user.update_online() if tournamentId in sockets and user.username in sockets[ tournamentId]: del sockets[tournamentId][user.username] tournament = await load_tournament( request.app, tournamentId) tournament.spactator_leave(user) await tournament.broadcast( tournament.spectator_list) if not user.online: response = { "type": "u_cnt", "cnt": online_count(users) } await lobby_broadcast(lobby_sockets, response) break return ws
async def lobby_socket_handler(request): users = request.app["users"] sockets = request.app["lobbysockets"] seeks = request.app["seeks"] db = request.app["db"] invites = request.app["invites"] twitch = request.app["twitch"] youtube = request.app["youtube"] lobbychat = request.app["lobbychat"] ws = MyWebSocketResponse(heartbeat=3.0, receive_timeout=10.0) ws_ready = ws.can_prepare(request) if not ws_ready.ok: return web.HTTPFound("/") await ws.prepare(request) session = await aiohttp_session.get_session(request) session_user = session.get("user_name") user = users[ session_user] if session_user is not None and session_user in users else None if (user is not None) and (not user.enabled): await ws.close() session.invalidate() return web.HTTPFound("/") log.debug("-------------------------- NEW lobby WEBSOCKET by %s", user) try: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: if msg.data == "close": log.debug("Got 'close' msg.") break else: data = json.loads(msg.data) if not data["type"] == "pong": log.debug("Websocket (%s) message: %s", id(ws), msg) if data["type"] == "get_seeks": response = get_seeks(seeks) await ws.send_json(response) elif data["type"] == "create_ai_challenge": no = await is_playing(request, user, ws) if no: continue variant = data["variant"] engine = users.get("Fairy-Stockfish") if data["rm"] or (engine is None) or (not engine.online): # TODO: message that engine is offline, but Random-Mover BOT will play instead engine = users.get("Random-Mover") seek = Seek( user, variant, fen=data["fen"], color=data["color"], base=data["minutes"], inc=data["increment"], byoyomi_period=data["byoyomiPeriod"], level=0 if data["rm"] else data["level"], player1=user, rated=False, chess960=data["chess960"], alternate_start=data["alternateStart"], ) # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"]) seeks[seek.id] = seek response = await join_seek(request.app, engine, seek.id) await ws.send_json(response) if response["type"] != "error": gameId = response["gameId"] engine.game_queues[gameId] = asyncio.Queue() await engine.event_queue.put( challenge(seek, response)) elif data["type"] == "create_seek": no = await is_playing(request, user, ws) if no: continue print("create_seek", data) seek = await create_seek(db, invites, seeks, user, data, ws) await lobby_broadcast(sockets, get_seeks(seeks)) await discord_message(request.app, "create_seek", seek.discord_msg) elif data["type"] == "create_invite": no = await is_playing(request, user, ws) if no: continue print("create_invite", data) seek = await create_seek(db, invites, seeks, user, data, ws) response = { "type": "invite_created", "gameId": seek.game_id } await ws.send_json(response) elif data["type"] == "create_host": no = user.username not in TOURNAMENT_DIRECTORS if no: continue print("create_host", data) seek = await create_seek(db, invites, seeks, user, data, ws, True) response = { "type": "host_created", "gameId": seek.game_id } await ws.send_json(response) elif data["type"] == "delete_seek": try: seek = seeks[data["seekID"]] if seek.game_id is not None: # delete game invite del invites[seek.game_id] del seeks[data["seekID"]] del user.seeks[data["seekID"]] except KeyError: # Seek was already deleted pass await lobby_broadcast(sockets, get_seeks(seeks)) elif data["type"] == "accept_seek": no = await is_playing(request, user, ws) if no: continue if data["seekID"] not in seeks: continue seek = seeks[data["seekID"]] # print("accept_seek", seek.as_json) response = await join_seek(request.app, user, data["seekID"]) await ws.send_json(response) if seek.creator.bot: gameId = response["gameId"] seek.creator.game_queues[gameId] = asyncio.Queue() await seek.creator.event_queue.put( challenge(seek, response)) else: if seek.ws is None: remove_seek(seeks, seek) await lobby_broadcast(sockets, get_seeks(seeks)) else: await seek.ws.send_json(response) # Inform others, new_game() deleted accepted seek allready. await lobby_broadcast(sockets, get_seeks(seeks)) elif data["type"] == "lobby_user_connected": if session_user is not None: if data["username"] and data[ "username"] != session_user: log.info( "+++ Existing lobby_user %s socket connected as %s.", session_user, data["username"], ) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-"), ) users[user.username] = user else: if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-"), ) users[user.username] = user else: log.info( "+++ Existing lobby_user %s socket reconnected.", data["username"], ) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith("Anon-"), ) users[user.username] = user # update websocket user.lobby_sockets.add(ws) user.update_online() sockets[user.username] = user.lobby_sockets response = { "type": "lobby_user_connected", "username": user.username, } await ws.send_json(response) response = { "type": "fullchat", "lines": list(lobbychat) } await ws.send_json(response) # send game count response = { "type": "g_cnt", "cnt": request.app["g_cnt"][0] } await ws.send_json(response) # send user count response = { "type": "u_cnt", "cnt": online_count(users) } if len(user.game_sockets) == 0: await lobby_broadcast(sockets, response) else: await ws.send_json(response) spotlights = tournament_spotlights( request.app["tournaments"]) if len(spotlights) > 0: await ws.send_json({ "type": "spotlights", "items": spotlights }) streams = twitch.live_streams + youtube.live_streams if len(streams) > 0: await ws.send_json({ "type": "streams", "items": streams }) elif data["type"] == "lobbychat": if user.username.startswith("Anon-"): continue message = data["message"] response = None if user.username in ADMINS: if message.startswith("/silence"): response = silence(message, lobbychat, users) # silence message was already added to lobbychat in silence() elif message.startswith("/stream"): parts = message.split() if len(parts) >= 3: if parts[1] == "add": if len(parts) >= 5: youtube.add( parts[2], parts[3], parts[4]) elif len(parts) >= 4: youtube.add(parts[2], parts[3]) else: youtube.add(parts[2]) elif parts[1] == "remove": youtube.remove(parts[2]) await broadcast_streams(request.app) elif message == "/state": server_state(request.app) else: response = chat_response( "lobbychat", user.username, data["message"]) lobbychat.append(response) elif user.anon and user.username != "Discord-Relay": pass else: if user.silence == 0: response = chat_response( "lobbychat", user.username, data["message"]) lobbychat.append(response) if response is not None: await lobby_broadcast(sockets, response) elif data["type"] == "logout": await ws.close() elif data["type"] == "disconnect": # Used only to test socket disconnection... await ws.close(code=1009) elif msg.type == aiohttp.WSMsgType.CLOSED: log.debug( "--- Lobby websocket %s msg.type == aiohttp.WSMsgType.CLOSED", id(ws), ) break elif msg.type == aiohttp.WSMsgType.ERROR: log.error( "--- Lobby ws %s msg.type == aiohttp.WSMsgType.ERROR", id(ws)) break else: log.debug("--- Lobby ws other msg.type %s %s", msg.type, msg) except OSError: # disconnected pass except Exception: log.exception( "ERROR: Exception in lobby_socket_handler() owned by %s ", session_user) finally: log.debug("--- wsl.py fianlly: await ws.close() %s", session_user) await ws.close() if user is not None: if ws in user.lobby_sockets: user.lobby_sockets.remove(ws) user.update_online() # online user counter will be updated in quit_lobby also! if len(user.lobby_sockets) == 0: if user.username in sockets: del sockets[user.username] # not connected to lobby socket and not connected to game socket if len(user.game_sockets) == 0: response = {"type": "u_cnt", "cnt": online_count(users)} await lobby_broadcast(sockets, response) # response = {"type": "lobbychat", "user": "", "message": "%s left the lobby" % user.username} # await lobby_broadcast(sockets, response) await user.clear_seeks() return ws
async def lobby_socket_handler(request): users = request.app["users"] sockets = request.app["lobbysockets"] seeks = request.app["seeks"] db = request.app["db"] invites = request.app["invites"] ws = MyWebSocketResponse(heartbeat=3.0, receive_timeout=10.0) ws_ready = ws.can_prepare(request) if not ws_ready.ok: return web.HTTPFound("/") await ws.prepare(request) session = await aiohttp_session.get_session(request) session_user = session.get("user_name") user = users[ session_user] if session_user is not None and session_user in users else None if (user is not None) and (not user.enabled): await ws.close() session.invalidate() return web.HTTPFound("/") log.debug("-------------------------- NEW lobby WEBSOCKET by %s", user) try: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: if msg.data == "close": log.debug("Got 'close' msg.") break else: data = json.loads(msg.data) if not data["type"] == "pong": log.debug("Websocket (%s) message: %s", id(ws), msg) if data["type"] == "get_seeks": response = get_seeks(seeks) await ws.send_json(response) elif data["type"] == "create_ai_challenge": no = await is_playing(request, user, ws) if no: continue variant = data["variant"] engine = users.get("Fairy-Stockfish") if engine is None or not engine.online: # TODO: message that engine is offline, but capture BOT will play instead engine = users.get("Random-Mover") seek = Seek(user, variant, fen=data["fen"], color=data["color"], base=data["minutes"], inc=data["increment"], byoyomi_period=data["byoyomiPeriod"], level=data["level"], rated=data["rated"], chess960=data["chess960"], alternate_start=data["alternateStart"]) # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"]) seeks[seek.id] = seek response = await new_game(request.app, engine, seek.id) await ws.send_json(response) if response["type"] != "error": gameId = response["gameId"] engine.game_queues[gameId] = asyncio.Queue() await engine.event_queue.put( challenge(seek, response)) elif data["type"] == "create_seek": no = await is_playing(request, user, ws) if no: continue print("create_seek", data) seek = await create_seek(db, invites, seeks, user, data, ws) await lobby_broadcast(sockets, get_seeks(seeks)) if data.get("target"): queue = users[data["target"]].notify_queue if queue is not None: await queue.put( json.dumps({"notify": "new_challenge"})) # Send msg to discord-relay BOT try: for dr_ws in sockets["Discord-Relay"]: await dr_ws.send_json({ "type": "create_seek", "message": seek.discord_msg }) break except (KeyError, ConnectionResetError): # BOT disconnected log.error("--- Discord-Relay disconnected!") elif data["type"] == "create_invite": no = await is_playing(request, user, ws) if no: continue print("create_invite", data) seek = await create_seek(db, invites, seeks, user, data, ws) response = get_seeks(seeks) await ws.send_json(response) response = { "type": "invite_created", "gameId": seek.game_id } await ws.send_json(response) elif data["type"] == "delete_seek": try: seek = seeks[data["seekID"]] if seek.game_id is not None: # delete game invite del invites[seek.game_id] del seeks[data["seekID"]] del user.seeks[data["seekID"]] except KeyError: # Seek was already deleted pass await lobby_broadcast(sockets, get_seeks(seeks)) elif data["type"] == "accept_seek": no = await is_playing(request, user, ws) if no: continue if data["seekID"] not in seeks: continue seek = seeks[data["seekID"]] # print("accept_seek", seek.as_json) response = await new_game(request.app, user, data["seekID"]) await ws.send_json(response) if seek.user.bot: gameId = response["gameId"] seek.user.game_queues[gameId] = asyncio.Queue() await seek.user.event_queue.put( challenge(seek, response)) else: await seek.ws.send_json(response) # Inform others, new_game() deleted accepted seek allready. await lobby_broadcast(sockets, get_seeks(seeks)) elif data["type"] == "lobby_user_connected": if session_user is not None: if data["username"] and data[ "username"] != session_user: log.info( "+++ Existing lobby_user %s socket connected as %s.", session_user, data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user # response = {"type": "lobbychat", "user": "", "message": "%s joined the lobby" % session_user} else: if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith( "Anon-")) users[user.username] = user # response = {"type": "lobbychat", "user": "", "message": "%s joined the lobby" % session_user} else: log.info( "+++ Existing lobby_user %s socket reconnected.", data["username"]) session_user = data["username"] if session_user in users: user = users[session_user] else: user = User( request.app, username=data["username"], anon=data["username"].startswith("Anon-")) users[user.username] = user # response = {"type": "lobbychat", "user": "", "message": "%s rejoined the lobby" % session_user} # await lobby_broadcast(sockets, response) # update websocket user.lobby_sockets.add(ws) user.update_online() sockets[user.username] = user.lobby_sockets response = { "type": "lobby_user_connected", "username": user.username } await ws.send_json(response) response = { "type": "fullchat", "lines": list(request.app["chat"]) } await ws.send_json(response) # send game count response = { "type": "g_cnt", "cnt": request.app["g_cnt"] } await ws.send_json(response) # send user count response = { "type": "u_cnt", "cnt": online_count(users) } if len(user.game_sockets) == 0: await lobby_broadcast(sockets, response) else: await ws.send_json(response) elif data["type"] == "lobbychat": message = data["message"] response = None if user.username in ADMINS: if message.startswith("/silence"): spammer = data["message"].split()[-1] if spammer in users: users[spammer].set_silence() response = { "type": "lobbychat", "user": "", "message": "%s was timed out 10 minutes for spamming the chat." % spammer } elif message == "/growth": server_growth() elif message == "/state": server_state(request.app) else: response = { "type": "lobbychat", "user": user.username, "message": data["message"] } elif user.anon and user.username != "Discord-Relay": pass else: if user.silence == 0: response = { "type": "lobbychat", "user": user.username, "message": data["message"] } if response is not None: await lobby_broadcast(sockets, response) request.app["chat"].append(response) elif data["type"] == "logout": await ws.close() elif data["type"] == "disconnect": # Used only to test socket disconnection... await ws.close(code=1009) elif msg.type == aiohttp.WSMsgType.CLOSED: log.debug( "--- Lobby websocket %s msg.type == aiohttp.WSMsgType.CLOSED", id(ws)) break elif msg.type == aiohttp.WSMsgType.ERROR: log.error( "--- Lobby ws %s msg.type == aiohttp.WSMsgType.ERROR", id(ws)) break else: log.debug("--- Lobby ws other msg.type %s %s", msg.type, msg) except OSError: # disconnected pass except Exception: log.exception( "ERROR: Exception in lobby_socket_handler() owned by %s ", session_user) finally: log.debug("--- wsl.py fianlly: await ws.close() %s", session_user) await ws.close() if user is not None: if ws in user.lobby_sockets: user.lobby_sockets.remove(ws) user.update_online() # online user counter will be updated in quit_lobby also! if len(user.lobby_sockets) == 0: if user.username in sockets: del sockets[user.username] # not connected to lobby socket and not connected to game socket if len(user.game_sockets) == 0: response = {"type": "u_cnt", "cnt": online_count(users)} await lobby_broadcast(sockets, response) # response = {"type": "lobbychat", "user": "", "message": "%s left the lobby" % user.username} # await lobby_broadcast(sockets, response) await user.clear_seeks(sockets, seeks) return ws