Ticket #3143: 3143_lobby_data_v3.patch
File 3143_lobby_data_v3.patch, 20.4 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/common/functions_utility.js
function sortNameIgnoreCase(x, y) 57 57 58 58 /** 59 59 * Escape tag start and escape characters, so users cannot use special formatting. 60 60 * Also limit string length to 256 characters (not counting escape characters). 61 61 */ 62 function escapeText(text )62 function escapeText(text, limitLength = true) 63 63 { 64 64 if (!text) 65 65 return text; 66 66 67 return text.substr(0, 255).replace(/\\/g, "\\\\").replace(/\[/g, "\\["); 67 if (limitLength) 68 text = text.substr(0, 255); 69 70 return text.replace(/\\/g, "\\\\").replace(/\[/g, "\\["); 71 } 72 73 function unescapeText(text) 74 { 75 if (!text) 76 return text; 77 return text.replace(/\\\\/g, "\\").replace(/\\\[/g, "\["); 78 } 79 80 /** 81 * Merge players by team to remove duplicate Team entries, thus reducing the packet size of the lobby report. 82 */ 83 function playerDataToStringifiedTeamList(playerData) 84 { 85 let teamList = {}; 86 87 for (let pData of playerData) 88 { 89 let team = pData.Team === undefined ? -1 : pData.Team; 90 if (!teamList[team]) 91 teamList[team] = []; 92 teamList[team].push(pData); 93 delete teamList[team].Team; 94 } 95 96 return escapeText(JSON.stringify(teamList), false); 97 } 98 99 function stringifiedTeamListToPlayerData(stringifiedTeamList) 100 { 101 let teamList = JSON.parse(unescapeText(stringifiedTeamList)); 102 let playerData = []; 103 104 for (let team in teamList) 105 for (let pData of teamList[team]) 106 { 107 pData.Team = +team; 108 playerData.push(pData); 109 } 110 111 return playerData; 68 112 } 69 113 70 114 function translateMapTitle(mapTitle) 71 115 { 72 116 return mapTitle == "random" ? translateWithContext("map selection", "Random") : translate(mapTitle); … … function formatPlayerInfo(playerDataArra 224 268 let playerDescriptions = {}; 225 269 let playerIdx = 0; 226 270 227 271 for (let playerData of playerDataArray) 228 272 { 229 if (playerData == null || playerData.Civ == "gaia")273 if (playerData == null || playerData.Civ && playerData.Civ == "gaia") 230 274 continue; 231 275 232 276 ++playerIdx; 233 277 let teamIdx = playerData.Team; 234 278 let isAI = playerData.AI && playerData.AI != ""; 235 let playerState = playerStates && playerStates[playerIdx] ;279 let playerState = playerStates && playerStates[playerIdx] || playerData.State; 236 280 let isActive = !playerState || playerState == "active"; 281 let isOffline = playerData.Offline; 237 282 238 283 let playerDescription; 239 284 if (isAI) 240 285 { 241 if (isActive) 242 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 243 playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s)"); 286 if (playerData.Civ) 287 { 288 if (isActive) 289 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 290 playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s)"); 291 else 292 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 293 playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s, %(state)s)"); 294 } 244 295 else 245 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 246 playerDescription = translate("%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s, %(state)s)"); 296 { 297 if (isActive) 298 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 299 playerDescription = translate("%(playerName)s (%(AIdifficulty)s %(AIname)s)"); 300 else 301 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 302 playerDescription = translate("%(playerName)s (%(AIdifficulty)s %(AIname)s, %(state)s)"); 303 } 247 304 } 248 305 else 249 306 { 250 if (isActive) 251 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 252 playerDescription = translate("%(playerName)s (%(civ)s)"); 307 // Can only occur in the lobby for now, so no strings with civ needed 308 if (isOffline) 309 { 310 if (isActive) 311 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 312 playerDescription = translate("%(playerName)s (OFFLINE)"); 313 else 314 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 315 playerDescription = translate("%(playerName)s (OFFLINE, %(state)s)"); 316 } 253 317 else 254 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 255 playerDescription = translate("%(playerName)s (%(civ)s, %(state)s)"); 318 { 319 if (playerData.Civ) 320 if (isActive) 321 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 322 playerDescription = translate("%(playerName)s (%(civ)s)"); 323 else 324 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 325 playerDescription = translate("%(playerName)s (%(civ)s, %(state)s)"); 326 else 327 if (isActive) 328 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 329 playerDescription = translate("%(playerName)s"); 330 else 331 // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu 332 playerDescription = translate("%(playerName)s (%(state)s)"); 333 } 256 334 } 257 335 258 336 // Sort player descriptions by team 259 337 if (!playerDescriptions[teamIdx]) 260 338 playerDescriptions[teamIdx] = []; 261 339 262 340 playerDescriptions[teamIdx].push(sprintf(playerDescription, { 263 341 "playerName": 264 342 '[color="' + 265 rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color) + 343 (typeof getPlayerColor == 'function' ? 344 (isAI ? 345 "255 255 255" : 346 getPlayerColor(playerData.Name)) : 347 rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color)) + 266 348 '"]' + escapeText(playerData.Name) + "[/color]", 267 349 268 350 "civ": 269 351 !playerData.Civ ? 270 352 translate("Unknown Civilization") : … … function formatPlayerInfo(playerDataArra 281 363 "AIdifficulty": isAI ? translateAIDifficulty(playerData.AIDiff) : "" 282 364 })); 283 365 } 284 366 285 367 let teams = Object.keys(playerDescriptions); 368 if (teams.indexOf("observer") > -1) 369 teams.splice(teams.indexOf("observer"), 1); 370 371 let teamDescription = []; 286 372 287 373 // If there are no teams, merge all playersDescriptions 288 374 if (teams.length == 1) 289 return playerDescriptions[teams[0]].join("\n") + "\n";375 teamDescription.push(playerDescriptions[teams[0]].join("\n")); 290 376 291 377 // If there are teams, merge "Team N:" + playerDescriptions 292 return teams.map(team => { 378 else 379 teamDescription = teamDescription.concat(teams.map(team => { 380 381 let teamCaption = team == -1 ? 382 translate("No Team") : 383 sprintf(translate("Team %(team)s"), { "team": +team + 1 }); 384 385 // Translation: Describe players of one team in a selected game, f.e. in the replay- or savegame menu or lobby 386 return sprintf(translate("%(team)s:\n%(playerDescriptions)s"), { 387 "team": '[font="sans-bold-14"]' + teamCaption + "[/font]", 388 "playerDescriptions": playerDescriptions[team].join("\n") 389 }); 390 })); 391 392 if (playerDescriptions.observer) 393 teamDescription.push(sprintf(translate("%(team)s:\n%(playerDescriptions)s"), { 394 "team": '[font="sans-bold-14"]' + translatePlural("Observer", "Observers", playerDescriptions.observer.length) + "[/font]", 395 "playerDescriptions": playerDescriptions.observer.join("\n") 396 })); 293 397 294 let teamCaption = team == -1 ? 295 translate("No Team") : 296 sprintf(translate("Team %(team)s"), { "team": +team + 1 }); 297 298 // Translation: Describe players of one team in a selected game, f.e. in the replay- or savegame menu or lobby 299 return sprintf(translate("%(team)s:\n%(playerDescriptions)s"), { 300 "team": '[font="sans-bold-14"]' + teamCaption + "[/font]", 301 "playerDescriptions": playerDescriptions[team].join("\n") 302 }); 303 }).join("\n\n"); 398 return teamDescription.join("\n\n"); 304 399 } -
binaries/data/mods/public/gui/gamesetup/gamesetup.js
function handleReadyMessage(message) 676 676 */ 677 677 function handleGamestartMessage(message) 678 678 { 679 679 if (g_IsController && Engine.HasXmppClient()) 680 680 { 681 let playerNames = Object.keys(g_PlayerAssignments).map(guid => g_PlayerAssignments[guid].name);682 Engine.SendChangeStateGame( playerNames.length, playerNames.join(", "));681 let clients = formatClientsForStanza(); 682 Engine.SendChangeStateGame(clients.connectedPlayers, clients.list); 683 683 } 684 684 685 685 Engine.SwitchGuiPage("page_loading.xml", { 686 686 "attribs": g_GameAttributes, 687 687 "isNetworked" : g_IsNetworked, … … function resetReadyData() 1937 1937 else 1938 1938 setReady(false, false); 1939 1939 } 1940 1940 1941 1941 /** 1942 * Don't send teams until the game was started. 1943 * The playerData format from g_GameAttributes is kept to reuse the GUI function presenting the data. 1944 */ 1945 function formatClientsForStanza() 1946 { 1947 let connectedPlayers = 0; 1948 let list = []; 1949 1950 for (let guid in g_PlayerAssignments) 1951 { 1952 if (g_GameAttributes.settings.PlayerData[g_PlayerAssignments[guid].player - 1]) 1953 { 1954 ++connectedPlayers; 1955 list.push({ 1956 "Name": g_PlayerAssignments[guid].name 1957 }); 1958 } 1959 else 1960 list.push({ 1961 "Name": g_PlayerAssignments[guid].name, 1962 "Team": "observer" 1963 }); 1964 } 1965 1966 return { 1967 "list": playerDataToStringifiedTeamList(list), 1968 "connectedPlayers": connectedPlayers 1969 }; 1970 } 1971 1972 /** 1942 1973 * Send the relevant gamesettings to the lobbybot. 1943 1974 */ 1944 1975 function sendRegisterGameStanza() 1945 1976 { 1946 1977 if (!g_IsController || !Engine.HasXmppClient()) … … function sendRegisterGameStanza() 1949 1980 let selectedMapSize = Engine.GetGUIObjectByName("mapSize").selected; 1950 1981 let selectedVictoryCondition = Engine.GetGUIObjectByName("victoryCondition").selected; 1951 1982 1952 1983 let mapSize = g_GameAttributes.mapType == "random" ? Engine.GetGUIObjectByName("mapSize").list_data[selectedMapSize] : "Default"; 1953 1984 let victoryCondition = Engine.GetGUIObjectByName("victoryCondition").list[selectedVictoryCondition]; 1954 let playerNames = Object.keys(g_PlayerAssignments).map(guid => g_PlayerAssignments[guid].name).sort();1985 let clients = formatClientsForStanza(); 1955 1986 1956 1987 let stanza = { 1957 1988 "name": g_ServerName, 1958 1989 "port": g_ServerPort, 1959 1990 "mapName": g_GameAttributes.map, 1960 1991 "niceMapName": getMapDisplayName(g_GameAttributes.map), 1961 1992 "mapSize": mapSize, 1962 1993 "mapType": g_GameAttributes.mapType, 1963 1994 "victoryCondition": victoryCondition, 1964 "nbp": Object.keys(g_PlayerAssignments).length || 1,1965 " tnbp": g_GameAttributes.settings.PlayerData.length,1966 "players": playerNames.join(", ")1995 "nbp": clients.connectedPlayers, 1996 "maxnbp": g_GameAttributes.settings.PlayerData.length, 1997 "players": clients.list, 1967 1998 }; 1968 1999 1969 2000 // Only send the stanza if the relevant settings actually changed 1970 2001 if (g_LastGameStanza && Object.keys(stanza).every(prop => g_LastGameStanza[prop] == stanza[prop])) 1971 2002 return; -
binaries/data/mods/public/gui/lobby/lobby.js
function filterGame(game) 283 283 if (mapSizeFilter.selected != 0 && 284 284 game.mapSize != mapSizeFilter.list_data[mapSizeFilter.selected]) 285 285 return true; 286 286 287 287 if (playersNumberFilter.selected != 0 && 288 game. tnbp != playersNumberFilter.list_data[playersNumberFilter.selected])288 game.maxnbp != playersNumberFilter.list_data[playersNumberFilter.selected]) 289 289 return true; 290 290 291 291 if (mapTypeFilter.selected != 0 && 292 292 game.mapType != mapTypeFilter.list_data[mapTypeFilter.selected]) 293 293 return true; 294 294 295 if (!showFullFilter.checked && game. tnbp <= game.nbp)295 if (!showFullFilter.checked && game.maxnbp <= game.nbp) 296 296 return true; 297 297 298 298 return false; 299 299 } 300 300 … … function updateGameList() 544 544 sortA = translate(a.niceMapName); 545 545 sortB = translate(b.niceMapName); 546 546 break; 547 547 case 'nPlayers': 548 548 // Compare playercount ratio 549 sortA = a.nbp * b. tnbp;550 sortB = b.nbp * a. tnbp;549 sortA = a.nbp * b.maxnbp; 550 sortB = b.nbp * a.maxnbp; 551 551 break; 552 552 case 'status': 553 553 default: 554 554 sortA = g_GameStatusOrder.indexOf(a.state); 555 555 sortB = g_GameStatusOrder.indexOf(b.state); … … function updateGameList() 580 580 581 581 list_name.push('[color="' + g_GameColors[game.state] + '"]' + gameName); 582 582 list_mapName.push(translateMapTitle(game.niceMapName)); 583 583 list_mapSize.push(translateMapSize(game.mapSize)); 584 584 list_mapType.push(g_MapTypes.Title[mapTypeIdx] || ""); 585 list_nPlayers.push(game.nbp + "/" + game. tnbp);585 list_nPlayers.push(game.nbp + "/" + game.maxnbp); 586 586 list.push(gameName); 587 587 list_data.push(i); 588 588 } 589 589 590 590 gamesBox.list_name = list_name; … … function updateGameSelection() 614 614 if (!game) 615 615 return; 616 616 617 617 Engine.GetGUIObjectByName("sgMapName").caption = translateMapTitle(game.niceMapName); 618 618 Engine.GetGUIObjectByName("sgNbPlayers").caption = sprintf( 619 translate("Players: %(current)s/%( total)s"), {619 translate("Players: %(current)s/%(max)s"), { 620 620 "current": game.nbp, 621 " total": game.tnbp621 "max": game.maxnbp 622 622 }); 623 623 624 Engine.GetGUIObjectByName("sgPlayersNames").caption = game.players;624 Engine.GetGUIObjectByName("sgPlayersNames").caption = formatPlayerInfo(stringifiedTeamListToPlayerData(game.players)); 625 625 Engine.GetGUIObjectByName("sgMapSize").caption = translateMapSize(game.mapSize); 626 626 627 627 let mapTypeIdx = g_MapTypes.Name.indexOf(game.mapType); 628 628 Engine.GetGUIObjectByName("sgMapType").caption = g_MapTypes.Title[mapTypeIdx] || ""; 629 629 … … function joinButton() 651 651 if (!game) 652 652 return; 653 653 654 654 let username = g_UserRating ? g_Username + " (" + g_UserRating + ")" : g_Username; 655 655 656 if (game.state == "init" || game.players.split(", ").indexOf(username) > -1)656 if (game.state == "init" || stringifiedTeamListToPlayerData(game.players).some(player => player.Name == username)) 657 657 joinSelectedGame(); 658 658 else 659 659 messageBox( 660 660 400, 200, 661 661 translate("The game has already started.") + "\n" + -
binaries/data/mods/public/gui/session/messages.js
var g_NotificationsTypes = 248 248 "guid": findGuidForPlayerID(player), 249 249 "player": player, 250 250 "resign": !!notification.resign 251 251 }); 252 252 playerFinished(player, false); 253 sendLobbyPlayerlistUpdate(); 253 254 }, 254 255 "won": function(notification, player) 255 256 { 256 257 addChatMessage({ 257 258 "type": "won", 258 259 "guid": findGuidForPlayerID(player), 259 260 "player": player 260 261 }); 261 262 playerFinished(player, true); 263 sendLobbyPlayerlistUpdate(); 262 264 }, 263 265 "diplomacy": function(notification, player) 264 266 { 265 267 addChatMessage({ 266 268 "type": "diplomacy", … … function handlePlayerAssignmentsMessage( 539 541 joins.forEach(guid => { 540 542 onClientJoin(guid); 541 543 }); 542 544 543 545 updateChatAddressees(); 544 545 // Update lobby gamestatus 546 if (g_IsController && Engine.HasXmppClient()) 547 { 548 let players = Object.keys(g_PlayerAssignments).map(guid => g_PlayerAssignments[guid].name); 549 Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", ")); 550 } 546 sendLobbyPlayerlistUpdate(); 551 547 } 552 548 553 549 function onClientJoin(guid) 554 550 { 555 551 let playerID = g_PlayerAssignments[guid].player; -
binaries/data/mods/public/gui/session/session.js
const g_PopulationAlertColor = "orange"; 13 13 const g_Ambient = [ "audio/ambient/dayscape/day_temperate_gen_03.ogg" ]; 14 14 15 15 /** 16 16 * Map, player and match settings set in gamesetup. 17 17 */ 18 var g_GameAttributes;18 const g_GameAttributes = Object.freeze(Engine.GetInitAttributes()); 19 19 20 20 /** 21 21 * Is this user in control of game settings (i.e. is a network server, or offline player). 22 22 */ 23 23 var g_IsController; … … function init(initData, hotloadData) 223 223 Engine.EndGame(); 224 224 Engine.SwitchGuiPage("page_pregame.xml"); 225 225 return; 226 226 } 227 227 228 g_GameAttributes = Engine.GetInitAttributes();229 230 228 if (initData) 231 229 { 232 230 g_IsNetworked = initData.isNetworked; 233 231 g_IsController = initData.isController; 234 232 g_PlayerAssignments = initData.playerAssignments; … … function init(initData, hotloadData) 287 285 initHotkeyTooltips(); 288 286 289 287 if (hotloadData) 290 288 g_Selection.selected = hotloadData.selection; 291 289 290 sendLobbyPlayerlistUpdate(); 292 291 onSimulationUpdate(); 293 294 292 setTimeout(displayGamestateNotifications, 1000); 295 293 296 294 // Report the performance after 5 seconds (when we're still near 297 295 // the initial camera view) and a minute (when the profiler will 298 296 // have settled down if framerates as very low), to give some -
binaries/data/mods/public/gui/session/utility_functions.js
function resourcesToAlphaMask(neededReso 129 129 totalCost += +neededResources[resource]; 130 130 var alpha = 50 + Math.round(+totalCost/10.0); 131 131 alpha = alpha > 125 ? 125 : alpha; 132 132 return "color:255 0 0 " + alpha; 133 133 } 134 135 /** 136 * The playerData format from g_GameAttributes is kept to reuse the GUI function presenting the data. 137 */ 138 function sendLobbyPlayerlistUpdate() 139 { 140 if (!g_IsController || !Engine.HasXmppClient()) 141 return; 142 143 // Extract the relevant player data and minimize packet load 144 let output = []; 145 for (let playerID in g_GameAttributes.settings.PlayerData) 146 { 147 let pData = g_GameAttributes.settings.PlayerData; 148 149 let outData = { "Name": pData.Name }; 150 151 if (g_GameAttributes.settings.LockTeams) 152 outData.Team = pData.Team; 153 154 if (pData.AI) 155 { 156 outData.AI = pData.AI; 157 outData.AIDiff = pData.AIDiff; 158 } 159 160 if (g_Players[playerID + 1].Offline) 161 outData.Offline = true; 162 163 // Whether the player has won or was defeated 164 let state = g_Players[playerID + 1].State; 165 if (state != "active") 166 outData.State = state; 167 168 output.push(outData); 169 } 170 171 // Add observers 172 let connectedPlayers = 0; 173 for (let guid in g_PlayerAssignments) 174 { 175 let pData = g_GameAttributes.settings.PlayerData[g_PlayerAssignments[guid].player - 1]; 176 177 if (pData) 178 ++connectedPlayers; 179 else 180 playerData.push({ 181 "Name": g_PlayerAssignments[guid].name, 182 "Team": "observer" 183 }); 184 } 185 186 Engine.SendChangeStateGame(connectedPlayers, playerDataToStringifiedTeamList(output)); 187 } -
source/lobby/XmppClient.cpp
void XmppClient::GUIGetGameList(ScriptIn 500 500 { 501 501 JSContext* cx = scriptInterface.GetContext(); 502 502 JSAutoRequest rq(cx); 503 503 504 504 scriptInterface.Eval("([])", ret); 505 const char* stats[] = { "name", "ip", "port", "state", "nbp", " tnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", "victoryCondition" };505 const char* stats[] = { "name", "ip", "port", "state", "nbp", "maxnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", "victoryCondition" }; 506 506 for(const glooxwrapper::Tag* const& t : m_GameList) 507 507 { 508 508 JS::RootedValue game(cx); 509 509 scriptInterface.Eval("({})", &game); 510 510 -
source/tools/XpartaMuPP/XpartaMuPP.py
class GameList(): 299 299 """ 300 300 JID = str(JID) 301 301 if JID in self.gameList: 302 302 if self.gameList[JID]['nbp-init'] > data['nbp']: 303 303 logging.debug("change game (%s) state from %s to %s", JID, self.gameList[JID]['state'], 'waiting') 304 self.gameList[JID]['nbp'] = data['nbp']305 304 self.gameList[JID]['state'] = 'waiting' 306 305 else: 307 306 logging.debug("change game (%s) state from %s to %s", JID, self.gameList[JID]['state'], 'running') 308 self.gameList[JID]['nbp'] = data['nbp']309 307 self.gameList[JID]['state'] = 'running' 308 self.gameList[JID]['nbp'] = data['nbp'] 309 self.gameList[JID]['players'] = data['players'] 310 310 311 311 ## Class which manages different game reports from clients ## 312 312 ## and calls leaderboard functions as appropriate. ## 313 313 class ReportManager(): 314 314 def __init__(self, leaderboard):