Ticket #3241: t3241_kick_v5.patch

File t3241_kick_v5.patch, 27.9 KB (added by elexis, 9 years ago)

Introduces the net message CKickedMessage to correctly translate the messages on every client. Notice that it correctly works for non-ascii playernames too. Sanitizes usernames in non-lobby games to remove trailing whitespace. Moves more logic to C++ so that the JS code becomes more cleaner. This way the functionsGetHostGUID and GetPlayerGUID (which were introduced in the previous patch) are not needed anymore (They will have to be introduced in #3270 though). Removes partial username matching. Introduces the dot at the end of the getDisconnectReason string at the right place. Uses emplace_back instead of push_back and few whitespace fixes.

  • 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

     
    495495
    496496    case "chat":
    497497        addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
    498498        break;
    499499
     500    case "kicked":
     501        addChatMessage({ "type": message.ban ? "banned" : "kicked", "username": message.username });
     502        break;
     503
    500504    // Singular client to host message
    501505    case "ready":
    502506        g_ReadyChanged -= 1;
    503507        if (g_ReadyChanged < 1 && g_PlayerAssignments[message.guid].player != -1)
    504508            addChatMessage({ "type": "ready", "guid": message.guid, "ready": +message.status == 1 });
     
    16801684}
    16811685
    16821686function submitChatInput()
    16831687{
    16841688    var input = Engine.GetGUIObjectByName("chatInput");
    1685     var text = input.caption;
    1686     if (text.length)
     1689    var text = input.caption.trim();
     1690    input.caption = "";
     1691
     1692    if (!text.length)
     1693        return;
     1694
     1695    if (text.indexOf("/") == 0)
    16871696    {
    1688         Engine.SendNetworkChat(text);
    1689         input.caption = "";
     1697        let kick = text.indexOf("/kick ") == 0;
     1698        let ban = text.indexOf("/ban ") == 0;
     1699        if (kick || ban)
     1700            Engine.KickPlayer(text.substr(text.indexOf(" ") + 1), ban);
     1701        return;
    16901702    }
     1703
     1704    Engine.SendNetworkChat(text);
    16911705}
    16921706
    16931707function addChatMessage(msg)
    16941708{
    16951709    var username = "";
     
    17291743    case "disconnect":
    17301744        var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    17311745        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]';
    17321746        break;
    17331747
     1748    case "kicked":
     1749        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has been kicked."), { username: username }) }) + '[/font]';
     1750        break;
     1751
     1752    case "banned":
     1753        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has been banned."), { username: username }) }) + '[/font]';
     1754        break;
     1755       
    17341756    case "message":
    17351757        var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    17361758        var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
    17371759        formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message });
    17381760        break;
  • binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml

     
    22
    33<objects>
    44
    55    <script file="gui/common/network.js"/>
    66    <script file="gui/common/functions_global_object.js"/>
     7    <script file="gui/common/functions_utility.js"/>
    78    <script file="gui/gamesetup/gamesetup_mp.js"/>
    89
    910    <!-- Add a translucent black background to fade out the menu page -->
    1011    <object type="image" sprite="ModernFade"/>
    1112
     
    4647            </object>
    4748
    4849            <object hotkey="confirm" type="button" size="50%+5 100%-45 100%-18 100%-17" style="ModernButtonRed">
    4950                <translatableAttribute id="caption">Continue</translatableAttribute>
    5051                <action on="Press">
    51                     var joinPlayerName = Engine.GetGUIObjectByName("joinPlayerName").caption;
     52                    var joinPlayerName = sanitizePlayerName(Engine.GetGUIObjectByName("joinPlayerName").caption, true, true);
    5253                    var joinServer = Engine.GetGUIObjectByName("joinServer").caption;
    5354                    if (startJoin(joinPlayerName, joinServer))
    5455                        switchSetupPage("pageJoin", "pageConnecting");
    5556                </action>
    5657            </object>
     
    8889            </object>
    8990
    9091            <object type="button" size="50%+5 100%-45 100%-18 100%-17" style="ModernButtonRed">
    9192                <translatableAttribute id="caption">Continue</translatableAttribute>
    9293                <action on="Press">
    93                     var hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption;
     94                    var hostPlayerName = sanitizePlayerName(Engine.GetGUIObjectByName("hostPlayerName").caption, true, true);
    9495                    var hostServerName = Engine.GetGUIObjectByName("hostServerName").caption;
    9596                    if (startHost(hostPlayerName, hostServerName))
    9697                        switchSetupPage("pageHost", "pageConnecting");
    9798                </action>
    9899            </object>
  • binaries/data/mods/public/gui/session/messages.js

     
    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                sprintf(translate("Reason: %(reason)s."), { reason: getDisconnectReason(message.reason) }) + "\n" +
     248                translate("The game has ended.");
    247249            obj.hidden = false;
    248250            break;
    249251        default:
    250252            error("Unrecognised netstatus type '" + message.status + "'");
    251253            break;
     
    296298    case "aichat":
    297299        addChatMessage({ "type": "message", "guid": message.guid, "text": message.text, "translate": true });
    298300        break;
    299301
    300302    case "rejoined":
    301         addChatMessage({ "type": "rejoined", "guid": message.guid});
     303        addChatMessage({ "type": "rejoined", "guid": message.guid });
    302304        break;
    303        
     305
     306    case "kicked":
     307        addChatMessage({ "type": message.ban ? "banned" : "kicked", "username": message.username });
     308        break;
     309
    304310    // To prevent errors, ignore these message types that occur during autostart
    305311    case "gamesetup":
    306312    case "start":
    307313        break;
    308314
     
    323329}
    324330
    325331function submitChatInput()
    326332{
    327333    var input = Engine.GetGUIObjectByName("chatInput");
    328     var text = input.caption;
     334    var text = input.caption.trim();
    329335    var isCheat = false;
     336   
    330337    if (text.length)
    331338    {
     339        // Parse cheats
    332340        if (!g_IsObserver && g_Players[Engine.GetPlayerID()].cheatsEnabled)
    333341        {
    334342            for each (var cheat in Object.keys(cheats))
    335343            {
    336344                // Line must start with the cheat.
     
    374382                isCheat = true;
    375383                break;
    376384            }
    377385        }
    378386
     387        // Parse direct commands
     388        if (text.indexOf("/") == 0)
     389        {
     390            if (text == "/list")
     391                addChatMessage({ "type": "clientlist", "guid": "local"});
     392            else
     393            {
     394                let kick = text.indexOf("/kick ") == 0;
     395                let ban = text.indexOf("/ban ") == 0;
     396                if (kick || ban)
     397                    Engine.KickPlayer(text.substr(text.indexOf(" ") + 1), ban);
     398            }
     399        }
    379400        // Observers should only send messages to "/all"
    380         if (!isCheat && (!g_IsObserver || text.indexOf("/") == -1 || text.indexOf("/all ") == 0))
     401        else if (!isCheat && (!g_IsObserver || text.indexOf("/") == -1 || text.indexOf("/all ") == 0))
    381402        {
    382403            if (Engine.GetGUIObjectByName("toggleTeamChat").checked)
    383404                text = "/team " + text;
    384405
    385406            if (g_IsNetworked)
     
    446467        formatted = sprintf(translate("%(player)s has left the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    447468        break;
    448469    case "rejoined":
    449470        formatted = sprintf(translate("%(player)s has rejoined the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    450471        break;
     472    case "kicked":
     473        formatted = sprintf(translate("%(username)s has been kicked."), { "username": msg.username });
     474        break;
     475    case "banned":
     476        formatted = sprintf(translate("%(username)s has been banned."), { "username": msg.username });
     477        break;
    451478    case "defeat":
    452479        // In singleplayer, the local player is "You". "You has" is incorrect.
    453480        if (!g_IsNetworked && msg.player == Engine.GetPlayerID())
    454481            formatted = translate("You have been defeated.");
    455482        else
    456483            formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    457484        break;
     485    case "clientlist":
     486        // List playernames sorted by slot, observers last
     487        let userlist = Object.keys(playerAssignments).sort((guidA, guidB) =>
     488        {
     489            let playerA = playerAssignments[guidA].player;
     490            let playerB = playerAssignments[guidB].player;
     491            return  playerA == -1 || playerB == -1 ? 1 : playerA - playerB;
     492        }).map(guid => playerAssignments[guid].name).join(",");
     493        formatted = sprintf(translate("Users: %(users)s"), { "users": userlist });
     494        break;
    458495    case "diplomacy":
    459496        var message;
    460497        if (msg.player == Engine.GetPlayerID())
    461498        {
    462499            [username, playerColor] = getUsernameAndColor(msg.player1);
  • 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

     
    345345    SAFE_DELETE(g_NetServer);
    346346    SAFE_DELETE(g_NetClient);
    347347    SAFE_DELETE(g_Game);
    348348}
    349349
     350void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), CStrW playerName, bool ban)
     351{
     352    if (g_NetServer)
     353        g_NetServer->KickPlayer(playerName, ban);
     354}
     355
    350356JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate)
    351357{
    352358    if (!g_NetClient)
    353359        return JS::UndefinedValue();
    354360
     
    954960    scriptInterface.RegisterFunction<void, JS::HandleValue, int, &StartGame>("StartGame");
    955961    scriptInterface.RegisterFunction<void, &Script_EndGame>("EndGame");
    956962    scriptInterface.RegisterFunction<void, std::wstring, &StartNetworkHost>("StartNetworkHost");
    957963    scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin");
    958964    scriptInterface.RegisterFunction<void, &DisconnectNetworkGame>("DisconnectNetworkGame");
     965    scriptInterface.RegisterFunction<void, CStrW, bool, &KickPlayer>("KickPlayer");
    959966    scriptInterface.RegisterFunction<JS::Value, &PollNetworkClient>("PollNetworkClient");
    960967    scriptInterface.RegisterFunction<void, JS::HandleValue, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
    961968    scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer");
    962969    scriptInterface.RegisterFunction<void, std::string, int, &SetNetworkPlayerStatus>("SetNetworkPlayerStatus");
    963970    scriptInterface.RegisterFunction<void, &ClearAllPlayerReady>("ClearAllPlayerReady");
  • source/network/NetClient.cpp

     
    9191
    9292    AddTransition(NCS_PREGAME, (uint)NMT_CHAT, NCS_PREGAME, (void*)&OnChat, context);
    9393    AddTransition(NCS_PREGAME, (uint)NMT_READY, NCS_PREGAME, (void*)&OnReady, context);
    9494    AddTransition(NCS_PREGAME, (uint)NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnGameSetup, context);
    9595    AddTransition(NCS_PREGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_PREGAME, (void*)&OnPlayerAssignment, context);
     96    AddTransition(NCS_PREGAME, (uint)NMT_KICKED, NCS_PREGAME, (void*)&OnKicked, context);
    9697    AddTransition(NCS_PREGAME, (uint)NMT_GAME_START, NCS_LOADING, (void*)&OnGameStart, context);
    9798    AddTransition(NCS_PREGAME, (uint)NMT_JOIN_SYNC_START, NCS_JOIN_SYNCING, (void*)&OnJoinSyncStart, context);
    9899
    99100    AddTransition(NCS_JOIN_SYNCING, (uint)NMT_CHAT, NCS_JOIN_SYNCING, (void*)&OnChat, context);
    100101    AddTransition(NCS_JOIN_SYNCING, (uint)NMT_GAME_SETUP, NCS_JOIN_SYNCING, (void*)&OnGameSetup, context);
     
    108109    AddTransition(NCS_LOADING, (uint)NMT_GAME_SETUP, NCS_LOADING, (void*)&OnGameSetup, context);
    109110    AddTransition(NCS_LOADING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_LOADING, (void*)&OnPlayerAssignment, context);
    110111    AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context);
    111112
    112113    AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context);
     114    AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context);
    113115    AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context);
    114116    AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context);
    115117    AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context);
    116118    AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context);
    117119    AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context);
     
    605607    client->PushGuiMessage(msg);
    606608
    607609    return true;
    608610}
    609611
     612bool CNetClient::OnKicked(void *context, CFsmEvent* event)
     613{
     614    ENSURE(event->GetType() == (uint)NMT_KICKED);
     615
     616    CNetClient* client = (CNetClient*)context;
     617    JSContext* cx = client->GetScriptInterface().GetContext();
     618
     619    CKickedMessage* message = (CKickedMessage*)event->GetParamRef();
     620    JS::RootedValue msg(cx);
     621    client->GetScriptInterface().Eval("({'type':'kicked'})", &msg);
     622    client->GetScriptInterface().SetProperty(msg, "username", message->m_Name, false);
     623    client->GetScriptInterface().SetProperty(msg, "ban", message->m_Ban, false);
     624    client->PushGuiMessage(msg);
     625
     626    return true;
     627}
     628
    610629bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event)
    611630{
    612631    ENSURE(event->GetType() == (uint)NMT_LOADED_GAME);
    613632
    614633    CNetClient* client = (CNetClient*)context;
  • source/network/NetClient.h

     
    199199    static bool OnInGame(void* context, CFsmEvent* event);
    200200    static bool OnGameStart(void* context, CFsmEvent* event);
    201201    static bool OnJoinSyncStart(void* context, CFsmEvent* event);
    202202    static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event);
    203203    static bool OnRejoined(void* context, CFsmEvent* event);
     204    static bool OnKicked(void* context, CFsmEvent* event);
    204205    static bool OnLoadedGame(void* context, CFsmEvent* event);
    205206
    206207    /**
    207208     * Take ownership of a session object, and use it for all network communication.
    208209     */
  • 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/NetMessage.cpp

     
    133133
    134134    case NMT_REJOINED:
    135135        pNewMessage = new CRejoinedMessage;
    136136        break;
    137137
     138    case NMT_KICKED:
     139        pNewMessage = new CKickedMessage;
     140        break;
     141
    138142    case NMT_LOADED_GAME:
    139143        pNewMessage = new CLoadedGameMessage;
    140144        break;
    141145
    142146    case NMT_SERVER_HANDSHAKE:
  • source/network/NetMessages.h

     
    5656    NMT_FILE_TRANSFER_ACK,
    5757
    5858    NMT_JOIN_SYNC_START,
    5959
    6060    NMT_REJOINED,
     61    NMT_KICKED,
    6162
    6263    NMT_LOADED_GAME,
    6364    NMT_GAME_START,
    6465    NMT_END_COMMAND_BATCH,
    6566    NMT_SYNC_CHECK, // OOS-detection hash checking
     
    159160
    160161START_NMT_CLASS_(Rejoined, NMT_REJOINED)
    161162    NMT_FIELD(CStr8, m_GUID)
    162163END_NMT_CLASS()
    163164
     165START_NMT_CLASS_(Kicked, NMT_KICKED)
     166    NMT_FIELD(CStrW, m_Name)
     167    NMT_FIELD_INT(m_Ban, bool, 1)
     168END_NMT_CLASS()
     169
    164170START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
    165171    NMT_FIELD_INT(m_CurrentTurn, u32, 4)
    166172END_NMT_CLASS()
    167173
    168174START_NMT_CLASS_(GameStart, NMT_GAME_START)
  • source/network/NetServer.cpp

     
    119119
    120120CNetServerWorker::CNetServerWorker(int autostartPlayers) :
    121121    m_AutostartPlayers(autostartPlayers),
    122122    m_Shutdown(false),
    123123    m_ScriptInterface(NULL),
    124     m_NextHostID(1), m_Host(NULL), m_Stats(NULL)
     124    m_NextHostID(1), m_Host(NULL), m_HostGUID(), m_Stats(NULL)
    125125{
    126126    m_State = SERVER_STATE_UNCONNECTED;
    127127
    128128    m_ServerTurnManager = NULL;
    129129
     
    604604    session->SetFirstState(NSS_HANDSHAKE);
    605605}
    606606
    607607bool CNetServerWorker::HandleConnect(CNetServerSession* session)
    608608{
     609    CNetServerWorker& server = session->GetServer();
     610
     611    // Disconnect banned IPs
     612    if (std::find(server.m_BannedIPs.begin(), server.m_BannedIPs.end(), session->GetIPAddress()) != server.m_BannedIPs.end())
     613    {
     614        session->Disconnect(NDR_BANNED);
     615        return false;
     616    }
     617
     618    // Send handshake challenge
    609619    CSrvHandshakeMessage handshake;
    610620    handshake.m_Magic = PS_PROTOCOL_MAGIC;
    611621    handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION;
    612622    handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION;
    613623    return session->SendMessage(&handshake);
     
    615625
    616626void CNetServerWorker::OnUserJoin(CNetServerSession* session)
    617627{
    618628    AddPlayer(session->GetGUID(), session->GetUserName());
    619629
     630    if (m_HostGUID.empty())
     631        m_HostGUID = session->GetGUID();
     632
    620633    CGameSetupMessage gameSetupMessage(GetScriptInterface());
    621634    gameSetupMessage.m_Data = m_GameAttributes.get();
    622635    session->SendMessage(&gameSetupMessage);
    623636
    624637    CPlayerAssignmentMessage assignMessage;
     
    630643{
    631644    RemovePlayer(session->GetGUID());
    632645
    633646    if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING)
    634647        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?
    638648}
    639649
    640650void CNetServerWorker::AddPlayer(const CStr& guid, const CStrW& name)
    641651{
    642652    // Find all player IDs in active use; we mustn't give them to a second player (excluding the unassigned ID: -1)
     
    707717        it->second.m_Status = 0;
    708718
    709719    SendPlayerAssignments();
    710720}
    711721
     722void CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban)
     723{
     724    // Check if actually a user with that name is connected
     725    bool exists = false;
     726    for (CNetServerSession* session : m_Sessions)
     727        if (session->GetUserName() == playerName)
     728        {
     729            // Prevent the host from kicking him/herself
     730            if (session->GetGUID() == m_HostGUID)
     731                return;
     732            exists = true;
     733            break;
     734        }
     735
     736    if (!exists)
     737        return;
     738
     739    if (ban)
     740    {
     741        // Remember playername
     742        if (std::find(m_BannedPlayers.begin(), m_BannedPlayers.end(), playerName) == m_BannedPlayers.end())
     743            m_BannedPlayers.emplace_back(playerName);
     744
     745        // Remember IP address
     746        CStr ipAddress = GetPlayerIPAddress(playerName);
     747        if (std::find(m_BannedIPs.begin(), m_BannedIPs.end(), ipAddress) == m_BannedIPs.end())
     748            m_BannedIPs.push_back(ipAddress);
     749    }
     750
     751    // Disconnect user
     752    for (CNetServerSession* session : m_Sessions)
     753        if (session->GetUserName() == playerName)
     754        {
     755            session->Disconnect(ban ? NDR_BANNED : NDR_KICKED);
     756            break;
     757        }
     758
     759    // Send message notifying other clients
     760    CKickedMessage kickedMessage;
     761    kickedMessage.m_Name = playerName;
     762    kickedMessage.m_Ban = ban;
     763    Broadcast(&kickedMessage);
     764}
     765
     766CStr CNetServerWorker::GetPlayerIPAddress(const CStrW& playerName)
     767{
     768    for (CNetServerSession* session : m_Sessions)
     769        if (session->GetUserName() == playerName)
     770            return session->GetIPAddress();
     771    return "(error)";
     772}
     773
    712774void CNetServerWorker::AssignPlayer(int playerID, const CStr& guid)
    713775{
    714776    // Remove anyone who's already assigned to this player
    715777    for (PlayerAssignmentMap::iterator it = m_PlayerAssignments.begin(); it != m_PlayerAssignments.end(); ++it)
    716778    {
     
    764826    ENSURE(event->GetType() == (uint)NMT_CLIENT_HANDSHAKE);
    765827
    766828    CNetServerSession* session = (CNetServerSession*)context;
    767829    CNetServerWorker& server = session->GetServer();
    768830
     831    // Check protocol version
    769832    CCliHandshakeMessage* message = (CCliHandshakeMessage*)event->GetParamRef();
    770833    if (message->m_ProtocolVersion != PS_PROTOCOL_VERSION)
    771834    {
    772835        session->Disconnect(NDR_INCORRECT_PROTOCOL_VERSION);
    773836        return false;
    774837    }
    775838
     839    // Send handshake response
    776840    CSrvHandshakeResponseMessage handshakeResponse;
    777841    handshakeResponse.m_UseProtocolVersion = PS_PROTOCOL_VERSION;
    778842    handshakeResponse.m_Message = server.m_WelcomeMessage;
    779843    handshakeResponse.m_Flags = 0;
    780844    session->SendMessage(&handshakeResponse);
     
    791855
    792856    CAuthenticateMessage* message = (CAuthenticateMessage*)event->GetParamRef();
    793857
    794858    CStrW username = server.DeduplicatePlayerName(SanitisePlayerName(message->m_Name));
    795859
     860    // Disconnect banned usernames
     861    if (std::find(server.m_BannedPlayers.begin(), server.m_BannedPlayers.end(), username) != server.m_BannedPlayers.end())
     862    {
     863        session->Disconnect(NDR_BANNED);
     864        return true;
     865    }
     866
    796867    bool isRejoining = false;
    797868
    798869    if (server.m_State != SERVER_STATE_PREGAME)
    799870    {
    800871//      isRejoining = true; // uncomment this to test rejoining even if the player wasn't connected previously
     
    11331204bool CNetServer::SetupConnection()
    11341205{
    11351206    return m_Worker->SetupConnection();
    11361207}
    11371208
     1209void CNetServer::KickPlayer(const CStrW& playerName, const bool ban)
     1210{
     1211    CScopeLock lock(m_Worker->m_WorkerMutex);
     1212    m_Worker->KickPlayer(playerName, ban);
     1213}
     1214
    11381215void CNetServer::AssignPlayer(int playerID, const CStr& guid)
    11391216{
    11401217    CScopeLock lock(m_Worker->m_WorkerMutex);
    11411218    m_Worker->m_AssignPlayerQueue.emplace_back(playerID, guid);
    11421219}
  • source/network/NetServer.h

     
    120120     * The given GUID will be (re)assigned to the given player ID.
    121121     * Any player currently using that ID will be unassigned.
    122122     * The changes will be asynchronously propagated to all clients.
    123123     */
    124124    void AssignPlayer(int playerID, const CStr& guid);
    125    
     125
    126126    /**
    127127     * Call from the GUI to update the player readiness.
    128128     * The changes will be asynchronously propagated to all clients.
    129129     */
    130130    void SetPlayerReady(const CStr& guid, int ready);
     
    132132    /**
    133133     * Call from the GUI to set the all player readiness to 0.
    134134     * The changes will be asynchronously propagated to all clients.
    135135     */
    136136    void ClearAllPlayerReady();
    137    
     137
     138    /**
     139     * Disconnects a player from the gamesetup / session.
     140     */
     141    void KickPlayer(const CStrW& playerName, const bool ban);
     142
    138143    /**
    139144     * Call from the GUI to asynchronously notify all clients that they should start loading the game.
    140145     */
    141146    void StartGame();
    142147
     
    181186     * Send a message to the given network peer.
    182187     */
    183188    bool SendMessage(ENetPeer* peer, const CNetMessage* message);
    184189
    185190    /**
     191     * Disconnected a player from the match / gamesetup and optionally prevents him/her from rejoining.
     192     */
     193    void KickPlayer(const CStrW& playerName, const bool ban);
     194
     195    /**
    186196     * Send a message to all clients who have completed the full connection process
    187197     * (i.e. are in the pre-game or in-game states).
    188198     */
    189199    bool Broadcast(const CNetMessage* message);
    190200
     201    /**
     202     * Returns the IP address of the given connected player.
     203     */
     204    CStr GetPlayerIPAddress(const CStrW& playerName);
     205
    191206private:
    192207    friend class CNetServer;
    193208    friend class CNetFileReceiveTask_ServerRejoin;
    194209
    195210    CNetServerWorker(int autostartPlayers);
     
    243258     */
    244259    void SetTurnLength(u32 msecs);
    245260
    246261    void AddPlayer(const CStr& guid, const CStrW& name);
    247262    void RemovePlayer(const CStr& guid);
    248     void SetPlayerReady(const CStr& guid, const int ready);
     263    void SetPlayerReady(const CStr& guid, const int ready);
     264    CStr GetHostGUID();
    249265    void SendPlayerAssignments();
    250266    void ClearAllPlayerReady();
    251267
    252268    void SetupSession(CNetServerSession* session);
    253269    bool HandleConnect(CNetServerSession* session);
     
    269285
    270286    void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
    271287
    272288    void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session);
    273289
    274 
    275290    /**
    276291     * Internal script context for (de)serializing script messages,
    277292     * and for storing game attributes.
    278293     * (TODO: we shouldn't bother deserializing (except for debug printing of messages),
    279294     * we should just forward messages blindly and efficiently.)
     
    297312    NetServerState m_State;
    298313
    299314    CStrW m_ServerName;
    300315    CStrW m_WelcomeMessage;
    301316
     317    std::vector<CStr> m_BannedIPs;
     318    std::vector<CStrW> m_BannedPlayers;
     319
    302320    u32 m_NextHostID;
    303321
    304322    CNetServerTurnManager* m_ServerTurnManager;
    305323
     324    CStr m_HostGUID;
     325
    306326    /**
    307327     * A copy of all simulation commands received so far, indexed by
    308328     * turn number, to simplify support for rejoining etc.
    309329     * TODO: verify this doesn't use too much RAM.
    310330     */
  • 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
     178CStr CNetServerSession::GetIPAddress()
     179{
     180    char ipAddress[256] = "(error)";
     181    enet_address_get_host_ip(&(m_Peer->address), ipAddress, ARRAY_SIZE(ipAddress));
     182    return CStr(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    CStr 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     */