diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index fe5ac7be..e7d46679 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 62d73521..52d3c9ba 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 9ce55480..ceaa9bb5 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 9ee76d72..ed0b66db 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 ebb46c62..264e4de8 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 f350cf75..19d061f8 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 094f2e37..ee9c601a 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 a3a69716..2db2e067 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 cffc8555..33711560 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 2b2d16e8..ac468880 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 1f95369a..639ca720 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 8d9e2363..ae8f1e52 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 ce7fa059..027ba64f 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 94b9174c..15ef757a 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 2d58da26..b5ed81d6 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 3c721df8..39110540 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 de164167..53182ab2 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 018e4a89..296569a3 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 7edf26dd..671cfda0 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 bbc67a0d..8dae20bb 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 1ffee6cd..df133a2a 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 4067a137..27c9a148 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 84497cbc..49c83520 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;