@@ -18,6 +18,7 @@
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/UPnP.h"
#include "Core/ConfigManager.h"
#include "Core/HW/Sram.h"
#include "Core/NetPlayClient.h" //for NetPlayUI
@@ -57,16 +58,13 @@ NetPlayServer::~NetPlayServer()
}

#ifdef USE_UPNP
if (m_upnp_thread.joinable())
m_upnp_thread.join();
m_upnp_thread = std::thread(&NetPlayServer::unmapPortThread);
m_upnp_thread.join();
UPnP::StopPortmapping();
#endif
}

// called from ---GUI--- thread
NetPlayServer::NetPlayServer(const u16 port, bool traversal, const std::string& centralServer,
u16 centralPort)
NetPlayServer::NetPlayServer(const u16 port, const bool forward_port,
const NetTraversalConfig& traversal_config)
{
//--use server time
if (enet_initialize() != 0)
@@ -77,9 +75,10 @@ NetPlayServer::NetPlayServer(const u16 port, bool traversal, const std::string&
m_pad_map.fill(-1);
m_wiimote_map.fill(-1);

if (traversal)
if (traversal_config.use_traversal)
{
if (!EnsureTraversalClient(centralServer, centralPort, port))
if (!EnsureTraversalClient(traversal_config.traversal_host, traversal_config.traversal_port,
port))
return;

g_TraversalClient->m_Client = this;
@@ -105,6 +104,11 @@ NetPlayServer::NetPlayServer(const u16 port, bool traversal, const std::string&
m_do_loop = true;
m_thread = std::thread(&NetPlayServer::ThreadFunc, this);
m_target_buffer_size = 5;

#ifdef USE_UPNP
if (forward_port)
UPnP::TryPortmapping(port);
#endif
}
}

@@ -933,155 +937,3 @@ std::vector<std::pair<std::string, std::string>> NetPlayServer::GetInterfaceList
result.emplace_back(std::make_pair("!local!", "127.0.0.1"));
return result;
}

#ifdef USE_UPNP
#include <miniupnpc.h>
#include <miniwget.h>
#include <upnpcommands.h>

struct UPNPUrls NetPlayServer::m_upnp_urls;
struct IGDdatas NetPlayServer::m_upnp_data;
std::string NetPlayServer::m_upnp_ourip;
u16 NetPlayServer::m_upnp_mapped = 0;
std::thread NetPlayServer::m_upnp_thread;

// called from ---GUI--- thread
void NetPlayServer::TryPortmapping(u16 port)
{
if (m_upnp_thread.joinable())
m_upnp_thread.join();
m_upnp_thread = std::thread(&NetPlayServer::mapPortThread, port);
}

// UPnP thread: try to map a port
void NetPlayServer::mapPortThread(const u16 port)
{
if (initUPnP() && UPnPMapPort(m_upnp_ourip, port))
{
NOTICE_LOG(NETPLAY, "Successfully mapped port %d to %s.", port, m_upnp_ourip.c_str());
return;
}

WARN_LOG(NETPLAY, "Failed to map port %d to %s.", port, m_upnp_ourip.c_str());
}

// UPnP thread: try to unmap a port
void NetPlayServer::unmapPortThread()
{
if (m_upnp_mapped > 0)
UPnPUnmapPort(m_upnp_mapped);
}

// called from ---UPnP--- thread
// discovers the IGD
bool NetPlayServer::initUPnP()
{
static bool s_inited = false;
static bool s_error = false;

std::vector<UPNPDev*> igds;
int descXMLsize = 0, upnperror = 0;
char cIP[20];

// Don't init if already inited
if (s_inited)
return true;

// Don't init if it failed before
if (s_error)
return false;

memset(&m_upnp_urls, 0, sizeof(UPNPUrls));
memset(&m_upnp_data, 0, sizeof(IGDdatas));

// Find all UPnP devices
std::unique_ptr<UPNPDev, decltype(&freeUPNPDevlist)> devlist(nullptr, freeUPNPDevlist);
#if MINIUPNPC_API_VERSION >= 14
devlist.reset(upnpDiscover(2000, nullptr, nullptr, 0, 0, 2, &upnperror));
#else
devlist.reset(upnpDiscover(2000, nullptr, nullptr, 0, 0, &upnperror));
#endif
if (!devlist)
{
WARN_LOG(NETPLAY, "An error occurred trying to discover UPnP devices.");

s_error = true;

return false;
}

// Look for the IGD
for (UPNPDev* dev = devlist.get(); dev; dev = dev->pNext)
{
if (strstr(dev->st, "InternetGatewayDevice"))
igds.push_back(dev);
}

for (const UPNPDev* dev : igds)
{
std::unique_ptr<char, decltype(&std::free)> descXML(nullptr, std::free);
int statusCode = 200;
#if MINIUPNPC_API_VERSION >= 16
descXML.reset(static_cast<char*>(
miniwget_getaddr(dev->descURL, &descXMLsize, cIP, sizeof(cIP), 0, &statusCode)));
#else
descXML.reset(
static_cast<char*>(miniwget_getaddr(dev->descURL, &descXMLsize, cIP, sizeof(cIP), 0)));
#endif
if (descXML && statusCode == 200)
{
parserootdesc(descXML.get(), descXMLsize, &m_upnp_data);
GetUPNPUrls(&m_upnp_urls, &m_upnp_data, dev->descURL, 0);

m_upnp_ourip = cIP;

NOTICE_LOG(NETPLAY, "Got info from IGD at %s.", dev->descURL);
break;
}
else
{
WARN_LOG(NETPLAY, "Error getting info from IGD at %s.", dev->descURL);
}
}

s_inited = true;
return true;
}

// called from ---UPnP--- thread
// Attempt to portforward!
bool NetPlayServer::UPnPMapPort(const std::string& addr, const u16 port)
{
if (m_upnp_mapped > 0)
UPnPUnmapPort(m_upnp_mapped);

std::string port_str = StringFromFormat("%d", port);
int result = UPNP_AddPortMapping(
m_upnp_urls.controlURL, m_upnp_data.first.servicetype, port_str.c_str(), port_str.c_str(),
addr.c_str(), (std::string("dolphin-emu UDP on ") + addr).c_str(), "UDP", nullptr, nullptr);

if (result != 0)
return false;

m_upnp_mapped = port;

return true;
}

// called from ---UPnP--- thread
// Attempt to stop portforwarding.
// --
// NOTE: It is important that this happens! A few very crappy routers
// apparently do not delete UPnP mappings on their own, so if you leave them
// hanging, the NVRAM will fill with portmappings, and eventually all UPnP
// requests will fail silently, with the only recourse being a factory reset.
// --
bool NetPlayServer::UPnPUnmapPort(const u16 port)
{
std::string port_str = StringFromFormat("%d", port);
UPNP_DeletePortMapping(m_upnp_urls.controlURL, m_upnp_data.first.servicetype, port_str.c_str(),
"UDP", nullptr);

return true;
}
#endif
@@ -28,7 +28,7 @@ class NetPlayServer : public TraversalClientClient
void ThreadFunc();
void SendAsyncToClients(sf::Packet&& packet);

NetPlayServer(const u16 port, bool traversal, const std::string& centralServer, u16 centralPort);
NetPlayServer(u16 port, bool forward_port, const NetTraversalConfig& traversal_config);
~NetPlayServer();

bool ChangeGame(const std::string& game);
@@ -58,10 +58,6 @@ class NetPlayServer : public TraversalClientClient

bool is_connected = false;

#ifdef USE_UPNP
void TryPortmapping(u16 port);
#endif

private:
class Client
{
@@ -123,19 +119,4 @@ class NetPlayServer : public TraversalClientClient
ENetHost* m_server = nullptr;
TraversalClient* m_traversal_client = nullptr;
NetPlayUI* m_dialog = nullptr;

#ifdef USE_UPNP
static void mapPortThread(const u16 port);
static void unmapPortThread();

static bool initUPnP();
static bool UPnPMapPort(const std::string& addr, const u16 port);
static bool UPnPUnmapPort(const u16 port);

static struct UPNPUrls m_upnp_urls;
static struct IGDdatas m_upnp_data;
static std::string m_upnp_ourip;
static u16 m_upnp_mapped;
static std::thread m_upnp_thread;
#endif
};
@@ -663,9 +663,8 @@ bool MainWindow::NetPlayJoin()
}

