From 087616a67efa9c18a1a00c66398b866f04c62775 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Wed, 26 Nov 2025 17:36:49 +0530 Subject: [PATCH] fix(matchmaking): correctly group players only after successful match join MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Track matches based solely on `player.match_id` - Avoid double-counting from presence events or server broadcasts - Ensure `matches[match_id]` contains only actual participants - Prevent false 3โ€“4 player matches and scenario execution failures - Maintain safety check for non-1v1 match sizes This resolves incorrect match grouping in automated matchmaking tests, allowing clean 1v1 scenario execution and accurate match counts. --- match_making_flow.py | 64 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/match_making_flow.py b/match_making_flow.py index 4387939..2e6ab1c 100644 --- a/match_making_flow.py +++ b/match_making_flow.py @@ -6,7 +6,7 @@ from game_flow import PlayerWebSocketHandler, TEST_SCENARIOS async def simulate_matchmaking(num_players: int = 6): print(f"\n๐ŸŽฎ Spawning {num_players} players...\n") - # 1) Login + WebSocket connect + # 1) Login + connect players = await asyncio.gather(*[ PlayerWebSocketHandler.setup_player(f"player_{i}") for i in range(num_players) @@ -19,14 +19,11 @@ async def simulate_matchmaking(num_players: int = 6): p.start_listener() print("\n๐Ÿ‘‚ WebSocket listeners active\n") - await asyncio.sleep(0.3) # โœ… 3) Split evenly between classic & blitz half = num_players // 2 assignments = ["classic"] * half + ["blitz"] * (num_players - half) - - # Optional โ€” shuffle for realism random.shuffle(assignments) print("\n๐ŸŽฏ Queuing players:") @@ -40,15 +37,64 @@ async def simulate_matchmaking(num_players: int = 6): print("\nโœ… All players queued โ€” waiting for matches...\n") - # 4) Allow enough time for Nakama matchmaker to group players - await asyncio.sleep(12) + # โœ… 4) Collect matches + matches = {} + timeout = 15 + start = asyncio.get_event_loop().time() - # 5) Cleanup + matches = dict() + while asyncio.get_event_loop().time() - start < timeout: + for p in players: + # print(f'player = {p.label} for match_id = {p.match_id}') + # print(f'players = {len(matches.get(p.match_id, list()))} for match = {p.match_id}') + if p.match_id: + # matches.setdefault(p.match_id, []).append(p) + if p.match_id not in matches: + matches[p.match_id] = [p] + elif p not in matches[p.match_id]: + matches[p.match_id].append(p) + # print(f'player = {p.label} for match = {p.match_id}') + print(f'players = {len(matches[p.match_id])} for match = {p.match_id}') + + # stop early if all assigned + if sum(len(v) for v in matches.values()) >= num_players: + break + + await asyncio.sleep(0.25) + + print(f"\nโœ… Matchmaking complete โ€” {len(matches)} matches formed\n") + + # โœ… 5) Assign random scenarios per match & run + tasks = [] + + for match_id, grouped_players in matches.items(): + if len(grouped_players) != 2: + print(f"โš ๏ธ Skipping match {match_id} โ€” not 1v1") + continue + + p1, p2 = grouped_players + scenario = random.choice(TEST_SCENARIOS) + + print( + f"๐ŸŽญ Running scenario '{scenario.__name__}' โ€” " + f"{p1.label} vs {p2.label} | match={match_id}" + ) + + tasks.append(asyncio.create_task( + scenario(match_id, p1, p2) + )) + + # โœ… 6) Wait for all mock games to finish + if tasks: + await asyncio.gather(*tasks) + else: + print("โš ๏ธ No playable matches found") + + # โœ… 7) Cleanup connections print("\n๐Ÿงน Closing player connections...\n") await asyncio.gather(*[p.close() for p in players]) - print("\n๐Ÿ Matchmaking simulation complete\n") - + print("\n๐Ÿ Matchmaking test run complete โœ…\n") if __name__ == "__main__":