Index: ps/trunk/binaries/data/mods/public/gui/common/network.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/network.js +++ ps/trunk/binaries/data/mods/public/gui/common/network.js @@ -76,6 +76,9 @@ case 10: return translate("Error: Server failed to allocate a unique client identifier."); case 11: return translate("Error: Client commands were ready for an unexpected game turn."); case 12: return translate("Error: Client simulated an unexpected game turn."); + case 13: return translate("Password is invalid."); + case 14: return translate("Could not find an unused port for the enet STUN client."); + case 15: return translate("Could not find the STUN endpoint."); default: warn("Unknown disconnect-reason ID received: " + id); return sprintf(translate("\\[Invalid value %(id)s]"), { "id": id }); Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js @@ -10,8 +10,7 @@ this.mapCache = mapCache; this.serverName = initData.serverName; - this.serverPort = initData.serverPort; - this.stunEndpoint = initData.stunEndpoint; + this.hasPassword = initData.hasPassword; this.mods = JSON.stringify(Engine.GetEngineInfo().mods); this.timer = undefined; @@ -83,7 +82,6 @@ let stanza = { "name": this.serverName, - "port": this.serverPort, "hostUsername": Engine.LobbyGetNick(), "mapName": g_GameAttributes.map, "niceMapName": this.mapCache.getTranslatableMapName(g_GameAttributes.mapType, g_GameAttributes.map), @@ -93,9 +91,8 @@ "nbp": clients.connectedPlayers, "maxnbp": g_GameAttributes.settings.PlayerData.length, "players": clients.list, - "stunIP": this.stunEndpoint ? this.stunEndpoint.ip : "", - "stunPort": this.stunEndpoint ? this.stunEndpoint.port : "", - "mods": this.mods + "mods": this.mods, + "hasPassword": this.hasPassword || "" }; // Only send the stanza if one of these properties changed Index: ps/trunk/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js +++ ps/trunk/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js @@ -14,20 +14,17 @@ var g_ServerName = ""; /** - * Cached to pass it to the game setup of the controller to report the game to the lobby. + * Identifier if server is using password. */ -var g_ServerPort; +var g_ServerHasPassword = false; + +var g_ServerId; var g_IsRejoining = false; var g_GameAttributes; // used when rejoining var g_PlayerAssignments; // used when rejoining var g_UserRating; -/** - * Object containing the IP address and port of the STUN server. - */ -var g_StunEndpoint; - function init(attribs) { g_UserRating = attribs.rating; @@ -36,19 +33,27 @@ { case "join": { - if (Engine.HasXmppClient()) + if (!Engine.HasXmppClient()) { - if (startJoin(attribs.name, attribs.ip, getValidPort(attribs.port), attribs.useSTUN, attribs.hostJID)) - switchSetupPage("pageConnecting"); - } - else switchSetupPage("pageJoin"); + break; + } + if (attribs.hasPassword) + { + g_ServerName = attribs.name; + g_ServerId = attribs.hostJID; + switchSetupPage("pagePassword"); + } + else if (startJoinFromLobby(attribs.name, attribs.hostJID, "")) + switchSetupPage("pageConnecting"); break; } case "host": { - Engine.GetGUIObjectByName("hostSTUNWrapper").hidden = !Engine.HasXmppClient(); - if (Engine.HasXmppClient()) + let hasXmppClient = Engine.HasXmppClient(); + Engine.GetGUIObjectByName("hostSTUNWrapper").hidden = !hasXmppClient; + Engine.GetGUIObjectByName("hostPasswordWrapper").hidden = !hasXmppClient; + if (hasXmppClient) { Engine.GetGUIObjectByName("hostPlayerName").caption = attribs.name; Engine.GetGUIObjectByName("hostServerName").caption = @@ -92,6 +97,14 @@ error("cancelSetup: Unrecognised multiplayer game type: " + g_GameType); } +function confirmPassword() +{ + if (Engine.GetGUIObjectByName("pagePassword").hidden) + return; + if (startJoinFromLobby(g_ServerName, g_ServerId, Engine.GetGUIObjectByName("clientPassword").caption)) + switchSetupPage("pageConnecting"); +} + function confirmSetup() { if (!Engine.GetGUIObjectByName("pageJoin").hidden) @@ -105,16 +118,14 @@ } else if (!Engine.GetGUIObjectByName("pageHost").hidden) { - let hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption; let hostServerName = Engine.GetGUIObjectByName("hostServerName").caption; - let hostPort = Engine.GetGUIObjectByName("hostPort").caption; - if (!hostServerName) { Engine.GetGUIObjectByName("hostFeedback").caption = translate("Please enter a valid server name."); return; } + let hostPort = Engine.GetGUIObjectByName("hostPort").caption; if (getValidPort(hostPort) != +hostPort) { Engine.GetGUIObjectByName("hostFeedback").caption = sprintf( @@ -125,7 +136,9 @@ return; } - if (startHost(hostPlayerName, hostServerName, getValidPort(hostPort))) + let hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption; + let hostPassword = Engine.GetGUIObjectByName("hostPassword").caption; + if (startHost(hostPlayerName, hostServerName, getValidPort(hostPort), hostPassword)) switchSetupPage("pageConnecting"); } } @@ -146,6 +159,28 @@ pollAndHandleNetworkClient(); } +function getConnectionFailReason(reason) +{ + switch (reason) + { + case "not_server": return translate("Server is not running."); + case "invalid_password": return translate("Password is invalid."); + default: + warn("Unknown connection failure reason: " + reason); + return sprintf(translate("\\[Invalid value %(reason)s]"), { "reason": id }); + } +} + +function reportConnectionFail(reason) +{ + messageBox( + 400, 200, + (translate("Failed to connect to the server.") + ) + "\n\n" + getConnectionFailReason(reason), + translate("Connection failed") + ); +} + function pollAndHandleNetworkClient() { while (true) @@ -155,13 +190,27 @@ break; log(sprintf(translate("Net message: %(message)s"), { "message": uneval(message) })); - // If we're rejoining an active game, we don't want to actually display // the game setup screen, so perform similar processing to gamesetup.js // in this screen if (g_IsRejoining) + { switch (message.type) { + case "serverdata": + switch (message.status) + { + case "failed": + cancelSetup(); + reportConnectionFail(message.reason, false); + return; + + default: + error("Unrecognised netstatus type: " + message.status); + break; + } + break; + case "netstatus": switch (message.status) { @@ -211,11 +260,26 @@ default: error("Unrecognised net message type: " + message.type); } + } else - // Not rejoining - just trying to connect to server - + // Not rejoining - just trying to connect to server. + { switch (message.type) { + case "serverdata": + switch (message.status) + { + case "failed": + cancelSetup(); + reportConnectionFail(message.reason, false); + return; + + default: + error("Unrecognised netstatus type: " + message.status); + break; + } + break; + case "netstatus": switch (message.status) { @@ -232,8 +296,7 @@ } Engine.SwitchGuiPage("page_gamesetup.xml", { "serverName": g_ServerName, - "serverPort": g_ServerPort, - "stunEndpoint": g_StunEndpoint + "hasPassword": g_ServerHasPassword }); return; // don't process any more messages - leave them for the game GUI loop @@ -255,6 +318,7 @@ error("Unrecognised net message type: " + message.type); break; } + } } } @@ -273,16 +337,24 @@ pageSize.bottom = halfHeight; multiplayerPages.size = pageSize; } + else if (newPage == "pagePassword") + { + let pageSize = multiplayerPages.size; + let halfHeight = 60; + pageSize.top = -halfHeight; + pageSize.bottom = halfHeight; + multiplayerPages.size = pageSize; + } Engine.GetGUIObjectByName(newPage).hidden = false; Engine.GetGUIObjectByName("hostPlayerNameWrapper").hidden = Engine.HasXmppClient(); Engine.GetGUIObjectByName("hostServerNameWrapper").hidden = !Engine.HasXmppClient(); - Engine.GetGUIObjectByName("continueButton").hidden = newPage == "pageConnecting"; + Engine.GetGUIObjectByName("continueButton").hidden = newPage == "pageConnecting" || newPage == "pagePassword"; } -function startHost(playername, servername, port) +function startHost(playername, servername, port, password) { startConnectionStatus("server"); @@ -301,20 +373,11 @@ return false; } - if (Engine.HasXmppClient() && Engine.GetGUIObjectByName("useSTUN").checked) - { - g_StunEndpoint = Engine.FindStunEndpoint(port); - if (!g_StunEndpoint) - { - cancelSetup(); - hostFeedback.caption = translate("Failed to host via STUN."); - return false; - } - } + let useSTUN = Engine.HasXmppClient() && Engine.GetGUIObjectByName("useSTUN").checked; try { - Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, playername); + Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, playername, useSTUN, password); } catch (e) { @@ -328,7 +391,7 @@ } g_ServerName = servername; - g_ServerPort = port; + g_ServerHasPassword = !!password; if (Engine.HasXmppClient()) Engine.LobbySetPlayerPresence("playing"); @@ -370,9 +433,49 @@ return true; } +function startJoinFromLobby(playername, hostJID, password) +{ + if (!Engine.HasXmppClient()) + { + cancelSetup(); + messageBox( + 400, 200, + sprintf("You cannot join a lobby game without logging in to the lobby."), + translate("Error") + ); + return false; + } + + try + { + Engine.StartNetworkJoinLobby(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), hostJID, password); + } + 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"), { "playername": multiplayerName() }); } + +function getDefaultPassword() +{ + return ""; +} Index: ps/trunk/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.xml +++ ps/trunk/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.xml @@ -106,7 +106,21 @@ - + + + + Engine.ConfigDB_CreateAndWriteValueToFile("user", "lobby.stun.enabled", String(this.checked), "config/user.cfg"); @@ -129,9 +143,26 @@