Changeset View
Standalone View
binaries/data/mods/public/gui/lobby/lobby.js
Show First 20 Lines • Show All 148 Lines • ▼ Show 20 Lines | var g_NetMessageTypes = { | ||||
}, | }, | ||||
"chat": { | "chat": { | ||||
"subject": msg => { | "subject": msg => { | ||||
updateSubject(msg.text); | updateSubject(msg.text); | ||||
}, | }, | ||||
"join": msg => { | "join": msg => { | ||||
addChatMessage({ | addChatMessage({ | ||||
"text": "/special " + sprintf(translate("%(nick)s has joined."), { | "text": "/special " + sprintf(translate("%(nick)s has joined."), { | ||||
"nick": msg.text | "nick": escapeText(msg.text) | ||||
}), | }), | ||||
"time": msg.time, | "time": msg.time, | ||||
"isSpecial": true | "isSpecial": true | ||||
}); | }); | ||||
}, | }, | ||||
"leave": msg => { | "leave": msg => { | ||||
addChatMessage({ | addChatMessage({ | ||||
"text": "/special " + sprintf(translate("%(nick)s has left."), { | "text": "/special " + sprintf(translate("%(nick)s has left."), { | ||||
"nick": msg.text | "nick": escapeText(msg.text) | ||||
}), | }), | ||||
"time": msg.time, | "time": msg.time, | ||||
"isSpecial": true | "isSpecial": true | ||||
}); | }); | ||||
if (msg.text == g_Username) | if (msg.text == g_Username) | ||||
Engine.DisconnectXmppClient(); | Engine.DisconnectXmppClient(); | ||||
}, | }, | ||||
Show All 17 Lines | "role": msg => { | ||||
me ? | me ? | ||||
translate("You have been unmuted.") : | translate("You have been unmuted.") : | ||||
translate("%(nick)s has been unmuted.") : | translate("%(nick)s has been unmuted.") : | ||||
me ? | me ? | ||||
translate("You are not a moderator anymore.") : | translate("You are not a moderator anymore.") : | ||||
translate("%(nick)s is not a moderator anymore."); | translate("%(nick)s is not a moderator anymore."); | ||||
addChatMessage({ | addChatMessage({ | ||||
"text": "/special " + sprintf(txt, { "nick": msg.text }), | "text": "/special " + sprintf(txt, { "nick": escapeText(msg.text) }), | ||||
"time": msg.time, | "time": msg.time, | ||||
"isSpecial": true | "isSpecial": true | ||||
}); | }); | ||||
if (g_SelectedPlayer == msg.text) | if (g_SelectedPlayer == msg.text) | ||||
updateUserRoleText(g_SelectedPlayer); | updateUserRoleText(g_SelectedPlayer); | ||||
}, | }, | ||||
"nick": msg => { | "nick": msg => { | ||||
addChatMessage({ | addChatMessage({ | ||||
"text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { | "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { | ||||
"oldnick": msg.text, | "oldnick": escapeText(msg.text), | ||||
"newnick": msg.data | "newnick": escapeText(msg.data) | ||||
}), | }), | ||||
"time": msg.time, | "time": msg.time, | ||||
"isSpecial": true | "isSpecial": true | ||||
}); | }); | ||||
}, | }, | ||||
"kicked": msg => { | "kicked": msg => { | ||||
handleKick(false, msg.text, msg.data || "", msg.time); | handleKick(false, msg.text, msg.data || "", msg.time); | ||||
}, | }, | ||||
"banned": msg => { | "banned": msg => { | ||||
handleKick(true, msg.text, msg.data || "", msg.time); | handleKick(true, msg.text, msg.data || "", msg.time); | ||||
}, | }, | ||||
"room-message": msg => { | "room-message": msg => { | ||||
addChatMessage({ | addChatMessage({ | ||||
"from": escapeText(msg.from), | "from": escapeText(msg.from), | ||||
"text": escapeText(msg.text), | "text": escapeText(msg.text), | ||||
elexis: These are too early for my taste, would rather have them in the return value of ircFormat, i.e. | |||||
"time": msg.time | "time": msg.time | ||||
}); | }); | ||||
}, | }, | ||||
"private-message": msg => { | "private-message": msg => { | ||||
if (Engine.LobbyGetPlayerRole(msg.from) == "moderator") | if (Engine.LobbyGetPlayerRole(msg.from) == "moderator") | ||||
// some XMPP clients send trailing whitespace | // some XMPP clients send trailing whitespace | ||||
addChatMessage({ | addChatMessage({ | ||||
"from": escapeText(msg.from), | "from": escapeText(msg.from), | ||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | banned ? | ||||
translate("You have been banned from the lobby!") : | translate("You have been banned from the lobby!") : | ||||
translate("You have been kicked from the lobby!") : | translate("You have been kicked from the lobby!") : | ||||
banned ? | banned ? | ||||
translate("%(nick)s has been banned from the lobby.") : | translate("%(nick)s has been banned from the lobby.") : | ||||
translate("%(nick)s has been kicked from the lobby."); | translate("%(nick)s has been kicked from the lobby."); | ||||
if (reason) | if (reason) | ||||
reason = sprintf(translateWithContext("lobby kick", "Reason: %(reason)s"), { | reason = sprintf(translateWithContext("lobby kick", "Reason: %(reason)s"), { | ||||
"reason": reason | "reason": escapeText(reason) | ||||
}); | }); | ||||
if (nick != g_Username) | if (nick != g_Username) | ||||
{ | { | ||||
addChatMessage({ | addChatMessage({ | ||||
"text": "/special " + sprintf(kickString, { "nick": nick }) + " " + reason, | "text": "/special " + sprintf(kickString, { "nick": escapeText(nick) }) + " " + reason, | ||||
"time": time, | "time": time, | ||||
"isSpecial": true | "isSpecial": true | ||||
}); | }); | ||||
return; | return; | ||||
} | } | ||||
addChatMessage({ | addChatMessage({ | ||||
"from": "system", | "from": "system", | ||||
Show All 14 Lines | |||||
/** | /** | ||||
* Update the subject GUI object. | * Update the subject GUI object. | ||||
* | * | ||||
* @param {string} newSubject | * @param {string} newSubject | ||||
*/ | */ | ||||
function updateSubject(newSubject) | function updateSubject(newSubject) | ||||
{ | { | ||||
// Do not escapeText so that moderators can use fonts and colors | |||||
elexisAuthorUnsubmitted Done Inline ActionsMore likely that moderators will be unaware of color parsing and then use [foo] and thus not conveying the kick reason to the subject, so better escapeText the reason too. elexis: More likely that moderators will be unaware of color parsing and then use [foo] and thus not… | |||||
Engine.GetGUIObjectByName("subject").caption = newSubject; | Engine.GetGUIObjectByName("subject").caption = newSubject; | ||||
// If the subject is only whitespace, hide it and reposition the logo. | // If the subject is only whitespace, hide it and reposition the logo. | ||||
let subjectBox = Engine.GetGUIObjectByName("subjectBox"); | let subjectBox = Engine.GetGUIObjectByName("subjectBox"); | ||||
subjectBox.hidden = !newSubject.trim(); | subjectBox.hidden = !newSubject.trim(); | ||||
let logo = Engine.GetGUIObjectByName("logo"); | let logo = Engine.GetGUIObjectByName("logo"); | ||||
if (subjectBox.hidden) | if (subjectBox.hidden) | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | if (player.rating && player.name == g_Username) | ||||
g_UserRating = player.rating; | g_UserRating = player.rating; | ||||
let rating = player.rating ? (" " + player.rating).substr(-5) : " -"; | let rating = player.rating ? (" " + player.rating).substr(-5) : " -"; | ||||
let presence = g_PlayerStatuses[player.presence] ? player.presence : "unknown"; | let presence = g_PlayerStatuses[player.presence] ? player.presence : "unknown"; | ||||
if (presence == "unknown") | if (presence == "unknown") | ||||
warn("Unknown presence:" + player.presence); | warn("Unknown presence:" + player.presence); | ||||
let statusColor = g_PlayerStatuses[presence].color; | let statusColor = g_PlayerStatuses[presence].color; | ||||
let coloredName = colorPlayerName((player.role == "moderator" ? g_ModeratorPrefix : "") + player.name); | let coloredName = colorPlayerName((player.role == "moderator" ? g_ModeratorPrefix : "") + escapeText(player.name)); | ||||
let coloredPresence = '[color="' + statusColor + '"]' + g_PlayerStatuses[presence].status + "[/color]"; | let coloredPresence = '[color="' + statusColor + '"]' + g_PlayerStatuses[presence].status + "[/color]"; | ||||
let coloredRating = '[color="' + statusColor + '"]' + rating + "[/color]"; | let coloredRating = '[color="' + statusColor + '"]' + rating + "[/color]"; | ||||
buddyStatusList.push(player.isBuddy ? '[color="' + statusColor + '"]' + g_BuddySymbol + '[/color]' : ""); | buddyStatusList.push(player.isBuddy ? '[color="' + statusColor + '"]' + g_BuddySymbol + '[/color]' : ""); | ||||
playerList.push(coloredName); | playerList.push(coloredName); | ||||
presenceList.push(coloredPresence); | presenceList.push(coloredPresence); | ||||
ratingList.push(coloredRating); | ratingList.push(coloredRating); | ||||
nickList.push(player.name); | nickList.push(player.name); | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | function lookupSelectedUserProfile(guiObjectName) | ||||
let playerName = playerList.list[playerList.selected]; | let playerName = playerList.list[playerList.selected]; | ||||
Engine.GetGUIObjectByName("profileArea").hidden = !playerName && !Engine.GetGUIObjectByName("usernameText").caption; | Engine.GetGUIObjectByName("profileArea").hidden = !playerName && !Engine.GetGUIObjectByName("usernameText").caption; | ||||
if (!playerName) | if (!playerName) | ||||
return; | return; | ||||
Engine.SendGetProfile(playerName); | Engine.SendGetProfile(playerName); | ||||
Engine.GetGUIObjectByName("usernameText").caption = playerName; | Engine.GetGUIObjectByName("usernameText").caption = escapeText(playerName); | ||||
Engine.GetGUIObjectByName("rankText").caption = translate("N/A"); | Engine.GetGUIObjectByName("rankText").caption = translate("N/A"); | ||||
Engine.GetGUIObjectByName("highestRatingText").caption = translate("N/A"); | Engine.GetGUIObjectByName("highestRatingText").caption = translate("N/A"); | ||||
Engine.GetGUIObjectByName("totalGamesText").caption = translate("N/A"); | Engine.GetGUIObjectByName("totalGamesText").caption = translate("N/A"); | ||||
Engine.GetGUIObjectByName("winsText").caption = translate("N/A"); | Engine.GetGUIObjectByName("winsText").caption = translate("N/A"); | ||||
Engine.GetGUIObjectByName("lossesText").caption = translate("N/A"); | Engine.GetGUIObjectByName("lossesText").caption = translate("N/A"); | ||||
Engine.GetGUIObjectByName("ratioText").caption = translate("N/A"); | Engine.GetGUIObjectByName("ratioText").caption = translate("N/A"); | ||||
updateUserRoleText(playerName); | updateUserRoleText(playerName); | ||||
} | } | ||||
function updateUserRoleText(playerName) | function updateUserRoleText(playerName) | ||||
{ | { | ||||
Engine.GetGUIObjectByName("roleText").caption = g_RoleNames[Engine.LobbyGetPlayerRole(playerName) || "participant"]; | Engine.GetGUIObjectByName("roleText").caption = g_RoleNames[Engine.LobbyGetPlayerRole(playerName) || "participant"]; | ||||
} | } | ||||
/** | /** | ||||
* Update the profile of the selected player with data from the bot. | * Update the profile of the selected player with data from the bot. | ||||
*/ | */ | ||||
function updateProfile() | function updateProfile() | ||||
{ | { | ||||
let attributes = Engine.GetProfile()[0]; | let attributes = Engine.GetProfile()[0]; | ||||
let user = colorPlayerName(attributes.player, attributes.rating); | let user = colorPlayerName(escapeText(attributes.player), attributes.rating); | ||||
if (!Engine.GetGUIObjectByName("profileFetch").hidden) | if (!Engine.GetGUIObjectByName("profileFetch").hidden) | ||||
{ | { | ||||
let profileFound = attributes.rating != "-2"; | let profileFound = attributes.rating != "-2"; | ||||
Engine.GetGUIObjectByName("profileWindowArea").hidden = !profileFound; | Engine.GetGUIObjectByName("profileWindowArea").hidden = !profileFound; | ||||
Engine.GetGUIObjectByName("profileErrorText").hidden = profileFound; | Engine.GetGUIObjectByName("profileErrorText").hidden = profileFound; | ||||
if (!profileFound) | if (!profileFound) | ||||
{ | { | ||||
Engine.GetGUIObjectByName("profileErrorText").caption = sprintf( | Engine.GetGUIObjectByName("profileErrorText").caption = sprintf( | ||||
translate("Player \"%(nick)s\" not found."), | translate("Player \"%(nick)s\" not found."), | ||||
{ "nick": attributes.player } | { "nick": escapeText(attributes.player) } | ||||
); | ); | ||||
return; | return; | ||||
} | } | ||||
Engine.GetGUIObjectByName("profileUsernameText").caption = user; | Engine.GetGUIObjectByName("profileUsernameText").caption = user; | ||||
Engine.GetGUIObjectByName("profileRankText").caption = attributes.rank; | Engine.GetGUIObjectByName("profileRankText").caption = attributes.rank; | ||||
Engine.GetGUIObjectByName("profileHighestRatingText").caption = attributes.highestRating; | Engine.GetGUIObjectByName("profileHighestRatingText").caption = attributes.highestRating; | ||||
Engine.GetGUIObjectByName("profileTotalGamesText").caption = attributes.totalGamesPlayed; | Engine.GetGUIObjectByName("profileTotalGamesText").caption = attributes.totalGamesPlayed; | ||||
Show All 11 Lines | function updateProfile() | ||||
if (attributes.rating == "-2") | if (attributes.rating == "-2") | ||||
return; | return; | ||||
// Make sure the stats we have received coincide with the selected player. | // Make sure the stats we have received coincide with the selected player. | ||||
if (attributes.player != playerList.list[playerList.selected]) | if (attributes.player != playerList.list[playerList.selected]) | ||||
return; | return; | ||||
Engine.GetGUIObjectByName("usernameText").caption = user; | Engine.GetGUIObjectByName("usernameText").caption = user; | ||||
bbUnsubmitted Not Done Inline Actionswhy not here bb: why not here | |||||
elexisAuthorUnsubmitted Not Done Inline Actionscorrect elexis: correct | |||||
Engine.GetGUIObjectByName("rankText").caption = attributes.rank; | Engine.GetGUIObjectByName("rankText").caption = attributes.rank; | ||||
Engine.GetGUIObjectByName("highestRatingText").caption = attributes.highestRating; | Engine.GetGUIObjectByName("highestRatingText").caption = attributes.highestRating; | ||||
Engine.GetGUIObjectByName("totalGamesText").caption = attributes.totalGamesPlayed; | Engine.GetGUIObjectByName("totalGamesText").caption = attributes.totalGamesPlayed; | ||||
Engine.GetGUIObjectByName("winsText").caption = attributes.wins; | Engine.GetGUIObjectByName("winsText").caption = attributes.wins; | ||||
Engine.GetGUIObjectByName("lossesText").caption = attributes.losses; | Engine.GetGUIObjectByName("lossesText").caption = attributes.losses; | ||||
Engine.GetGUIObjectByName("ratioText").caption = formatWinRate(attributes); | Engine.GetGUIObjectByName("ratioText").caption = formatWinRate(attributes); | ||||
} | } | ||||
/** | /** | ||||
* Update the leaderboard from data cached in C++. | * Update the leaderboard from data cached in C++. | ||||
*/ | */ | ||||
function updateLeaderboard() | function updateLeaderboard() | ||||
{ | { | ||||
let leaderboard = Engine.GetGUIObjectByName("leaderboardBox"); | let leaderboard = Engine.GetGUIObjectByName("leaderboardBox"); | ||||
let boardList = Engine.GetBoardList().sort((a, b) => b.rating - a.rating); | let boardList = Engine.GetBoardList().sort((a, b) => b.rating - a.rating); | ||||
let list = []; | let list = []; | ||||
let list_name = []; | let list_name = []; | ||||
let list_rank = []; | let list_rank = []; | ||||
let list_rating = []; | let list_rating = []; | ||||
for (let i in boardList) | for (let i in boardList) | ||||
{ | { | ||||
list_name.push(boardList[i].name); | list_name.push(escapeText(boardList[i].name)); | ||||
list_rating.push(boardList[i].rating); | list_rating.push(boardList[i].rating); | ||||
list_rank.push(+i+1); | list_rank.push(+i+1); | ||||
list.push(boardList[i].name); | list.push(boardList[i].name); | ||||
bbUnsubmitted Not Done Inline Actionsand here bb: and here | |||||
elexisAuthorUnsubmitted Not Done Inline Actionsincorrect, that list uses the nick as an identifier, not to be displayed elexis: incorrect, that list uses the nick as an identifier, not to be displayed | |||||
elexisAuthorUnsubmitted Done Inline ActionsI was incorrect, It's still of CGUIString type and thus will still complain about this not being a proper tag even if not displayed. elexis: I was incorrect, It's still of CGUIString type and thus will still complain about this not… | |||||
} | } | ||||
leaderboard.list_name = list_name; | leaderboard.list_name = list_name; | ||||
leaderboard.list_rating = list_rating; | leaderboard.list_rating = list_rating; | ||||
leaderboard.list_rank = list_rank; | leaderboard.list_rank = list_rank; | ||||
leaderboard.list = list; | leaderboard.list = list; | ||||
if (leaderboard.selected >= leaderboard.list.length) | if (leaderboard.selected >= leaderboard.list.length) | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | for (let i in g_GameList) | ||||
let gameName = escapeText(game.name); | let gameName = escapeText(game.name); | ||||
let mapTypeIdx = g_MapTypes.Name.indexOf(game.mapType); | let mapTypeIdx = g_MapTypes.Name.indexOf(game.mapType); | ||||
if (game.ip == g_SelectedGameIP && game.port == g_SelectedGamePort) | if (game.ip == g_SelectedGameIP && game.port == g_SelectedGamePort) | ||||
selectedGameIndex = +i; | selectedGameIndex = +i; | ||||
list_buddy.push(game.hasBuddies ? '[color="' + g_GameColors[game.state] + '"]' + g_BuddySymbol + '[/color]' : ""); | list_buddy.push(game.hasBuddies ? '[color="' + g_GameColors[game.state] + '"]' + g_BuddySymbol + '[/color]' : ""); | ||||
list_name.push('[color="' + g_GameColors[game.state] + '"]' + gameName); | list_name.push('[color="' + g_GameColors[game.state] + '"]' + gameName); | ||||
list_mapName.push(translateMapTitle(game.niceMapName)); | list_mapName.push(translateMapTitle(game.niceMapName)); | ||||
bbUnsubmitted Not Done Inline Actions(in theory ppl could play on custom maps) bb: (in theory ppl could play on custom maps) | |||||
elexisAuthorUnsubmitted Not Done Inline ActionsFirst I thought you meant that translate could fail and mods should broadcast translations of the mapname. But you meant that missing escapeText of course, correct. elexis: First I thought you meant that translate could fail and mods should broadcast translations of… | |||||
elexisAuthorUnsubmitted Done Inline Actionsand not only a map author making a mistake but a malicious player inserting an wicked game with broken tags. elexis: and not only a map author making a mistake but a malicious player inserting an wicked game with… | |||||
list_mapSize.push(translateMapSize(game.mapSize)); | list_mapSize.push(translateMapSize(game.mapSize)); | ||||
list_mapType.push(g_MapTypes.Title[mapTypeIdx] || ""); | list_mapType.push(g_MapTypes.Title[mapTypeIdx] || ""); | ||||
list_nPlayers.push(game.nbp + "/" + game.maxnbp); | list_nPlayers.push(game.nbp + "/" + game.maxnbp); | ||||
elexisAuthorUnsubmitted Not Done Inline Actionshere too then elexis: here too then | |||||
elexisAuthorUnsubmitted Done Inline ActionsThese values are actually under control of the player, so should escapeText. elexis: These values are actually under control of the player, so should escapeText.
Also sprintf for… | |||||
list_gameRating.push(game.gameRating); | list_gameRating.push(game.gameRating); | ||||
list.push(gameName); | list.push(gameName); | ||||
list_data.push(i); | list_data.push(i); | ||||
} | } | ||||
gamesBox.list_buddy = list_buddy; | gamesBox.list_buddy = list_buddy; | ||||
gamesBox.list_name = list_name; | gamesBox.list_name = list_name; | ||||
gamesBox.list_mapName = list_mapName; | gamesBox.list_mapName = list_mapName; | ||||
▲ Show 20 Lines • Show All 466 Lines • Show Last 20 Lines |
These are too early for my taste, would rather have them in the return value of ircFormat, i.e. in the last call before the displayed string is constructed, so we work with the original data as long as possible (and could for example decide to not colorize under some conditions, such as moderators and combine the escapeText with the gui tags in the same function, making it easier to read and maintain. Also it's easier to memorize if everything works with the original value except the return value.)
But the nickname highlighting conflicts with the implementation and highlighting doesn't work with nicknames that have characters that become escaped.