def gameOver(jsn): """Recieved when the game ends for a pair of players.""" game_id = jsn["gameId"] ### Make sure that the game is either in the live games, or has an entry # in the database already. db.write_connection_event(request.sid, "Game Ended Normally") if game_id in live_games: logger.debug('game over (ID: ' + str(game_id) + ')') db.write_final_turn(game_id, jsn["time"]) db.write_game_end(game_id, jsn["score"]) live_games.pop(game_id) kill_agent(game_id) else: # If it's not, make sure that the score has been set and accurately # reflects what was in the database already. expected_score = db.get_game_score(game_id) if not expected_score == jsn["score"]: logger.warn("Didn't find game " + game_id + " in live games, but the score was not " "expected! (database) " + str(expected_score) + "; (client) " + str(jsn["score"]))
def parse_credentials(jsn): # This is a hashed worker ID. Look it up against the hash. worker_id = jsn['worker_id'] if db.is_qualified(worker_id): # Check if the assignment ID is already used. if db.assignment_id_used(worker_id, jsn['assignment_id']): db.update_client_info(request.sid, worker_id, jsn['assignment_id'], 'Already Used Assignment ID') logger.debug( 'identified worker using assignment ID another time; SID=' + request.sid) emit('assignmentIdUsed') else: # Check for worker ID in list of currently-connected workers if db.worker_connected(worker_id): db.update_client_info(request.sid, worker_id, jsn['assignment_id'], 'Already Connected to Game') logger.debug('identified already-connected worker ' + jsn['worker_id'] + '; SID=' + request.sid) emit('alreadyConnected') else: db.update_client_info(request.sid, worker_id, jsn['assignment_id'], 'Validated Connection') logger.debug('recognizing connected worker ' + jsn['worker_id'] + '; SID=' + request.sid) db.write_connection_event(request.sid, 'Connected and Validated Identity') emit('recognizedWorker') elif worker_id.startswith('agent'): db.update_client_info(request.sid, worker_id, jsn['assignment_id'], 'Agent Connected') logger.debug('recognized agent; SID=' + request.sid + '; ID=' + str(worker_id)) emit('recognizedAgent') db.write_connection_event(request.sid, 'Connected with Agent ID') else: if worker_id: db.update_client_info(request.sid, worker_id, jsn['assignment_id'], 'Non-Recognized Worker') logger.debug( 'did not recognize connector as qualified worker; SID=' + request.sid) emit('unqualifiedWorker') db.write_connection_event(request.sid, 'Connected with Unrecognized Worker ID') else: db.update_client_info(request.sid, worker_id, jsn['assignment_id'], 'No-credentials Connection') logger.debug('did not recognize connector; SID=' + request.sid) db.write_connection_event(request.sid, 'Connected with No Credentials')
def remove_ghost_sessions(): cur_age = sys.maxsize ghost_start_time = time.time() - SERVER_START_TIME while cur_age > TIMEOUT_AGE and not lobby_clients.empty(): client_entered_time, client = lobby_clients.get() client_age = ghost_start_time - client_entered_time if client_age <= TIMEOUT_AGE: # If hasn't been waiting long, put it back and break lobby_clients.put((client_entered_time, client)) else: lobby_clients_active[client.sid] = False socketio.emit('reset', room=client.sid) logger.debug('marked inactive client ' + str(client) + '; waited for ' + str(client_age) + ' s') db.write_connection_event(client.sid, "Removed Ghost Session") cur_age = client_age
def ready_pressed(jsn): """Recieved when player presses button to indicate they are ready to start. Game actually starts loading after both lobby_clients send this message, i.e. they are both ready. """ logger.debug("%s from %s pressed ready", jsn["character"], jsn["gameId"]) game_id = jsn["gameId"] try: game = live_games[game_id] except KeyError as e: logger.warn('Game ' + str(game_id) + ' not found in live games') return # Set ready, and also join the room actually corresponding to the in-play # game if jsn["character"] == "Human": game.set_human_ready() logger.debug('human joined room: ' + game.human_room()) join_room(game.human_room()) db.write_connection_event(request.sid, "Pressed Ready (Leader)") elif jsn["character"] == "Agent": game.set_agent_ready() logger.debug('agent joined room: ' + game.agent_room()) join_room(game.agent_room()) db.write_connection_event(request.sid, "Pressed Ready (Follower)") else: raise ValueError('Unrecognized character name: ' + jsn['character']) if game.both_ready(): emit('startGame', room=game.human_room()) emit('startGame', room=game.agent_room()) # Mark the assignment IDs as used db.mark_assignment_id_used(game.human_id(), game.human_assignment_id()) db.mark_assignment_id_used(game.agent_id(), game.agent_assignment_id())
def join_lobby(jsn): """Player joins lobby for partner matchmaking. 'lobby_clients' holds all players currently in queue to find a partner. """ # Just put them in the lobby. worker_id = jsn['worker_id'] my_request = CerealRequest(request.remote_addr, request.sid, worker_id, jsn['assignment_id']) if worker_id.startswith('agent'): agent_clients.put((time.time() - SERVER_START_TIME, my_request)) agent_clients_active[request.sid] = True else: lobby_clients.put((time.time() - SERVER_START_TIME, my_request)) lobby_clients_active[request.sid] = True logger.info('MATCHING WITH HUMAN? ' + str(CURRENT_MATCH_MODE.use_human)) if not CURRENT_MATCH_MODE.use_human: # Spawn an agent client logger.debug('Spawning an agent client...') agent_uuid = 'agent_' + str(uuid.uuid4().hex) process = subprocess.Popen( ['python3', '-m', 'webapp.agent', agent_uuid]) agent_processes[agent_uuid] = process logger.debug('Spawned agent=' + agent_uuid + ', pid=' + str(process.pid)) logger.debug("player joined lobby: " + str(my_request)) db.write_connection_event(request.sid, "Join Lobby") # Each player's room is just their unique request debugrmation for now # Later on, when they are ready, they will enter their game's room join_room(my_request.sid)
def start_gameplay(jsn): """Recieved when one player's game is loaded, so they are ready to start playing. Gameplay starts after both players send this message. """ game_id = jsn["gameId"] requester_sid = request.sid logger.debug("player: " + requester_sid + " loaded the game " + game_id) db.write_connection_event(requester_sid, 'Game Loaded') try: game = live_games[game_id] except KeyError: logger.error('Game ' + str(game_id) + ' not found in live games') return if jsn['character'] == 'Human': game.set_human_loaded() partner_room = game.agent_room() elif jsn['character'] == 'Agent': game.set_agent_loaded() partner_room = game.human_room() else: raise ValueError('Unrecognized character type: ' + jsn['character']) if game.both_loaded(): # need both players to be in the same room so we can send the # start msg in one emit, to get each player to recieve the message # as close to the same time as possible join_room(partner_room) logger.debug('sending start game play to my partner') emit('startGamePlay', room=partner_room) leave_room(partner_room) db.write_first_turn(game_id)
def changed_focus(jsn): focus = jsn['data'] db.write_connection_event(request.sid, 'Changed Focus to ' + str(focus))
def complete_tutorial(jsn): sid = request.sid db.mark_tutorial_complete(sid, jsn['isLeader'], jsn['workerId'], jsn['assignmentId']) db.write_connection_event(sid, "Tutorial Complete")
def verify_passcode(jsn): db.update_client_info(request.sid, PASSID, '', 'Passcode Connection') db.write_connection_event(request.sid, "Passcode Used") emit('checkPasscode', {'valid': jsn['passcode'] == PASSID})
def match_human_players(): if lobby_clients.qsize() >= 2: try: client_1 = lobby_clients.get() except queue.Empty as e: logger.debug("Couldn't get client 1 in human-human match, " + str(e)) return try: client_2 = lobby_clients.get() except queue.Empty as e: logger.debug("Couldn't get client 2 in human-human match, " + str(e)) lobby_clients.put(client_1) return # If one of them isn't there anymore, remove (put the other one back) if not (lobby_clients_active[client_1[1].sid] and lobby_clients_active[client_2[1].sid]): logger.debug("Pair between " + client_1[1].sid + " and " + client_2[1].sid + " didn't work; removing one or both of them") if lobby_clients_active[client_1[1].sid]: lobby_clients.put(client_1) elif lobby_clients_active[client_2[1].sid]: lobby_clients.put(client_2) return if client_1[1].sid == client_2[1].sid: # It is possible they can match with themselves in the case # that they time out and restart from the same page. Avoid this # by removing a client if it has the same sid. # This only happens because the player joins the lobby twice from the # same sid (we don't make them reload the page to return to the # lobby). lobby_clients_active is updated to True for the second # time they join the lobby, so for both clients in the lobby, they # are considered active (the only thing different between the two # clients is the time they entered, which we don't want to require # for lookups in lobby_clients_active). Return the one to the # queue that has waited shorter (i.e., the one that just joined). lobby_clients.put(client_2) return client_1_waittime = time.time() - client_1[0] - SERVER_START_TIME client_2_waittime = time.time() - client_2[0] - SERVER_START_TIME client_1 = client_1[1] client_2 = client_2[1] lobby_clients_active[client_1.sid] = False lobby_clients_active[client_2.sid] = False db.write_connection_event(client_1.sid, "Matched With Partner") db.write_connection_event(client_2.sid, "Matched With Partner") # pair them up # guaranteed to be free of collisions and we don't need ordering on # game IDs so just use uuid game_id = str(uuid.uuid4().hex) game_seed = random.randint(0, pow(2, 30)) print('game seed: ' + str(game_seed)) logger.debug('game seed: ' + str(game_seed)) num_cards = 21 character_1 = "Human" if game_seed % 2 == 0 else "Agent" character_2 = "Agent" if character_1 == "Human" else "Human" if character_1 == "Human": game = GameData(game_seed, game_id, num_cards, human=client_1, agent=client_2, using_agent=False) else: game = GameData(game_seed, game_id, num_cards, human=client_2, agent=client_1, using_agent=False) live_games[game_id] = game logger.debug("starting game #" + str(game_id) + " with seed " + str(game_seed) + " and worker ids " + client_1.worker_id + " / " + client_2.worker_id) logger.debug("client 1 (" + str(client_1) + " waited for " + str(client_1_waittime) + " s") logger.debug("client 2 (" + str(client_2) + " waited for " + str(client_2_waittime) + " s") # Initialize the game for both players, and also say that the lobby # is ready (players matched) -- this will play the audio reminder # For now, each player is in a room corresponding to their sid. # But later they will go into a room corresponding to the game, so # movement communication is easier. socketio.emit('initGame', {'character': character_1, 'gameId': game_id, 'seed': game_seed, 'num_cards': num_cards}, room=client_1.sid) socketio.emit('lobbyReady', room=client_1.sid) socketio.emit('initGame', {'character': character_2, 'gameId': game_id, 'seed': game_seed, 'num_cards': num_cards}, room=client_2.sid) socketio.emit('lobbyReady', room=client_2.sid) db.write_game_start(game) # Switch the mode if MATCH_MODE == MatchMode.HUMAN_AND_AGENT: CURRENT_MATCH_MODE.use_human = False logger.debug('Switching to matching with human: ' + str(CURRENT_MATCH_MODE.use_human))
def match_human_with_agent(): # Don't allow more than N concurrent games if len(live_games) >= 30: if lobby_clients.qsize() > LOGGED_OVER_CAPACITY.num_connected: logger.debug("New maximum connections reached: %r" % lobby_clients.qsize()) LOGGED_OVER_CAPACITY.num_connected = lobby_clients.qsize() elif lobby_clients.qsize() < LOGGED_OVER_CAPACITY.num_connected: logger.debug("Number of connections lowered: %r" % lobby_clients.qsize()) LOGGED_OVER_CAPACITY.num_connected = lobby_clients.qsize() return if lobby_clients.qsize() >= 1 and agent_clients.qsize() >= 1: try: human_client = lobby_clients.get() except queue.Empty as e: logger.debug("Couldn't get human in human-agent match, " + str(e)) return try: agent_client = agent_clients.get() except queue.Empty as e: logger.debug("Couldn't get agent in human-agent match, " + str(e)) lobby_clients.put(human_client) return # If one of them isn't there anymore, remove (put the other one back) if not (lobby_clients_active[human_client[1].sid] and agent_clients_active[agent_client[1].sid]): logger.debug("Pair between " + human_client[1].sid + " and " + agent_client[1].sid + " didn't work; removing one or both of them") if lobby_clients_active[human_client[1].sid]: lobby_clients.put(human_client) elif agent_clients_active[agent_client[1].sid]: agent_clients.put(agent_client) return if human_client[1].sid == agent_client[1].sid: raise ValueError('Agent and human client sid should never be the same!' + str(human_client[1].sid)) client_1_waittime = time.time() - human_client[0] - SERVER_START_TIME client_2_waittime = time.time() - agent_client[0] - SERVER_START_TIME human_client = human_client[1] agent_client = agent_client[1] lobby_clients_active[human_client.sid] = False agent_clients_active[agent_client.sid] = False db.write_connection_event(human_client.sid, "Matched With Partner") db.write_connection_event(agent_client.sid, "Matched With Partner") # pair them up # guaranteed to be free of collisions and we don't need ordering on # game IDs so just use uuid game_id = str(uuid.uuid4().hex) game_seed = random.randint(0, pow(2, 30)) num_cards = 21 game = GameData(game_seed, game_id, num_cards, human=human_client, agent=agent_client, using_agent=True) live_games[game_id] = game logger.debug("starting game #" + str(game_id) + " with seed " + str(game_seed) + " and worker ids " + human_client.worker_id + " / " + agent_client.worker_id) logger.debug("client 1 (" + str(human_client) + " waited for " + str(client_1_waittime) + " s") logger.debug("client 2 (" + str(agent_client) + " waited for " + str(client_2_waittime) + " s") # Initialize the game for both players, and also say that the lobby # is ready (players matched) -- this will play the audio reminder # For now, each player is in a room corresponding to their sid. # But later they will go into a room corresponding to the game, so # movement communication is easier. socketio.emit('initGame', {'character': 'Human', 'gameId': game_id, 'seed': game_seed, 'num_cards': num_cards}, room=human_client.sid) socketio.emit('lobbyReady', room=human_client.sid) socketio.emit('initGame', {'character': 'Agent', 'gameId': game_id, 'seed': game_seed, 'num_cards': num_cards}, room=agent_client.sid) socketio.emit('lobbyReady', room=agent_client.sid) db.write_game_start(game) # Switch the mode if MATCH_MODE == MatchMode.HUMAN_AND_AGENT: CURRENT_MATCH_MODE.use_human = True logger.debug('Switching to matching with human: ' + str(CURRENT_MATCH_MODE.use_human))
def disconnectEvent(): """Recieved when a client exits from their game.""" db.disconnect_client(request.sid, 'Disconnected') db.write_connection_event(request.sid, "Disconnected") if request.sid in lobby_clients_active and lobby_clients_active[ request.sid]: # If was in the lobby, logger.debug('Lobby client ' + request.sid + ' disconnected') db.write_connection_event(request.sid, "Disconnected from Lobby") db.disconnect_client(request.sid, 'Disconnected from Lobby') lobby_clients_active[request.sid] = False return # Find the game that this request was connected to request_is_human = False found_game = None for game_id, game in live_games.items(): if game.human.sid == request.sid: request_is_human = True found_game = game logger.debug('Requester ' + request.sid + ' was human in game ' + str(game_id)) break elif not game.using_agent and game.agent.sid == request.sid: found_game = game logger.debug('Requester ' + request.sid + ' was agent in game ' + str(game_id)) break if not found_game: return # Log that they exited prematurely game_id = found_game.game_id if request_is_human: if found_game.using_agent: logger.debug('Removing game ' + str(game_id) + ' from live games; human from agent-human game.') db.disconnect_client(request.sid, 'Disconnected After Game') db.write_connection_event(request.sid, "Quit Game") # Check if in live games first -- might not be anymore if the partner # concurrently ended the game kill_agent(game_id) if game_id in live_games: # Get the agent ID live_games.pop(game_id) else: if found_game.agent_quit: logger.debug('Removing game ' + str(game_id) + ' from live games; both people quit.') db.disconnect_client(request.sid, 'Disconnected After Game') db.write_connection_event(request.sid, "Quit Game") # Check if in live games first -- might not be anymore if the partner # concurrently ended the game if game_id in live_games: live_games.pop(game_id) else: logger.debug('Agent from game ' + str(game_id) + ' is still there.') found_game.set_human_quit() # Notify the agent that their partner left. Use the sid, rather than # the game room, because they may not have pressed that they are # ready yet. emit('partnerLeft', room=found_game.agent.sid) # If the game hasn't started, remove it. if not found_game.both_loaded(): logger.debug( "Completely removed game " + str(game_id) + " because it has not started or loaded for both players." ) live_games.pop(game_id) db.disconnect_client(request.sid, 'Disconnected Before Loading Game') db.write_connection_event( request.sid, "Quit Game Prematurely (Before Loading)") else: db.disconnect_client(request.sid, 'Disconnected During Game') db.write_connection_event( request.sid, "Quit Game Prematurely (Ongoing Game)") else: if found_game.using_agent: raise ValueError( '(game ID: ' + str(game_id) + ' When using the agent, the requester who disconnects should be a human.' ) if found_game.human_quit: logger.debug('Removing game ' + str(game_id) + ' from live games; both people quit.') db.disconnect_client(request.sid, 'Disconnected After Game') db.write_connection_event(request.sid, "Quit Game") # Check if in live games first -- might not be anymore if the partner # concurrently ended the game if game_id in live_games: live_games.pop(game_id) else: logger.debug('Human from game ' + str(game_id) + ' is still there.') found_game.set_agent_quit() # Notify the agent that their partner left. emit('partnerLeft', room=found_game.human.sid) # If the game hasn't started, remove it. if not found_game.both_loaded(): logger.debug( "Completely removed game " + str(game_id) + " because it has not started or loaded for both players.") live_games.pop(game_id) db.disconnect_client(request.sid, 'Disconnected Before Loading Game') db.write_connection_event( request.sid, "Quit Game Prematurely (Before Loading)") else: db.disconnect_client(request.sid, 'Disconnected During Game') db.write_connection_event( request.sid, "Quit Game Prematurely (Ongoing Game)")