Flutter/.NET Core/SignalR双客户端同步页面跳转问题排查
问题描述
已正确配置后端Program.cs及相关URL信息,本地使用两个不同浏览器通过HTTP/Ws的WebSocket测试时,无法实现两个客户端同步跳转至对战页面。目前仅一个客户端能完成跳转,该跳转来自按钮点击的导航逻辑,而非SignalR的MatchFound事件触发的导航。希望通过监听MatchFound事件实现双客户端同步跳转。
MatchMakingController.cs
[HttpPost("joinQueue")] public async Task<IActionResult> JoinQueue([FromBody] JoinQueueModel model) { Console.WriteLine("1: JoinQueue called"); int playerCharacterId = model.PlayerCharacterId; // Check if player is already in queue to prevent duplicates if (!matchmakingQueue.Contains(playerCharacterId)) { matchmakingQueue.Add(playerCharacterId); } // Try to match players if there are at least 2 in the queue if (matchmakingQueue.Count >= 2) { Console.WriteLine("2: Matchmaking logic processing"); int player1Id = matchmakingQueue[0]; int player2Id = matchmakingQueue[1]; matchmakingQueue.RemoveRange(0, 2); // Remove matched players from the queue var match = new Match { Player1ID = player1Id, Player2ID = player2Id, Status = "in progress" }; _context.Matches.Add(match); await _context.SaveChangesAsync(); // Inside your JoinQueue method, after saving the match await _hubContext.Clients.All.SendAsync("MatchFound", new { matchId = match.MatchID, player1Id, player2Id }); return Ok(new { matchId = match.MatchID, player1Id, player2Id }); } return Ok(new { message = "Waiting for an opponent..." }); }
前端SignalR服务代码
late HubConnection _hubConnection; final String serverUrl = "http://localhost:5111/battleHub"; final void Function(int matchId, int player1Id, int player2Id) onMatchFound; SignalRService({required this.onMatchFound}) { // Now a named parameter _hubConnection = HubConnectionBuilder().withUrl(serverUrl).build(); initConnection(); } Future<void> initConnection() async { if (_hubConnection.state == HubConnectionState.Disconnected) { try { await _hubConnection.start(); print("Connection Started"); _setupListeners(); } catch (e) { print("Error starting connection: ${e.toString()}"); } } } void _setupListeners() { _hubConnection.on('MatchFound', (arguments) { print('MatchFound event received'); if (arguments != null && arguments.length >= 3) { final int matchId = arguments[0] as int; final int player1Id = arguments[1] as int; final int player2Id = arguments[2] as int; onMatchFound(matchId, player1Id, player2Id); } }); }
Api服务代码
Future<MatchmakingResponse?> joinMatchmakingQueue( int playerCharacterId) async { print('Joining queue with playerCharacterId: $playerCharacterId'); final response = await http.post( Uri.parse('http://localhost:5111/api/matchmaking/joinQueue'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode({'playerCharacterId': playerCharacterId}), ); print('Response body1: ${response.body}'); if (response.statusCode == 200) { final data = jsonDecode(response.body); // Adjusted to return a MatchmakingResponse object return MatchmakingResponse.fromJson(data); } else { print( 'Failed to join matchmaking queue. Status code: ${response.statusCode}'); return null; } } Future<MatchDetails> fetchMatchDetails( int matchId, int requesterCharacterId) async { final response = await http.get( Uri.parse( 'http://localhost:5111/api/matchmaking/match/$matchId/details/$requesterCharacterId'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); return MatchDetails.fromJson(data); } else { throw Exception('Failed to load match details'); } }
Lobby页面(按钮所在页面)代码
@override void initState() { super.initState(); _signalRService = SignalRService(onMatchFound: (matchId, player1Id, player2Id) { _navigateToBattleScreen(matchId, player1Id, player2Id); }); } @override void dispose() { _signalRService.closeConnection(); super.dispose(); } void _fetchCharacterDetails() async { try { final fetchedCharacter = await ApiService() .fetchPlayerCharacterDetails(widget.playerCharacterId); setState(() { _latestCharacterDetails = fetchedCharacter; // Update state with the latest character details }); } catch (e) { print("Error fetching character details: $e"); } } void _navigateToBattleScreen(int matchId, int player1Id, int player2Id) { print('Am i trying to navigate?'); int opponentCharacterId = (widget.playerCharacterId == player1Id) ? player2Id : player1Id; Navigator.push( context, MaterialPageRoute( builder: (context) => BattlePvPScreen( playerCharacter: widget.character, matchId: matchId, opponentCharacterId: opponentCharacterId, ))); } ElevatedButton.icon( icon: Icon(Icons.people), label: Text('Battle PvP'), onPressed: () async { final matchmakingResponse = await ApiService() .joinMatchmakingQueue( widget.character.playerCharacterId); if (matchmakingResponse != null && matchmakingResponse.matchId != null) { // Determine the opponent's character ID int opponentCharacterId = matchmakingResponse.player1Id == widget.character.playerCharacterId ? matchmakingResponse.player2Id! : matchmakingResponse.player1Id!; // Navigate to BattlePvPScreen Navigator.push( context, MaterialPageRoute( builder: (context) => BattlePvPScreen( playerCharacter: widget.character, matchId: matchmakingResponse.matchId!, opponentCharacterId: opponentCharacterId, )), ); } else { // Handle waiting or error print("Waiting for an opponent or an error occurred."); } }, )
调试输出
Response Body: {"playerCharacterId":58,"playerId":27,"characterId":1,"health":105,"damage":25,"armour":10,"level":6,"experience":10,"wins":6,"losses":0,"player":null,"character":{"characterId":1,"name":"Mage","damage":20,"health":80,"level":1,"experience":0,"armour":5}} Connection Started Joining queue with playerCharacterId: 58 MatchFound event received Response body1: {"matchId":65,"player1Id":61,"player2Id":58}
内容的提问来源于stack exchange,提问作者littlekirkycode
相关产品推荐
相关产品推荐

