fix(matchmaking): correctly group players only after successful match join
- 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.
This commit is contained in:
@@ -6,7 +6,7 @@ from game_flow import PlayerWebSocketHandler, TEST_SCENARIOS
|
|||||||
async def simulate_matchmaking(num_players: int = 6):
|
async def simulate_matchmaking(num_players: int = 6):
|
||||||
print(f"\n🎮 Spawning {num_players} players...\n")
|
print(f"\n🎮 Spawning {num_players} players...\n")
|
||||||
|
|
||||||
# 1) Login + WebSocket connect
|
# 1) Login + connect
|
||||||
players = await asyncio.gather(*[
|
players = await asyncio.gather(*[
|
||||||
PlayerWebSocketHandler.setup_player(f"player_{i}")
|
PlayerWebSocketHandler.setup_player(f"player_{i}")
|
||||||
for i in range(num_players)
|
for i in range(num_players)
|
||||||
@@ -19,14 +19,11 @@ async def simulate_matchmaking(num_players: int = 6):
|
|||||||
p.start_listener()
|
p.start_listener()
|
||||||
|
|
||||||
print("\n👂 WebSocket listeners active\n")
|
print("\n👂 WebSocket listeners active\n")
|
||||||
|
|
||||||
await asyncio.sleep(0.3)
|
await asyncio.sleep(0.3)
|
||||||
|
|
||||||
# ✅ 3) Split evenly between classic & blitz
|
# ✅ 3) Split evenly between classic & blitz
|
||||||
half = num_players // 2
|
half = num_players // 2
|
||||||
assignments = ["classic"] * half + ["blitz"] * (num_players - half)
|
assignments = ["classic"] * half + ["blitz"] * (num_players - half)
|
||||||
|
|
||||||
# Optional — shuffle for realism
|
|
||||||
random.shuffle(assignments)
|
random.shuffle(assignments)
|
||||||
|
|
||||||
print("\n🎯 Queuing players:")
|
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")
|
print("\n✅ All players queued — waiting for matches...\n")
|
||||||
|
|
||||||
# 4) Allow enough time for Nakama matchmaker to group players
|
# ✅ 4) Collect matches
|
||||||
await asyncio.sleep(12)
|
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")
|
print("\n🧹 Closing player connections...\n")
|
||||||
await asyncio.gather(*[p.close() for p in players])
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user