public async Task Process(SteamHostGameRequest args) { try { steam.PrepareToHostP2PGame(args); } catch (Exception ex) { Trace.TraceError("Error processing steamhostgamerequest: {0}", ex); } }
/// <summary> /// chobby request p2p game to be created /// </summary> public void PrepareToHostP2PGame(SteamHostGameRequest request) { // clear old listeners DisposeExistingProxies(); gameHostUdpPort = SteamP2PPortProxy.GetFreeUdpPort(); // send channel numbers to players foreach (var player in request.Players) { ulong playerSteamID; ulong.TryParse(player.SteamID, out playerSteamID); p2pProxies[playerSteamID] = null; SendSteamMessage(playerSteamID, new SteamP2PRequestPrepareProxy() { Channel = steamChannelCounter++ }); } // wait for response var startWait = DateTime.UtcNow; Task.Factory.StartNew(() => { // wait 30s for all clients to respond while (p2pProxies.Any(x => x.Value == null)) { if (DateTime.UtcNow.Subtract(startWait).TotalSeconds > 30) { Listener.SendCommand(new SteamHostGameFailed() { CausedBySteamID = p2pProxies.Where(x => x.Value == null).Select(x => x.Key).FirstOrDefault().ToString(), Reason = "Client didn't send his confirmation" }); } Task.Delay(100); } // send command to start spring to all clients foreach (var cli in p2pProxies) { var player = request.Players.First(x => x.SteamID == cli.Key.ToString()); // tell clients to connect to server's external port/IP SendSteamMessage(cli.Key, new SteamP2PDirectConnectRequest() { Name = player.Name, Engine = request.Engine, Game = request.Game, Map = request.Map, ScriptPassword = player.ScriptPassword }); } // send command to start spring to self Listener.SendCommand(new SteamHostGameSuccess() { HostPort = gameHostUdpPort }); }); }
/// <summary> /// chobby request p2p game to be created /// </summary> public void PrepareToHostP2PGame(SteamHostGameRequest request) { var socket = new UdpClient(0); udpClient = socket; // send requests to clients to resolve their external IPs clientPorts.Clear(); foreach (var player in request.Players) { ulong playerSteamID; ulong.TryParse(player.SteamID, out playerSteamID); clientPorts[playerSteamID] = null; SendSteamMessage(playerSteamID, new SteamP2PRequestClientPort()); } // resolve my own address var hostResolve = StunUDP(udpClient); if ((hostResolve == null) || (hostResolve.NetType == STUN_NetType.UdpBlocked)) { Listener.SendCommand(new SteamHostGameFailed() { CausedBySteamID = GetSteamID().ToString(), Reason = "Host cannot open UDP port" }); return; } var startWait = DateTime.UtcNow; Task.Factory.StartNew(() => { // wait 30s for all clients to respond while (clientPorts.Any(x => x.Value == null)) { if (DateTime.UtcNow.Subtract(startWait).TotalSeconds > 30) { Listener.SendCommand(new SteamHostGameFailed() { CausedBySteamID = clientPorts.Where(x => x.Value == null).Select(x => x.Key).FirstOrDefault().ToString(), Reason = "Client didn't send his UDP port" }); } Task.Delay(100); } // any client without valid ip/port ? var failedClient = clientPorts.Where(x => (x.Value.IP == null) || (x.Value.Port == 0)).Select(x => x.Key).FirstOrDefault(); if (failedClient != 0) { Listener.SendCommand(new SteamHostGameFailed() { CausedBySteamID = failedClient.ToString(), Reason = "Client could not resolve his NAT/firewall" }); } var hostExtPort = hostResolve.PublicEndPoint.Port; var hostExtIP = hostResolve.PublicEndPoint.Address.ToString(); var hostLocalPort = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port; var buffer = new byte[] { 1, 2, 3, 4, 5, 6 }; foreach (var cli in clientPorts) { var player = request.Players.First(x => x.SteamID == cli.Key.ToString()); // send some packets to each client's external IP/port to punch NAT udpClient.Send(buffer, buffer.Length, cli.Value.IP, cli.Value.Port); udpClient.Send(buffer, buffer.Length, cli.Value.IP, cli.Value.Port); udpClient.Send(buffer, buffer.Length, cli.Value.IP, cli.Value.Port); // tell clients to connect to server's external port/IP SendSteamMessage(cli.Key, new SteamP2PDirectConnectRequest() { HostPort = hostExtPort, HostIP = hostExtIP, Name = player.Name, Engine = request.Engine, Game = request.Game, Map = request.Map, ScriptPassword = player.ScriptPassword }); } udpClient.Close(); // release socket Listener.SendCommand(new SteamHostGameSuccess() { HostPort = hostLocalPort }); }); }