summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/Common.vcxproj5
-rw-r--r--Source/Core/Common/Common.vcxproj.filters7
-rw-r--r--Source/Core/Common/TraversalClient.cpp370
-rw-r--r--Source/Core/Common/TraversalClient.h88
-rw-r--r--Source/Core/Common/TraversalProto.h96
-rw-r--r--Source/Core/Core/NetPlayClient.cpp159
-rw-r--r--Source/Core/Core/NetPlayClient.h22
-rw-r--r--Source/Core/Core/NetPlayProto.h37
-rw-r--r--Source/Core/Core/NetPlayServer.cpp139
-rw-r--r--Source/Core/Core/NetPlayServer.h18
-rw-r--r--Source/Core/DolphinWX/NetWindow.cpp429
-rw-r--r--Source/Core/DolphinWX/NetWindow.h43
12 files changed, 1249 insertions, 164 deletions
diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index dfe91db48b..fdd3183587 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -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> \ No newline at end of file
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index 2f121ca882..da11bd379b 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -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> \ No newline at end of file
diff --git a/Source/Core/Common/TraversalClient.cpp b/Source/Core/Common/TraversalClient.cpp
new file mode 100644
index 0000000000..9ac5eca238
--- /dev/null
+++ b/Source/Core/Common/TraversalClient.cpp
@@ -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();
+ }
+}
diff --git a/Source/Core/Common/TraversalClient.h b/Source/Core/Common/TraversalClient.h
new file mode 100644
index 0000000000..838ea6448a
--- /dev/null
+++ b/Source/Core/Common/TraversalClient.h
@@ -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();
diff --git a/Source/Core/Common/TraversalProto.h b/Source/Core/Common/TraversalProto.h
new file mode 100644
index 0000000000..32891beac6
--- /dev/null
+++ b/Source/Core/Common/TraversalProto.h
@@ -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)
+
diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp
index 8bb26feeb8..77c9a06e71 100644
--- a/Source/Core/Core/NetPlayClient.cpp
+++ b/Source/Core/Core/NetPlayClient.cpp
@@ -38,10 +38,25 @@ NetPlayClient::~NetPlayClient()
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
-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_client(nullptr)
, m_server(nullptr)
@@ -53,6 +68,8 @@ NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlay
, m_is_recording(false)
, m_pid(0)
, m_connecting(false)
+ , m_traversal_client(nullptr)
+ , m_state(Failure)
{
m_target_buffer_size = 20;
ClearBuffers();
@@ -61,35 +78,81 @@ NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlay
m_player_name = name;
- //Direct Connection
- m_client = enet_host_create(nullptr, 1, 3, 0, 0);
-
- if (m_client == nullptr)
+ if (!traversal)
{
- PanicAlertT("Couldn't Create Client");
- }
+ //Direct Connection
+ m_client = enet_host_create(nullptr, 1, 3, 0, 0);
- ENetAddress addr;
- enet_address_set_host(&addr, address.c_str());
- addr.port = port;
+ if (m_client == nullptr)
+ {
+ 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)
- {
- PanicAlertT("Couldn't create peer.");
- }
+ m_server = enet_host_connect(m_client, &addr, 3, 0);
+
+ 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
{
- 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()
{
ENetEvent netEvent;
+ m_state = Failure;
enet_peer_disconnect(m_server, 0);
while (enet_host_service(m_client, &netEvent, 3000) > 0)
{
@@ -407,6 +471,8 @@ void NetPlayClient::ThreadFunc()
int net;
{
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);
}
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
bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status)
{
diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h
index 6d923f0697..fab8e6548f 100644
--- a/Source/Core/Core/NetPlayClient.h
+++ b/Source/Core/Core/NetPlayClient.h
@@ -7,12 +7,11 @@
#include <map>
#include <queue>
#include <sstream>
-
-#include "enet/enet.h"
#include "Common/CommonTypes.h"
#include "Common/FifoQueue.h"
#include "Common/Thread.h"
#include "Common/Timer.h"
+#include "Common/TraversalClient.h"
#include "Core/NetPlayProto.h"
#include "InputCommon/GCPadStatus.h"
#include <SFML/Network/Packet.hpp>
@@ -43,12 +42,12 @@ public:
u32 ping;
};
-class NetPlayClient
+class NetPlayClient : public TraversalClientClient
{
public:
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();
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 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 InGamePadToLocalPad(u8 localPad);
u8 LocalWiimoteToInGameWiimote(u8 local_pad);
+ enum State
+ {
+ WaitingForTraversalClientConnection,
+ WaitingForTraversalClientConnectReady,
+ Connecting,
+ WaitingForHelloResponse,
+ Connected,
+ Failure
+ } m_state;
+
protected:
void ClearBuffers();
@@ -121,6 +134,7 @@ private:
std::string m_host_spec;
std::string m_player_name;
bool m_connecting;
+ TraversalClient* m_traversal_client;
};
void NetPlay_Enable(NetPlayClient* const np);
diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h
index 255635810b..b7409fa54b 100644
--- a/Source/Core/Core/NetPlayProto.h
+++ b/Source/Core/Core/NetPlayProto.h
@@ -34,32 +34,31 @@ typedef std::vector<u8> NetWiimote;
static const int NETPLAY_INITIAL_GCTIME = 1272737767;
-
// messages
enum
{
- NP_MSG_PLAYER_JOIN = 0x10,
- NP_MSG_PLAYER_LEAVE = 0x11,
+ NP_MSG_PLAYER_JOIN = 0x10,
+ NP_MSG_PLAYER_LEAVE = 0x11,
- NP_MSG_CHAT_MESSAGE = 0x30,
+ NP_MSG_CHAT_MESSAGE = 0x30,
- NP_MSG_PAD_DATA = 0x60,
- NP_MSG_PAD_MAPPING = 0x61,
- NP_MSG_PAD_BUFFER = 0x62,
+ NP_MSG_PAD_DATA = 0x60,
+ NP_MSG_PAD_MAPPING = 0x61,
+ NP_MSG_PAD_BUFFER = 0x62,
- NP_MSG_WIIMOTE_DATA = 0x70,
- NP_MSG_WIIMOTE_MAPPING = 0x71,
+ NP_MSG_WIIMOTE_DATA = 0x70,
+ NP_MSG_WIIMOTE_MAPPING = 0x71,
- NP_MSG_START_GAME = 0xA0,
- NP_MSG_CHANGE_GAME = 0xA1,
- NP_MSG_STOP_GAME = 0xA2,
- NP_MSG_DISABLE_GAME = 0xA3,
+ NP_MSG_START_GAME = 0xA0,
+ NP_MSG_CHANGE_GAME = 0xA1,
+ NP_MSG_STOP_GAME = 0xA2,
+ NP_MSG_DISABLE_GAME = 0xA3,
- NP_MSG_READY = 0xD0,
- NP_MSG_NOT_READY = 0xD1,
+ NP_MSG_READY = 0xD0,
+ NP_MSG_NOT_READY = 0xD1,
- NP_MSG_PING = 0xE0,
- NP_MSG_PONG = 0xE1,
+ NP_MSG_PING = 0xE0,
+ NP_MSG_PONG = 0xE1,
NP_MSG_PLAYER_PING_DATA = 0xE2,
};
@@ -70,8 +69,8 @@ typedef u32 FrameNum;
enum
{
- CON_ERR_SERVER_FULL = 1,
- CON_ERR_GAME_RUNNING = 2,
+ CON_ERR_SERVER_FULL = 1,
+ CON_ERR_GAME_RUNNING = 2,
CON_ERR_VERSION_MISMATCH = 3
};
diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp
index f8861c32e4..9282d71dbc 100644
--- a/Source/Core/Core/NetPlayServer.cpp
+++ b/Source/Core/Core/NetPlayServer.cpp
@@ -19,6 +19,17 @@ NetPlayServer::~NetPlayServer()
m_do_loop = false;
m_thread.join();
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
@@ -30,7 +41,7 @@ NetPlayServer::~NetPlayServer()
}
// called from ---GUI--- thread
-NetPlayServer::NetPlayServer(const u16 port)
+NetPlayServer::NetPlayServer(const u16 port, bool traversal)
: is_connected(false)
, m_is_running(false)
, m_do_loop(false)
@@ -40,6 +51,8 @@ NetPlayServer::NetPlayServer(const u16 port)
, m_target_buffer_size(0)
, m_selected_game("")
, m_server(nullptr)
+ , m_traversal_client(nullptr)
+ , m_dialog(nullptr)
{
//--use server time
if (enet_initialize() != 0)
@@ -49,11 +62,26 @@ NetPlayServer::NetPlayServer(const u16 port)
memset(m_pad_map, -1, sizeof(m_pad_map));
memset(m_wiimote_map, -1, sizeof(m_wiimote_map));
-
- ENetAddress serverAddr;
- serverAddr.host = ENET_HOST_ANY;
- serverAddr.port = port;
- m_server = enet_host_create(&serverAddr, 10, 3, 0, 0);
+ if (traversal)
+ {
+ if (!EnsureTraversalClient("dolphin-emu.org", 0))
+ return;
+
+ g_TraversalClient->m_Client = this;
+ m_traversal_client = g_TraversalClient.get();
+
+ 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)
{
@@ -89,6 +117,8 @@ void NetPlayServer::ThreadFunc()
int net;
{
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);
}
if (net > 0)
@@ -533,6 +563,13 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
return 0;
}
+
+void NetPlayServer::OnTraversalStateChanged()
+{
+ if (m_dialog)
+ m_dialog->Update();
+}
+
// called from ---GUI--- thread / and ---NETPLAY--- thread
void NetPlayServer::SendChatMessage(const std::string& msg)
{
@@ -638,6 +675,96 @@ u16 NetPlayServer::GetPort()
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
#include <miniwget.h>
#include <miniupnpc.h>
diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h
index d82195f5f7..6fa7909822 100644
--- a/Source/Core/Core/NetPlayServer.h
+++ b/Source/Core/Core/NetPlayServer.h
@@ -9,18 +9,20 @@
#include <sstream>
#include <unordered_set>
-#include "enet/enet.h"
#include "Common/Thread.h"
+#include "Common/TraversalClient.h"
#include "Common/Timer.h"
#include "Core/NetPlayProto.h"
#include <SFML/Network/Packet.hpp>
-class NetPlayServer
+class NetPlayUI;
+
+class NetPlayServer : public TraversalClientClient
{
public:
void ThreadFunc();
- NetPlayServer(const u16 port);
+ NetPlayServer(const u16 port, bool traversal);
~NetPlayServer();
bool ChangeGame(const std::string& game);
@@ -42,6 +44,10 @@ public:
u16 GetPort();
+ void SetNetPlayUI(NetPlayUI* dialog);
+ std::unordered_set<std::string> GetInterfaceSet();
+ std::string GetInterfaceHost(const std::string inter);
+
bool is_connected;
#ifdef USE_UPNP
@@ -71,8 +77,12 @@ private:
unsigned int OnConnect(ENetPeer* socket);
unsigned int OnDisconnect(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 UpdateWiimoteMapping();
+ std::vector<std::pair<std::string, std::string>> GetInterfaceListInternal();
NetSettings m_settings;
@@ -99,6 +109,8 @@ private:
std::thread m_thread;
ENetHost* m_server;
+ TraversalClient* m_traversal_client;
+ NetPlayUI* m_dialog;
#ifdef USE_UPNP
static void mapPortThread(const u16 port);
diff --git a/Source/Core/DolphinWX/NetWindow.cpp b/Source/Core/DolphinWX/NetWindow.cpp
index eb15e533a0..8d99a9403b 100644
--- a/Source/Core/DolphinWX/NetWindow.cpp
+++ b/Source/Core/DolphinWX/NetWindow.cpp
@@ -27,6 +27,7 @@
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/translation.h>
+#include <wx/clipbrd.h>
#include "Common/CommonTypes.h"
#include "Common/FifoQueue.h"
@@ -53,6 +54,26 @@ static NetPlayServer* netplay_server = nullptr;
static NetPlayClient* netplay_client = 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)
{
// 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(m_nickname_text, 0, wxALL, 5);
-
// tabs
wxNotebook* const notebook = new wxNotebook(panel, 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);
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
{
- wxStaticText* const ip_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Address :"));
-
- std::string address;
- netplay_section.Get("Address", &address, "localhost");
- m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(address));
-
- wxStaticText* const port_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Port :"));
-
- // string? w/e
- std::string port;
- netplay_section.Get("ConnectPort", &port, "2626");
- m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(port));
-
- wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, _("Connect"));
- connect_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnJoin, this);
-
- wxStaticText* const alert_lbl = new wxStaticText(connect_tab, wxID_ANY,
- _("ALERT:\n\n"
- "Netplay will only work with the following settings:\n"
- " - DSP Emulator Engine Must be the same on all computers!\n"
- " - DSP on Dedicated Thread [OFF]\n"
- " - Manually set the extensions for each Wiimote\n"
- "\n"
- "All players should use the same Dolphin version and settings.\n"
- "All memory cards must be identical between players or disabled.\n"
- "Wiimote support is probably terrible. Don't use it.\n"
- "\n"
- "The host must have the chosen TCP port open/forwarded!\n"));
-
- 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);
- 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);
+ m_ip_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Host Code :"));
+
+ std::string address;
+ netplay_section.Get("HostCode", &address, "00000000");
+ m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(address));
+
+ m_client_port_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Port :"));
+
+ // string? w/e
+ std::string port;
+ netplay_section.Get("ConnectPort", &port, "2626");
+ m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(port));
+
+ wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, _("Connect"));
+ connect_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnJoin, this);
+
+ wxStaticText* const alert_lbl = new wxStaticText(connect_tab, wxID_ANY,
+ _("ALERT:\n\n"
+ "Netplay will only work with the following settings:\n"
+ " - DSP Emulator Engine Must be the same on all computers!\n"
+ " - DSP on Dedicated Thread [OFF]\n"
+ " - Manually set the extensions for each Wiimote\n"
+ "\n"
+ "All players should use the same Dolphin version and settings.\n"
+ "All memory cards must be identical between players or disabled.\n"
+ "Wiimote support is probably terrible. Don't use it.\n"
+ "\n"
+ "If connecting directly host must have the chosen UDP port open/forwarded!\n"));
+
+ wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
+
+ top_szr->Add(m_ip_lbl, 0, wxCENTER | wxRIGHT, 5);
+ top_szr->Add(m_connect_ip_text, 3);
+ top_szr->Add(m_client_port_lbl, 0, wxCENTER | wxRIGHT | wxLEFT, 5);
+ top_szr->Add(m_connect_port_text, 1);
+
+ 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
{
- 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
- std::string port;
- netplay_section.Get("HostPort", &port, "2626");
- m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, StrToWxStr(port));
+ // string? w/e
+ std::string port;
+ netplay_section.Get("HostPort", &port, "2626");
+ m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, StrToWxStr(port));
- wxButton* const host_btn = new wxButton(host_tab, wxID_ANY, _("Host"));
- host_btn->Bind(wxEVT_BUTTON, &NetPlaySetupDiag::OnHost, this);
+ wxButton* const host_btn = new wxButton(host_tab, wxID_ANY, _("Host"));
+ 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->Bind(wxEVT_LISTBOX_DCLICK, &NetPlaySetupDiag::OnHost, this);
+ 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);
- FillWithGameNames(m_game_lbox, *game_list);
+ FillWithGameNames(m_game_lbox, *game_list);
- wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
- top_szr->Add(port_lbl, 0, wxCENTER | wxRIGHT, 5);
- top_szr->Add(m_host_port_text, 0);
+ wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
+ top_szr->Add(m_host_port_lbl, 0, wxCENTER | wxRIGHT, 5);
+ top_szr->Add(m_host_port_text, 0);
#ifdef USE_UPNP
- m_upnp_chk = new wxCheckBox(host_tab, wxID_ANY, _("Forward port (UPnP)"));
- top_szr->Add(m_upnp_chk, 0, wxALL | wxALIGN_RIGHT, 5);
+ m_upnp_chk = new wxCheckBox(host_tab, wxID_ANY, _("Forward port (UPnP)"));
+ top_szr->Add(m_upnp_chk, 0, wxALL | wxALIGN_RIGHT, 5);
#endif
- wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL);
- host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
- host_szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxEXPAND, 5);
- host_szr->Add(host_btn, 0, wxALL | wxALIGN_RIGHT, 5);
+ wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL);
+ host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
+ host_szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxEXPAND, 5);
+ host_szr->Add(host_btn, 0, wxALL | wxALIGN_RIGHT, 5);
- host_tab->SetSizerAndFit(host_szr);
+ host_tab->SetSizerAndFit(host_szr);
}
// bottom row
@@ -202,6 +228,21 @@ NetPlaySetupDiag::NetPlaySetupDiag(wxWindow* const parent, const CGameListCtrl*
Center();
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()
@@ -212,7 +253,14 @@ NetPlaySetupDiag::~NetPlaySetupDiag()
IniFile::Section& netplay_section = *inifile.GetOrCreateSection("NetPlay");
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("HostPort", WxStrToStr(m_host_port_text->GetValue()));
@@ -230,7 +278,13 @@ void NetPlaySetupDiag::MakeNetPlayDiag(int port, const std::string &game, bool i
else
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)
{
npd->Show();
@@ -259,9 +313,15 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&)
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;
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->AdjustPadBufferSize(INITIAL_PAD_BUFFER_SIZE);
if (netplay_server->is_connected)
@@ -270,7 +330,8 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&)
if (m_upnp_chk->GetValue())
netplay_server->TryPortmapping(port);
#endif
- MakeNetPlayDiag(port, game, true);
+ MakeNetPlayDiag(netplay_server->GetPort(), game, true);
+ netplay_server->SetNetPlayUI(NetPlayDiag::GetInstance());
}
else
{
@@ -292,17 +353,75 @@ void NetPlaySetupDiag::OnJoin(wxCommandEvent&)
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&)
{
Destroy();
}
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"))
, m_selected_game(game)
, m_start_btn(nullptr)
, 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);
@@ -310,10 +429,10 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
// top crap
m_game_btn = new wxButton(panel, wxID_ANY,
- StrToWxStr(m_selected_game).Prepend(_(" Game : ")),
- wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+ StrToWxStr(m_selected_game).Prepend(_(" Game : ")),
+ wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
- if (is_hosting)
+ if (m_is_hosting)
m_game_btn->Bind(wxEVT_BUTTON, &NetPlayDiag::OnChangeGame, this);
else
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));
wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Players"));
- player_szr->Add(m_player_lbox, 1, wxEXPAND);
+
// 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_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);
main_szr->SetSizeHints(this);
- SetSize(512, 512-128);
+ SetSize(512, 512 - 128);
Center();
}
@@ -531,6 +675,11 @@ void NetPlayDiag::OnQuit(wxCommandEvent&)
// update gui
void NetPlayDiag::OnThread(wxThreadEvent& event)
{
+ if (m_is_hosting && m_host_label && g_TraversalClient)
+ {
+ UpdateHostLabel();
+ }
+
// player list
m_playerids.clear();
std::string tmps;
@@ -563,8 +712,8 @@ void NetPlayDiag::OnThread(wxThreadEvent& event)
// flash window in taskbar when someone joins if window isn't active
static u8 numPlayers = 1;
bool focus = (wxWindow::FindFocus() == this || (wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() == this) ||
- (wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() != nullptr
- && wxWindow::FindFocus()->GetParent()->GetParent() == this));
+ (wxWindow::FindFocus() != nullptr && wxWindow::FindFocus()->GetParent() != nullptr
+ && wxWindow::FindFocus()->GetParent()->GetParent() == this));
if (netplay_server != nullptr && numPlayers < m_playerids.size() && !focus)
{
RequestUserAttention();
@@ -575,25 +724,25 @@ void NetPlayDiag::OnThread(wxThreadEvent& event)
{
case NP_GUI_EVT_CHANGE_GAME:
// update selected game :/
- {
+ {
m_selected_game.assign(WxStrToStr(event.GetString()));
wxString button_label = event.GetString();
m_game_btn->SetLabel(button_label.Prepend(_(" Game : ")));
- }
- break;
- case NP_GUI_EVT_START_GAME :
+ }
+ break;
+ case NP_GUI_EVT_START_GAME:
// client start game :/
- {
+ {
netplay_client->StartGame(FindGame());
- }
- break;
- case NP_GUI_EVT_STOP_GAME :
+ }
+ break;
+ case NP_GUI_EVT_STOP_GAME:
// client stop game
- {
+ {
netplay_client->StopGame();
- }
- break;
+ }
+ break;
}
// chat messages
@@ -660,6 +809,98 @@ bool NetPlayDiag::IsRecording()
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)
: wxDialog(parent, wxID_ANY, _("Change Game"))
, 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))),
1, wxALIGN_CENTER_HORIZONTAL);
- 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] = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, player_names);
+ m_map_cbox[i + 4]->Bind(wxEVT_CHOICE, &PadMapDiag::OnAdjust, this);
if (m_wiimapping[i] == -1)
- m_map_cbox[i+4]->Select(0);
+ m_map_cbox[i + 4]->Select(0);
else
for (unsigned int j = 0; j < m_player_list.size(); j++)
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->AddSpacer(10);
@@ -763,7 +1004,7 @@ void PadMapDiag::OnAdjust(wxCommandEvent& event)
else
m_mapping[i] = -1;
- player_idx = m_map_cbox[i+4]->GetSelection();
+ player_idx = m_map_cbox[i + 4]->GetSelection();
if (player_idx > 0)
m_wiimapping[i] = m_player_list[player_idx - 1]->pid;
else
diff --git a/Source/Core/DolphinWX/NetWindow.h b/Source/Core/DolphinWX/NetWindow.h
index 90d0dbd8c4..31cbf17b71 100644
--- a/Source/Core/DolphinWX/NetWindow.h
+++ b/Source/Core/DolphinWX/NetWindow.h
@@ -42,10 +42,16 @@ private:
void MakeNetPlayDiag(int port, const std::string &game, bool is_hosting);
- wxTextCtrl* m_nickname_text;
- wxTextCtrl* m_host_port_text;
- wxTextCtrl* m_connect_port_text;
- wxTextCtrl* m_connect_ip_text;
+ void OnChoice(wxCommandEvent& event);
+
+ wxStaticText* m_ip_lbl;
+ 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;
#ifdef USE_UPNP
@@ -93,16 +99,25 @@ private:
void GetNetSettings(NetSettings &settings);
std::string FindGame();
- wxListBox* m_player_lbox;
- wxTextCtrl* m_chat_text;
- wxTextCtrl* m_chat_msg_text;
- 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;
+ void OnCopyIP(wxCommandEvent&);
+ void OnChoice(wxCommandEvent& event);
+ void UpdateHostLabel();
+
+ wxListBox* m_player_lbox;
+ wxTextCtrl* m_chat_text;
+ wxTextCtrl* m_chat_msg_text;
+ 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;