From 042c9a9334c0bf2dade1ae303f1cbcc1e1f13b2f Mon Sep 17 00:00:00 2001 From: elexis Date: Sun, 13 Mar 2016 16:52:00 +0000 Subject: [PATCH] Allow hosting matches with 8 players and up to 16 observers, fixes #3254. The host can change the number of allowed observers in running games. Make sure that joining observers won't take the "slot" of disconnected players, fixes #3671. Send clients a new disconnect reason "Server full" instead of letting them timeout. This was SVN commit r17881. --- binaries/data/config/default.cfg | 1 + .../data/mods/public/gui/common/network.js | 1 + .../data/mods/public/gui/options/options.json | 6 ++ source/network/NetHost.h | 3 +- source/network/NetMessages.h | 2 +- source/network/NetServer.cpp | 60 +++++++++++++++---- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index e3fc4a42fe..461337d210 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -354,6 +354,7 @@ enabledmods = "mod public" [network] duplicateplayernames = false ; Rename joining player to "User (2)" if "User" is already connected, otherwise prohibit join. lateobserverjoins = false ; Allow observers to join the game after it started +observerlimit = 6 ; Prevent further observer joins in running games if this limit is reached [overlay] fps = "false" ; Show frames per second in top right corner diff --git a/binaries/data/mods/public/gui/common/network.js b/binaries/data/mods/public/gui/common/network.js index bf88125937..ec3f712ebd 100644 --- a/binaries/data/mods/public/gui/common/network.js +++ b/binaries/data/mods/public/gui/common/network.js @@ -58,6 +58,7 @@ function getDisconnectReason(id) case 5: return translate("You have been kicked"); case 6: return translate("You have been banned"); case 7: return translate("Playername in use. If you were disconnected, retry in few seconds"); + case 8: return translate("Server full"); default: warn("Unknown disconnect-reason ID received: " + id); return sprintf(translate("\\[Invalid value %(id)s]"), { "id": id }); diff --git a/binaries/data/mods/public/gui/options/options.json b/binaries/data/mods/public/gui/options/options.json index 3f7487c20b..6c79ca2a69 100644 --- a/binaries/data/mods/public/gui/options/options.json +++ b/binaries/data/mods/public/gui/options/options.json @@ -66,6 +66,12 @@ "label": "Late Observer Joins", "tooltip": "Allow observers to join the game after it started", "parameters": { "config": "network.lateobserverjoins" } + }, + { + "type": "number", + "label": "Observer Limit", + "tooltip": "Prevent further observer from joining if the limit is reached", + "parameters": { "config": "network.observerlimit", "min": 0, "max": 16 } } ], "graphicsSetting": diff --git a/source/network/NetHost.h b/source/network/NetHost.h index 8b72d7dd1b..789efd8a6b 100644 --- a/source/network/NetHost.h +++ b/source/network/NetHost.h @@ -66,7 +66,8 @@ enum NetDisconnectReason NDR_SERVER_ALREADY_IN_GAME, NDR_KICKED, NDR_BANNED, - NDR_PLAYERNAME_IN_USE + NDR_PLAYERNAME_IN_USE, + NDR_SERVER_FULL }; class CNetHost diff --git a/source/network/NetMessages.h b/source/network/NetMessages.h index 6c9d0136a2..7dc678c75f 100644 --- a/source/network/NetMessages.h +++ b/source/network/NetMessages.h @@ -28,7 +28,7 @@ #define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?' #define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!' -#define PS_PROTOCOL_VERSION 0x01010010 // Arbitrary protocol +#define PS_PROTOCOL_VERSION 0x01010011 // Arbitrary protocol #define PS_DEFAULT_PORT 0x5073 // 'P', 's' // Defines the list of message types. The order of the list must not change. diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index 8b5126f92e..8c5925b2fb 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -39,9 +39,16 @@ #include #endif +/** + * Number of peers to allocate for the enet host. + * Limited by ENET_PROTOCOL_MAXIMUM_PEER_ID (4096). + * + * At most 8 players, 16 observers and 1 temporary connection to send the "server full" disconnect-reason. + */ +#define MAX_CLIENTS 25 + #define DEFAULT_SERVER_NAME L"Unnamed Server" #define DEFAULT_WELCOME_MESSAGE L"Welcome" -#define MAX_CLIENTS 10 static const int CHANNEL_COUNT = 1; @@ -929,19 +936,39 @@ bool CNetServerWorker::OnAuthenticate(void* context, CFsmEvent* event) bool observerLateJoin = false; CFG_GET_VAL("network.lateobserverjoins", observerLateJoin); - // If the game has already started, only allow rejoins + int maxObservers = 0; + CFG_GET_VAL("network.observerlimit", maxObservers); + bool isRejoining = false; - if (server.m_State != SERVER_STATE_PREGAME) + bool serverFull = false; + if (server.m_State == SERVER_STATE_PREGAME) { - // Search for an old disconnected player of the same name + // Don't check for maxObservers in the gamesetup, as we don't know yet who will be assigned + serverFull = server.m_Sessions.size() >= MAX_CLIENTS; + } + else + { + isRejoining = observerLateJoin; + bool isObserver = true; + int disconnectedPlayers = 0; + int connectedPlayers = 0; // (TODO: if GUIDs were stable, we should use them instead) - isRejoining = - observerLateJoin || - std::find_if( - server.m_PlayerAssignments.begin(), server.m_PlayerAssignments.end(), - [&username] (const std::pair& pair) - { return !pair.second.m_Enabled && pair.second.m_Name == username; }) - != server.m_PlayerAssignments.end(); + for (PlayerAssignmentMap::iterator it = server.m_PlayerAssignments.begin(); it != server.m_PlayerAssignments.end(); ++it) + { + if (!it->second.m_Enabled && it->second.m_Name == username) + { + isObserver = it->second.m_PlayerID == -1; + isRejoining = true; + } + + if (it->second.m_PlayerID == -1) + continue; + + if (it->second.m_Enabled) + ++connectedPlayers; + else + ++disconnectedPlayers; + } // Players who weren't already in the game are not allowed to join now that it's started if (!isRejoining) @@ -950,6 +977,17 @@ bool CNetServerWorker::OnAuthenticate(void* context, CFsmEvent* event) session->Disconnect(NDR_SERVER_ALREADY_IN_GAME); return true; } + + // Ensure all players will be able to rejoin + serverFull = isObserver && ( + (int) server.m_Sessions.size() - connectedPlayers > maxObservers || + (int) server.m_Sessions.size() + disconnectedPlayers >= MAX_CLIENTS); + } + + if (serverFull) + { + session->Disconnect(NDR_SERVER_FULL); + return true; } // TODO: check server password etc?