Always require lobby authentication for lobby matches, refs #3549 / rP21520 / D897.
[0ad.git] / binaries / data / mods / public / gui / gamesetup_mp / gamesetup_mp.js
blob63e6d200e02c8692662aa43cacd5f6fc2a53ba93
1 /**
2  * Whether we are attempting to join or host a game.
3  */
4 var g_IsConnecting = false;
6 /**
7  * "server" or "client"
8  */
9 var g_GameType;
11 /**
12  * Server title shown in the lobby gamelist.
13  */
14 var g_ServerName = "";
16 /**
17  * Cached to pass it to the gamesetup of the controller to report the game to the lobby.
18  */
19 var g_ServerPort;
21 var g_IsRejoining = false;
22 var g_GameAttributes; // used when rejoining
23 var g_PlayerAssignments; // used when rejoining
24 var g_UserRating;
26 /**
27  * Object containing the IP address and port of the STUN server.
28  */
29 var g_StunEndpoint;
31 function init(attribs)
33         g_UserRating = attribs.rating;
35         switch (attribs.multiplayerGameType)
36         {
37         case "join":
38         {
39                 if (Engine.HasXmppClient())
40                 {
41                         if (startJoin(attribs.name, attribs.ip, getValidPort(attribs.port), attribs.useSTUN, attribs.hostJID))
42                                 switchSetupPage("pageConnecting");
43                 }
44                 else
45                         switchSetupPage("pageJoin");
46                 break;
47         }
48         case "host":
49         {
50                 Engine.GetGUIObjectByName("hostSTUNWrapper").hidden = !Engine.HasXmppClient();
51                 if (Engine.HasXmppClient())
52                 {
53                         Engine.GetGUIObjectByName("hostPlayerName").caption = attribs.name;
54                         Engine.GetGUIObjectByName("hostServerName").caption =
55                                 sprintf(translate("%(name)s's game"), { "name": attribs.name });
57                         Engine.GetGUIObjectByName("useSTUN").checked = Engine.ConfigDB_GetValue("user", "lobby.stun.enabled") == "true";
58                 }
60                 switchSetupPage("pageHost");
61                 break;
62         }
63         default:
64                 error("Unrecognised multiplayer game type: " + attribs.multiplayerGameType);
65                 break;
66         }
69 function cancelSetup()
71         if (g_IsConnecting)
72                 Engine.DisconnectNetworkGame();
74         if (Engine.HasXmppClient())
75                 Engine.LobbySetPlayerPresence("available");
77         // Keep the page open if an attempt to join/host by ip failed
78         if (!g_IsConnecting || (Engine.HasXmppClient() && g_GameType == "client"))
79         {
80                 Engine.PopGuiPage();
81                 return;
82         }
84         g_IsConnecting = false;
85         Engine.GetGUIObjectByName("hostFeedback").caption = "";
87         if (g_GameType == "client")
88                 switchSetupPage("pageJoin");
89         else if (g_GameType == "server")
90                 switchSetupPage("pageHost");
91         else
92                 error("cancelSetup: Unrecognised multiplayer game type: " + g_GameType);
95 function confirmSetup()
97         if (!Engine.GetGUIObjectByName("pageJoin").hidden)
98         {
99                 let joinPlayerName = Engine.GetGUIObjectByName("joinPlayerName").caption;
100                 let joinServer = Engine.GetGUIObjectByName("joinServer").caption;
101                 let joinPort = Engine.GetGUIObjectByName("joinPort").caption;
103                 if (startJoin(joinPlayerName, joinServer, getValidPort(joinPort), false))
104                         switchSetupPage("pageConnecting");
105         }
106         else if (!Engine.GetGUIObjectByName("pageHost").hidden)
107         {
108                 let hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption;
109                 let hostServerName = Engine.GetGUIObjectByName("hostServerName").caption;
110                 let hostPort = Engine.GetGUIObjectByName("hostPort").caption;
112                 if (!hostServerName)
113                 {
114                         Engine.GetGUIObjectByName("hostFeedback").caption = translate("Please enter a valid server name.");
115                         return;
116                 }
118                 if (getValidPort(hostPort) != +hostPort)
119                 {
120                         Engine.GetGUIObjectByName("hostFeedback").caption = sprintf(
121                                 translate("Server port number must be between %(min)s and %(max)s."), {
122                                         "min": g_ValidPorts.min,
123                                         "max": g_ValidPorts.max
124                                 });
125                         return;
126                 }
128                 if (startHost(hostPlayerName, hostServerName, getValidPort(hostPort)))
129                         switchSetupPage("pageConnecting");
130         }
133 function startConnectionStatus(type)
135         g_GameType = type;
136         g_IsConnecting = true;
137         g_IsRejoining = false;
138         Engine.GetGUIObjectByName("connectionStatus").caption = translate("Connecting to server...");
141 function onTick()
143         if (!g_IsConnecting)
144                 return;
146         pollAndHandleNetworkClient();
149 function pollAndHandleNetworkClient()
151         while (true)
152         {
153                 var message = Engine.PollNetworkClient();
154                 if (!message)
155                         break;
157                 log(sprintf(translate("Net message: %(message)s"), { "message": uneval(message) }));
159                 // If we're rejoining an active game, we don't want to actually display
160                 // the game setup screen, so perform similar processing to gamesetup.js
161                 // in this screen
162                 if (g_IsRejoining)
163                         switch (message.type)
164                         {
165                         case "netstatus":
166                                 switch (message.status)
167                                 {
168                                 case "disconnected":
169                                         cancelSetup();
170                                         reportDisconnect(message.reason, false);
171                                         return;
173                                 default:
174                                         error("Unrecognised netstatus type: " + message.status);
175                                         break;
176                                 }
177                                 break;
179                         case "gamesetup":
180                                 g_GameAttributes = message.data;
181                                 break;
183                         case "players":
184                                 g_PlayerAssignments = message.newAssignments;
185                                 break;
187                         case "start":
188                                 // Copy playernames from initial player assignment to the settings
189                                 for (let guid in g_PlayerAssignments)
190                                 {
191                                         let player = g_PlayerAssignments[guid];
192                                         if (player.player > 0)  // not observer or GAIA
193                                                 g_GameAttributes.settings.PlayerData[player.player - 1].Name = player.name;
194                                 }
196                                 Engine.SwitchGuiPage("page_loading.xml", {
197                                         "attribs": g_GameAttributes,
198                                         "isRejoining": g_IsRejoining,
199                                         "playerAssignments": g_PlayerAssignments
200                                 });
201                                 break;
203                         case "chat":
204                                 break;
206                         case "netwarn":
207                                 break;
209                         default:
210                                 error("Unrecognised net message type: " + message.type);
211                         }
212                 else
213                         // Not rejoining - just trying to connect to server
215                         switch (message.type)
216                         {
217                         case "netstatus":
218                                 switch (message.status)
219                                 {
220                                 case "connected":
221                                         Engine.GetGUIObjectByName("connectionStatus").caption = translate("Registering with server...");
222                                         break;
224                                 case "authenticated":
225                                         if (message.rejoining)
226                                         {
227                                                 Engine.GetGUIObjectByName("connectionStatus").caption = translate("Game has already started, rejoining...");
228                                                 g_IsRejoining = true;
229                                                 return; // we'll process the game setup messages in the next tick
230                                         }
231                                         Engine.SwitchGuiPage("page_gamesetup.xml", {
232                                                 "type": g_GameType,
233                                                 "serverName": g_ServerName,
234                                                 "serverPort": g_ServerPort,
235                                                 "stunEndpoint": g_StunEndpoint
236                                         });
237                                         return; // don't process any more messages - leave them for the game GUI loop
239                                 case "disconnected":
240                                         cancelSetup();
241                                         reportDisconnect(message.reason, false);
242                                         return;
244                                 default:
245                                         error("Unrecognised netstatus type: " + message.status);
246                                         break;
247                                 }
248                                 break;
250                         case "netwarn":
251                                 break;
253                         default:
254                                 error("Unrecognised net message type: " + message.type);
255                                 break;
256                         }
257         }
260 function switchSetupPage(newPage)
262         let multiplayerPages = Engine.GetGUIObjectByName("multiplayerPages");
263         for (let page of multiplayerPages.children)
264                 if (page.name.startsWith("page"))
265                         page.hidden = true;
267         if (newPage == "pageJoin" || newPage == "pageHost")
268         {
269                 let pageSize = multiplayerPages.size;
270                 let halfHeight = newPage == "pageJoin" ? 130 : Engine.HasXmppClient() ? 125 : 110;
271                 pageSize.top = -halfHeight;
272                 pageSize.bottom = halfHeight;
273                 multiplayerPages.size = pageSize;
274         }
276         Engine.GetGUIObjectByName(newPage).hidden = false;
278         Engine.GetGUIObjectByName("hostPlayerNameWrapper").hidden = Engine.HasXmppClient();
279         Engine.GetGUIObjectByName("hostServerNameWrapper").hidden = !Engine.HasXmppClient();
281         Engine.GetGUIObjectByName("continueButton").hidden = newPage == "pageConnecting";
284 function startHost(playername, servername, port)
286         startConnectionStatus("server");
288         saveSettingAndWriteToUserConfig("playername.multiplayer", playername);
290         saveSettingAndWriteToUserConfig("multiplayerhosting.port", port);
292         let hostFeedback = Engine.GetGUIObjectByName("hostFeedback");
294         // Disallow identically named games in the multiplayer lobby
295         if (Engine.HasXmppClient() &&
296             Engine.GetGameList().some(game => game.name == servername))
297         {
298                 cancelSetup();
299                 hostFeedback.caption = translate("Game name already in use.");
300                 return false;
301         }
303         if (Engine.HasXmppClient() && Engine.GetGUIObjectByName("useSTUN").checked)
304         {
305                 g_StunEndpoint = Engine.FindStunEndpoint(port);
306                 if (!g_StunEndpoint)
307                 {
308                         cancelSetup();
309                         hostFeedback.caption = translate("Failed to host via STUN.");
310                         return false;
311                 }
312         }
314         try
315         {
316                 Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, playername);
317         }
318         catch (e)
319         {
320                 cancelSetup();
321                 messageBox(
322                         400, 200,
323                         sprintf(translate("Cannot host game: %(message)s."), { "message": e.message }),
324                         translate("Error")
325                 );
326                 return false;
327         }
329         g_ServerName = servername;
330         g_ServerPort = port;
332         if (Engine.HasXmppClient())
333                 Engine.LobbySetPlayerPresence("playing");
335         return true;
339  * Connects via STUN if the hostJID is given.
340  */
341 function startJoin(playername, ip, port, useSTUN, hostJID = "")
343         try
344         {
345                 Engine.StartNetworkJoin(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), ip, port, useSTUN, hostJID);
346         }
347         catch (e)
348         {
349                 cancelSetup();
350                 messageBox(
351                         400, 200,
352                         sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }),
353                         translate("Error")
354                 );
355                 return false;
356         }
358         startConnectionStatus("client");
360         if (Engine.HasXmppClient())
361                 Engine.LobbySetPlayerPresence("playing");
362         else
363         {
364                 // Only save the player name and host address if they're valid and we're not in the lobby
365                 saveSettingAndWriteToUserConfig("playername.multiplayer", playername);
366                 saveSettingAndWriteToUserConfig("multiplayerserver", ip);
367                 saveSettingAndWriteToUserConfig("multiplayerjoining.port", port);
368         }
369         return true;
372 function getDefaultGameName()
374         return sprintf(translate("%(playername)s's game"), {
375                 "playername": multiplayerName()
376         });