From 8c7301b19f9b532863e33ce229e62fab5c5add52 Mon Sep 17 00:00:00 2001 From: woogerboy21 Date: Wed, 29 Jul 2015 23:44:00 -0400 Subject: [PATCH] Updated server and client ping to 5 seconds. Also changed hard set 1 second value on the ping timers to read values from the settings cache. --- cockatrice/src/remoteclient.cpp | 37 +++--- cockatrice/src/settingscache.cpp | 9 +- cockatrice/src/settingscache.h | 2 + servatrice/servatrice.ini.example | 18 +-- servatrice/src/servatrice.cpp | 189 +++++++++++++++--------------- 5 files changed, 133 insertions(+), 122 deletions(-) diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 3a597589..260e3986 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -1,7 +1,7 @@ #include #include #include "remoteclient.h" - +#include "settingscache.h" #include "pending_command.h" #include "pb/commands.pb.h" #include "pb/session_commands.pb.h" @@ -16,8 +16,10 @@ static const unsigned int protocolVersion = 14; RemoteClient::RemoteClient(QObject *parent) : AbstractClient(parent), timeRunning(0), lastDataReceived(0), messageInProgress(false), handshakeStarted(false), messageLength(0) { + + int keepalive = settingsCache->getKeepAlive(); timer = new QTimer(this); - timer->setInterval(1000); + timer->setInterval(keepalive * 1000); connect(timer, SIGNAL(timeout()), this, SLOT(ping())); socket = new QTcpSocket(this); @@ -102,11 +104,11 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica void RemoteClient::doLogin() { setStatus(StatusLoggingIn); - + Command_Login cmdLogin; cmdLogin.set_user_name(userName.toStdString()); cmdLogin.set_password(password.toStdString()); - + PendingCommand *pend = prepareSessionCommand(cmdLogin); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(loginResponse(Response))); sendCommand(pend); @@ -123,12 +125,12 @@ void RemoteClient::loginResponse(const Response &response) if (response.response_code() == Response::RespOk) { setStatus(StatusLoggedIn); emit userInfoChanged(resp.user_info()); - + QList buddyList; for (int i = resp.buddy_list_size() - 1; i >= 0; --i) buddyList.append(resp.buddy_list(i)); emit buddyListReceived(buddyList); - + QList ignoreList; for (int i = resp.ignore_list_size() - 1; i >= 0; --i) ignoreList.append(resp.ignore_list(i)); @@ -177,7 +179,7 @@ void RemoteClient::readData() QByteArray data = socket->readAll(); inputBuffer.append(data); - + do { if (!messageInProgress) { if (inputBuffer.size() >= 4) { @@ -202,7 +204,7 @@ void RemoteClient::readData() } if (inputBuffer.size() < messageLength) return; - + ServerMessage newServerMessage; newServerMessage.ParseFromArray(inputBuffer.data(), messageLength); #ifdef QT_DEBUG @@ -210,9 +212,9 @@ void RemoteClient::readData() #endif inputBuffer.remove(0, messageLength); messageInProgress = false; - + processProtocolItem(newServerMessage); - + if (getStatus() == StatusDisconnecting) // use thread-safe getter doDisconnectFromServer(); } while (!inputBuffer.isEmpty()); @@ -231,14 +233,14 @@ void RemoteClient::sendCommandContainer(const CommandContainer &cont) buf.data()[2] = (unsigned char) (size >> 8); buf.data()[1] = (unsigned char) (size >> 16); buf.data()[0] = (unsigned char) (size >> 24); - + socket->write(buf); } void RemoteClient::doConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password) { doDisconnectFromServer(); - + userName = _userName; password = _password; lastHostname = hostname; @@ -251,7 +253,7 @@ void RemoteClient::doConnectToServer(const QString &hostname, unsigned int port, void RemoteClient::doRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname) { doDisconnectFromServer(); - + userName = _userName; password = _password; email = _email; @@ -268,7 +270,7 @@ void RemoteClient::doRegisterToServer(const QString &hostname, unsigned int port void RemoteClient::doActivateToServer(const QString &_token) { doDisconnectFromServer(); - + token = _token; socket->connectToHost(lastHostname, lastPort); @@ -278,7 +280,7 @@ void RemoteClient::doActivateToServer(const QString &_token) void RemoteClient::doDisconnectFromServer() { timer->stop(); - + messageInProgress = false; handshakeStarted = false; messageLength = 0; @@ -289,7 +291,7 @@ void RemoteClient::doDisconnectFromServer() response.set_response_code(Response::RespNotConnected); response.set_cmd_id(pc[i]->getCommandContainer().cmd_id()); pc[i]->processResponse(response); - + delete pc[i]; } pendingCommands.clear(); @@ -309,9 +311,10 @@ void RemoteClient::ping() } } + int keepalive = settingsCache->getKeepAlive(); int maxTime = timeRunning - lastDataReceived; emit maxPingTime(maxTime, maxTimeout); - if (maxTime >= maxTimeout) { + if (maxTime >= (keepalive * maxTimeout)) { disconnectFromServer(); emit serverTimeout(); } else { diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 2ebe6815..9cdf7c8b 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -6,6 +6,7 @@ SettingsCache::SettingsCache() settings = new QSettings(this); lang = settings->value("personal/lang").toString(); + keepalive = settings->value("personal/keepalive", 5).toInt(); deckPath = settings->value("paths/decks").toString(); replaysPath = settings->value("paths/replays").toString(); @@ -429,7 +430,7 @@ QStringList SettingsCache::getCountries() const << "ad" << "ae" << "af" << "ag" << "ai" << "al" << "am" << "ao" << "aq" << "ar" << "as" << "at" << "au" << "aw" << "ax" << "az" << "ba" << "bb" << "bd" << "be" << "bf" << "bg" << "bh" << "bi" << "bj" << "bl" << "bm" << "bn" << "bo" << "bq" - << "br" << "bs" << "bt" << "bv" << "bw" << "by" << "bz" << "ca" << "cc" << "cd" + << "br" << "bs" << "bt" << "bv" << "bw" << "by" << "bz" << "ca" << "cc" << "cd" << "cf" << "cg" << "ch" << "ci" << "ck" << "cl" << "cm" << "cn" << "co" << "cr" << "cu" << "cv" << "cw" << "cx" << "cy" << "cz" << "de" << "dj" << "dk" << "dm" << "do" << "dz" << "ec" << "ee" << "eg" << "eh" << "er" << "es" << "et" << "fi" @@ -440,10 +441,10 @@ QStringList SettingsCache::getCountries() const << "je" << "jm" << "jo" << "jp" << "ke" << "kg" << "kh" << "ki" << "km" << "kn" << "kp" << "kr" << "kw" << "ky" << "kz" << "la" << "lb" << "lc" << "li" << "lk" << "lr" << "ls" << "lt" << "lu" << "lv" << "ly" << "ma" << "mc" << "md" << "me" - << "mf" << "mg" << "mh" << "mk" << "ml" << "mm" << "mn" << "mo" << "mp" << "mq" + << "mf" << "mg" << "mh" << "mk" << "ml" << "mm" << "mn" << "mo" << "mp" << "mq" << "mr" << "ms" << "mt" << "mu" << "mv" << "mw" << "mx" << "my" << "mz" << "na" << "nc" << "ne" << "nf" << "ng" << "ni" << "nl" << "no" << "np" << "nr" << "nu" - << "nz" << "om" << "pa" << "pe" << "pf" << "pg" << "ph" << "pk" << "pl" << "pm" + << "nz" << "om" << "pa" << "pe" << "pf" << "pg" << "ph" << "pk" << "pl" << "pm" << "pn" << "pr" << "ps" << "pt" << "pw" << "py" << "qa" << "re" << "ro" << "rs" << "ru" << "rw" << "sa" << "sb" << "sc" << "sd" << "se" << "sg" << "sh" << "si" << "sj" << "sk" << "sl" << "sm" << "sn" << "so" << "sr" << "ss" << "st" << "sv" @@ -507,4 +508,4 @@ void SettingsCache::setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEv { spectatorsCanSeeEverything = _spectatorsCanSeeEverything; settings->setValue("game/spectatorscanseeeverything", spectatorsCanSeeEverything); -} \ No newline at end of file +} diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index a4017638..14be4312 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -97,6 +97,7 @@ private: bool spectatorsNeedPassword; bool spectatorsCanTalk; bool spectatorsCanSeeEverything; + int keepalive; public: SettingsCache(); const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; } @@ -167,6 +168,7 @@ public: bool getSpectatorsNeedPassword() const { return spectatorsNeedPassword; } bool getSpectatorsCanTalk() const { return spectatorsCanTalk; } bool getSpectatorsCanSeeEverything() const { return spectatorsCanSeeEverything; } + int getKeepAlive() const { return keepalive; } public slots: void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setLang(const QString &_lang); diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index ac0d6707..7402edfa 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -38,6 +38,14 @@ logfile=server.log ; it won't be logged. Default is empty; example: "kittens,ponies,faires" logfilters="" +; Set the time interval in seconds that servatrice will use to communicate with each connected client +; to verify the client has not timed out. Defaults is 5 seconds +clientkeepalive=5 + +; Maximum time in seconds a player can stay inactive with there client not even responding to pings, before is +; considered disconnected; default is 15 +max_player_inactivity_time=15 + [authentication] @@ -83,8 +91,8 @@ allowpunctuationprefix=false ; Enable this feature? Default false. ;enabled=false -; Require users to provide an email address in order to register. Newly registered users will receive an -; activation token by email, and will be required to input back this token on cockatrice at the first login +; Require users to provide an email address in order to register. Newly registered users will receive an +; activation token by email, and will be required to input back this token on cockatrice at the first login ; to get their account activated. Default true. ;requireemail=true @@ -119,7 +127,7 @@ subject="Cockatrice server account activation token" ; Email body. You can use these tags here: %username %token ; They will be substituted with the actual values in the email -; +; body="Hi %username, thank our for registering on our Cockatrice server\r\nHere's the activation token you need to supply for activating your account:\r\n\r\n%token\r\n\r\nHappy gaming!" [database] @@ -178,10 +186,6 @@ roomlist\1\game_types\3\name="GameType3" [game] -; Maximum time in seconds a player can stay inactive, with his client hot even responding to pings, before is -; considered disconnected; default is 15 -max_player_inactivity_time=15 - ; Maximum time in seconds all players in a game can stay inactive before the game is automatically closed; ; default is 120 max_game_inactivity_time=120 diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index b88b4f47..b0d3d581 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -161,13 +161,13 @@ bool Servatrice::initServer() authenticationMethod = AuthenticationNone; } - bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool(); - qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled; + bool 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(); - qDebug() << "Maximum user limit: " << maxUserLimit; - } + if (maxUserLimitEnabled){ + int maxUserLimit = settingsCache->value("security/max_users_total", 500).toInt(); + qDebug() << "Maximum user limit: " << maxUserLimit; + } bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); @@ -273,7 +273,7 @@ bool Servatrice::initServer() updateLoginMessage(); maxGameInactivityTime = settingsCache->value("game/max_game_inactivity_time", 120).toInt(); - maxPlayerInactivityTime = settingsCache->value("game/max_player_inactivity_time", 15).toInt(); + maxPlayerInactivityTime = settingsCache->value("server/max_player_inactivity_time", 15).toInt(); maxUsersPerAddress = settingsCache->value("security/max_users_per_address", 4).toInt(); messageCountingInterval = settingsCache->value("security/message_counting_interval", 10).toInt(); @@ -283,90 +283,91 @@ bool Servatrice::initServer() commandCountingInterval = settingsCache->value("game/command_counting_interval", 10).toInt(); maxCommandCountPerInterval = settingsCache->value("game/max_command_count_per_interval", 20).toInt(); - try { if (settingsCache->value("servernetwork/active", 0).toInt()) { - qDebug() << "Connecting to ISL network."; - const QString certFileName = settingsCache->value("servernetwork/ssl_cert").toString(); - const QString keyFileName = settingsCache->value("servernetwork/ssl_key").toString(); - qDebug() << "Loading certificate..."; - QFile certFile(certFileName); - if (!certFile.open(QIODevice::ReadOnly)) - throw QString("Error opening certificate file: %1").arg(certFileName); - QSslCertificate cert(&certFile); + try { if (settingsCache->value("servernetwork/active", 0).toInt()) { + qDebug() << "Connecting to ISL network."; + const QString certFileName = settingsCache->value("servernetwork/ssl_cert").toString(); + const QString keyFileName = settingsCache->value("servernetwork/ssl_key").toString(); + qDebug() << "Loading certificate..."; + QFile certFile(certFileName); + if (!certFile.open(QIODevice::ReadOnly)) + throw QString("Error opening certificate file: %1").arg(certFileName); + QSslCertificate cert(&certFile); #if QT_VERSION < 0x050000 - if (!cert.isValid()) - throw(QString("Invalid certificate.")); + if (!cert.isValid()) + throw(QString("Invalid certificate.")); #else - const QDateTime currentTime = QDateTime::currentDateTime(); - if(currentTime < cert.effectiveDate() || - currentTime > cert.expiryDate() || - cert.isBlacklisted()) - throw(QString("Invalid certificate.")); + const QDateTime currentTime = QDateTime::currentDateTime(); + if(currentTime < cert.effectiveDate() || + currentTime > cert.expiryDate() || + cert.isBlacklisted()) + throw(QString("Invalid certificate.")); #endif - qDebug() << "Loading private key..."; - QFile keyFile(keyFileName); - if (!keyFile.open(QIODevice::ReadOnly)) - throw QString("Error opening private key file: %1").arg(keyFileName); - QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); - if (key.isNull()) - throw QString("Invalid private key."); + qDebug() << "Loading private key..."; + QFile keyFile(keyFileName); + if (!keyFile.open(QIODevice::ReadOnly)) + throw QString("Error opening private key file: %1").arg(keyFileName); + QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + if (key.isNull()) + throw QString("Invalid private key."); - QMutableListIterator serverIterator(serverList); - while (serverIterator.hasNext()) { - const ServerProperties &prop = serverIterator.next(); - if (prop.cert == cert) { - serverIterator.remove(); - continue; - } + QMutableListIterator serverIterator(serverList); + while (serverIterator.hasNext()) { + const ServerProperties &prop = serverIterator.next(); + if (prop.cert == cert) { + serverIterator.remove(); + continue; + } - QThread *thread = new QThread; - thread->setObjectName("isl_" + QString::number(prop.id)); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + QThread *thread = new QThread; + thread->setObjectName("isl_" + QString::number(prop.id)); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); - interface->moveToThread(thread); - connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); + IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); + interface->moveToThread(thread); + connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); - thread->start(); - QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); - } + thread->start(); + QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); + } - const int networkPort = settingsCache->value("servernetwork/port", 14747).toInt(); - qDebug() << "Starting ISL server on port" << networkPort; + const int networkPort = settingsCache->value("servernetwork/port", 14747).toInt(); + qDebug() << "Starting ISL server on port" << networkPort; - islServer = new Servatrice_IslServer(this, cert, key, this); - if (islServer->listen(QHostAddress::Any, networkPort)) - qDebug() << "ISL server listening."; - else - throw QString("islServer->listen()"); - } } catch (QString error) { - qDebug() << "ERROR --" << error; - return false; - } + islServer = new Servatrice_IslServer(this, cert, key, this); + if (islServer->listen(QHostAddress::Any, networkPort)) + qDebug() << "ISL server listening."; + else + throw QString("islServer->listen()"); + } } catch (QString error) { + qDebug() << "ERROR --" << error; + return false; + } - pingClock = new QTimer(this); - connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); - pingClock->start(1000); + int clientkeepalive = settingsCache->value("server/clientkeepalive", 1).toInt(); + pingClock = new QTimer(this); + connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); + pingClock->start(clientkeepalive * 1000); - int statusUpdateTime = settingsCache->value("server/statusupdate", 15000).toInt(); - statusUpdateClock = new QTimer(this); - connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); - if (statusUpdateTime != 0) { - qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; - statusUpdateClock->start(statusUpdateTime); - } + int statusUpdateTime = settingsCache->value("server/statusupdate", 15000).toInt(); + statusUpdateClock = new QTimer(this); + connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); + if (statusUpdateTime != 0) { + qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; + statusUpdateClock->start(statusUpdateTime); + } - const int numberPools = settingsCache->value("server/number_pools", 1).toInt(); - gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this); - gameServer->setMaxPendingConnections(1000); - const int gamePort = settingsCache->value("server/port", 4747).toInt(); - qDebug() << "Starting server on port" << gamePort; - if (gameServer->listen(QHostAddress::Any, gamePort)) - qDebug() << "Server listening."; - else { - qDebug() << "gameServer->listen(): Error:" << gameServer->errorString(); - return false; - } - return true; + const int numberPools = settingsCache->value("server/number_pools", 1).toInt(); + gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this); + gameServer->setMaxPendingConnections(1000); + const int gamePort = settingsCache->value("server/port", 4747).toInt(); + qDebug() << "Starting server on port" << gamePort; + if (gameServer->listen(QHostAddress::Any, gamePort)) + qDebug() << "Server listening."; + else { + qDebug() << "gameServer->listen(): Error:" << gameServer->errorString(); + return false; + } + return true; } void Servatrice::addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface) @@ -376,20 +377,20 @@ void Servatrice::addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterf void Servatrice::updateServerList() { - qDebug() << "Updating server list..."; + qDebug() << "Updating server list..."; - serverListMutex.lock(); - serverList.clear(); + serverListMutex.lock(); + serverList.clear(); - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, ssl_cert, hostname, address, game_port, control_port from {prefix}_servers order by id asc"); - servatriceDatabaseInterface->execSqlQuery(query); - while (query->next()) { - ServerProperties prop(query->value(0).toInt(), QSslCertificate(query->value(1).toString().toUtf8()), query->value(2).toString(), QHostAddress(query->value(3).toString()), query->value(4).toInt(), query->value(5).toInt()); - serverList.append(prop); - qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6").arg(prop.id).arg(QString(prop.cert.digest().toHex())).arg(prop.hostname).arg(prop.address.toString()).arg(prop.gamePort).arg(prop.controlPort); - } + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, ssl_cert, hostname, address, game_port, control_port from {prefix}_servers order by id asc"); + servatriceDatabaseInterface->execSqlQuery(query); + while (query->next()) { + ServerProperties prop(query->value(0).toInt(), QSslCertificate(query->value(1).toString().toUtf8()), query->value(2).toString(), QHostAddress(query->value(3).toString()), query->value(4).toInt(), query->value(5).toInt()); + serverList.append(prop); + qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6").arg(prop.id).arg(QString(prop.cert.digest().toHex())).arg(prop.hostname).arg(prop.address.toString()).arg(prop.gamePort).arg(prop.controlPort); + } - serverListMutex.unlock(); + serverListMutex.unlock(); } QList Servatrice::getServerList() const @@ -408,7 +409,7 @@ int Servatrice::getUsersWithAddress(const QHostAddress &address) const for (int i = 0; i < clients.size(); ++i) if (static_cast(clients[i])->getPeerAddress() == address) ++result; - + return result; } @@ -483,9 +484,9 @@ void Servatrice::statusUpdate() QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_activation_emails a left join {prefix}_users b on a.name = b.name"); if (!servatriceDatabaseInterface->execSqlQuery(query)) return; - + QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name"); - + while (query->next()) { const QString userName = query->value(0).toString(); const QString emailAddress = query->value(1).toString(); @@ -551,7 +552,7 @@ void Servatrice::shutdownTimeout() clients[i]->sendProtocolItem(*se); clientsLock.unlock(); delete se; - + if (!shutdownMinutes) deleteLater(); }