diff --git a/server/ladder_service/ladder_service.py b/server/ladder_service/ladder_service.py
index 1f75d251d..89fc810d6 100644
--- a/server/ladder_service/ladder_service.py
+++ b/server/ladder_service/ladder_service.py
@@ -339,20 +339,27 @@ def start_search(
on_matched=on_matched
)
- for player in players:
+ self._add_search_to_queue(search, queue)
+
+ def _add_search_to_queue(
+ self,
+ search: Search,
+ queue: MatchmakerQueue,
+ ):
+ for player in search.players:
player.state = PlayerState.SEARCHING_LADDER
self.write_rating_progress(player, queue.rating_type)
player.write_message({
"command": "search_info",
- "queue_name": queue_name,
+ "queue_name": queue.name,
"state": "start"
})
- self._searches[player][queue_name] = search
+ self._searches[player][queue.name] = search
- self._logger.info("%s started searching for %s", search, queue_name)
+ self._logger.info("%s started searching for %s", search, queue.name)
asyncio.create_task(queue.search(search))
@@ -418,26 +425,28 @@ def _clear_search(
return search
def write_rating_progress(self, player: Player, rating_type: str) -> None:
- if player not in self._informed_players:
- self._informed_players.add(player)
- _, deviation = player.ratings[rating_type]
+ if player in self._informed_players:
+ return
- if deviation > 490:
- player.write_message({
- "command": "notice",
- "style": "info",
- "text": (
- "Welcome to the matchmaker
The "
- "matchmaking system needs to calibrate your skill level; "
- "your first few games may be more imbalanced as the "
- "system attempts to learn your capability as a player."
- "
"
- "Afterwards, you'll be more reliably matched up with "
- "people of your skill level: so don't worry if your "
- "first few games are uneven. This will improve as you "
- "play!"
- )
- })
+ self._informed_players.add(player)
+ _, deviation = player.ratings[rating_type]
+
+ if deviation > 490:
+ player.write_message({
+ "command": "notice",
+ "style": "info",
+ "text": (
+ "Welcome to the matchmaker
The "
+ "matchmaking system needs to calibrate your skill level; "
+ "your first few games may be more imbalanced as the "
+ "system attempts to learn your capability as a player."
+ "
"
+ "Afterwards, you'll be more reliably matched up with "
+ "people of your skill level: so don't worry if your "
+ "first few games are uneven. This will improve as you "
+ "play!"
+ )
+ })
def on_match_found(
self,
@@ -504,16 +513,29 @@ async def confirm_match(
player.state = PlayerState.IDLE
player.write_message(msg)
- # Return any player that accepted the match back to the queue
- # TODO: make this work with parties
+ # Return any search that fully accepted the match back to the queue
for search in (s1, s2):
- for player in search.players:
- if player in unready_players:
- self.cancel_search(player)
- else:
- search.unmatch()
- player.state = PlayerState.SEARCHING_LADDER
- asyncio.create_task(queue.search(search))
+ search_players = search.players
+ search_unready_players = [
+ player
+ for player in unready_players
+ if player in search_players
+ ]
+ if not search_unready_players:
+ search.unmatch()
+ self._add_search_to_queue(search, queue)
+ self._logger.debug(
+ "%s auto requeued after failed match",
+ search
+ )
+ else:
+ for player in search_players:
+ player.write_message({
+ "command": "match_notice",
+ "unready_players": [
+ p.id for p in search_unready_players
+ ]
+ })
self.violation_service.register_violations(unready_players)
diff --git a/tests/integration_tests/test_game.py b/tests/integration_tests/test_game.py
index cbfe1cd2d..bc68a537e 100644
--- a/tests/integration_tests/test_game.py
+++ b/tests/integration_tests/test_game.py
@@ -219,13 +219,14 @@ async def queue_temp_players_for_matchmaking(
tmp_user,
num_players,
queue_name,
+ player_name=None,
):
"""
Queue an arbitrary number of players for matchmaking in a particular queue
by setting up temp users.
"""
users = await asyncio.gather(*[
- tmp_user(queue_name)
+ tmp_user(player_name or queue_name)
for _ in range(num_players)
])
responses = await asyncio.gather(*[
diff --git a/tests/integration_tests/test_matchmaker.py b/tests/integration_tests/test_matchmaker.py
index f9d2589ec..01f4cf3b6 100644
--- a/tests/integration_tests/test_matchmaker.py
+++ b/tests/integration_tests/test_matchmaker.py
@@ -418,6 +418,57 @@ async def test_game_matchmaking_close_fa_and_requeue(lobby_server):
await read_until_command(proto1, "match_found", timeout=5)
+@fast_forward(130)
+async def test_game_matchmaking_no_accept_offer_auto_requeue(
+ lobby_server,
+ tmp_user,
+):
+ proto1, proto2 = await queue_temp_players_for_matchmaking(
+ lobby_server,
+ tmp_user,
+ num_players=2,
+ queue_name="ladder1v1",
+ player_name="Player",
+ )
+
+ await read_until_command(proto1, "match_found", timeout=30)
+ await read_until_command(proto2, "match_found", timeout=5)
+
+ # Only player 1 accepts the match
+ await read_until_command(proto1, "match_info", timeout=5)
+ await read_until_command(proto2, "match_info", timeout=5)
+ await proto1.send_message({"command": "match_ready"})
+ await read_until_command(proto1, "match_cancelled", timeout=120)
+
+ # Player 1 is automatically re-added to the queue, but player 2 is not
+ await read_until_command(proto1, "search_info", state="start", timeout=5)
+ msg = await read_until_command(proto1, "matchmaker_info", timeout=5)
+ queue_message = next(
+ queue
+ for queue in msg["queues"]
+ if queue["queue_name"] == "ladder1v1"
+ )
+ assert queue_message["num_players"] == 1
+
+ # A third player joins the queue and is matched with player 1
+ proto3, = await queue_temp_players_for_matchmaking(
+ lobby_server,
+ tmp_user,
+ num_players=1,
+ queue_name="ladder1v1",
+ player_name="Player",
+ )
+
+ await asyncio.gather(*[
+ read_until_match(proto)
+ for proto in (proto1, proto3)
+ ])
+ await asyncio.gather(
+ client_response(proto1),
+ client_response(proto3)
+ )
+
+
@pytest.mark.flaky
@fast_forward(200)
async def test_anti_map_repetition(lobby_server):
@@ -567,7 +618,9 @@ async def read_update_msg():
msg = await read_until_command(proto, "matchmaker_info")
queue_message = next(
- q for q in msg["queues"] if q["queue_name"] == "ladder1v1"
+ queue
+ for queue in msg["queues"]
+ if queue["queue_name"] == "ladder1v1"
)
if queue_message["num_players"] == 0:
continue
@@ -586,7 +639,11 @@ async def read_update_msg():
# Update message because we left the queue
msg = await read_until_command(proto, "matchmaker_info")
- queue_message = next(q for q in msg["queues"] if q["queue_name"] == "ladder1v1")
+ queue_message = next(
+ queue
+ for queue in msg["queues"]
+ if queue["queue_name"] == "ladder1v1"
+ )
assert queue_message["num_players"] == 0