| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | // This file is public domain, in case it's useful to anyone. -comex
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-16 22:53:50 -08:00
										 |  |  | #include "Common/Timer.h"
 | 
					
						
							| 
									
										
										
										
											2015-02-02 02:08:58 -08:00
										 |  |  | #include "Common/TraversalClient.h"
 | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void GetRandomishBytes(u8* buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	// We don't need high quality random numbers (which might not be available),
 | 
					
						
							|  |  |  | 	// just non-repeating numbers!
 | 
					
						
							| 
									
										
										
										
											2015-03-10 01:47:29 +01:00
										 |  |  | 	static std::mt19937 prng(enet_time_get()); | 
					
						
							|  |  |  | 	static std::uniform_int_distribution<unsigned int> u8_distribution(0, 255); | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 	for (size_t i = 0; i < size; i++) | 
					
						
							| 
									
										
										
										
											2015-03-10 01:47:29 +01:00
										 |  |  | 		buf[i] = u8_distribution(prng); | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | TraversalClient::TraversalClient(ENetHost* netHost, const std::string& server, const u16 port) | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 	: m_NetHost(netHost) | 
					
						
							|  |  |  | 	, m_Client(nullptr) | 
					
						
							|  |  |  | 	, m_FailureReason(0) | 
					
						
							|  |  |  | 	, m_ConnectRequestId(0) | 
					
						
							|  |  |  | 	, m_PendingConnect(false) | 
					
						
							| 
									
										
										
										
											2015-02-02 02:08:58 -08:00
										 |  |  | 	, m_Server(server) | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | 	, m_port(port) | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 	, m_PingTime(0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	netHost->intercept = TraversalClient::InterceptCallback; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Reset(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ReconnectToServer(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TraversalClient::~TraversalClient() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::ReconnectToServer() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (enet_address_set_host(&m_ServerAddress, m_Server.c_str())) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		OnFailure(BadHost); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | 	m_ServerAddress.port = m_port; | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	m_State = Connecting; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	TraversalPacket hello = {}; | 
					
						
							|  |  |  | 	hello.type = TraversalPacketHelloFromClient; | 
					
						
							|  |  |  | 	hello.helloFromClient.protoVersion = TraversalProtoVersion; | 
					
						
							|  |  |  | 	SendTraversalPacket(hello); | 
					
						
							|  |  |  | 	if (m_Client) | 
					
						
							|  |  |  | 		m_Client->OnTraversalStateChanged(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ENetAddress MakeENetAddress(TraversalInetAddress* address) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ENetAddress eaddr; | 
					
						
							|  |  |  | 	if (address->isIPV6) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		eaddr.port = 0; // no support yet :(
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		eaddr.host = address->address[0]; | 
					
						
							|  |  |  | 		eaddr.port = ntohs(address->port); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return eaddr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::ConnectToClient(const std::string& host) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (host.size() > sizeof(TraversalHostId)) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		PanicAlert("host too long"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	TraversalPacket packet = {}; | 
					
						
							|  |  |  | 	packet.type = TraversalPacketConnectPlease; | 
					
						
							|  |  |  | 	memcpy(packet.connectPlease.hostId.data(), host.c_str(), host.size()); | 
					
						
							|  |  |  | 	m_ConnectRequestId = SendTraversalPacket(packet); | 
					
						
							|  |  |  | 	m_PendingConnect = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool TraversalClient::TestPacket(u8* data, size_t size, ENetAddress* from) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (from->host == m_ServerAddress.host && | 
					
						
							|  |  |  | 	    from->port == m_ServerAddress.port) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if (size < sizeof(TraversalPacket)) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			ERROR_LOG(NETPLAY, "Received too-short traversal packet."); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			HandleServerPacket((TraversalPacket*) data); | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //--Temporary until more of the old netplay branch is moved over
 | 
					
						
							|  |  |  | void TraversalClient::Update() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ENetEvent netEvent; | 
					
						
							|  |  |  | 	if (enet_host_service(m_NetHost, &netEvent, 4) > 0) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		switch (netEvent.type) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 		case ENET_EVENT_TYPE_RECEIVE: | 
					
						
							|  |  |  | 			TestPacket(netEvent.packet->data, netEvent.packet->dataLength, &netEvent.peer->address); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			enet_packet_destroy(netEvent.packet); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2015-02-02 02:08:58 -08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	HandleResends(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::HandleServerPacket(TraversalPacket* packet) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 ok = 1; | 
					
						
							|  |  |  | 	switch (packet->type) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case TraversalPacketAck: | 
					
						
							|  |  |  | 		if (!packet->ack.ok) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			OnFailure(ServerForgotAboutUs); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for (auto it = m_OutgoingTraversalPackets.begin(); it != m_OutgoingTraversalPackets.end(); ++it) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if (it->packet.requestId == packet->requestId) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				m_OutgoingTraversalPackets.erase(it); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TraversalPacketHelloFromServer: | 
					
						
							|  |  |  | 		if (m_State != Connecting) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if (!packet->helloFromServer.ok) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			OnFailure(VersionTooOld); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		m_HostId = packet->helloFromServer.yourHostId; | 
					
						
							|  |  |  | 		m_State = Connected; | 
					
						
							|  |  |  | 		if (m_Client) | 
					
						
							|  |  |  | 			m_Client->OnTraversalStateChanged(); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TraversalPacketPleaseSendPacket: | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 		// security is overrated.
 | 
					
						
							|  |  |  | 		ENetAddress addr = MakeENetAddress(&packet->pleaseSendPacket.address); | 
					
						
							|  |  |  | 		if (addr.port != 0) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			char message[] = "Hello from Dolphin Netplay..."; | 
					
						
							|  |  |  | 			ENetBuffer buf; | 
					
						
							|  |  |  | 			buf.data = message; | 
					
						
							|  |  |  | 			buf.dataLength = sizeof(message) - 1; | 
					
						
							|  |  |  | 			enet_socket_send(m_NetHost->socket, &addr, &buf, 1); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			// invalid IPV6
 | 
					
						
							|  |  |  | 			ok = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case TraversalPacketConnectReady: | 
					
						
							|  |  |  | 	case TraversalPacketConnectFailed: | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 		if (!m_PendingConnect || packet->connectReady.requestId != m_ConnectRequestId) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		m_PendingConnect = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!m_Client) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (packet->type == TraversalPacketConnectReady) | 
					
						
							|  |  |  | 			m_Client->OnConnectReady(MakeENetAddress(&packet->connectReady.address)); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			m_Client->OnConnectFailed(packet->connectFailed.reason); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		WARN_LOG(NETPLAY, "Received unknown packet with type %d", packet->type); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (packet->type != TraversalPacketAck) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		TraversalPacket ack = {}; | 
					
						
							|  |  |  | 		ack.type = TraversalPacketAck; | 
					
						
							|  |  |  | 		ack.requestId = packet->requestId; | 
					
						
							|  |  |  | 		ack.ack.ok = ok; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ENetBuffer buf; | 
					
						
							|  |  |  | 		buf.data = &ack; | 
					
						
							|  |  |  | 		buf.dataLength = sizeof(ack); | 
					
						
							|  |  |  | 		if (enet_socket_send(m_NetHost->socket, &m_ServerAddress, &buf, 1) == -1) | 
					
						
							|  |  |  | 			OnFailure(SocketSendError); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-16 22:53:50 -08:00
										 |  |  | void TraversalClient::OnFailure(FailureReason reason) | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	m_State = Failure; | 
					
						
							|  |  |  | 	m_FailureReason = reason; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (reason) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case TraversalClient::BadHost: | 
					
						
							| 
									
										
										
										
											2015-02-16 22:53:50 -08:00
										 |  |  | 		PanicAlertT("Couldn't look up central server %s", m_Server.c_str()); | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case TraversalClient::VersionTooOld: | 
					
						
							|  |  |  | 		PanicAlertT("Dolphin too old for traversal server"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TraversalClient::ServerForgotAboutUs: | 
					
						
							|  |  |  | 		PanicAlertT("Disconnected from traversal server"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TraversalClient::SocketSendError: | 
					
						
							|  |  |  | 		PanicAlertT("Socket error sending to traversal server"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TraversalClient::ResendTimeout: | 
					
						
							|  |  |  | 		PanicAlertT("Timeout connecting to traversal server"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (m_Client) | 
					
						
							|  |  |  | 		m_Client->OnTraversalStateChanged(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::ResendPacket(OutgoingTraversalPacketInfo* info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	info->sendTime = enet_time_get(); | 
					
						
							|  |  |  | 	info->tries++; | 
					
						
							|  |  |  | 	ENetBuffer buf; | 
					
						
							|  |  |  | 	buf.data = &info->packet; | 
					
						
							|  |  |  | 	buf.dataLength = sizeof(info->packet); | 
					
						
							|  |  |  | 	if (enet_socket_send(m_NetHost->socket, &m_ServerAddress, &buf, 1) == -1) | 
					
						
							|  |  |  | 		OnFailure(SocketSendError); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::HandleResends() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	enet_uint32 now = enet_time_get(); | 
					
						
							|  |  |  | 	for (auto& tpi : m_OutgoingTraversalPackets) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if (now - tpi.sendTime >= (u32) (300 * tpi.tries)) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if (tpi.tries >= 5) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				OnFailure(ResendTimeout); | 
					
						
							|  |  |  | 				m_OutgoingTraversalPackets.clear(); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				ResendPacket(&tpi); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	HandlePing(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::HandlePing() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	enet_uint32 now = enet_time_get(); | 
					
						
							|  |  |  | 	if (m_State == Connected && now - m_PingTime >= 500) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-02-16 22:53:50 -08:00
										 |  |  | 		TraversalPacket ping = {}; | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 		ping.type = TraversalPacketPing; | 
					
						
							|  |  |  | 		ping.ping.hostId = m_HostId; | 
					
						
							|  |  |  | 		SendTraversalPacket(ping); | 
					
						
							|  |  |  | 		m_PingTime = now; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TraversalRequestId TraversalClient::SendTraversalPacket(const TraversalPacket& packet) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	OutgoingTraversalPacketInfo info; | 
					
						
							|  |  |  | 	info.packet = packet; | 
					
						
							|  |  |  | 	GetRandomishBytes((u8*) &info.packet.requestId, sizeof(info.packet.requestId)); | 
					
						
							|  |  |  | 	info.tries = 0; | 
					
						
							|  |  |  | 	m_OutgoingTraversalPackets.push_back(info); | 
					
						
							|  |  |  | 	ResendPacket(&m_OutgoingTraversalPackets.back()); | 
					
						
							|  |  |  | 	return info.packet.requestId; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TraversalClient::Reset() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_PendingConnect = false; | 
					
						
							|  |  |  | 	m_Client = nullptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ENET_CALLBACK TraversalClient::InterceptCallback(ENetHost* host, ENetEvent* event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	auto traversalClient = g_TraversalClient.get(); | 
					
						
							| 
									
										
										
										
											2015-03-14 15:19:18 +01:00
										 |  |  | 	if (traversalClient->TestPacket(host->receivedData, host->receivedDataLength, &host->receivedAddress) | 
					
						
							|  |  |  | 			|| (host->receivedDataLength == 1 && host->receivedData[0] == 0)) | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		event->type = (ENetEventType)42; | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::unique_ptr<TraversalClient> g_TraversalClient; | 
					
						
							|  |  |  | std::unique_ptr<ENetHost> g_MainNetHost; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The settings at the previous TraversalClient reset - notably, we
 | 
					
						
							|  |  |  | // need to know not just what port it's on, but whether it was
 | 
					
						
							|  |  |  | // explicitly requested.
 | 
					
						
							|  |  |  | static std::string g_OldServer; | 
					
						
							|  |  |  | static u16 g_OldPort; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool EnsureTraversalClient(const std::string& server, u16 port) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 	if (!g_MainNetHost || !g_TraversalClient || server != g_OldServer || port != g_OldPort) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		g_OldServer = server; | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | 		g_OldPort = port ; | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | 		ENetAddress addr = { ENET_HOST_ANY, 0 }; | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 		ENetHost* host = enet_host_create( | 
					
						
							|  |  |  | 			&addr, // address
 | 
					
						
							|  |  |  | 			50, // peerCount
 | 
					
						
							|  |  |  | 			1, // channelLimit
 | 
					
						
							|  |  |  | 			0, // incomingBandwidth
 | 
					
						
							|  |  |  | 			0); // outgoingBandwidth
 | 
					
						
							|  |  |  | 		if (!host) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			g_MainNetHost.reset(); | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		g_MainNetHost.reset(host); | 
					
						
							| 
									
										
										
										
											2015-02-14 19:51:08 -08:00
										 |  |  | 		g_TraversalClient.reset(new TraversalClient(g_MainNetHost.get(), server, port)); | 
					
						
							| 
									
										
										
										
											2015-02-02 01:56:53 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ReleaseTraversalClient() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!g_TraversalClient) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 22:16:34 -08:00
										 |  |  | 	g_TraversalClient.release(); | 
					
						
							|  |  |  | 	g_MainNetHost.release(); | 
					
						
							| 
									
										
										
										
											2015-03-01 16:17:32 +01:00
										 |  |  | } |