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:
Ziek 2015-02-02 01:56:53 -08:00
parent 461a54338b
commit 4cdc307b87
12 changed files with 1240 additions and 155 deletions

View File

@ -80,6 +80,8 @@
<ClInclude Include="SysConf.h" /> <ClInclude Include="SysConf.h" />
<ClInclude Include="Thread.h" /> <ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" /> <ClInclude Include="Timer.h" />
<ClInclude Include="TraversalClient.h" />
<ClInclude Include="TraversalProto.h" />
<ClInclude Include="x64ABI.h" /> <ClInclude Include="x64ABI.h" />
<ClInclude Include="x64Analyzer.h" /> <ClInclude Include="x64Analyzer.h" />
<ClInclude Include="x64Emitter.h" /> <ClInclude Include="x64Emitter.h" />
@ -116,6 +118,7 @@
<ClCompile Include="SysConf.cpp" /> <ClCompile Include="SysConf.cpp" />
<ClCompile Include="Thread.cpp" /> <ClCompile Include="Thread.cpp" />
<ClCompile Include="Timer.cpp" /> <ClCompile Include="Timer.cpp" />
<ClCompile Include="TraversalClient.cpp" />
<ClCompile Include="Version.cpp" /> <ClCompile Include="Version.cpp" />
<ClCompile Include="x64ABI.cpp" /> <ClCompile Include="x64ABI.cpp" />
<ClCompile Include="x64Analyzer.cpp" /> <ClCompile Include="x64Analyzer.cpp" />

View File

@ -71,6 +71,9 @@
</ClInclude> </ClInclude>
<ClInclude Include="GekkoDisassembler.h" /> <ClInclude Include="GekkoDisassembler.h" />
<ClInclude Include="Event.h" /> <ClInclude Include="Event.h" />
<ClInclude Include="JitRegister.h" />
<ClInclude Include="TraversalClient.h" />
<ClInclude Include="TraversalProto.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="BreakPoints.cpp" /> <ClCompile Include="BreakPoints.cpp" />
@ -117,6 +120,8 @@
</ClCompile> </ClCompile>
<ClCompile Include="XSaveWorkaround.cpp" /> <ClCompile Include="XSaveWorkaround.cpp" />
<ClCompile Include="GekkoDisassembler.cpp" /> <ClCompile Include="GekkoDisassembler.cpp" />
<ClCompile Include="JitRegister.cpp" />
<ClCompile Include="TraversalClient.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="CMakeLists.txt" /> <Text Include="CMakeLists.txt" />

View 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();
}
}

View 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();

View 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)

View File

