Improve client matchmaking flow with ticket handling, auto-join, and mode distribution
### PlayerWebSocketHandler updates - Track `ticket` and `match_id` per player instance - Handle `matchmaker_ticket` messages and store ticket - Handle `matchmaker_matched` and automatically join created match - Enhance matchmaking debug output - Update join_matchmaking() to include mode-based string_properties + query ### Matchmaking simulation improvements - Evenly distribute players between "classic" and "blitz" modes - Randomize assignment order to simulate real queue behavior - Log player→mode mapping for visibility during tests Example: player_0 -> classic player_3 -> blitz player_5 -> classic Client test harness now accurately reflects multi-mode matchmaking behavior.
This commit is contained in:
22
game_flow.py
22
game_flow.py
@@ -96,7 +96,23 @@ class PlayerWebSocketHandler(WebSocketHandler):
|
|||||||
await handler.connect()
|
await handler.connect()
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
def __init__(self, custom_id: str, label: str):
|
||||||
|
super().__init__(custom_id, label)
|
||||||
|
self.ticket = None
|
||||||
|
self.match_id = None
|
||||||
|
|
||||||
async def on_message(self, msg):
|
async def on_message(self, msg):
|
||||||
|
if "matchmaker_matched" in msg:
|
||||||
|
match_id = msg["matchmaker_matched"]["match_id"]
|
||||||
|
print(f"[{self.label}] ✅ Match found: {match_id}")
|
||||||
|
await self.join_match(match_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if "matchmaker_ticket" in msg:
|
||||||
|
self.ticket = msg["matchmaker_ticket"]["ticket"]
|
||||||
|
print(f"[{self.label}] ✅ Received ticket: {self.ticket}")
|
||||||
|
return
|
||||||
|
|
||||||
if "match_data" not in msg:
|
if "match_data" not in msg:
|
||||||
print(f"[{self.label}] {msg}")
|
print(f"[{self.label}] {msg}")
|
||||||
return
|
return
|
||||||
@@ -124,9 +140,8 @@ class PlayerWebSocketHandler(WebSocketHandler):
|
|||||||
"matchmaker_add": {
|
"matchmaker_add": {
|
||||||
"min_count": 2,
|
"min_count": 2,
|
||||||
"max_count": 2,
|
"max_count": 2,
|
||||||
"string_properties": {
|
"string_properties": {"mode": mode},
|
||||||
"mode": mode
|
"query": f"+mode:{mode}"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
print(f"[{self.label}] Searching match for mode={mode}...")
|
print(f"[{self.label}] Searching match for mode={mode}...")
|
||||||
@@ -151,6 +166,7 @@ class PlayerWebSocketHandler(WebSocketHandler):
|
|||||||
async def join_match(self, match_id: str):
|
async def join_match(self, match_id: str):
|
||||||
await self.ws.send(json.dumps({"match_join": {"match_id": match_id}}))
|
await self.ws.send(json.dumps({"match_join": {"match_id": match_id}}))
|
||||||
print(f"[{self.label}] Joined match: {match_id}")
|
print(f"[{self.label}] Joined match: {match_id}")
|
||||||
|
self.match_id = match_id
|
||||||
|
|
||||||
# ---------- Gameplay ----------
|
# ---------- Gameplay ----------
|
||||||
async def send_move(self, match_id: str, row: int, col: int):
|
async def send_move(self, match_id: str, row: int, col: int):
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from game_flow import PlayerWebSocketHandler
|
import random
|
||||||
|
from game_flow import PlayerWebSocketHandler, TEST_SCENARIOS
|
||||||
|
|
||||||
|
|
||||||
async def simulate_matchmaking(num_players: int = 6, mode: str = "classic"):
|
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 + WebSocket connect
|
||||||
@@ -21,12 +22,20 @@ async def simulate_matchmaking(num_players: int = 6, mode: str = "classic"):
|
|||||||
|
|
||||||
await asyncio.sleep(0.3)
|
await asyncio.sleep(0.3)
|
||||||
|
|
||||||
# 3) Queue all players in matchmaking
|
# ✅ 3) Split evenly between classic & blitz
|
||||||
print(f"\n🎯 Queuing players for mode={mode}...\n")
|
half = num_players // 2
|
||||||
|
assignments = ["classic"] * half + ["blitz"] * (num_players - half)
|
||||||
|
|
||||||
|
# Optional — shuffle for realism
|
||||||
|
random.shuffle(assignments)
|
||||||
|
|
||||||
|
print("\n🎯 Queuing players:")
|
||||||
|
for p, mode in zip(players, assignments):
|
||||||
|
print(f" - {p.label} -> {mode}")
|
||||||
|
|
||||||
await asyncio.gather(*[
|
await asyncio.gather(*[
|
||||||
p.join_matchmaking(mode)
|
p.join_matchmaking(mode)
|
||||||
for p in players
|
for p, mode in zip(players, assignments)
|
||||||
])
|
])
|
||||||
|
|
||||||
print("\n✅ All players queued — waiting for matches...\n")
|
print("\n✅ All players queued — waiting for matches...\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user