/// <summary> /// Fired when packet received from a client. /// </summary> private void PacketReceived(Client client, PacketBase packet) { try { //heartbeat if (packet is HeartbeatPacket hp) { NetworkPlayer player = NetworkPlayer.FromPacket(hp); if (player != null) { AddOrUpdatePlayer(player, client); } } //command request else if (packet is CommandRequestPacket req) { Log.Write($"Received '{req.CommandType}' request from '{client.RemoteIP}'"); switch (req.CommandType) { //get-players command case CommandType.GetPlayers: Answer_GetPlayers(client, req); break; //quit-game command case CommandType.QuitGame: Answer_QuitGame(client, req); break; //all other commands default: Passthrough_Command(client, req); break; } } //command response else if (packet is CommandResponsePacket resp) { Log.Write($"Received '{resp.CommandType}' ({resp.Code}) response from '{client.RemoteIP}'"); _commandManager.ResponseReceived(resp); } //data else if (packet is DataPacket dp) { Passthrough_Data(client, dp); } } catch (Exception ex) { Log.Write("PacketReceived: Error processing packet"); ErrorHandler.LogError(ex); } }
/// <summary> /// Answers a 'GetPlayers' request, responds with a list of connected players (of same game). /// </summary> private void Answer_GetPlayers(Client sourceClient, CommandRequestPacket packet) { try { //get source player NetworkPlayer player = NetworkPlayer.FromPacket(packet); //get list of connected players (not source player) List <NetworkPlayer> otherPlayers = GetMatchingPlayers(player.GameTitle, player.GameVersion) .Where(p => p.PlayerKey != player.PlayerKey) .ToList(); //message Log.Write($"Sending '{packet.CommandType}' ({otherPlayers.Count} player{(otherPlayers.Count != 1 ? "s" : "")}) response to '{player.IP}'"); //serialize list to bytes PacketBuilder builder = new PacketBuilder(); builder.AddUInt16((ushort)otherPlayers.Count); foreach (NetworkPlayer p in otherPlayers) { builder.AddBytes(p.ToBytes()); } //create packet CommandResponsePacket response = new CommandResponsePacket( gameTitle: player.GameTitle, gameVersion: player.GameVersion, sourceIP: _ip, destinationIP: player.IP, playerName: player.Name, commandType: CommandType.GetPlayers, sequence: packet.Sequence, code: ResultCode.Accept, data: builder.ToBytes()); //send response back to source sourceClient.SendPacket(response); } catch (Exception ex) { Log.Write("Answer_GetPlayers: Error returning player list"); ErrorHandler.LogError(ex); } }
/// <summary> /// Answers a 'QuitGame' request, responds with acceptance. /// </summary> private void Answer_QuitGame(Client sourceClient, CommandRequestPacket packet) { try { //get source player NetworkPlayer player = NetworkPlayer.FromPacket(packet); player = GetPlayerByKey(player.PlayerKey); //set quit flag player.QuitGame = true; //remove expired players RemoveExpiredPlayers(); //remove expired sessions RemoveExpiredSessions(); //message Log.Write($"Sending '{packet.CommandType}' ({ResultCode.Accept}) response to '{player.IP}'"); //create packet CommandResponsePacket response = new CommandResponsePacket( gameTitle: player.GameTitle, gameVersion: player.GameVersion, sourceIP: _ip, destinationIP: player.IP, playerName: player.Name, commandType: CommandType.QuitGame, sequence: packet.Sequence, code: ResultCode.Accept, data: null); //send response back to source sourceClient.SendPacket(response); } catch (Exception ex) { Log.Write("Answer_QuitGame: Unknown error"); ErrorHandler.LogError(ex); } }
/// <summary> /// Forwards a data packet to destination player. No waiting or response. /// </summary> private void Passthrough_Data(Client sourceClient, DataPacket dataPacket) { NetworkPlayer sourcePlayer = null; NetworkPlayer destinationPlayer; bool sessionEnded = false; try { //get source player sourcePlayer = NetworkPlayer.FromPacket(dataPacket); if (sourcePlayer == null) { Log.Write($"Passthrough_Data: Unable to parse packet"); return; } //get destination player destinationPlayer = GetMatchingPlayer(dataPacket.DestinationIP, dataPacket.GameTitle, dataPacket.GameVersion); if (destinationPlayer == null) { Log.Write($"Passthrough_Data: Cannot find destination player at '{dataPacket.DestinationIP}'"); sessionEnded = true; return; } //get session Session session = GetSession(sourcePlayer.PlayerKey, destinationPlayer.PlayerKey); if (session == null) { Log.Write($"Passthrough_Data: Live session does not exist between '{sourcePlayer.IP}' and '{destinationPlayer.IP}'"); sessionEnded = true; return; } //get destination client Client destinationClient = GetClientByPlayer(destinationPlayer); if (destinationClient == null) { Log.Write($"Passthrough_Data: Destination player at {destinationClient.RemoteIP} does not have assigned TCP client"); sessionEnded = true; return; } ////message (keep) //Log.Write($"Forwarding data packet from '{sourcePlayer.IP}' to '{destinationPlayer.IP}'"); //forward request to destination destinationClient.SendPacket(dataPacket); } catch (Exception ex) { Log.Write("Passthrough_Data: Error forwarding data packet"); ErrorHandler.LogError(ex); } finally { try { //send session-ended command? if (sessionEnded) { SendEndSessionCommand(sourceClient, sourcePlayer); } } catch (Exception ex) { Log.Write("Passthrough_Data: Finalizer error"); ErrorHandler.LogError(ex); } } }
/// <summary> /// Forwards a command request packet to destination player, waits for response (or timeout), /// responds to source player with answer. /// </summary> private void Passthrough_Command(Client sourceClient, CommandRequestPacket requestPacket) { CommandResult result = new CommandResult(ResultCode.Unspecified); NetworkPlayer sourcePlayer = null; NetworkPlayer destinationPlayer = null; bool sessionEnded = false; try { //get source player sourcePlayer = NetworkPlayer.FromPacket(requestPacket); if (sourcePlayer == null) { Log.Write($"Passthrough_Command: Unable to parse packet"); result.Code = ResultCode.Error; return; } //get destination player destinationPlayer = GetMatchingPlayer(requestPacket.DestinationIP, requestPacket.GameTitle, requestPacket.GameVersion); if (destinationPlayer == null) { Log.Write($"Passthrough_Command: Cannot find destination player at '{requestPacket.DestinationIP}'"); result.Code = ResultCode.Error; return; } //create session? if (requestPacket.CommandType == CommandType.ConnectToPlayer) { CreateSession(sourcePlayer.PlayerKey, destinationPlayer.PlayerKey); } //get session Session session = GetSession(sourcePlayer.PlayerKey, destinationPlayer.PlayerKey); if ((session == null) && (requestPacket.CommandType != CommandType.ConnectToPlayer)) { Log.Write($"Passthrough_Command: Live session does not exist between '{sourcePlayer.IP}' and '{destinationPlayer.IP}'"); result.Code = ResultCode.Error; sessionEnded = true; return; } //get destination client Client destinationClient = GetClientByPlayer(destinationPlayer); if (destinationClient == null) { Log.Write($"Passthrough_Command: Destination player at {destinationClient.RemoteIP} does not have assigned TCP client"); result.Code = ResultCode.Error; return; } //message Log.Write($"Forwarding '{requestPacket.CommandType}' request from '{sourcePlayer.IP}' to '{destinationPlayer.IP}'"); //record command request being sent _commandManager.RequestSent(requestPacket); //forward request to destination destinationClient.SendPacket(requestPacket); //wait for response or timeout while (true) { //get current status result = _commandManager.GetCommandStatus(requestPacket.Sequence); //have answer or timeout? return! if (result.Code != ResultCode.Unspecified) { return; } //sleep Thread.Sleep(15); } } catch (Exception ex) { Log.Write("Passthrough_Command: Error forwarding request or waiting for response"); ErrorHandler.LogError(ex); } finally { try { //create response packet CommandResponsePacket responsePacket; if ((result.Code.In(ResultCode.Accept, ResultCode.Reject)) && (result.ResponsePacket != null)) { ////create session? //if ((requestPacket.CommandType == CommandType.ConnectToPlayer) && (result.Code == ResultCode.Accept)) // CreateSession(sourcePlayer.UniqueKey, destinationPlayer.UniqueKey); //confirm session? if ((requestPacket.CommandType == CommandType.ConnectToPlayer) && (result.Code == ResultCode.Accept)) { ConfirmSession(sourcePlayer.PlayerKey, destinationPlayer.PlayerKey); } //get original packet responsePacket = result.ResponsePacket; //message Log.Write($"Forwarding '{result.ResponsePacket.CommandType}' ({result.ResponsePacket.Code}) response from '{destinationPlayer.IP}' to '{sourcePlayer.IP}'"); } else { //create new timeout/error packet responsePacket = new CommandResponsePacket( gameTitle: requestPacket.GameTitle, gameVersion: requestPacket.GameVersion, sourceIP: requestPacket.DestinationIP, destinationIP: requestPacket.SourceIP, playerName: "", commandType: requestPacket.CommandType, sequence: requestPacket.Sequence, code: result.Code, data: null); //message Log.Write($"Sending '{responsePacket.CommandType}' ({responsePacket.Code}) response to '{sourceClient.RemoteIP}'"); } //send response to source sourceClient.SendPacket(responsePacket); //send session-ended command? if (sessionEnded) { SendEndSessionCommand(sourceClient, sourcePlayer); } } catch (Exception ex) { Log.Write("Passthrough_Command: Error sending command response to source"); ErrorHandler.LogError(ex); } } }