async def test_game_results(game: Game, players): game.state = GameState.LOBBY host_id = players.hosting.id join_id = players.joining.id add_connected_players(game, [players.hosting, players.joining]) game.set_player_option(players.hosting.id, "Team", 1) game.set_player_option(players.joining.id, "Team", 1) await game.launch() await game.add_result(host_id, 0, "victory", 1) await game.add_result(join_id, 1, "defeat", 0) game_results = await game.resolve_game_results() result_dict = game_results.to_dict() assert result_dict["validity"] == "VALID" assert result_dict["rating_type"] == "global" assert len(result_dict["teams"]) == 2 for team in result_dict["teams"]: assert team["outcome"] == ("VICTORY" if team["player_ids"] == [host_id] else "DEFEAT") assert result_dict["game_id"] == game.id assert result_dict["map_id"] == game.map_id assert result_dict["featured_mod"] == "faf" assert result_dict["sim_mod_ids"] == []
async def test_disconnect_all_peers(game_connection: GameConnection, real_game: Game, players): real_game.state = GameState.LOBBY game_connection.player = players.hosting game_connection.game = real_game disconnect_done = mock.Mock() async def fake_send_dc(player_id): await asyncio.sleep(1) # Take some time disconnect_done.success() return "OK" # Set up a peer that will disconnect without error ok_disconnect = asynctest.create_autospec(GameConnection) ok_disconnect.state = GameConnectionState.CONNECTED_TO_HOST ok_disconnect.send_DisconnectFromPeer = fake_send_dc # Set up a peer that will throw an exception fail_disconnect = asynctest.create_autospec(GameConnection) fail_disconnect.send_DisconnectFromPeer.return_value = Exception( "Test exception") fail_disconnect.state = GameConnectionState.CONNECTED_TO_HOST # Add the peers to the game real_game.add_game_connection(fail_disconnect) real_game.add_game_connection(ok_disconnect) await game_connection.disconnect_all_peers() disconnect_done.success.assert_called_once()
async def test_handle_action_OperationComplete_invalid( ugame: Game, game_connection: GameConnection, database): """ Sends an OperationComplete action to handle action and verifies that the `coop_leaderboard` table is updated accordingly. Requires that the map from `game.map_file_path` exists in the database. """ ugame.map_file_path = "maps/prothyon16.v0005.zip" ugame.validity = ValidityState.OTHER_UNRANK game_connection.game = ugame secondary = 1 time_taken = '09:08:07.654321' await game_connection.handle_action('OperationComplete', ['1', secondary, time_taken]) async with database.acquire() as conn: result = await conn.execute( "SELECT secondary, gameuid from `coop_leaderboard` where gameuid=%s", ugame.id) row = await result.fetchone() assert row is None
async def test_team_sets_missing_team_disallowed(game: Game, game_add_players): game.state = GameState.LOBBY player_id = 5 game_add_players(game, player_id, team=1) del game._player_options[player_id]["Team"] with pytest.raises(GameError): assert game.get_team_sets()
async def check_game_settings(game: Game, settings: List[Tuple[str, Any, ValidityState]]): for key, value, expected in settings: old = game.gameOptions.get(key) game.gameOptions[key] = value await game.validate_game_settings() assert game.validity is expected game.gameOptions[key] = old
async def test_remove_game_connection(game: Game, players, mock_game_connection): game.state = GameState.LOBBY mock_game_connection.player = players.hosting mock_game_connection.state = GameConnectionState.CONNECTED_TO_HOST game.add_game_connection(mock_game_connection) await game.remove_game_connection(mock_game_connection) assert players.hosting not in game.players
async def test_add_game_connection_throws_if_not_lobby_state( game: Game, players, mock_game_connection): game.state = GameState.INITIALIZING mock_game_connection.player = players.hosting mock_game_connection.state = GameConnectionState.CONNECTED_TO_HOST with pytest.raises(GameError): game.add_game_connection(mock_game_connection) assert players.hosting not in game.players
async def test_add_game_connection_throws_if_not_connected_to_host( game: Game, players, mock_game_connection): game.state = GameState.LOBBY mock_game_connection.player = players.hosting mock_game_connection.state = GameConnectionState.INITIALIZED with pytest.raises(GameError): game.add_game_connection(mock_game_connection) assert players.hosting not in game.players
async def test_game_is_invalid_due_to_desyncs(game: Game, players): game.state = GameState.LOBBY add_connected_players(game, [players.hosting, players.joining]) game.host = players.hosting await game.launch() game.desyncs = 30 await game.on_game_end() assert game.validity is ValidityState.TOO_MANY_DESYNCS
async def test_handle_action_GameState_lobby_sends_HostGame( game: Game, game_connection: GameConnection, event_loop, players): game_connection.player = players.hosting game.map_file_path = "maps/some_map.zip" game.map_folder_name = "some_map" await game_connection.handle_action("GameState", ["Lobby"]) await exhaust_callbacks(event_loop) assert_message_sent(game_connection, "HostGame", [game.map_folder_name])
async def test_handle_action_GameState_lobby_sends_HostGame( game: Game, game_connection: GameConnection, event_loop, players): game_connection.player = players.hosting game.map_file_path = 'maps/some_map.zip' game.map_folder_name = 'some_map' await game_connection.handle_action('GameState', ['Lobby']) await exhaust_callbacks(event_loop) assert_message_sent(game_connection, 'HostGame', [game.map_folder_name])
async def test_game_end_when_no_more_connections(game: Game, mock_game_connection): game.state = GameState.LOBBY game.on_game_end = CoroutineMock() mock_game_connection.state = GameConnectionState.CONNECTED_TO_HOST game.add_game_connection(mock_game_connection) await game.remove_game_connection(mock_game_connection) game.on_game_end.assert_any_call()
async def test_game_visible_for_rating(game: Game, players): game.enforce_rating_range = True game.displayed_rating_range = InclusiveRange(2000, None) game.host = players.hosting players.joining.ratings[RatingType.GLOBAL] = (1500, 1) assert not game.is_visible_to_player(players.joining) players.joining.ratings[RatingType.GLOBAL] = (2100, 1) assert game.is_visible_to_player(players.joining)
async def test_game_sim_ends_when_no_more_connections(game: Game, players): game.state = GameState.LOBBY host_conn = add_connected_player(game, players.hosting) join_conn = add_connected_player(game, players.joining) game.host = players.hosting await game.launch() await game.remove_game_connection(host_conn) await game.remove_game_connection(join_conn) assert game.ended
async def test_handle_action_GameState_lobby_sends_HostGame( game: Game, game_connection: GameConnection, loop, players): game_connection.player = players.hosting game.map_file_path = 'maps/some_map.zip' game.map_folder_name = 'some_map' await game_connection.handle_action('GameState', ['Lobby']) # Give the connection coro time to run await asyncio.sleep(0.1) assert_message_sent(game_connection, 'HostGame', [game.map_folder_name])
def test_send_game_list(mocker, lobbyconnection, game_stats_service): protocol = mocker.patch.object(lobbyconnection, 'protocol') games = mocker.patch.object(lobbyconnection, 'game_service') # type: GameService game1, game2 = mock.create_autospec(Game(42, mock.Mock(), game_stats_service)),\ mock.create_autospec(Game(22, mock.Mock(), game_stats_service)) games.open_games = [game1, game2] lobbyconnection.send_game_list() protocol.send_message.assert_any_call({'command': 'game_info', 'games': [game1.to_dict(), game2.to_dict()]})
async def test_game_ends_in_mutually_agreed_draw(game: Game, game_add_players): game.state = GameState.LOBBY players = game_add_players(game, 2) await game.launch() game.launched_at = time.time() - 60 * 60 await game.add_result(players[0].id, 0, "mutual_draw", 0) await game.add_result(players[1].id, 1, "mutual_draw", 0) await game.on_game_end() assert game.validity is ValidityState.MUTUAL_DRAW
async def test_game_sim_ends_when_connections_ended_sim(game: Game, players): game.state = GameState.LOBBY host_conn = add_connected_player(game, players.hosting) join_conn = add_connected_player(game, players.joining) game.host = players.hosting await game.launch() host_conn.finished_sim = True join_conn.finished_sim = True await game.check_sim_end() assert game.ended
async def test_game_launch_freezes_players(game: Game, players): game.state = GameState.LOBBY host_conn = add_connected_player(game, players.hosting) game.host = players.hosting add_connected_player(game, players.joining) await game.launch() assert game.state is GameState.LIVE assert game.players == {players.hosting, players.joining} await game.remove_game_connection(host_conn) assert game.players == {players.hosting, players.joining}
async def test_game_not_ends_in_unilatery_agreed_draw(game: Game, players, game_add_players): game.state = GameState.LOBBY game_add_players(game, 2) await game.launch() game.launched_at = time.time() - 60 * 60 await game.add_result(players.hosting.id, 0, "mutual_draw", 0) await game.add_result(players.joining.id, 1, "victory", 10) await game.on_game_end() assert game.validity is not ValidityState.MUTUAL_DRAW
async def test_send_game_list(mocker, database, lobbyconnection, game_stats_service): games = mocker.patch.object(lobbyconnection, "game_service") # type: GameService game1, game2 = mock.create_autospec(Game(42, database, mock.Mock(), game_stats_service)), \ mock.create_autospec(Game(22, database, mock.Mock(), game_stats_service)) games.open_games = [game1, game2] lobbyconnection.send = CoroutineMock() await lobbyconnection.send_game_list() lobbyconnection.send.assert_any_call({ "command": "game_info", "games": [game1.to_dict(), game2.to_dict()] })
async def test_handle_action_GameMods_post_launch_updates_played_cache( game: Game, game_connection: GameConnection, database): game.launch = CoroutineMock() game.remove_game_connection = CoroutineMock() await game_connection.handle_action( 'GameMods', ['uids', 'foo bar EA040F8E-857A-4566-9879-0D37420A5B9D']) await game_connection.handle_action('GameState', ['Launching']) async with database.acquire() as conn: result = await conn.execute( "select `played` from table_mod where uid=%s", ('EA040F8E-857A-4566-9879-0D37420A5B9D', )) row = await result.fetchone() assert 2 == row[0]
async def test_handle_action_GameState_lobby_calls_abort( game: Game, game_connection: GameConnection, event_loop, players): game_connection.send = CoroutineMock() game_connection.abort = CoroutineMock() game_connection.player = players.joining players.joining.game = game game.host = players.hosting game.host.state = PlayerState.IDLE game.map_file_path = 'maps/some_map.zip' game.map_folder_name = 'some_map' await game_connection.handle_action('GameState', ['Lobby']) await exhaust_callbacks(event_loop) game_connection.abort.assert_called_once()
async def test_handle_action_GameState_lobby_calls_ConnectToHost( game: Game, game_connection: GameConnection, event_loop, players): game_connection.send = CoroutineMock() game_connection.connect_to_host = CoroutineMock() game_connection.player = players.joining players.joining.game = game game.host = players.hosting game.map_file_path = "maps/some_map.zip" game.map_folder_name = "some_map" await game_connection.handle_action("GameState", ["Lobby"]) await exhaust_callbacks(event_loop) game_connection.connect_to_host.assert_called_with( players.hosting.game_connection)
def add_connected_players(game: Game, players): """ Utility to add players with army and StartSpot indexed by a list """ for army, player in enumerate(players): add_connected_player(game, player) game.set_player_option(player.id, 'Army', army) game.set_player_option(player.id, 'StartSpot', army) game.set_player_option(player.id, 'Team', army) game.set_player_option(player.id, 'Faction', 0) game.set_player_option(player.id, 'Color', 0) game.host = players[0]
def add_connected_players(game: Game, players): """ Utility to add players with army and StartSpot indexed by a list """ for army, player in enumerate(players): add_connected_player(game, player) game.set_player_option(player.id, "Army", army) game.set_player_option(player.id, "StartSpot", army) game.set_player_option(player.id, "Team", army) game.set_player_option(player.id, "Faction", 0) game.set_player_option(player.id, "Color", 0) game.host = players[0]
async def test_handle_action_GameMods_activated( game: Game, game_connection: GameConnection): game.mods = {"a": "b"} await game_connection.handle_action('GameMods', ['activated', 0]) assert game.mods == {} await game_connection.handle_action('GameMods', ['activated', '0']) assert game.mods == {}
async def test_handle_lobby_state_handles_GameError( real_game: Game, game_connection: GameConnection, event_loop, players): game_connection.abort = CoroutineMock() game_connection.connect_to_host = CoroutineMock() game_connection.player = players.joining game_connection.game = real_game players.joining.game = real_game real_game.host = players.hosting real_game.state = GameState.ENDED await game_connection.handle_action('GameState', ['Lobby']) await exhaust_callbacks(event_loop) game_connection.abort.assert_called_once()
async def test_handle_action_GameMods_activated( game: Game, game_connection: GameConnection): game.mods = {"a": "b"} await game_connection.handle_action("GameMods", ["activated", 0]) assert game.mods == {} await game_connection.handle_action("GameMods", ["activated", "0"]) assert game.mods == {}
async def test_game_connection_not_restored_if_game_state_prohibits( lobbyconnection: LobbyConnection, game_service: GameService, game_stats_service, game_state, mocker, database): del lobbyconnection.player.game_connection lobbyconnection.player.state = PlayerState.IDLE lobbyconnection.game_service = game_service game = mock.create_autospec( Game(42, database, game_service, game_stats_service)) game.state = game_state game.password = None game.game_mode = 'faf' game.id = 42 game_service._games[42] = game await lobbyconnection.on_message_received({ 'command': 'restore_game_session', 'game_id': 42 }) assert not lobbyconnection.game_connection assert lobbyconnection.player.state == PlayerState.IDLE lobbyconnection.protocol.send_message.assert_any_call({ "command": "notice", "style": "info", "text": "The game you were connected to is no longer available" })
async def process_game_stats(self, player: Player, game: Game, stats_json): stats = None number_of_humans = 0 highest_score = 0 highest_scorer = None for army_stats in json.loads(stats_json)['stats']: if army_stats['type'] == 'AI' and army_stats['name'] != 'civilian': self._logger.debug("Ignoring AI game reported by %s", player.login) return if army_stats['type'] == 'Human': number_of_humans += 1 if highest_score < army_stats['general']['score']: highest_score = army_stats['general']['score'] highest_scorer = army_stats['name'] if army_stats['name'] == player.login: stats = army_stats if number_of_humans < 2: self._logger.debug("Ignoring single player game reported by %s", player.login) return if stats is None: self._logger.warn("Player %s reported stats of a game he was not part of", player.login) return army_result = game.get_army_result(player) if not army_result: self._logger.warn("No army result available for player %s", player.login) return self._logger.debug("Processing game stats for player: %s", player.login) faction = stats['faction'] # Stores achievements to batch update a_queue = [] # Stores events to batch update e_queue = [] survived = army_result[1] == 'victory' blueprint_stats = stats['blueprints'] unit_stats = stats['units'] scored_highest = highest_scorer == player.login if survived and game.game_mode == 'ladder1v1': self._unlock(ACH_FIRST_SUCCESS, a_queue) self._increment(ACH_NOVICE, 1, a_queue) self._increment(ACH_JUNIOR, 1, a_queue) self._increment(ACH_SENIOR, 1, a_queue) self._increment(ACH_VETERAN, 1, a_queue) self._increment(ACH_ADDICT, 1, a_queue) self._faction_played(faction, survived, a_queue, e_queue) self._category_stats(unit_stats, survived, a_queue, e_queue) self._killed_acus(unit_stats, survived, a_queue) self._built_mercies(_count_built_units(blueprint_stats, Unit.MERCY), a_queue) self._built_fire_beetles(_count_built_units(blueprint_stats, Unit.FIRE_BEETLE), a_queue) self._built_salvations(_count_built_units(blueprint_stats, Unit.SALVATION), survived, a_queue) self._built_yolona_oss(_count_built_units(blueprint_stats, Unit.YOLONA_OSS), survived, a_queue) self._built_paragons(_count_built_units(blueprint_stats, Unit.PARAGON), survived, a_queue) self._built_atlantis(_count_built_units(blueprint_stats, Unit.ATLANTIS), a_queue) self._built_tempests(_count_built_units(blueprint_stats, Unit.TEMPEST), a_queue) self._built_scathis(_count_built_units(blueprint_stats, Unit.SCATHIS), survived, a_queue) self._built_mavors(_count_built_units(blueprint_stats, Unit.MAVOR), survived, a_queue) self._built_czars(_count_built_units(blueprint_stats, Unit.CZAR), a_queue) self._built_ahwassas(_count_built_units(blueprint_stats, Unit.AHWASSA), a_queue) self._built_ythothas(_count_built_units(blueprint_stats, Unit.YTHOTHA), a_queue) self._built_fatboys(_count_built_units(blueprint_stats, Unit.FATBOY), a_queue) self._built_monkeylords(_count_built_units(blueprint_stats, Unit.MONKEYLORD), a_queue) self._built_galactic_colossus(_count_built_units(blueprint_stats, Unit.GALACTIC_COLOSSUS), a_queue) self._built_soul_rippers(_count_built_units(blueprint_stats, Unit.SOUL_RIPPER), a_queue) self._built_megaliths(_count_built_units(blueprint_stats, Unit.MEGALITH), a_queue) self._built_asfs(_count_built_units(blueprint_stats, *ASFS), a_queue) self._built_transports(unit_stats['transportation'].get('built', 0), a_queue) self._built_sacus(unit_stats['sacu'].get('built', 0), a_queue) self._lowest_acu_health(_count(blueprint_stats, lambda x: x.get('lowest_health', 0), *ACUS), survived, a_queue) self._highscore(scored_highest, number_of_humans, a_queue) updated_achievements = await self._achievement_service.execute_batch_update(player.id, a_queue) await self._event_service.execute_batch_update(player.id, e_queue) if player.lobby_connection is not None: player.lobby_connection.send_updated_achievements(updated_achievements)