Skip to content

Commit 02e1939

Browse files
committed
WIP: Make re-queue work for parties
1 parent 378ba27 commit 02e1939

3 files changed

Lines changed: 115 additions & 35 deletions

File tree

server/ladder_service/ladder_service.py

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -339,20 +339,27 @@ def start_search(
339339
on_matched=on_matched
340340
)
341341

342-
for player in players:
342+
self._add_search_to_queue(search, queue)
343+
344+
def _add_search_to_queue(
345+
self,
346+
search: Search,
347+
queue: MatchmakerQueue,
348+
):
349+
for player in search.players:
343350
player.state = PlayerState.SEARCHING_LADDER
344351

345352
self.write_rating_progress(player, queue.rating_type)
346353

347354
player.write_message({
348355
"command": "search_info",
349-
"queue_name": queue_name,
356+
"queue_name": queue.name,
350357
"state": "start"
351358
})
352359

353-
self._searches[player][queue_name] = search
360+
self._searches[player][queue.name] = search
354361

355-
self._logger.info("%s started searching for %s", search, queue_name)
362+
self._logger.info("%s started searching for %s", search, queue.name)
356363

357364
asyncio.create_task(queue.search(search))
358365

@@ -418,26 +425,28 @@ def _clear_search(
418425
return search
419426

420427
def write_rating_progress(self, player: Player, rating_type: str) -> None:
421-
if player not in self._informed_players:
422-
self._informed_players.add(player)
423-
_, deviation = player.ratings[rating_type]
428+
if player in self._informed_players:
429+
return
424430

425-
if deviation > 490:
426-
player.write_message({
427-
"command": "notice",
428-
"style": "info",
429-
"text": (
430-
"<i>Welcome to the matchmaker</i><br><br><b>The "
431-
"matchmaking system needs to calibrate your skill level; "
432-
"your first few games may be more imbalanced as the "
433-
"system attempts to learn your capability as a player."
434-
"</b><br><b>"
435-
"Afterwards, you'll be more reliably matched up with "
436-
"people of your skill level: so don't worry if your "
437-
"first few games are uneven. This will improve as you "
438-
"play!</b>"
439-
)
440-
})
431+
self._informed_players.add(player)
432+
_, deviation = player.ratings[rating_type]
433+
434+
if deviation > 490:
435+
player.write_message({
436+
"command": "notice",
437+
"style": "info",
438+
"text": (
439+
"<i>Welcome to the matchmaker</i><br><br><b>The "
440+
"matchmaking system needs to calibrate your skill level; "
441+
"your first few games may be more imbalanced as the "
442+
"system attempts to learn your capability as a player."
443+
"</b><br><b>"
444+
"Afterwards, you'll be more reliably matched up with "
445+
"people of your skill level: so don't worry if your "
446+
"first few games are uneven. This will improve as you "
447+
"play!</b>"
448+
)
449+
})
441450

442451
def on_match_found(
443452
self,
@@ -504,16 +513,29 @@ async def confirm_match(
504513
player.state = PlayerState.IDLE
505514
player.write_message(msg)
506515

507-
# Return any player that accepted the match back to the queue
508-
# TODO: make this work with parties
516+
# Return any search that fully accepted the match back to the queue
509517
for search in (s1, s2):
510-
for player in search.players:
511-
if player in unready_players:
512-
self.cancel_search(player)
513-
else:
514-
search.unmatch()
515-
player.state = PlayerState.SEARCHING_LADDER
516-
asyncio.create_task(queue.search(search))
518+
search_players = search.players
519+
search_unready_players = [
520+
player
521+
for player in unready_players
522+
if player in search_players
523+
]
524+
if not search_unready_players:
525+
search.unmatch()
526+
self._add_search_to_queue(search, queue)
527+
self._logger.debug(
528+
"%s auto requeued after failed match",
529+
search
530+
)
531+
else:
532+
for player in search_players:
533+
player.write_message({
534+
"command": "match_notice",
535+
"unready_players": [
536+
p.id for p in search_unready_players
537+
]
538+
})
517539

518540
self.violation_service.register_violations(unready_players)
519541

tests/integration_tests/test_game.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,14 @@ async def queue_temp_players_for_matchmaking(
219219
tmp_user,
220220
num_players,
221221
queue_name,
222+
player_name=None,
222223
):
223224
"""
224225
Queue an arbitrary number of players for matchmaking in a particular queue
225226
by setting up temp users.
226227
"""
227228
users = await asyncio.gather(*[
228-
tmp_user(queue_name)
229+
tmp_user(player_name or queue_name)
229230
for _ in range(num_players)
230231
])
231232
responses = await asyncio.gather(*[

tests/integration_tests/test_matchmaker.py

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,57 @@ async def test_game_matchmaking_close_fa_and_requeue(lobby_server):
418418
await read_until_command(proto1, "match_found", timeout=5)
419419

420420

421+
@fast_forward(130)
422+
async def test_game_matchmaking_no_accept_offer_auto_requeue(
423+
lobby_server,
424+
tmp_user,
425+
):
426+
proto1, proto2 = await queue_temp_players_for_matchmaking(
427+
lobby_server,
428+
tmp_user,
429+
num_players=2,
430+
queue_name="ladder1v1",
431+
player_name="Player",
432+
)
433+
434+
await read_until_command(proto1, "match_found", timeout=30)
435+
await read_until_command(proto2, "match_found", timeout=5)
436+
437+
# Only player 1 accepts the match
438+
await read_until_command(proto1, "match_info", timeout=5)
439+
await read_until_command(proto2, "match_info", timeout=5)
440+
await proto1.send_message({"command": "match_ready"})
441+
await read_until_command(proto1, "match_cancelled", timeout=120)
442+
443+
# Player 1 is automatically re-added to the queue, but player 2 is not
444+
await read_until_command(proto1, "search_info", state="start", timeout=5)
445+
msg = await read_until_command(proto1, "matchmaker_info", timeout=5)
446+
queue_message = next(
447+
queue
448+
for queue in msg["queues"]
449+
if queue["queue_name"] == "ladder1v1"
450+
)
451+
assert queue_message["num_players"] == 1
452+
453+
# A third player joins the queue and is matched with player 1
454+
proto3, = await queue_temp_players_for_matchmaking(
455+
lobby_server,
456+
tmp_user,
457+
num_players=1,
458+
queue_name="ladder1v1",
459+
player_name="Player",
460+
)
461+
462+
await asyncio.gather(*[
463+
read_until_match(proto)
464+
for proto in (proto1, proto3)
465+
])
466+
await asyncio.gather(
467+
client_response(proto1),
468+
client_response(proto3)
469+
)
470+
471+
421472
@pytest.mark.flaky
422473
@fast_forward(200)
423474
async def test_anti_map_repetition(lobby_server):
@@ -567,7 +618,9 @@ async def read_update_msg():
567618
msg = await read_until_command(proto, "matchmaker_info")
568619

569620
queue_message = next(
570-
q for q in msg["queues"] if q["queue_name"] == "ladder1v1"
621+
queue
622+
for queue in msg["queues"]
623+
if queue["queue_name"] == "ladder1v1"
571624
)
572625
if queue_message["num_players"] == 0:
573626
continue
@@ -586,7 +639,11 @@ async def read_update_msg():
586639
# Update message because we left the queue
587640
msg = await read_until_command(proto, "matchmaker_info")
588641

589-
queue_message = next(q for q in msg["queues"] if q["queue_name"] == "ladder1v1")
642+
queue_message = next(
643+
queue
644+
for queue in msg["queues"]
645+
if queue["queue_name"] == "ladder1v1"
646+
)
590647
assert queue_message["num_players"] == 0
591648

592649

0 commit comments

Comments
 (0)