diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index f63ce12d..05c0f938 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -30,12 +30,17 @@ 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); connect(socket, SIGNAL(connected()), this, SLOT(slotConnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError))); - connect(this, SIGNAL(serverIdentificationEventReceived(const Event_ServerIdentification &)), this, SLOT(processServerIdentificationEvent(const Event_ServerIdentification &))); connect(this, SIGNAL(connectionClosedEventReceived(Event_ConnectionClosed)), this, SLOT(processConnectionClosedEvent(Event_ConnectionClosed))); connect(this, SIGNAL(sigConnectToServer(QString, unsigned int, QString, QString)), this, SLOT(doConnectToServer(QString, unsigned int, QString, QString))); @@ -61,6 +66,7 @@ void RemoteClient::slotConnected() { timeRunning = lastDataReceived = 0; timer->start(); + idleTimer->start(); // dirty hack to be compatible with v14 server sendCommandContainer(CommandContainer()); @@ -302,6 +308,7 @@ void RemoteClient::doActivateToServer(const QString &_token) void RemoteClient::doDisconnectFromServer() { timer->stop(); + idleTimer->stop(); messageInProgress = false; handshakeStarted = false; @@ -380,3 +387,14 @@ QString RemoteClient::getSrvClientID(const QString _hostname) QString uniqueServerClientID = QCryptographicHash::hash(srvClientID.toUtf8(), QCryptographicHash::Sha1).toHex().right(15); return uniqueServerClientID; } + +void RemoteClient::doIdleTimeOut() +{ + doDisconnectFromServer(); + emit idleTimeout(); +} + +void RemoteClient::resetIdleTimer() +{ + emit resetIdleTimerClock(); +} diff --git a/cockatrice/src/remoteclient.h b/cockatrice/src/remoteclient.h index ed0b66db..9ee76d72 100644 --- a/cockatrice/src/remoteclient.h +++ b/cockatrice/src/remoteclient.h @@ -11,6 +11,8 @@ 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(); @@ -37,6 +39,7 @@ private slots: void doLogin(); void doDisconnectFromServer(); void doActivateToServer(const QString &_token); + void doIdleTimeOut(); private: static const int maxTimeout = 10; @@ -48,6 +51,7 @@ private: int messageLength; QTimer *timer; + QTimer *idleTimer; QTcpSocket *socket; QString lastHostname; int lastPort; @@ -62,6 +66,7 @@ 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 264e4de8..9b91c251 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -162,6 +162,7 @@ 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", 36000).toInt(); deckPath = getSafeConfigPath("paths/decks", dataPath + "/decks/"); replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/"); diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 19d061f8..46318bf2 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -104,7 +104,8 @@ private: bool spectatorsNeedPassword; bool spectatorsCanTalk; bool spectatorsCanSeeEverything; - int keepalive; + int keepalive; + int idlekeepalive; void translateLegacySettings(); QString getSafeConfigPath(QString configEntry, QString defaultPath) const; QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const; @@ -180,6 +181,7 @@ 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; } diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index ee9c601a..094f2e37 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -547,6 +547,7 @@ void TabGame::replayFinished() void TabGame::replayStartButtonClicked() { + emit notIdle(); replayStartButton->setEnabled(false); replayPauseButton->setEnabled(true); replayFastForwardButton->setEnabled(true); @@ -556,6 +557,7 @@ void TabGame::replayStartButtonClicked() void TabGame::replayPauseButtonClicked() { + emit notIdle(); replayStartButton->setEnabled(true); replayPauseButton->setEnabled(false); replayFastForwardButton->setEnabled(false); @@ -565,6 +567,7 @@ void TabGame::replayPauseButtonClicked() void TabGame::replayFastForwardButtonToggled(bool checked) { + emit notIdle(); timelineWidget->setTimeScaleFactor(checked ? 10.0 : 1.0); } @@ -594,6 +597,7 @@ 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; @@ -602,6 +606,7 @@ 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) @@ -625,6 +630,7 @@ void TabGame::actSay() sendGameCommand(cmd); sayEdit->clear(); } + emit notIdle(); } void TabGame::actPhaseAction() @@ -778,6 +784,7 @@ AbstractClient *TabGame::getClientForPlayer(int playerId) const void TabGame::sendGameCommand(PendingCommand *pend, int playerId) { + emit notIdle(); AbstractClient *client = getClientForPlayer(playerId); if (!client) return; @@ -788,6 +795,7 @@ 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 213d5e92..a3a69716 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -65,6 +65,7 @@ private: bool state; signals: void stateChanged(); + void notIdle(); public: ToggleButton(QWidget *parent = 0); bool getState() const { return state; } @@ -92,6 +93,7 @@ private slots: void refreshShortcuts(); signals: void newCardAdded(AbstractCardItem *card); + void notIdle(); public: DeckViewContainer(int _playerId, TabGame *parent); void retranslateUi(); @@ -193,6 +195,7 @@ signals: void containerProcessingDone(); void openMessageDialog(const QString &userName, bool focus); void openDeckEditor(const DeckLoader *deck); + void notIdle(); private slots: void replayNextEvent(); void replayFinished(); diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index 33711560..cffc8555 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -99,6 +99,7 @@ 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 ac468880..2b2d16e8 100644 --- a/cockatrice/src/tab_message.h +++ b/cockatrice/src/tab_message.h @@ -26,6 +26,7 @@ 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 95356a57..1f95369a 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -24,7 +24,6 @@ #include "settingscache.h" #include "main.h" #include "lineeditcompleter.h" - #include "get_pb_extension.h" #include "pb/room_commands.pb.h" #include "pb/serverinfo_room.pb.h" @@ -200,6 +199,7 @@ void TabRoom::sendMessage() sendRoomCommand(pend); sayEdit->clear(); } + emit notIdle(); } void TabRoom::sayFinished(const Response &response) diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index 41b26b75..75aacc6d 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -59,6 +59,7 @@ signals: void roomClosing(TabRoom *tab); void openMessageDialog(const QString &userName, bool focus); void maximizeClient(); + void notIdle(); private slots: void sendMessage(); void sayFinished(const Response &response); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index efb8a51f..8d9e2363 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -342,10 +342,12 @@ 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) @@ -383,11 +385,13 @@ void TabSupervisor::addRoomTab(const ServerInfo_Room &info, bool setCurrent) connect(tab, SIGNAL(maximizeClient()), this, SLOT(maximizeMainWindow())); connect(tab, SIGNAL(roomClosing(TabRoom *)), this, SLOT(roomLeft(TabRoom *))); connect(tab, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); + connect(tab, SIGNAL(notIdle()), this, SLOT(resetIdleTimer())); int tabIndex = myAddTab(tab); addCloseButtonToTab(tab, tabIndex); roomTabs.insert(info.room_id(), tab); if (setCurrent) setCurrentWidget(tab); + emit idleTimerReset(); } void TabSupervisor::roomLeft(TabRoom *tab) @@ -440,6 +444,7 @@ 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); @@ -589,3 +594,7 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) } +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 027ba64f..ce7fa059 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -83,6 +83,7 @@ signals: void localGameEnded(); void adminLockChanged(bool lock); void showWindowIfHidden(); + void idleTimerReset(); public slots: TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen); void openReplay(GameReplay *replay); @@ -108,6 +109,7 @@ 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 8b49b1b7..5c1b97b5 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -321,6 +321,17 @@ 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) { @@ -639,6 +650,7 @@ 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); @@ -660,6 +672,7 @@ 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 b5ed81d6..2d58da26 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -46,6 +46,8 @@ 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..c1082baf 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("client_inactivetimeout", false); } void FeatureSet::enableRequiredFeature(QMap &featureList, QString featureName){