Ticket #3241: t3241_kick_v3.patch
File t3241_kick_v3.patch, 39.1 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/gui/common/network.js
5 5 { 6 6 case 0: return translate("Unknown reason"); 7 7 case 1: return translate("Unexpected shutdown"); 8 8 case 2: return translate("Incorrect network protocol version"); 9 9 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"); 10 12 default: return sprintf(translate("\\[Invalid value %(id)s]"), { id: id }); 11 13 } 12 14 } 13 15 14 16 function reportDisconnect(reason) -
binaries/data/mods/public/gui/gamesetup/gamesetup.js
1695 1695 } 1696 1696 } 1697 1697 1698 1698 function addChatMessage(msg) 1699 1699 { 1700 varusername = "";1700 let username = ""; 1701 1701 if (msg.username) 1702 1702 username = escapeText(msg.username); 1703 1703 else if (msg.guid && g_PlayerAssignments[msg.guid]) 1704 1704 username = escapeText(g_PlayerAssignments[msg.guid].name); 1705 1705 1706 var message = "";1706 let message; 1707 1707 if ("text" in msg && msg.text) 1708 1708 message = escapeText(msg.text); 1709 1709 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 1710 1739 // TODO: Maybe host should have distinct font/color? 1711 varcolor = "white";1740 let color = "white"; 1712 1741 1713 1742 if (msg.guid && g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != -1) 1714 1743 { 1715 1744 // Valid player who has been assigned - get player color 1716 varplayer = g_PlayerAssignments[msg.guid].player - 1;1717 varmapName = g_GameAttributes.map;1718 varmapData = loadMapData(mapName);1719 varmapSettings = (mapData && mapData.settings ? mapData.settings : {});1720 varpData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};1721 varpDefs = 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] : {}; 1722 1751 1723 1752 color = rgbToGuiColor(getSetting(pData, pDefs, "Color")); 1724 1753 } 1725 1754 1726 var formatted;1755 let formatted, formattedUsername, formattedUsernamePrefix; 1727 1756 switch (msg.type) 1728 1757 { 1729 1758 case "connect": 1730 varformattedUsername = '[color="'+ color +'"]' + username + '[/color]';1759 formattedUsername = '[color="'+ color +'"]' + username + '[/color]'; 1731 1760 formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has joined"), { username: formattedUsername }) }) + '[/font]'; 1732 1761 break; 1733 1762 1734 1763 case "disconnect": 1735 varformattedUsername = '[color="'+ color +'"]' + username + '[/color]';1764 formattedUsername = '[color="'+ color +'"]' + username + '[/color]'; 1736 1765 formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]'; 1737 1766 break; 1738 1767 1739 1768 case "message": 1740 varformattedUsername = '[color="'+ color +'"]' + username + '[/color]';1741 varformattedUsernamePrefix = '[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]' 1742 1771 formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message }); 1743 1772 break; 1744 1773 1745 1774 case "ready": 1746 varformattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]'1775 formattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]' 1747 1776 if (msg.ready) 1748 1777 formatted = ' ' + sprintf(translate("* %(username)s is ready!"), { username: formattedUsername }); 1749 1778 else 1750 1779 formatted = ' ' + sprintf(translate("* %(username)s is not ready."), { username: formattedUsername }); 1751 1780 break; -
binaries/data/mods/public/gui/session/messages.js
11 11 var notificationsTimers = []; 12 12 var cheats = getCheatsData(); 13 13 14 14 function getCheatsData() 15 15 { 16 varcheats = {};17 varcheatFileList = getJSONFileList("simulation/data/cheats/");18 for each ( varfileName in cheatFileList)16 let cheats = {}; 17 let cheatFileList = getJSONFileList("simulation/data/cheats/"); 18 for each (let fileName in cheatFileList) 19 19 { 20 varcurrentCheat = Engine.ReadJSONFile("simulation/data/cheats/"+fileName+".json");20 let currentCheat = Engine.ReadJSONFile("simulation/data/cheats/"+fileName+".json"); 21 21 if (!currentCheat) 22 22 continue; 23 23 if (Object.keys(cheats).indexOf(currentCheat.Name) !== -1) 24 24 warn("Cheat name '" + currentCheat.Name + "' is already present"); 25 25 else … … 30 30 31 31 var g_NotificationsTypes = 32 32 { 33 33 "chat": function(notification, player) 34 34 { 35 varmessage = {35 let message = { 36 36 "type": "message", 37 37 "text": notification.message 38 38 } 39 varguid = findGuidForPlayerID(g_PlayerAssignments, player);39 let guid = findGuidForPlayerID(g_PlayerAssignments, player); 40 40 if (guid == undefined) 41 41 { 42 42 message["guid"] = -1; 43 43 message["player"] = player; 44 44 } else { … … 46 46 } 47 47 addChatMessage(message); 48 48 }, 49 49 "aichat": function(notification, player) 50 50 { 51 varmessage = {51 let message = { 52 52 "type": "message", 53 53 "text": notification.message 54 54 } 55 55 message["translate"] = true; 56 56 if ("translateParameters" in notification) … … 64 64 continue; 65 65 let colorName = getUsernameAndColor(+message["parameters"][param]); 66 66 message["parameters"][param] = "[color=\"" + colorName[1] + "\"]" + colorName[0] + "[/color]"; 67 67 } 68 68 } 69 varguid = findGuidForPlayerID(g_PlayerAssignments, player);69 let guid = findGuidForPlayerID(g_PlayerAssignments, player); 70 70 if (guid == undefined) 71 71 { 72 72 message["guid"] = -1; 73 73 message["player"] = player; 74 74 } else { … … 137 137 }; 138 138 139 139 // Notifications 140 140 function handleNotifications() 141 141 { 142 varnotifications = Engine.GuiInterfaceCall("GetNotifications");142 let notifications = Engine.GuiInterfaceCall("GetNotifications"); 143 143 144 for ( varnotification of notifications)144 for (let notification of notifications) 145 145 { 146 146 if (!notification.type) 147 147 { 148 148 error("Notification without type found.\n"+uneval(notification)) 149 149 continue; … … 153 153 { 154 154 error("Notification without players found.\n"+uneval(notification)) 155 155 continue; 156 156 } 157 157 158 varaction = g_NotificationsTypes[notification.type];158 let action = g_NotificationsTypes[notification.type]; 159 159 if (!action) 160 160 { 161 161 error("Unknown notification type '" + notification.type + "' found."); 162 162 continue; 163 163 } 164 164 165 for ( varplayer of notification.players)165 for (let player of notification.players) 166 166 action(notification, player); 167 167 } 168 168 } 169 169 170 170 function updateDiplomacy() … … 176 176 openDiplomacy(); 177 177 } 178 178 179 179 function updateTimeNotifications() 180 180 { 181 varnotifications = Engine.GuiInterfaceCall("GetTimeNotifications");182 varnotificationText = "";183 varplayerID = Engine.GetPlayerID();184 for ( varn of notifications)181 let notifications = Engine.GuiInterfaceCall("GetTimeNotifications"); 182 let notificationText = ""; 183 let playerID = Engine.GetPlayerID(); 184 for (let n of notifications) 185 185 { 186 varmessage = n.message;186 let message = n.message; 187 187 if (n.translateMessage) 188 188 message = translate(message); 189 varparameters = n.parameters || {};189 let parameters = n.parameters || {}; 190 190 if (n.translateParameters) 191 191 translateObjectKeys(parameters, n.translateParameters); 192 192 parameters.time = timeToString(n.endTime - g_SimState.timeElapsed); 193 193 notificationText += sprintf(message, parameters) + "\n"; 194 194 } … … 197 197 198 198 // Returns [username, playercolor] for the given player 199 199 function getUsernameAndColor(player) 200 200 { 201 201 // This case is hit for AIs, whose names don't exist in playerAssignments. 202 varcolor = g_Players[player].color;202 let color = g_Players[player].color; 203 203 return [ 204 204 escapeText(g_Players[player].name), 205 205 color.r + " " + color.g + " " + color.b, 206 206 ]; 207 207 } … … 216 216 case "netstatus": 217 217 // If we lost connection, further netstatus messages are useless 218 218 if (g_Disconnected) 219 219 return; 220 220 221 varobj = Engine.GetGUIObjectByName("netStatus");221 let obj = Engine.GetGUIObjectByName("netStatus"); 222 222 switch (message.status) 223 223 { 224 224 case "waiting_for_players": 225 225 obj.caption = translate("Waiting for other players to connect..."); 226 226 obj.hidden = false; … … 241 241 obj.caption = translate("Connection to the server has been authenticated."); 242 242 obj.hidden = false; 243 243 break; 244 244 case "disconnected": 245 245 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."); 247 250 obj.hidden = false; 248 251 break; 249 252 default: 250 253 error("Unrecognised netstatus type '" + message.status + "'"); 251 254 break; 252 255 } 253 256 break; 254 257 255 258 case "players": 256 259 // Find and report all leavings 257 for ( varhost in g_PlayerAssignments)260 for (let host in g_PlayerAssignments) 258 261 { 259 262 if (!message.hosts[host]) 260 263 { 261 264 // Tell the user about the disconnection 262 265 addChatMessage({ "type": "disconnect", "guid": host }); … … 265 268 updatePlayerDataRemove(g_Players, host); 266 269 } 267 270 } 268 271 269 272 // Find and report all joinings 270 for ( varhost in message.hosts)273 for (let host in message.hosts) 271 274 { 272 275 if (!g_PlayerAssignments[host]) 273 276 { 274 277 // Update the cached player data, so we can display the correct name 275 278 updatePlayerDataAdd(g_Players, host, message.hosts[host]); … … 281 284 282 285 g_PlayerAssignments = message.hosts; 283 286 284 287 if (g_IsController && Engine.HasXmppClient()) 285 288 { 286 varplayers = [ assignment.name for each (assignment in g_PlayerAssignments) ]289 let players = [ assignment.name for each (assignment in g_PlayerAssignments) ] 287 290 Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", ")); 288 291 } 289 292 290 293 break; 291 294 … … 322 325 } 323 326 } 324 327 325 328 function submitChatInput() 326 329 { 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 330 334 if (text.length) 331 335 { 336 // Check cheats 332 337 if (!g_IsObserver && g_Players[Engine.GetPlayerID()].cheatsEnabled) 333 338 { 334 for each ( varcheat in Object.keys(cheats))339 for each (let cheat in Object.keys(cheats)) 335 340 { 336 341 // Line must start with the cheat. 337 342 if (text.indexOf(cheat) !== 0) 338 343 continue; 339 344 340 345 // test for additional parameter which is the rest of the string after the cheat 341 varparameter = "";346 let parameter = ""; 342 347 if (cheats[cheat].DefaultParameter !== undefined) 343 348 { 344 varpar = text.substr(cheat.length);349 let par = text.substr(cheat.length); 345 350 par = par.replace(/^\W+/, '').replace(/\W+$/, ''); // remove whitespaces at start and end 346 351 347 352 // check, if the isNumeric flag is set 348 353 if (cheats[cheat].isNumeric) 349 354 { 350 355 // Match the first word in the substring. 351 varmatch = par.match(/\S+/);356 let match = par.match(/\S+/); 352 357 if (match && match[0]) 353 358 par = Math.floor(match[0]); 354 359 // check, if valid number could be parsed 355 360 if (par <= 0 || isNaN(par)) 356 361 par = ""; … … 374 379 isCheat = true; 375 380 break; 376 381 } 377 382 } 378 383 379 // Observers should only send messages to "/all" 380 if (!isCheat && (!g_IsObserver || text.indexOf("/") == -1 || text.indexOf("/all ") == 0)) 384 if (!isCheat) 381 385 { 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"}); 387 388 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 } 389 398 } 390 399 input.caption = ""; // Clear chat input 391 400 } 392 401 393 402 input.blur(); // Remove focus … … 400 409 // Default to global assignments, but allow overriding for when reporting 401 410 // new players joining 402 411 if (!playerAssignments) 403 412 playerAssignments = g_PlayerAssignments; 404 413 405 varplayerColor, username;414 let playerColor, username; 406 415 407 416 // No context by default. May be set by parseChatCommands(). 408 417 msg.context = ""; 409 418 410 419 if ("guid" in msg && playerAssignments[msg.guid]) 411 420 { 412 varn = playerAssignments[msg.guid].player;421 let n = playerAssignments[msg.guid].player; 413 422 // Observers have an ID of -1 which is not a valid index. 414 423 if (n < 0) 415 424 n = 0; 416 425 playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b; 417 426 username = escapeText(playerAssignments[msg.guid].name); … … 433 442 { 434 443 playerColor = "255 255 255"; 435 444 username = translate("Unknown player"); 436 445 } 437 446 438 var formatted;447 let formatted, message; 439 448 440 449 switch (msg.type) 441 450 { 442 451 case "connect": 443 452 formatted = sprintf(translate("%(player)s is starting to rejoin the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); … … 453 462 if (!g_IsNetworked && msg.player == Engine.GetPlayerID()) 454 463 formatted = translate("You have been defeated."); 455 464 else 456 465 formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); 457 466 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; 458 479 case "diplomacy": 459 var message;460 480 if (msg.player == Engine.GetPlayerID()) 461 481 { 462 482 [username, playerColor] = getUsernameAndColor(msg.player1); 463 483 if (msg.status == "ally") 464 484 message = translate("You are now allied with %(player)s."); … … 516 536 517 537 [username, playerColor] = getUsernameAndColor(msg.attacker); 518 538 // Since livestock can be attacked/gathered by other players, 519 539 // we display a more specific notification in this case to not confuse the player 520 540 if (msg.targetIsDomesticAnimal) 521 varmessage = translate("Your livestock have been attacked by %(attacker)s!");541 message = translate("Your livestock have been attacked by %(attacker)s!"); 522 542 else 523 varmessage = translate("You have been attacked by %(attacker)s!");543 message = translate("You have been attacked by %(attacker)s!"); 524 544 formatted = sprintf(message, { attacker: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); 525 545 break; 526 546 case "message": 527 547 // May have been hidden by the 'team' command. 528 548 if (msg.hide) 529 549 return; 530 550 531 var message;532 551 if ("translate" in msg && msg.translate) 533 552 { 534 553 message = translate(msg.text); // No need to escape, not a user message. 535 554 if ("translateParameters" in msg && msg.translateParameters) 536 555 { 537 varparameters = msg.parameters || {};556 let parameters = msg.parameters || {}; 538 557 translateObjectKeys(parameters, msg.translateParameters); 539 558 message = sprintf(message, parameters); 540 559 } 541 560 } 542 561 else … … 560 579 }); 561 580 } 562 581 } 563 582 else 564 583 { 565 varuserTag = sprintf(translate("<%(user)s>"), { user: username })566 varformattedUserTag = 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]" }) 567 586 if (msg.context !== "") 568 587 { 569 588 formatted = sprintf(translate("(%(context)s) %(userTag)s %(message)s"), { 570 589 context: msg.context, 571 590 userTag: formattedUserTag, … … 605 624 { 606 625 // Only interested in messages that start with '/'. 607 626 if (!msg.text || msg.text[0] != '/') 608 627 return; 609 628 610 varsender;629 let sender; 611 630 if (playerAssignments[msg.guid]) 612 631 sender = playerAssignments[msg.guid].player; 613 632 else 614 633 sender = msg.player; 615 634 616 635 // TODO: It would be nice to display multiple different contexts. 617 636 // 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; 621 640 // Parse commands embedded in the message. 622 641 switch (split[0]) 623 642 { 624 643 case "/all": 625 644 // Resets values that 'team' or 'enemy' may have set. … … 627 646 msg.hide = false; 628 647 recurse = true; 629 648 break; 630 649 case "/team": 631 650 // 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) 633 652 { 634 653 if (g_Players[Engine.GetPlayerID()].team != g_Players[sender].team) 635 654 msg.hide = true; 636 655 else 637 656 msg.context = translate("Team"); … … 641 660 recurse = true; 642 661 break; 643 662 case "/ally": 644 663 case "/allies": 645 664 // 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()]))) 647 666 msg.context = translate("Ally"); 648 667 else 649 668 msg.hide = true; 650 669 651 670 recurse = true; 652 671 break; 653 672 case "/enemy": 654 673 case "/enemies": 655 674 // 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()]))) 657 676 msg.context = translate("Enemy"); 658 677 else 659 678 msg.hide = true; 660 679 661 680 recurse = true; 662 681 break; 663 682 case "/me": 664 683 msg.action = true; 665 684 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; 666 707 case "/msg": 667 vartrimmed = msg.text.substr(split[0].length + 1);668 varmatched = "";708 trimmed = msg.text.substr(split[0].length + 1); 709 matched = ""; 669 710 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 670 718 // Reject names which don't match or are a superset of the intended name. 671 for each ( varplayer in playerAssignments)719 for each (let player in playerAssignments) 672 720 if (trimmed.indexOf(player.name + " ") == 0 && player.name.length > matched.length) 673 721 matched = player.name; 674 722 675 723 // 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 && (m atched == 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)) 678 726 { 679 727 msg.context = translate("Private"); 680 728 msg.text = trimmed.substr(matched.length + 1); 681 729 msg.hide = false; // Might override team message hiding. 682 730 return; … … 712 760 resumeGame(); 713 761 } 714 762 715 763 function openDialog(dialogName, data, player) 716 764 { 717 vardialog = Engine.GetGUIObjectByName(dialogName+"-dialog");765 let dialog = Engine.GetGUIObjectByName(dialogName+"-dialog"); 718 766 if (!dialog) 719 767 { 720 768 warn("messages.js: Unknow dialog with name "+dialogName); 721 769 return; 722 770 } 723 771 dialog.hidden = false; 724 772 725 for ( varobjName in data)773 for (let objName in data) 726 774 { 727 varobj = Engine.GetGUIObjectByName(dialogName + "-dialog-" + objName);775 let obj = Engine.GetGUIObjectByName(dialogName + "-dialog-" + objName); 728 776 if (!obj) 729 777 { 730 778 warn("messages.js: Key '" + objName + "' not found in '" + dialogName + "' dialog."); 731 779 continue; 732 780 } 733 for ( varkey in data[objName])781 for (let key in data[objName]) 734 782 { 735 varn = data[objName][key];783 let n = data[objName][key]; 736 784 if (typeof n == "object" && n.message) 737 785 { 738 varmessage = n.message;786 let message = n.message; 739 787 if (n.translateMessage) 740 788 message = translate(message); 741 varparameters = n.parameters || {};789 let parameters = n.parameters || {}; 742 790 if (n.translateParameters) 743 791 translateObjectKeys(parameters, n.translateParameters); 744 792 obj[key] = sprintf(message, parameters); 745 793 } 746 794 else -
binaries/data/mods/public/gui/session/session.xml
5 5 <script file="gui/common/colorFades.js"/> 6 6 <script file="gui/common/functions_civinfo.js"/> 7 7 <script file="gui/common/functions_global_object.js"/> 8 8 <script file="gui/common/functions_utility.js"/> 9 9 <script file="gui/common/l10n.js"/> 10 <script file="gui/common/network.js"/> 10 11 <script file="gui/common/music.js"/> 11 12 <script file="gui/common/timer.js"/> 12 13 <script file="gui/common/tooltips.js"/> 13 14 <!-- load all scripts in this directory --> 14 15 <script directory="gui/session/"/> -
source/gui/scripting/ScriptFunctions.cpp
186 186 if (g_Game) 187 187 return g_Game->GetPlayerID(); 188 188 return -1; 189 189 } 190 190 191 std::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 191 201 void SetPlayerID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int id) 192 202 { 193 203 if (g_Game) 194 204 g_Game->SetPlayerID(id); 195 205 } … … 345 355 SAFE_DELETE(g_NetServer); 346 356 SAFE_DELETE(g_NetClient); 347 357 SAFE_DELETE(g_Game); 348 358 } 349 359 360 void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring playerName, bool ban) 361 { 362 if (g_NetServer) 363 g_NetServer->KickPlayer(playerName, ban); 364 } 365 350 366 JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate) 351 367 { 352 368 if (!g_NetClient) 353 369 return JS::UndefinedValue(); 354 370 … … 954 970 scriptInterface.RegisterFunction<void, JS::HandleValue, int, &StartGame>("StartGame"); 955 971 scriptInterface.RegisterFunction<void, &Script_EndGame>("EndGame"); 956 972 scriptInterface.RegisterFunction<void, std::wstring, &StartNetworkHost>("StartNetworkHost"); 957 973 scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin"); 958 974 scriptInterface.RegisterFunction<void, &DisconnectNetworkGame>("DisconnectNetworkGame"); 975 scriptInterface.RegisterFunction<void, std::wstring, bool, &KickPlayer>("KickPlayer"); 959 976 scriptInterface.RegisterFunction<JS::Value, &PollNetworkClient>("PollNetworkClient"); 960 977 scriptInterface.RegisterFunction<void, JS::HandleValue, &SetNetworkGameAttributes>("SetNetworkGameAttributes"); 961 978 scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer"); 962 979 scriptInterface.RegisterFunction<void, std::string, int, &SetNetworkPlayerStatus>("SetNetworkPlayerStatus"); 963 980 scriptInterface.RegisterFunction<void, &ClearAllPlayerReady>("ClearAllPlayerReady"); … … 977 994 scriptInterface.RegisterFunction<void, &QuickLoad>("QuickLoad"); 978 995 979 996 // Misc functions 980 997 scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor"); 981 998 scriptInterface.RegisterFunction<int, &GetPlayerID>("GetPlayerID"); 999 scriptInterface.RegisterFunction<std::string, &GetPlayerGUID>("GetPlayerGUID"); 982 1000 scriptInterface.RegisterFunction<void, int, &SetPlayerID>("SetPlayerID"); 983 1001 scriptInterface.RegisterFunction<void, std::string, &OpenURL>("OpenURL"); 984 1002 scriptInterface.RegisterFunction<std::wstring, &GetMatchID>("GetMatchID"); 985 1003 scriptInterface.RegisterFunction<void, &RestartInAtlas>("RestartInAtlas"); 986 1004 scriptInterface.RegisterFunction<bool, &AtlasIsAvailable>("AtlasIsAvailable"); -
source/network/NetClient.cpp
139 139 ENSURE(!m_Session); // must be called before we start the connection 140 140 141 141 m_UserName = username; 142 142 } 143 143 144 CStr CNetClient::GetGUID() 145 { 146 return m_GUID; 147 } 148 144 149 bool CNetClient::SetupConnection(const CStr& server) 145 150 { 146 151 CNetClientSession* session = new CNetClientSession(*this); 147 152 bool ok = session->Connect(PS_DEFAULT_PORT, server); 148 153 SetAndOwnSession(session); -
source/network/NetClient.h
86 86 * Set the user's name that will be displayed to all players. 87 87 * This must not be called after the connection setup. 88 88 */ 89 89 void SetUserName(const CStrW& username); 90 90 91 /** Returns the GUID of the client. */ 92 CStr GetGUID(); 93 91 94 /** 92 95 * Set up a connection to the remote networked server. 93 96 * @param server IP address or host name to connect to 94 97 * @return true on success, false on connection failure 95 98 */ -
source/network/NetHost.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 60 60 enum NetDisconnectReason 61 61 { 62 62 NDR_UNKNOWN = 0, 63 63 NDR_UNEXPECTED_SHUTDOWN, 64 64 NDR_INCORRECT_PROTOCOL_VERSION, 65 NDR_SERVER_ALREADY_IN_GAME 65 NDR_SERVER_ALREADY_IN_GAME, 66 NDR_KICKED, 67 NDR_BANNED 66 68 }; 67 69 68 70 class CNetHost 69 71 { 70 72 public: -
source/network/NetServer.cpp
127 127 128 128 m_ServerTurnManager = NULL; 129 129 130 130 m_ServerName = DEFAULT_SERVER_NAME; 131 131 m_WelcomeMessage = DEFAULT_WELCOME_MESSAGE; 132 m_HostGUID = std::string(""); 132 133 } 133 134 134 135 CNetServerWorker::~CNetServerWorker() 135 136 { 136 137 if (m_State != SERVER_STATE_UNCONNECTED) … … 604 605 session->SetFirstState(NSS_HANDSHAKE); 605 606 } 606 607 607 608 bool CNetServerWorker::HandleConnect(CNetServerSession* session) 608 609 { 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 609 621 CSrvHandshakeMessage handshake; 610 622 handshake.m_Magic = PS_PROTOCOL_MAGIC; 611 623 handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION; 612 624 handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION; 613 625 return session->SendMessage(&handshake); … … 615 627 616 628 void CNetServerWorker::OnUserJoin(CNetServerSession* session) 617 629 { 618 630 AddPlayer(session->GetGUID(), session->GetUserName()); 619 631 632 if (m_HostGUID == "") 633 m_HostGUID = session->GetGUID(); 634 620 635 CGameSetupMessage gameSetupMessage(GetScriptInterface()); 621 636 gameSetupMessage.m_Data = m_GameAttributes.get(); 622 637 session->SendMessage(&gameSetupMessage); 623 638 624 639 CPlayerAssignmentMessage assignMessage; … … 630 645 { 631 646 RemovePlayer(session->GetGUID()); 632 647 633 648 if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING) 634 649 m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers 635 636 // TODO: ought to switch the player controlled by that client637 // back to AI control, or something?638 650 } 639 651 640 652 void CNetServerWorker::AddPlayer(const CStr& guid, const CStrW& name) 641 653 { 642 654 // Find all player IDs in active use; we mustn't give them to a second player (excluding the unassigned ID: -1) … … 707 719 it->second.m_Status = 0; 708 720 709 721 SendPlayerAssignments(); 710 722 } 711 723 724 void 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 746 std::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 757 std::string CNetServerWorker::GetHostGUID() 758 { 759 return m_HostGUID; 760 } 761 712 762 void CNetServerWorker::AssignPlayer(int playerID, const CStr& guid) 713 763 { 714 764 // Remove anyone who's already assigned to this player 715 765 for (PlayerAssignmentMap::iterator it = m_PlayerAssignments.begin(); it != m_PlayerAssignments.end(); ++it) 716 766 { … … 764 814 ENSURE(event->GetType() == (uint)NMT_CLIENT_HANDSHAKE); 765 815 766 816 CNetServerSession* session = (CNetServerSession*)context; 767 817 CNetServerWorker& server = session->GetServer(); 768 818 819 // Check protocol version 769 820 CCliHandshakeMessage* message = (CCliHandshakeMessage*)event->GetParamRef(); 770 821 if (message->m_ProtocolVersion != PS_PROTOCOL_VERSION) 771 822 { 772 823 session->Disconnect(NDR_INCORRECT_PROTOCOL_VERSION); 773 824 return false; 774 825 } 775 826 827 // Send handshake response 776 828 CSrvHandshakeResponseMessage handshakeResponse; 777 829 handshakeResponse.m_UseProtocolVersion = PS_PROTOCOL_VERSION; 778 830 handshakeResponse.m_Message = server.m_WelcomeMessage; 779 831 handshakeResponse.m_Flags = 0; 780 832 session->SendMessage(&handshakeResponse); … … 791 843 792 844 CAuthenticateMessage* message = (CAuthenticateMessage*)event->GetParamRef(); 793 845 794 846 CStrW username = server.DeduplicatePlayerName(SanitisePlayerName(message->m_Name)); 795 847 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 796 855 bool isRejoining = false; 797 856 798 857 if (server.m_State != SERVER_STATE_PREGAME) 799 858 { 800 859 // isRejoining = true; // uncomment this to test rejoining even if the player wasn't connected previously … … 1145 1204 { 1146 1205 CScopeLock lock(m_Worker->m_WorkerMutex); 1147 1206 m_Worker->m_PlayerReadyQueue.push_back(std::make_pair(guid, ready)); 1148 1207 } 1149 1208 1209 void CNetServer::KickPlayer(const CStrW& playerName, bool ban) 1210 { 1211 CScopeLock lock(m_Worker->m_WorkerMutex); 1212 m_Worker->KickPlayer(playerName, ban); 1213 } 1214 1215 std::string CNetServer::GetHostGUID() 1216 { 1217 CScopeLock lock(m_Worker->m_WorkerMutex); 1218 return m_Worker->GetHostGUID(); 1219 } 1220 1150 1221 void CNetServer::ClearAllPlayerReady() 1151 1222 { 1152 1223 CScopeLock lock(m_Worker->m_WorkerMutex); 1153 1224 m_Worker->m_PlayerResetReadyQueue.push_back(false); 1154 1225 } -
source/network/NetServer.h
134 134 * The changes will be asynchronously propagated to all clients. 135 135 */ 136 136 void ClearAllPlayerReady(); 137 137 138 138 /** 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 /** 139 149 * Call from the GUI to asynchronously notify all clients that they should start loading the game. 140 150 */ 141 151 void StartGame(); 142 152 143 153 /** … … 181 191 * Send a message to the given network peer. 182 192 */ 183 193 bool SendMessage(ENetPeer* peer, const CNetMessage* message); 184 194 185 195 /** 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 /** 186 201 * Send a message to all clients who have completed the full connection process 187 202 * (i.e. are in the pre-game or in-game states). 188 203 */ 189 204 bool Broadcast(const CNetMessage* message); 190 205 206 /** 207 * Returns the IP address of the given connected player. 208 */ 209 std::string GetPlayerIPAddress(const CStrW& playerName); 210 191 211 private: 192 212 friend class CNetServer; 193 213 friend class CNetFileReceiveTask_ServerRejoin; 194 214 195 215 CNetServerWorker(int autostartPlayers); … … 244 264 void SetTurnLength(u32 msecs); 245 265 246 266 void AddPlayer(const CStr& guid, const CStrW& name); 247 267 void RemovePlayer(const CStr& guid); 248 268 void SetPlayerReady(const CStr& guid, const int ready); 269 std::string GetHostGUID(); 249 270 void SendPlayerAssignments(); 250 271 void ClearAllPlayerReady(); 251 272 252 273 void SetupSession(CNetServerSession* session); 253 274 bool HandleConnect(CNetServerSession* session); … … 269 290 270 291 void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message); 271 292 272 293 void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session); 273 294 274 275 295 /** 276 296 * Internal script context for (de)serializing script messages, 277 297 * and for storing game attributes. 278 298 * (TODO: we shouldn't bother deserializing (except for debug printing of messages), 279 299 * we should just forward messages blindly and efficiently.) … … 285 305 /** 286 306 * Stores the most current game attributes. 287 307 */ 288 308 DefPersistentRooted<JS::Value> m_GameAttributes; 289 309 310 std::vector<std::string> m_BannedIPs; 311 std::vector<std::wstring> m_BannedPlayers; 312 290 313 int m_AutostartPlayers; 291 314 292 315 ENetHost* m_Host; 293 316 std::vector<CNetServerSession*> m_Sessions; 294 317 … … 301 324 302 325 u32 m_NextHostID; 303 326 304 327 CNetServerTurnManager* m_ServerTurnManager; 305 328 329 std::string m_HostGUID; 330 306 331 /** 307 332 * A copy of all simulation commands received so far, indexed by 308 333 * turn number, to simplify support for rejoining etc. 309 334 * TODO: verify this doesn't use too much RAM. 310 335 */ -
source/network/NetSession.cpp
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 173 173 CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) : 174 174 m_Server(server), m_FileTransferer(this), m_Peer(peer) 175 175 { 176 176 } 177 177 178 std::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 178 185 void CNetServerSession::Disconnect(u32 reason) 179 186 { 180 187 Update((uint)NMT_CONNECTION_LOST, NULL); 181 188 182 189 enet_peer_disconnect(m_Peer, reason); -
source/network/NetSession.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 5 * it under the terms of the GNU General Public License as published by 6 6 * the Free Software Foundation, either version 2 of the License, or … … 121 121 122 122 u32 GetHostID() const { return m_HostID; } 123 123 void SetHostID(u32 id) { m_HostID = id; } 124 124 125 125 /** 126 * Returns the IP address of the client. 127 */ 128 std::string GetIPAddress(); 129 130 /** 126 131 * Sends a disconnection notification to the client, 127 132 * and sends a NMT_CONNECTION_LOST message to the session FSM. 128 133 * The server will receive a disconnection notification after a while. 129 134 * The server will not receive any further messages sent via this session. 130 135 */ -
source/ps/GameSetup/GameSetup.cpp
680 680 } 681 681 682 682 683 683 void EndGame() 684 684 { 685 //TODO: The server should send a message telling the clients that the host quit on purpose 685 686 SAFE_DELETE(g_NetClient); 686 687 SAFE_DELETE(g_NetServer); 687 688 SAFE_DELETE(g_Game); 688 689 689 690 ISoundManager::CloseGame();