Index: binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js =================================================================== --- binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js +++ binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js @@ -38,7 +38,7 @@ { if (Engine.HasXmppClient()) { - if (startJoin(attribs.name, attribs.ip, getValidPort(attribs.port), attribs.useSTUN, attribs.hostJID)) + if (startJoinFromLobby(attribs.name, getValidPort(attribs.port), attribs.useSTUN, attribs.hostJID)) switchSetupPage("pageConnecting"); } else @@ -232,8 +232,7 @@ } Engine.SwitchGuiPage("page_gamesetup.xml", { "serverName": g_ServerName, - "serverPort": g_ServerPort, - "stunEndpoint": g_StunEndpoint + "serverPort": g_ServerPort }); return; // don't process any more messages - leave them for the game GUI loop @@ -301,20 +300,25 @@ return false; } - if (Engine.HasXmppClient() && Engine.GetGUIObjectByName("useSTUN").checked) + let useSTUN = Engine.HasXmppClient() && Engine.GetGUIObjectByName("useSTUN").checked; + let stunPort; + let stunIp; + if (useSTUN) { - g_StunEndpoint = Engine.FindStunEndpoint(port); - if (!g_StunEndpoint) + let stunEndpoint = Engine.FindStunEndpoint(port); + if (!stunEndpoint) { cancelSetup(); hostFeedback.caption = translate("Failed to host via STUN."); return false; } + stunIp = stunEndpoint.ip; + stunPort = stunEndpoint.port; } try { - Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, playername); + Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, playername, useSTUN, stunIp, stunPort); } catch (e) { @@ -370,6 +374,41 @@ return true; } +function startJoinFromLobby(playername, port, useSTUN, hostJID) +{ + if (!Engine.HasXmppClient()) + { + cancelSetup(); + messageBox( + 400, 200, + sprintf("You are not in lobby. Wrong function was called."), + translate("Error") + ); + return false; + } + + try + { + Engine.StartNetworkJoinLobby(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, useSTUN, hostJID); + } + catch (e) + { + cancelSetup(); + messageBox( + 400, 200, + sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }), + translate("Error") + ); + return false; + } + + startConnectionStatus("client"); + + Engine.LobbySetPlayerPresence("playing"); + + return true; +} + function getDefaultGameName() { return sprintf(translate("%(playername)s's game"), { Index: binaries/data/mods/public/gui/lobby/LobbyPage/Buttons/JoinButton.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/Buttons/JoinButton.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/Buttons/JoinButton.js @@ -67,38 +67,19 @@ if (!game) return; - let ip; let port; let stanza = game.stanza; - if (stanza.stunIP) - { - ip = stanza.stunIP; + if (stanza.useSTUN) port = stanza.stunPort; - } else - { - ip = stanza.ip; port = stanza.port; - } - - if (ip.split('.').length != 4) - { - messageBox( - 400, 250, - sprintf( - translate("This game's address '%(ip)s' does not appear to be valid."), - { "ip": escapeText(stanza.ip) }), - translate("Error")); - return; - } Engine.PushGuiPage("page_gamesetup_mp.xml", { "multiplayerGameType": "join", - "ip": ip, "port": port, "name": g_Nickname, "rating": this.getRejoinRating(stanza), - "useSTUN": !!stanza.stunIP, + "useSTUN": !!stanza.useSTUN, "hostJID": stanza.hostUsername + "@" + Engine.ConfigDB_GetValue("user", "lobby.server") + "/0ad" }); } Index: binaries/data/mods/public/gui/lobby/LobbyPage/Game.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/Game.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/Game.js @@ -270,9 +270,8 @@ */ Game.prototype.StanzaKeys = [ "name", - "ip", + "useSTUN", "port", - "stunIP", "stunPort", "hostUsername", "state", Index: binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js =================================================================== --- binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js +++ binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js @@ -97,13 +97,13 @@ let newGames = {}; for (let stanza of gameListData) { - let game = this.games[stanza.ip] || undefined; + let game = this.games[stanza.hostUsername] || undefined; let exists = !!game; if (!exists) game = new Game(this.mapCache); game.update(stanza, selectedColumn); - newGames[stanza.ip] = game; + newGames[stanza.hostUsername] = game; } this.games = newGames; Engine.ProfileStop(); @@ -157,8 +157,7 @@ this.list_maxnbp[i] = displayData.playerCount; this.list_gameRating[i] = game.gameRating; this.list[i] = ""; - - if (selectedGame && game.stanza.ip == selectedGame.stanza.ip && game.stanza.port == selectedGame.stanza.port) + if (selectedGame && game.hostUsername == selectedGame.hostUsername && game.stanza.port == selectedGame.stanza.port) selectedGameIndex = i; }); Engine.ProfileStop(); Index: source/lobby/IXmppClient.h =================================================================== --- source/lobby/IXmppClient.h +++ source/lobby/IXmppClient.h @@ -39,6 +39,7 @@ virtual void SendIqGetProfile(const std::string& player) = 0; virtual void SendIqGameReport(const ScriptInterface& scriptInterface, JS::HandleValue data) = 0; virtual void SendIqRegisterGame(const ScriptInterface& scriptInterface, JS::HandleValue data) = 0; + virtual void SendIqGetConnectionData(const std::string& jid) = 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; @@ -60,6 +61,8 @@ virtual JS::Value GuiPollHistoricMessages(const ScriptInterface& scriptInterface) = 0; virtual bool GuiPollHasPlayerListUpdate() = 0; + virtual std::string GetGameIp(const ScriptInterface& scriptInterface, const std::string& hostname, bool stun) = 0; + virtual void SendMUCMessage(const std::string& message) = 0; virtual void SendStunEndpointToHost(const StunClient::StunEndpoint& stunEndpoint, const std::string& hostJID) = 0; }; Index: source/lobby/StanzaExtensions.h =================================================================== --- source/lobby/StanzaExtensions.h +++ source/lobby/StanzaExtensions.h @@ -41,6 +41,28 @@ #define EXTLOBBYAUTH 1407 #define XMLNS_LOBBYAUTH "jabber:iq:lobbyauth" +#define EXTCONNECTIONDATA 1408 +#define XMLNS_CONNECTIONDATA "jabber:iq:connectiondata" + +class ConnectionData : public glooxwrapper::StanzaExtension +{ +public: + ConnectionData(const glooxwrapper::Tag* tag = 0); + + // Following four methods are all required by gloox + virtual StanzaExtension* newInstance(const glooxwrapper::Tag* tag) const + { + return new ConnectionData(tag); + } + virtual const glooxwrapper::string& filterString() const; + virtual glooxwrapper::Tag* tag() const; + virtual glooxwrapper::StanzaExtension* clone() const; + + glooxwrapper::string m_Ip; + glooxwrapper::string m_port; + glooxwrapper::string m_useSTUN; +}; + class GameReport : public glooxwrapper::StanzaExtension { public: Index: source/lobby/StanzaExtensions.cpp =================================================================== --- source/lobby/StanzaExtensions.cpp +++ source/lobby/StanzaExtensions.cpp @@ -283,3 +283,59 @@ { return new LobbyAuth(); } + +/****************************************************** + * ConnectionData, a custom IQ Stanza, used to send and + * receive a ip and port of the server. + */ +ConnectionData::ConnectionData(const glooxwrapper::Tag* tag) + : StanzaExtension(EXTCONNECTIONDATA) +{ + if (!tag || tag->name() != "connectiondata" || tag->xmlns() != XMLNS_CONNECTIONDATA) + return; + + const glooxwrapper::Tag* c = tag->findTag_clone("connectiondata/ip"); + if (c) + m_Ip = c->cdata(); + const glooxwrapper::Tag* p= tag->findTag_clone("connectiondata/port"); + if (p) + m_port = p->cdata(); + const glooxwrapper::Tag* s = tag->findTag_clone("connectiondata/useSTUN"); + if (s) + m_useSTUN = s->cdata(); + + glooxwrapper::Tag::free(c); + glooxwrapper::Tag::free(p); + glooxwrapper::Tag::free(s); +} + +/** + * Required by gloox, used to find the LobbyAuth element in a received IQ. + */ +const glooxwrapper::string& ConnectionData::filterString() const +{ + static const glooxwrapper::string filter = "/iq/connectiondata[@xmlns='" XMLNS_CONNECTIONDATA "']"; + return filter; +} + +/** + * Required by gloox, used to serialize the auth object into XML for sending. + */ +glooxwrapper::Tag* ConnectionData::tag() const +{ + glooxwrapper::Tag* t = glooxwrapper::Tag::allocate("connectiondata"); + t->setXmlns(XMLNS_CONNECTIONDATA); + + if (!m_Ip.empty()) + t->addChild(glooxwrapper::Tag::allocate("ip", m_Ip)); + if (!m_port.empty()) + t->addChild(glooxwrapper::Tag::allocate("port", m_port)); + if (!m_useSTUN.empty()) + t->addChild(glooxwrapper::Tag::allocate("useSTUN", m_useSTUN)); + return t; +} + +glooxwrapper::StanzaExtension* ConnectionData::clone() const +{ + return new ConnectionData(); +} Index: source/lobby/XmppClient.h =================================================================== --- source/lobby/XmppClient.h +++ source/lobby/XmppClient.h @@ -82,6 +82,7 @@ void SendIqGetProfile(const std::string& player); void SendIqGameReport(const ScriptInterface& scriptInterface, JS::HandleValue data); void SendIqRegisterGame(const ScriptInterface& scriptInterface, JS::HandleValue data); + void SendIqGetConnectionData(const std::string& jid); void SendIqUnregisterGame(); void SendIqChangeStateGame(const std::string& nbp, const std::string& players); void SendIqLobbyAuth(const std::string& to, const std::string& token); @@ -100,6 +101,8 @@ void GUIGetBoardList(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret); void GUIGetProfile(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret); + std::string GetGameIp(const ScriptInterface& scriptInterface, const std::string& hostname, bool stun); + void SendStunEndpointToHost(const StunClient::StunEndpoint& stunEndpoint, const std::string& hostJID); /** Index: source/lobby/XmppClient.cpp =================================================================== --- source/lobby/XmppClient.cpp +++ source/lobby/XmppClient.cpp @@ -28,6 +28,7 @@ #include "lib/external_libraries/enet.h" #include "lib/utf8.h" #include "network/NetServer.h" +#include "network/NetClient.h" #include "network/StunClient.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" @@ -40,7 +41,7 @@ #include //debug -#if 1 +#if 0 #define DbgXMPP(x) #else #define DbgXMPP(x) std::cout << x << std::endl; @@ -148,6 +149,9 @@ m_client->registerStanzaExtension(new LobbyAuth()); m_client->registerIqHandler(this, EXTLOBBYAUTH); + m_client->registerStanzaExtension(new ConnectionData()); + m_client->registerIqHandler(this, EXTCONNECTIONDATA); + m_client->registerMessageHandler(this); // Uncomment to see the raw stanzas @@ -361,6 +365,17 @@ m_client->send(iq); } +void XmppClient::SendIqGetConnectionData(const std::string& jid) +{ + glooxwrapper::JID targetJID(jid); + + ConnectionData* b = new ConnectionData(); + glooxwrapper::IQ iq(gloox::IQ::Get, targetJID, m_client->getID()); + iq.addExtension(b); + DbgXMPP("SendIqGetConnectionData []"); + m_client->send(iq); +} + /** * Send game report containing numerous game properties to the server. * @@ -394,6 +409,20 @@ m_client->send(iq); }; +std::string XmppClient::GetGameIp(const ScriptInterface& scriptInterface, const std::string& hostName, bool stun) +{ + ScriptRequest rq(scriptInterface); + for (const glooxwrapper::Tag* const& t : m_GameList) + { + JS::RootedValue game(rq.cx); + ScriptInterface::CreateObject(rq, &game); + glooxwrapper::string userName = t->findAttribute("hostUsername"); + if (userName.to_string().compare(hostName) == 0) + return t->findAttribute(stun ? "stunIP" : "ip").to_string(); + } + return ""; +} + /** * Send a request to register a game to the server. * @@ -573,7 +602,7 @@ ScriptInterface::CreateArray(rq, ret); int j = 0; - const char* stats[] = { "name", "ip", "port", "stunIP", "stunPort", "hostUsername", "state", + const char* stats[] = { "name", "port", "stunPort", "hostUsername", "state", "nbp", "maxnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", "victoryConditions", "startTime", "mods" }; @@ -585,6 +614,7 @@ for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) scriptInterface.SetProperty(game, stats[i], t->findAttribute(stats[i])); + scriptInterface.SetProperty(game, "useSTUN", !t->findAttribute("stunIP").empty()); scriptInterface.SetPropertyInt(ret, j++, game); } } @@ -805,12 +835,36 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq) { DbgXMPP("handleIq [" << tag_xml(iq) << "]"); + LOGWARNING("handleIq[%s]", tag_xml(iq)); if (iq.subtype() == gloox::IQ::Result) { const GameListQuery* gq = iq.findExtension(EXTGAMELISTQUERY); const BoardListQuery* bq = iq.findExtension(EXTBOARDLISTQUERY); const ProfileQuery* pq = iq.findExtension(EXTPROFILEQUERY); + const ConnectionData* cd = iq.findExtension(EXTCONNECTIONDATA); + if (cd) + { + LOGMESSAGE("XmppClient: Received connection data from %s", iq.from().username()); + + if (cd->m_Ip.empty()) + { + LOGMESSAGE("Refused connection from %s" + iq.from().username()); + g_NetClient->SetupServerData("", 0, false); + g_NetClient->TryToConnect(iq.from().full()); + SAFE_DELETE(g_NetClient); + return true; + } + + g_NetClient->SetupServerData(cd->m_Ip.to_string(), stoi(cd->m_port.to_string()), !cd->m_useSTUN.empty()); + + if (!g_NetClient->TryToConnect(iq.from().full())) + { + LOGMESSAGE("Failed to connect to %s", iq.from().username()); + SAFE_DELETE(g_NetClient); + } + + } if (gq) { for (const glooxwrapper::Tag* const& t : m_GameList) @@ -877,6 +931,44 @@ LOGERROR("Received lobby authentication request, but not hosting currently!"); } } + else if (iq.subtype() == gloox::IQ::Get) + { + const ConnectionData* cd = iq.findExtension(EXTCONNECTIONDATA); + if (cd) + { + LOGMESSAGE("XmppClient: Recieved request for connection data from %s", iq.from().username()); + if (!g_NetServer) + return true; + + bool useSTUN = g_NetServer->GetUseSTUN(); + u16 port = useSTUN ? g_NetServer->GetSTUNport() : g_NetServer->GetPort(); + std::string ip = "127.0.0.1"; + + if (useSTUN) + { + ip = g_NetServer->GetSTUNip(); + } + else + // Get public ip + { + if (!StunClient::FindStunEndpoint(ip, port)) + ip = ""; + + LOGWARNING("Found public ip and port: %s %u", ip, port); + } + + glooxwrapper::IQ response(gloox::IQ::Result, iq.from(), iq.id()); + ConnectionData* connectionData = new ConnectionData(); + connectionData->m_Ip = ip; + connectionData->m_port = std::to_string(port); + connectionData->m_useSTUN = useSTUN ? "true" : ""; + + response.addExtension(connectionData); + + m_client->send(response); + } + + } else if (iq.subtype() == gloox::IQ::Error) CreateGUIMessage("system", "error", std::time(nullptr), "text", iq.error_error()); else Index: source/network/NetClient.h =================================================================== --- source/network/NetClient.h +++ source/network/NetClient.h @@ -108,7 +108,11 @@ * @param server IP address or host name to connect to * @return true on success, false on connection failure */ - bool SetupConnection(const CStr& server, const u16 port, ENetHost* enetClient); + bool SetupConnection(ENetHost* enetClient); + + void SetupServerData(CStr address, u16 port, bool stun); + + bool TryToConnect(const CStr& hostJID); /** * Destroy the connection to the server. @@ -271,6 +275,9 @@ CGame *m_Game; CStrW m_UserName; CStr m_HostingPlayerName; + CStr m_ServerAddress; + u16 m_ServerPort; + bool m_UseSTUN; /// Current network session (or NULL if not connected) CNetClientSession* m_Session; Index: source/network/NetClient.cpp =================================================================== --- source/network/NetClient.cpp +++ source/network/NetClient.cpp @@ -35,6 +35,7 @@ #include "ps/Loader.h" #include "scriptinterface/ScriptInterface.h" #include "simulation2/Simulation2.h" +#include "network/StunClient.h" CNetClient *g_NetClient = NULL; @@ -74,6 +75,8 @@ m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetGeneralJSContext()), m_IsLocalClient(isLocalClient), m_LastConnectionCheck(0), + m_ServerAddress(""), + m_ServerPort(0), m_Rejoin(false) { m_Game->SetTurnManager(NULL); // delete the old local turn manager so we don't accidentally use it @@ -165,14 +168,80 @@ m_HostingPlayerName = hostingPlayerName; } -bool CNetClient::SetupConnection(const CStr& server, const u16 port, ENetHost* enetClient) +bool CNetClient::SetupConnection(ENetHost* enetClient) { CNetClientSession* session = new CNetClientSession(*this); - bool ok = session->Connect(server, port, m_IsLocalClient, enetClient); + bool ok = session->Connect(m_ServerAddress, m_ServerPort, m_IsLocalClient, enetClient); SetAndOwnSession(session); return ok; } +void CNetClient::SetupServerData(CStr address, u16 port, bool stun) +{ + ENSURE(!m_Session); // must be called before we start the connection + + m_ServerAddress = address; + m_ServerPort = port; + m_UseSTUN = stun; +} + +bool CNetClient::TryToConnect(const CStr& hostJID) +{ + if (m_ServerAddress.empty()) + { + SAFE_DELETE(g_Game); + return false; + } + LOGWARNING("CNetClient: TryToConnect to %s", hostJID); + ENetHost* enetClient = nullptr; + if (g_XmppClient && m_UseSTUN) + { + // Find an unused port + for (int i = 0; i < 5 && !enetClient; ++i) + { + // Ports below 1024 are privileged on unix + u16 port = 1024 + rand() % (UINT16_MAX - 1024); + LOGWARNING("CNetClient: Stun, trying port %u", port); + ENetAddress hostAddr{ ENET_HOST_ANY, port }; + enetClient = enet_host_create(&hostAddr, 1, 1, 0, 0); + ++hostAddr.port; + } + + if (!enetClient) + { + SAFE_DELETE(g_Game); + LOGWARNING("CNetClient: Could not find an unused port for the enet STUN client"); + // Could not find an unused port for the enet STUN client + return false; + } + + StunClient::StunEndpoint stunEndpoint; + if (!StunClient::FindStunEndpointJoin(*enetClient, stunEndpoint)) + { + SAFE_DELETE(g_Game); + // Could not find the STUN endpoint + LOGWARNING("CNetClient: Could not find the STUN endpoint"); + return false; + } + + LOGWARNING("CNetClient: Send Stun to host"); + g_XmppClient->SendStunEndpointToHost(stunEndpoint, hostJID); + + LOGWARNING("CNetClient: Punch hole to %s", m_ServerAddress); + StunClient::SendHolePunchingMessages(*enetClient, m_ServerAddress, m_ServerPort); + } + + LOGWARNING("CNetClient: Setup connection to %s", m_ServerAddress); + if (!g_NetClient->SetupConnection(enetClient)) + { + SAFE_DELETE(g_Game); + LOGWARNING("CNetClient: Failed to connect to server"); + return false; + } + + return true; +} + void CNetClient::SetAndOwnSession(CNetClientSession* session) { delete m_Session; Index: source/network/NetServer.h =================================================================== --- source/network/NetServer.h +++ source/network/NetServer.h @@ -147,9 +147,25 @@ void SendHolePunchingMessage(const CStr& ip, u16 port); + void SetUseSTUN(bool); + + bool GetUseSTUN() const; + + void SetSTUN(const CStr& ip, u16 port); + + CStr GetSTUNip() const; + + u16 GetSTUNport() const; + + u16 GetPort() const; + private: CNetServerWorker* m_Worker; const bool m_LobbyAuth; + bool m_UseSTUN; + u16 m_Port; + u16 m_PortSTUN; + CStr m_IpSTUN; }; /** Index: source/network/NetServer.cpp =================================================================== --- source/network/NetServer.cpp +++ source/network/NetServer.cpp @@ -1581,7 +1581,7 @@ CNetServer::CNetServer(bool useLobbyAuth, int autostartPlayers) : m_Worker(new CNetServerWorker(useLobbyAuth, autostartPlayers)), - m_LobbyAuth(useLobbyAuth) + m_LobbyAuth(useLobbyAuth), m_UseSTUN(false), m_Port(256) { } @@ -1590,6 +1590,16 @@ delete m_Worker; } +bool CNetServer::GetUseSTUN() const +{ + return m_UseSTUN; +} + +void CNetServer::SetUseSTUN(bool useSTUN) +{ + m_UseSTUN = useSTUN; +} + bool CNetServer::UseLobbyAuth() const { return m_LobbyAuth; @@ -1597,9 +1607,31 @@ bool CNetServer::SetupConnection(const u16 port) { + m_Port = port; return m_Worker->SetupConnection(port); } +u16 CNetServer::GetPort() const +{ + return m_Port; +} + +u16 CNetServer::GetSTUNport() const +{ + return m_PortSTUN; +} + +CStr CNetServer::GetSTUNip() const +{ + return m_IpSTUN; +} + +void CNetServer::SetSTUN(const CStr& ip, const u16 port) +{ + m_IpSTUN = ip; + m_PortSTUN = port; +} + void CNetServer::StartGame() { std::lock_guard lock(m_Worker->m_WorkerMutex); Index: source/network/StunClient.h =================================================================== --- source/network/StunClient.h +++ source/network/StunClient.h @@ -40,7 +40,7 @@ bool FindStunEndpointJoin(ENetHost& transactionHost, StunClient::StunEndpoint& stunEndpoint); void SendHolePunchingMessages(ENetHost& enetClient, const std::string& serverAddress, u16 serverPort); - +bool FindStunEndpoint(std::string& ip, u16& port); } #endif // STUNCLIENT_H Index: source/network/StunClient.cpp =================================================================== --- source/network/StunClient.cpp +++ source/network/StunClient.cpp @@ -369,6 +369,32 @@ ParseStunResponse(buffer); } +bool StunClient::FindStunEndpoint(std::string& ip, u16& port) +{ + ENetAddress hostAddr{ ENET_HOST_ANY, static_cast(port) }; + ENetHost* transactionHost = enet_host_create(&hostAddr, 1, 1, 0, 0); + if (!transactionHost) + { + LOGERROR("FindStunEndpointHost: Failed to create enet host"); + return false; + } + + bool success = STUNRequestAndResponse(*transactionHost); + enet_host_destroy(transactionHost); + if (!success) + return false; + + // Convert m_IP to string + char ipStr[256] = "(error)"; + ENetAddress addr; + addr.host = ntohl(m_IP); + enet_address_get_host_ip(&addr, ipStr, ARRAY_SIZE(ipStr)); + + ip = ipStr; + port = m_Port; + return true; +} + JS::Value StunClient::FindStunEndpointHost(const ScriptInterface& scriptInterface, int port) { ENetAddress hostAddr{ENET_HOST_ANY, static_cast(port)}; Index: source/network/scripting/JSInterface_Network.h =================================================================== --- source/network/scripting/JSInterface_Network.h +++ source/network/scripting/JSInterface_Network.h @@ -29,8 +29,9 @@ bool HasNetClient(ScriptInterface::CmptPrivate* pCmptPrivate); void StartNetworkGame(ScriptInterface::CmptPrivate* pCmptPrivate); void SetNetworkGameAttributes(ScriptInterface::CmptPrivate* pCmptPrivate, JS::HandleValue attribs1); - void StartNetworkHost(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, const u16 serverPort, const CStr& hostLobbyName); + void StartNetworkHost(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, const u16 serverPort, const CStr& hostLobbyName, bool useSTUN, const CStr& stunIp, const u16 stunPort); void StartNetworkJoin(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, const CStr& serverAddress, u16 serverPort, bool useSTUN, const CStr& hostJID); + void StartNetworkJoinLobby(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, u16 serverPort, bool useSTUN, const CStr& hostJID); JS::Value FindStunEndpoint(ScriptInterface::CmptPrivate* pCmptPrivate, int port); void DisconnectNetworkGame(ScriptInterface::CmptPrivate* pCmptPrivate); JS::Value PollNetworkClient(ScriptInterface::CmptPrivate* pCmptPrivate); Index: source/network/scripting/JSInterface_Network.cpp =================================================================== --- source/network/scripting/JSInterface_Network.cpp +++ source/network/scripting/JSInterface_Network.cpp @@ -51,7 +51,7 @@ return StunClient::FindStunEndpointHost(*(pCmptPrivate->pScriptInterface), port); } -void JSI_Network::StartNetworkHost(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, const u16 serverPort, const CStr& hostLobbyName) +void JSI_Network::StartNetworkHost(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, const u16 serverPort, const CStr& hostLobbyName, bool useSTUN,const CStr& stunIp, const u16 stunPort) { ENSURE(!g_NetClient); ENSURE(!g_NetServer); @@ -66,13 +66,16 @@ SAFE_DELETE(g_NetServer); return; } + g_NetServer->SetUseSTUN(useSTUN); + g_NetServer->SetSTUN(stunIp, stunPort); g_Game = new CGame(true); g_NetClient = new CNetClient(g_Game, true); g_NetClient->SetUserName(playerName); g_NetClient->SetHostingPlayerName(hostLobbyName); + g_NetClient->SetupServerData("127.0.0.1", serverPort, false); - if (!g_NetClient->SetupConnection("127.0.0.1", serverPort, nullptr)) + if (!g_NetClient->SetupConnection(nullptr)) { ScriptRequest rq(pCmptPrivate->pScriptInterface); ScriptException::Raise(rq, "Failed to connect to server"); @@ -124,11 +127,12 @@ g_NetClient = new CNetClient(g_Game, false); g_NetClient->SetUserName(playerName); g_NetClient->SetHostingPlayerName(hostJID.substr(0, hostJID.find("@"))); + g_NetClient->SetupServerData(serverAddress, serverPort, useSTUN); if (g_XmppClient && useSTUN) StunClient::SendHolePunchingMessages(*enetClient, serverAddress, serverPort); - if (!g_NetClient->SetupConnection(serverAddress, serverPort, enetClient)) + if (!g_NetClient->SetupConnection(enetClient)) { ScriptRequest rq(pCmptPrivate->pScriptInterface); ScriptException::Raise(rq, "Failed to connect to server"); @@ -137,6 +141,31 @@ } } +/*** +* Require XmppClient to communicate with game server. +*/ +void JSI_Network::StartNetworkJoinLobby(ScriptInterface::CmptPrivate* pCmptPrivate, const CStrW& playerName, u16 serverPort, bool useSTUN, const CStr& hostJID) +{ + ENSURE(!!g_XmppClient); + // Get ip of server + /* + const CStr serverAddress = CStr(g_XmppClient->GetGameIp(*pCmptPrivate->pScriptInterface, hostJID.substr(0, hostJID.find("@")), useSTUN)); + if (serverAddress.size() == 0) + { + ScriptRequest rq(pCmptPrivate->pScriptInterface); + ScriptException::Raise(rq, "Could not retrieve server address."); + return; + } + + StartNetworkJoin(pCmptPrivate, playerName, serverAddress, serverPort, useSTUN, hostJID); + */ + g_Game = new CGame(true); + g_NetClient = new CNetClient(g_Game, false); + g_NetClient->SetUserName(playerName); + g_NetClient->SetHostingPlayerName(hostJID.substr(0, hostJID.find("@"))); + g_XmppClient->SendIqGetConnectionData(hostJID); +} + void JSI_Network::DisconnectNetworkGame(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivate)) { // TODO: we ought to do async reliable disconnections @@ -232,8 +261,9 @@ scriptInterface.RegisterFunction("HasNetServer"); scriptInterface.RegisterFunction("HasNetClient"); scriptInterface.RegisterFunction("FindStunEndpoint"); - scriptInterface.RegisterFunction("StartNetworkHost"); + scriptInterface.RegisterFunction("StartNetworkHost"); scriptInterface.RegisterFunction("StartNetworkJoin"); + scriptInterface.RegisterFunction("StartNetworkJoinLobby"); scriptInterface.RegisterFunction("DisconnectNetworkGame"); scriptInterface.RegisterFunction("GetPlayerGUID"); scriptInterface.RegisterFunction("PollNetworkClient"); Index: source/ps/GameSetup/GameSetup.cpp =================================================================== --- source/ps/GameSetup/GameSetup.cpp +++ source/ps/GameSetup/GameSetup.cpp @@ -1558,7 +1558,8 @@ g_NetClient = new CNetClient(g_Game, true); g_NetClient->SetUserName(userName); - g_NetClient->SetupConnection("127.0.0.1", PS_DEFAULT_PORT, nullptr); + g_NetClient->SetupServerData("127.0.0.1", PS_DEFAULT_PORT, false); + g_NetClient->SetupConnection(nullptr); } else if (args.Has("autostart-client")) { @@ -1571,7 +1572,8 @@ if (ip.empty()) ip = "127.0.0.1"; - bool ok = g_NetClient->SetupConnection(ip, PS_DEFAULT_PORT, nullptr); + g_NetClient->SetupServerData(ip, PS_DEFAULT_PORT, false); + bool ok = g_NetClient->SetupConnection(nullptr); ENSURE(ok); } else