private void onPlayerCreated(object sender, PlayerCreatedEventArgs e) { if (serverId == 0) { // this is not a real player, just the server starting serverId = e.Message.PlayerID; return; } else if (server.Groups.Count == 0) { // this is the first player to connect, therefore it becomes the hosting player hostingPlayerId = e.Message.PlayerID; } else { // add this player to all existing groups of "other players" foreach (int groupId in server.Groups) { server.AddPlayerToGroup(groupId, e.Message.PlayerID, 0); } // send a notification to the NAT traversal server if (natTraversalSession.Enabled) { using (Address playerAddress = server.GetClientAddress(e.Message.PlayerID)) { string playerIpAsString = playerAddress.GetComponentString(Address.KeyHostname); Regex ipAddressRegex = new Regex(@"^(?<1>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Singleline); Match ipAddressMatch = ipAddressRegex.Match(playerIpAsString); if (ipAddressMatch.Success) { IPAddress playerIp = IPAddress.Parse(ipAddressMatch.Groups[1].Value); int playerPort = playerAddress.GetComponentInteger(Address.KeyPort); NatResolver.NotifyPlayerHasJoined(natTraversalSession, playerIp, playerPort); } } } } // link the newly created player's context to the new "other players" group PlayerContext playerAndGroupContext = new PlayerContext(); playerAndGroupContext.AllOtherPlayersGroupId = e.Message.PlayerID; // to be passed to onGroupCreated e.Message.PlayerContext = playerAndGroupContext; // create a new "other players" group for this player server.CreateGroup(new GroupInformation(), SyncFlags.CreateGroup, playerAndGroupContext, null); // add all players except the owner to this "other players" group foreach (int playerId in server.Players) { if (playerId != e.Message.PlayerID && playerId != serverId) { server.AddPlayerToGroup(playerAndGroupContext.AllOtherPlayersGroupId, playerId, 0); } } // notify host if (e.Message.PlayerID != hostingPlayerId) { using (NetworkPacket packet = new NetworkPacket(5)) { packet.Write((byte)ReservedMessageType.PlayerWantsToJoin); packet.Write(e.Message.PlayerID); server.SendTo(hostingPlayerId, packet, 0, SendFlags.Guaranteed | SendFlags.Coalesce | SendFlags.PriorityHigh); } } }
/// <summary>Begin a new game as a host.</summary> /// <param name="mode">Server codec and mixing policy.</param> /// <param name="port">IP port that will listen.</param> public void Start(VoiceServerMode mode, int port) { uncompressJobsPending = new ManualResetEvent(false); try { ApplicationDescription description = new ApplicationDescription(); description.GuidApplication = new Guid("{920BAF09-A06C-47d8-BCE0-21B30D0C3586}"); description.MaxPlayers = 0; // unlimited description.SessionName = "ZunTzu"; description.Flags = Microsoft.DirectX.DirectPlay.SessionFlags.ClientServer | Microsoft.DirectX.DirectPlay.SessionFlags.FastSigned | Microsoft.DirectX.DirectPlay.SessionFlags.NoDpnServer | Microsoft.DirectX.DirectPlay.SessionFlags.NoEnumerations; using (Address address = new Address()) { address.ServiceProvider = Address.ServiceProviderTcpIp; address.AddComponent(Address.KeyPort, port); server.Host(description, address); } if (System.Environment.OSVersion.Version.Major < 6) // not Vista? // launch a voice session { voiceServer = new Microsoft.DirectX.DirectPlay.Voice.Server(server); SessionDescription desc = new SessionDescription(); desc.SessionType = SessionType.Fowarding; // (mode == VoiceServerMode.ForwardingAdpcm || mode == VoiceServerMode.ForwardingGsm ? SessionType.Fowarding : SessionType.Mixing); desc.BufferQuality = BufferQuality.Default; desc.GuidCompressionType = (mode == VoiceServerMode.ForwardingAdpcm || mode == VoiceServerMode.MixingAdpcm ? CompressionGuid.AdPcm : CompressionGuid.Gsm); desc.BufferAggressiveness = BufferAggressiveness.Default; desc.Flags = Microsoft.DirectX.DirectPlay.Voice.SessionFlags.NoHostMigration; //desc.Flags = Microsoft.DirectX.DirectPlay.Voice.SessionFlags.ServerControlTarget; voiceServer.StartSession(desc); } // allow NAT traversal (3 trials) InternetConnectivity connectivity = InternetConnectivity.Unknown; for (int trial = 0; (natTraversalSession == null || !natTraversalSession.Enabled) && trial < 3; ++trial) { natTraversalSession = NatResolver.EnableNatTraversal(port); } string fallbackPublicIpAddress = null; if (natTraversalSession.Enabled) { // notify the parent process via the standard output connectivity = InternetConnectivity.Full; } else { // fallback: discover public IP through HTTP try { // Start a synchronous request. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://www.zuntzu.com/hostfallback.php"); request.UserAgent = "ZunTzu"; request.Timeout = 10000; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; byte[] bytes = System.Text.ASCIIEncoding.ASCII.GetBytes("id=" + natTraversalSession.SessionId.ToString("N")); request.ContentLength = bytes.Length; using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(bytes, 0, bytes.Length); } using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK) { using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream)) { string responseContent = reader.ReadToEnd(); Regex ipAddressRegex = new Regex(@"^(?<1>[012])(?<2>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Singleline); Match ipAddressMatch = ipAddressRegex.Match(responseContent); if (ipAddressMatch.Success) { switch (ipAddressMatch.Groups[1].Value) { case "0": connectivity = InternetConnectivity.Unknown; break; case "1": connectivity = InternetConnectivity.NoEgress; break; case "2": connectivity = InternetConnectivity.NoIngress; break; } fallbackPublicIpAddress = ipAddressMatch.Groups[2].Value; } } } } else { throw new WebException(); } } } catch (Exception) { // fallback: query a public web site to check Internet connectivity try { // Start a synchronous request. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://www.google.com/"); request.Timeout = 10000; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode != HttpStatusCode.OK) { throw new WebException(); } } } catch (Exception) { connectivity = InternetConnectivity.None; } } } // notify the parent process via the standard output switch (connectivity) { case InternetConnectivity.Unknown: case InternetConnectivity.None: Console.Out.WriteLine("Server started {0}/?/?", (int)connectivity); break; case InternetConnectivity.NoEgress: case InternetConnectivity.NoIngress: Console.Out.WriteLine("Server started {0}/{1}/?", (int)connectivity, fallbackPublicIpAddress); break; case InternetConnectivity.Full: Console.Out.WriteLine("Server started {0}/{1}/{2}", (int)connectivity, natTraversalSession.PublicIpAddress, natTraversalSession.PublicPort); break; } } catch (InvalidDeviceAddressException) { // notify the parent process via the standard output Console.Out.WriteLine("Invalid Device Address"); } catch (Exception e) { // notify the parent process via the standard output Console.Out.WriteLine(e.Message); } Console.Out.Flush(); processVideoFrames(); }
/// <summary>Connect to a server.</summary> /// <param name="serverName">IP address or hostname of the server.</param> /// <param name="serverPort">IP port on which the server is listening.</param> /// <remarks>The first client to connect becomes the hosting player.</remarks> public void Connect(string serverName, int serverPort) { Debug.Assert(status == NetworkStatus.Disconnected); serverIsOnSameComputer = (serverName == "localhost"); outboundVideoFrameHistory = new OutboundVideoFrameHistory(); inboundVideoFrameHistories = new Dictionary <int, InboundVideoFrameHistory>(); soundBuffers = new Dictionary <int, Buffer3D>(); client = new Microsoft.DirectX.DirectPlay.Client(InitializeFlags.DisableParameterValidation); client.ConnectComplete += new ConnectCompleteEventHandler(onConnectComplete); client.Receive += new ReceiveEventHandler(onReceive); client.SessionTerminated += new SessionTerminatedEventHandler(onSessionTerminated); status = NetworkStatus.Connecting; // trigger NAT traversal EnabledAddresses enabledAddresses = NatResolver.TestNatTraversal(serverName, serverPort); ApplicationDescription description = new ApplicationDescription(); description.GuidApplication = new Guid("{920BAF09-A06C-47d8-BCE0-21B30D0C3586}"); // try first using the host's public address using (Address hostAddress = (enabledAddresses == null ? new Address(serverName, serverPort) : new Address(enabledAddresses.HostPublicAddress, enabledAddresses.HostPublicPort))) { hostAddress.ServiceProvider = Address.ServiceProviderTcpIp; using (Address device = new Address()) { device.ServiceProvider = Address.ServiceProviderTcpIp; device.AddComponent(Address.KeyTraversalMode, Address.TraversalModeNone); if (enabledAddresses != null) { device.AddComponent(Address.KeyPort, enabledAddresses.ClientPrivatePort); } using (NetworkPacket packet = new NetworkPacket()) { try { client.Connect(description, hostAddress, device, packet, 0); } catch (Exception e) { status = NetworkStatus.Disconnected; ConnectionFailureCause cause = (e is NoConnectionException ? ConnectionFailureCause.NoConnection : (e is NotHostException ? ConnectionFailureCause.NotHost : (e is SessionFullException ? ConnectionFailureCause.SessionFull : ConnectionFailureCause.Other))); // try again using the host's private address if (enabledAddresses != null) { using (Address hostPrivateAddress = new Address(enabledAddresses.HostPrivateAddress, enabledAddresses.HostPrivatePort)) { try { client.Connect(description, hostAddress, device, packet, 0); } catch { NetworkMessage message = new NetworkMessage(0, (byte)ReservedMessageType.ConnectionFailed, new byte[1] { (byte)cause }); lock (networkMessages) { networkMessages.Enqueue(message); } return; } } } else { NetworkMessage message = new NetworkMessage(0, (byte)ReservedMessageType.ConnectionFailed, new byte[1] { (byte)cause }); lock (networkMessages) { networkMessages.Enqueue(message); } return; } } } } } // launch a timer to monitor timeout timeoutTimer = new System.Threading.Timer(onTimeout, client, 4000, 0); }