From 32da740f147f4fa03b8b5676db81cab3414a20d7 Mon Sep 17 00:00:00 2001 From: leper Date: Wed, 11 Nov 2015 02:05:23 +0000 Subject: [PATCH] Allow kicking/banning players from gamesetup and session. Patch by elexis. Fixes #3241. This was SVN commit r17217. --- .../data/mods/public/gui/common/network.js | 77 +++++++++++++++++-- .../mods/public/gui/gamesetup/gamesetup.js | 22 +++++- .../data/mods/public/gui/session/messages.js | 21 ++++- .../data/mods/public/gui/session/session.xml | 1 + source/gui/scripting/ScriptFunctions.cpp | 9 +++ source/network/NetClient.cpp | 20 +++++ source/network/NetClient.h | 1 + source/network/NetHost.h | 4 +- source/network/NetMessage.cpp | 4 + source/network/NetMessages.h | 8 +- source/network/NetServer.cpp | 59 +++++++++++++- source/network/NetServer.h | 17 +++- source/network/NetSession.cpp | 11 ++- source/network/NetSession.h | 4 +- 14 files changed, 245 insertions(+), 13 deletions(-) diff --git a/binaries/data/mods/public/gui/common/network.js b/binaries/data/mods/public/gui/common/network.js index 1de4c0fa22..68d92e8da7 100644 --- a/binaries/data/mods/public/gui/common/network.js +++ b/binaries/data/mods/public/gui/common/network.js @@ -1,3 +1,9 @@ +/** + * Get a human readable version of the given disconnect reason. + * + * @param reason {number} + * @returns {string} + */ function getDisconnectReason(id) { // Must be kept in sync with source/network/NetHost.h @@ -8,15 +14,76 @@ function getDisconnectReason(id) case 2: return translate("Incorrect network protocol version"); case 3: return translate("Game is loading, please try later"); case 4: return translate("Game has already started, no observers allowed"); + case 5: return translate("You have been kicked"); + case 6: return translate("You have been banned"); default: return sprintf(translate("\\[Invalid value %(id)s]"), { "id": id }); } } +/** + * Show the disconnect reason in a message box. + * + * @param {number} reason + */ function reportDisconnect(reason) { - var reasontext = getDisconnectReason(reason); - - messageBox(400, 200, - translate("Lost connection to the server.") + "\n\n" + sprintf(translate("Reason: %(reason)s."), { reason: reasontext }), - translate("Disconnected"), 2); + // Translation: States the reason why the client disconnected from the server. + var reasonText = sprintf(translate("Reason: %(reason)s."), { "reason": getDisconnectReason(reason) }) + messageBox(400, 200, translate("Lost connection to the server.") + "\n\n" + reasonText, translate("Disconnected"), 2); +} + +/** + * Get usernames sorted by player slot, observers last. + * Requires g_PlayerAssignments. + * + * @returns {string[]} + */ +function getUsernameList() +{ + return Object.keys(g_PlayerAssignments).sort((guidA, guidB) => { + + var playerIdA = g_PlayerAssignments[guidA].player; + var playerIdB = g_PlayerAssignments[guidB].player; + + // Sort observers last + if (playerIdA == -1) return +1; + if (playerIdB == -1) return -1; + + // Sort players + return playerIdA - playerIdB; + + }).map(guid => g_PlayerAssignments[guid].name); +} + +/** + * Execute a command locally. Requires addChatMessage. + * + * @param {string} input + * @returns {Boolean} whether a command was executed + */ +function executeNetworkCommand(input) +{ + if (input.indexOf("/") != 0) + return false; + + var command = input.split(" ", 1)[0]; + var argument = input.substr(command.length + 1); + + switch (command) + { + case "/list": + addChatMessage({ "type": "clientlist", "guid": "local" }); + return true; + + case "/kick": + if (!Engine.KickPlayer(argument, false)) + addChatMessage({ "type": "system", "text": sprintf(translate("Could not kick %(name)s."), { "name": argument }) }); + return true; + + case "/ban": + if (!Engine.KickPlayer(argument, true)) + addChatMessage({ "type": "system", "text": sprintf(translate("Could not ban %(name)s."), { "name": argument }) }); + return true; + } + return false; } diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index a8b3517c5a..1cb8e5ceb6 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -508,6 +508,14 @@ function handleNetMessage(message) addChatMessage({ "type": "message", "guid": message.guid, "text": message.text }); break; + case "kicked": + addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been kicked"), { "username": message.username })}); + break; + + case "banned": + addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been banned"), { "username": message.username })}); + break; + // Singular client to host message case "ready": g_ReadyChanged -= 1; @@ -1776,8 +1784,12 @@ function submitChatInput() if (!text.length) return; - Engine.SendNetworkChat(text); input.caption = ""; + + if (executeNetworkCommand(text)) + return; + + Engine.SendNetworkChat(text); } function addChatMessage(msg) @@ -1812,6 +1824,14 @@ function addChatMessage(msg) formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { "message": sprintf(translate("%(username)s has left"), { "username": formattedUsername }) }) + '[/font]'; break; + case "clientlist": + formatted = sprintf(translate("Users: %(users)s"), { "users": getUsernameList().join(translate(", ")) }); + break; + + case "system": + formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { "message": msg.text }) + '[/font]'; + break; + case "message": var formattedUsername = '[color="'+ color +'"]' + username + '[/color]'; var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { "username": formattedUsername }) + '[/font]'; diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index be41e25a37..648a3cb8a9 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -269,7 +269,9 @@ function handleNetMessage(message) break; case "disconnected": g_Disconnected = true; - obj.caption = translate("Connection to the server has been lost.") + "\n\n" + translate("The game has ended."); + // Translation: States the reason why the client disconnected from the server. + let reason = sprintf(translate("Reason: %(reason)s."), { "reason": getDisconnectReason(message.reason) }); + obj.caption = translate("Connection to the server has been lost.") + "\n" + reason + "\n" + translate("The game has ended."); obj.hidden = false; break; default: @@ -325,6 +327,14 @@ function handleNetMessage(message) addChatMessage({ "type": "rejoined", "guid": message.guid }); break; + case "kicked": + addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been kicked"), { "username": message.username })}); + break; + + case "banned": + addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been banned"), { "username": message.username })}); + break; + // To prevent errors, ignore these message types that occur during autostart case "gamesetup": case "start": @@ -359,6 +369,9 @@ function submitChatInput() if (!text.length) return; + if (executeNetworkCommand(text)) + return; + if (executeCheat(text)) return; @@ -420,6 +433,12 @@ function addChatMessage(msg) case "rejoined": formatted = sprintf(translate("%(player)s has rejoined the game."), { "player": "[color=\"" + playerColor + "\"]" + username + "[/color]" }); break; + case "clientlist": + formatted = sprintf(translate("Users: %(users)s"), { "users": getUsernameList().join(translate(", ")) }); + break; + case "system": + formatted = msg.text; + break; case "defeat": // In singleplayer, the local player is "You". "You has" is incorrect. if (!g_IsNetworked && msg.player == Engine.GetPlayerID()) diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml index 9e2bc57e83..67628a4d2b 100644 --- a/binaries/data/mods/public/gui/session/session.xml +++ b/binaries/data/mods/public/gui/session/session.xml @@ -8,6 +8,7 @@