@ -38,10 +38,25 @@ NetPlayClient::~NetPlayClient()
Disconnect(); Disconnect();
} }
if (g_MainNetHost.get() == m_client)
{
g_MainNetHost.release();
}
if (m_client)
{
enet_host_destroy(m_client);
m_client = nullptr;
}
if (m_traversal_client)
{
ReleaseTraversalClient();
}
} }
// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name, bool traversal)
: m_dialog(dialog) : m_dialog(dialog)
, m_client(nullptr) , m_client(nullptr)
, m_server(nullptr) , m_server(nullptr)
@ -53,6 +68,8 @@ NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlay
, m_is_recording(false) , m_is_recording(false)
, m_pid(0) , m_pid(0)
, m_connecting(false) , m_connecting(false)
, m_traversal_client(nullptr)
, m_state(Failure)
{ {
m_target_buffer_size = 20; m_target_buffer_size = 20;
ClearBuffers(); ClearBuffers();
@ -61,35 +78,81 @@ NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlay
m_player_name = name; m_player_name = name;
//Direct Connection if (!traversal)
m_client = enet_host_create(nullptr, 1, 3, 0, 0);
if (m_client == nullptr)
{ {
PanicAlertT("Couldn't Create Client"); //Direct Connection
} m_client = enet_host_create(nullptr, 1, 3, 0, 0);
ENetAddress addr; if (m_client == nullptr)
enet_address_set_host(&addr, address.c_str()); {
addr.port = port; PanicAlertT("Couldn't Create Client");
}
m_server = enet_host_connect(m_client, &addr, 3, 0); ENetAddress addr;
enet_address_set_host(&addr, address.c_str());
addr.port = port;
if (m_server == nullptr) m_server = enet_host_connect(m_client, &addr, 3, 0);
{
PanicAlertT("Couldn't create peer."); if (m_server == nullptr)
} {
PanicAlertT("Couldn't create peer.");
}
ENetEvent netEvent;
int net = enet_host_service(m_client, &netEvent, 5000);
if (net > 0 && netEvent.type == ENET_EVENT_TYPE_CONNECT)
{
if (Connect())
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
}
else
{
PanicAlertT("Failed to Connect!");
}
ENetEvent netEvent;
int net = enet_host_service(m_client, &netEvent, 5000);
if (net > 0 && netEvent.type == ENET_EVENT_TYPE_CONNECT)
{
if (Connect())
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
} }
else else
{ {
PanicAlertT("Failed to Connect!"); //Traversal Server
if (!EnsureTraversalClient("dolphin-emu.org", 0))
return;
m_client = g_MainNetHost.get();
m_traversal_client = g_TraversalClient.get();
// If we were disconnected in the background, reconnect.
if (m_traversal_client->m_State == TraversalClient::Failure)
m_traversal_client->ReconnectToServer();
m_traversal_client->m_Client = this;
m_host_spec = address;
m_state = WaitingForTraversalClientConnection;
OnTraversalStateChanged();
m_connecting = true;
while (m_connecting)
{
ENetEvent netEvent;
if (m_traversal_client)
m_traversal_client->HandleResends();
while (enet_host_service(m_client, &netEvent, 4) > 0)
{
sf::Packet rpac;
switch (netEvent.type)
{
case ENET_EVENT_TYPE_CONNECT:
m_server = netEvent.peer;
if (Connect())
{
m_state = Connected;
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
}
return;
}
}
}
PanicAlertT("Failed To Connect!");
} }
} }
@ -380,6 +443,7 @@ void NetPlayClient::Send(sf::Packet& packet)
void NetPlayClient::Disconnect() void NetPlayClient::Disconnect()
{ {
ENetEvent netEvent; ENetEvent netEvent;
m_state = Failure;
enet_peer_disconnect(m_server, 0); enet_peer_disconnect(m_server, 0);
while (enet_host_service(m_client, &netEvent, 3000) > 0) while (enet_host_service(m_client, &netEvent, 3000) > 0)
{ {
@ -407,6 +471,8 @@ void NetPlayClient::ThreadFunc()
int net; int net;
{ {
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
if (m_traversal_client)
m_traversal_client->HandleResends();
net = enet_host_service(m_client, &netEvent, 4); net = enet_host_service(m_client, &netEvent, 4);
} }
if (net > 0) if (net > 0)
@ -630,6 +696,55 @@ void NetPlayClient::ClearBuffers()
} }
} }
// called from ---NETPLAY--- thread
void NetPlayClient::OnTraversalStateChanged()
{
if (m_state == WaitingForTraversalClientConnection &&
m_traversal_client->m_State == TraversalClient::Connected)
{
m_state = WaitingForTraversalClientConnectReady;
m_traversal_client->ConnectToClient(m_host_spec);
}
else if (m_state != Failure &&
m_traversal_client->m_State == TraversalClient::Failure)
{
Disconnect();
}
}
// called from ---NETPLAY--- thread
void NetPlayClient::OnConnectReady(ENetAddress addr)
{
if (m_state == WaitingForTraversalClientConnectReady)
{
m_state = Connecting;
enet_host_connect(m_client, &addr, 0, 0);
}
}
// called from ---NETPLAY--- thread
void NetPlayClient::OnConnectFailed(u8 reason)
{
m_connecting = false;
m_state = Failure;
int swtch = TraversalClient::ConnectFailedError + reason;
switch (swtch)
{
case TraversalClient::ConnectFailedError + TraversalConnectFailedClientDidntRespond:
PanicAlertT("Traversal server timed out connecting to the host");
break;
case TraversalClient::ConnectFailedError + TraversalConnectFailedClientFailure:
PanicAlertT("Server rejected traversal attempt");
break;
case TraversalClient::ConnectFailedError + TraversalConnectFailedNoSuchClient:
PanicAlertT("Invalid host");
break;
default:
PanicAlertT("Unknown error %x", swtch);
break;
}
}
// called from ---CPU--- thread // called from ---CPU--- thread
bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status) bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status)
{ {

View File

@ -7,12 +7,11 @@
#include <map> #include <map>
#include <queue> #include <queue>
#include <sstream> #include <sstream>
#include "enet/enet.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FifoQueue.h" #include "Common/FifoQueue.h"
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Common/Timer.h" #include "Common/Timer.h"
#include "Common/TraversalClient.h"
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
#include "InputCommon/GCPadStatus.h" #include "InputCommon/GCPadStatus.h"
#include <SFML/Network/Packet.hpp> #include <SFML/Network/Packet.hpp>
@ -43,12 +42,12 @@ public:
u32 ping; u32 ping;
}; };
class NetPlayClient class NetPlayClient : public TraversalClientClient
{ {
public: public:
void ThreadFunc(); void ThreadFunc();
NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name); NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name, bool traversal);
~NetPlayClient(); ~NetPlayClient();
void GetPlayerList(std::string& list, std::vector<int>& pid_list); void GetPlayerList(std::string& list, std::vector<int>& pid_list);
@ -66,11 +65,25 @@ public:
bool WiimoteUpdate(int _number, u8* data, const u8 size); bool WiimoteUpdate(int _number, u8* data, const u8 size);
bool GetNetPads(const u8 pad_nb, GCPadStatus* pad_status); bool GetNetPads(const u8 pad_nb, GCPadStatus* pad_status);
void OnTraversalStateChanged() override;
void OnConnectReady(ENetAddress addr) override;
void OnConnectFailed(u8 reason) override;
u8 LocalPadToInGamePad(u8 localPad); u8 LocalPadToInGamePad(u8 localPad);
u8 InGamePadToLocalPad(u8 localPad); u8 InGamePadToLocalPad(u8 localPad);
u8 LocalWiimoteToInGameWiimote(u8 local_pad); u8 LocalWiimoteToInGameWiimote(u8 local_pad);
enum State
{
WaitingForTraversalClientConnection,
WaitingForTraversalClientConnectReady,
Connecting,
WaitingForHelloResponse,
Connected,
Failure
} m_state;
protected: protected:
void ClearBuffers(); void ClearBuffers();
@ -121,6 +134,7 @@ private:
std::string m_host_spec; std::string m_host_spec;
std::string m_player_name; std::string m_player_name;
bool m_connecting; bool m_connecting;
TraversalClient* m_traversal_client;
}; };
void NetPlay_Enable(NetPlayClient* const np); void NetPlay_Enable(NetPlayClient* const np);

View File

@ -34,32 +34,31 @@ typedef std::vector<u8> NetWiimote;
static const int NETPLAY_INITIAL_GCTIME = 1272737767; static const int NETPLAY_INITIAL_GCTIME = 1272737767;
// messages // messages
enum enum
{ {
NP_MSG_PLAYER_JOIN = 0x10, NP_MSG_PLAYER_JOIN = 0x10,
NP_MSG_PLAYER_LEAVE = 0x11, NP_MSG_PLAYER_LEAVE = 0x11,
NP_MSG_CHAT_MESSAGE = 0x30, NP_MSG_CHAT_MESSAGE = 0x30,
NP_MSG_PAD_DATA = 0x60, NP_MSG_PAD_DATA = 0x60,
NP_MSG_PAD_MAPPING = 0x61, NP_MSG_PAD_MAPPING = 0x61,
NP_MSG_PAD_BUFFER = 0x62, NP_MSG_PAD_BUFFER = 0x62,
NP_MSG_WIIMOTE_DATA = 0x70, NP_MSG_WIIMOTE_DATA = 0x70,
NP_MSG_WIIMOTE_MAPPING = 0x71, NP_MSG_WIIMOTE_MAPPING = 0x71,
NP_MSG_START_GAME = 0xA0, NP_MSG_START_GAME = 0xA0,
NP_MSG_CHANGE_GAME = 0xA1, NP_MSG_CHANGE_GAME = 0xA1,
NP_MSG_STOP_GAME = 0xA2, NP_MSG_STOP_GAME = 0xA2,
NP_MSG_DISABLE_GAME = 0xA3, NP_MSG_DISABLE_GAME = 0xA3,
NP_MSG_READY = 0xD0, NP_MSG_READY = 0xD0,
NP_MSG_NOT_READY = 0xD1, NP_MSG_NOT_READY = 0xD1,
NP_MSG_PING = 0xE0, NP_MSG_PING = 0xE0,
NP_MSG_PONG = 0xE1, NP_MSG_PONG = 0xE1,
NP_MSG_PLAYER_PING_DATA = 0xE2, NP_MSG_PLAYER_PING_DATA = 0xE2,
}; };
@ -70,8 +69,8 @@ typedef u32 FrameNum;
enum enum
{ {
CON_ERR_SERVER_FULL = 1, CON_ERR_SERVER_FULL = 1,
CON_ERR_GAME_RUNNING = 2, CON_ERR_GAME_RUNNING = 2,
CON_ERR_VERSION_MISMATCH = 3 CON_ERR_VERSION_MISMATCH = 3
}; };

