Index: source/lobby/IXmppClient.h =================================================================== --- source/lobby/IXmppClient.h +++ source/lobby/IXmppClient.h @@ -36,7 +36,7 @@ virtual void SendIqGetProfile(const std::string& player) = 0; virtual void SendIqGameReport(const ScriptRequest& rq, JS::HandleValue data) = 0; virtual void SendIqRegisterGame(const ScriptRequest& rq, JS::HandleValue data) = 0; - virtual void SendIqGetConnectionData(const std::string& jid, const std::string& password, bool localIP) = 0; + virtual void SendIqGetConnectionData(const std::string& jid, const std::string& password, bool localIP, const std::string& myIP = "", u16 myPort = 0) = 0; virtual void SendIqUnregisterGame() = 0; virtual void SendIqChangeStateGame(const std::string& nbp, const std::string& players) = 0; virtual void SendIqLobbyAuth(const std::string& to, const std::string& token) = 0; Index: source/lobby/XmppClient.h =================================================================== --- source/lobby/XmppClient.h +++ source/lobby/XmppClient.h @@ -86,7 +86,7 @@ void SendIqGetProfile(const std::string& player); void SendIqGameReport(const ScriptRequest& rq, JS::HandleValue data); void SendIqRegisterGame(const ScriptRequest& rq, JS::HandleValue data); - void SendIqGetConnectionData(const std::string& jid, const std::string& password, bool localIP); + void SendIqGetConnectionData(const std::string& jid, const std::string& password, bool localIP, const std::string& myIP, u16 myPort); void SendIqUnregisterGame(); void SendIqChangeStateGame(const std::string& nbp, const std::string& players); void SendIqLobbyAuth(const std::string& to, const std::string& token); Index: source/lobby/XmppClient.cpp =================================================================== --- source/lobby/XmppClient.cpp +++ source/lobby/XmppClient.cpp @@ -365,13 +365,15 @@ /** * Request the Connection data (ip, port...) from the server. */ -void XmppClient::SendIqGetConnectionData(const std::string& jid, const std::string& password, bool localIP) +void XmppClient::SendIqGetConnectionData(const std::string& jid, const std::string& password, bool localIP, const std::string& myIP, u16 myPort) { glooxwrapper::JID targetJID(jid); ConnectionData* connectionData = new ConnectionData(); connectionData->m_Password = password; connectionData->m_IsLocalIP = localIP ? "1" : "0"; + connectionData->m_Ip = myIP; + connectionData->m_Port = std::to_string(myPort); glooxwrapper::IQ iq(gloox::IQ::Get, targetJID, m_client->getID()); iq.addExtension(connectionData); m_connectionDataJid = iq.from().full(); @@ -986,10 +988,18 @@ return true; } + // If the client forwarded its IP, attempt hole-punching. + CStr clientIP = cd->m_Ip.to_string(); + if (!clientIP.empty()) + { + LOGWARNING("punching hole to %s", clientIP); + g_NetServer->SendHolePunchingMessage(clientIP, std::strtol(cd->m_Port.to_string().c_str(), nullptr, 10)); + } + glooxwrapper::IQ response(gloox::IQ::Result, iq.from(), iq.id()); ConnectionData* connectionData = new ConnectionData(); - if (cd->m_IsLocalIP.to_string() == "0") + if (cd->m_IsLocalIP.to_string() == "0" && clientIP != g_NetServer->GetPublicIp()) { connectionData->m_Ip = g_NetServer->GetPublicIp(); connectionData->m_Port = std::to_string(g_NetServer->GetPublicPort()); Index: source/network/NetClient.h =================================================================== --- source/network/NetClient.h +++ source/network/NetClient.h @@ -125,6 +125,11 @@ */ bool SetupConnection(ENetHost* enetClient); + /** + * Send a request for server connection data via the lobby. + */ + bool SetupConnectionViaLobby(const CStr& hostJID, bool localIP); + /** * Connect to the remote networked server using lobby. * Push netstatus messages on failure. Index: source/network/NetClient.cpp =================================================================== --- source/network/NetClient.cpp +++ source/network/NetClient.cpp @@ -195,22 +195,53 @@ bool CNetClient::SetupConnection(ENetHost* enetClient) { - CNetClientSession* session = new CNetClientSession(*this); - bool ok = session->Connect(m_ServerAddress, m_ServerPort, enetClient); - SetAndOwnSession(session); + bool ok = false; + if (m_Session) + { + ok = m_Session->Connect(m_ServerAddress, m_ServerPort, nullptr); + } + else + { + CNetClientSession* session = new CNetClientSession(*this); + ok = session->Connect(m_ServerAddress, m_ServerPort, enetClient); + SetAndOwnSession(session); + } m_PollingThread = std::thread(Threading::HandleExceptions::Wrapper, m_Session); return ok; } void CNetClient::SetupServerData(CStr address, u16 port, bool stun) { - ENSURE(!m_Session); - m_ServerAddress = address; m_ServerPort = port; m_UseSTUN = stun; } +bool CNetClient::SetupConnectionViaLobby(const CStr& hostJID, bool localIP) +{ + if (!g_XmppClient) + return false; + m_UseSTUN = true; + m_HostJID = hostJID; + + ENetAddress hostAddr{ ENET_HOST_ANY, ENET_PORT_ANY }; + ENetHost* enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0); + CStr ip; + u16 port; + if (!StunClient::FindPublicIP(*enetClient, ip, port)) + { + enet_host_destroy(enetClient); + return false; + } + + CNetClientSession* session = new CNetClientSession(*this, enetClient); + SetAndOwnSession(session); + + LOGWARNING("NetClient: found external address at %s:%i", ip.c_str(), port); + g_XmppClient->SendIqGetConnectionData(m_HostJID, m_Password, localIP, ip, port); + return true; +} + void CNetClient::HandleGetServerDataFailed(const CStr& error) { if (m_Session) @@ -225,9 +256,6 @@ bool CNetClient::TryToConnect(const CStr& hostJID) { - if (m_Session) - return false; - if (m_ServerAddress.empty()) { PushGuiMessage( @@ -238,7 +266,7 @@ } ENetHost* enetClient = nullptr; - if (g_XmppClient && m_UseSTUN) + if (false && g_XmppClient && m_UseSTUN) { ENetAddress hostAddr{ ENET_HOST_ANY, ENET_PORT_ANY }; enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0); @@ -276,10 +304,10 @@ g_XmppClient->SendStunEndpointToHost(ip, port, hostJID); SDL_Delay(1000); - - StunClient::SendHolePunchingMessages(*enetClient, m_ServerAddress, m_ServerPort); } + StunClient::SendHolePunchingMessages(*enetClient, m_ServerAddress, m_ServerPort); + if (!g_NetClient->SetupConnection(enetClient)) { PushGuiMessage( Index: source/network/NetSession.h =================================================================== --- source/network/NetSession.h +++ source/network/NetSession.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -70,6 +70,7 @@ public: CNetClientSession(CNetClient& client); + CNetClientSession(CNetClient& client, ENetHost* enetClient); ~CNetClientSession(); bool Connect(const CStr& server, const u16 port, ENetHost* enetClient); Index: source/network/NetSession.cpp =================================================================== --- source/network/NetSession.cpp +++ source/network/NetSession.cpp @@ -37,6 +37,11 @@ { } +CNetClientSession::CNetClientSession(CNetClient& client, ENetHost* enetClient) : CNetClientSession(client) +{ + m_Host = enetClient; +} + CNetClientSession::~CNetClientSession() { ENSURE(!m_LoopRunning); @@ -57,18 +62,26 @@ bool CNetClientSession::Connect(const CStr& server, const u16 port, ENetHost* enetClient) { ENSURE(!m_LoopRunning); - ENSURE(!m_Host); ENSURE(!m_Server); - // Create ENet host - ENetHost* host; - if (enetClient != nullptr) - host = enetClient; + if (m_Host) + { + // Cannot pass a client if we already have a host: code logic error. + ENSURE(!enetClient); + } else - host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0); + { + // Create ENet host + ENetHost* host; + if (enetClient != nullptr) + host = enetClient; + else + host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0); - if (!host) - return false; + if (!host) + return false; + m_Host = host; + } // Bind to specified host ENetAddress addr; @@ -77,11 +90,10 @@ return false; // Initiate connection to server - ENetPeer* peer = enet_host_connect(host, &addr, CHANNEL_COUNT, 0); + ENetPeer* peer = enet_host_connect(m_Host, &addr, CHANNEL_COUNT, 0); if (!peer) return false; - m_Host = host; m_Server = peer; m_Stats = new CNetStatsTable(m_Server); Index: source/network/scripting/JSInterface_Network.cpp =================================================================== --- source/network/scripting/JSInterface_Network.cpp +++ source/network/scripting/JSInterface_Network.cpp @@ -186,7 +186,7 @@ g_NetClient->SetUserName(playerName); g_NetClient->SetHostJID(hostJID); g_NetClient->SetGamePassword(hashedPass); - g_XmppClient->SendIqGetConnectionData(hostJID, hashedPass.c_str(), false); + g_NetClient->SetupConnectionViaLobby(hostJID, false); } void DisconnectNetworkGame()