private void mqttConnection_OnConnectionError(MqttConnection Sender, Exception Exception)
		{
			this.exception = Exception;
			this.State = MultiPlayerState.Error;
		}
		private void mqttConnection_OnContentReceived(MqttConnection Sender, MqttContent Content)
		{
			BinaryInput Input = Content.DataInput;
			byte Command = Input.ReadByte();

			switch (Command)
			{
				case 0:	// Hello
					string ApplicationName = Input.ReadString();
					if (ApplicationName != this.applicationName)
						break;

					Player Player = this.Deserialize(Input);
					if (Player == null)
						break;

#if LineListener
					Console.Out.WriteLine("Rx: HELLO(" + Player.ToString() + ")");
#endif
					IPEndPoint ExpectedEndpoint = Player.GetExpectedEndpoint(this.p2pNetwork);

					lock (this.remotePlayersByEndpoint)
					{
						this.remotePlayersByEndpoint[ExpectedEndpoint] = Player;
						this.remotePlayerIPs[ExpectedEndpoint.Address] = true;
						this.playersById[Player.PlayerId] = Player;

						this.UpdateRemotePlayersLocked();
					}

					MultiPlayerEnvironmentPlayerInformationEventHandler h = this.OnPlayerAvailable;
					if (h != null)
					{
						try
						{
							h(this, Player);
						}
						catch (Exception ex)
						{
							Debug.WriteLine(ex.Message);
							Debug.WriteLine(ex.StackTrace.ToString());
						}
					}
					break;

				case 1:		// Interconnect
					ApplicationName = Input.ReadString();
					if (ApplicationName != this.applicationName)
						break;

					Player = this.Deserialize(Input);
					if (Player == null)
						break;

#if LineListener
					Console.Out.Write("Rx: INTERCONNECT(" + Player.ToString());
#endif
					int Index = 0;
					int i, c;
					LinkedList<Player> Players = new LinkedList<Networking.Player>();
					bool LocalPlayerIncluded = false;

					Player.Index = Index++;
					Players.AddLast(Player);

					c = (int)Input.ReadUInt();
					for (i = 0; i < c; i++)
					{
						Player = this.Deserialize(Input);
						if (Player == null)
						{
#if LineListener
							Console.Out.Write("," + this.localPlayer.ToString());
#endif
							this.localPlayer.Index = Index++;
							LocalPlayerIncluded = true;
						}
						else
						{
#if LineListener
							Console.Out.Write("," + Player.ToString());
#endif
							Player.Index = Index++;
							Players.AddLast(Player);
						}
					}

#if LineListener
					Console.Out.WriteLine(")");
#endif
					if (!LocalPlayerIncluded)
						break;

					this.mqttConnection.Dispose();
					this.mqttConnection = null;

					lock (this.remotePlayersByEndpoint)
					{
						this.remotePlayersByEndpoint.Clear();
						this.remotePlayerIPs.Clear();
						this.remotePlayersByIndex.Clear();
						this.playersById.Clear();

						this.remotePlayersByIndex[this.localPlayer.Index] = this.localPlayer;
						this.playersById[this.localPlayer.PlayerId] = this.localPlayer;

						foreach (Player Player2 in Players)
						{
							ExpectedEndpoint = Player2.GetExpectedEndpoint(this.p2pNetwork);

							this.remotePlayersByIndex[Player2.Index] = Player2;
							this.remotePlayersByEndpoint[ExpectedEndpoint] = Player2;
							this.remotePlayerIPs[ExpectedEndpoint.Address] = true;
							this.playersById[Player2.PlayerId] = Player2;
						}

						this.UpdateRemotePlayersLocked();
					}

					this.State = MultiPlayerState.ConnectingPlayers;
					this.StartConnecting();
					break;

				case 2:		// Bye
					ApplicationName = Input.ReadString();
					if (ApplicationName != this.applicationName)
						break;

					Guid PlayerId = Input.ReadGuid();
					lock (this.remotePlayersByEndpoint)
					{
						if (!this.playersById.TryGetValue(PlayerId, out Player))
							break;

#if LineListener
						Console.Out.WriteLine("Rx: BYE(" + Player.ToString() + ")");
#endif
						ExpectedEndpoint = Player.GetExpectedEndpoint(this.p2pNetwork);

						this.playersById.Remove(PlayerId);
						this.remotePlayersByEndpoint.Remove(ExpectedEndpoint);
						this.remotePlayersByIndex.Remove(Player.Index);

						IPAddress ExpectedAddress = ExpectedEndpoint.Address;
						bool AddressFound = false;

						foreach (IPEndPoint EP in this.remotePlayersByEndpoint.Keys)
						{
							if (IPAddress.Equals(EP.Address, ExpectedAddress))
							{
								AddressFound = true;
								break;
							}
						}

						if (!AddressFound)
							this.remotePlayerIPs.Remove(ExpectedAddress);

						this.UpdateRemotePlayersLocked();
					}
					break;
			}
		}
		private void mqttConnection_OnStateChanged(MqttConnection Sender, MqttState NewState)
		{
			if (NewState == MqttState.Connected)
			{
				this.mqttConnection.SUBSCRIBE(this.mqttNegotiationTopic);

				BinaryOutput Output = new BinaryOutput();
				Output.WriteByte(0);
				Output.WriteString(this.applicationName);

				this.localPlayer.SetEndpoints(this.p2pNetwork.ExternalEndpoint, this.p2pNetwork.LocalEndpoint);
				this.Serialize(this.localPlayer, Output);

				this.mqttConnection.PUBLISH(this.mqttNegotiationTopic, MqttQualityOfService.AtLeastOne, false, Output);

#if LineListener
				Console.Out.WriteLine("Tx: HELLO(" + this.localPlayer.ToString() + ")");
#endif
			}
		}
		private void P2PNetworkStateChange(object Sender, PeerToPeerNetworkState NewState)
		{
			switch (NewState)
			{
				case PeerToPeerNetworkState.Created:
					this.State = MultiPlayerState.Created;
					break;

				case PeerToPeerNetworkState.SearchingForGateway:
					this.State = MultiPlayerState.SearchingForGateway;
					break;

				case PeerToPeerNetworkState.RegisteringApplicationInGateway:
					this.State = MultiPlayerState.RegisteringApplicationInGateway;
					break;

				case PeerToPeerNetworkState.Ready:
					try
					{
						this.localPlayer.SetEndpoints(this.p2pNetwork.ExternalEndpoint, this.p2pNetwork.LocalEndpoint);

						this.mqttConnection = new MqttConnection(this.mqttServer, this.mqttPort, this.mqttTls, this.mqttUserName, this.mqttPassword);
						this.mqttConnection.OnConnectionError += new MqttExceptionEventHandler(mqttConnection_OnConnectionError);
						this.mqttConnection.OnError += new MqttExceptionEventHandler(mqttConnection_OnError);
						this.mqttConnection.OnStateChanged += new StateChangedEventHandler(mqttConnection_OnStateChanged);
						this.mqttConnection.OnContentReceived += new ContentReceivedEventHandler(mqttConnection_OnContentReceived);

						this.State = MultiPlayerState.FindingPlayers;
					}
					catch (Exception ex)
					{
						this.exception = ex;
						this.State = MultiPlayerState.Error;
					}
					break;

				case PeerToPeerNetworkState.Error:
					this.exception = this.p2pNetwork.Exception;
					this.State = MultiPlayerState.Error;
					break;

				case PeerToPeerNetworkState.Closed:
					this.State = MultiPlayerState.Closed;
					break;
			}
		}
		private void mqttConnection_OnPublished(MqttConnection Sender, ushort PacketIdentifier)
		{
			if (this.mqttConnection != null && PacketIdentifier == this.mqttTerminatedPacketIdentifier)
			{
				this.mqttConnection.Dispose();
				this.mqttConnection = null;
			}
		}
		private void CloseMqtt()
		{
			if (this.mqttConnection != null)
			{
				if (this.mqttConnection.State == MqttState.Connected)
				{
					BinaryOutput Output = new BinaryOutput();
					Output.WriteByte(2);
					Output.WriteString(this.applicationName);
					Output.WriteGuid(this.localPlayer.PlayerId);

					this.mqttTerminatedPacketIdentifier = this.mqttConnection.PUBLISH(this.mqttNegotiationTopic, MqttQualityOfService.AtLeastOne, false, Output);
					this.mqttConnection.OnPublished += new PacketAcknowledgedEventHandler(mqttConnection_OnPublished);

#if LineListener
					Console.Out.WriteLine("Tx: BYE(" + this.localPlayer.ToString() + ")");
#endif
				}
				else
				{
					this.mqttConnection.Dispose();
					this.mqttConnection = null;
				}
			}
		}