// Settings
std::string host_ip, traversal_host, nickname;
int host_port, traversal_port;
bool is_traversal;
std::string host_ip;
u16 host_port;
if (Settings::Instance().GetNetPlayServer() != nullptr)
{
host_ip = "127.0.0.1";
@@ -677,18 +676,18 @@ bool MainWindow::NetPlayJoin()
host_port = Config::Get(Config::NETPLAY_HOST_PORT);
}

std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
is_traversal = traversal_choice == "traversal";
const std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
const bool is_traversal = traversal_choice == "traversal";

traversal_host = Config::Get(Config::NETPLAY_TRAVERSAL_SERVER);
traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT);
nickname = Config::Get(Config::NETPLAY_NICKNAME);
const std::string traversal_host = Config::Get(Config::NETPLAY_TRAVERSAL_SERVER);
const u16 traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT);
const std::string nickname = Config::Get(Config::NETPLAY_NICKNAME);

// Create Client
Settings::Instance().ResetNetPlayClient(
new NetPlayClient(host_ip, host_port, m_netplay_dialog, nickname,
Settings::Instance().GetNetPlayServer() != nullptr ? false : is_traversal,
traversal_host, traversal_port));
Settings::Instance().ResetNetPlayClient(new NetPlayClient(
host_ip, host_port, m_netplay_dialog, nickname,
NetTraversalConfig{Settings::Instance().GetNetPlayServer() != nullptr ? false : is_traversal,
traversal_host, traversal_port}));

