diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index fe5ac7be1..e7d466791 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -398,16 +398,12 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() annotateTokensCheckBox.setChecked(settingsCache->getAnnotateTokens()); connect(&annotateTokensCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setAnnotateTokens(int))); - idleClientTimeOutCheckBox.setChecked(settingsCache->getIdleClientTimeOutEnabled()); - connect(&idleClientTimeOutCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setIdleClientTimeOutEnabled(int))); - QGridLayout *generalGrid = new QGridLayout; generalGrid->addWidget(¬ificationsEnabledCheckBox, 0, 0); generalGrid->addWidget(&specNotificationsEnabledCheckBox, 1, 0); generalGrid->addWidget(&doubleClickToPlayCheckBox, 2, 0); generalGrid->addWidget(&playToStackCheckBox, 3, 0); generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); - generalGrid->addWidget(&idleClientTimeOutCheckBox, 5, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -442,7 +438,6 @@ void UserInterfaceSettingsPage::retranslateUi() annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); animationGroupBox->setTitle(tr("Animation settings")); tapAnimationCheckBox.setText(tr("&Tap/untap animation")); - idleClientTimeOutCheckBox.setText(tr("Disconnect from server if idle for 1 hour")); } diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 62d735217..52d3c9ba0 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -112,7 +112,6 @@ private: QCheckBox playToStackCheckBox; QCheckBox annotateTokensCheckBox; QCheckBox tapAnimationCheckBox; - QCheckBox idleClientTimeOutCheckBox; QGroupBox *generalGroupBox; QGroupBox *animationGroupBox; diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 9ce554805..ceaa9bb50 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -30,11 +30,6 @@ RemoteClient::RemoteClient(QObject *parent) timer->setInterval(keepalive * 1000); connect(timer, SIGNAL(timeout()), this, SLOT(ping())); - int idlekeepalive = settingsCache->getIdleKeepAlive(); - idleTimer = new QTimer(this); - idleTimer->setInterval(idlekeepalive * 1000); - connect(idleTimer, SIGNAL(timeout()), this, SLOT(doIdleTimeOut())); - connect(this, SIGNAL(resetIdleTimerClock()), idleTimer, SLOT(start())); socket = new QTcpSocket(this); socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); @@ -66,7 +61,6 @@ void RemoteClient::slotConnected() { timeRunning = lastDataReceived = 0; timer->start(); - idleTimer->start(); // dirty hack to be compatible with v14 server sendCommandContainer(CommandContainer()); @@ -308,7 +302,6 @@ void RemoteClient::doActivateToServer(const QString &_token) void RemoteClient::doDisconnectFromServer() { timer->stop(); - idleTimer->stop(); messageInProgress = false; handshakeStarted = false; @@ -387,17 +380,3 @@ QString RemoteClient::getSrvClientID(const QString _hostname) QString uniqueServerClientID = QCryptographicHash::hash(srvClientID.toUtf8(), QCryptographicHash::Sha1).toHex().right(15); return uniqueServerClientID; } - -void RemoteClient::doIdleTimeOut() -{ - if (settingsCache->getIdleClientTimeOutEnabled()) { - doDisconnectFromServer(); - emit idleTimeout(); - } - -} - -void RemoteClient::resetIdleTimer() -{ - emit resetIdleTimerClock(); -} diff --git a/cockatrice/src/remoteclient.h b/cockatrice/src/remoteclient.h index 9ee76d724..ed0b66db5 100644 --- a/cockatrice/src/remoteclient.h +++ b/cockatrice/src/remoteclient.h @@ -11,8 +11,6 @@ class RemoteClient : public AbstractClient { signals: void maxPingTime(int seconds, int maxSeconds); void serverTimeout(); - void idleTimeout(); - void resetIdleTimerClock(); void loginError(Response::ResponseCode resp, QString reasonStr, quint32 endTime, QList missingFeatures); void registerError(Response::ResponseCode resp, QString reasonStr, quint32 endTime); void activateError(); @@ -39,7 +37,6 @@ private slots: void doLogin(); void doDisconnectFromServer(); void doActivateToServer(const QString &_token); - void doIdleTimeOut(); private: static const int maxTimeout = 10; @@ -51,7 +48,6 @@ private: int messageLength; QTimer *timer; - QTimer *idleTimer; QTcpSocket *socket; QString lastHostname; int lastPort; @@ -66,7 +62,6 @@ public: void registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname); void activateToServer(const QString &_token); void disconnectFromServer(); - void resetIdleTimer(); }; #endif diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index ebb46c626..264e4de8b 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -162,7 +162,6 @@ SettingsCache::SettingsCache() notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool(); lang = settings->value("personal/lang").toString(); keepalive = settings->value("personal/keepalive", 5).toInt(); - idlekeepalive = settings->value("personal/idlekeepalive", 3600).toInt(); deckPath = getSafeConfigPath("paths/decks", dataPath + "/decks/"); replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/"); @@ -250,7 +249,6 @@ SettingsCache::SettingsCache() spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool(); rememberGameSettings = settings->value("game/remembergamesettings", true).toBool(); clientID = settings->value("personal/clientid", "notset").toString(); - idleClientTimeOutEnabled = settings->value("interface/idleClientTimeOutEnabled", true).toBool(); } void SettingsCache::setCardInfoViewMode(const int _viewMode) { @@ -392,12 +390,6 @@ void SettingsCache::setAnnotateTokens(int _annotateTokens) settings->setValue("interface/annotatetokens", annotateTokens); } -void SettingsCache::setIdleClientTimeOutEnabled(int _idleClientTimeOutEnabled) -{ - idleClientTimeOutEnabled = _idleClientTimeOutEnabled; - settings->setValue("interface/idleClientTimeOutEnabled", idleClientTimeOutEnabled); -} - void SettingsCache::setTabGameSplitterSizes(const QByteArray &_tabGameSplitterSizes) { tabGameSplitterSizes = _tabGameSplitterSizes; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index f350cf759..19d061f8d 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -64,7 +64,6 @@ private: bool doubleClickToPlay; bool playToStack; bool annotateTokens; - bool idleClientTimeOutEnabled; QByteArray tabGameSplitterSizes; bool displayCardNames; bool horizontalHand; @@ -105,8 +104,7 @@ private: bool spectatorsNeedPassword; bool spectatorsCanTalk; bool spectatorsCanSeeEverything; - int keepalive; - int idlekeepalive; + int keepalive; void translateLegacySettings(); QString getSafeConfigPath(QString configEntry, QString defaultPath) const; QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const; @@ -132,7 +130,6 @@ public: bool getNotificationsEnabled() const { return notificationsEnabled; } bool getSpectatorNotificationsEnabled() const { return spectatorNotificationsEnabled; } bool getNotifyAboutUpdates() const { return notifyAboutUpdates; } - bool getIdleClientTimeOutEnabled() const { return idleClientTimeOutEnabled; } bool getDoubleClickToPlay() const { return doubleClickToPlay; } bool getPlayToStack() const { return playToStack; } @@ -183,7 +180,6 @@ public: bool getSpectatorsCanSeeEverything() const { return spectatorsCanSeeEverything; } bool getRememberGameSettings() const { return rememberGameSettings; } int getKeepAlive() const { return keepalive; } - int getIdleKeepAlive() const { return idlekeepalive; } void setClientID(QString clientID); QString getClientID() { return clientID; } ShortcutsSettings& shortcuts() const { return *shortcutsSettings; } @@ -209,7 +205,6 @@ public slots: void setDoubleClickToPlay(int _doubleClickToPlay); void setPlayToStack(int _playToStack); void setAnnotateTokens(int _annotateTokens); - void setIdleClientTimeOutEnabled(int _idleClientTimeOutEnabled); void setTabGameSplitterSizes(const QByteArray &_tabGameSplitterSizes); void setDisplayCardNames(int _displayCardNames); void setHorizontalHand(int _horizontalHand); diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 094f2e379..ee9c601a5 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -547,7 +547,6 @@ void TabGame::replayFinished() void TabGame::replayStartButtonClicked() { - emit notIdle(); replayStartButton->setEnabled(false); replayPauseButton->setEnabled(true); replayFastForwardButton->setEnabled(true); @@ -557,7 +556,6 @@ void TabGame::replayStartButtonClicked() void TabGame::replayPauseButtonClicked() { - emit notIdle(); replayStartButton->setEnabled(true); replayPauseButton->setEnabled(false); replayFastForwardButton->setEnabled(false); @@ -567,7 +565,6 @@ void TabGame::replayPauseButtonClicked() void TabGame::replayFastForwardButtonToggled(bool checked) { - emit notIdle(); timelineWidget->setTimeScaleFactor(checked ? 10.0 : 1.0); } @@ -597,7 +594,6 @@ void TabGame::actGameInfo() void TabGame::actConcede() { - emit notIdle(); if (QMessageBox::question(this, tr("Concede"), tr("Are you sure you want to concede this game?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return; @@ -606,7 +602,6 @@ void TabGame::actConcede() void TabGame::actLeaveGame() { - emit notIdle(); if (!gameClosed) { if (!spectator) if (QMessageBox::question(this, tr("Leave game"), tr("Are you sure you want to leave this game?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) @@ -630,7 +625,6 @@ void TabGame::actSay() sendGameCommand(cmd); sayEdit->clear(); } - emit notIdle(); } void TabGame::actPhaseAction() @@ -784,7 +778,6 @@ AbstractClient *TabGame::getClientForPlayer(int playerId) const void TabGame::sendGameCommand(PendingCommand *pend, int playerId) { - emit notIdle(); AbstractClient *client = getClientForPlayer(playerId); if (!client) return; @@ -795,7 +788,6 @@ void TabGame::sendGameCommand(PendingCommand *pend, int playerId) void TabGame::sendGameCommand(const google::protobuf::Message &command, int playerId) { - emit notIdle(); AbstractClient *client = getClientForPlayer(playerId); if (!client) return; diff --git a/cockatrice/src/tab_game.h b/cockatrice/src/tab_game.h index a3a697167..2db2e0675 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -65,7 +65,6 @@ private: bool state; signals: void stateChanged(); - void notIdle(); public: ToggleButton(QWidget *parent = 0); bool getState() const { return state; } diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index cffc8555b..337115601 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -99,7 +99,6 @@ void TabMessage::sendMessage() client->sendCommand(pend); sayEdit->clear(); - emit notIdle(); } void TabMessage::messageSent(const Response &response) diff --git a/cockatrice/src/tab_message.h b/cockatrice/src/tab_message.h index 2b2d16e85..ac4688803 100644 --- a/cockatrice/src/tab_message.h +++ b/cockatrice/src/tab_message.h @@ -26,7 +26,6 @@ private: signals: void talkClosing(TabMessage *tab); void maximizeClient(); - void notIdle(); private slots: void sendMessage(); void actLeave(); diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index 1f95369a7..639ca7206 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -199,7 +199,6 @@ void TabRoom::sendMessage() sendRoomCommand(pend); sayEdit->clear(); } - emit notIdle(); } void TabRoom::sayFinished(const Response &response) diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 8d9e23632..ae8f1e52a 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -342,12 +342,10 @@ void TabSupervisor::gameJoined(const Event_GameJoined &event) connect(tab, SIGNAL(gameClosing(TabGame *)), this, SLOT(gameLeft(TabGame *))); connect(tab, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); connect(tab, SIGNAL(openDeckEditor(const DeckLoader *)), this, SLOT(addDeckEditorTab(const DeckLoader *))); - connect(tab, SIGNAL(notIdle()), this, SLOT(resetIdleTimer())); int tabIndex = myAddTab(tab); addCloseButtonToTab(tab, tabIndex); gameTabs.insert(event.game_info().game_id(), tab); setCurrentWidget(tab); - emit idleTimerReset(); } void TabSupervisor::localGameJoined(const Event_GameJoined &event) @@ -391,7 +389,6 @@ void TabSupervisor::addRoomTab(const ServerInfo_Room &info, bool setCurrent) roomTabs.insert(info.room_id(), tab); if (setCurrent) setCurrentWidget(tab); - emit idleTimerReset(); } void TabSupervisor::roomLeft(TabRoom *tab) @@ -444,7 +441,6 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus tab = new TabMessage(this, client, *userInfo, otherUser); connect(tab, SIGNAL(talkClosing(TabMessage *)), this, SLOT(talkLeft(TabMessage *))); connect(tab, SIGNAL(maximizeClient()), this, SLOT(maximizeMainWindow())); - connect(tab, SIGNAL(notIdle()), this, SLOT(resetIdleTimer())); int tabIndex = myAddTab(tab); addCloseButtonToTab(tab, tabIndex); messageTabs.insert(receiverName, tab); @@ -583,6 +579,8 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) { switch ((Event_NotifyUser::NotificationType) event.type()) { + case Event_NotifyUser::UNKNOWN: QMessageBox::information(this, tr("Unknown Event"), tr("The server has sent you a message that your client does not understand.\nThis message might mean there is a new version of Cockatrice available or this server is running a custom or pre-release version.\n\nTo update your client, go to Help -> Update Cockatrice.")); break; + case Event_NotifyUser::IDLEWARNING: QMessageBox::information(this, tr("Idle Timeout"), tr("You are about to be logged out due to inactivity.")); break; case Event_NotifyUser::PROMOTED: QMessageBox::information(this, tr("Promotion"), tr("You have been promoted to moderator. Please log out and back in for changes to take effect.")); break; case Event_NotifyUser::WARNING: { if (!QString::fromStdString(event.warning_reason()).simplified().isEmpty()) @@ -592,9 +590,4 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) default: ; } -} - -void TabSupervisor::resetIdleTimer() -{ - emit idleTimerReset(); } \ No newline at end of file diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index ce7fa0597..027ba64f9 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -83,7 +83,6 @@ signals: void localGameEnded(); void adminLockChanged(bool lock); void showWindowIfHidden(); - void idleTimerReset(); public slots: TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen); void openReplay(GameReplay *replay); @@ -109,7 +108,6 @@ private slots: void processGameEventContainer(const GameEventContainer &cont); void processUserMessageEvent(const Event_UserMessage &event); void processNotifyUserEvent(const Event_NotifyUser &event); - void resetIdleTimer(); }; #endif \ No newline at end of file diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 94b9174c5..15ef757aa 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -321,17 +321,6 @@ void MainWindow::serverTimeout() actConnect(); } -void MainWindow::idleTimeout() -{ - QMessageBox::critical(this, tr("Inactivity Timeout"), tr("You have been signed out due to inactivity.")); - actConnect(); -} - -void MainWindow::idleTimerReset() -{ - client->resetIdleTimer(); -} - void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 endTime, QList missingFeatures) { switch (r) { @@ -665,7 +654,6 @@ MainWindow::MainWindow(QWidget *parent) connect(client, SIGNAL(loginError(Response::ResponseCode, QString, quint32, QList)), this, SLOT(loginError(Response::ResponseCode, QString, quint32, QList))); connect(client, SIGNAL(socketError(const QString &)), this, SLOT(socketError(const QString &))); connect(client, SIGNAL(serverTimeout()), this, SLOT(serverTimeout())); - connect(client, SIGNAL(idleTimeout()), this, SLOT(idleTimeout())); connect(client, SIGNAL(statusChanged(ClientStatus)), this, SLOT(statusChanged(ClientStatus))); connect(client, SIGNAL(protocolVersionMismatch(int, int)), this, SLOT(protocolVersionMismatch(int, int))); connect(client, SIGNAL(userInfoChanged(const ServerInfo_User &)), this, SLOT(userInfoReceived(const ServerInfo_User &)), Qt::BlockingQueuedConnection); @@ -687,7 +675,6 @@ MainWindow::MainWindow(QWidget *parent) connect(tabSupervisor, SIGNAL(setMenu(QList)), this, SLOT(updateTabMenu(QList))); connect(tabSupervisor, SIGNAL(localGameEnded()), this, SLOT(localGameEnded())); connect(tabSupervisor, SIGNAL(showWindowIfHidden()), this, SLOT(showWindowIfHidden())); - connect(tabSupervisor, SIGNAL(idleTimerReset()), this, SLOT(idleTimerReset())); tabSupervisor->addDeckEditorTab(0); setCentralWidget(tabSupervisor); diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index 2d58da26a..b5ed81d6d 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -46,8 +46,6 @@ private slots: void processConnectionClosedEvent(const Event_ConnectionClosed &event); void processServerShutdownEvent(const Event_ServerShutdown &event); void serverTimeout(); - void idleTimerReset(); - void idleTimeout(); void loginError(Response::ResponseCode r, QString reasonStr, quint32 endTime, QList missingFeatures); void registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime); void activateError(); diff --git a/common/featureset.cpp b/common/featureset.cpp index 3c721df85..39110540f 100644 --- a/common/featureset.cpp +++ b/common/featureset.cpp @@ -20,6 +20,7 @@ void FeatureSet::initalizeFeatureList(QMap &featureList) { featureList.insert("room_chat_history", false); featureList.insert("client_warnings", false); featureList.insert("mod_log_lookup", false); + featureList.insert("idle_client", false); } void FeatureSet::enableRequiredFeature(QMap &featureList, QString featureName){ diff --git a/common/pb/event_notify_user.proto b/common/pb/event_notify_user.proto index de1641672..53182ab23 100644 --- a/common/pb/event_notify_user.proto +++ b/common/pb/event_notify_user.proto @@ -3,8 +3,10 @@ import "session_event.proto"; message Event_NotifyUser { enum NotificationType { + UNKNOWN = 0; // Default enum value if no "type" is defined when used PROMOTED = 1; WARNING = 2; + IDLEWARNING = 3; } extend SessionEvent { diff --git a/common/server.h b/common/server.h index 018e4a892..296569a35 100644 --- a/common/server.h +++ b/common/server.h @@ -64,6 +64,7 @@ public: virtual bool getClientIDRequiredEnabled() const { return false; } virtual bool getRegOnlyServerEnabled() const { return false; } virtual bool getMaxUserLimitEnabled() const { return false; } + virtual int getIdleClientTimeout() const { return 0; } virtual int getClientKeepAlive() const { return 0; } virtual int getMaxGameInactivityTime() const { return 9999999; } virtual int getMaxPlayerInactivityTime() const { return 9999999; } diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 7edf26dd2..671cfda0d 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "server_protocolhandler.h" #include "server_database_interface.h" #include "server_room.h" @@ -19,6 +20,7 @@ #include "pb/event_game_joined.pb.h" #include "pb/event_room_say.pb.h" #include "pb/serverinfo_user.pb.h" +#include "pb/event_notify_user.pb.h" #include #include "featureset.h" @@ -31,8 +33,11 @@ Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, Server_DatabaseI authState(NotLoggedIn), acceptsUserListChanges(false), acceptsRoomListChanges(false), + idleClientWarningSent(false), timeRunning(0), - lastDataReceived(0) + lastDataReceived(0), + lastActionReceived(0) + { connect(server, SIGNAL(pingClockTimeout()), this, SLOT(pingClockTimeout())); } @@ -171,6 +176,8 @@ Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const if (!room) return Response::RespNotInRoom; + resetIdleTimer(); + Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.room_command_size() - 1; i >= 0; --i) { Response::ResponseCode resp = Response::RespInvalidCommand; @@ -240,6 +247,8 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const if (!player) return Response::RespNotInRoom; + resetIdleTimer(); + int commandCountingInterval = server->getCommandCountingInterval(); int maxCommandCountPerInterval = server->getMaxCommandCountPerInterval(); GameEventStorage ges; @@ -280,6 +289,8 @@ Response::ResponseCode Server_ProtocolHandler::processModeratorCommandContainer( if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) return Response::RespLoginNeeded; + resetIdleTimer(); + Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.moderator_command_size() - 1; i >= 0; --i) { Response::ResponseCode resp = Response::RespInvalidCommand; @@ -301,6 +312,8 @@ Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(cons if (!(userInfo->user_level() & ServerInfo_User::IsAdmin)) return Response::RespLoginNeeded; + resetIdleTimer(); + Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.admin_command_size() - 1; i >= 0; --i) { Response::ResponseCode resp = Response::RespInvalidCommand; @@ -373,6 +386,24 @@ void Server_ProtocolHandler::pingClockTimeout() if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime()) prepareDestroy(); + + if (QString::fromStdString(userInfo->privlevel()).toLower() == "none") { + if ((server->getIdleClientTimeout() > 0) && (idleClientWarningSent)) { + if (timeRunning - lastActionReceived > server->getIdleClientTimeout()) { + prepareDestroy(); + } + } + + if (((timeRunning - lastActionReceived) >= ceil(server->getIdleClientTimeout() *.9)) && (!idleClientWarningSent)) { + Event_NotifyUser event; + event.set_type(Event_NotifyUser::IDLEWARNING); + SessionEvent *se = prepareSessionEvent(event); + sendProtocolItem(*se); + delete se; + idleClientWarningSent = true; + } + } + ++timeRunning; } @@ -728,3 +759,9 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinGame(const Command_JoinGam return room->processJoinGameCommand(cmd, rc, this); } + +void Server_ProtocolHandler::resetIdleTimer() +{ + lastActionReceived = timeRunning; + idleClientWarningSent = false; +} diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index bbc67a0d6..8dae20bbe 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -52,10 +52,11 @@ protected: AuthenticationResult authState; bool acceptsUserListChanges; bool acceptsRoomListChanges; + bool idleClientWarningSent; virtual void logDebugMessage(const QString & /* message */) { } private: QList messageSizeOverTime, messageCountOverTime, commandCountOverTime; - int timeRunning, lastDataReceived; + int timeRunning, lastDataReceived, lastActionReceived; QTimer *pingClock; virtual void transmitProtocolItem(const ServerMessage &item) = 0; @@ -81,6 +82,8 @@ private: virtual Response::ResponseCode processExtendedModeratorCommand(int /* cmdType */, const ModeratorCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; } Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc); virtual Response::ResponseCode processExtendedAdminCommand(int /* cmdType */, const AdminCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; } + + void resetIdleTimer(); private slots: void pingClockTimeout(); public slots: diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index 1ffee6cde..df133a2a6 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -71,6 +71,10 @@ requiredfeatures="" ; to choose from. Example: "Flaming,Foul Language" officialwarnings="Flamming,Spamming,Causing Drama,Abusive Language" +; Maximum time in seconds a player can stay connected but idle. Default is 3600 (0 = disabled) +; Clients will be notified at the 90% time period of pending disconnection if they do not take action. +idleclienttimeout=3600 + [authentication] ; Servatrice can authenticate users connecting. It currently supports 3 different authentication methods: diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 4067a1376..27c9a1488 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -405,6 +405,13 @@ bool Servatrice::initServer() } } #endif + + if (getIdleClientTimeout() > 0) { + qDebug() << "Idle client timeout value: " << getIdleClientTimeout(); + if (getIdleClientTimeout() < 300) + qDebug() << "WARNING: It is not recommended to set the IdleClientTimeout value very low. Doing so will cause clients to very quickly be disconnected. Many players when connected may be searching for card details outside the client in the middle of matches or possibly drafting outside the client and short time out values will remove these players."; + } + setRequiredFeatures(getRequiredFeatures()); return true; } @@ -814,4 +821,8 @@ QString Servatrice::getISLNetworkSSLKeyFile() const { int Servatrice::getISLNetworkPort() const { return settingsCache->value("servernetwork/port", 14747).toInt(); +} + +int Servatrice::getIdleClientTimeout() const { + return settingsCache->value("server/idleclienttimeout", 3600).toInt(); } \ No newline at end of file diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 84497cbc1..49c83520b 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -189,6 +189,7 @@ public: bool getRegistrationEnabled() const; bool getRequireEmailForRegistrationEnabled() const; bool getRequireEmailActivationEnabled() const; + int getIdleClientTimeout() const; int getServerID() const; int getMaxGameInactivityTime() const; int getMaxPlayerInactivityTime() const;