diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 5c1b97b5..2b0b4c4d 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -393,6 +393,10 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 client->disconnectFromServer(); break; } + case Response::RespServerFull: { + QMessageBox::critical(this, tr("Server Full"), tr("The server has reached its maximum user capacity, please check back later.")); + break; + } default: QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast(r)) + tr("\nThis usually means that your client version is out of date, and the server sent a reply your client doesn't understand.")); break; diff --git a/common/pb/response.proto b/common/pb/response.proto index 0c058173..7a97acce 100644 --- a/common/pb/response.proto +++ b/common/pb/response.proto @@ -37,6 +37,7 @@ message Response { RespRegistrationAcceptedNeedsActivation = 33; // Server accepted cient registration, but it will need token activation RespClientIdRequired = 34; // Server requires client to generate and send its client id before allowing access RespClientUpdateRequired = 35; // Client is missing features that the server is requiring + RespServerFull = 36; // Server user limit reached } enum ResponseType { JOIN_ROOM = 1000; diff --git a/common/server.h b/common/server.h index f27ae843..6fd89c1c 100644 --- a/common/server.h +++ b/common/server.h @@ -62,6 +62,7 @@ public: virtual bool getGameShouldPing() const { return false; } virtual bool getClientIdRequired() const { return false; } virtual bool getRegOnlyServer() const { return false; } + virtual bool getmaxUserLimitEnabled() const { return false; } virtual int getPingClockInterval() const { return 0; } virtual int getMaxGameInactivityTime() const { return 9999999; } virtual int getMaxPlayerInactivityTime() const { return 9999999; } @@ -71,6 +72,7 @@ public: virtual int getMaxGamesPerUser() const { return 0; } virtual int getCommandCountingInterval() const { return 0; } virtual int getMaxCommandCountPerInterval() const { return 0; } + virtual int getMaxUserLimit() const { return 9999999; } Server_DatabaseInterface *getDatabaseInterface() const; int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; } @@ -89,6 +91,8 @@ public: void addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); void removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); QList getPersistentPlayerReferences(const QString &userName) const; + int getUsersCount() const; + int getGamesCount() const; private: QMultiMap persistentPlayers; mutable QReadWriteLock persistentPlayersLock; @@ -118,9 +122,6 @@ protected: QMap externalUsers; QMap rooms; QMap databaseInterfaces; - - int getUsersCount() const; - int getGamesCount() const; void addRoom(Server_Room *newRoom); }; diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index f9a3d319..d18791d0 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -383,6 +383,14 @@ Response::ResponseCode Server_ProtocolHandler::cmdPing(const Command_Ping & /*cm Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd, ResponseContainer &rc) { + // limit the number of users that can connect to the server based on configuration settings + if (server->getmaxUserLimitEnabled()) { + if (server->getUsersCount() >= server->getMaxUserLimit()) { + qDebug() << "Max Users Total Limit Reached, please increase the max_users_total setting."; + return Response::RespServerFull; + } + } + QString userName = QString::fromStdString(cmd.user_name()).simplified(); QString clientId = QString::fromStdString(cmd.clientid()).simplified(); QString clientVersion = QString::fromStdString(cmd.clientver()).simplified(); diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 32067065..3a58b3ef 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -234,11 +234,11 @@ bool Servatrice::initServer() qDebug() << "Store Replays: " << settingsCache->value("game/store_replays", true).toBool(); qDebug() << "Client ID Required: " << clientIdRequired; - bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool(); + maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool(); qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled; if (maxUserLimitEnabled){ - int maxUserLimit = settingsCache->value("security/max_users_total", 500).toInt(); + maxUserLimit = settingsCache->value("security/max_users_total", 500).toInt(); qDebug() << "Maximum total user limit: " << maxUserLimit; int maxTcpUserLimit = settingsCache->value("security/max_users_tcp", 500).toInt(); qDebug() << "Maximum tcp user limit: " << maxTcpUserLimit; diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index d40929d4..3cafae83 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -134,14 +134,14 @@ private: int uptime; QMutex txBytesMutex, rxBytesMutex; quint64 txBytes, rxBytes; - int maxGameInactivityTime, maxPlayerInactivityTime; + int maxGameInactivityTime, maxPlayerInactivityTime, maxUserLimit; int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser, commandCountingInterval, maxCommandCountPerInterval, pingClockInterval; QString shutdownReason; int shutdownMinutes; int nextShutdownMessageMinutes; QTimer *shutdownTimer; - bool isFirstShutdownMessage, clientIdRequired, regServerOnly; + bool isFirstShutdownMessage, clientIdRequired, regServerOnly, maxUserLimitEnabled; mutable QMutex serverListMutex; QList serverList; @@ -164,6 +164,7 @@ public: bool getGameShouldPing() const { return true; } bool getClientIdRequired() const { return clientIdRequired; } bool getRegOnlyServer() const { return regServerOnly; } + bool getmaxUserLimitEnabled() const {return maxUserLimitEnabled; } int getPingClockInterval() const { return pingClockInterval; } int getMaxGameInactivityTime() const { return maxGameInactivityTime; } int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; } @@ -174,6 +175,7 @@ public: int getMaxGamesPerUser() const { return maxGamesPerUser; } int getCommandCountingInterval() const { return commandCountingInterval; } int getMaxCommandCountPerInterval() const { return maxCommandCountPerInterval; } + int getMaxUserLimit() const { return maxUserLimit; } AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; } QString getDbPrefix() const { return dbPrefix; } int getServerId() const { return serverId; } diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index fd68bdd3..eeee6d1f 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -94,23 +94,6 @@ bool AbstractServerSocketInterface::initSession() sendProtocolItem(*identSe); delete identSe; - //limit the number of total users based on configuration settings - bool enforceUserLimit = settingsCache->value("security/enable_max_user_limit", false).toBool(); - if (enforceUserLimit){ - int userLimit = settingsCache->value("security/max_users_total", 500).toInt(); - int playerCount = (databaseInterface->getActiveUserCount() + 1); - if (playerCount > userLimit){ - std::cerr << "Max Users Total Limit Reached, please increase the max_users_total setting." << std::endl; - logger->logMessage(QString("Max Users Total Limit Reached, please increase the max_users_total setting."), this); - Event_ConnectionClosed event; - event.set_reason(Event_ConnectionClosed::USER_LIMIT_REACHED); - SessionEvent *se = prepareSessionEvent(event); - sendProtocolItem(*se); - delete se; - return false; - } - } - //allow unlimited number of connections from the trusted sources QString trustedSources = settingsCache->value("security/trusted_sources","127.0.0.1,::1").toString(); if (trustedSources.contains(getAddress(),Qt::CaseInsensitive)) @@ -123,7 +106,6 @@ bool AbstractServerSocketInterface::initSession() SessionEvent *se = prepareSessionEvent(event); sendProtocolItem(*se); delete se; - return false; }