mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
Moved Traversal Client code over from old netplay
Moved over gui code for copying host code added gui to netplay diag setup to switch between direct and traversal connection
This commit is contained in:
@ -80,6 +80,8 @@
|
||||
<ClInclude Include="SysConf.h" />
|
||||
<ClInclude Include="Thread.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="TraversalClient.h" />
|
||||
<ClInclude Include="TraversalProto.h" />
|
||||
<ClInclude Include="x64ABI.h" />
|
||||
<ClInclude Include="x64Analyzer.h" />
|
||||
<ClInclude Include="x64Emitter.h" />
|
||||
@ -116,6 +118,7 @@
|
||||
<ClCompile Include="SysConf.cpp" />
|
||||
<ClCompile Include="Thread.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="TraversalClient.cpp" />
|
||||
<ClCompile Include="Version.cpp" />
|
||||
<ClCompile Include="x64ABI.cpp" />
|
||||
<ClCompile Include="x64Analyzer.cpp" />
|
||||
@ -142,4 +145,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -71,6 +71,9 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="GekkoDisassembler.h" />
|
||||
<ClInclude Include="Event.h" />
|
||||
<ClInclude Include="JitRegister.h" />
|
||||
<ClInclude Include="TraversalClient.h" />
|
||||
<ClInclude Include="TraversalProto.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BreakPoints.cpp" />
|
||||
@ -117,8 +120,10 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="XSaveWorkaround.cpp" />
|
||||
<ClCompile Include="GekkoDisassembler.cpp" />
|
||||
<ClCompile Include="JitRegister.cpp" />
|
||||
<ClCompile Include="TraversalClient.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
370
Source/Core/Common/TraversalClient.cpp
Normal file
370
Source/Core/Common/TraversalClient.cpp
Normal file
@ -0,0 +1,370 @@
|
||||
// This file is public domain, in case it's useful to anyone. -comex
|
||||
|
||||
#include "Common/TraversalClient.h"
|
||||
#include "enet/enet.h"
|
||||
#include "Timer.h"
|
||||
|
||||
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!
|
||||
srand(enet_time_get());
|
||||
for (size_t i = 0; i < size; i++)
|
||||
buf[i] = rand() & 0xff;
|
||||
}
|
||||
|
||||
TraversalClient::TraversalClient(ENetHost* netHost, const std::string& server)
|
||||
: m_NetHost(netHost)
|
||||
, m_Server(server)
|
||||
, m_Client(nullptr)
|
||||
, m_FailureReason(0)
|
||||
, m_ConnectRequestId(0)
|
||||
, m_PendingConnect(false)
|
||||
, m_PingTime(0)
|
||||
{
|
||||
netHost->intercept = TraversalClient::InterceptCallback;
|
||||
|
||||
Reset();
|
||||
|
||||
ReconnectToServer();
|
||||
}
|
||||
|
||||
TraversalClient::~TraversalClient()
|
||||
{
|
||||
}
|
||||
|
||||
void TraversalClient::ReconnectToServer()
|
||||
{
|
||||
m_Server = "vps.qoid.us"; // XXX
|
||||
if (enet_address_set_host(&m_ServerAddress, m_Server.c_str()))
|
||||
{
|
||||
OnFailure(BadHost);
|
||||
return;
|
||||
}
|
||||
m_ServerAddress.port = 6262;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void TraversalClient::OnFailure(int reason)
|
||||
{
|
||||
m_State = Failure;
|
||||
m_FailureReason = reason;
|
||||
|
||||
switch (reason)
|
||||
{
|
||||
case TraversalClient::BadHost:
|
||||
{
|
||||
auto server = "dolphin-emu.org";
|
||||
PanicAlertT("Couldn't look up central server %s", server);
|
||||
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;
|
||||
default:
|
||||
PanicAlertT("Unknown error %x", reason);
|
||||
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)
|
||||
{
|
||||
TraversalPacket ping = {0};
|
||||
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();
|
||||
if (traversalClient->TestPacket(host->receivedData, host->receivedDataLength, &host->receivedAddress))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!g_MainNetHost || !g_TraversalClient || server != g_OldServer || port != g_OldPort)
|
||||
{
|
||||
g_OldServer = server;
|
||||
g_OldPort = port;
|
||||
|
||||
ENetAddress addr = { ENET_HOST_ANY, port };
|
||||
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);
|
||||
|
||||
g_TraversalClient.reset(new TraversalClient(g_MainNetHost.get(), server));
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseTraversalClient()
|
||||
{
|
||||
if (!g_TraversalClient)
|
||||
return;
|
||||
|
||||
if (g_OldPort != 0)
|
||||
{
|
||||
// If we were listening at a specific port, kill the
|
||||
// TraversalClient to avoid hanging on to the port.
|
||||
g_TraversalClient.reset();
|
||||
g_MainNetHost.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset any pending connection attempts.
|
||||
g_TraversalClient->Reset();
|
||||
}
|
||||
}
|
88
Source/Core/Common/TraversalClient.h
Normal file
88
Source/Core/Common/TraversalClient.h
Normal file
@ -0,0 +1,88 @@
|
||||
// This file is public domain, in case it's useful to anyone. -comex
|
||||
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/TraversalProto.h"
|
||||
|
||||
#include "enet/enet.h"
|
||||
|
||||
class TraversalClientClient
|
||||
{
|
||||
public:
|
||||
virtual ~TraversalClientClient(){};
|
||||
virtual void OnTraversalStateChanged()=0;
|
||||
virtual void OnConnectReady(ENetAddress addr)=0;
|
||||
virtual void OnConnectFailed(u8 reason)=0;
|
||||
};
|
||||
|
||||
class TraversalClient
|
||||
{
|
||||
public:
|
||||
enum State
|
||||
{
|
||||
Connecting,
|
||||
Connected,
|
||||
Failure
|
||||
};
|
||||
|
||||
enum FailureReason
|
||||
{
|
||||
BadHost = 0x300,
|
||||
VersionTooOld,
|
||||
ServerForgotAboutUs,
|
||||
SocketSendError,
|
||||
ResendTimeout,
|
||||
ConnectFailedError = 0x400,
|
||||
};
|
||||
|
||||
TraversalClient(ENetHost* netHost, const std::string& server);
|
||||
~TraversalClient();
|
||||
void Reset();
|
||||
void ConnectToClient(const std::string& host);
|
||||
void ReconnectToServer();
|
||||
void Update();
|
||||
|
||||
// called from NetHost
|
||||
bool TestPacket(u8* data, size_t size, ENetAddress* from);
|
||||
void HandleResends();
|
||||
|
||||
ENetHost* m_NetHost;
|
||||
TraversalClientClient* m_Client;
|
||||
TraversalHostId m_HostId;
|
||||
State m_State;
|
||||
int m_FailureReason;
|
||||
|
||||
private:
|
||||
struct OutgoingTraversalPacketInfo
|
||||
{
|
||||
TraversalPacket packet;
|
||||
int tries;
|
||||
enet_uint32 sendTime;
|
||||
};
|
||||
|
||||
void HandleServerPacket(TraversalPacket* packet);
|
||||
void ResendPacket(OutgoingTraversalPacketInfo* info);
|
||||
TraversalRequestId SendTraversalPacket(const TraversalPacket& packet);
|
||||
void OnFailure(int reason);
|
||||
void HandlePing();
|
||||
static int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event);
|
||||
|
||||
TraversalRequestId m_ConnectRequestId;
|
||||
bool m_PendingConnect;
|
||||
std::list<OutgoingTraversalPacketInfo> m_OutgoingTraversalPackets;
|
||||
ENetAddress m_ServerAddress;
|
||||
std::string m_Server;
|
||||
enet_uint32 m_PingTime;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<TraversalClient> g_TraversalClient;
|
||||
// the NetHost connected to the TraversalClient.
|
||||
extern std::unique_ptr<ENetHost> g_MainNetHost;
|
||||
|
||||
// Create g_TraversalClient and g_MainNetHost if necessary.
|
||||
bool EnsureTraversalClient(const std::string& server, u16 port);
|
||||
void ReleaseTraversalClient();
|
96
Source/Core/Common/TraversalProto.h
Normal file
96
Source/Core/Common/TraversalProto.h
Normal file
@ -0,0 +1,96 @@
|
||||
// This file is public domain, in case it's useful to anyone. -comex
|
||||
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
|
||||
typedef std::array<char, 8> TraversalHostId;
|
||||
typedef u64 TraversalRequestId;
|
||||
|
||||
enum TraversalPacketType
|
||||
{
|
||||
// [*->*]
|
||||
TraversalPacketAck = 0,
|
||||
// [c->s]
|
||||
TraversalPacketPing = 1,
|
||||
// [c->s]
|
||||
TraversalPacketHelloFromClient = 2,
|
||||
// [s->c]
|
||||
TraversalPacketHelloFromServer = 3,
|
||||
// [c->s] When connecting, first the client asks the central server...
|
||||
TraversalPacketConnectPlease = 4,
|
||||
// [s->c] ...who asks the game host to send a UDP packet to the
|
||||
// client... (an ack implies success)
|
||||
TraversalPacketPleaseSendPacket = 5,
|
||||
// [s->c] ...which the central server relays back to the client.
|
||||
TraversalPacketConnectReady = 6,
|
||||
// [s->c] Alternately, the server might not have heard of this host.
|
||||
TraversalPacketConnectFailed = 7
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TraversalProtoVersion = 0
|
||||
};
|
||||
|
||||
enum TraversalConnectFailedReason
|
||||
{
|
||||
TraversalConnectFailedClientDidntRespond = 0,
|
||||
TraversalConnectFailedClientFailure,
|
||||
TraversalConnectFailedNoSuchClient
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct TraversalInetAddress
|
||||
{
|
||||
u8 isIPV6;
|
||||
u32 address[4];
|
||||
u16 port;
|
||||
};
|
||||
struct TraversalPacket
|
||||
{
|
||||
u8 type;
|
||||
TraversalRequestId requestId;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 ok;
|
||||
} ack;
|
||||
struct
|
||||
{
|
||||
TraversalHostId hostId;
|
||||
} ping;
|
||||
struct
|
||||
{
|
||||
u8 protoVersion;
|
||||
} helloFromClient;
|
||||
struct
|
||||
{
|
||||
u8 ok;
|
||||
TraversalHostId yourHostId;
|
||||
TraversalInetAddress yourAddress; // currently unused
|
||||
} helloFromServer;
|
||||
struct
|
||||
{
|
||||
TraversalHostId hostId;
|
||||
} connectPlease;
|
||||
struct
|
||||
{
|
||||
TraversalInetAddress address;
|
||||
} pleaseSendPacket;
|
||||
struct
|
||||
{
|
||||
TraversalRequestId requestId;
|
||||
TraversalInetAddress address;
|
||||
} connectReady;
|
||||
struct
|
||||
{
|
||||
TraversalRequestId requestId;
|
||||
u8 reason;
|
||||
} connectFailed;
|
||||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
Reference in New Issue
Block a user