Ticket #3241: t3241_kick_v3.patch

File t3241_kick_v3.patch, 39.1 KB (added by elexis, 9 years ago)

Changes var to let for all local variables in messages.js and in the touched gamesetup functions. Fixes whitespace in two empty lines.

  • binaries/data/mods/public/gui/common/network.js

     
    55    {
    66    case 0: return translate("Unknown reason");
    77    case 1: return translate("Unexpected shutdown");
    88    case 2: return translate("Incorrect network protocol version");
    99    case 3: return translate("Game has already started");
     10    case 4: return translate("You have been kicked from the match");
     11    case 5: return translate("You have been banned from the match");
    1012    default: return sprintf(translate("\\[Invalid value %(id)s]"), { id: id });
    1113    }
    1214}
    1315
    1416function reportDisconnect(reason)
  • binaries/data/mods/public/gui/gamesetup/gamesetup.js

     
    16951695    }
    16961696}
    16971697
    16981698function addChatMessage(msg)
    16991699{
    1700     var username = "";
     1700    let username = "";
    17011701    if (msg.username)
    17021702        username = escapeText(msg.username);
    17031703    else if (msg.guid && g_PlayerAssignments[msg.guid])
    17041704        username = escapeText(g_PlayerAssignments[msg.guid].name);
    17051705
    1706     var message = "";
     1706    let message;
    17071707    if ("text" in msg && msg.text)
    17081708        message = escapeText(msg.text);
    17091709
     1710    // Kick / ban players
     1711    if ("text" in msg)
     1712    {
     1713        let split = msg.text.split(/\s/);
     1714        let kick = msg.text.indexOf("/kick") == 0;
     1715        let ban = msg.text.indexOf("/ban") == 0;
     1716        let playerGUID = Engine.GetPlayerGUID();
     1717        if (kick || ban)
     1718        {
     1719            if (!g_IsNetworked || !g_IsController || msg.guid != playerGUID)
     1720                return;
     1721            let trimmed = msg.text.substr(split[0].length + 1);
     1722            let matched = "";
     1723            // Reject names which don't match or are a superset of the intended name.
     1724            for each (let player in g_PlayerAssignments)
     1725                if (trimmed.indexOf(player.name) == 0 && player.name.length > matched.length)
     1726                    matched = player.name;
     1727            // Prevent the host from kicking him/herself
     1728            if (matched.length && matched != g_PlayerAssignments[playerGUID].name)
     1729            {
     1730                // Sending the message takes a while, so we phrase this past tense.
     1731                // The text should be translated for each client individually (see #3304)
     1732                Engine.SendNetworkChat(matched + " has been " + (ban ? "banned" : "kicked") + ".");
     1733                Engine.KickPlayer(matched, ban);
     1734            }
     1735            return;
     1736        }
     1737    }
     1738
    17101739    // TODO: Maybe host should have distinct font/color?
    1711     var color = "white";
     1740    let color = "white";
    17121741
    17131742    if (msg.guid && g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != -1)
    17141743    {
    17151744        // Valid player who has been assigned - get player color
    1716         var player = g_PlayerAssignments[msg.guid].player - 1;
    1717         var mapName = g_GameAttributes.map;
    1718         var mapData = loadMapData(mapName);
    1719         var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
    1720         var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
    1721         var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
     1745        let player = g_PlayerAssignments[msg.guid].player - 1;
     1746        let mapName = g_GameAttributes.map;
     1747        let mapData = loadMapData(mapName);
     1748        let mapSettings = (mapData && mapData.settings ? mapData.settings : {});
     1749        let pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
     1750        let pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
    17221751
    17231752        color = rgbToGuiColor(getSetting(pData, pDefs, "Color"));
    17241753    }
    17251754
    1726     var formatted;
     1755    let formatted, formattedUsername, formattedUsernamePrefix;
    17271756    switch (msg.type)
    17281757    {
    17291758    case "connect":
    1730         var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
     1759        formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    17311760        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has joined"), { username: formattedUsername }) }) + '[/font]';
    17321761        break;
    17331762
    17341763    case "disconnect":
    1735         var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
     1764        formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    17361765        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]';
    17371766        break;
    17381767
    17391768    case "message":
    1740         var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    1741         var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
     1769        formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
     1770        formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
    17421771        formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message });
    17431772        break;
    17441773
    17451774    case "ready":
    1746         var formattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]'
     1775        formattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]'
    17471776        if (msg.ready)
    17481777            formatted = ' ' + sprintf(translate("* %(username)s is ready!"), { username: formattedUsername });
    17491778        else
    17501779            formatted = ' ' + sprintf(translate("* %(username)s is not ready."), { username: formattedUsername });
    17511780        break;
  • binaries/data/mods/public/gui/session/messages.js

     
    1111var notificationsTimers = [];
    1212var cheats = getCheatsData();
    1313
    1414function getCheatsData()
    1515{
    16     var cheats = {};
    17     var cheatFileList = getJSONFileList("simulation/data/cheats/");
    18     for each (var fileName in cheatFileList)
     16    let cheats = {};
     17    let cheatFileList = getJSONFileList("simulation/data/cheats/");
     18    for each (let fileName in cheatFileList)
    1919    {
    20         var currentCheat = Engine.ReadJSONFile("simulation/data/cheats/"+fileName+".json");
     20        let currentCheat = Engine.ReadJSONFile("simulation/data/cheats/"+fileName+".json");
    2121        if (!currentCheat)
    2222            continue;
    2323        if (Object.keys(cheats).indexOf(currentCheat.Name) !== -1)
    2424            warn("Cheat name '" + currentCheat.Name + "' is already present");
    2525        else
     
    3030
    3131var g_NotificationsTypes =
    3232{
    3333    "chat": function(notification, player)
    3434    {
    35         var message = {
     35        let message = {
    3636            "type": "message",
    3737            "text": notification.message
    3838        }
    39         var guid = findGuidForPlayerID(g_PlayerAssignments, player);
     39        let guid = findGuidForPlayerID(g_PlayerAssignments, player);
    4040        if (guid == undefined)
    4141        {
    4242            message["guid"] = -1;
    4343            message["player"] = player;
    4444        } else {
     
    4646        }
    4747        addChatMessage(message);
    4848    },
    4949    "aichat": function(notification, player)
    5050    {
    51         var message = {
     51        let message = {
    5252            "type": "message",
    5353            "text": notification.message
    5454        }
    5555        message["translate"] = true;
    5656        if ("translateParameters" in notification)
     
    6464                    continue;
    6565                let colorName = getUsernameAndColor(+message["parameters"][param]);
    6666                message["parameters"][param] = "[color=\"" + colorName[1] + "\"]" + colorName[0] + "[/color]";
    6767            }
    6868        }
    69         var guid = findGuidForPlayerID(g_PlayerAssignments, player);
     69        let guid = findGuidForPlayerID(g_PlayerAssignments, player);
    7070        if (guid == undefined)
    7171        {
    7272            message["guid"] = -1;
    7373            message["player"] = player;
    7474        } else {
     
    137137};
    138138
    139139// Notifications
    140140function handleNotifications()
    141141{
    142     var notifications = Engine.GuiInterfaceCall("GetNotifications");
     142    let notifications = Engine.GuiInterfaceCall("GetNotifications");
    143143
    144     for (var notification of notifications)
     144    for (let notification of notifications)
    145145    {
    146146        if (!notification.type)
    147147        {
    148148            error("Notification without type found.\n"+uneval(notification))
    149149            continue;
     
    153153        {
    154154            error("Notification without players found.\n"+uneval(notification))
    155155            continue;
    156156        }
    157157       
    158         var action = g_NotificationsTypes[notification.type];
     158        let action = g_NotificationsTypes[notification.type];
    159159        if (!action)
    160160        {
    161161            error("Unknown notification type '" + notification.type + "' found.");
    162162            continue;
    163163        }
    164164       
    165         for (var player of notification.players)
     165        for (let player of notification.players)
    166166            action(notification, player);
    167167    }
    168168}
    169169
    170170function updateDiplomacy()
     
    176176        openDiplomacy();
    177177}
    178178
    179179function updateTimeNotifications()
    180180{
    181     var notifications =  Engine.GuiInterfaceCall("GetTimeNotifications");
    182     var notificationText = "";
    183     var playerID = Engine.GetPlayerID();
    184     for (var n of notifications)
     181    let notifications =  Engine.GuiInterfaceCall("GetTimeNotifications");
     182    let notificationText = "";
     183    let playerID = Engine.GetPlayerID();
     184    for (let n of notifications)
    185185    {
    186         var message = n.message;
     186        let message = n.message;
    187187        if (n.translateMessage)
    188188            message = translate(message);
    189         var parameters = n.parameters || {};
     189        let parameters = n.parameters || {};
    190190        if (n.translateParameters)
    191191            translateObjectKeys(parameters, n.translateParameters);
    192192        parameters.time = timeToString(n.endTime - g_SimState.timeElapsed);
    193193        notificationText += sprintf(message, parameters) + "\n";
    194194    }
     
    197197
    198198// Returns [username, playercolor] for the given player
    199199function getUsernameAndColor(player)
    200200{
    201201    // This case is hit for AIs, whose names don't exist in playerAssignments.
    202     var color = g_Players[player].color;
     202    let color = g_Players[player].color;
    203203    return [
    204204        escapeText(g_Players[player].name),
    205205        color.r + " " + color.g + " " + color.b,
    206206    ];
    207207}
     
    216216    case "netstatus":
    217217        // If we lost connection, further netstatus messages are useless
    218218        if (g_Disconnected)
    219219            return;
    220220
    221         var obj = Engine.GetGUIObjectByName("netStatus");
     221        let obj = Engine.GetGUIObjectByName("netStatus");
    222222        switch (message.status)
    223223        {
    224224        case "waiting_for_players":
    225225            obj.caption = translate("Waiting for other players to connect...");
    226226            obj.hidden = false;
     
    241241            obj.caption = translate("Connection to the server has been authenticated.");
    242242            obj.hidden = false;
    243243            break;
    244244        case "disconnected":
    245245            g_Disconnected = true;
    246             obj.caption = translate("Connection to the server has been lost.") + "\n\n" + translate("The game has ended.");
     246            obj.caption = translate("Connection to the server has been lost.") + "\n" + 
     247                getDisconnectReason(message.reason)
     248            if (!message.reason)
     249                obj.caption += ".\n" + translate("The game has ended.");
    247250            obj.hidden = false;
    248251            break;
    249252        default:
    250253            error("Unrecognised netstatus type '" + message.status + "'");
    251254            break;
    252255        }
    253256        break;
    254257
    255258    case "players":
    256259        // Find and report all leavings
    257         for (var host in g_PlayerAssignments)
     260        for (let host in g_PlayerAssignments)
    258261        {
    259262            if (!message.hosts[host])
    260263            {
    261264                // Tell the user about the disconnection
    262265                addChatMessage({ "type": "disconnect", "guid": host });
     
    265268                updatePlayerDataRemove(g_Players, host);
    266269            }
    267270        }
    268271
    269272        // Find and report all joinings
    270         for (var host in message.hosts)
     273        for (let host in message.hosts)
    271274        {
    272275            if (!g_PlayerAssignments[host])
    273276            {
    274277                // Update the cached player data, so we can display the correct name
    275278                updatePlayerDataAdd(g_Players, host, message.hosts[host]);
     
    281284
    282285        g_PlayerAssignments = message.hosts;
    283286
    284287        if (g_IsController && Engine.HasXmppClient())
    285288        {
    286             var players = [ assignment.name for each (assignment in g_PlayerAssignments) ]
     289            let players = [ assignment.name for each (assignment in g_PlayerAssignments) ]
    287290            Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", "));
    288291        }
    289292
    290293        break;
    291294
     
    322325    }
    323326}
    324327
    325328function submitChatInput()
    326329{
    327     var input = Engine.GetGUIObjectByName("chatInput");
    328     var text = input.caption;
    329     var isCheat = false;
     330    let input = Engine.GetGUIObjectByName("chatInput");
     331    let text = input.caption;
     332    let isCheat = false;
     333
    330334    if (text.length)
    331335    {
     336        // Check cheats
    332337        if (!g_IsObserver && g_Players[Engine.GetPlayerID()].cheatsEnabled)
    333338        {
    334             for each (var cheat in Object.keys(cheats))
     339            for each (let cheat in Object.keys(cheats))
    335340            {
    336341                // Line must start with the cheat.
    337342                if (text.indexOf(cheat) !== 0)
    338343                    continue;
    339344
    340345                // test for additional parameter which is the rest of the string after the cheat
    341                 var parameter = "";
     346                let parameter = "";
    342347                if (cheats[cheat].DefaultParameter !== undefined)
    343348                {
    344                     var par = text.substr(cheat.length);
     349                    let par = text.substr(cheat.length);
    345350                    par = par.replace(/^\W+/, '').replace(/\W+$/, ''); // remove whitespaces at start and end
    346351
    347352                    // check, if the isNumeric flag is set
    348353                    if (cheats[cheat].isNumeric)
    349354                    {
    350355                        // Match the first word in the substring.
    351                         var match = par.match(/\S+/);
     356                        let match = par.match(/\S+/);
    352357                        if (match && match[0])
    353358                            par = Math.floor(match[0]);
    354359                        // check, if valid number could be parsed
    355360                        if (par <= 0 || isNaN(par))
    356361                            par = "";
     
    374379                isCheat = true;
    375380                break;
    376381            }
    377382        }
    378383
    379         // Observers should only send messages to "/all"
    380         if (!isCheat && (!g_IsObserver || text.indexOf("/") == -1 || text.indexOf("/all ") == 0))
     384        if (!isCheat)
    381385        {
    382             if (Engine.GetGUIObjectByName("toggleTeamChat").checked)
    383                 text = "/team " + text;
    384 
    385             if (g_IsNetworked)
    386                 Engine.SendNetworkChat(text);
     386            if (text.trim() == "/list")
     387                addChatMessage({ "type": "clientlist", "guid": "local"});
    387388            else
    388                 addChatMessage({ "type": "message", "guid": "local", "text": text });
     389            {
     390                if (Engine.GetGUIObjectByName("toggleTeamChat").checked)
     391                    text = "/team " + text;
     392
     393                if (g_IsNetworked)
     394                    Engine.SendNetworkChat(text);
     395                else
     396                    addChatMessage({ "type": "message", "guid": "local", "text": text });
     397            }
    389398        }
    390399        input.caption = ""; // Clear chat input
    391400    }
    392401
    393402    input.blur(); // Remove focus
     
    400409    // Default to global assignments, but allow overriding for when reporting
    401410    // new players joining
    402411    if (!playerAssignments)
    403412        playerAssignments = g_PlayerAssignments;
    404413
    405     var playerColor, username;
     414    let playerColor, username;
    406415
    407416    // No context by default. May be set by parseChatCommands().
    408417    msg.context = "";
    409418
    410419    if ("guid" in msg && playerAssignments[msg.guid])
    411420    {
    412         var n = playerAssignments[msg.guid].player;
     421        let n = playerAssignments[msg.guid].player;
    413422        // Observers have an ID of -1 which is not a valid index.
    414423        if (n < 0)
    415424            n = 0;
    416425        playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b;
    417426        username = escapeText(playerAssignments[msg.guid].name);
     
    433442    {
    434443        playerColor = "255 255 255";
    435444        username = translate("Unknown player");
    436445    }
    437446
    438     var formatted;
     447    let formatted, message;
    439448
    440449    switch (msg.type)
    441450    {
    442451    case "connect":
    443452        formatted = sprintf(translate("%(player)s is starting to rejoin the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
     
    453462        if (!g_IsNetworked && msg.player == Engine.GetPlayerID())
    454463            formatted = translate("You have been defeated.");
    455464        else
    456465            formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    457466        break;
     467    case "clientlist":
     468        let userlist = Object.keys(playerAssignments).sort(function(a,b)
     469        {
     470            let playerA = playerAssignments[a].player;
     471            let playerB = playerAssignments[b].player;
     472            if (playerA == -1 || playerB == -1)
     473                return 1;
     474            else
     475                return playerA - playerB;
     476        }).map(guid => playerAssignments[guid].name).join(",");
     477        formatted = sprintf(translate("Users: %(users)s."), { "users": userlist });
     478        break;
    458479    case "diplomacy":
    459         var message;
    460480        if (msg.player == Engine.GetPlayerID())
    461481        {
    462482            [username, playerColor] = getUsernameAndColor(msg.player1);
    463483            if (msg.status == "ally")
    464484                message = translate("You are now allied with %(player)s.");
     
    516536
    517537        [username, playerColor] = getUsernameAndColor(msg.attacker);
    518538        // Since livestock can be attacked/gathered by other players,
    519539        // we display a more specific notification in this case to not confuse the player
    520540        if (msg.targetIsDomesticAnimal)
    521             var message = translate("Your livestock have been attacked by %(attacker)s!");
     541            message = translate("Your livestock have been attacked by %(attacker)s!");
    522542        else
    523             var message = translate("You have been attacked by %(attacker)s!");
     543            message = translate("You have been attacked by %(attacker)s!");
    524544        formatted = sprintf(message, { attacker: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    525545        break;
    526546    case "message":
    527547        // May have been hidden by the 'team' command.
    528548        if (msg.hide)
    529549            return;
    530550
    531         var message;
    532551        if ("translate" in msg && msg.translate)
    533552        {
    534553            message = translate(msg.text); // No need to escape, not a user message.
    535554            if ("translateParameters" in msg && msg.translateParameters)
    536555            {
    537                 var parameters = msg.parameters || {};
     556                let parameters = msg.parameters || {};
    538557                translateObjectKeys(parameters, msg.translateParameters);
    539558                message = sprintf(message, parameters);
    540559            }
    541560        }
    542561        else
     
    560579                });
    561580            }
    562581        }
    563582        else
    564583        {
    565             var userTag = sprintf(translate("<%(user)s>"), { user: username })
    566             var formattedUserTag = sprintf(translate("<%(user)s>"), { user: "[color=\"" + playerColor + "\"]" + username + "[/color]" })
     584            let userTag = sprintf(translate("<%(user)s>"), { user: username })
     585            let formattedUserTag = sprintf(translate("<%(user)s>"), { user: "[color=\"" + playerColor + "\"]" + username + "[/color]" })
    567586            if (msg.context !== "")
    568587            {
    569588                formatted = sprintf(translate("(%(context)s) %(userTag)s %(message)s"), {
    570589                    context: msg.context,
    571590                    userTag: formattedUserTag,
     
    605624{
    606625    // Only interested in messages that start with '/'.
    607626    if (!msg.text || msg.text[0] != '/')
    608627        return;
    609628
    610     var sender;
     629    let sender;
    611630    if (playerAssignments[msg.guid])
    612631        sender = playerAssignments[msg.guid].player;
    613632    else
    614633        sender = msg.player;
    615634
    616635    // TODO: It would be nice to display multiple different contexts.
    617636    // It should be made clear that only players matching the union of those receive the message.
    618     var recurse = false;
    619     var split = msg.text.split(/\s/);
    620 
     637    let recurse = false, ban = false;
     638    let split = msg.text.split(/\s/)
     639    let trimmed, matched;
    621640    // Parse commands embedded in the message.
    622641    switch (split[0])
    623642    {
    624643    case "/all":
    625644        // Resets values that 'team' or 'enemy' may have set.
     
    627646        msg.hide = false;
    628647        recurse = true;
    629648        break;
    630649    case "/team":
    631650        // Check if we are in a team.
    632         if (g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
     651        if (!g_IsObserver && g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
    633652        {
    634653            if (g_Players[Engine.GetPlayerID()].team != g_Players[sender].team)
    635654                msg.hide = true;
    636655            else
    637656                msg.context = translate("Team");
     
    641660        recurse = true;
    642661        break;
    643662    case "/ally":
    644663    case "/allies":
    645664        // Check if we sent the message, or are the sender's (mutual) ally
    646         if (Engine.GetPlayerID() == sender || (g_Players[sender] && g_Players[sender].isMutualAlly[Engine.GetPlayerID()]))
     665        if (!g_IsObserver && (Engine.GetPlayerID() == sender || (g_Players[sender] && g_Players[sender].isMutualAlly[Engine.GetPlayerID()])))
    647666            msg.context = translate("Ally");
    648667        else
    649668            msg.hide = true;
    650669
    651670        recurse = true;
    652671        break;
    653672    case "/enemy":
    654673    case "/enemies":
    655674        // Check if we sent the message, or are the sender's enemy
    656         if (Engine.GetPlayerID() == sender || (g_Players[sender] && g_Players[sender].isEnemy[Engine.GetPlayerID()]))
     675        if (!g_IsObserver && (Engine.GetPlayerID() == sender || (g_Players[sender] && g_Players[sender].isEnemy[Engine.GetPlayerID()])))
    657676            msg.context = translate("Enemy");
    658677        else
    659678            msg.hide = true;
    660679
    661680        recurse = true;
    662681        break;
    663682    case "/me":
    664683        msg.action = true;
    665684        break;
     685    case "/ban":
     686        ban = true;
     687    case "/kick":
     688        msg.hide = true;
     689        if (!g_IsNetworked || !g_IsController || msg.guid != Engine.GetPlayerGUID())
     690            break;
     691        trimmed = msg.text.substr(split[0].length + 1);
     692        matched = "";
     693        let playerGUID = Engine.GetPlayerGUID();
     694        // Reject names which don't match or are a superset of the intended name.
     695        for each (let player in playerAssignments)
     696            if (trimmed.indexOf(player.name) == 0 && player.name.length > matched.length)
     697                matched = player.name;
     698        // Prevent the host from kicking him/herself
     699        if (matched.length && matched != g_PlayerAssignments[playerGUID].name)
     700        {
     701            Engine.KickPlayer(matched, ban);
     702            // Sending the message takes a while, so we phrase this past tense.
     703            // The text should be translated for each client individually (see #3304)
     704            submitChatDirectly(matched + " has been " + (ban ? "banned" : "kicked") + ".");
     705        }
     706        return;
    666707    case "/msg":
    667         var trimmed = msg.text.substr(split[0].length + 1);
    668         var matched = "";
     708        trimmed = msg.text.substr(split[0].length + 1);
     709        matched = "";
    669710
     711        // Don't show private messages from observers to players
     712        if (g_PlayerAssignments[msg.guid].player == -1 && g_PlayerAssignments[Engine.GetPlayerGUID()].player != -1)
     713        {
     714            msg.hide = true;
     715            return;
     716        }
     717       
    670718        // Reject names which don't match or are a superset of the intended name.
    671         for each (var player in playerAssignments)
     719        for each (let player in playerAssignments)
    672720            if (trimmed.indexOf(player.name + " ") == 0 && player.name.length > matched.length)
    673721                matched = player.name;
    674722
    675723        // If the local player's name was the longest one matched, show the message.
    676         var playerName = g_Players[Engine.GetPlayerID()].name;
    677         if (matched.length && (matched == playerName || sender == Engine.GetPlayerID()))
     724        // Show the message only for sender and recipient.
     725        if (matched.length && (msg.guid == Engine.GetPlayerGUID() || matched == g_PlayerAssignments[Engine.GetPlayerGUID()].name))
    678726        {
    679727            msg.context = translate("Private");
    680728            msg.text = trimmed.substr(matched.length + 1);
    681729            msg.hide = false; // Might override team message hiding.
    682730            return;
     
    712760    resumeGame();
    713761}
    714762
    715763function openDialog(dialogName, data, player)
    716764{
    717     var dialog = Engine.GetGUIObjectByName(dialogName+"-dialog");
     765    let dialog = Engine.GetGUIObjectByName(dialogName+"-dialog");
    718766    if (!dialog)
    719767    {
    720768        warn("messages.js: Unknow dialog with name "+dialogName);
    721769        return;
    722770    }
    723771    dialog.hidden = false;
    724772
    725     for (var objName in data)
     773    for (let objName in data)
    726774    {
    727         var obj = Engine.GetGUIObjectByName(dialogName + "-dialog-" + objName);
     775        let obj = Engine.GetGUIObjectByName(dialogName + "-dialog-" + objName);
    728776        if (!obj)
    729777        {
    730778            warn("messages.js: Key '" + objName + "' not found in '" + dialogName + "' dialog.");
    731779            continue;
    732780        }
    733         for (var key in data[objName])
     781        for (let key in data[objName])
    734782        {
    735             var n = data[objName][key];
     783            let n = data[objName][key];
    736784            if (typeof n == "object" && n.message)
    737785            {
    738                 var message = n.message;
     786                let message = n.message;
    739787                if (n.translateMessage)
    740788                    message = translate(message);
    741                 var parameters = n.parameters || {};
     789                let parameters = n.parameters || {};
    742790                if (n.translateParameters)
    743791                    translateObjectKeys(parameters, n.translateParameters);
    744792                obj[key] = sprintf(message, parameters);
    745793            }
    746794            else
  • binaries/data/mods/public/gui/session/session.xml

     
    55<script file="gui/common/colorFades.js"/>
    66<script file="gui/common/functions_civinfo.js"/>
    77<script file="gui/common/functions_global_object.js"/>
    88<script file="gui/common/functions_utility.js"/>
    99<script file="gui/common/l10n.js"/>
     10<script file="gui/common/network.js"/>
    1011<script file="gui/common/music.js"/>
    1112<script file="gui/common/timer.js"/>
    1213<script file="gui/common/tooltips.js"/>
    1314<!-- load all scripts in this directory -->
    1415<script directory="gui/session/"/>
  • source/gui/scripting/ScriptFunctions.cpp

     
    186186    if (g_Game)
    187187        return g_Game->GetPlayerID();
    188188    return -1;
    189189}
    190190
     191std::string GetPlayerGUID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
     192{
     193    if (g_NetServer)
     194        return g_NetServer->GetHostGUID();
     195    else if (g_NetClient)
     196        return g_NetClient->GetGUID();
     197    else
     198        return "";
     199}
     200
    191201void SetPlayerID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int id)
    192202{
    193203    if (g_Game)
    194204        g_Game->SetPlayerID(id);
    195205}
     
    345355    SAFE_DELETE(g_NetServer);
    346356    SAFE_DELETE(g_NetClient);
    347357    SAFE_DELETE(g_Game);
    348358}
    349359
     360void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring playerName, bool ban)
     361{
     362    if (g_NetServer)
     363        g_NetServer->KickPlayer(playerName, ban);
     364}
     365
    350366JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate)
    351367{
    352368    if (!g_NetClient)
    353369        return JS::UndefinedValue();
    354370
     
    954970    scriptInterface.RegisterFunction<void, JS::HandleValue, int, &StartGame>("StartGame");
    955971    scriptInterface.RegisterFunction<void, &Script_EndGame>("EndGame");
    956972    scriptInterface.RegisterFunction<void, std::wstring, &StartNetworkHost>("StartNetworkHost");
    957973    scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin");
    958974    scriptInterface.RegisterFunction<void, &DisconnectNetworkGame>("DisconnectNetworkGame");
     975    scriptInterface.RegisterFunction<void, std::wstring, bool, &KickPlayer>("KickPlayer");
    959976    scriptInterface.RegisterFunction<JS::Value, &PollNetworkClient>("PollNetworkClient");
    960977    scriptInterface.RegisterFunction<void, JS::HandleValue, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
    961978    scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer");
    962979    scriptInterface.RegisterFunction<void, std::string, int, &SetNetworkPlayerStatus>("SetNetworkPlayerStatus");
    963980    scriptInterface.RegisterFunction<void, &ClearAllPlayerReady>("ClearAllPlayerReady");
     
    977994    scriptInterface.RegisterFunction<void, &QuickLoad>("QuickLoad");
    978995
    979996    // Misc functions
    980997    scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");
    981998    scriptInterface.RegisterFunction<int, &GetPlayerID>("GetPlayerID");
     999    scriptInterface.RegisterFunction<std::string, &GetPlayerGUID>("GetPlayerGUID");
    9821000    scriptInterface.RegisterFunction<void, int, &SetPlayerID>("SetPlayerID");
    9831001    scriptInterface.RegisterFunction<void, std::string, &OpenURL>("OpenURL");
    9841002    scriptInterface.RegisterFunction<std::wstring, &GetMatchID>("GetMatchID");
    9851003    scriptInterface.RegisterFunction<void, &RestartInAtlas>("RestartInAtlas");
    9861004    scriptInterface.RegisterFunction<bool, &AtlasIsAvailable>("AtlasIsAvailable");
  • source/network/NetClient.cpp

     
    139139    ENSURE(!m_Session); // must be called before we start the connection
    140140
    141141    m_UserName = username;
    142142}
    143143
     144CStr CNetClient::GetGUID()
     145{
     146    return m_GUID;
     147}
     148
    144149bool CNetClient::SetupConnection(const CStr& server)
    145150{
    146151    CNetClientSession* session = new CNetClientSession(*this);
    147152    bool ok = session->Connect(PS_DEFAULT_PORT, server);
    148153    SetAndOwnSession(session);
  • source/network/NetClient.h

     
    8686     * Set the user's name that will be displayed to all players.
    8787     * This must not be called after the connection setup.
    8888     */
    8989    void SetUserName(const CStrW& username);
    9090
     91    /** Returns the GUID of the client. */
     92    CStr GetGUID();
     93
    9194    /**
    9295     * Set up a connection to the remote networked server.
    9396     * @param server IP address or host name to connect to
    9497     * @return true on success, false on connection failure
    9598     */
  • source/network/NetHost.h

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    6060enum NetDisconnectReason
    6161{
    6262    NDR_UNKNOWN = 0,
    6363    NDR_UNEXPECTED_SHUTDOWN,
    6464    NDR_INCORRECT_PROTOCOL_VERSION,
    65     NDR_SERVER_ALREADY_IN_GAME
     65    NDR_SERVER_ALREADY_IN_GAME,
     66    NDR_KICKED,
     67    NDR_BANNED
    6668};
    6769
    6870class CNetHost
    6971{
    7072public:
  • source/network/NetServer.cpp

     
    127127
    128128    m_ServerTurnManager = NULL;
    129129
    130130    m_ServerName = DEFAULT_SERVER_NAME;
    131131    m_WelcomeMessage = DEFAULT_WELCOME_MESSAGE;
     132    m_HostGUID = std::string("");
    132133}
    133134
    134135CNetServerWorker::~CNetServerWorker()
    135136{
    136137    if (m_State != SERVER_STATE_UNCONNECTED)
     
    604605    session->SetFirstState(NSS_HANDSHAKE);
    605606}
    606607
    607608bool CNetServerWorker::HandleConnect(CNetServerSession* session)
    608609{
     610    CNetServerWorker& server = session->GetServer();
     611
     612    // Disconnect banned IPs
     613    std::string ipAddress = session -> GetIPAddress();
     614    if(std::find(server.m_BannedIPs.begin(), server.m_BannedIPs.end(), ipAddress) != server.m_BannedIPs.end())
     615    {
     616        session->Disconnect(NDR_BANNED);
     617        return false;
     618    }
     619
     620    // Send handshake challenge
    609621    CSrvHandshakeMessage handshake;
    610622    handshake.m_Magic = PS_PROTOCOL_MAGIC;
    611623    handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION;
    612624    handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION;
    613625    return session->SendMessage(&handshake);
     
    615627
    616628void CNetServerWorker::OnUserJoin(CNetServerSession* session)
    617629{
    618630    AddPlayer(session->GetGUID(), session->GetUserName());
    619631
     632    if (m_HostGUID == "")
     633        m_HostGUID = session->GetGUID();
     634
    620635    CGameSetupMessage gameSetupMessage(GetScriptInterface());
    621636    gameSetupMessage.m_Data = m_GameAttributes.get();
    622637    session->SendMessage(&gameSetupMessage);
    623638
    624639    CPlayerAssignmentMessage assignMessage;
     
    630645{
    631646    RemovePlayer(session->GetGUID());
    632647
    633648    if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING)
    634649        m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers
    635 
    636     // TODO: ought to switch the player controlled by that client
    637     // back to AI control, or something?
    638650}
    639651
    640652void CNetServerWorker::AddPlayer(const CStr& guid, const CStrW& name)
    641653{
    642654    // Find all player IDs in active use; we mustn't give them to a second player (excluding the unassigned ID: -1)
     
    707719        it->second.m_Status = 0;
    708720
    709721    SendPlayerAssignments();
    710722}
    711723
     724void CNetServerWorker::KickPlayer(const CStrW& playerName, bool ban)
     725{
     726    if (ban)
     727    {
     728        // Add playername to blacklist
     729        if (std::find(m_BannedPlayers.begin(), m_BannedPlayers.end(), playerName) == m_BannedPlayers.end())
     730            m_BannedPlayers.push_back(playerName);
     731
     732        // Add IP address to blacklist
     733        std::string ipAddress = GetPlayerIPAddress(playerName);
     734        if (std::find(m_BannedIPs.begin(), m_BannedIPs.end(), ipAddress) == m_BannedIPs.end())
     735            m_BannedIPs.push_back(ipAddress);
     736    }
     737
     738    // Disconnect everyone with that nick except the host
     739    for (auto session : m_Sessions)
     740    {
     741        if (session->GetUserName() == playerName && session->GetGUID() != m_HostGUID)
     742            session->Disconnect(ban ? NDR_BANNED : NDR_KICKED);
     743    }
     744}
     745
     746std::string CNetServerWorker::GetPlayerIPAddress(const CStrW& playerName)
     747{
     748    std::string ipAddress = "error";
     749    for (auto session : m_Sessions)
     750    {
     751        if (session->GetUserName() == playerName)
     752            return session->GetIPAddress();
     753    }
     754    return ipAddress;
     755}
     756
     757std::string CNetServerWorker::GetHostGUID()
     758{
     759    return m_HostGUID;
     760}
     761
    712762void CNetServerWorker::AssignPlayer(int playerID, const CStr& guid)
    713763{
    714764    // Remove anyone who's already assigned to this player
    715765    for (PlayerAssignmentMap::iterator it = m_PlayerAssignments.begin(); it != m_PlayerAssignments.end(); ++it)
    716766    {
     
    764814    ENSURE(event->GetType() == (uint)NMT_CLIENT_HANDSHAKE);
    765815
    766816    CNetServerSession* session = (CNetServerSession*)context;
    767817    CNetServerWorker& server = session->GetServer();
    768818
     819    // Check protocol version
    769820    CCliHandshakeMessage* message = (CCliHandshakeMessage*)event->GetParamRef();
    770821    if (message->m_ProtocolVersion != PS_PROTOCOL_VERSION)
    771822    {
    772823        session->Disconnect(NDR_INCORRECT_PROTOCOL_VERSION);
    773824        return false;
    774825    }
    775826
     827    // Send handshake response
    776828    CSrvHandshakeResponseMessage handshakeResponse;
    777829    handshakeResponse.m_UseProtocolVersion = PS_PROTOCOL_VERSION;
    778830    handshakeResponse.m_Message = server.m_WelcomeMessage;
    779831    handshakeResponse.m_Flags = 0;
    780832    session->SendMessage(&handshakeResponse);
     
    791843
    792844    CAuthenticateMessage* message = (CAuthenticateMessage*)event->GetParamRef();
    793845
    794846    CStrW username = server.DeduplicatePlayerName(SanitisePlayerName(message->m_Name));
    795847
     848    // Disconnect banned usernames
     849    if(std::find(server.m_BannedPlayers.begin(), server.m_BannedPlayers.end(), username) != server.m_BannedPlayers.end())
     850    {
     851        session->Disconnect(NDR_BANNED);
     852        return true;
     853    }
     854
    796855    bool isRejoining = false;
    797856
    798857    if (server.m_State != SERVER_STATE_PREGAME)
    799858    {
    800859//      isRejoining = true; // uncomment this to test rejoining even if the player wasn't connected previously
     
    11451204{
    11461205    CScopeLock lock(m_Worker->m_WorkerMutex);
    11471206    m_Worker->m_PlayerReadyQueue.push_back(std::make_pair(guid, ready));
    11481207}
    11491208
     1209void CNetServer::KickPlayer(const CStrW& playerName, bool ban)
     1210{
     1211    CScopeLock lock(m_Worker->m_WorkerMutex);
     1212    m_Worker->KickPlayer(playerName, ban);
     1213}
     1214
     1215std::string CNetServer::GetHostGUID()
     1216{
     1217    CScopeLock lock(m_Worker->m_WorkerMutex);
     1218    return m_Worker->GetHostGUID();
     1219}
     1220
    11501221void CNetServer::ClearAllPlayerReady()
    11511222{
    11521223    CScopeLock lock(m_Worker->m_WorkerMutex);
    11531224    m_Worker->m_PlayerResetReadyQueue.push_back(false);
    11541225}
  • source/network/NetServer.h

     
    134134     * The changes will be asynchronously propagated to all clients.
    135135     */
    136136    void ClearAllPlayerReady();
    137137   
    138138    /**
     139     * Disconnects a player from the gamesetup / session.
     140     */
     141    void KickPlayer(const CStrW& playerName, bool ban);
     142
     143    /**
     144     * Returns the GUID of the host.
     145     */
     146    std::string GetHostGUID();
     147
     148    /**
    139149     * Call from the GUI to asynchronously notify all clients that they should start loading the game.
    140150     */
    141151    void StartGame();
    142152
    143153    /**
     
    181191     * Send a message to the given network peer.
    182192     */
    183193    bool SendMessage(ENetPeer* peer, const CNetMessage* message);
    184194
    185195    /**
     196     * Disconnected a player from the match / gamesetup and optionally prevents him/her from rejoining.
     197     */
     198    void KickPlayer(const CStrW& playerName, bool ban);
     199
     200    /**
    186201     * Send a message to all clients who have completed the full connection process
    187202     * (i.e. are in the pre-game or in-game states).
    188203     */
    189204    bool Broadcast(const CNetMessage* message);
    190205
     206    /**
     207     * Returns the IP address of the given connected player.
     208     */
     209    std::string GetPlayerIPAddress(const CStrW& playerName);
     210
    191211private:
    192212    friend class CNetServer;
    193213    friend class CNetFileReceiveTask_ServerRejoin;
    194214
    195215    CNetServerWorker(int autostartPlayers);
     
    244264    void SetTurnLength(u32 msecs);
    245265
    246266    void AddPlayer(const CStr& guid, const CStrW& name);
    247267    void RemovePlayer(const CStr& guid);
    248268    void SetPlayerReady(const CStr& guid, const int ready);
     269    std::string GetHostGUID();
    249270    void SendPlayerAssignments();
    250271    void ClearAllPlayerReady();
    251272
    252273    void SetupSession(CNetServerSession* session);
    253274    bool HandleConnect(CNetServerSession* session);
     
    269290
    270291    void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
    271292
    272293    void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session);
    273294
    274 
    275295    /**
    276296     * Internal script context for (de)serializing script messages,
    277297     * and for storing game attributes.
    278298     * (TODO: we shouldn't bother deserializing (except for debug printing of messages),
    279299     * we should just forward messages blindly and efficiently.)
     
    285305    /**
    286306     * Stores the most current game attributes.
    287307     */
    288308    DefPersistentRooted<JS::Value> m_GameAttributes;
    289309
     310    std::vector<std::string> m_BannedIPs;
     311    std::vector<std::wstring> m_BannedPlayers;
     312
    290313    int m_AutostartPlayers;
    291314
    292315    ENetHost* m_Host;
    293316    std::vector<CNetServerSession*> m_Sessions;
    294317
     
    301324
    302325    u32 m_NextHostID;
    303326
    304327    CNetServerTurnManager* m_ServerTurnManager;
    305328
     329    std::string m_HostGUID;
     330
    306331    /**
    307332     * A copy of all simulation commands received so far, indexed by
    308333     * turn number, to simplify support for rejoining etc.
    309334     * TODO: verify this doesn't use too much RAM.
    310335     */
  • source/network/NetSession.cpp

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    173173CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
    174174    m_Server(server), m_FileTransferer(this), m_Peer(peer)
    175175{
    176176}
    177177
     178std::string CNetServerSession::GetIPAddress()
     179{
     180    char ipAddress[256] = "(error)";
     181    enet_address_get_host_ip(&(m_Peer->address), ipAddress, ARRAY_SIZE(ipAddress));
     182    return std::string(ipAddress);
     183}
     184
    178185void CNetServerSession::Disconnect(u32 reason)
    179186{
    180187    Update((uint)NMT_CONNECTION_LOST, NULL);
    181188
    182189    enet_peer_disconnect(m_Peer, reason);
  • source/network/NetSession.h

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    121121
    122122    u32 GetHostID() const { return m_HostID; }
    123123    void SetHostID(u32 id) { m_HostID = id; }
    124124
    125125    /**
     126     * Returns the IP address of the client.
     127     */
     128    std::string GetIPAddress();
     129
     130    /**
    126131     * Sends a disconnection notification to the client,
    127132     * and sends a NMT_CONNECTION_LOST message to the session FSM.
    128133     * The server will receive a disconnection notification after a while.
    129134     * The server will not receive any further messages sent via this session.
    130135     */
  • source/ps/GameSetup/GameSetup.cpp

     
    680680}
    681681
    682682
    683683void EndGame()
    684684{
     685    //TODO: The server should send a message telling the clients that the host quit on purpose
    685686    SAFE_DELETE(g_NetClient);
    686687    SAFE_DELETE(g_NetServer);
    687688    SAFE_DELETE(g_Game);
    688689
    689690    ISoundManager::CloseGame();