diff --git a/server/ladder_service.py b/server/ladder_service.py index c0163eae3..83e626de9 100644 --- a/server/ladder_service.py +++ b/server/ladder_service.py @@ -373,10 +373,6 @@ async def start_game( all_players = team1 + team2 all_guests = all_players[1:] - host.state = PlayerState.HOSTING - for guest in all_guests: - guest.state = PlayerState.JOINING - played_map_ids = await self.get_game_history( all_players, queue.id, @@ -475,7 +471,8 @@ def game_options(player: Player) -> GameLaunchOptions: msg = {"command": "match_cancelled"} for player in all_players: - player.state = PlayerState.IDLE + if player.state == PlayerState.STARTING_AUTOMATCH: + player.state = PlayerState.IDLE player.write_message(msg) async def get_game_history( diff --git a/tests/integration_tests/test_matchmaker.py b/tests/integration_tests/test_matchmaker.py index 098825413..adf4ddcb2 100644 --- a/tests/integration_tests/test_matchmaker.py +++ b/tests/integration_tests/test_matchmaker.py @@ -148,13 +148,27 @@ async def test_game_matchmaking_timeout(lobby_server, game_service): ) assert game_service._games == {} - # Player's state is reset so they are able to queue again + # Player's state is reset once they leave the game await proto1.send_message({ + "command": "GameState", + "target": "game", + "args": ["Ended"] + }) + await proto1.send_message({ + "command": "game_matchmaking", + "state": "start", + "faction": "uef" + }) + await read_until_command(proto1, "search_info", state="start", timeout=5) + + # And not before they've left the game + await proto2.send_message({ "command": "game_matchmaking", "state": "start", "faction": "uef" }) - await read_until_command(proto1, "search_info", state="start", timeout=15) + with pytest.raises(asyncio.TimeoutError): + await read_until_command(proto2, "search_info", state="start", timeout=5) @fast_forward(120) @@ -182,7 +196,12 @@ async def test_game_matchmaking_timeout_guest(lobby_server, game_service): ) assert game_service._games == {} - # Player's state is reset so they are able to queue again + # Player's state is reset once they leave the game + await proto1.send_message({ + "command": "GameState", + "target": "game", + "args": ["Ended"] + }) await proto1.send_message({ "command": "game_matchmaking", "state": "start", @@ -190,6 +209,15 @@ async def test_game_matchmaking_timeout_guest(lobby_server, game_service): }) await read_until_command(proto1, "search_info", state="start", timeout=5) + # And not before they've left the game + await proto2.send_message({ + "command": "game_matchmaking", + "state": "start", + "faction": "uef" + }) + with pytest.raises(asyncio.TimeoutError): + await read_until_command(proto2, "search_info", state="start", timeout=5) + @fast_forward(15) async def test_game_matchmaking_cancel(lobby_server): @@ -234,6 +262,44 @@ async def test_game_matchmaking_disconnect(lobby_server): assert msg == {"command": "match_cancelled"} +@fast_forward(130) +async def test_game_matchmaking_close_fa_and_requeue(lobby_server): + proto1, proto2 = await queue_players_for_matchmaking(lobby_server) + + _, _ = await asyncio.gather( + client_response(proto1), + client_response(proto2) + ) + # Players can't connect to eachother, so one of them abandons the game and + # joins the queue again + await proto1.send_message({ + "command": "GameState", + "target": "game", + "args": ["Ended"] + }) + await proto1.send_message({ + "command": "game_matchmaking", + "state": "start", + "queue": "ladder1v1" + }) + await read_until_command(proto1, "search_info", state="start", timeout=5) + + # The other player waits for the game to time out and then queues again + await read_until_command(proto2, "match_cancelled", timeout=120) + await proto2.send_message({ + "command": "GameState", + "target": "game", + "args": ["Ended"] + }) + await proto2.send_message({ + "command": "game_matchmaking", + "state": "start", + "queue": "ladder1v1" + }) + + await read_until_command(proto1, "match_found", timeout=5) + + @fast_forward(10) async def test_matchmaker_info_message(lobby_server, mocker): mocker.patch("server.matchmaker.pop_timer.time", return_value=1_562_000_000) diff --git a/tests/integration_tests/test_teammatchmaker.py b/tests/integration_tests/test_teammatchmaker.py index 593d246d5..b91753651 100644 --- a/tests/integration_tests/test_teammatchmaker.py +++ b/tests/integration_tests/test_teammatchmaker.py @@ -282,7 +282,7 @@ async def test_newbie_matchmaking_with_parties(lobby_server): assert msg["map_position"] in (1, 2, 3, 4) -@fast_forward(60) +@fast_forward(120) async def test_game_matchmaking_multiqueue_timeout(lobby_server): protos, _ = await connect_players(lobby_server) @@ -306,6 +306,7 @@ async def test_game_matchmaking_multiqueue_timeout(lobby_server): }) for proto in protos ]) + await read_until_command(protos[1], "search_info", state="start") msg = await read_until_command( protos[0], "search_info", @@ -316,7 +317,12 @@ async def test_game_matchmaking_multiqueue_timeout(lobby_server): # Don't send any GPGNet messages so the match times out await read_until_command(protos[0], "match_cancelled", timeout=120) - # Player's state is reset so they are able to queue again + # Player's state is reset once they leave the game + await protos[0].send_message({ + "command": "GameState", + "target": "game", + "args": ["Ended"] + }) await protos[0].send_message({ "command": "game_matchmaking", "state": "start", @@ -330,6 +336,15 @@ async def test_game_matchmaking_multiqueue_timeout(lobby_server): timeout=5 ) + # And not before they've left the game + await protos[1].send_message({ + "command": "game_matchmaking", + "state": "start", + "faction": "uef" + }) + with pytest.raises(asyncio.TimeoutError): + await read_until_command(protos[1], "search_info", state="start", timeout=5) + @fast_forward(60) async def test_game_matchmaking_multiqueue_multimatch(lobby_server): @@ -395,7 +410,7 @@ def other_cancelled(msg): assert msg1["state"] == "stop" -@fast_forward(60) +@fast_forward(120) async def test_game_matchmaking_timeout(lobby_server): protos, _ = await queue_players_for_matchmaking(lobby_server) @@ -404,7 +419,12 @@ async def test_game_matchmaking_timeout(lobby_server): read_until_command(proto, "match_cancelled", timeout=120) for proto in protos ]) - # Player's state is reset so they are able to queue again + # Player's state is reset once they leave the game + await protos[0].send_message({ + "command": "GameState", + "target": "game", + "args": ["Ended"] + }) await protos[0].send_message({ "command": "game_matchmaking", "state": "start", @@ -418,6 +438,15 @@ async def test_game_matchmaking_timeout(lobby_server): timeout=5 ) + # And not before they've left the game + await protos[1].send_message({ + "command": "game_matchmaking", + "state": "start", + "faction": "uef" + }) + with pytest.raises(asyncio.TimeoutError): + await read_until_command(protos[1], "search_info", state="start", timeout=5) + @fast_forward(60) async def test_game_ratings(lobby_server): diff --git a/tests/unit_tests/test_ladder_service.py b/tests/unit_tests/test_ladder_service.py index 0f383a6ea..450ead13a 100644 --- a/tests/unit_tests/test_ladder_service.py +++ b/tests/unit_tests/test_ladder_service.py @@ -136,7 +136,7 @@ async def test_start_game_1v1( game = game_service[game_service.game_id_counter] assert player1.lobby_connection.launch_game.called - # TODO: Once client supports `game_launch_timeout` change this to `assert not ...` + # TODO: Once client supports `match_cancelled` change this to `assert not` assert player2.lobby_connection.launch_game.called assert isinstance(game, LadderGame) assert game.rating_type == queue.rating_type @@ -159,10 +159,11 @@ async def test_start_game_timeout( p1.lobby_connection.write.assert_called_once_with({"command": "match_cancelled"}) p2.lobby_connection.write.assert_called_once_with({"command": "match_cancelled"}) assert p1.lobby_connection.launch_game.called - # TODO: Once client supports `match_cancelled` change this to `assert not ...` + # TODO: Once client supports `match_cancelled` change this to `assert not` + # and uncomment the following lines. assert p2.lobby_connection.launch_game.called - assert p1.state is PlayerState.IDLE - assert p2.state is PlayerState.IDLE + # assert p1.state is PlayerState.IDLE + # assert p2.state is PlayerState.IDLE @given(