internal GameDataEventArgs(Player FromPlayer, PeerConnection Connection, byte[] Packet)
		{
			this.fromPlayer = FromPlayer;
			this.connection = Connection;
			this.packet = Packet;
			this.data = new BinaryInput(Packet);
		}
        /// <summary>
        /// Called when a new peer has connected.
        /// </summary>
        /// <param name="Connection">Peer connection</param>
        protected virtual void PeerConnected(PeerConnection Connection)
        {
            PeerConnectedEventHandler h = this.OnPeerConnected;

            if (!(h is null))
            {
                try
                {
                    h(this, Connection);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace.ToString());
                }
            }
        }
        /// <summary>
        /// Connects to a peer in the peer-to-peer network. If the remote end point resides behind the same firewall as the current application,
        /// a direct connection to the local peer is made, for improved performance.
        /// </summary>
        /// <param name="RemoteEndPoint">Remote End-point.</param>
        /// <returns>Peer connection</returns>
        public PeerConnection ConnectToPeer(IPEndPoint RemoteEndPoint)
        {
            if (this.state != PeerToPeerNetworkState.Ready)
            {
                throw new IOException("Peer-to-peer network not ready.");
            }

            TcpClient  Client = new TcpClient(new IPEndPoint(this.localAddress, 0));
            IPEndPoint RemoteEndPoint2;

            try
            {
                if (IPAddress.Equals(RemoteEndPoint.Address, this.externalAddress))
                {
                    this.serviceWANIPConnectionV1.GetSpecificPortMappingEntry(string.Empty, (ushort)RemoteEndPoint.Port, "TCP",
                                                                              out ushort InternalPort, out string InternalClient, out bool Enabled, out string PortMappingDescription,
                                                                              out uint LeaseDuration);

                    RemoteEndPoint2 = new IPEndPoint(IPAddress.Parse(InternalClient), InternalPort);
                    Client.Connect(RemoteEndPoint2);
                }
                else
                {
                    RemoteEndPoint2 = RemoteEndPoint;
                    Client.Connect(RemoteEndPoint);
                }
            }
            catch (Exception)
            {
                Client.Close();
                throw;
            }

            PeerConnection Result = new PeerConnection(Client, this, RemoteEndPoint2);

            Result.StartIdleTimer();

            return(Result);
        }
		/// <summary>
		/// Connects to a peer in the peer-to-peer network. If the remote end point resides behind the same firewall as the current application,
		/// a direct connection to the local peer is made, for improved performance.
		/// </summary>
		/// <param name="RemoteEndPoint">Remote End-point.</param>
		/// <returns>Peer connection</returns>
		public PeerConnection ConnectToPeer(IPEndPoint RemoteEndPoint)
		{
			if (this.state != PeerToPeerNetworkState.Ready)
				throw new IOException("Peer-to-peer network not ready.");

			TcpClient Client = new TcpClient(new IPEndPoint(this.localAddress, 0));
			IPEndPoint RemoteEndPoint2;

			try
			{
				if (IPAddress.Equals(RemoteEndPoint.Address, this.externalAddress))
				{
					ushort InternalPort;
					string InternalClient;
					string PortMappingDescription;
					uint LeaseDuration;
					bool Enabled;

					this.serviceWANIPConnectionV1.GetSpecificPortMappingEntry(string.Empty, (ushort)RemoteEndPoint.Port, "TCP",
						out InternalPort, out InternalClient, out Enabled, out PortMappingDescription, out LeaseDuration);

					RemoteEndPoint2 = new IPEndPoint(IPAddress.Parse(InternalClient), InternalPort);
					Client.Connect(RemoteEndPoint2);
				}
				else
				{
					RemoteEndPoint2 = RemoteEndPoint;
					Client.Connect(RemoteEndPoint);
				}
			}
			catch (Exception)
			{
				Client.Close();
				throw;
			}

			PeerConnection Result = new PeerConnection(Client, this, RemoteEndPoint2);

			Result.StartIdleTimer();

			return Result;
		}
		/// <summary>
		/// Called when a new peer has connected.
		/// </summary>
		/// <param name="Connection">Peer connection</param>
		protected virtual void PeerConnected(PeerConnection Connection)
		{
			PeerConnectedEventHandler h = this.OnPeerConnected;
			if (h != null)
			{
				try
				{
					h(this, Connection);
				}
				catch (Exception ex)
				{
					Debug.WriteLine(ex.Message);
					Debug.WriteLine(ex.StackTrace.ToString());
				}
			}
		}
		private void EndAcceptTcpClient(IAsyncResult ar)
		{
			if (this.tcpListener != null)
			{
				try
				{
					TcpClient Client = this.tcpListener.EndAcceptTcpClient(ar);
					PeerConnection Connection = new PeerConnection(Client, this, (IPEndPoint)Client.Client.RemoteEndPoint);

					this.tcpListener.BeginAcceptTcpClient(this.EndAcceptTcpClient, null);
					this.State = PeerToPeerNetworkState.Ready;

					this.PeerConnected(Connection);

					Connection.Start();
				}
				catch (Exception)
				{
					if (this.state != PeerToPeerNetworkState.Closed)
						this.State = PeerToPeerNetworkState.Error;
				}
			}
		}
		/// <summary>
		/// Is called when game data has been received.
		/// </summary>
		/// <param name="FromPlayer">Data came from this player.</param>
		/// <param name="Connection">Data came over this connection.</param>
		/// <param name="Packet">Data received.</param>
		protected virtual void GameDataReceived(Player FromPlayer, PeerConnection Connection, byte[] Packet)
		{
			GameDataEventHandler h = this.OnGameDataReceived;
			if (h != null)
			{
				try
				{
					h(this, new GameDataEventArgs(FromPlayer, Connection, Packet));
				}
				catch (Exception ex)
				{
					Debug.WriteLine(ex.Message);
					Debug.WriteLine(ex.StackTrace.ToString());
				}
			}
		}
		private void p2pNetwork_OnPeerConnected(PeerToPeerNetwork Listener, PeerConnection Peer)
		{
			IPEndPoint Endpoint = (IPEndPoint)Peer.Tcp.Client.RemoteEndPoint;

#if LineListener
			Console.Out.WriteLine("Receiving connection from " + Endpoint.ToString());
#endif

			lock (this.remotePlayersByEndpoint)
			{
				if (!this.remotePlayerIPs.ContainsKey(Endpoint.Address))
				{
					Peer.Dispose();
					return;
				}
			}

			Peer.OnClosed += new EventHandler(Peer_OnClosed);
			Peer.OnReceived += new BinaryEventHandler(Peer_OnReceived);

			BinaryOutput Output = new BinaryOutput();

			Output.WriteGuid(this.localPlayer.PlayerId);
			Output.WriteString(this.ExternalEndpoint.Address.ToString());
			Output.WriteUInt16((ushort)this.ExternalEndpoint.Port);

			Peer.SendTcp(Output.GetPacket());
		}