diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index d020046d7..3b5d43b6e 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -110,6 +110,7 @@ set(cockatrice_SOURCES src/client/ui/widgets/printing_selector/printing_selector_view_options_widget.cpp src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp src/client/network/release_channel.cpp + src/client/network/client_update_checker.cpp src/server/remote/remote_client.cpp src/server/remote/remote_decklist_tree_widget.cpp src/server/remote/remote_replay_list_tree_widget.cpp diff --git a/cockatrice/src/client/network/client_update_checker.cpp b/cockatrice/src/client/network/client_update_checker.cpp new file mode 100644 index 000000000..23c52d08d --- /dev/null +++ b/cockatrice/src/client/network/client_update_checker.cpp @@ -0,0 +1,35 @@ +#include "client_update_checker.h" + +#include "../../settings/cache_settings.h" +#include "release_channel.h" + +ClientUpdateChecker::ClientUpdateChecker(QObject *parent) : QObject(parent) +{ +} + +void ClientUpdateChecker::check() +{ + auto releaseChannel = SettingsCache::instance().getUpdateReleaseChannel(); + + finishedCheckConnection = + connect(releaseChannel, &ReleaseChannel::finishedCheck, this, &ClientUpdateChecker::actFinishedCheck); + errorConnection = connect(releaseChannel, &ReleaseChannel::error, this, &ClientUpdateChecker::actError); + + releaseChannel->checkForUpdates(); +} + +void ClientUpdateChecker::actFinishedCheck(bool needToUpdate, bool isCompatible, Release *release) +{ + disconnect(finishedCheckConnection); + disconnect(errorConnection); + + emit finishedCheck(needToUpdate, isCompatible, release); +} + +void ClientUpdateChecker::actError(const QString &errorString) +{ + disconnect(finishedCheckConnection); + disconnect(errorConnection); + + emit error(errorString); +} \ No newline at end of file diff --git a/cockatrice/src/client/network/client_update_checker.h b/cockatrice/src/client/network/client_update_checker.h new file mode 100644 index 000000000..96a7213b1 --- /dev/null +++ b/cockatrice/src/client/network/client_update_checker.h @@ -0,0 +1,45 @@ +#ifndef CLIENT_UPDATE_CHECKER_H +#define CLIENT_UPDATE_CHECKER_H +#include + +class Release; + +/** + * We use a singleton instance of UpdateChannel, which can cause interference and feedback loops when multiple objects + * connect to it. + * + * This class encapsulates the usage of that UpdateChannel to ensure that the check only happens once per connection and + * the connection is destroyed after it's been used. + */ +class ClientUpdateChecker : public QObject +{ + Q_OBJECT + + QMetaObject::Connection finishedCheckConnection; + QMetaObject::Connection errorConnection; + + void actFinishedCheck(bool needToUpdate, bool isCompatible, Release *release); + void actError(const QString &errorString); + +public: + explicit ClientUpdateChecker(QObject *parent = nullptr); + /** + * Actually performs the check, using the currently selected update channel in the settings. + * Any resulting signals will only be sent once. + * This method should only be called ONCE per instance. + */ + void check(); + +signals: + /** + * Forwarded from UpdateChannel::finishedCheck + */ + void finishedCheck(bool needToUpdate, bool isCompatible, Release *release); + + /** + * Forwarded from UpdateChannel::error + */ + void error(const QString &errorString); +}; + +#endif // CLIENT_UPDATE_CHECKER_H diff --git a/cockatrice/src/client/network/release_channel.cpp b/cockatrice/src/client/network/release_channel.cpp index d5cd672dc..edcb8d240 100644 --- a/cockatrice/src/client/network/release_channel.cpp +++ b/cockatrice/src/client/network/release_channel.cpp @@ -112,7 +112,7 @@ void StableReleaseChannel::releaseListFinished() QVariantMap resultMap = jsonResponse.toVariant().toMap(); if (!(resultMap.contains("name") && resultMap.contains("html_url") && resultMap.contains("tag_name") && resultMap.contains("published_at"))) { - qWarning() << "Invalid received from the release update server."; + qWarning() << "Invalid received from the release update server:" << resultMap; emit error(tr("Invalid reply received from the release update server.")); return; } diff --git a/cockatrice/src/client/ui/window_main.cpp b/cockatrice/src/client/ui/window_main.cpp index 2a847423d..6d6d4aa49 100644 --- a/cockatrice/src/client/ui/window_main.cpp +++ b/cockatrice/src/client/ui/window_main.cpp @@ -40,6 +40,8 @@ #include "../../settings/cache_settings.h" #include "../../utility/logger.h" #include "../get_text_with_max.h" +#include "../network/client_update_checker.h" +#include "../network/release_channel.h" #include "../tabs/tab_game.h" #include "../tabs/tab_supervisor.h" #include "pb/event_connection_closed.pb.h" @@ -879,6 +881,10 @@ MainWindow::MainWindow(QWidget *parent) void MainWindow::startupConfigCheck() { + if (SettingsCache::instance().getCheckUpdatesOnStartup()) { + actCheckClientUpdates(); + } + if (SettingsCache::instance().getClientVersion() == CLIENT_INFO_NOT_SET) { // no config found, 99% new clean install qDebug() << "Startup: old client version empty, assuming first start after clean install"; @@ -1223,6 +1229,21 @@ void MainWindow::actCheckServerUpdates() connect(hps, &HandlePublicServers::sigPublicServersDownloadedSuccessfully, [=]() { hps->deleteLater(); }); } +void MainWindow::actCheckClientUpdates() +{ + auto checker = new ClientUpdateChecker(this); + connect(checker, &ClientUpdateChecker::finishedCheck, this, &MainWindow::checkClientUpdatesFinished); + checker->check(); +} + +void MainWindow::checkClientUpdatesFinished(bool needToUpdate, bool /* isCompatible */, Release * /* release */) +{ + if (needToUpdate) { + DlgUpdate dlg(this); + dlg.exec(); + } +} + void MainWindow::refreshShortcuts() { ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); diff --git a/cockatrice/src/client/ui/window_main.h b/cockatrice/src/client/ui/window_main.h index 2b240beb1..0c52200ce 100644 --- a/cockatrice/src/client/ui/window_main.h +++ b/cockatrice/src/client/ui/window_main.h @@ -30,6 +30,7 @@ #include #include +class Release; class DlgConnect; class DlgViewLog; class GameReplay; @@ -49,6 +50,7 @@ class MainWindow : public QMainWindow public slots: void actCheckCardUpdates(); void actCheckServerUpdates(); + void actCheckClientUpdates(); private slots: void updateTabMenu(const QList &newMenuList); void statusChanged(ClientStatus _status); @@ -95,6 +97,8 @@ private slots: void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames); void cardDatabaseAllNewSetsEnabled(); + void checkClientUpdatesFinished(bool needToUpdate, bool isCompatible, Release *release); + void actOpenCustomFolder(); void actOpenCustomsetsFolder(); void actAddCustomSet(); diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 4fcbeb407..d4ffd2815 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -61,6 +61,7 @@ GeneralSettingsPage::GeneralSettingsPage() // updates SettingsCache &settings = SettingsCache::instance(); + startupUpdateCheckCheckBox.setChecked(settings.getCheckUpdatesOnStartup()); updateNotificationCheckBox.setChecked(settings.getNotifyAboutUpdates()); newVersionOracleCheckBox.setChecked(settings.getNotifyAboutNewVersion()); @@ -68,6 +69,8 @@ GeneralSettingsPage::GeneralSettingsPage() connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); connect(&updateReleaseChannelBox, SIGNAL(currentIndexChanged(int)), &settings, SLOT(setUpdateReleaseChannel(int))); + connect(&startupUpdateCheckCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setCheckUpdatesOnStartup); connect(&updateNotificationCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setNotifyAboutUpdate); connect(&newVersionOracleCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setNotifyAboutNewVersion); @@ -78,9 +81,10 @@ GeneralSettingsPage::GeneralSettingsPage() personalGrid->addWidget(&languageBox, 0, 1); personalGrid->addWidget(&updateReleaseChannelLabel, 1, 0); personalGrid->addWidget(&updateReleaseChannelBox, 1, 1); - personalGrid->addWidget(&updateNotificationCheckBox, 3, 0, 1, 2); - personalGrid->addWidget(&newVersionOracleCheckBox, 4, 0, 1, 2); - personalGrid->addWidget(&showTipsOnStartup, 5, 0, 1, 2); + personalGrid->addWidget(&startupUpdateCheckCheckBox, 3, 0, 1, 2); + personalGrid->addWidget(&updateNotificationCheckBox, 4, 0, 1, 2); + personalGrid->addWidget(&newVersionOracleCheckBox, 5, 0, 1, 2); + personalGrid->addWidget(&showTipsOnStartup, 6, 0, 1, 2); personalGroupBox = new QGroupBox; personalGroupBox->setLayout(personalGrid); @@ -288,6 +292,7 @@ void GeneralSettingsPage::retranslateUi() customCardDatabasePathLabel.setText(tr("Custom database directory:")); tokenDatabasePathLabel.setText(tr("Token database:")); updateReleaseChannelLabel.setText(tr("Update channel")); + startupUpdateCheckCheckBox.setText(tr("Check for client updates on startup")); updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client")); newVersionOracleCheckBox.setText(tr("Automatically run Oracle when running a new version of Cockatrice")); showTipsOnStartup.setText(tr("Show tips on startup")); diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index d7ecbd95c..983312184 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -65,6 +65,7 @@ private: QGroupBox *personalGroupBox; QGroupBox *pathsGroupBox; QComboBox languageBox; + QCheckBox startupUpdateCheckCheckBox; QCheckBox updateNotificationCheckBox; QCheckBox newVersionOracleCheckBox; QComboBox updateReleaseChannelBox; diff --git a/cockatrice/src/dialogs/dlg_update.cpp b/cockatrice/src/dialogs/dlg_update.cpp index 21f08f827..8ef8ea09b 100644 --- a/cockatrice/src/dialogs/dlg_update.cpp +++ b/cockatrice/src/dialogs/dlg_update.cpp @@ -1,5 +1,6 @@ #include "dlg_update.h" +#include "../client/network/client_update_checker.h" #include "../client/network/release_channel.h" #include "../client/ui/window_main.h" #include "../settings/cache_settings.h" @@ -71,11 +72,6 @@ DlgUpdate::DlgUpdate(QWidget *parent) : QDialog(parent) connect(uDownloader, SIGNAL(progressMade(qint64, qint64)), this, SLOT(downloadProgressMade(qint64, qint64))); connect(uDownloader, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); - ReleaseChannel *channel = SettingsCache::instance().getUpdateReleaseChannel(); - connect(channel, SIGNAL(finishedCheck(bool, bool, Release *)), this, - SLOT(finishedUpdateCheck(bool, bool, Release *))); - connect(channel, SIGNAL(error(QString)), this, SLOT(updateCheckError(QString))); - // Check for updates beginUpdateCheck(); } @@ -110,7 +106,12 @@ void DlgUpdate::beginUpdateCheck() progress->setMinimum(0); progress->setMaximum(0); setLabel(tr("Checking for updates...")); - SettingsCache::instance().getUpdateReleaseChannel()->checkForUpdates(); + + auto checker = new ClientUpdateChecker(this); + connect(checker, SIGNAL(finishedCheck(bool, bool, Release *)), this, + SLOT(finishedUpdateCheck(bool, bool, Release *))); + connect(checker, SIGNAL(error(QString)), this, SLOT(updateCheckError(QString))); + checker->check(); } void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Release *release) diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index 204497c87..618892044 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -192,6 +192,7 @@ SettingsCache::SettingsCache() mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool(); + checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool(); notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool(); notifyAboutNewVersion = settings->value("personal/newversionnotification", true).toBool(); updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt(); @@ -1107,6 +1108,12 @@ void SettingsCache::setDefaultStartingLifeTotal(const int _defaultStartingLifeTo settings->setValue("game/defaultstartinglifetotal", defaultStartingLifeTotal); }; +void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value) +{ + checkUpdatesOnStartup = static_cast(value); + settings->setValue("personal/startupUpdateCheck", checkUpdatesOnStartup); +} + void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings) { rememberGameSettings = _rememberGameSettings; diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index 19c736fbd..6b0a24598 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -93,6 +93,7 @@ private: QString lang; QString deckPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath, customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName; + bool checkUpdatesOnStartup; bool notifyAboutUpdates; bool notifyAboutNewVersion; bool showTipsOnStartup; @@ -269,6 +270,10 @@ public: { return buddyConnectNotificationsEnabled; } + bool getCheckUpdatesOnStartup() const + { + return checkUpdatesOnStartup; + } bool getNotifyAboutUpdates() const { return notifyAboutUpdates; @@ -704,6 +709,7 @@ public slots: void setCreateGameAsSpectator(const bool _createGameAsSpectator); void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal); void setRememberGameSettings(const bool _rememberGameSettings); + void setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value); void setNotifyAboutUpdate(QT_STATE_CHANGED_T _notifyaboutupdate); void setNotifyAboutNewVersion(QT_STATE_CHANGED_T _notifyaboutnewversion); void setUpdateReleaseChannel(int _updateReleaseChannel); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index a8d4cbf90..58ed79bf5 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -309,6 +309,9 @@ void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */) { } +void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T /* value */) +{ +} void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T /* _notifyaboutupdate */) { } diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index b251bbd7f..cfa2e947d 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -313,6 +313,9 @@ void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */) { } +void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T /* value */) +{ +} void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T /* _notifyaboutupdate */) { }