if (!Settings::Instance().GetNetPlayClient()->IsConnected())
{
@@ -721,26 +720,21 @@ bool MainWindow::NetPlayHost(const QString& game_id)
}

// Settings
std::string traversal_host, nickname;
int host_port, traversal_port;
bool is_traversal, use_upnp;
u16 host_port = Config::Get(Config::NETPLAY_HOST_PORT);
const std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
const bool is_traversal = traversal_choice == "traversal";
const bool use_upnp = Config::Get(Config::NETPLAY_USE_UPNP);

host_port = Config::Get(Config::NETPLAY_HOST_PORT);
std::string traversal_choice;
traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
is_traversal = traversal_choice == "traversal";
use_upnp = Config::Get(Config::NETPLAY_USE_UPNP);

traversal_host = Config::Get(Config::NETPLAY_TRAVERSAL_SERVER);
traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT);
nickname = Config::Get(Config::NETPLAY_NICKNAME);
const std::string traversal_host = Config::Get(Config::NETPLAY_TRAVERSAL_SERVER);
const u16 traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT);
const std::string nickname = Config::Get(Config::NETPLAY_NICKNAME);

if (is_traversal)
host_port = Config::Get(Config::NETPLAY_LISTEN_PORT);

// Create Server
Settings::Instance().ResetNetPlayServer(
new NetPlayServer(host_port, is_traversal, traversal_host, traversal_port));
Settings::Instance().ResetNetPlayServer(new NetPlayServer(
host_port, use_upnp, NetTraversalConfig{is_traversal, traversal_host, traversal_port}));

if (!Settings::Instance().GetNetPlayServer()->is_connected)
{
@@ -754,11 +748,6 @@ bool MainWindow::NetPlayHost(const QString& game_id)

Settings::Instance().GetNetPlayServer()->ChangeGame(game_id.toStdString());

#ifdef USE_UPNP
if (use_upnp)
Settings::Instance().GetNetPlayServer()->TryPortmapping(host_port);
#endif

// Join our local server
return NetPlayJoin();
}
@@ -23,8 +23,9 @@ bool NetPlayLauncher::Host(const NetPlayHostConfig& config)
return false;
}

netplay_server = new NetPlayServer(config.listen_port, config.use_traversal,
config.traversal_host, config.traversal_port);
netplay_server = new NetPlayServer(
config.listen_port, config.forward_port,
NetTraversalConfig{config.use_traversal, config.traversal_host, config.traversal_port});

if (!netplay_server->is_connected)
{
@@ -35,19 +36,11 @@ bool NetPlayLauncher::Host(const NetPlayHostConfig& config)

netplay_server->ChangeGame(config.game_name);

#ifdef USE_UPNP
if (config.forward_port)
{
netplay_server->TryPortmapping(config.listen_port);
}
#endif

npd = new NetPlayDialog(config.parent_window, config.game_list_ctrl, config.game_name, true);

NetPlayClient*& netplay_client = NetPlayDialog::GetNetPlayClient();
netplay_client =
new NetPlayClient("127.0.0.1", netplay_server->GetPort(), npd, config.player_name, false,
config.traversal_host, config.traversal_port);
netplay_client = new NetPlayClient("127.0.0.1", netplay_server->GetPort(), npd,
config.player_name, NetTraversalConfig{});

if (netplay_client->IsConnected())
{
@@ -76,9 +69,9 @@ bool NetPlayLauncher::Join(const NetPlayJoinConfig& config)
else
host = config.connect_host;

netplay_client =
new NetPlayClient(host, config.connect_port, npd, config.player_name, config.use_traversal,
config.traversal_host, config.traversal_port);
netplay_client = new NetPlayClient(
host, config.connect_port, npd, config.player_name,
NetTraversalConfig{config.use_traversal, config.traversal_host, config.traversal_port});
if (netplay_client->IsConnected())
{
npd->SetSize(config.window_pos);
@@ -34,9 +34,7 @@ class NetPlayHostConfig : public NetPlayLaunchConfig

std::string game_name;
u16 listen_port = 0;
#ifdef USE_UPNP
bool forward_port;
#endif
bool forward_port = false;
};

class NetPlayJoinConfig : public NetPlayLaunchConfig