View File

@ -19,6 +19,17 @@ NetPlayServer::~NetPlayServer()
m_do_loop = false; m_do_loop = false;
m_thread.join(); m_thread.join();
enet_host_destroy(m_server); enet_host_destroy(m_server);
if (g_MainNetHost.get() == m_server)
{
g_MainNetHost.release();
}
if (m_traversal_client)
{
g_TraversalClient->m_Client = nullptr;
ReleaseTraversalClient();
}
} }
#ifdef USE_UPNP #ifdef USE_UPNP
@ -30,7 +41,7 @@ NetPlayServer::~NetPlayServer()
} }
// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayServer::NetPlayServer(const u16 port) NetPlayServer::NetPlayServer(const u16 port, bool traversal)
: is_connected(false) : is_connected(false)
, m_is_running(false) , m_is_running(false)
, m_do_loop(false) , m_do_loop(false)
@ -40,6 +51,8 @@ NetPlayServer::NetPlayServer(const u16 port)
, m_target_buffer_size(0) , m_target_buffer_size(0)
, m_selected_game("") , m_selected_game("")
, m_server(nullptr) , m_server(nullptr)
, m_traversal_client(nullptr)
, m_dialog(nullptr)
{ {
//--use server time //--use server time
if (enet_initialize() != 0) if (enet_initialize() != 0)
@ -49,11 +62,26 @@ NetPlayServer::NetPlayServer(const u16 port)
memset(m_pad_map, -1, sizeof(m_pad_map)); memset(m_pad_map, -1, sizeof(m_pad_map));
memset(m_wiimote_map, -1, sizeof(m_wiimote_map)); memset(m_wiimote_map, -1, sizeof(m_wiimote_map));
if (traversal)
{
if (!EnsureTraversalClient("dolphin-emu.org", 0))
return;
ENetAddress serverAddr; g_TraversalClient->m_Client = this;
serverAddr.host = ENET_HOST_ANY; m_traversal_client = g_TraversalClient.get();
serverAddr.port = port;
m_server = enet_host_create(&serverAddr, 10, 3, 0, 0); m_server = g_MainNetHost.get();
if (g_TraversalClient->m_State == TraversalClient::Failure)
g_TraversalClient->ReconnectToServer();
}
else
{
ENetAddress serverAddr;
serverAddr.host = ENET_HOST_ANY;
serverAddr.port = port;
m_server = enet_host_create(&serverAddr, 10, 3, 0, 0);
}
if (m_server != nullptr) if (m_server != nullptr)
{ {
@ -89,6 +117,8 @@ void NetPlayServer::ThreadFunc()
int net; int net;
{ {
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
if (m_traversal_client)
m_traversal_client->HandleResends();
net = enet_host_service(m_server, &netEvent, 4); net = enet_host_service(m_server, &netEvent, 4);
} }
if (net > 0) if (net > 0)
@ -533,6 +563,13 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
return 0; return 0;
} }
void NetPlayServer::OnTraversalStateChanged()
{
if (m_dialog)
m_dialog->Update();
}
// called from ---GUI--- thread / and ---NETPLAY--- thread // called from ---GUI--- thread / and ---NETPLAY--- thread
void NetPlayServer::SendChatMessage(const std::string& msg) void NetPlayServer::SendChatMessage(const std::string& msg)
{ {
@ -638,6 +675,96 @@ u16 NetPlayServer::GetPort()
return m_server->address.port; return m_server->address.port;
} }
void NetPlayServer::SetNetPlayUI(NetPlayUI* dialog)
{
m_dialog = dialog;
}
// called from ---GUI--- thread
std::unordered_set<std::string> NetPlayServer::GetInterfaceSet()
{
std::unordered_set<std::string> result;
auto lst = GetInterfaceListInternal();
for (auto list_entry : lst)
result.emplace(list_entry.first);
return result;
}
// called from ---GUI--- thread
std::string NetPlayServer::GetInterfaceHost(const std::string inter)
{
char buf[16];
sprintf(buf, ":%d", GetPort());
auto lst = GetInterfaceListInternal();
for (const auto& list_entry : lst)
{
if (list_entry.first == inter)
{
return list_entry.second + buf;
}
}
return "?";
}
// called from ---GUI--- thread
std::vector<std::pair<std::string, std::string>> NetPlayServer::GetInterfaceListInternal()
{
std::vector<std::pair<std::string, std::string>> result;
#if defined(_WIN32)
#elif defined(__APPLE__)
// we do this to get the friendly names rather than the BSD ones. ew.
if (m_dynamic_store && m_prefs)
{
CFArrayRef ary = SCNetworkServiceCopyAll((SCPreferencesRef)m_prefs);
for (CFIndex i = 0; i < CFArrayGetCount(ary); i++)
{
SCNetworkServiceRef ifr = (SCNetworkServiceRef)CFArrayGetValueAtIndex(ary, i);
std::string name = CFStrToStr(SCNetworkServiceGetName(ifr));
CFStringRef key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, SCNetworkServiceGetServiceID(ifr), kSCEntNetIPv4);
CFDictionaryRef props = (CFDictionaryRef)SCDynamicStoreCopyValue((SCDynamicStoreRef)m_dynamic_store, key);
CFRelease(key);
if (!props)
continue;
CFArrayRef ipary = (CFArrayRef)CFDictionaryGetValue(props, kSCPropNetIPv4Addresses);
if (ipary)
{
for (CFIndex j = 0; j < CFArrayGetCount(ipary); j++)
result.emplace_back(std::make_pair(name, CFStrToStr((CFStringRef)CFArrayGetValueAtIndex(ipary, j))));
CFRelease(ipary);
}
}
CFRelease(ary);
}
#elif defined(ANDROID)
// Android has no getifaddrs for some stupid reason. If this
// functionality ends up actually being used on Android, fix this.
#else
ifaddrs* ifp;
char buf[512];
if (getifaddrs(&ifp) != -1)
{
for (struct ifaddrs* curifp = ifp; curifp; curifp = curifp->ifa_next)
{
struct sockaddr* sa = curifp->ifa_addr;
if (sa->sa_family != AF_INET)
continue;
struct sockaddr_in* sai = (struct sockaddr_in*) sa;
if (ntohl(((struct sockaddr_in*) sa)->sin_addr.s_addr) == 0x7f000001)
continue;
const char* ip = inet_ntop(sa->sa_family, &sai->sin_addr, buf, sizeof(buf));
if (ip == nullptr)
continue;
result.emplace_back(std::make_pair(curifp->ifa_name, ip));
}
freeifaddrs(ifp);
}
#endif
if (result.empty())
result.push_back(std::make_pair("!local!", "127.0.0.1"));
return result;
}
#ifdef USE_UPNP #ifdef USE_UPNP
#include <miniwget.h> #include <miniwget.h>
#include <miniupnpc.h> #include <miniupnpc.h>

View File

@ -9,18 +9,20 @@
#include <sstream> #include <sstream>
#include <unordered_set> #include <unordered_set>
#include "enet/enet.h"
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Common/TraversalClient.h"
#include "Common/Timer.h" #include "Common/Timer.h"
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
#include <SFML/Network/Packet.hpp> #include <SFML/Network/Packet.hpp>
class NetPlayServer class NetPlayUI;
class NetPlayServer : public TraversalClientClient
{ {
public: public:
void ThreadFunc(); void ThreadFunc();
NetPlayServer(const u16 port); NetPlayServer(const u16 port, bool traversal);
~NetPlayServer(); ~NetPlayServer();
bool ChangeGame(const std::string& game); bool ChangeGame(const std::string& game);
@ -42,6 +44,10 @@ public:
u16 GetPort(); u16 GetPort();
void SetNetPlayUI(NetPlayUI* dialog);
std::unordered_set<std::string> GetInterfaceSet();
std::string GetInterfaceHost(const std::string inter);
bool is_connected; bool is_connected;
#ifdef USE_UPNP #ifdef USE_UPNP
@ -71,8 +77,12 @@ private:
unsigned int OnConnect(ENetPeer* socket); unsigned int OnConnect(ENetPeer* socket);
unsigned int OnDisconnect(Client& player); unsigned int OnDisconnect(Client& player);
unsigned int OnData(sf::Packet& packet, Client& player); unsigned int OnData(sf::Packet& packet, Client& player);
virtual void OnTraversalStateChanged();
virtual void OnConnectReady(ENetAddress addr) {}
virtual void OnConnectFailed(u8 reason) {}
void UpdatePadMapping(); void UpdatePadMapping();
void UpdateWiimoteMapping(); void UpdateWiimoteMapping();
std::vector<std::pair<std::string, std::string>> GetInterfaceListInternal();
NetSettings m_settings; NetSettings m_settings;
@ -99,6 +109,8 @@ private:
std::thread m_thread; std::thread m_thread;
ENetHost* m_server; ENetHost* m_server;
TraversalClient* m_traversal_client;
NetPlayUI* m_dialog;
#ifdef USE_UPNP #ifdef USE_UPNP
static void mapPortThread(const u16 port); static void mapPortThread(const u16 port);

View File

@ -27,6 +27,7 @@
#include <wx/string.h> #include <wx/string.h>
#include <wx/textctrl.h> #include <wx/textctrl.h>
#include <wx/translation.h> #include <wx/translation.h>
#include <wx/clipbrd.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FifoQueue.h" #include "Common/FifoQueue.h"
@ -53,6 +54,26 @@ static NetPlayServer* netplay_server = nullptr;
static NetPlayClient* netplay_client = nullptr; static NetPlayClient* netplay_client = nullptr;
NetPlayDiag *NetPlayDiag::npd = nullptr; NetPlayDiag *NetPlayDiag::npd = nullptr;
static wxString FailureReasonStringForHostLabel(int reason)
{
switch (reason)
{
case TraversalClient::BadHost:
return _("(Error: Bad host)");
case TraversalClient::VersionTooOld:
return _("(Error: Dolphin too old)");
case TraversalClient::ServerForgotAboutUs:
return _("(Error: Disconnected)");
case TraversalClient::SocketSendError:
return _("(Error: Socket)");
case TraversalClient::ResendTimeout:
return _("(Error: Timeout)");
default:
return _("(Error: Unknown)");
}
}
static std::string BuildGameName(const GameListItem& game) static std::string BuildGameName(const GameListItem& game)
{ {
// Lang needs to be consistent // Lang needs to be consistent
@ -93,7 +114,6 @@ NetPlaySetupDiag::NetPlaySetupDiag(wxWindow* const parent, const CGameListCtrl*
nick_szr->Add(nick_lbl, 0, wxCENTER); nick_szr->Add(nick_lbl, 0, wxCENTER);
nick_szr->Add(m_nickname_text, 0, wxALL, 5); nick_szr->Add(m_nickname_text, 0, wxALL, 5);
// tabs // tabs
wxNotebook* const notebook = new wxNotebook(panel, wxID_ANY); wxNotebook* const notebook = new wxNotebook(panel, wxID_ANY);
wxPanel* const connect_tab = new wxPanel(notebook, wxID_ANY); wxPanel* const connect_tab = new wxPanel(notebook, wxID_ANY);
@ -101,85 +121,91 @@ NetPlaySetupDiag::NetPlaySetupDiag(wxWindow* const parent, const CGameListCtrl*
wxPanel* const host_tab = new wxPanel(notebook, wxID_ANY); wxPanel* const host_tab = new wxPanel(notebook, wxID_ANY);
notebook->AddPage(host_tab, _("Host")); notebook->AddPage(host_tab, _("Host"));
m_direct_traversal = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(75, -1));
m_direct_traversal->Bind(wxEVT_COMMAND_CHOICE_SELECTED, &NetPlaySetupDiag::OnChoice, this);
m_direct_traversal->Append(_("Traversal"));
m_direct_traversal->Append(_("Direct"));
m_direct_traversal->Select(0);
// connect tab // connect tab
{ {
wxStaticText* const ip_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Address :")); m_ip_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Host Code :"));
std::string address; std::string address;
netplay_section.Get("Address", &address, "localhost"); netplay_section.Get("HostCode", &address, "00000000");
m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(address)); m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(address));
wxStaticText* const port_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Port :")); m_client_port_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Port :"));
// string? w/e // string? w/e
std::string port; std::string port;
netplay_section.Get("ConnectPort", &port, "2626"); netplay_section.Get("ConnectPort", &port, "2626");
m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(port)); m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(port));
wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, _("Connect")); wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, _("Connect"));
connect_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnJoin, this); connect_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnJoin, this);
wxStaticText* const alert_lbl = new wxStaticText(connect_tab, wxID_ANY, wxStaticText* const alert_lbl = new wxStaticText(connect_tab, wxID_ANY,
_("ALERT:\n\n" _("ALERT:\n\n"
"Netplay will only work with the following settings:\n" "Netplay will only work with the following settings:\n"
" - DSP Emulator Engine Must be the same on all computers!\n" " - DSP Emulator Engine Must be the same on all computers!\n"
" - DSP on Dedicated Thread [OFF]\n" " - DSP on Dedicated Thread [OFF]\n"
" - Manually set the extensions for each Wiimote\n" " - Manually set the extensions for each Wiimote\n"
"\n" "\n"
"All players should use the same Dolphin version and settings.\n" "All players should use the same Dolphin version and settings.\n"
"All memory cards must be identical between players or disabled.\n" "All memory cards must be identical between players or disabled.\n"
"Wiimote support is probably terrible. Don't use it.\n" "Wiimote support is probably terrible. Don't use it.\n"
"\n" "\n"
"The host must have the chosen TCP port open/forwarded!\n")); "If connecting directly host must have the chosen UDP port open/forwarded!\n"));
wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
top_szr->Add(ip_lbl, 0, wxCENTER | wxRIGHT, 5);
top_szr->Add(m_connect_ip_text, 3);
top_szr->Add(port_lbl, 0, wxCENTER | wxRIGHT | wxLEFT, 5);
top_szr->Add(m_connect_port_text, 1);
wxBoxSizer* const con_szr = new wxBoxSizer(wxVERTICAL); top_szr->Add(m_ip_lbl, 0, wxCENTER | wxRIGHT, 5);
con_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5); top_szr->Add(m_connect_ip_text, 3);
con_szr->AddStretchSpacer(1); top_szr->Add(m_client_port_lbl, 0, wxCENTER | wxRIGHT | wxLEFT, 5);
con_szr->Add(alert_lbl, 0, wxLEFT | wxRIGHT | wxEXPAND, 5); top_szr->Add(m_connect_port_text, 1);
con_szr->AddStretchSpacer(1);
con_szr->Add(connect_btn, 0, wxALL | wxALIGN_RIGHT, 5);
connect_tab->SetSizerAndFit(con_szr); wxBoxSizer* const con_szr = new wxBoxSizer(wxVERTICAL);
con_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
con_szr->AddStretchSpacer(1);
con_szr->Add(alert_lbl, 0, wxLEFT | wxRIGHT | wxEXPAND, 5);
con_szr->AddStretchSpacer(1);
con_szr->Add(connect_btn, 0, wxALL | wxALIGN_RIGHT, 5);
connect_tab->SetSizerAndFit(con_szr);
} }
// host tab // host tab
{ {
wxStaticText* const port_lbl = new wxStaticText(host_tab, wxID_ANY, _("Port :")); m_host_port_lbl = new wxStaticText(host_tab, wxID_ANY, _("Port :"));
// string? w/e // string? w/e
std::string port; std::string port;
netplay_section.Get("HostPort", &port, "2626"); netplay_section.Get("HostPort", &port, "2626");
m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, StrToWxStr(port)); m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, StrToWxStr(port));
wxButton* const host_btn = new wxButton(host_tab, wxID_ANY, _("Host")); wxButton* const host_btn = new wxButton(host_tab, wxID_ANY, _("Host"));
host_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnHost, this); host_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnHost, this);
m_game_lbox = new wxListBox(host_tab, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SORT); m_game_lbox = new wxListBox(host_tab, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SORT);
m_game_lbox->Bind(wxEVT_LISTBOX_DCLICK, &NetPlaySetupDiag::OnHost, this); m_game_lbox->Bind(wxEVT_LISTBOX_DCLICK, &NetPlaySetupDiag::OnHost, this);
FillWithGameNames(m_game_lbox, *game_list); FillWithGameNames(m_game_lbox, *game_list);
wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
top_szr->Add(port_lbl, 0, wxCENTER | wxRIGHT, 5); top_szr->Add(m_host_port_lbl, 0, wxCENTER | wxRIGHT, 5);
top_szr->Add(m_host_port_text, 0); top_szr->Add(m_host_port_text, 0);
#ifdef USE_UPNP #ifdef USE_UPNP
m_upnp_chk = new wxCheckBox(host_tab, wxID_ANY, _("Forward port (UPnP)")); m_upnp_chk = new wxCheckBox(host_tab, wxID_ANY, _("Forward port (UPnP)"));
top_szr->Add(m_upnp_chk, 0, wxALL | wxALIGN_RIGHT, 5); top_szr->Add(m_upnp_chk, 0, wxALL | wxALIGN_RIGHT, 5);
#endif #endif
wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL); wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL);
host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5); host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
host_szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxEXPAND, 5); host_szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxEXPAND, 5);
host_szr->Add(host_btn, 0, wxALL | wxALIGN_RIGHT, 5); host_szr->Add(host_btn, 0, wxALL | wxALIGN_RIGHT, 5);
host_tab->SetSizerAndFit(host_szr); host_tab->SetSizerAndFit(host_szr);
} }
// bottom row // bottom row
@ -202,6 +228,21 @@ NetPlaySetupDiag::NetPlaySetupDiag(wxWindow* const parent, const CGameListCtrl*
Center(); Center();
Show(); Show();
// Needs to be done last or it set up the spacing on the page correctly
//client tab
{
m_client_port_lbl->Show(false);
m_connect_port_text->Show(false);
}
//server tab
{
m_host_port_lbl->Show(false);
m_host_port_text->Show(false);
m_upnp_chk->Show(false);
}
} }
NetPlaySetupDiag::~NetPlaySetupDiag() NetPlaySetupDiag::~NetPlaySetupDiag()
@ -212,7 +253,14 @@ NetPlaySetupDiag::~NetPlaySetupDiag()
IniFile::Section& netplay_section = *inifile.GetOrCreateSection("NetPlay"); IniFile::Section& netplay_section = *inifile.GetOrCreateSection("NetPlay");
netplay_section.Set("Nickname", WxStrToStr(m_nickname_text->GetValue())); netplay_section.Set("Nickname", WxStrToStr(m_nickname_text->GetValue()));
netplay_section.Set("Address", WxStrToStr(m_connect_ip_text->GetValue())); if (m_direct_traversal->GetCurrentSelection() == 1)
{
netplay_section.Set("Address", WxStrToStr(m_connect_ip_text->GetValue()));
}
else
{
netplay_section.Set("HostCode", WxStrToStr(m_connect_ip_text->GetValue()));
}
netplay_section.Set("ConnectPort", WxStrToStr(m_connect_port_text->GetValue())); netplay_section.Set("ConnectPort", WxStrToStr(m_connect_port_text->GetValue()));
netplay_section.Set("HostPort", WxStrToStr(m_host_port_text->GetValue())); netplay_section.Set("HostPort", WxStrToStr(m_host_port_text->GetValue()));
@ -230,7 +278,13 @@ void NetPlaySetupDiag::MakeNetPlayDiag(int port, const std::string &game, bool i
else else
ip = WxStrToStr(m_connect_ip_text->GetValue()); ip = WxStrToStr(m_connect_ip_text->GetValue());
netplay_client = new NetPlayClient(ip, (u16)port, npd, WxStrToStr(m_nickname_text->GetValue())); bool trav;
if (!is_hosting && m_direct_traversal->GetCurrentSelection() == 0)
trav = true;
else
trav = false;
netplay_client = new NetPlayClient(ip, (u16)port, npd, WxStrToStr(m_nickname_text->GetValue()), trav);
if (netplay_client->is_connected) if (netplay_client->is_connected)
{ {
npd->Show(); npd->Show();
@ -259,9 +313,15 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&)
std::string game(WxStrToStr(m_game_lbox->GetStringSelection())); std::string game(WxStrToStr(m_game_lbox->GetStringSelection()));
bool trav;
if (m_direct_traversal->GetCurrentSelection() == 0)
trav = true;
else
trav = false;
unsigned long port = 0; unsigned long port = 0;
m_host_port_text->GetValue().ToULong(&port); m_host_port_text->GetValue().ToULong(&port);
netplay_server = new NetPlayServer(u16(port)); netplay_server = new NetPlayServer(u16(port), trav);
netplay_server->ChangeGame(game); netplay_server->ChangeGame(game);
netplay_server->AdjustPadBufferSize(INITIAL_PAD_BUFFER_SIZE); netplay_server->AdjustPadBufferSize(INITIAL_PAD_BUFFER_SIZE);
if (netplay_server->is_connected) if (netplay_server->is_connected)
@ -270,7 +330,8 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&)
if (m_upnp_chk->GetValue()) if (m_upnp_chk->GetValue())
netplay_server->TryPortmapping(port); netplay_server->TryPortmapping(port);
#endif #endif
MakeNetPlayDiag(port, game, true); MakeNetPlayDiag(netplay_server->GetPort(), game, true);
netplay_server->SetNetPlayUI(NetPlayDiag::GetInstance());
} }
else else
{ {
@ -292,17 +353,75 @@ void NetPlaySetupDiag::OnJoin(wxCommandEvent&)
MakeNetPlayDiag(port, "", false); MakeNetPlayDiag(port, "", false);
} }
void NetPlaySetupDiag::OnChoice(wxCommandEvent& event)
{
int sel = m_direct_traversal->GetSelection();
IniFile inifile;
inifile.Load(File::GetUserPath(D_CONFIG_IDX) + "Dolphin.ini");
IniFile::Section& netplay_section = *inifile.GetOrCreateSection("NetPlay");
if (sel == 0)
{
//Traversal
//client tab
{
m_ip_lbl->SetLabelText("Host Code: ");
std::string address;
netplay_section.Get("HostCode", &address, "00000000");
m_connect_ip_text->SetLabelText(address);
m_client_port_lbl->Show(false);
m_connect_port_text->Show(false);
}
//server tab
{
m_host_port_lbl->Show(false);
m_host_port_text->Show(false);
m_upnp_chk->Show(false);
}
}
else
{
//Direct
//client tab
{
m_ip_lbl->SetLabelText("IP Address :");
std::string address;
netplay_section.Get("Address", &address, "127.0.0.1");
m_connect_ip_text->SetLabelText(address);
m_client_port_lbl->Show(true);
m_connect_port_text->Show(true);
}
//server tab
{
m_host_port_lbl->Show(true);
m_host_port_text->Show(true);
m_upnp_chk->Show(true);
}
}
}
void NetPlaySetupDiag::OnQuit(wxCommandEvent&) void NetPlaySetupDiag::OnQuit(wxCommandEvent&)
{ {
Destroy(); Destroy();
} }
NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game_list, NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game_list,
const std::string& game, const bool is_hosting) const std::string& game, const bool is_hosting)
: wxFrame(parent, wxID_ANY, _("Dolphin NetPlay")) : wxFrame(parent, wxID_ANY, _("Dolphin NetPlay"))
, m_selected_game(game) , m_selected_game(game)
, m_start_btn(nullptr) , m_start_btn(nullptr)
, m_game_list(game_list) , m_game_list(game_list)
, m_host_type_choice(nullptr)
, m_host_label(nullptr)
, m_host_copy_btn(nullptr)
, m_host_copy_btn_is_retry(false)
, m_is_hosting(is_hosting)
{ {
Bind(wxEVT_THREAD, &NetPlayDiag::OnThread, this); Bind(wxEVT_THREAD, &NetPlayDiag::OnThread, this);
@ -310,10 +429,10 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
// top crap // top crap
m_game_btn = new wxButton(panel, wxID_ANY, m_game_btn = new wxButton(panel, wxID_ANY,
StrToWxStr(m_selected_game).Prepend(_(" Game : ")), StrToWxStr(m_selected_game).Prepend(_(" Game : ")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT); wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
if (is_hosting) if (m_is_hosting)
m_game_btn->Bind(wxEVT_BUTTON, &NetPlayDiag::OnChangeGame, this); m_game_btn->Bind(wxEVT_BUTTON, &NetPlayDiag::OnChangeGame, this);
else else
m_game_btn->Disable(); m_game_btn->Disable();
@ -343,9 +462,34 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
m_player_lbox = new wxListBox(panel, wxID_ANY, wxDefaultPosition, wxSize(256, -1)); m_player_lbox = new wxListBox(panel, wxID_ANY, wxDefaultPosition, wxSize(256, -1));
wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Players")); wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Players"));
player_szr->Add(m_player_lbox, 1, wxEXPAND);
// player list // player list
if (is_hosting) if (m_is_hosting && g_TraversalClient)
{
wxBoxSizer* const host_szr = new wxBoxSizer(wxHORIZONTAL);
m_host_type_choice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(60, -1));
m_host_type_choice->Bind(wxEVT_COMMAND_CHOICE_SELECTED, &NetPlayDiag::OnChoice, this);
m_host_type_choice->Append(_("ID:"));
host_szr->Add(m_host_type_choice);
m_host_label = new wxStaticText(panel, wxID_ANY, "555.555.555.555:55555", wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE | wxALIGN_LEFT);
// Update() should fix this immediately.
m_host_label->SetLabel(_(""));
host_szr->Add(m_host_label, 1, wxLEFT | wxCENTER, 5);
m_host_copy_btn = new wxButton(panel, wxID_ANY, _("Copy"));
m_host_copy_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &NetPlayDiag::OnCopyIP, this);
m_host_copy_btn->Disable();
host_szr->Add(m_host_copy_btn, 0, wxLEFT | wxCENTER, 5);
player_szr->Add(host_szr, 0, wxEXPAND | wxBOTTOM, 5);
m_host_type_choice->Select(0);
UpdateHostLabel();
}
player_szr->Add(m_player_lbox, 1, wxEXPAND);
if (m_is_hosting)
{ {
m_player_lbox->Bind(wxEVT_LISTBOX, &NetPlayDiag::OnPlayerSelect, this); m_player_lbox->Bind(wxEVT_LISTBOX, &NetPlayDiag::OnPlayerSelect, this);
m_kick_btn = new wxButton(panel, wxID_ANY, _("Kick Player")); m_kick_btn = new wxButton(panel, wxID_ANY, _("Kick Player"));
@ -398,7 +542,7 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
panel->SetSizerAndFit(main_szr); panel->SetSizerAndFit(main_szr);
main_szr->SetSizeHints(this); main_szr->SetSizeHints(this);
SetSize(512, 512-128); SetSize(512, 512 - 128);
Center(); Center();
} }
@ -531,6 +675,11 @@ void NetPlayDiag::OnQuit(wxCommandEvent&)
// update gui // update gui
void NetPlayDiag::OnThread(wxThreadEvent& event) void NetPlayDiag::OnThread(wxThreadEvent& event)
{ {
if (m_is_hosting && m_host_label && g_TraversalClient)
{
UpdateHostLabel();
}
// player list // player list
m_playerids.clear(); m_playerids.clear();
std::string tmps; std::string tmps;
@ -563,8 +712,8 @@ void NetPlayDiag::OnThread(wxThreadEvent& event)
// flash window in taskbar when someone joins if window isn't active // flash window in taskbar when someone joins if window isn't active
static u8 numPlayers = 1; static u8 numPlayers = 1;
bool focus = (wxWindow::FindFocus() == this || (wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() == this) || bool focus = (wxWindow::FindFocus() == this || (wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() == this) ||
(wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() != nullptr (wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() != nullptr
&& wxWindow::FindFocus()->GetParent()->GetParent() == this)); && wxWindow::FindFocus()->GetParent()->GetParent() == this));
if (netplay_server != nullptr && numPlayers < m_playerids.size() && !focus) if (netplay_server != nullptr && numPlayers < m_playerids.size() && !focus)
{ {
RequestUserAttention(); RequestUserAttention();
@ -575,25 +724,25 @@ void NetPlayDiag::OnThread(wxThreadEvent& event)
{ {
case NP_GUI_EVT_CHANGE_GAME: case NP_GUI_EVT_CHANGE_GAME:
// update selected game :/ // update selected game :/
{ {
m_selected_game.assign(WxStrToStr(event.GetString())); m_selected_game.assign(WxStrToStr(event.GetString()));
wxString button_label = event.GetString(); wxString button_label = event.GetString();
m_game_btn->SetLabel(button_label.Prepend(_(" Game : "))); m_game_btn->SetLabel(button_label.Prepend(_(" Game : ")));
} }
break; break;
case NP_GUI_EVT_START_GAME : case NP_GUI_EVT_START_GAME:
// client start game :/ // client start game :/
{ {
netplay_client->StartGame(FindGame()); netplay_client->StartGame(FindGame());
} }
break; break;
case NP_GUI_EVT_STOP_GAME : case NP_GUI_EVT_STOP_GAME:
// client stop game // client stop game
{ {
netplay_client->StopGame(); netplay_client->StopGame();
} }
break; break;
} }
// chat messages // chat messages
@ -660,6 +809,98 @@ bool NetPlayDiag::IsRecording()
return m_record_chkbox->GetValue(); return m_record_chkbox->GetValue();
} }
void NetPlayDiag::OnCopyIP(wxCommandEvent&)
{
if (m_host_copy_btn_is_retry)
{
g_TraversalClient->ReconnectToServer();
Update();
}
else
{
if (wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxTextDataObject(m_host_label->GetLabel()));
wxTheClipboard->Close();
}
}
}
void NetPlayDiag::OnChoice(wxCommandEvent& event)
{
UpdateHostLabel();
}
void NetPlayDiag::UpdateHostLabel()
{
wxString label = _(" (internal IP)");
auto DeLabel = [=](wxString str) {
if (str == _("Localhost"))
return std::string("!local!");
return WxStrToStr(str.Left(str.Len() - label.Len()));
};
auto EnLabel = [=](std::string str) -> wxString {
if (str == "!local!")
return _("Localhost");
return StrToWxStr(str) + label;
};
int sel = m_host_type_choice->GetSelection();
if (sel == 0)
{
// the traversal ID
switch (g_TraversalClient->m_State)
{
case TraversalClient::Connecting:
m_host_label->SetForegroundColour(*wxLIGHT_GREY);
m_host_label->SetLabel("...");
m_host_copy_btn->SetLabel(_("Copy"));
m_host_copy_btn->Disable();
break;
case TraversalClient::Connected:
m_host_label->SetForegroundColour(*wxBLACK);
m_host_label->SetLabel(wxString(g_TraversalClient->m_HostId.data(), g_TraversalClient->m_HostId.size()));
m_host_copy_btn->SetLabel(_("Copy"));
m_host_copy_btn->Enable();
m_host_copy_btn_is_retry = false;
break;
case TraversalClient::Failure:
m_host_label->SetForegroundColour(*wxBLACK);
m_host_label->SetLabel(FailureReasonStringForHostLabel(g_TraversalClient->m_FailureReason));
m_host_copy_btn->SetLabel(_("Retry"));
m_host_copy_btn->Enable();
m_host_copy_btn_is_retry = true;
break;
}
}
else if (sel != wxNOT_FOUND) // wxNOT_FOUND shouldn't generally happen
{
m_host_label->SetForegroundColour(*wxBLACK);
m_host_label->SetLabel(netplay_server->GetInterfaceHost(DeLabel(m_host_type_choice->GetString(sel))));
m_host_copy_btn->SetLabel(_("Copy"));
m_host_copy_btn->Enable();
m_host_copy_btn_is_retry = false;
}
auto set = netplay_server->GetInterfaceSet();
for (const std::string& iface : set)
{
wxString wxIface = EnLabel(iface);
if (m_host_type_choice->FindString(wxIface) == wxNOT_FOUND)
m_host_type_choice->Append(wxIface);
}
for (unsigned i = 1, count = m_host_type_choice->GetCount(); i != count; i++)
{
if (set.find(DeLabel(m_host_type_choice->GetString(i))) == set.end())
{
m_host_type_choice->Delete(i);
i--;
count--;
}
}
}
ChangeGameDiag::ChangeGameDiag(wxWindow* const parent, const CGameListCtrl* const game_list, wxString& game_name) ChangeGameDiag::ChangeGameDiag(wxWindow* const parent, const CGameListCtrl* const game_list, wxString& game_name)
: wxDialog(parent, wxID_ANY, _("Change Game")) : wxDialog(parent, wxID_ANY, _("Change Game"))
, m_game_name(game_name) , m_game_name(game_name)
@ -728,16 +969,16 @@ PadMapDiag::PadMapDiag(wxWindow* const parent, PadMapping map[], PadMapping wiim
v_szr->Add(new wxStaticText(this, wxID_ANY, (wxString(_("Wiimote ")) + (wxChar)('0' + i))), v_szr->Add(new wxStaticText(this, wxID_ANY, (wxString(_("Wiimote ")) + (wxChar)('0' + i))),
1, wxALIGN_CENTER_HORIZONTAL); 1, wxALIGN_CENTER_HORIZONTAL);
m_map_cbox[i+4] = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, player_names); m_map_cbox[i + 4] = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, player_names);
m_map_cbox[i+4]->Bind(wxEVT_CHOICE, &PadMapDiag::OnAdjust, this); m_map_cbox[i + 4]->Bind(wxEVT_CHOICE, &PadMapDiag::OnAdjust, this);
if (m_wiimapping[i] == -1) if (m_wiimapping[i] == -1)
m_map_cbox[i+4]->Select(0); m_map_cbox[i + 4]->Select(0);
else else
for (unsigned int j = 0; j < m_player_list.size(); j++) for (unsigned int j = 0; j < m_player_list.size(); j++)
if (m_wiimapping[i] == m_player_list[j]->pid) if (m_wiimapping[i] == m_player_list[j]->pid)
m_map_cbox[i+4]->Select(j + 1); m_map_cbox[i + 4]->Select(j + 1);
v_szr->Add(m_map_cbox[i+4], 1); v_szr->Add(m_map_cbox[i + 4], 1);
h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20); h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20);
h_szr->AddSpacer(10); h_szr->AddSpacer(10);
@ -763,7 +1004,7 @@ void PadMapDiag::OnAdjust(wxCommandEvent& event)
else else
m_mapping[i] = -1; m_mapping[i] = -1;
player_idx = m_map_cbox[i+4]->GetSelection(); player_idx = m_map_cbox[i + 4]->GetSelection();
if (player_idx > 0) if (player_idx > 0)
m_wiimapping[i] = m_player_list[player_idx - 1]->pid; m_wiimapping[i] = m_player_list[player_idx - 1]->pid;
else else

