1
0
forked from 0ad/0ad

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.
This commit is contained in:
elexis 2016-03-13 16:52:00 +00:00
parent dbbc600dc3
commit 042c9a9334
6 changed files with 60 additions and 13 deletions

View File

@ -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

View File

@ -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 });

View File

@ -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":

View File

@ -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

View File

@ -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.

View File

@ -39,9 +39,16 @@
#include <miniupnpc/upnperrors.h>
#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<CStr, PlayerAssignment>& 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?