View File

@ -42,10 +42,16 @@ private:
void MakeNetPlayDiag(int port, const std::string &game, bool is_hosting); void MakeNetPlayDiag(int port, const std::string &game, bool is_hosting);
wxTextCtrl* m_nickname_text; void OnChoice(wxCommandEvent& event);
wxTextCtrl* m_host_port_text;
wxTextCtrl* m_connect_port_text; wxStaticText* m_ip_lbl;
wxTextCtrl* m_connect_ip_text; wxStaticText* m_client_port_lbl;
wxTextCtrl* m_nickname_text;
wxStaticText* m_host_port_lbl;
wxTextCtrl* m_host_port_text;
wxTextCtrl* m_connect_port_text;
wxTextCtrl* m_connect_ip_text;
wxChoice* m_direct_traversal;
wxListBox* m_game_lbox; wxListBox* m_game_lbox;
#ifdef USE_UPNP #ifdef USE_UPNP
@ -93,16 +99,25 @@ private:
void GetNetSettings(NetSettings &settings); void GetNetSettings(NetSettings &settings);
std::string FindGame(); std::string FindGame();
wxListBox* m_player_lbox; void OnCopyIP(wxCommandEvent&);
wxTextCtrl* m_chat_text; void OnChoice(wxCommandEvent& event);
wxTextCtrl* m_chat_msg_text; void UpdateHostLabel();
wxCheckBox* m_memcard_write;
wxCheckBox* m_record_chkbox;
std::string m_selected_game; wxListBox* m_player_lbox;
wxButton* m_game_btn; wxTextCtrl* m_chat_text;
wxButton* m_start_btn; wxTextCtrl* m_chat_msg_text;
wxButton* m_kick_btn; wxCheckBox* m_memcard_write;
wxCheckBox* m_record_chkbox;
std::string m_selected_game;
wxButton* m_game_btn;
wxButton* m_start_btn;
wxButton* m_kick_btn;
wxStaticText* m_host_label;
wxChoice* m_host_type_choice;
wxButton* m_host_copy_btn;
bool m_host_copy_btn_is_retry;
bool m_is_hosting;
std::vector<int> m_playerids; std::vector<int> m_playerids;