diff --git a/cmake/FindQtRuntime.cmake b/cmake/FindQtRuntime.cmake index 971c9094d..0259d12e1 100644 --- a/cmake/FindQtRuntime.cmake +++ b/cmake/FindQtRuntime.cmake @@ -28,7 +28,7 @@ if(WITH_CLIENT) ) endif() if(WITH_ORACLE) - set(_ORACLE_NEEDED Concurrent Network Svg Widgets Xml) + set(_ORACLE_NEEDED Concurrent Network Svg Widgets) endif() if(TEST) # Union of Qt modules required across all test targets (independent of application targets). diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 63ccc4e9c..9e0331d69 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -382,7 +382,6 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/tab_card_art_rules.cpp src/interface/widgets/tabs/tab_deck_editor.cpp src/interface/widgets/tabs/tab_deck_storage.cpp - src/interface/widgets/tabs/tab_developer.cpp src/interface/widgets/tabs/tab_game.cpp src/interface/widgets/tabs/tab_home.cpp src/interface/widgets/tabs/tab_logs.cpp diff --git a/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp b/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp index c1598bd25..890a621c8 100644 --- a/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp +++ b/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp @@ -1,6 +1,5 @@ #include "remote_connection_controller.h" -#include "../../../interface/pixel_map_generator.h" #include "../../settings/cache_settings.h" #include "../interface/widgets/dialogs/dlg_connect.h" #include "../interface/widgets/dialogs/dlg_forgot_password_challenge.h" @@ -181,7 +180,7 @@ void ConnectionController::onServerShutdownEvent(const Event_ServerShutdown &eve "games will be lost.\nReason for shutdown: %1", "", event.minutes()) .arg(QString::fromStdString(event.reason()))); - serverShutdownMessageBox.setIconPixmap(themePixmap(QStringLiteral("cockatrice")).scaled(64, 64)); + serverShutdownMessageBox.setIconPixmap(QPixmap("theme:cockatrice").scaled(64, 64)); serverShutdownMessageBox.setText(tr("Scheduled server shutdown")); serverShutdownMessageBox.setWindowModality(Qt::ApplicationModal); serverShutdownMessageBox.setVisible(true); diff --git a/cockatrice/src/filters/filter_builder.cpp b/cockatrice/src/filters/filter_builder.cpp index f109fbcd3..785f753e7 100644 --- a/cockatrice/src/filters/filter_builder.cpp +++ b/cockatrice/src/filters/filter_builder.cpp @@ -1,6 +1,5 @@ #include "filter_builder.h" -#include "../interface/pixel_map_generator.h" #include "../interface/widgets/utility/custom_line_edit.h" #include @@ -22,7 +21,7 @@ FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent) typeCombo->addItem(CardFilter::typeName(static_cast(i)), QVariant(i)); } - QPushButton *ok = new QPushButton(themePixmap(QStringLiteral("icons/increment")), QString()); + QPushButton *ok = new QPushButton(QPixmap("theme:icons/increment"), QString()); ok->setObjectName("ok"); ok->setMaximumSize(20, 20); diff --git a/cockatrice/src/game_graphics/hand_counter.cpp b/cockatrice/src/game_graphics/hand_counter.cpp index 8dcbcfdaa..35989ff38 100644 --- a/cockatrice/src/game_graphics/hand_counter.cpp +++ b/cockatrice/src/game_graphics/hand_counter.cpp @@ -1,6 +1,5 @@ #include "hand_counter.h" -#include "../interface/pixel_map_generator.h" #include "zones/card_zone.h" #include @@ -33,8 +32,7 @@ void HandCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op QSize translatedSize = painter->combinedTransform().mapRect(boundingRect()).size().toSize(); QPixmap cachedPixmap; if (!QPixmapCache::find("handCounter" + QString::number(translatedSize.width()), &cachedPixmap)) { - cachedPixmap = - themePixmap(QStringLiteral("hand")).scaled(translatedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + cachedPixmap = QPixmap("theme:hand").scaled(translatedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); QPixmapCache::insert("handCounter" + QString::number(translatedSize.width()), cachedPixmap); } resetPainterTransform(painter); diff --git a/cockatrice/src/game_graphics/player/player_list_widget.cpp b/cockatrice/src/game_graphics/player/player_list_widget.cpp index a7ec2e4a9..13a077af8 100644 --- a/cockatrice/src/game_graphics/player/player_list_widget.cpp +++ b/cockatrice/src/game_graphics/player/player_list_widget.cpp @@ -53,13 +53,13 @@ PlayerListWidget::PlayerListWidget(TabSupervisor *_tabSupervisor, QWidget *parent) : QTreeWidget(parent), tabSupervisor(_tabSupervisor), client(_client), game(_game), gameStarted(false) { - readyIcon = themePixmap(QStringLiteral("icons/ready_start")); - notReadyIcon = themePixmap(QStringLiteral("icons/not_ready_start")); - concededIcon = themePixmap(QStringLiteral("icons/conceded")); + readyIcon = QPixmap("theme:icons/ready_start"); + notReadyIcon = QPixmap("theme:icons/not_ready_start"); + concededIcon = QPixmap("theme:icons/conceded"); playerIcon = loadColorAdjustedPixmap("theme:icons/player"); judgeIcon = loadColorAdjustedPixmap("theme:icons/scales"); spectatorIcon = loadColorAdjustedPixmap("theme:icons/spectator"); - lockIcon = themePixmap(QStringLiteral("icons/lock")); + lockIcon = QPixmap("theme:icons/lock"); if (tabSupervisor) { itemDelegate = new PlayerListItemDelegate(this); diff --git a/cockatrice/src/game_graphics/zones/view_zone_widget.cpp b/cockatrice/src/game_graphics/zones/view_zone_widget.cpp index c13b79fea..17118e80d 100644 --- a/cockatrice/src/game_graphics/zones/view_zone_widget.cpp +++ b/cockatrice/src/game_graphics/zones/view_zone_widget.cpp @@ -62,7 +62,7 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player, searchEdit.setPlaceholderText(tr("Search by card name (or search expressions)")); searchEdit.setClearButtonEnabled(true); searchEdit.addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); - auto help = searchEdit.addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition); + auto help = searchEdit.addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(&searchEdit); }); @@ -549,7 +549,7 @@ void ZoneViewWidget::initStyleOption(QStyleOption *option) const { QStyleOptionTitleBar *titleBar = qstyleoption_cast(option); if (titleBar) { - titleBar->icon = themePixmap(QStringLiteral("cockatrice")); + titleBar->icon = QPixmap("theme:cockatrice"); } } diff --git a/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp b/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp index 8c81d641d..7daafb610 100644 --- a/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp +++ b/cockatrice/src/interface/card_picture_loader/card_picture_loader.cpp @@ -1,7 +1,6 @@ #include "card_picture_loader.h" #include "../../client/settings/cache_settings.h" -#include "../pixel_map_generator.h" #include "card_picture_loader_cache_method.h" #include "card_picture_loader_local_schemes.h" @@ -63,7 +62,7 @@ void CardPictureLoader::getCardBackPixmap(QPixmap &pixmap, QSize size) QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + "x" + QString::number(size.height()); if (!QPixmapCache::find(backCacheKey, &pixmap)) { qCDebug(CardPictureLoaderLog) << "PictureLoader: cache miss for" << backCacheKey; - QPixmap tmpPixmap = themePixmap(QStringLiteral("cardback")); + QPixmap tmpPixmap("theme:cardback"); if (tmpPixmap.isNull()) { qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback'! Using fallback pixmap."; @@ -84,7 +83,7 @@ void CardPictureLoader::getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSiz "_trice_card_back_inprogress_" + QString::number(size.width()) + "x" + QString::number(size.height()); if (!QPixmapCache::find(backCacheKey, &pixmap)) { qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey; - QPixmap tmpPixmap = themePixmap(QStringLiteral("cardback")); + QPixmap tmpPixmap("theme:cardback"); if (tmpPixmap.isNull()) { qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for in-progress state! Using fallback."; @@ -106,7 +105,7 @@ void CardPictureLoader::getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize si "_trice_card_back_failed_" + QString::number(size.width()) + "x" + QString::number(size.height()); if (!QPixmapCache::find(backCacheKey, &pixmap)) { qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey; - QPixmap tmpPixmap = themePixmap(QStringLiteral("cardback")); + QPixmap tmpPixmap("theme:cardback"); if (tmpPixmap.isNull()) { qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for failed state! Using fallback."; diff --git a/cockatrice/src/interface/palette_editor/palette_editor_dialog.cpp b/cockatrice/src/interface/palette_editor/palette_editor_dialog.cpp index d5a168708..9cde72c01 100644 --- a/cockatrice/src/interface/palette_editor/palette_editor_dialog.cpp +++ b/cockatrice/src/interface/palette_editor/palette_editor_dialog.cpp @@ -1,5 +1,6 @@ #include "palette_editor_dialog.h" +#include "../../client/settings/cache_settings.h" #include "../theme_manager.h" #include "palette_generator.h" #include "palette_grid_widget.h" @@ -10,11 +11,31 @@ #include #include #include +#include #include +#include #include +#include #include #include +#include #include +#include + +// Probe whether a directory is truly writable by trying to create and remove a +// temporary file. QFileInfo::isWritable() on a directory is unreliable (notably +// on Windows where UAC VirtualStore can make a system dir appear writable). +static bool isDirReallyWritable(const QString &dirPath) +{ + const QString probe = QDir(dirPath).absoluteFilePath(".cockatrice_write_test"); + QFile f(probe); + if (!f.open(QIODevice::WriteOnly)) { + return false; + } + f.close(); + f.remove(); + return true; +} PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QString &_themeName, QWidget *parent) : QDialog(parent), themeDirPath(_themeDirPath), themeName(_themeName) @@ -25,7 +46,14 @@ PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QSt // Resolve a writable directory for saving. Built-in (Default / Fusion) and // other read-only theme directories must be customised in the user-writable // themes directory; otherwise the write would fail or be lost on upgrade. - saveDir = ThemeManager::writableThemeDir(themeName); + if (!themeDirPath.isEmpty() && isDirReallyWritable(themeDirPath)) { + saveDir = themeDirPath; + } else { + saveDir = QDir(SettingsCache::instance().paths().getThemesPath()).absoluteFilePath(themeName); + if (!QDir().mkpath(saveDir)) { + qWarning() << "Failed to create palette save directory:" << saveDir; + } + } // Load both scheme configs upfront so switching is instant loadSchemes(); @@ -186,7 +214,7 @@ void PaletteEditorDialog::retranslateUi() resetBtn->setToolTip(tr("Discard unsaved edits and restore the last saved palette")); saveBtn->setToolTip(tr("Write palette-%1.toml and reload the theme").arg(loadedScheme.toLower())); - if (saveDir.isEmpty() || !ThemeManager::isDirReallyWritable(saveDir)) { + if (saveDir.isEmpty() || !isDirReallyWritable(saveDir)) { saveBtn->setEnabled(false); saveBtn->setToolTip(tr("Cannot save: this theme has no writable directory")); } @@ -269,7 +297,7 @@ void PaletteEditorDialog::onSave() if (it.key() == loadedScheme) { continue; } - if (it.value() == savedConfig.value(it.key())) { + if (it.value().colors == savedConfig.value(it.key()).colors) { continue; } if (!ThemeManager::commitPalette(saveDir, it.key(), it.value())) { @@ -280,7 +308,7 @@ void PaletteEditorDialog::onSave() } // Commit the active scheme last so the global colour scheme matches. - if (workingConfig[loadedScheme] != savedConfig.value(loadedScheme)) { + if (workingConfig[loadedScheme].colors != savedConfig.value(loadedScheme).colors) { if (!ThemeManager::commitPalette(saveDir, loadedScheme, workingConfig[loadedScheme])) { QMessageBox::warning(this, tr("Save failed"), tr("Could not write %1 to:\n%2").arg(PaletteConfig::fileName(loadedScheme), saveDir)); diff --git a/cockatrice/src/interface/palette_editor/palette_grid_widget.cpp b/cockatrice/src/interface/palette_editor/palette_grid_widget.cpp index 97d28b731..f66ef1032 100644 --- a/cockatrice/src/interface/palette_editor/palette_grid_widget.cpp +++ b/cockatrice/src/interface/palette_editor/palette_grid_widget.cpp @@ -49,7 +49,7 @@ static const QMap ROLE_DESCRIPTIONS = { static const QMap APP_ROLE_DESCRIPTIONS = { {AppColor::AccentStrong, QT_TR_NOOP("Vivid primary accent (e.g. home-tab button gradient start)")}, - {AppColor::AccentSoft, QT_TR_NOOP("Lightened, desaturated accent (e.g. home-tab button gradient end)")}, + {AppColor::AccentSoft, QT_TR_NOOP("Lighted, desaturated accent (e.g. home-tab button gradient end)")}, }; PaletteGridWidget::PaletteGridWidget(QWidget *parent) : QWidget(parent) @@ -144,10 +144,9 @@ void PaletteGridWidget::buildGrid(QWidget *host) appHeader->setAutoFillBackground(true); appHeader->setContentsMargins(4, 4, 4, 4); grid->addWidget(appHeader, appHeaderRow, 0, 1, 4); - headerLabels.append(appHeader); for (int i = 0; i < appEnum.keyCount(); ++i) { - auto role = static_cast(appEnum.value(i)); + auto role = static_cast(i); const int row = appHeaderRow + 1 + i; if (i % 2 == 0) { @@ -216,7 +215,7 @@ void PaletteGridWidget::loadPalette(const PaletteConfig &cfg) QMetaEnum appEnum = QMetaEnum::fromType(); for (int i = 0; i < appEnum.keyCount(); ++i) { - auto role = static_cast(appEnum.value(i)); + auto role = static_cast(i); QColor color = cfg.appColors.value(role); if (!color.isValid()) { color = themeManager->appColor(role); @@ -236,7 +235,7 @@ PaletteConfig PaletteGridWidget::currentPaletteConfig() const QMetaEnum appEnum = QMetaEnum::fromType(); for (int i = 0; i < appEnum.keyCount(); ++i) { - auto role = static_cast(appEnum.value(i)); + auto role = static_cast(i); cfg.appColors[role] = appColorButtons[role]->getColor(); } diff --git a/cockatrice/src/interface/pixel_map_generator.cpp b/cockatrice/src/interface/pixel_map_generator.cpp index e74e86471..9b8c4bcdc 100644 --- a/cockatrice/src/interface/pixel_map_generator.cpp +++ b/cockatrice/src/interface/pixel_map_generator.cpp @@ -1,7 +1,5 @@ #include "pixel_map_generator.h" -#include "theme_manager.h" - #include #include #include @@ -16,7 +14,6 @@ #define DEFAULT_COLOR_MODERATOR_LEFT "#ffffff"; #define DEFAULT_COLOR_MODERATOR_RIGHT "#000000"; #define DEFAULT_COLOR_ADMIN "#ff2701"; -#define DEFAULT_COLOR_DEVELOPER "#B8B8B8" /** * Clamps an svg render size so that rendering does not exceed a multiple of the requested size. @@ -85,13 +82,7 @@ static QPixmap loadSvg(const QString &svgPath, const QSize &size, bool expandOnl /** * Try to load path image from non-SVG formats, otherwise fall back to SVG. * This is to allow custom themes to support non-SVG format type overrides, since SVG requires custom loading. - * - * The path may already carry the resolved file extension (e.g. via - * ThemeManager::assetPath); such paths are loaded directly. Otherwise a - * format-agnostic lookup probes png, jpg and finally svg. - * - * @param path The path to the file, with no file extension unless the caller - * already resolved it. File formats will be automatically detected. + * @param path The path to the file, with no file extension. File formats will be automatically detected. * @param size The desired size of the pixmap. * @param expandOnly If true, then keep the size of the initial pixmap to at least the size (Only relevant if SVG). * @@ -99,19 +90,6 @@ static QPixmap loadSvg(const QString &svgPath, const QSize &size, bool expandOnl */ static QPixmap tryLoadImage(const QString &path, const QSize &size, bool expandOnly = false) { - if (path.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)) { - return loadSvg(path, size, expandOnly); - } - if (path.endsWith(QLatin1String(".png"), Qt::CaseInsensitive) || - path.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) || - path.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive)) { - QPixmap pix(path); - if (!pix.isNull()) { - return pix.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - } - return {}; - } - const auto formats = {"png", "jpg"}; QPixmap returnPixmap; @@ -133,8 +111,7 @@ QPixmap PhasePixmapGenerator::generatePixmap(int height, QString name) return pmCache.value(key); } - QPixmap pixmap = tryLoadImage(QStringLiteral("theme:") + themeManager->assetPath(QStringLiteral("phases/") + name), - QSize(height, height)); + QPixmap pixmap = tryLoadImage("theme:phases/" + name, QSize(height, height)); pmCache.insert(key, pixmap); return pixmap; @@ -382,8 +359,6 @@ QIcon UserLevelPixmapGenerator::generateIconDefault(int height, if (userLevel.testFlag(ServerInfo_User::IsAdmin)) { colorLeft = DEFAULT_COLOR_ADMIN; - } else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) { - colorLeft = DEFAULT_COLOR_DEVELOPER; } else if (userLevel.testFlag(ServerInfo_User::IsModerator)) { colorLeft = DEFAULT_COLOR_MODERATOR_LEFT; colorRight = DEFAULT_COLOR_MODERATOR_RIGHT; @@ -421,8 +396,7 @@ QPixmap LockPixmapGenerator::generatePixmap(int height) return pmCache.value(key); } - QPixmap pixmap = tryLoadImage(QStringLiteral("theme:") + themeManager->assetPath(QStringLiteral("icons/lock")), - QSize(height, height), true); + QPixmap pixmap = tryLoadImage("theme:icons/lock", QSize(height, height), true); pmCache.insert(key, pixmap); return pixmap; } @@ -437,8 +411,7 @@ QPixmap DropdownIconPixmapGenerator::generatePixmap(int height, bool expanded) } QString name = expanded ? "dropdown_expanded" : "dropdown_collapsed"; - QPixmap pixmap = tryLoadImage(QStringLiteral("theme:") + themeManager->assetPath(QStringLiteral("icons/") + name), - QSize(height, height), true); + QPixmap pixmap = tryLoadImage("theme:icons/" + name, QSize(height, height), true); pmCache.insert(key, pixmap); return pixmap; @@ -499,13 +472,6 @@ QHash ManaSymbolPixmapGenerator::scaledCache; QPixmap loadColorAdjustedPixmap(const QString &name) { - // Prefer an authored scheme-qualified variant when one exists for this asset. - const QString variant = themeManager->schemeVariantPath(QStringView(name).mid(QStringLiteral("theme:").size())); - if (!variant.isEmpty()) { - return QPixmap(QStringLiteral("theme:") + variant); - } - - // Legacy fallback: runtime-invert for dark mode when no authored variant. if (qApp->palette().windowText().color().lightness() > 200) { QImage img(name); img.invertPixels(); @@ -516,21 +482,3 @@ QPixmap loadColorAdjustedPixmap(const QString &name) return QPixmap(name); } } - -QPixmap themePixmap(QStringView prefix) -{ - const QString resolved = themeManager->assetPath(prefix); - return QPixmap(QStringLiteral("theme:") + resolved); -} - -void clearPixmapGeneratorCaches() -{ - PhasePixmapGenerator::clear(); - CounterPixmapGenerator::clear(); - PingPixmapGenerator::clear(); - CountryPixmapGenerator::clear(); - UserLevelPixmapGenerator::clear(); - LockPixmapGenerator::clear(); - DropdownIconPixmapGenerator::clear(); - ManaSymbolPixmapGenerator::clear(); -} diff --git a/cockatrice/src/interface/pixel_map_generator.h b/cockatrice/src/interface/pixel_map_generator.h index b6e822fd9..17720166a 100644 --- a/cockatrice/src/interface/pixel_map_generator.h +++ b/cockatrice/src/interface/pixel_map_generator.h @@ -156,15 +156,4 @@ public: QPixmap loadColorAdjustedPixmap(const QString &name); -// Loads a "theme:" asset (with no file extension in prefix), preferring the -// scheme-qualified variant (prefix-dark / prefix-light, resolved via -// ThemeManager::assetPath) and falling back to the plain asset. Callers load -// the returned path directly. Use for scheme-sensitive pixmaps like -// backgrounds, the card back, and the app logo. -QPixmap themePixmap(QStringView prefix); - -// Clears every PixmapGenerator's static cache so scheme variants are -// re-resolved when the active theme or color scheme changes. -void clearPixmapGeneratorCaches(); - #endif diff --git a/cockatrice/src/interface/theme_config.cpp b/cockatrice/src/interface/theme_config.cpp index 4420eefe1..8de2fe6b9 100644 --- a/cockatrice/src/interface/theme_config.cpp +++ b/cockatrice/src/interface/theme_config.cpp @@ -96,7 +96,7 @@ bool ThemeConfig::save(const QString &themeDirPath) const bool PaletteConfig::hasPalette() const { - return !colors.isEmpty() || !appColors.isEmpty(); + return !colors.isEmpty(); } QString PaletteConfig::toToml() const diff --git a/cockatrice/src/interface/theme_config.h b/cockatrice/src/interface/theme_config.h index 567aeccda..08978d1f7 100644 --- a/cockatrice/src/interface/theme_config.h +++ b/cockatrice/src/interface/theme_config.h @@ -39,14 +39,6 @@ struct PaletteConfig QMap> colors; QMap appColors; - bool operator==(const PaletteConfig &rhs) const - { - return colors == rhs.colors && appColors == rhs.appColors; - } - bool operator!=(const PaletteConfig &rhs) const - { - return !(*this == rhs); - } bool hasPalette() const; QString toToml() const; diff --git a/cockatrice/src/interface/theme_manager.cpp b/cockatrice/src/interface/theme_manager.cpp index 0435f1e1d..a0549a5f9 100644 --- a/cockatrice/src/interface/theme_manager.cpp +++ b/cockatrice/src/interface/theme_manager.cpp @@ -1,13 +1,10 @@ #include "theme_manager.h" #include "../../client/settings/cache_settings.h" -#include "pixel_map_generator.h" #include #include #include -#include -#include #include #include #include @@ -143,74 +140,11 @@ bool ThemeManager::isDarkMode(const QString &themeDirPath) const } } -QString ThemeManager::schemeVariantPath(QStringView prefix) const +bool ThemeManager::isBuiltInTheme() { - static const QStringList formats = {QStringLiteral(".png"), QStringLiteral(".jpg"), QStringLiteral(".jpeg"), - QStringLiteral(".svg")}; - const QString scheme = isDarkMode(currentThemePath) ? QStringLiteral("dark") : QStringLiteral("light"); - const QString variantStem = prefix.toString() + QLatin1Char('-') + scheme; + const auto themeName = SettingsCache::instance().getThemeName(); - for (const QString &format : formats) { - if (QFileInfo::exists(QStringLiteral("theme:") + variantStem + format)) { - return variantStem + format; - } - } - return QString(); -} - -QString ThemeManager::assetPath(QStringView prefix) const -{ - // Probe order mirrors tryLoadImage: a theme may override the default SVG - // with a raster of the same stem, so raster wins over SVG within a stem. - static const QStringList formats = {QStringLiteral(".png"), QStringLiteral(".jpg"), QStringLiteral(".jpeg"), - QStringLiteral(".svg")}; - - auto findExisting = [](const QString &stem) { - for (const QString &format : formats) { - if (QFileInfo::exists(QStringLiteral("theme:") + stem + format)) { - return stem + format; - } - } - return QString(); - }; - - // Prefer the scheme-qualified variant when it exists, else the plain - // asset as the super fallback. Both return the resolved path including - // its file extension so callers can load it directly. - const QString variant = schemeVariantPath(prefix); - if (!variant.isEmpty()) { - return variant; - } - const QString resolvedPlain = findExisting(prefix.toString()); - return resolvedPlain.isEmpty() ? prefix.toString() : resolvedPlain; -} - -// Probe whether a directory is truly writable by trying to create and remove a -// temporary file. QFileInfo::isWritable() on a directory is unreliable (notably -// on Windows where UAC VirtualStore can make a system dir appear writable). -bool ThemeManager::isDirReallyWritable(const QString &dirPath) -{ - const QString probe = QDir(dirPath).absoluteFilePath(".cockatrice_write_test"); - QFile f(probe); - if (!f.open(QIODevice::WriteOnly)) { - return false; - } - f.close(); - f.remove(); - return true; -} - -QString ThemeManager::writableThemeDir(const QString &themeName) -{ - // All theme writes go to the user themes directory regardless of whether - // the resolved (system) theme directory happens to be writable. Even when a - // write would succeed in-place, routing it to the user directory keeps the - // install intact and guarantees changes survive upgrades. - const QString dirPath = QDir(SettingsCache::instance().paths().getThemesPath()).absoluteFilePath(themeName); - if (!QDir().mkpath(dirPath)) { - qWarning() << "Failed to create theme save directory:" << dirPath; - } - return dirPath; + return themeName == NONE_THEME_NAME || themeName == FUSION_THEME_NAME; } // System (read-only) themes location, relative to the application binary. @@ -261,7 +195,7 @@ QStringMap &ThemeManager::getAvailableThemes() QBrush ThemeManager::loadBrush(QString fileName, QColor fallbackColor) { QBrush brush; - QPixmap tmp = QPixmap("theme:" + assetPath(QStringLiteral("zones/") + fileName)); + QPixmap tmp = QPixmap("theme:zones/" + fileName); if (tmp.isNull()) { brush.setColor(fallbackColor); brush.setStyle(Qt::SolidPattern); @@ -275,7 +209,7 @@ QBrush ThemeManager::loadBrush(QString fileName, QColor fallbackColor) QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush) { QBrush brush; - QPixmap tmp = QPixmap("theme:" + assetPath(QStringLiteral("zones/") + fileName)); + QPixmap tmp = QPixmap("theme:zones/" + fileName); if (tmp.isNull()) { brush = fallbackBrush; @@ -353,7 +287,7 @@ bool ThemeManager::commitPalette(const QString &themeDirPath, const QString &col void ThemeManager::setColorScheme(const QString &scheme) { - const QString dirPath = writableThemeDir(SettingsCache::instance().getThemeName()); + const QString dirPath = getAvailableThemes().value(SettingsCache::instance().getThemeName()); ThemeConfig cfg = ThemeConfig::fromThemeDir(dirPath); cfg.colorScheme = scheme; @@ -364,7 +298,7 @@ void ThemeManager::setColorScheme(const QString &scheme) void ThemeManager::setStyleName(const QString &styleName) { - const QString dirPath = writableThemeDir(SettingsCache::instance().getThemeName()); + const QString dirPath = getAvailableThemes().value(SettingsCache::instance().getThemeName()); ThemeConfig cfg = ThemeConfig::fromThemeDir(dirPath); cfg.styleName = styleName; @@ -463,12 +397,14 @@ QColor ThemeManager::appColor(AppColor::Role role) const return it.value(); } - // QPalette::Accent was introduced in Qt 6.6 and several shipped palettes - // set it to a value barely distinguishable from Window, so it is not a - // reliable accent source. The selection highlight is the stable accent - // (Accent defaults to Highlight when unset), and deriving from it - // unconditionally keeps every Qt version rendering identically. - const QColor accent = qApp->palette().color(QPalette::Active, QPalette::Highlight); + // QPalette::Accent was introduced in Qt 6.6; before that the nearest + // accent is the selection highlight, which Accent defaults to when unset. + const QColor accent = qApp->palette().color(QPalette::Active, +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + QPalette::Accent); +#else + QPalette::Highlight); +#endif if (role == AppColor::AccentSoft) { constexpr int SOFT_SATURATION_PERCENT = 70; @@ -490,19 +426,9 @@ void ThemeManager::themeChangedSlot() currentThemePath = dirPath; QDir dir(dirPath); - // CSS — prefer the scheme-qualified stylesheet (style-dark.css / - // style-light.css) when present, else the plain style.css as fallback. - if (!dirPath.isEmpty()) { - const QString scheme = isDarkMode(dirPath) ? QStringLiteral("dark") : QStringLiteral("light"); - const QString schemeCss = QFileInfo(QStringLiteral(STYLE_CSS_NAME)).completeBaseName() + QLatin1Char('-') + - scheme + QStringLiteral(".css"); - if (dir.exists(schemeCss)) { - qApp->setStyleSheet("file:///" + dir.absoluteFilePath(schemeCss)); - } else if (dir.exists(STYLE_CSS_NAME)) { - qApp->setStyleSheet("file:///" + dir.absoluteFilePath(STYLE_CSS_NAME)); - } else { - qApp->setStyleSheet(""); - } + // CSS + if (!dirPath.isEmpty() && dir.exists(STYLE_CSS_NAME)) { + qApp->setStyleSheet("file:///" + dir.absoluteFilePath(STYLE_CSS_NAME)); } else { qApp->setStyleSheet(""); } @@ -517,19 +443,8 @@ void ThemeManager::themeChangedSlot() // ── Load palette: custom first, then theme default ──────────────────── PaletteConfig palette = PaletteConfig::fromScheme(dirPath, activeScheme); - const PaletteConfig themeDefault = ThemeManager::loadDefaultPaletteConfig(dirPath, themeName, activeScheme); - if (palette.hasPalette()) { - // A custom palette written before [AppColors] existed carries no app - // colors; merge the theme's shipped defaults so the identity colors - // survive (hasPalette() counts an app-colors-only file as a palette, - // so those are kept wholesale and never reach here empty). - for (auto it = themeDefault.appColors.cbegin(); it != themeDefault.appColors.cend(); ++it) { - if (!palette.appColors.contains(it.key())) { - palette.appColors.insert(it.key(), it.value()); - } - } - } else { - palette = themeDefault; + if (!palette.hasPalette()) { + palette = ThemeManager::loadDefaultPaletteConfig(dirPath, themeName, activeScheme); } applyStyleAndPalette(themeName, themeCfg, palette, activeScheme); @@ -564,7 +479,6 @@ void ThemeManager::themeChangedSlot() } QPixmapCache::clear(); - clearPixmapGeneratorCaches(); emit themeChanged(); } diff --git a/cockatrice/src/interface/theme_manager.h b/cockatrice/src/interface/theme_manager.h index aadb38ee9..00ee9fce9 100644 --- a/cockatrice/src/interface/theme_manager.h +++ b/cockatrice/src/interface/theme_manager.h @@ -66,16 +66,7 @@ protected: const QString &activeScheme); public: - // Resolves the directory to write theme changes to for the given theme - // name. The resolved theme dir (user or system) is used when writable; - // read-only system themes fall back to the user themes directory, creating - // it if needed, so customisations never get lost on upgrade. - static QString writableThemeDir(const QString &themeName); - // Probe whether a directory is truly writable by trying to create and remove - // a temporary file. QFileInfo::isWritable() on a directory is unreliable - // (notably on Windows where UAC VirtualStore can make a system dir appear - // writable). - static bool isDirReallyWritable(const QString &dirPath); + bool isBuiltInTheme(); // Explicit color scheme of the theme: theme.cfg's ColorScheme setting // (Dark/Light), falling back to the OS color scheme when it is "System". bool isDarkMode(const QString &themeDirPath) const; @@ -97,20 +88,6 @@ public: // Load/save per-scheme palette colors static PaletteConfig loadPaletteConfig(const QString &themeDirPath, const QString &colorScheme); static bool savePaletteConfig(const QString &themeDirPath, const QString &colorScheme, const PaletteConfig &cfg); - // Resolve prefix to a scheme-qualified "theme:" path. Existence is probed - // internally across the formats themes may ship (.png/.jpg/.svg), so - // callers load the returned path directly. Prefers "-" - // when a file exists at that stem, otherwise the plain "" as the - // super fallback. The resolved scheme covers explicit light/dark as well - // as OS-resolved "system". Returns the path with its file extension when a - // match is found; unqualified assets keep working unchanged. - QString assetPath(QStringView prefix) const; - // Like assetPath, but resolves only the scheme-qualified variant - // ("-.") and returns an empty string when no - // variant exists — it never falls back to the plain "" asset. - // Callers that must distinguish "no authored variant" (e.g. to keep a - // legacy runtime fallback alive) should use this instead of assetPath. - QString schemeVariantPath(QStringView prefix) const; // Load the theme's shipped default palette, falling back to the system // theme directory when it is absent from the resolved (user) directory. static PaletteConfig diff --git a/cockatrice/src/interface/widgets/deck_analytics/abstract_analytics_panel_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/abstract_analytics_panel_widget.cpp index 483fc71b0..089abc5c8 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/abstract_analytics_panel_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/abstract_analytics_panel_widget.cpp @@ -1,6 +1,5 @@ #include "abstract_analytics_panel_widget.h" -#include "../../pixel_map_generator.h" #include "deck_list_statistics_analyzer.h" #include @@ -21,7 +20,7 @@ AbstractAnalyticsPanelWidget::AbstractAnalyticsPanelWidget(QWidget *parent, Deck // config button configureButton = new QPushButton(this); - configureButton->setIcon(themePixmap(QStringLiteral("icons/cogwheel"))); + configureButton->setIcon(QPixmap("theme:icons/cogwheel")); configureButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); connect(configureButton, &QPushButton::clicked, this, &AbstractAnalyticsPanelWidget::applyConfigFromDialog); bannerAndSettingsLayout->addWidget(configureButton, 0); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp index c8bdacf0d..9da821813 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp @@ -28,7 +28,7 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)")); searchEdit->setClearButtonEnabled(true); searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); - auto help = searchEdit->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition); + auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); setFocusProxy(searchEdit); setFocusPolicy(Qt::ClickFocus); @@ -59,13 +59,13 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent &DeckEditorDatabaseDisplayWidget::onRelatedCardClicked); aAddCard = new QAction(QString(), this); - aAddCard->setIcon(themePixmap(QStringLiteral("icons/arrow_right_green"))); + aAddCard->setIcon(QPixmap("theme:icons/arrow_right_green")); connect(aAddCard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); auto *tbAddCard = new QToolButton(this); tbAddCard->setDefaultAction(aAddCard); aAddCardToSideboard = new QAction(QString(), this); - aAddCardToSideboard->setIcon(themePixmap(QStringLiteral("icons/arrow_right_blue"))); + aAddCardToSideboard->setIcon(QPixmap("theme:icons/arrow_right_blue")); connect(aAddCardToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); auto *tbAddCardToSideboard = new QToolButton(this); tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index a3653f03e..e2175a358 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../../pixel_map_generator.h" #include "../playmat/playmat_settings_dialog.h" #include "../settings_page/user_interface_settings_page.h" #include "../tabs/api/commander_spellbook/commander_bracket_widget.h" @@ -193,25 +192,25 @@ void DeckEditorDeckDockWidget::createDeckDock() &DeckEditorDeckDockWidget::applyActiveGroupCriteria); aIncrement = new QAction(QString(), this); - aIncrement->setIcon(themePixmap(QStringLiteral("icons/increment"))); + aIncrement->setIcon(QPixmap("theme:icons/increment")); connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection); auto *tbIncrement = new QToolButton(this); tbIncrement->setDefaultAction(aIncrement); aDecrement = new QAction(QString(), this); - aDecrement->setIcon(themePixmap(QStringLiteral("icons/decrement"))); + aDecrement->setIcon(QPixmap("theme:icons/decrement")); connect(aDecrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actDecrementSelection); auto *tbDecrement = new QToolButton(this); tbDecrement->setDefaultAction(aDecrement); aRemoveCard = new QAction(QString(), this); - aRemoveCard->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + aRemoveCard->setIcon(QPixmap("theme:icons/remove_row")); connect(aRemoveCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actRemoveCard); auto *tbRemoveCard = new QToolButton(this); tbRemoveCard->setDefaultAction(aRemoveCard); aSwapCard = new QAction(QString(), this); - aSwapCard->setIcon(themePixmap(QStringLiteral("icons/swap"))); + aSwapCard->setIcon(QPixmap("theme:icons/swap")); connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapSelection); auto *tbSwapCard = new QToolButton(this); tbSwapCard->setDefaultAction(aSwapCard); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp index c6b6e4416..2d4fb60e8 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp @@ -4,7 +4,6 @@ #include "../../../client/settings/shortcuts_settings.h" #include "../../../filters/filter_builder.h" #include "../../../filters/filter_tree_model.h" -#include "../../pixel_map_generator.h" #include #include @@ -43,11 +42,11 @@ void DeckEditorFilterDockWidget::createFiltersDock() connect(filterBuilder, &FilterBuilder::add, filterModel, &FilterTreeModel::addFilter); aClearFilterOne = new QAction(QString(), this); - aClearFilterOne->setIcon(themePixmap(QStringLiteral("icons/decrement"))); + aClearFilterOne->setIcon(QPixmap("theme:icons/decrement")); connect(aClearFilterOne, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterOne); aClearFilterAll = new QAction(QString(), this); - aClearFilterAll->setIcon(themePixmap(QStringLiteral("icons/clearsearch"))); + aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch")); connect(aClearFilterAll, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterAll); auto *filterDelOne = new QToolButton(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp index c93f12b34..cef459752 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp @@ -1,6 +1,5 @@ #include "deck_list_history_manager_widget.h" -#include "../../pixel_map_generator.h" #include "deck_state_manager.h" DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager, @@ -11,7 +10,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_de layout = new QHBoxLayout(this); aUndo = new QAction(QString(), this); - aUndo->setIcon(themePixmap(QStringLiteral("icons/arrow_undo"))); + aUndo->setIcon(QPixmap("theme:icons/arrow_undo")); aUndo->setShortcut(QKeySequence::Undo); aUndo->setShortcutContext(Qt::ApplicationShortcut); connect(aUndo, &QAction::triggered, this, &DeckListHistoryManagerWidget::doUndo); @@ -20,7 +19,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_de undoButton->setDefaultAction(aUndo); aRedo = new QAction(QString(), this); - aRedo->setIcon(themePixmap(QStringLiteral("icons/arrow_redo"))); + aRedo->setIcon(QPixmap("theme:icons/arrow_redo")); aRedo->setShortcut(QKeySequence::Redo); aRedo->setShortcutContext(Qt::ApplicationShortcut); connect(aRedo, &QAction::triggered, this, &DeckListHistoryManagerWidget::doRedo); @@ -32,7 +31,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_de layout->addWidget(redoButton); historyButton = new SettingsButtonWidget(this); - historyButton->setButtonIcon(themePixmap(QStringLiteral("icons/arrow_history"))); + historyButton->setButtonIcon(QPixmap("theme:icons/arrow_history")); historyLabel = new QLabel(this); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_connect.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_connect.cpp index 700012d45..aa8a916f8 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_connect.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_connect.cpp @@ -1,7 +1,6 @@ #include "dlg_connect.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include #include @@ -22,7 +21,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent) previousHosts = new QComboBox(this); btnDeleteServer = new QPushButton(this); - btnDeleteServer->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + btnDeleteServer->setIcon(QPixmap("theme:icons/remove_row")); btnDeleteServer->setToolTip(tr("Delete the currently selected saved server")); btnDeleteServer->setFixedWidth(30); @@ -30,7 +29,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent) hps = new HandlePublicServers(this); btnRefreshServers = new QPushButton(this); - btnRefreshServers->setIcon(themePixmap(QStringLiteral("icons/sync"))); + btnRefreshServers->setIcon(QPixmap("theme:icons/sync")); btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers")); btnRefreshServers->setFixedWidth(30); @@ -100,7 +99,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent) updateDisplayInfo(previousHosts->currentText()); btnForgotPassword = new QPushButton(this); - btnForgotPassword->setIcon(themePixmap(QStringLiteral("icons/forgot_password"))); + btnForgotPassword->setIcon(QPixmap("theme:icons/forgot_password")); btnForgotPassword->setToolTip(tr("Reset Password")); btnForgotPassword->setFixedWidth(30); connect(btnForgotPassword, &QPushButton::released, this, &DlgConnect::actForgotPassword); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_edit_tokens.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_edit_tokens.cpp index a56aa8e35..f249976c2 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_edit_tokens.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_edit_tokens.cpp @@ -1,6 +1,5 @@ #include "dlg_edit_tokens.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/utility/get_text_with_max.h" #include @@ -91,10 +90,10 @@ DlgEditTokens::DlgEditTokens(QWidget *parent) : QDialog(parent), currentCard(nul &DlgEditTokens::tokenSelectionChanged); QAction *aAddToken = new QAction(tr("Add token"), this); - aAddToken->setIcon(themePixmap(QStringLiteral("icons/increment"))); + aAddToken->setIcon(QPixmap("theme:icons/increment")); connect(aAddToken, &QAction::triggered, this, &DlgEditTokens::actAddToken); QAction *aRemoveToken = new QAction(tr("Remove token"), this); - aRemoveToken->setIcon(themePixmap(QStringLiteral("icons/decrement"))); + aRemoveToken->setIcon(QPixmap("theme:icons/decrement")); connect(aRemoveToken, &QAction::triggered, this, &DlgEditTokens::actRemoveToken); auto *databaseToolBar = new QToolBar; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_manage_sets.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_manage_sets.cpp index 556c270be..7c107eb2f 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_manage_sets.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_manage_sets.cpp @@ -1,7 +1,6 @@ #include "dlg_manage_sets.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "../interface/card_picture_loader/card_picture_loader.h" #include "../interface/widgets/utility/custom_line_edit.h" @@ -36,28 +35,28 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent) setsEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); aTop = new QAction(QString(), this); - aTop->setIcon(themePixmap(QStringLiteral("icons/arrow_top_green"))); + aTop->setIcon(QPixmap("theme:icons/arrow_top_green")); aTop->setToolTip(tr("Move selected set to the top")); aTop->setEnabled(false); connect(aTop, &QAction::triggered, this, &WndSets::actTop); setsEditToolBar->addAction(aTop); aUp = new QAction(QString(), this); - aUp->setIcon(themePixmap(QStringLiteral("icons/arrow_up_green"))); + aUp->setIcon(QPixmap("theme:icons/arrow_up_green")); aUp->setToolTip(tr("Move selected set up")); aUp->setEnabled(false); connect(aUp, &QAction::triggered, this, &WndSets::actUp); setsEditToolBar->addAction(aUp); aDown = new QAction(QString(), this); - aDown->setIcon(themePixmap(QStringLiteral("icons/arrow_down_green"))); + aDown->setIcon(QPixmap("theme:icons/arrow_down_green")); aDown->setToolTip(tr("Move selected set down")); aDown->setEnabled(false); connect(aDown, &QAction::triggered, this, &WndSets::actDown); setsEditToolBar->addAction(aDown); aBottom = new QAction(QString(), this); - aBottom->setIcon(themePixmap(QStringLiteral("icons/arrow_bottom_green"))); + aBottom->setIcon(QPixmap("theme:icons/arrow_bottom_green")); aBottom->setToolTip(tr("Move selected set to the bottom")); aBottom->setEnabled(false); connect(aBottom, &QAction::triggered, this, &WndSets::actBottom); @@ -67,7 +66,7 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent) searchField = new LineEditUnfocusable; searchField->setObjectName("searchEdit"); searchField->setPlaceholderText(tr("Search by set name, code, type, or release date")); - searchField->addAction(themePixmap(QStringLiteral("icons/search")), LineEditUnfocusable::LeadingPosition); + searchField->addAction(QPixmap("theme:icons/search"), LineEditUnfocusable::LeadingPosition); searchField->setClearButtonEnabled(true); setFocusProxy(searchField); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp index 6d022861c..6ae8c9adb 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp @@ -1,7 +1,6 @@ #include "dlg_register.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "../server/handle_public_servers.h" #include "../server/user/user_info_connection.h" @@ -25,7 +24,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent) previousHosts = new QComboBox(this); btnDeleteServer = new QPushButton(this); - btnDeleteServer->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + btnDeleteServer->setIcon(QPixmap("theme:icons/remove_row")); btnDeleteServer->setToolTip(tr("Delete the currently selected saved server")); btnDeleteServer->setFixedWidth(30); @@ -33,7 +32,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent) hps = new HandlePublicServers(this); btnRefreshServers = new QPushButton(this); - btnRefreshServers->setIcon(themePixmap(QStringLiteral("icons/sync"))); + btnRefreshServers->setIcon(QPixmap("theme:icons/sync")); btnRefreshServers->setToolTip(tr("Refresh the server list with known public servers")); btnRefreshServers->setFixedWidth(30); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp index de7dd3e97..4698b011f 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp @@ -6,7 +6,6 @@ #include "dlg_settings.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "../main.h" #include "../settings_page/appearance_settings_page.h" #include "../settings_page/deck_editor_settings_page.h" @@ -97,7 +96,7 @@ void DlgSettings::setupUi() // Search bar searchEdit = new QLineEdit; searchEdit->setClearButtonEnabled(true); - searchEdit->addAction(themePixmap(QStringLiteral("icons/search")), QLineEdit::LeadingPosition); + searchEdit->addAction(QPixmap("theme:icons/search"), QLineEdit::LeadingPosition); searchEdit->installEventFilter(this); connect(searchEdit, &QLineEdit::textChanged, this, &DlgSettings::onSearchTextChanged); diff --git a/cockatrice/src/interface/widgets/general/home_tab_button_color.h b/cockatrice/src/interface/widgets/general/home_tab_button_color.h index b45bd7a92..1550b57e7 100644 --- a/cockatrice/src/interface/widgets/general/home_tab_button_color.h +++ b/cockatrice/src/interface/widgets/general/home_tab_button_color.h @@ -11,8 +11,8 @@ namespace HomeTabButtonColor */ enum Source { - FromThemeColors, ///< Use the theme's identity accent colors - FromBackground, ///< Extract colour from the background image + Automatic, ///< Extract color from background, or use theme color if no background + FromBackground, ///< Always extract color from background }; struct Entry @@ -23,7 +23,7 @@ struct Entry inline QList all() { - static QList entries = {{FromThemeColors, QT_TR_NOOP("From theme colors")}, + static QList entries = {{Automatic, QT_TR_NOOP("Automatic")}, {FromBackground, QT_TR_NOOP("Extract from background")}}; return entries; @@ -33,12 +33,12 @@ inline QList all() * Safely converts an int into the corresponding Source. * * @param value The int value - * @return The Source. Returns Source::FromThemeColors if the value is not within range + * @return The Source. Returns Source::Automatic if the value is not within range */ inline Source intToSource(int value) { if (value > FromBackground) { - return FromThemeColors; // default + return Automatic; // default } return static_cast(value); diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 27fd065a5..037f70406 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../interface/widgets/tabs/tab_supervisor.h" -#include "../../pixel_map_generator.h" #include "../../theme_manager.h" #include "../../window_main.h" #include "../cards/art_crop_attribution.h" @@ -21,8 +20,7 @@ #include HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) - : QWidget(parent), tabSupervisor(_tabSupervisor), background(themePixmap(QStringLiteral("backgrounds/home"))), - overlay(themePixmap(QStringLiteral("cockatrice"))) + : QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice") { layout = new QGridLayout(this); @@ -58,9 +56,6 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) &HomeWidget::initializeBackgroundFromSource); connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this, &HomeWidget::updateButtonsToBackgroundColor); - // Scheme flips (light/dark/system with an OS switch) fire on themeManager, - // not on SettingsCache::themeChanged, so re-resolve the variant background. - connect(themeManager, &ThemeManager::themeChanged, this, &HomeWidget::initializeBackgroundFromSource); connect(themeManager, &ThemeManager::paletteChanged, this, &HomeWidget::updateButtonsToBackgroundColor); connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabButtonColorChanged, this, &HomeWidget::updateButtonsToBackgroundColor); @@ -80,7 +75,7 @@ void HomeWidget::initializeBackgroundFromSource() switch (backgroundSourceType) { case BackgroundSources::Theme: cardChangeTimer->stop(); - background = themePixmap(QStringLiteral("backgrounds/home")); + background = QPixmap("theme:backgrounds/home"); backgroundSourceDeck = DeckList(); backgroundSourceCard->setCard(ExactCard()); updateButtonsToBackgroundColor(); @@ -106,6 +101,12 @@ void HomeWidget::loadBackgroundSourceDeck() backgroundSourceDeck = deckOpt.has_value() ? deckOpt.value().deckList : DeckList(); } +static bool usesThemeBackground() +{ + QString sourceId = SettingsCache::instance().appearance().getHomeTabBackgroundSource(); + return BackgroundSources::fromId(sourceId) == BackgroundSources::Theme; +} + static QPair paletteDerivedButtonColors() { return {themeManager->appColor(AppColor::AccentStrong), themeManager->appColor(AppColor::AccentSoft)}; @@ -117,8 +118,14 @@ QPair HomeWidget::determineButtonColor() const HomeTabButtonColor::intToSource(SettingsCache::instance().appearance().getHomeTabButtonColorSourceIndex()); switch (colorSource) { - case HomeTabButtonColor::FromThemeColors: - return paletteDerivedButtonColors(); + case HomeTabButtonColor::Automatic: { + if (usesThemeBackground()) { + // Static theme background: follow the theme's accent colors. + return paletteDerivedButtonColors(); + } else { + return extractDominantColors(background); + } + } case HomeTabButtonColor::FromBackground: return extractDominantColors(background); } diff --git a/cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp b/cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp index 881c3e3ac..9c433ab5a 100644 --- a/cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp +++ b/cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp @@ -1,7 +1,5 @@ #include "settings_button_widget.h" -#include "../../pixel_map_generator.h" - #include #include #include @@ -10,7 +8,7 @@ SettingsButtonWidget::SettingsButtonWidget(QWidget *parent) : QWidget(parent), button(new QToolButton(this)), popup(new SettingsPopupWidget(nullptr)) { - button->setIcon(themePixmap(QStringLiteral("icons/cogwheel"))); + button->setIcon(QPixmap("theme:icons/cogwheel")); button->setCheckable(true); button->setFixedSize(32, 32); connect(button, &QToolButton::clicked, this, &SettingsButtonWidget::togglePopup); diff --git a/cockatrice/src/interface/widgets/replay/replay_widget.cpp b/cockatrice/src/interface/widgets/replay/replay_widget.cpp index c92771f8c..6c85d950e 100644 --- a/cockatrice/src/interface/widgets/replay/replay_widget.cpp +++ b/cockatrice/src/interface/widgets/replay/replay_widget.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/tabs/tab_game.h" #include "replay_manager.h" #include "replay_quick_settings_widget.h" @@ -51,15 +50,15 @@ ReplayWidget::ReplayWidget(QWidget *parent, GameReplay *replay) replayPlayButton = new QToolButton; replayPlayButton->setIconSize(QSize(32, 32)); QIcon playButtonIcon = QIcon(); - playButtonIcon.addPixmap(themePixmap(QStringLiteral("replay/start")), QIcon::Normal, QIcon::Off); - playButtonIcon.addPixmap(themePixmap(QStringLiteral("replay/pause")), QIcon::Normal, QIcon::On); + playButtonIcon.addPixmap(QPixmap("theme:replay/start"), QIcon::Normal, QIcon::Off); + playButtonIcon.addPixmap(QPixmap("theme:replay/pause"), QIcon::Normal, QIcon::On); replayPlayButton->setIcon(playButtonIcon); replayPlayButton->setCheckable(true); connect(replayPlayButton, &QToolButton::toggled, this, &ReplayWidget::replayPlayButtonToggled); replayFastForwardButton = new QToolButton; replayFastForwardButton->setIconSize(QSize(32, 32)); - replayFastForwardButton->setIcon(themePixmap(QStringLiteral("replay/fastforward"))); + replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward")); replayFastForwardButton->setCheckable(true); connect(replayFastForwardButton, &QToolButton::toggled, this, &ReplayWidget::updateTimeScaleFactor); diff --git a/cockatrice/src/interface/widgets/server/game_selector.cpp b/cockatrice/src/interface/widgets/server/game_selector.cpp index a8bf54e91..f41002247 100644 --- a/cockatrice/src/interface/widgets/server/game_selector.cpp +++ b/cockatrice/src/interface/widgets/server/game_selector.cpp @@ -1,6 +1,5 @@ #include "game_selector.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/dialogs/dlg_create_game.h" #include "../interface/widgets/dialogs/dlg_filter_games.h" #include "../interface/widgets/tabs/tab_account.h" @@ -96,10 +95,10 @@ GameSelector::GameSelector(AbstractClient *_client, } filterButton = new QPushButton; - filterButton->setIcon(themePixmap(QStringLiteral("icons/search"))); + filterButton->setIcon(QPixmap("theme:icons/search")); connect(filterButton, &QPushButton::clicked, this, &GameSelector::actSetFilter); clearFilterButton = new QPushButton; - clearFilterButton->setIcon(themePixmap(QStringLiteral("icons/clearsearch"))); + clearFilterButton->setIcon(QPixmap("theme:icons/clearsearch")); bool filtersSetToDefault = showFilters && gameListProxyModel->areFilterParametersSetToDefaults(); clearFilterButton->setEnabled(!filtersSetToDefault); connect(clearFilterButton, &QPushButton::clicked, this, &GameSelector::actClearFilter); diff --git a/cockatrice/src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp b/cockatrice/src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp index d62bf81aa..1f034b767 100644 --- a/cockatrice/src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp +++ b/cockatrice/src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp @@ -1,7 +1,5 @@ #include "remote_replay_list_tree_widget.h" -#include "../../../pixel_map_generator.h" - #include #include #include @@ -39,7 +37,7 @@ RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, QFileIconProvider fip; dirIcon = fip.icon(QFileIconProvider::Folder); fileIcon = fip.icon(QFileIconProvider::File); - lockIcon = themePixmap(QStringLiteral("icons/lock")); + lockIcon = QPixmap("theme:icons/lock"); } RemoteReplayList_TreeModel::~RemoteReplayList_TreeModel() diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp index 0d2267a63..6b61d14ec 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp @@ -51,8 +51,6 @@ UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent, aDemoteFromMod = new QAction(QString(), this); aPromoteToJudge = new QAction(QString(), this); aDemoteFromJudge = new QAction(QString(), this); - aPromoteToDeveloper = new QAction(QString(), this); - aDemoteFromDeveloper = new QAction(QString(), this); aGetAdminNotes = new QAction(QString(), this); aInvestigateUser = new QAction(QString(), this); @@ -78,8 +76,6 @@ void UserContextMenu::retranslateUi() aDemoteFromMod->setText(tr("Dem&ote user from moderator")); aPromoteToJudge->setText(tr("Promote user to &judge")); aDemoteFromJudge->setText(tr("Demote user from judge")); - aPromoteToDeveloper->setText(tr("Promote user to &developer")); - aDemoteFromDeveloper->setText(tr("Demote user from de&veloper")); aGetAdminNotes->setText(tr("View admin notes")); aInvestigateUser->setText(tr("Investigate user")); } @@ -272,7 +268,7 @@ void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const const Command_AdjustMod &cmd = commandContainer.admin_command(0).GetExtension(Command_AdjustMod::ext); if (resp.response_code() == Response::RespOk) { - if (cmd.should_be_mod() || cmd.should_be_judge() || cmd.should_be_developer()) { + if (cmd.should_be_mod() || cmd.should_be_judge()) { QMessageBox::information(static_cast(parent()), tr("Success"), tr("Successfully promoted user.")); } else { @@ -280,7 +276,7 @@ void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const } } else { - if (cmd.should_be_mod() || cmd.should_be_judge() || cmd.should_be_developer()) { + if (cmd.should_be_mod() || cmd.should_be_judge()) { QMessageBox::information(static_cast(parent()), tr("Failed"), tr("Failed to promote user.")); } else { QMessageBox::information(static_cast(parent()), tr("Failed"), tr("Failed to demote user.")); @@ -441,15 +437,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { menu->addAction(aPromoteToJudge); } - - if (userLevel.testFlag(ServerInfo_User::IsDeveloper) && - (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { - menu->addAction(aDemoteFromDeveloper); - - } else if (userLevel.testFlag(ServerInfo_User::IsRegistered) && - (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { - menu->addAction(aPromoteToDeveloper); - } } aDetails->setEnabled(true); aChat->setEnabled(anotherUser && online && !userListProxy->isUserIgnored(userName)); @@ -468,10 +455,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, aInvestigateUser->setEnabled(anotherUser); aPromoteToMod->setEnabled(anotherUser); aDemoteFromMod->setEnabled(anotherUser); - aPromoteToJudge->setEnabled(anotherUser); - aDemoteFromJudge->setEnabled(anotherUser); - aPromoteToDeveloper->setEnabled(anotherUser); - aDemoteFromDeveloper->setEnabled(anotherUser); QAction *actionClicked = menu->exec(pos); if (actionClicked == nullptr) { @@ -506,8 +489,6 @@ void UserContextMenu::showContextMenu(const QPoint &pos, execAdjustMod(userName, actionClicked == aPromoteToMod); } else if (actionClicked == aPromoteToJudge || actionClicked == aDemoteFromJudge) { execAdjustJudge(userName, actionClicked == aPromoteToJudge); - } else if (actionClicked == aPromoteToDeveloper || actionClicked == aDemoteFromDeveloper) { - execAdjustDeveloper(userName, actionClicked == aPromoteToDeveloper); } else if (actionClicked == aBanHistory) { execBanHistory(userName); } else if (actionClicked == aWarnUser) { @@ -725,14 +706,4 @@ void UserContextMenu::execAdjustJudge(const QString &userName, bool shouldBeJudg PendingCommand *pend = client->prepareAdminCommand(cmd); connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); client->sendCommand(pend); -} - -void UserContextMenu::execAdjustDeveloper(const QString &userName, bool shouldBeDeveloper) -{ - Command_AdjustMod cmd; - cmd.set_user_name(userName.toStdString()); - cmd.set_should_be_developer(shouldBeDeveloper); - PendingCommand *pend = client->prepareAdminCommand(cmd); - connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); - client->sendCommand(pend); -} +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.h b/cockatrice/src/interface/widgets/server/user/user_context_menu.h index 6abbc057a..f1ce931f8 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.h +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.h @@ -45,7 +45,6 @@ private: QAction *aBan, *aBanHistory; QAction *aPromoteToMod, *aDemoteFromMod; QAction *aPromoteToJudge, *aDemoteFromJudge; - QAction *aPromoteToDeveloper, *aDemoteFromDeveloper; QAction *aWarnUser, *aWarnHistory; QAction *aGetAdminNotes; std::function()> gameInviteLinkProvider; @@ -124,7 +123,6 @@ public: void execInvestigateUser(const QString &userName); void execAdjustMod(const QString &userName, bool shouldBeMod); void execAdjustJudge(const QString &userName, bool shouldBeJudge); - void execAdjustDeveloper(const QString &userName, bool shouldBeDeveloper); private: void execInvite(const QString &userName, const GameInviteOption &option); diff --git a/cockatrice/src/interface/widgets/server/user/user_info_box.cpp b/cockatrice/src/interface/widgets/server/user/user_info_box.cpp index 3d89cecf5..875bdfb05 100644 --- a/cockatrice/src/interface/widgets/server/user/user_info_box.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_info_box.cpp @@ -122,8 +122,6 @@ void UserInfoBox::updateInfo(const ServerInfo_User &user) QString userLevelText; if (userLevel.testFlag(ServerInfo_User::IsAdmin)) { userLevelText = tr("Administrator"); - } else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) { - userLevelText = tr("Developer"); } else if (userLevel.testFlag(ServerInfo_User::IsModerator)) { userLevelText = tr("Moderator"); } else if (userLevel.testFlag(ServerInfo_User::IsRegistered)) { diff --git a/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp b/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp index 8be76eea0..fb610e814 100644 --- a/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp @@ -245,9 +245,6 @@ void UserInfoHeaderWidget::paintEvent(QPaintEvent *) if (level.testFlag(ServerInfo_User::IsAdmin)) { return QColor(245, 158, 11); } - if (level.testFlag(ServerInfo_User::IsDeveloper)) { - return QColor(185, 28, 28); - } if (level.testFlag(ServerInfo_User::IsModerator)) { return QColor(59, 130, 246); } @@ -303,8 +300,6 @@ void UserInfoHeaderWidget::paintEvent(QPaintEvent *) } badge; if (level.testFlag(ServerInfo_User::IsAdmin)) { badge = {"ADMIN", QColor(245, 158, 11)}; - } else if (level.testFlag(ServerInfo_User::IsDeveloper)) { - badge = {"DEV", QColor(185, 28, 28)}; } else if (level.testFlag(ServerInfo_User::IsModerator)) { badge = {"MOD", QColor(59, 130, 246)}; } else if (level.testFlag(ServerInfo_User::IsJudge)) { diff --git a/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp b/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp index 34a3d6ae1..5c65b090d 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp @@ -49,8 +49,6 @@ QColor UserListPainter::getAccentColor(const UserLevelFlags &userLevel, bool onl if (userLevel.testFlag(ServerInfo_User::IsAdmin)) { accentColor = QColor(245, 158, 11); - } else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) { - accentColor = QColor(185, 28, 28); } else if (userLevel.testFlag(ServerInfo_User::IsModerator)) { accentColor = QColor(59, 130, 246); } else if (userLevel.testFlag(ServerInfo_User::IsJudge)) { @@ -301,8 +299,6 @@ QList UserListPainter::buildBadges(const UserLevelFlags if (userLevel.testFlag(ServerInfo_User::IsAdmin)) { badges << Badge{"ADMIN", QColor(245, 158, 11)}; - } else if (userLevel.testFlag(ServerInfo_User::IsDeveloper)) { - badges << Badge{"DEV", QColor(185, 28, 28)}; } else if (userLevel.testFlag(ServerInfo_User::IsModerator)) { badges << Badge{"MOD", QColor(59, 130, 246)}; } else if (userLevel.testFlag(ServerInfo_User::IsJudge)) { @@ -389,9 +385,9 @@ void UserListPainter::paint(QPainter *painter, const QString userName = QString::fromStdString(userInfo.name()); const QString privLevel = QString::fromStdString(userInfo.privlevel()); const QColor accentColor = getAccentColor(userLevel, online); - const bool hasRole = - userLevel.testFlag(ServerInfo_User::IsAdmin) || userLevel.testFlag(ServerInfo_User::IsDeveloper) || - userLevel.testFlag(ServerInfo_User::IsModerator) || userLevel.testFlag(ServerInfo_User::IsJudge); + const bool hasRole = userLevel.testFlag(ServerInfo_User::IsAdmin) || + userLevel.testFlag(ServerInfo_User::IsModerator) || + userLevel.testFlag(ServerInfo_User::IsJudge); const QRectF cardRect = QRectF(rect).adjusted(3, 2, -3, -2); const int cardRight = getCardRight(option, rect); diff --git a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp index 1bb7c5288..a8c99c979 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp @@ -234,11 +234,9 @@ bool UserListTWI::operator<(const QTreeWidgetItem &other) const const auto &lhsUserLevelFlags = UserLevelFlags(data(0, Qt::UserRole).toInt()); const auto &rhsUserLevelFlags = UserLevelFlags(other.data(0, Qt::UserRole).toInt()); - // Admins, Developers & Mods need no additional comparison checks, just to see if they're an admin, a developer - // or a moderator + // Admins & Mods need no additional comparison checks, just to see if they're an admin or a moderator static const QList userLevelWithNoOtherPrefOrder = { - ServerInfo_User_UserLevelFlag_IsAdmin, ServerInfo_User_UserLevelFlag_IsDeveloper, - ServerInfo_User_UserLevelFlag_IsModerator}; + ServerInfo_User_UserLevelFlag_IsAdmin, ServerInfo_User_UserLevelFlag_IsModerator}; for (const auto &userLevelEntry : userLevelWithNoOtherPrefOrder) { if (lhsUserLevelFlags.testFlag(userLevelEntry) && lhsUserLevelFlags.testFlag(userLevelEntry) == rhsUserLevelFlags.testFlag(userLevelEntry)) { diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp index c45373757..c8494f095 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp @@ -510,7 +510,7 @@ void AppearanceSettingsPage::retranslateUi() homeTabDisplayCardNameCheckBox.setText(tr("Display card name of background in bottom right")); homeTabButtonColorSourceLabel.setText(tr("Home tab button color:")); homeTabButtonColorSourceBox.setToolTip( - tr("Use the theme's identity accent colors, or extract colors from the background image")); + tr("Automatic: extract from background if present, otherwise use theme default")); playmatGroupBox->setTitle(tr("Playmat settings")); playmatVisibilityLabel.setText(tr("Playmat visibility:")); diff --git a/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp index f3eac05b8..f425afe60 100644 --- a/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp @@ -1,7 +1,6 @@ #include "deck_editor_settings_page.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "update/card_spoiler/spoiler_background_updater.h" #include @@ -54,15 +53,15 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); aAdd = new QAction(this); - aAdd->setIcon(themePixmap(QStringLiteral("icons/increment"))); + aAdd->setIcon(QPixmap("theme:icons/increment")); connect(aAdd, &QAction::triggered, this, &DeckEditorSettingsPage::actAddURL); aEdit = new QAction(this); - aEdit->setIcon(themePixmap(QStringLiteral("icons/pencil"))); + aEdit->setIcon(QPixmap("theme:icons/pencil")); connect(aEdit, &QAction::triggered, this, &DeckEditorSettingsPage::actEditURL); aRemove = new QAction(this); - aRemove->setIcon(themePixmap(QStringLiteral("icons/decrement"))); + aRemove->setIcon(QPixmap("theme:icons/decrement")); connect(aRemove, &QAction::triggered, this, &DeckEditorSettingsPage::actRemoveURL); auto *urlToolBar = new QToolBar; diff --git a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp index a3b89f8c4..c161030d8 100644 --- a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp @@ -1,7 +1,6 @@ #include "messages_settings_page.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/utility/get_text_with_max.h" #include @@ -108,15 +107,15 @@ MessagesSettingsPage::MessagesSettingsPage() } aAdd = new QAction(this); - aAdd->setIcon(themePixmap(QStringLiteral("icons/increment"))); + aAdd->setIcon(QPixmap("theme:icons/increment")); connect(aAdd, &QAction::triggered, this, &MessagesSettingsPage::actAdd); aEdit = new QAction(this); - aEdit->setIcon(themePixmap(QStringLiteral("icons/pencil"))); + aEdit->setIcon(QPixmap("theme:icons/pencil")); connect(aEdit, &QAction::triggered, this, &MessagesSettingsPage::actEdit); aRemove = new QAction(this); - aRemove->setIcon(themePixmap(QStringLiteral("icons/decrement"))); + aRemove->setIcon(QPixmap("theme:icons/decrement")); connect(aRemove, &QAction::triggered, this, &MessagesSettingsPage::actRemove); auto *messageToolBar = new QToolBar; diff --git a/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp index 1277d0e49..1f1867f7c 100644 --- a/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp @@ -3,7 +3,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcut_treeview.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/utility/custom_line_edit.h" #include "../interface/widgets/utility/sequence_edit.h" @@ -48,8 +47,8 @@ ShortcutSettingsPage::ShortcutSettingsPage() btnResetAll = new QPushButton(this); btnClearAll = new QPushButton(this); - btnResetAll->setIcon(themePixmap(QStringLiteral("icons/update"))); - btnClearAll->setIcon(themePixmap(QStringLiteral("icons/clearsearch"))); + btnResetAll->setIcon(QPixmap("theme:icons/update")); + btnClearAll->setIcon(QPixmap("theme:icons/clearsearch")); // layout auto *_editLayout = new QGridLayout; diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp index 888197f53..98b21d0f1 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp @@ -1,7 +1,6 @@ #include "tab_archidekt.h" #include "../../../../../client/settings/cache_settings.h" -#include "../../../../pixel_map_generator.h" #include "../../../cards/additional_info/mana_symbol_widget.h" #include "../../../utility/completer_utils.h" #include "../../tab_supervisor.h" @@ -214,7 +213,7 @@ void TabArchidekt::setupFilterWidgets() // Format filter (collapsible) formatButton = new SettingsButtonWidget(secondaryToolbar); formatButton->setButtonText(tr("Formats")); - formatButton->setButtonIcon(themePixmap(QStringLiteral("icons/scale_balanced"))); + formatButton->setButtonIcon(QPixmap("theme:icons/scale_balanced")); QWidget *formatContainer = new QWidget(secondaryToolbar); QGridLayout *formatLayout = new QGridLayout(formatContainer); diff --git a/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_widget.cpp index 2cbbedfa4..4f50e38a6 100644 --- a/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_widget.cpp @@ -1,7 +1,6 @@ #include "commander_bracket_widget.h" #include "../../../../../client/settings/cache_settings.h" -#include "../../../../pixel_map_generator.h" #include "commander_bracket_service.h" #include @@ -31,7 +30,7 @@ CommanderBracketWidget::CommanderBracketWidget(QWidget *parent) : QWidget(parent bracketInfoButton->setEnabled(false); bracketRefreshButton = new QToolButton(this); - bracketRefreshButton->setIcon(themePixmap(QStringLiteral("icons/reload"))); + bracketRefreshButton->setIcon(QPixmap("theme:icons/reload")); bracketRefreshButton->setAutoRaise(true); connect(bracketRefreshButton, &QToolButton::clicked, this, &CommanderBracketWidget::requestBracketEstimate); diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp index f3535d850..49e42e4cf 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../deck_loader/deck_loader.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/server/remote/remote_decklist_tree_widget.h" #include "../interface/widgets/utility/get_text_with_max.h" @@ -106,19 +105,19 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, // Left side actions aOpenLocalDeck = new QAction(this); - aOpenLocalDeck->setIcon(themePixmap(QStringLiteral("icons/pencil"))); + aOpenLocalDeck->setIcon(QPixmap("theme:icons/pencil")); connect(aOpenLocalDeck, &QAction::triggered, this, &TabDeckStorage::actOpenLocalDeck); aRenameLocal = new QAction(this); - aRenameLocal->setIcon(themePixmap(QStringLiteral("icons/rename"))); + aRenameLocal->setIcon(QPixmap("theme:icons/rename")); connect(aRenameLocal, &QAction::triggered, this, &TabDeckStorage::actRenameLocal); aUpload = new QAction(this); - aUpload->setIcon(themePixmap(QStringLiteral("icons/arrow_right_green"))); + aUpload->setIcon(QPixmap("theme:icons/arrow_right_green")); connect(aUpload, &QAction::triggered, this, &TabDeckStorage::actUpload); aNewLocalFolder = new QAction(this); aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder)); connect(aNewLocalFolder, &QAction::triggered, this, &TabDeckStorage::actNewLocalFolder); aDeleteLocalDeck = new QAction(this); - aDeleteLocalDeck->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + aDeleteLocalDeck->setIcon(QPixmap("theme:icons/remove_row")); connect(aDeleteLocalDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteLocalDeck); aOpenDecksFolder = new QAction(this); @@ -127,16 +126,16 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, // Right side actions aOpenRemoteDeck = new QAction(this); - aOpenRemoteDeck->setIcon(themePixmap(QStringLiteral("icons/pencil"))); + aOpenRemoteDeck->setIcon(QPixmap("theme:icons/pencil")); connect(aOpenRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actOpenRemoteDeck); aDownload = new QAction(this); - aDownload->setIcon(themePixmap(QStringLiteral("icons/arrow_left_green"))); + aDownload->setIcon(QPixmap("theme:icons/arrow_left_green")); connect(aDownload, &QAction::triggered, this, &TabDeckStorage::actDownload); aNewFolder = new QAction(this); aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder)); connect(aNewFolder, &QAction::triggered, this, &TabDeckStorage::actNewFolder); aDeleteRemoteDeck = new QAction(this); - aDeleteRemoteDeck->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + aDeleteRemoteDeck->setIcon(QPixmap("theme:icons/remove_row")); connect(aDeleteRemoteDeck, &QAction::triggered, this, &TabDeckStorage::actDeleteRemoteDeck); // Add actions to toolbars diff --git a/cockatrice/src/interface/widgets/tabs/tab_developer.cpp b/cockatrice/src/interface/widgets/tabs/tab_developer.cpp deleted file mode 100644 index 39652ef7d..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_developer.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/** - * @file tab_developer.cpp - * @ingroup ServerTabs - */ -//! \todo Document this file. - -#include "tab_developer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr int DEFAULT_AUTO_REFRESH_INTERVAL_SECS = 30; - -TabDeveloper::TabDeveloper(TabSupervisor *_tabSupervisor, AbstractClient *_client) - : Tab(_tabSupervisor), client(_client) -{ - statsTable = new QTableWidget(0, 2); - statsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - statsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - statsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - statsTable->setSelectionMode(QAbstractItemView::SingleSelection); - statsTable->verticalHeader()->setVisible(false); - statsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); - statsTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive); - statsTable->horizontalHeader()->setStretchLastSection(true); - - commandTable = new QTableWidget(0, 4); - commandTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - commandTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - commandTable->setSelectionBehavior(QAbstractItemView::SelectRows); - commandTable->setSelectionMode(QAbstractItemView::SingleSelection); - commandTable->verticalHeader()->setVisible(false); - commandTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); - commandTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive); - commandTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Interactive); - commandTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Interactive); - - statusLabel = new QLabel; - - autoRefreshCheckBox = new QCheckBox; - autoRefreshCheckBox->setChecked(false); - - refreshIntervalSpinBox = new QSpinBox; - refreshIntervalSpinBox->setRange(5, 3600); - refreshIntervalSpinBox->setValue(DEFAULT_AUTO_REFRESH_INTERVAL_SECS); - refreshIntervalSpinBox->setEnabled(false); - - autoRefreshTimer = new QTimer(this); - connect(autoRefreshTimer, &QTimer::timeout, this, &TabDeveloper::refreshClicked); - connect(autoRefreshCheckBox, &QCheckBox::toggled, this, &TabDeveloper::autoRefreshToggled); - connect(refreshIntervalSpinBox, QOverload::of(&QSpinBox::valueChanged), this, - &TabDeveloper::refreshIntervalChanged); - - refreshButton = new QPushButton; - refreshButton->setAutoDefault(true); - connect(refreshButton, &QPushButton::clicked, this, &TabDeveloper::refreshClicked); - - auto *buttonLayout = new QHBoxLayout; - buttonLayout->addWidget(statusLabel, 1, Qt::AlignLeft); - buttonLayout->addWidget(autoRefreshCheckBox, 0, Qt::AlignRight); - buttonLayout->addWidget(refreshIntervalSpinBox, 0, Qt::AlignRight); - buttonLayout->addWidget(refreshButton, 0, Qt::AlignRight); - - auto *tableLayout = new QHBoxLayout; - tableLayout->addWidget(statsTable, 1); - tableLayout->addWidget(commandTable, 2); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addLayout(tableLayout, 1); - mainLayout->addLayout(buttonLayout); - - auto *central = new QWidget; - central->setLayout(mainLayout); - setCentralWidget(central); - - retranslateUi(); -} - -void TabDeveloper::retranslateUi() -{ - autoRefreshCheckBox->setText(tr("Auto-refresh")); - autoRefreshCheckBox->setToolTip(tr("Automatically request fresh server statistics at a fixed interval.")); - refreshIntervalSpinBox->setSuffix(tr(" s")); - refreshIntervalSpinBox->setToolTip(tr("Seconds between automatic refreshes.")); - refreshButton->setText(tr("Refresh server stats")); - statsTable->setHorizontalHeaderLabels(QString(tr("Statistic;Value")).split(";")); - commandTable->setHorizontalHeaderLabels(QString(tr("Command;Count;Total ms;Avg ms")).split(";")); - if (statsTable->rowCount() == 0) { - statusLabel->clear(); - } -} - -QString TabDeveloper::formatBytes(quint64 bytes) -{ - const quint64 kib = 1024; - const quint64 mib = 1024 * kib; - const quint64 gib = 1024 * mib; - if (bytes >= gib) { - return tr("%1 GiB").arg(QString::number(bytes / static_cast(gib), 'f', 2)); - } - if (bytes >= mib) { - return tr("%1 MiB").arg(QString::number(bytes / static_cast(mib), 'f', 2)); - } - if (bytes >= kib) { - return tr("%1 KiB").arg(QString::number(bytes / static_cast(kib), 'f', 2)); - } - return tr("%1 bytes").arg(bytes); -} - -QString TabDeveloper::formatDurationMs(qint64 ms) -{ - if (ms >= 1000) { - return tr("%1 s").arg(QString::number(ms / 1000.0, 'f', 2)); - } - return tr("%1 ms").arg(ms); -} - -void TabDeveloper::appendStatRow(const QString &name, const QString &value) -{ - const int row = statsTable->rowCount(); - statsTable->insertRow(row); - statsTable->setItem(row, 0, new QTableWidgetItem(name)); - statsTable->setItem(row, 1, new QTableWidgetItem(value)); -} - -void TabDeveloper::appendSeparatorRow(const QString §ionTitle) -{ - const int row = statsTable->rowCount(); - statsTable->insertRow(row); - auto *labelItem = new QTableWidgetItem(sectionTitle); - auto font = labelItem->font(); - font.setBold(true); - labelItem->setFont(font); - labelItem->setFlags(labelItem->flags() & ~Qt::ItemIsSelectable); - statsTable->setItem(row, 0, labelItem); - statsTable->setItem(row, 1, new QTableWidgetItem(QString())); -} - -void TabDeveloper::refreshClicked() -{ - if (requestPending) { - return; - } - requestPending = true; - Command_GetServerStats cmd; - PendingCommand *pend = client->prepareDeveloperCommand(cmd); - connect(pend, &PendingCommand::finished, this, &TabDeveloper::serverStatsResponse); - client->sendCommand(pend); -} - -void TabDeveloper::autoRefreshToggled(bool checked) -{ - refreshIntervalSpinBox->setEnabled(checked); - if (checked) { - refreshIntervalChanged(); - refreshClicked(); - } else { - autoRefreshTimer->stop(); - } -} - -void TabDeveloper::refreshIntervalChanged() -{ - if (autoRefreshCheckBox->isChecked()) { - autoRefreshTimer->start(refreshIntervalSpinBox->value() * 1000); - } -} - -void TabDeveloper::serverStatsResponse(const Response &resp) -{ - requestPending = false; - if (resp.response_code() != Response::RespOk) { - statusLabel->setText(tr("No server statistics available yet.")); - return; - } - - const Response_GetServerStats &response = resp.GetExtension(Response_GetServerStats::ext); - - statsTable->setRowCount(0); - - // Overview section - appendStatRow(tr("Registered users online"), QString::number(response.users_count())); - appendStatRow(tr("Moderators online"), QString::number(response.mods_count())); - appendStatRow(tr("Games running"), QString::number(response.games_count())); - appendStatRow(tr("Traffic sent (last tick)"), formatBytes(response.tx_bytes())); - appendStatRow(tr("Traffic received (last tick)"), formatBytes(response.rx_bytes())); - - const qint64 uptime = static_cast(response.uptime_secs()); - const int days = static_cast(uptime / 86400); - const int hours = static_cast((uptime % 86400) / 3600); - const int minutes = static_cast((uptime % 3600) / 60); - appendStatRow(tr("Server uptime"), days > 0 ? tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes) - : tr("%1h %2m").arg(hours).arg(minutes)); - - const QDateTime snapshotTime = QDateTime::fromSecsSinceEpoch(static_cast(response.timest())); - appendStatRow(tr("Snapshot taken"), snapshotTime.toLocalTime().toString("yyyy-MM-dd HH:mm")); - - // Live metrics section - appendSeparatorRow(tr("Live Metrics")); - appendStatRow(tr("Cards in live games"), QString::number(response.cards_in_games())); - appendStatRow(tr("Total commands processed"), QString::number(response.total_commands())); - - if (response.total_commands() > 0) { - const double avgMs = static_cast(response.total_command_time_ms()) / response.total_commands(); - appendStatRow(tr("Avg command time"), QString::number(avgMs, 'f', 2) + " ms"); - } - appendStatRow(tr("Active command types"), QString::number(response.active_command_types())); - - appendStatRow(tr("Event loop stalls"), QString::number(response.eventloop_stalls_total())); - appendStatRow(tr("Last stall overshoot"), formatDurationMs(response.eventloop_last_stall_ms())); - appendStatRow(tr("Worst stall overshoot"), formatDurationMs(response.eventloop_max_stall_ms())); - - if (response.game_start_count() > 0) { - appendStatRow(tr("Game starts"), QString::number(response.game_start_count())); - const double avgStartMs = static_cast(response.game_start_total_ms()) / response.game_start_count(); - appendStatRow(tr("Avg game start time"), QString::number(avgStartMs, 'f', 1) + " ms"); - } - - // Per-command breakdown table - QList sortedStats(response.command_stats().begin(), response.command_stats().end()); - std::sort(sortedStats.begin(), sortedStats.end(), - [](const auto &a, const auto &b) { return a.total_ms() > b.total_ms(); }); - - commandTable->setRowCount(0); - for (const auto &cs : sortedStats) { - const int row = commandTable->rowCount(); - commandTable->insertRow(row); - commandTable->setItem(row, 0, new QTableWidgetItem(QString::fromStdString(cs.command_name()))); - - auto *countItem = new QTableWidgetItem(QString::number(cs.count())); - countItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - commandTable->setItem(row, 1, countItem); - - auto *totalItem = new QTableWidgetItem(QString::number(cs.total_ms())); - totalItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - commandTable->setItem(row, 2, totalItem); - - const double avg = cs.count() > 0 ? static_cast(cs.total_ms()) / cs.count() : 0.0; - auto *avgItem = new QTableWidgetItem(QString::number(avg, 'f', 2)); - avgItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - commandTable->setItem(row, 3, avgItem); - } - commandTable->resizeColumnsToContents(); - statsTable->resizeColumnsToContents(); - commandTable->resizeColumnsToContents(); - - statusLabel->setText(tr("Updated %1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm"))); -} diff --git a/cockatrice/src/interface/widgets/tabs/tab_developer.h b/cockatrice/src/interface/widgets/tabs/tab_developer.h deleted file mode 100644 index 727a56bcb..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_developer.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file tab_developer.h - * @ingroup ServerTabs - */ -//! \todo Document this file. - -#ifndef TAB_DEVELOPER_H -#define TAB_DEVELOPER_H - -#include "tab.h" - -class AbstractClient; -class QCheckBox; -class QLabel; -class QPushButton; -class QSpinBox; -class QTableWidget; -class QTimer; -class Response; - -class TabDeveloper : public Tab -{ - Q_OBJECT -private: - AbstractClient *client; - QTableWidget *statsTable; - QTableWidget *commandTable; - QPushButton *refreshButton; - QLabel *statusLabel; - QCheckBox *autoRefreshCheckBox; - QSpinBox *refreshIntervalSpinBox; - QTimer *autoRefreshTimer; - bool requestPending = false; - - void appendStatRow(const QString &name, const QString &value); - void appendSeparatorRow(const QString §ionTitle); - static QString formatBytes(quint64 bytes); - static QString formatDurationMs(qint64 ms); - -private slots: - void refreshClicked(); - void serverStatsResponse(const Response &resp); - void autoRefreshToggled(bool checked); - void refreshIntervalChanged(); - -public: - explicit TabDeveloper(TabSupervisor *_tabSupervisor, AbstractClient *_client); - void retranslateUi() override; - [[nodiscard]] QString getTabText() const override - { - return tr("Developer"); - } -}; - -#endif diff --git a/cockatrice/src/interface/widgets/tabs/tab_logs.cpp b/cockatrice/src/interface/widgets/tabs/tab_logs.cpp index f73d06b57..e3678a903 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_logs.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_logs.cpp @@ -19,8 +19,7 @@ #include #include -TabLog::TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool _canUseDeveloperCommands) - : Tab(_tabSupervisor), client(_client), canUseDeveloperCommands(_canUseDeveloperCommands) +TabLog::TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client) { roomTable = new QTableWidget(); roomTable->setColumnCount(6); @@ -81,9 +80,7 @@ void TabLog::getClicked() if (!mainRoom->isChecked() && !gameRoom->isChecked() && !privateChat->isChecked()) { mainRoom->setChecked(true); gameRoom->setChecked(true); - if (!canUseDeveloperCommands) { - privateChat->setChecked(true); - } + privateChat->setChecked(true); } if (maximumResults->value() == 0) { @@ -120,15 +117,7 @@ void TabLog::getClicked() }; cmd.set_date_range(dateRange); cmd.set_maximum_results(maximumResults->value()); - - PendingCommand *pend; - if (canUseDeveloperCommands) { - // Developers query logs through the developer command family. - pend = client->prepareDeveloperCommand(cmd); - } else { - pend = client->prepareModeratorCommand(cmd); - } - + PendingCommand *pend = client->prepareModeratorCommand(cmd); connect(pend, &PendingCommand::finished, this, &TabLog::viewLogHistory_processResponse); client->sendCommand(pend); } @@ -182,14 +171,6 @@ void TabLog::createDock() mainRoom = new QCheckBox(tr("Main Room")); gameRoom = new QCheckBox(tr("Game Room")); privateChat = new QCheckBox(tr("Private Chat")); - if (canUseDeveloperCommands) { - // Developers cannot query private conversations. - privateChat->setVisible(false); - // The developer family ignores the IP filter server-side, so showing - // the field would silently unfilter the result by it. Hide it. - labelFindIPAddress->setVisible(false); - findIPAddress->setVisible(false); - } pastDays = new QRadioButton(tr("Past X Days: ")); today = new QRadioButton(tr("Today")); diff --git a/cockatrice/src/interface/widgets/tabs/tab_logs.h b/cockatrice/src/interface/widgets/tabs/tab_logs.h index 8e914ea64..5d164dc92 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_logs.h +++ b/cockatrice/src/interface/widgets/tabs/tab_logs.h @@ -33,7 +33,6 @@ class TabLog : public Tab Q_OBJECT private: AbstractClient *client; - bool canUseDeveloperCommands; QLabel *labelFindUserName, *labelFindIPAddress, *labelFindGameName, *labelFindGameID, *labelMessage, *labelMaximum, *labelDescription; LineEditUnfocusable *findUsername, *findIPAddress, *findGameName, *findGameID, *findMessage; @@ -59,7 +58,7 @@ private slots: void restartLayout(); public: - TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool _canUseDeveloperCommands = false); + TabLog(TabSupervisor *_tabSupervisor, AbstractClient *_client); ~TabLog() override; void retranslateUi() override; [[nodiscard]] QString getTabText() const override diff --git a/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp b/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp index b9c37f1af..077b876d2 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_moderation.cpp @@ -381,9 +381,6 @@ void TabModeration::moderatorLoginsResponse(const Response &response) if (login.user_level() & ServerInfo_User::IsAdmin) { levels << tr("Admin"); } - if (login.user_level() & ServerInfo_User::IsDeveloper) { - levels << tr("Developer"); - } if (login.user_level() & ServerInfo_User::IsModerator) { levels << tr("Moderator"); } diff --git a/cockatrice/src/interface/widgets/tabs/tab_replays.cpp b/cockatrice/src/interface/widgets/tabs/tab_replays.cpp index ac4b2cbe9..5618604df 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_replays.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_replays.cpp @@ -1,7 +1,6 @@ #include "tab_replays.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/server/remote/remote_replay_list_tree_widget.h" #include "tab_game.h" @@ -103,17 +102,17 @@ QGroupBox *TabReplays::createLeftLayout() // Left side actions aOpenLocalReplay = new QAction(this); - aOpenLocalReplay->setIcon(themePixmap(QStringLiteral("icons/view"))); + aOpenLocalReplay->setIcon(QPixmap("theme:icons/view")); connect(aOpenLocalReplay, &QAction::triggered, this, &TabReplays::actOpenLocalReplay); connect(localDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenLocalReplay); aRenameLocal = new QAction(this); - aRenameLocal->setIcon(themePixmap(QStringLiteral("icons/rename"))); + aRenameLocal->setIcon(QPixmap("theme:icons/rename")); connect(aRenameLocal, &QAction::triggered, this, &TabReplays::actRenameLocal); aNewLocalFolder = new QAction(this); aNewLocalFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder)); connect(aNewLocalFolder, &QAction::triggered, this, &TabReplays::actNewLocalFolder); aDeleteLocalReplay = new QAction(this); - aDeleteLocalReplay->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + aDeleteLocalReplay->setIcon(QPixmap("theme:icons/remove_row")); connect(aDeleteLocalReplay, &QAction::triggered, this, &TabReplays::actDeleteLocalReplay); aOpenReplaysFolder = new QAction(this); @@ -165,24 +164,24 @@ QGroupBox *TabReplays::createRightLayout() // Right side actions aOpenRemoteReplay = new QAction(this); - aOpenRemoteReplay->setIcon(themePixmap(QStringLiteral("icons/view"))); + aOpenRemoteReplay->setIcon(QPixmap("theme:icons/view")); connect(aOpenRemoteReplay, &QAction::triggered, this, &TabReplays::actOpenRemoteReplay); connect(serverDirView, &QTreeView::doubleClicked, this, &TabReplays::actOpenRemoteReplay); aDownload = new QAction(this); - aDownload->setIcon(themePixmap(QStringLiteral("icons/arrow_left_green"))); + aDownload->setIcon(QPixmap("theme:icons/arrow_left_green")); connect(aDownload, &QAction::triggered, this, &TabReplays::actDownload); aKeep = new QAction(this); - aKeep->setIcon(themePixmap(QStringLiteral("icons/lock"))); + aKeep->setIcon(QPixmap("theme:icons/lock")); connect(aKeep, &QAction::triggered, this, &TabReplays::actKeepRemoteReplay); aDeleteRemoteReplay = new QAction(this); - aDeleteRemoteReplay->setIcon(themePixmap(QStringLiteral("icons/remove_row"))); + aDeleteRemoteReplay->setIcon(QPixmap("theme:icons/remove_row")); connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay); aGetReplayCode = new QAction(this); - aGetReplayCode->setIcon(themePixmap(QStringLiteral("icons/share"))); + aGetReplayCode->setIcon(QPixmap("theme:icons/share")); connect(aGetReplayCode, &QAction::triggered, this, &TabReplays::actGetReplayCode); aSubmitReplayCode = new QAction(this); - aSubmitReplayCode->setIcon(themePixmap(QStringLiteral("icons/search"))); + aSubmitReplayCode->setIcon(QPixmap("theme:icons/search")); connect(aSubmitReplayCode, &QAction::triggered, this, &TabReplays::actSubmitReplayCode); // Add actions to toolbars diff --git a/cockatrice/src/interface/widgets/tabs/tab_room.cpp b/cockatrice/src/interface/widgets/tabs/tab_room.cpp index 866324367..6245b5301 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_room.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_room.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../../pixel_map_generator.h" #include "../interface/widgets/dialogs/dlg_settings.h" #include "../interface/widgets/server/chat_view/chat_view.h" #include "../interface/widgets/server/game_link.h" @@ -99,7 +98,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, connect(aOpenChatSettings, &QAction::triggered, this, &TabRoom::actOpenChatSettings); auto *chatSettingsButton = new QToolButton; - chatSettingsButton->setIcon(themePixmap(QStringLiteral("icons/settings"))); + chatSettingsButton->setIcon(QPixmap("theme:icons/settings")); chatSettingsButton->setMenu(chatSettingsMenu); chatSettingsButton->setPopupMode(QToolButton::InstantPopup); diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp index 462aa420b..07b724b52 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp @@ -15,7 +15,6 @@ #include "tab_card_art_rules.h" #include "tab_deck_editor.h" #include "tab_deck_storage.h" -#include "tab_developer.h" #include "tab_game.h" #include "tab_home.h" #include "tab_logs.h" @@ -120,7 +119,7 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget * : QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabHome(nullptr), tabVisualDeckStorage(nullptr), tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr), tabReplays(nullptr), tabAdmin(nullptr), tabCardArtRules(nullptr), tabLog(nullptr), tabReport(nullptr), - tabModeration(nullptr), tabDeveloper(nullptr), isLocalGame(false) + tabModeration(nullptr), isLocalGame(false) { setElideMode(Qt::ElideRight); setMovable(true); @@ -206,10 +205,6 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget * aTabModeration->setCheckable(true); connect(aTabModeration, &QAction::triggered, this, &TabSupervisor::actTabModeration); - aTabDeveloper = new QAction(this); - aTabDeveloper->setCheckable(true); - connect(aTabDeveloper, &QAction::triggered, this, &TabSupervisor::actTabDeveloper); - connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, &TabSupervisor::refreshShortcuts); refreshShortcuts(); @@ -252,7 +247,6 @@ void TabSupervisor::retranslateUi() aTabReport->setText(tr("Report Queue")); aTabModeration->setText(tr("Moderation")); aTabCardArtRules->setText(tr("Card Art Rules")); - aTabDeveloper->setText(tr("Developer")); // tabs QList tabs; @@ -265,7 +259,6 @@ void TabSupervisor::retranslateUi() tabs.append(tabReport); tabs.append(tabModeration); tabs.append(tabCardArtRules); - tabs.append(tabDeveloper); QMapIterator roomIterator(roomTabs); while (roomIterator.hasNext()) { tabs.append(roomIterator.next().value()); @@ -535,19 +528,6 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo) } } - if (userInfo->user_level() & ServerInfo_User::IsDeveloper) { - tabsMenu->addSeparator(); - tabsMenu->addAction(aTabDeveloper); - // Developers without moderation rights get log access through their - // own role. Moderators already have the Logs entry from above. - if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) { - tabsMenu->addAction(aTabLog); - if (SettingsCache::instance().tabs().getTabLogOpen()) { - openTabLog(); - } - } - } - retranslateUi(); } @@ -560,7 +540,6 @@ void TabSupervisor::startLocal(const QList &_clients) tabLog = nullptr; tabReport = nullptr; tabModeration = nullptr; - tabDeveloper = nullptr; isLocalGame = true; userInfo = new ServerInfo_User; localClients = _clients; @@ -611,9 +590,6 @@ void TabSupervisor::stop() if (tabCardArtRules) { tabCardArtRules->close(); } - if (tabDeveloper) { - tabDeveloper->close(); - } } QList tabsToDelete; @@ -843,13 +819,7 @@ void TabSupervisor::actTabLog(bool checked) void TabSupervisor::openTabLog() { - // Developers query logs through the developer command family, so tell the - // tab which family to use. The moderator family is strictly stronger, so a - // moderator who also holds the developer bit keeps the moderator path — the - // developer bit only selects the (narrowed) developer family on its own. - const bool useDeveloperCommands = (userInfo->user_level() & ServerInfo_User::IsDeveloper) && - !(userInfo->user_level() & ServerInfo_User::IsModerator); - tabLog = new TabLog(this, client, useDeveloperCommands); + tabLog = new TabLog(this, client); myAddTab(tabLog, aTabLog); connect(tabLog, &QObject::destroyed, this, [this] { tabLog = nullptr; @@ -911,27 +881,6 @@ void TabSupervisor::openTabModeration(const QString &userName) aTabModeration->setChecked(true); } -void TabSupervisor::actTabDeveloper(bool checked) -{ - if (checked && !tabDeveloper) { - openTabDeveloper(); - setCurrentWidget(tabDeveloper); - } else if (!checked && tabDeveloper) { - tabDeveloper->closeRequest(); - } -} - -void TabSupervisor::openTabDeveloper() -{ - tabDeveloper = new TabDeveloper(this, client); - myAddTab(tabDeveloper, aTabDeveloper); - connect(tabDeveloper, &QObject::destroyed, this, [this] { - tabDeveloper = nullptr; - aTabDeveloper->setChecked(false); - }); - aTabDeveloper->setChecked(true); -} - void TabSupervisor::updatePingTime(int value, int max) { if (!tabServer) { @@ -1300,7 +1249,7 @@ void TabSupervisor::tabUserEvent(bool globalEvent) auto *tab = static_cast(sender()); if (tab != currentWidget()) { tab->setContentsChanged(true); - setTabIcon(indexOf(tab), themePixmap(QStringLiteral("icons/tab_changed"))); + setTabIcon(indexOf(tab), QPixmap("theme:icons/tab_changed")); } if (globalEvent && SettingsCache::instance().userInterface().getNotificationsEnabled()) { QApplication::alert(this); diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h index aec1d7418..b389bad3e 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h @@ -45,7 +45,6 @@ class TabReport; class TabModeration; class TabAccount; class TabDeckEditor; -class TabDeveloper; class TabLog; class RoomEvent; class GameEventContainer; @@ -109,7 +108,6 @@ private: TabLog *tabLog; TabReport *tabReport; TabModeration *tabModeration; - TabDeveloper *tabDeveloper; QMap roomTabs; QMap gameTabs; QList replayTabs; @@ -119,7 +117,7 @@ private: QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage, *aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin, - *aTabCardArtRules, *aTabLog, *aTabReport, *aTabModeration, *aTabDeveloper; + *aTabCardArtRules, *aTabLog, *aTabReport, *aTabModeration; int myAddTab(Tab *tab, QAction *manager = nullptr); void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager); @@ -209,7 +207,6 @@ private slots: void actTabLog(bool checked); void actTabReport(bool checked); void actTabModeration(bool checked); - void actTabDeveloper(bool checked); void openTabVisualDeckStorage(); void openTabHome(); @@ -221,7 +218,6 @@ private slots: void openTabCardArtRules(); void openTabLog(); void openTabReport(); - void openTabDeveloper(); void updateCurrent(int index); void updatePingTime(int value, int max); diff --git a/cockatrice/src/interface/widgets/utility/sequence_edit.cpp b/cockatrice/src/interface/widgets/utility/sequence_edit.cpp index 06561a41d..c6bf289ba 100644 --- a/cockatrice/src/interface/widgets/utility/sequence_edit.cpp +++ b/cockatrice/src/interface/widgets/utility/sequence_edit.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" -#include "../../pixel_map_generator.h" #include #include @@ -15,8 +14,8 @@ SequenceEdit::SequenceEdit(const QString &_shortcutName, QWidget *parent) : QWid defaultButton = new QPushButton("", this); lineEdit->setMinimumWidth(70); - clearButton->setIcon(themePixmap(QStringLiteral("icons/clearsearch"))); - defaultButton->setIcon(themePixmap(QStringLiteral("icons/update"))); + clearButton->setIcon(QPixmap("theme:icons/clearsearch")); + defaultButton->setIcon(QPixmap("theme:icons/update")); auto *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp index a59a068dd..4a558a5e0 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp @@ -1,6 +1,5 @@ #include "visual_database_display_filter_toolbar_widget.h" -#include "../../pixel_map_generator.h" #include "../deck_editor/card_database_view.h" #include "visual_database_display_widget.h" @@ -61,22 +60,22 @@ VisualDatabaseDisplayFilterToolbarWidget::VisualDatabaseDisplayFilterToolbarWidg }); quickFilterSaveLoadWidget = new SettingsButtonWidget(this); - quickFilterSaveLoadWidget->setButtonIcon(themePixmap(QStringLiteral("icons/floppy_disk"))); + quickFilterSaveLoadWidget->setButtonIcon(QPixmap("theme:icons/floppy_disk")); quickFilterNameWidget = new SettingsButtonWidget(this); - quickFilterNameWidget->setButtonIcon(themePixmap(QStringLiteral("icons/pen_to_square"))); + quickFilterNameWidget->setButtonIcon(QPixmap("theme:icons/pen_to_square")); quickFilterMainTypeWidget = new SettingsButtonWidget(this); - quickFilterMainTypeWidget->setButtonIcon(themePixmap(QStringLiteral("icons/circle_half_stroke"))); + quickFilterMainTypeWidget->setButtonIcon(QPixmap("theme:icons/circle_half_stroke")); quickFilterSubTypeWidget = new SettingsButtonWidget(this); - quickFilterSubTypeWidget->setButtonIcon(themePixmap(QStringLiteral("icons/dragon"))); + quickFilterSubTypeWidget->setButtonIcon(QPixmap("theme:icons/dragon")); quickFilterSetWidget = new SettingsButtonWidget(this); - quickFilterSetWidget->setButtonIcon(themePixmap(QStringLiteral("icons/scroll"))); + quickFilterSetWidget->setButtonIcon(QPixmap("theme:icons/scroll")); quickFilterFormatLegalityWidget = new SettingsButtonWidget(this); - quickFilterFormatLegalityWidget->setButtonIcon(themePixmap(QStringLiteral("icons/scale_balanced"))); + quickFilterFormatLegalityWidget->setButtonIcon(QPixmap("theme:icons/scale_balanced")); retranslateUi(); } diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp index f4530a12b..76bbf344b 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp @@ -66,7 +66,7 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)")); searchEdit->setClearButtonEnabled(true); searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); - auto help = searchEdit->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition); + auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(searchEdit); }); setFocusProxy(searchEdit); @@ -121,7 +121,7 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, clearFilterWidget = new QToolButton(); clearFilterWidget->setFixedSize(32, 32); - clearFilterWidget->setIcon(themePixmap(QStringLiteral("icons/delete"))); + clearFilterWidget->setIcon(QPixmap("theme:icons/delete")); connect(clearFilterWidget, &QToolButton::clicked, this, [this] { filterModel->blockSignals(true); filterModel->filterTree()->blockSignals(true); diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp index 28abe480e..f44c9c3ef 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp @@ -1,6 +1,5 @@ #include "visual_deck_display_options_widget.h" -#include "../../pixel_map_generator.h" #include "../tabs/visual_deck_editor/tab_deck_editor_visual.h" #include @@ -48,7 +47,7 @@ VisualDeckDisplayOptionsWidget::VisualDeckDisplayOptionsWidget(QWidget *parent) sortByLabel = new QLabel(this); sortCriteriaButton = new SettingsButtonWidget(this); - sortCriteriaButton->setButtonIcon(themePixmap(QStringLiteral("icons/sort_arrow_down"))); + sortCriteriaButton->setButtonIcon(QPixmap("theme:icons/sort_arrow_down")); sortLabel = new QLabel(sortCriteriaButton); sortLabel->setWordWrap(true); @@ -93,7 +92,7 @@ void VisualDeckDisplayOptionsWidget::retranslateUi() sortLabel->setText(tr("Click and drag to change the sort order within the groups")); sortCriteriaButton->setToolTip(tr("Configure how cards are sorted within their groups")); displayTypeButton->setButtonText(tr("Toggle Layout: Overlap")); - displayTypeButton->setButtonIcon(themePixmap(QStringLiteral("icons/scales"))); + displayTypeButton->setButtonIcon(QPixmap("theme:icons/scales")); displayTypeButton->setToolTip( tr("Change how cards are displayed within zones (i.e. overlapped or fully visible.)")); } @@ -118,11 +117,11 @@ void VisualDeckDisplayOptionsWidget::updateDisplayType() switch (currentDisplayType) { case DisplayType::Flat: displayTypeButton->setButtonText(tr("Toggle Layout: Flat")); - displayTypeButton->setButtonIcon(themePixmap(QStringLiteral("icons/scroll"))); + displayTypeButton->setButtonIcon(QPixmap("theme:icons/scroll")); break; case DisplayType::Overlap: displayTypeButton->setButtonText(tr("Toggle Layout: Overlap")); - displayTypeButton->setButtonIcon(themePixmap(QStringLiteral("icons/scales"))); + displayTypeButton->setButtonIcon(QPixmap("theme:icons/scales")); break; } emit displayTypeChanged(currentDisplayType); diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index 064fbed5d..e3261b346 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -4,7 +4,6 @@ #include "../../../main.h" #include "../../deck_loader/deck_loader.h" #include "../../layouts/overlap_layout.h" -#include "../../pixel_map_generator.h" #include "../cards/card_info_picture_with_text_overlay_widget.h" #include "../cards/deck_card_zone_display_widget.h" #include "../general/layout_containers/flow_widget.h" @@ -132,7 +131,7 @@ void VisualDeckEditorWidget::initializeSearchBarAndCompleter() // Search button functionality searchPushButton = new CompactPushButton(searchContainer); - searchPushButton->setButtonIcon(themePixmap(QStringLiteral("icons/search"))); + searchPushButton->setButtonIcon(QPixmap("theme:icons/search")); connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { ExactCard card = CardDatabaseManager::query()->getCard({searchBar->text()}); if (card) { diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp index 406527893..baa5e5792 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp @@ -25,7 +25,7 @@ VisualDeckStorageSearchWidget::VisualDeckStorageSearchWidget(QWidget *parent) : searchBar->setClearButtonEnabled(true); searchBar->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); - auto help = searchBar->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition); + auto help = searchBar->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); connect(help, &QAction::triggered, this, [this] { createDeckSearchSyntaxHelpWindow(searchBar); }); layout->addWidget(searchBar); diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp index da7ddc368..acb0dcab2 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp @@ -1,7 +1,6 @@ #include "visual_deck_storage_widget.h" #include "../../../client/settings/cache_settings.h" -#include "../../pixel_map_generator.h" #include "../quick_settings/settings_button_widget.h" #include "deck_preview/deck_preview_color_identity_filter_widget.h" #include "deck_preview/deck_preview_widget.h" @@ -44,7 +43,7 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare searchWidget = new VisualDeckStorageSearchWidget(this); refreshButton = new QToolButton(this); - refreshButton->setIcon(themePixmap(QStringLiteral("icons/reload"))); + refreshButton->setIcon(QPixmap("theme:icons/reload")); refreshButton->setFixedSize(32, 32); connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible); diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp index ce28fc24a..43f1de9dd 100644 --- a/cockatrice/src/interface/window_main.cpp +++ b/cockatrice/src/interface/window_main.cpp @@ -44,7 +44,6 @@ #include "intents/intent_open_server_room_by_name.h" #include "intents/url_parser.h" #include "logger.h" -#include "pixel_map_generator.h" #include "version_string.h" #include "widgets/dialogs/dlg_connect.h" #include "widgets/server/handle_public_servers.h" @@ -272,8 +271,7 @@ void MainWindow::actAbout() GITHUB_TROUBLESHOOTING_URL + "'>" + tr("Troubleshooting") + "
" + "" + tr("F.A.Q.") + "
"), QMessageBox::Ok, this); - mb.setIconPixmap( - themePixmap(QStringLiteral("cockatrice")).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + mb.setIconPixmap(QPixmap("theme:cockatrice").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); mb.setTextInteractionFlags(Qt::TextBrowserInteraction); mb.exec(); } @@ -325,7 +323,7 @@ void MainWindow::retranslateUi() aRegister->setText(tr("&Register to server...")); aForgotPassword->setText(tr("&Restore password...")); aSettings->setText(tr("&Settings...")); - aSettings->setIcon(themePixmap(QStringLiteral("icons/settings"))); + aSettings->setIcon(QPixmap("theme:icons/settings")); aExit->setText(tr("&Exit")); #if defined(__APPLE__) /* For OSX */ @@ -819,7 +817,7 @@ void MainWindow::createTrayIcon() trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); - trayIcon->setIcon(themePixmap(QStringLiteral("cockatrice"))); + trayIcon->setIcon(QPixmap("theme:cockatrice")); trayIcon->show(); } diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index ac77b4241..d8aa1cd08 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -349,7 +349,7 @@ int main(int argc, char *argv[]) } qCInfo(MainLog) << "MainWindow constructor finished"; - ui.setWindowIcon(themePixmap(QStringLiteral("cockatrice"))); + ui.setWindowIcon(QPixmap("theme:cockatrice")); // set name of the app desktop file; used by wayland to load the window icon QGuiApplication::setDesktopFileName("cockatrice"); diff --git a/cockatrice/themes/Default/palette-default-light.toml b/cockatrice/themes/Default/palette-default-light.toml deleted file mode 100644 index 14c215cdf..000000000 --- a/cockatrice/themes/Default/palette-default-light.toml +++ /dev/null @@ -1,67 +0,0 @@ -[Palette] -WindowText = #ff000000 -Button = #fff0f0f0 -Light = #ffffffff -Midlight = #ffe3e3e3 -Dark = #ffa0a0a0 -Mid = #ffa0a0a0 -Text = #ff000000 -BrightText = #ffffffff -ButtonText = #ff000000 -Base = #ffffffff -Window = #fff0f0f0 -Shadow = #ff696969 -HighlightedText = #ffffffff -Link = #ff0d5f28 -LinkVisited = #ff08401b -AlternateBase = #ffe9e7e3 -ToolTipBase = #ffffffdc -ToolTipText = #ff000000 -PlaceholderText = #80000000 - -[Palette.Disabled] -WindowText = #ff787878 -Button = #fff0f0f0 -Light = #ffffffff -Midlight = #fff7f7f7 -Dark = #ffa0a0a0 -Mid = #ffa0a0a0 -Text = #ff787878 -BrightText = #ffffffff -ButtonText = #ff787878 -Base = #fff0f0f0 -Window = #fff0f0f0 -Shadow = #ff000000 -HighlightedText = #ffffffff -Link = #ff0000ff -LinkVisited = #ffff00ff -AlternateBase = #fff7f7f7 -ToolTipBase = #ffffffdc -ToolTipText = #ff000000 -PlaceholderText = #80000000 - -[Palette.Inactive] -WindowText = #ff000000 -Button = #fff0f0f0 -Light = #ffffffff -Midlight = #ffe3e3e3 -Dark = #ffa0a0a0 -Mid = #ffa0a0a0 -Text = #ff000000 -BrightText = #ffffffff -ButtonText = #ff000000 -Base = #ffffffff -Window = #fff0f0f0 -Shadow = #ff696969 -HighlightedText = #ff000000 -Link = #ff0d5f28 -LinkVisited = #ff08401b -AlternateBase = #ffe9e7e3 -ToolTipBase = #ffffffdc -ToolTipText = #ff000000 -PlaceholderText = #80000000 - - -[AppColors] -AccentStrong = #ff148c3c -AccentSoft = #ff78c850 \ No newline at end of file diff --git a/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.cpp b/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.cpp index 687d93666..d6316deb3 100644 --- a/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.cpp +++ b/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.cpp @@ -253,24 +253,3 @@ PendingCommand *AbstractClient::prepareAdminCommand(const ::google::protobuf::Me c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd); return new PendingCommand(cont); } - -PendingCommand *AbstractClient::prepareDeveloperCommand(const ::google::protobuf::Message &cmd) -{ - CommandContainer cont; - DeveloperCommand *c = cont.add_developer_command(); - // A developer command message may also be usable through other command - // families, so select the extension scoped to DeveloperCommand rather than - // guessing by name. - const ::google::protobuf::Descriptor *cmdDescriptor = cmd.GetDescriptor(); - const ::google::protobuf::Descriptor *developerDescriptor = DeveloperCommand::descriptor(); - const ::google::protobuf::FieldDescriptor *developerExtension = nullptr; - for (int i = 0; i < cmdDescriptor->extension_count(); ++i) { - if (cmdDescriptor->extension(i)->containing_type() == developerDescriptor) { - developerExtension = cmdDescriptor->extension(i); - break; - } - } - Q_ASSERT(developerExtension != nullptr); - c->GetReflection()->MutableMessage(c, developerExtension)->CopyFrom(cmd); - return new PendingCommand(cont); -} diff --git a/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.h b/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.h index af22a5c9d..1ef9a31e4 100644 --- a/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.h +++ b/libcockatrice_network/libcockatrice/network/client/abstract/abstract_client.h @@ -173,7 +173,6 @@ public: static PendingCommand *prepareRoomCommand(const ::google::protobuf::Message &cmd, int roomId); static PendingCommand *prepareModeratorCommand(const ::google::protobuf::Message &cmd); static PendingCommand *prepareAdminCommand(const ::google::protobuf::Message &cmd); - static PendingCommand *prepareDeveloperCommand(const ::google::protobuf::Message &cmd); QMap clientFeatures; }; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp index 6b4101a99..957a89792 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp @@ -66,15 +66,6 @@ Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game, Server_AbstractPlayer::~Server_AbstractPlayer() = default; -int Server_AbstractPlayer::getCardCount() const -{ - int result = 0; - for (auto *zone : zones) { - result += zone->getCards().size(); - } - return result; -} - void Server_AbstractPlayer::prepareDestroy() { delete deck; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h index 4cc79c5fe..85fbc0557 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h @@ -43,8 +43,6 @@ public: Server_AbstractUserInterface *_handler); ~Server_AbstractPlayer() override; void prepareDestroy() override; - /// Total cards across all of this player's zones. The caller must hold the game's mutex. - int getCardCount() const; const DeckList *getDeckList() const { return deck; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index 799b1e7ee..43209e994 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -32,7 +32,6 @@ #include "server_spectator.h" #include -#include #include #include #include @@ -239,17 +238,6 @@ int Server_Game::getPlayerCount() const return participants.size() - getSpectatorCount(); } -int Server_Game::getCardsInGame() const -{ - QMutexLocker locker(&gameMutex); - - int result = 0; - for (auto *player : getPlayers()) { - result += player->getCardCount(); - } - return result; -} - int Server_Game::getSpectatorCount() const { QMutexLocker locker(&gameMutex); @@ -342,9 +330,6 @@ void Server_Game::doStartGameIfReady(bool forceStartGame) } } - // Only actual starts are timed. The early returns above are no-ops. - QElapsedTimer startupTimer; - startupTimer.start(); players = getPlayers(); // players could have been kicked, get new list of players if (lifecycleStrategy->onGameStarting(this) == Server_GameLifecycleStrategy::StartAction::Handled) { locker.unlock(); @@ -388,7 +373,6 @@ void Server_Game::doStartGameIfReady(bool forceStartGame) activePlayer = -1; nextTurn(); - room->getServer()->observeGameStartDurationMs(startupTimer.nsecsElapsed() / 1000000); locker.unlock(); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h index 1ed4fe4ca..1b9f651bd 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h @@ -123,8 +123,6 @@ public: return gameStarted; } int getPlayerCount() const; - /// Total cards across all players' zones. Takes gameMutex itself. - int getCardsInGame() const; int getSpectatorCount() const; QMap getPlayers() const; Server_AbstractPlayer *getPlayer(int id) const; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server.h b/libcockatrice_network/libcockatrice/network/server/remote/server.h index 3d27f4210..0ded27afa 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server.h @@ -180,11 +180,6 @@ public: { return false; } - /// Called once per actual game start with how long bringing every player's - /// zones online took, so servers can spot deck sizes that wedge threads. - virtual void observeGameStartDurationMs(qint64 /* elapsedMs */) - { - } Server_DatabaseInterface *getDatabaseInterface() const; int getNextLocalGameId() diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp index 8422d703d..899df6529 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.cpp @@ -388,33 +388,6 @@ Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(cons return finalResponseCode; } -Response::ResponseCode Server_ProtocolHandler::processDeveloperCommandContainer(const CommandContainer &cont, - ResponseContainer &rc) -{ - if (!userInfo) { - return Response::RespLoginNeeded; - } - if (!(userInfo->user_level() & ServerInfo_User::IsDeveloper)) { - return Response::RespLoginNeeded; - } - - resetIdleTimer(); - - Response::ResponseCode finalResponseCode = Response::RespOk; - for (int i = cont.developer_command_size() - 1; i >= 0; --i) { - Response::ResponseCode resp = Response::RespInvalidCommand; - const DeveloperCommand &sc = cont.developer_command(i); - const int num = getPbExtension(sc); - logDebugMessage(getSafeDebugString(sc)); - - resp = processExtendedDeveloperCommand(num, sc, rc); - if (resp != Response::RespOk) { - finalResponseCode = resp; - } - } - return finalResponseCode; -} - void Server_ProtocolHandler::processCommandContainer(const CommandContainer &cont) { // Command processing must be disabled after prepareDestroy() has been called. @@ -437,8 +410,6 @@ void Server_ProtocolHandler::processCommandContainer(const CommandContainer &con finalResponseCode = processModeratorCommandContainer(cont, responseContainer); } else if (cont.admin_command_size()) { finalResponseCode = processAdminCommandContainer(cont, responseContainer); - } else if (cont.developer_command_size()) { - finalResponseCode = processDeveloperCommandContainer(cont, responseContainer); } else { finalResponseCode = Response::RespInvalidCommand; } @@ -483,12 +454,11 @@ void Server_ProtocolHandler::pingClockTimeout() prepareDestroy(); } - // PrivLevel users, Moderators, Admins, and Developers are not subject to the server idle timeout policy + // PrivLevel users, Moderators, and Admins are not subject to the server idle timeout policy const bool hasPrivLevel = userInfo && QString::fromStdString(userInfo->privlevel()).toLower() != "none"; - const bool isStaff = - userInfo && (userInfo->user_level() & - (ServerInfo_User::IsModerator | ServerInfo_User::IsAdmin | ServerInfo_User::IsDeveloper)); - if (!hasPrivLevel && !isStaff) { + const bool isModOrAdmin = + userInfo && (userInfo->user_level() & (ServerInfo_User::IsModerator | ServerInfo_User::IsAdmin)); + if (!hasPrivLevel && !isModOrAdmin) { if ((server->getIdleClientTimeout() > 0) && (idleClientWarningSent)) { if (timeRunning - lastActionReceived > server->getIdleClientTimeout()) { prepareDestroy(); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.h b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.h index 2c8efe50e..0d05b91c8 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_protocolhandler.h @@ -27,7 +27,6 @@ class CommandContainer; class SessionCommand; class ModeratorCommand; class AdminCommand; -class DeveloperCommand; class Command_Ping; class Command_Login; @@ -99,12 +98,6 @@ private: { return Response::RespFunctionNotAllowed; } - Response::ResponseCode processDeveloperCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode - processExtendedDeveloperCommand(int /* cmdType */, const DeveloperCommand & /* cmd */, ResponseContainer & /* rc */) - { - return Response::RespFunctionNotAllowed; - } void resetIdleTimer(); private slots: @@ -136,7 +129,7 @@ public: return timeRunning - lastDataReceived; } bool addSaidMessageSize(int size); - virtual void processCommandContainer(const CommandContainer &cont); + void processCommandContainer(const CommandContainer &cont); void sendProtocolItem(const Response &item); void sendProtocolItem(const SessionEvent &item); diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt index 0791f8c14..f22828f46 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt @@ -25,7 +25,6 @@ set(PROTO_FILES command_dump_zone.proto command_flip_card.proto command_game_say.proto - command_get_server_stats.proto command_inc_card_counter.proto command_inc_counter.proto command_kick_from_game.proto @@ -72,7 +71,6 @@ set(PROTO_FILES context_ready_start.proto context_set_sideboard_lock.proto context_undo_draw.proto - developer_commands.proto event_add_to_list.proto event_attach_card.proto event_change_zone_properties.proto @@ -142,7 +140,6 @@ set(PROTO_FILES response_forgotpasswordrequest.proto response_get_admin_notes.proto response_get_games_of_user.proto - response_get_server_stats.proto response_get_user_info.proto response_join_room.proto response_list_users.proto diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto index f1f85e376..f8b34b3f8 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/admin_commands.proto @@ -37,7 +37,6 @@ message Command_AdjustMod { required string user_name = 1; optional bool should_be_mod = 2; optional bool should_be_judge = 3; - optional bool should_be_developer = 4; } message Command_ResetUserPassword { diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_get_server_stats.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_get_server_stats.proto deleted file mode 100644 index 33c56293b..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_get_server_stats.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto2"; -import "developer_commands.proto"; - -message Command_GetServerStats { - extend DeveloperCommand { - optional Command_GetServerStats ext = 1000; - } -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/commands.proto index 964407819..b6eaf6733 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/commands.proto @@ -4,7 +4,6 @@ import "game_commands.proto"; import "room_commands.proto"; import "moderator_commands.proto"; import "admin_commands.proto"; -import "developer_commands.proto"; message CommandContainer { optional uint64 cmd_id = 1; @@ -17,5 +16,4 @@ message CommandContainer { repeated RoomCommand room_command = 102; repeated ModeratorCommand moderator_command = 103; repeated AdminCommand admin_command = 104; - repeated DeveloperCommand developer_command = 105; } diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/developer_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/developer_commands.proto deleted file mode 100644 index bed47d44c..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/developer_commands.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto2"; -message DeveloperCommand { - enum DeveloperCommandType { - GET_SERVER_STATS = 1000; - VIEWLOG_HISTORY = 1001; - } - extensions 100 to max; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto index 4f1e80c27..685408830 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto @@ -1,5 +1,4 @@ syntax = "proto2"; -import "developer_commands.proto"; message ModeratorCommand { enum ModeratorCommandType { BAN_FROM_SERVER = 1000; @@ -81,9 +80,6 @@ message Command_ViewLogHistory { extend ModeratorCommand { optional Command_ViewLogHistory ext = 1005; } - extend DeveloperCommand { - optional Command_ViewLogHistory dev_ext = 1001; - } optional string user_name = 1; // user that created message optional string ip_address = 2; // ip address of user that created message optional string game_name = 3; // client id of user that created the message diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto index 42a42fcc0..14ba737b5 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto @@ -77,7 +77,6 @@ message Response { FORGOT_PASSWORD_REQUEST = 1016; // Response to password reset request PASSWORD_SALT = 1017; // Response containing password salt GET_ADMIN_NOTES = 1018; // Response with admin notes - GET_SERVER_STATS = 1019; // Response with server status statistics REPLAY_LIST = 1100; // Response listing replays REPLAY_DOWNLOAD = 1101; // Response for replay download REPLAY_GET_CODE = 1102; // Response containing replay code diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_get_server_stats.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_get_server_stats.proto deleted file mode 100644 index bb8ff3c43..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_get_server_stats.proto +++ /dev/null @@ -1,41 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message CommandStats { - optional uint32 kind_index = 1; // 0=session, 1=room, 2=game, 3=moderator, 4=admin, 5=developer - optional uint32 extension_number = 2; // protobuf extension number within the kind - optional string command_name = 3; // e.g. "session/Command_Ping" - optional uint64 count = 4; // number of times observed - optional uint64 total_ms = 5; // cumulative processing milliseconds -} - -message Response_GetServerStats { - extend Response { - optional Response_GetServerStats ext = 1220; - } - - optional uint64 users_count = 1; - optional uint64 mods_count = 2; - optional uint64 games_count = 3; - - // Traffic recorded during the last status update tick - optional uint64 tx_bytes = 4; - optional uint64 rx_bytes = 5; - - optional uint64 uptime_secs = 6; - optional uint64 timest = 7; // unix timestamp of the snapshot - - // Live metrics from MetricsRegistry (reset on server restart) - optional uint64 cards_in_games = 8; - optional uint64 eventloop_stalls_total = 9; - optional uint64 eventloop_last_stall_ms = 10; - optional uint64 eventloop_max_stall_ms = 11; - optional uint64 total_commands = 12; - optional uint64 total_command_time_ms = 13; - optional int32 active_command_types = 14; - optional uint64 game_start_count = 15; - optional uint64 game_start_total_ms = 16; - - // Per-command breakdown (only types with count > 0) - repeated CommandStats command_stats = 20; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto index ea3f56705..98cc3ce6a 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto @@ -8,7 +8,6 @@ message ServerInfo_User { IsModerator = 4; IsAdmin = 8; IsJudge = 16; - IsDeveloper = 32; }; message PawnColorsOverride { optional string left_side = 1; diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 392184b6e..68c4709bb 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -28,7 +28,6 @@ set(oracle_SOURCES ../cockatrice/src/client/settings/card_counter_settings.cpp ../cockatrice/src/client/settings/shortcuts_settings.cpp ../cockatrice/src/client/network/update/client/release_channel.cpp - ../cockatrice/src/interface/pixel_map_generator.cpp ../cockatrice/src/interface/theme_config.cpp ../cockatrice/src/interface/theme_manager.cpp ../cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 68e422d8c..5d8089ad1 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -6,9 +6,7 @@ project(Servatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${ set(servatrice_SOURCES src/email_parser.cpp - src/event_loop_watchdog.cpp src/main.cpp - src/metrics_registry.cpp src/servatrice.cpp src/servatrice_connection_pool.cpp src/servatrice_database_interface.cpp diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index 23eca3f1b..c1940c22f 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -382,19 +382,6 @@ max_reports_per_day=10 ; Maximum number of report comments a single user can post per hour; default is 30; set to 0 to disable the limit max_comments_per_hour=30 -[metrics] -; Command containers that take longer than this many milliseconds are logged -; as slow commands. Set to 0 to disable the log line. A latency spike produces -; one warning per slow container with no rate limiting of its own -- a bad -; patch can briefly flood the log, which is how you notice it. -slow_command_ms=500 - -; Each socket pool thread runs a watchdog heartbeat. If a heartbeat arrives -; this many milliseconds late, the stall is logged and exposed as -; servatrice_eventloop_* metrics in the Developer tab. Set to 0 to disable -; the watchdogs. -stall_warn_ms=2000 - [logging] ; Admin/Moderators can query the stored logs for information when looking up reports by various players. This ; option can allow or disallow them from doing so. diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index cfb1ef5d8..5dbf69cbc 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -25,9 +25,6 @@ INSERT INTO cockatrice_schema_version VALUES(36); -- users and user data tables CREATE TABLE IF NOT EXISTS `cockatrice_users` ( `id` int(7) unsigned zerofill NOT NULL auto_increment, - -- Bitfield of staff levels: 1 = admin (implies moderator), 2 = moderator, - -- 4 = judge, 8 = developer. Operators set these by hand with - -- "UPDATE cockatrice_users SET admin = ...". `admin` tinyint(1) NOT NULL, `name` varchar(35) NOT NULL, `realname` varchar(255) NOT NULL, diff --git a/servatrice/src/event_loop_watchdog.cpp b/servatrice/src/event_loop_watchdog.cpp deleted file mode 100644 index e50bd4201..000000000 --- a/servatrice/src/event_loop_watchdog.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file event_loop_watchdog.cpp - * @ingroup Servatrice - */ - -#include "event_loop_watchdog.h" - -#include "servatrice.h" - -#include - -EventLoopWatchdog::EventLoopWatchdog(Servatrice *_servatrice, QString _threadName) - : QObject(nullptr), servatrice(_servatrice), threadName(std::move(_threadName)) -{ -} - -void EventLoopWatchdog::start() -{ - heartbeatTimer = new QTimer(this); - sinceLastTick.start(); - connect(heartbeatTimer, &QTimer::timeout, this, &EventLoopWatchdog::checkHeartbeat); - heartbeatTimer->start(HeartbeatIntervalMs); -} - -void EventLoopWatchdog::checkHeartbeat() -{ - const qint64 elapsedMs = sinceLastTick.restart(); - const qint64 overshootMs = qMax(0, elapsedMs - HeartbeatIntervalMs); - if (overshootMs < servatrice->getMetricsStallWarnMs()) { - return; - } - - servatrice->observeEventLoopStall(threadName, overshootMs); -} diff --git a/servatrice/src/event_loop_watchdog.h b/servatrice/src/event_loop_watchdog.h deleted file mode 100644 index b9061ff97..000000000 --- a/servatrice/src/event_loop_watchdog.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file event_loop_watchdog.h - * @ingroup Servatrice - */ - -#ifndef EVENT_LOOP_WATCHDOG_H -#define EVENT_LOOP_WATCHDOG_H - -#include -#include -#include - -class Servatrice; -class QTimer; - -/** - * @brief Detects blocked or overloaded worker event loops. - * - * One instance lives in each socket pool thread. A heartbeat timer tick that - * arrives late means the loop spent that time elsewhere: busy work, a queued - * slot, or a hard wedge. Overshoots past the configured threshold bump - * lock-free counters on the metrics registry and log one warning per stall, - * so a stuck pool thread becomes visible instead of silent lag. - */ -class EventLoopWatchdog : public QObject -{ - Q_OBJECT -public: - /// How often the heartbeat expects to fire. Small enough to catch short stalls. - static constexpr int HeartbeatIntervalMs = 500; - - EventLoopWatchdog(Servatrice *_servatrice, QString _threadName); - - /** - * Starts the heartbeat timer. Must be invoked queued after the instance - * was moved to its target thread so the timer lives there too. - */ - void start(); - -private slots: - void checkHeartbeat(); - -private: - Servatrice *servatrice; - QString threadName; - QElapsedTimer sinceLastTick; - QTimer *heartbeatTimer = nullptr; -}; - -#endif diff --git a/servatrice/src/metrics_registry.cpp b/servatrice/src/metrics_registry.cpp deleted file mode 100644 index 99ce390f1..000000000 --- a/servatrice/src/metrics_registry.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "metrics_registry.h" - -#include - -void MetricsRegistry::observeCommand(int typeId, qint64 elapsedMs) -{ - if (typeId < 0 || typeId >= MaxTypes) { - typeId = MaxTypes - 1; // overflow slot keeps misrouted ids visible - } - if (elapsedMs < 0) { - elapsedMs = 0; - } - - TypeStats &stats = slotFor(typeId); - stats.count.fetch_add(1, std::memory_order_relaxed); - stats.totalMs.fetch_add(elapsedMs, std::memory_order_relaxed); - totalCommandsCounter.fetch_add(1, std::memory_order_relaxed); - totalTimeCounter.fetch_add(elapsedMs, std::memory_order_relaxed); -} - -void MetricsRegistry::observeGameStartDurationMs(qint64 elapsedMs) -{ - if (elapsedMs < 0) { - elapsedMs = 0; - } - - gameStartStats.count.fetch_add(1, std::memory_order_relaxed); - gameStartStats.totalMs.fetch_add(elapsedMs, std::memory_order_relaxed); -} - -MetricsRegistry::TypeStats &MetricsRegistry::slotFor(int typeId) -{ - return typeSlots[static_cast(typeId)]; -} - -const MetricsRegistry::TypeStats &MetricsRegistry::slotFor(int typeId) const -{ - return typeSlots[static_cast(typeId)]; -} - -int MetricsRegistry::activeTypeCount() const -{ - int active = 0; - for (int type = 0; type < MaxTypes; ++type) { - if (slotFor(type).count.load(std::memory_order_relaxed) > 0) { - ++active; - } - } - return active; -} - -QList MetricsRegistry::collectActiveStats() const -{ - QList result; - for (int type = 0; type < MaxTypes; ++type) { - const TypeStats &stats = slotFor(type); - const qint64 count = stats.count.load(std::memory_order_relaxed); - if (count == 0) { - continue; - } - result.append({type, count, stats.totalMs.load(std::memory_order_relaxed)}); - } - return result; -} - -MetricsRegistry::GameStartSnapshot MetricsRegistry::getGameStartSnapshot() const -{ - return {gameStartStats.count.load(std::memory_order_relaxed), - gameStartStats.totalMs.load(std::memory_order_relaxed)}; -} \ No newline at end of file diff --git a/servatrice/src/metrics_registry.h b/servatrice/src/metrics_registry.h deleted file mode 100644 index 4df1057c2..000000000 --- a/servatrice/src/metrics_registry.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @file metrics_registry.h - * @ingroup Servatrice - */ - -#ifndef METRICS_REGISTRY_H -#define METRICS_REGISTRY_H - -#include -#include -#include -#include - -/** - * @brief Lock-free accumulation of command processing statistics. - * - * observeCommand() is called once per processed command from whichever socket - * thread handled it. It uses relaxed atomic adds on preallocated storage only, - * so it introduces no locks, allocations, or shared cache-line ping-pong - * beyond the unavoidable counter updates. - * - * Reading happens rarely (metrics scraping), accepts momentary tears between - * related counters, and therefore also needs no synchronization. - * - * Only counts and totals are retained. An earlier Prometheus-style cumulative - * histogram (per-type, time-bucketed) was cut because nothing in the server - * ever wrote it out; it belongs to the future /metrics exporter that needs it. - */ -class MetricsRegistry -{ -public: - /** - * Extension numbers are only unique per command kind, so recorded ids - * combine the kind index with the protobuf extension number. - * - * The stride is only as wide as it needs to be: 1280 is the first round - * number above the largest extension actually in use (ModeratorCommand = - * 1206) and keeps the preallocated TypeStats array small. Bump it if a new - * command exceeds it. - */ - static constexpr int KindStride = 1280; - - static constexpr int NumKinds = 6; - - static constexpr const char *KindNames[NumKinds] = {"session", "room", "game", "moderator", "admin", "developer"}; - - /// Upper bound on distinct command type ids (see typeIdFor). - static constexpr int MaxTypes = NumKinds * KindStride; - - /// Guard against typeIdFor() overflowing into the neighbouring kind's slots. - static_assert(KindStride > 1206, "KindStride must exceed the highest command extension number in use"); - - static int typeIdFor(int kindIndex, int extensionNumber) - { - return kindIndex * KindStride + extensionNumber; - } - - void observeCommand(int typeId, qint64 elapsedMs); - - /** - * Records how long one game start took to bring every player's zones - * online. Kept separate from command timings because it is triggered by - * the server itself and can dwarf any single command when decks are huge. - */ - void observeGameStartDurationMs(qint64 elapsedMs); - - /// Total number of observed commands across all types. - qint64 totalCommands() const - { - return totalCommandsCounter.load(std::memory_order_relaxed); - } - - /// Cumulative processing milliseconds across all types. - qint64 totalTimeMs() const - { - return totalTimeCounter.load(std::memory_order_relaxed); - } - - /// Number of distinct type slots that have seen at least one sample. - int activeTypeCount() const; - - struct ActiveTypeStats - { - int typeId; - qint64 count; - qint64 totalMs; - }; - - /** - * Returns stats for every type slot that has seen at least one sample. - * Callers resolve the numeric type id to a human-readable label via - * typeIdFor()/KindNames as needed. - */ - QList collectActiveStats() const; - - struct GameStartSnapshot - { - qint64 count; - qint64 totalMs; - }; - - GameStartSnapshot getGameStartSnapshot() const; - -private: - struct TypeStats - { - std::atomic count{0}; - std::atomic totalMs{0}; - }; - - TypeStats &slotFor(int typeId); - const TypeStats &slotFor(int typeId) const; - - std::array typeSlots{}; - TypeStats gameStartStats{}; - std::atomic totalCommandsCounter{0}; - std::atomic totalTimeCounter{0}; -}; - -#endif \ No newline at end of file diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 26352ccd7..db8751658 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -20,7 +20,6 @@ #include "servatrice.h" #include "email_parser.h" -#include "event_loop_watchdog.h" #include "isl_interface.h" #include "main.h" #include "servatrice_connection_pool.h" @@ -39,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -65,7 +63,6 @@ Servatrice_GameServer::Servatrice_GameServer(Servatrice *_server, server->addDatabaseInterface(newThread, newDatabaseInterface); newThread->start(); - server->watchWorkerThread(newThread); QMetaObject::invokeMethod(newDatabaseInterface, "initDatabase", Qt::BlockingQueuedConnection, Q_ARG(QSqlDatabase, _sqlDatabase)); @@ -89,6 +86,7 @@ void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor) Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); auto ssi = new TcpServerSocketInterface(server, pool->getDatabaseInterface()); + connect(ssi, SIGNAL(incTxBytes(qint64)), this, SLOT(incTxBytes(qint64))); ssi->moveToThread(pool->thread()); pool->addClient(); connect(ssi, SIGNAL(destroyed()), pool, SLOT(removeClient())); @@ -133,7 +131,6 @@ Servatrice_WebsocketGameServer::Servatrice_WebsocketGameServer(Servatrice *_serv server->addDatabaseInterface(newThread, newDatabaseInterface); newThread->start(); - server->watchWorkerThread(newThread); QMetaObject::invokeMethod(newDatabaseInterface, "initDatabase", Qt::BlockingQueuedConnection, Q_ARG(QSqlDatabase, _sqlDatabase)); @@ -159,6 +156,7 @@ void Servatrice_WebsocketGameServer::onNewConnection() Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); auto ssi = new WebsocketServerSocketInterface(server, pool->getDatabaseInterface()); + connect(ssi, SIGNAL(incTxBytes(quint64)), this, SLOT(incTxBytes(quint64))); /* * Due to a Qt limitation, websockets can't be moved to another thread. * This will hopefully change in Qt6 if QtWebSocket will be integrated in QtNetwork @@ -228,13 +226,6 @@ bool Servatrice::initServer() { serverId = getServerID(); - - // METRICS (always active. Slow-command logging and stall watchdogs are - // controlled by their respective thresholds below). Read up front so the - // values are available before any pool thread is started and watchdogged. - metricsSlowCommandMs = settingsCache->value("metrics/slow_command_ms", 500).toInt(); - metricsStallWarnMs = qMax(0, settingsCache->value("metrics/stall_warn_ms", 2000).toInt()); - if (getAuthenticationMethodString() == "sql") { qDebug() << "Authenticating method: sql"; authenticationMethod = AuthenticationSql; @@ -479,55 +470,9 @@ bool Servatrice::initServer() } setRequiredFeatures(getRequiredFeatures()); - return true; } -void Servatrice::observeGameStartDurationMs(qint64 elapsedMs) -{ - metricsRegistry.observeGameStartDurationMs(elapsedMs); -} - -void Servatrice::observeEventLoopStall(const QString &threadName, qint64 overshootMs) -{ - eventLoopStallsTotal.fetch_add(1, std::memory_order_relaxed); - eventLoopLastStallMs.store(overshootMs, std::memory_order_relaxed); - qint64 prevMax = eventLoopMaxStallMs.load(std::memory_order_relaxed); - while (overshootMs > prevMax && - !eventLoopMaxStallMs.compare_exchange_weak(prevMax, overshootMs, std::memory_order_relaxed)) { - // retry until the max is at least as high as the new sample - } - - qWarning() << "Event loop stall in" << threadName << "- heartbeat overshot by" << overshootMs << "ms"; -} - -void Servatrice::watchWorkerThread(QThread *thread) -{ - if (metricsStallWarnMs <= 0) { - return; // watchdogs disabled via metrics/stall_warn_ms = 0 - } - - auto *watchdog = new EventLoopWatchdog(this, thread->objectName()); - connect(thread, &QThread::finished, watchdog, &QObject::deleteLater); - watchdog->moveToThread(thread); - QMetaObject::invokeMethod(watchdog, &EventLoopWatchdog::start, Qt::QueuedConnection); -} - -qint64 Servatrice::getCardsInGamesTotal() const -{ - qint64 total = 0; - QReadLocker roomsLocker(&roomsLock); // locking order: roomsLock before gamesLock/gameMutex - QMapIterator roomIterator(rooms); - while (roomIterator.hasNext()) { - Server_Room *room = roomIterator.next().value(); - QReadLocker gamesLocker(&room->gamesLock); - for (auto *game : room->getGames()) { - total += game->getCardsInGame(); - } - } - return total; -} - void Servatrice::addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface) { databaseInterfaces.insert(thread, databaseInterface); diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 8d964a52b..8b0f5ad60 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -20,8 +20,6 @@ #ifndef SERVATRICE_H #define SERVATRICE_H -#include "metrics_registry.h" - #include #include #include @@ -32,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -173,12 +170,6 @@ private: int uptime; QMutex txBytesMutex, rxBytesMutex; quint64 txBytes, rxBytes; - MetricsRegistry metricsRegistry; - int metricsSlowCommandMs = 500; - int metricsStallWarnMs = 2000; - std::atomic eventLoopStallsTotal{0}; ///< heartbeat overshoots past the warn threshold - std::atomic eventLoopLastStallMs{0}; ///< overshoot of the most recent stall - std::atomic eventLoopMaxStallMs{0}; ///< worst overshoot seen since process start QString shutdownReason; int shutdownMinutes; @@ -295,49 +286,6 @@ public: void incRxBytes(quint64 num); void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface); - // Metrics (see [metrics] section in servatrice.ini.example) - MetricsRegistry &getMetricsRegistry() - { - return metricsRegistry; - } - /** - * Sums cards across all zones of all running games. Each game takes its - * own gameMutex -- the hot per-game lock every game action contends on -- - * and then iterates every player's zones, so the scrape cost is really - * O(total cards in play) plus one mutex acquisition per live game. Keep - * scrapes infrequent in big multiplayer rooms. - */ - qint64 getCardsInGamesTotal() const; - int getMetricsSlowCommandMs() const - { - return metricsSlowCommandMs; - } - /// Heartbeat overshoot that counts as a stall. A value of 0 disables the watchdogs. - int getMetricsStallWarnMs() const - { - return metricsStallWarnMs; - } - void observeGameStartDurationMs(qint64 elapsedMs) override; - qint64 getEventLoopStallsTotal() const - { - return eventLoopStallsTotal.load(std::memory_order_relaxed); - } - qint64 getEventLoopLastStallMs() const - { - return eventLoopLastStallMs.load(std::memory_order_relaxed); - } - qint64 getEventLoopMaxStallMs() const - { - return eventLoopMaxStallMs.load(std::memory_order_relaxed); - } - /// Records one heartbeat overshoot and logs a single warning for it. - void observeEventLoopStall(const QString &threadName, qint64 overshootMs); - /** - * Installs an EventLoopWatchdog in @p thread. Called once per socket pool - * thread right after it starts. - */ - void watchWorkerThread(QThread *thread); - bool islConnectionExists(int _serverId) const; void addIslInterface(int _serverId, IslInterface *interface); void removeIslInterface(int _serverId); diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index 36604bae7..847be61da 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -641,10 +641,6 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer userLevel |= ServerInfo_User::IsJudge; } - if (is_admin & 8) { - userLevel |= ServerInfo_User::IsDeveloper; - } - result.set_user_level(userLevel); const QString country = query->value(3).toString(); @@ -1452,7 +1448,7 @@ QList Servatrice_DatabaseInterface::getModeratorLastL QSqlQuery *query = prepareQuery("SELECT u.name, u.admin, UNIX_TIMESTAMP(a.last_login) " "FROM {prefix}_users u " "LEFT JOIN {prefix}_user_analytics a ON a.id = u.id " - "WHERE (u.admin & 15) <> 0 ORDER BY u.name"); + "WHERE (u.admin & 7) <> 0 ORDER BY u.name"); if (!execSqlQuery(query)) { qCWarning(DatabaseInterfaceLog) << "Failed to collect moderator login information: SQL Error"; @@ -1473,9 +1469,6 @@ QList Servatrice_DatabaseInterface::getModeratorLastL if (isAdmin & 4) { userLevel |= ServerInfo_User::IsJudge; } - if (isAdmin & 8) { - userLevel |= ServerInfo_User::IsDeveloper; - } loginDetails.set_user_level(userLevel); if (!query->value(2).isNull()) { @@ -1487,38 +1480,6 @@ QList Servatrice_DatabaseInterface::getModeratorLastL return results; } -Servatrice_DatabaseInterface::UptimeSnapshot Servatrice_DatabaseInterface::getLatestUptimeSnapshot(int serverId) -{ - UptimeSnapshot snapshot; - - if (!checkSql()) { - return snapshot; - } - - QSqlQuery *query = prepareQuery("SELECT users_count, mods_count, games_count, tx_bytes, rx_bytes, uptime, " - "UNIX_TIMESTAMP(timest) FROM {prefix}_uptime " - "WHERE id_server = :id_server ORDER BY timest DESC LIMIT 1"); - query->bindValue(":id_server", serverId); - - if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect server stats snapshot: SQL Error"; - return snapshot; - } - - if (query->next()) { - snapshot.valid = true; - snapshot.usersCount = query->value(0).toULongLong(); - snapshot.modsCount = query->value(1).toULongLong(); - snapshot.gamesCount = query->value(2).toULongLong(); - snapshot.txBytes = query->value(3).toULongLong(); - snapshot.rxBytes = query->value(4).toULongLong(); - snapshot.uptimeSecs = query->value(5).toULongLong(); - snapshot.timest = query->value(6).toULongLong(); - } - - return snapshot; -} - bool Servatrice_DatabaseInterface::removeUserAvatar(const QString &userName) { if (!checkSql()) { diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index a891c7a3d..cd76ae288 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -140,21 +140,6 @@ public: QList getUserSessions(const QString &userName, int limit); QList getUserAlts(const QString &userName); QList getModeratorLastLogins(); - - // Uptime snapshot as recorded by Servatrice::statusUpdate() into the - // {prefix}_uptime table. valid is false when no snapshot exists yet. - struct UptimeSnapshot - { - bool valid = false; - quint64 usersCount = 0; - quint64 modsCount = 0; - quint64 gamesCount = 0; - quint64 txBytes = 0; - quint64 rxBytes = 0; - quint64 uptimeSecs = 0; - quint64 timest = 0; - }; - UptimeSnapshot getLatestUptimeSnapshot(int serverId); bool removeUserAvatar(const QString &userName); bool addForgotPassword(const QString &user); bool removeForgotPassword(const QString &user) override; diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index aeffd7081..2a8b5f0a4 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -40,17 +39,14 @@ #include #include #include -#include #include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -84,7 +80,6 @@ #include #include #include -#include #include #include #include @@ -195,77 +190,6 @@ void AbstractServerSocketInterface::logDebugMessage(const QString &message) logger->logMessage(message, this); } -void AbstractServerSocketInterface::processCommandContainer(const CommandContainer &cont) -{ - QElapsedTimer timer; - timer.start(); - Server_ProtocolHandler::processCommandContainer(cont); - const qint64 elapsedMs = timer.nsecsElapsed() / 1000000; - - // The base dispatch is an if/else-if chain — at most one family is - // actually processed. Recording every family in the container would - // let an unauthenticated client stampforge developer/moderator/admin - // samples by batching them alongside a session command the server - // actually runs. Mirror the base's selection and skip entirely when - // deleted or when no family matched. - if (deleted) { - return; - } - - // When getPbExtension returns -1 (no extension set) and the kind is - // non-zero, typeIdFor wraps into the previous kind's range instead of - // hitting the typeId < 0 guard in observeCommand. Skip such entries. - int kind = -1; - if (cont.game_command_size()) { - kind = 2; - } else if (cont.room_command_size()) { - kind = 1; - } else if (cont.session_command_size()) { - kind = 0; - } else if (cont.moderator_command_size()) { - kind = 3; - } else if (cont.admin_command_size()) { - kind = 4; - } else if (cont.developer_command_size()) { - kind = 5; - } - - if (kind >= 0) { - auto recordDispatched = [&](int familyKind, const auto &cmds) { - for (const auto &cmd : cmds) { - const int ext = getPbExtension(cmd); - if (ext >= 0) { - servatrice->getMetricsRegistry().observeCommand(MetricsRegistry::typeIdFor(familyKind, ext), - elapsedMs); - } - } - }; - - if (kind == 0) { - recordDispatched(kind, cont.session_command()); - } else if (kind == 1) { - recordDispatched(kind, cont.room_command()); - } else if (kind == 2) { - recordDispatched(kind, cont.game_command()); - } else if (kind == 3) { - recordDispatched(kind, cont.moderator_command()); - } else if (kind == 4) { - recordDispatched(kind, cont.admin_command()); - } else { - recordDispatched(kind, cont.developer_command()); - } - } - - const int slowCommandMs = servatrice->getMetricsSlowCommandMs(); - if (slowCommandMs > 0 && elapsedMs >= slowCommandMs) { - const ServerInfo_User *info = getUserInfo(); - const QString user = authState == PasswordRight && info ? QString::fromStdString(info->name()) - : QStringLiteral("unauthenticated"); - qCWarning(AbstractServerSocketInterfaceLog) << "slow command container from" << user << "processed in" - << elapsedMs << "ms (" << cont.ByteSizeLong() << "bytes)"; - } -} - Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) @@ -360,7 +284,7 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo case ModeratorCommand::REPORT_RESOLVE: return cmdReportResolve(cmd.GetExtension(Command_ReportResolve::ext), rc); case ModeratorCommand::VIEWLOG_HISTORY: - return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::ext), rc, true); + return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::ext), rc); case ModeratorCommand::GRANT_REPLAY_ACCESS: return cmdGrantReplayAccess(cmd.GetExtension(Command_GrantReplayAccess::ext), rc); case ModeratorCommand::REPLAY_DOWNLOAD_BY_GAME_ID: @@ -413,26 +337,6 @@ AbstractServerSocketInterface::processExtendedAdminCommand(int cmdType, const Ad } } -// DEVELOPER FUNCTIONS. -// Permission is checked by processDeveloperCommandContainer. Only stats-style -// queries live here, never community moderation or server administration. -Response::ResponseCode AbstractServerSocketInterface::processExtendedDeveloperCommand(int cmdType, - const DeveloperCommand &cmd, - ResponseContainer &rc) -{ - switch ((DeveloperCommand::DeveloperCommandType)cmdType) { - case DeveloperCommand::GET_SERVER_STATS: - return cmdGetServerStats(cmd.GetExtension(Command_GetServerStats::ext), rc); - case DeveloperCommand::VIEWLOG_HISTORY: { - // Same query as the moderator log view, carried by the developer - // command family, but narrows out private chats and sender IPs. - return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::dev_ext), rc, false); - } - default: - return Response::RespFunctionNotAllowed; - } -} - Response::ResponseCode AbstractServerSocketInterface::cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc) { if (authState != PasswordRight) { @@ -1116,13 +1020,12 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplaySubmitCode(const // MODERATOR FUNCTIONS. // May be called by admins and moderators. Permission is checked by the calling function. Response::ResponseCode AbstractServerSocketInterface::cmdGetLogHistory(const Command_ViewLogHistory &cmd, - ResponseContainer &rc, - bool allowPrivateChat) + ResponseContainer &rc) { QList messageList; QString userName = nameFromStdString(cmd.user_name()); - QString ipAddress = allowPrivateChat ? nameFromStdString(cmd.ip_address()) : QString(); + QString ipAddress = nameFromStdString(cmd.ip_address()); QString gameName = nameFromStdString(cmd.game_name()); QString gameID = nameFromStdString(cmd.game_id()); QString message = textFromStdString(cmd.message()); @@ -1137,21 +1040,11 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetLogHistory(const Com if (nameFromStdString(cmd.log_location(i)).simplified() == "game") { gameType = true; } - if (nameFromStdString(cmd.log_location(i)).simplified() == "chat" && allowPrivateChat) { + if (nameFromStdString(cmd.log_location(i)).simplified() == "chat") { chatType = true; } } - // For callers that must not see private conversations, never leave the - // target-type filter empty: if the request only asked for "chat" (or for - // nothing at all) the query below would carry no target_type restriction - // and would return every row, private messages included. Fall back to the - // game/room diagnostics the caller is allowed to see. - if (!allowPrivateChat && !gameType && !roomType) { - gameType = true; - roomType = true; - } - int dateRange = cmd.date_range(); int maximumResults = cmd.maximum_results(); @@ -1161,11 +1054,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetLogHistory(const Com QListIterator messageIterator(sqlInterface->getMessageLogHistory( userName, ipAddress, gameName, gameID, message, chatType, gameType, roomType, dateRange, maximumResults)); while (messageIterator.hasNext()) { - ServerInfo_ChatMessage chatMessage = messageIterator.next(); - if (!allowPrivateChat) { - chatMessage.clear_sender_ip(); - } - re->add_log_message()->CopyFrom(chatMessage); + re->add_log_message()->CopyFrom(messageIterator.next()); } } else { ServerInfo_ChatMessage chatMessage; @@ -1754,79 +1643,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportUserInfo(const Co return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdGetServerStats(const Command_GetServerStats & /*cmd */, - ResponseContainer &rc) -{ - if (!sqlInterface->checkSql()) { - return Response::RespInternalError; - } - - // Servatrice::statusUpdate() periodically snapshots server health into the - // uptime table. Serve the freshest snapshot for this server. - const auto snapshot = sqlInterface->getLatestUptimeSnapshot(servatrice->getServerID()); - if (!snapshot.valid) { - // No snapshot yet (fresh server, or statusUpdate() has not ticked). - return Response::RespInternalError; - } - - auto *re = new Response_GetServerStats; - re->set_users_count(snapshot.usersCount); - re->set_mods_count(snapshot.modsCount); - re->set_games_count(snapshot.gamesCount); - re->set_tx_bytes(snapshot.txBytes); - re->set_rx_bytes(snapshot.rxBytes); - re->set_uptime_secs(snapshot.uptimeSecs); - re->set_timest(snapshot.timest); - - // Live metrics from the in-process MetricsRegistry (resets on server restart) - re->set_cards_in_games(static_cast(servatrice->getCardsInGamesTotal())); - re->set_eventloop_stalls_total(static_cast(servatrice->getEventLoopStallsTotal())); - re->set_eventloop_last_stall_ms(static_cast(servatrice->getEventLoopLastStallMs())); - re->set_eventloop_max_stall_ms(static_cast(servatrice->getEventLoopMaxStallMs())); - re->set_total_commands(static_cast(servatrice->getMetricsRegistry().totalCommands())); - re->set_total_command_time_ms( - static_cast(servatrice->getMetricsRegistry().totalTimeMs())); - re->set_active_command_types(servatrice->getMetricsRegistry().activeTypeCount()); - - const auto gameStart = servatrice->getMetricsRegistry().getGameStartSnapshot(); - re->set_game_start_count(static_cast(gameStart.count)); - re->set_game_start_total_ms(static_cast(gameStart.totalMs)); - - // Per-command breakdown: resolve protobuf extension names via the descriptor pool - static const char *messageNames[] = {"SessionCommand", "RoomCommand", "GameCommand", - "ModeratorCommand", "AdminCommand", "DeveloperCommand"}; - const auto activeStats = servatrice->getMetricsRegistry().collectActiveStats(); - for (const auto &stat : activeStats) { - const int kind = stat.typeId / MetricsRegistry::KindStride; - const int number = stat.typeId % MetricsRegistry::KindStride; - - QString label; - if (kind >= 0 && kind < MetricsRegistry::NumKinds) { - const google::protobuf::DescriptorPool *pool = google::protobuf::DescriptorPool::generated_pool(); - const google::protobuf::Descriptor *message = pool->FindMessageTypeByName(messageNames[kind]); - const google::protobuf::FieldDescriptor *extension = - message ? pool->FindExtensionByNumber(message, number) : nullptr; - if (extension) { - label = QString::fromLatin1(MetricsRegistry::KindNames[kind]) + QStringLiteral("/") + - QString::fromStdString(std::string(extension->message_type()->name())); - } - } - if (label.isEmpty()) { - label = QString::number(stat.typeId); - } - - CommandStats *cs = re->add_command_stats(); - cs->set_kind_index(static_cast(kind)); - cs->set_extension_number(static_cast(number)); - cs->set_command_name(label.toStdString()); - cs->set_count(static_cast(stat.count)); - cs->set_total_ms(static_cast(stat.totalMs)); - } - - rc.setResponseExtension(re); - return Response::RespOk; -} - Response::ResponseCode AbstractServerSocketInterface::cmdReportStats(const Command_ReportStats & /*cmd */, ResponseContainer &rc) { @@ -3399,7 +3215,7 @@ bool AbstractServerSocketInterface::removeAdminFlagFromUser(const QString &userN if (user) { Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::DEMOTED); - event.set_reason_str("Your moderator, judge, and/or developer status has been revoked."); + event.set_reason_str("Your moderator and/or judge status has been revoked."); event.set_end_time(QDateTime::currentDateTime().toSecsSinceEpoch()); SessionEvent *se = user->prepareSessionEvent(event); @@ -3441,18 +3257,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAdjustMod(const Command } } - if (cmd.has_should_be_developer()) { - if (cmd.should_be_developer()) { - if (!addAdminFlagToUser(userName, 8)) { - return Response::RespInternalError; - } - } else { - if (!removeAdminFlagFromUser(userName, 8)) { - return Response::RespInternalError; - } - } - } - return Response::RespOk; } diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index b464e6a9b..600796b5f 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -81,7 +80,6 @@ signals: protected: void logDebugMessage(const QString &message) override; bool tooManyRegistrationAttempts(const QString &ipAddress); - void processCommandContainer(const CommandContainer &cont) override; virtual void writeToSocket(QByteArray &data) = 0; virtual void flushSocket() = 0; @@ -117,8 +115,7 @@ private: Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdReportList(const Command_ReportList &cmd, ResponseContainer &rc); Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); - Response::ResponseCode - cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc, bool allowPrivateChat); + Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); @@ -154,8 +151,6 @@ private: processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) override; Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) override; - Response::ResponseCode - processExtendedDeveloperCommand(int cmdType, const DeveloperCommand &cmd, ResponseContainer &rc) override; Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); @@ -177,8 +172,6 @@ private: Response::ResponseCode cmdResetUserPassword(const Command_ResetUserPassword &cmd, ResponseContainer &rc); Response::ResponseCode cmdRemoveUserAvatar(const Command_RemoveUserAvatar &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetServerStats(const Command_GetServerStats &cmd, ResponseContainer &rc); - bool addAdminFlagToUser(const QString &user, int flag); bool removeAdminFlagFromUser(const QString &user, int flag); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4f5dc88eb..18ab60d06 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,11 +11,9 @@ add_test(NAME playmat_resolver_test COMMAND playmat_resolver_test) add_test(NAME server_card_counter_test COMMAND server_card_counter_test) add_test(NAME server_counter_test COMMAND server_counter_test) add_test(NAME server_rate_limiter_test COMMAND server_rate_limiter_test) -add_test(NAME server_developer_role_test COMMAND server_developer_role_test) add_test(NAME warning_categories_test COMMAND warning_categories_test) add_test(NAME lag_monitor_test COMMAND lag_monitor_test) add_test(NAME latency_tracker_test COMMAND latency_tracker_test) -add_test(NAME metrics_registry_test COMMAND metrics_registry_test) add_test(NAME deck_hash_performance_test COMMAND deck_hash_performance_test) set_tests_properties(deck_hash_performance_test PROPERTIES TIMEOUT 15) @@ -32,12 +30,10 @@ add_executable(deck_hash_performance_test deck_hash_performance_test.cpp) add_executable(server_card_counter_test server_card_counter_test.cpp) add_executable(server_counter_test server_counter_test.cpp) add_executable(server_rate_limiter_test server_rate_limiter_test.cpp) -add_executable(server_developer_role_test server_developer_role_test.cpp) add_executable(warning_categories_test warning_categories_test.cpp) add_executable(lag_monitor_test ${CMAKE_SOURCE_DIR}/cockatrice/src/client/lag_monitor.cpp lag_monitor_test.cpp) target_include_directories(lag_monitor_test PRIVATE ${CMAKE_SOURCE_DIR}/cockatrice/src) add_executable(latency_tracker_test latency_tracker_test.cpp) -add_executable(metrics_registry_test ../servatrice/src/metrics_registry.cpp metrics_registry_test.cpp) find_package(GTest) @@ -74,11 +70,9 @@ if(NOT GTEST_FOUND) add_dependencies(server_card_counter_test gtest) add_dependencies(server_counter_test gtest) add_dependencies(server_rate_limiter_test gtest) - add_dependencies(server_developer_role_test gtest) add_dependencies(warning_categories_test gtest) add_dependencies(lag_monitor_test gtest) add_dependencies(latency_tracker_test gtest) - add_dependencies(metrics_registry_test gtest) endif() include_directories(${GTEST_INCLUDE_DIRS}) @@ -110,10 +104,6 @@ target_link_libraries( target_link_libraries( server_rate_limiter_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) -target_link_libraries( - server_developer_role_test libcockatrice_network libcockatrice_rng Threads::Threads ${GTEST_BOTH_LIBRARIES} - ${TEST_QT_MODULES} -) target_link_libraries( warning_categories_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) @@ -121,8 +111,6 @@ target_link_libraries(lag_monitor_test Threads::Threads ${GTEST_BOTH_LIBRARIES} target_link_libraries( latency_tracker_test libcockatrice_network Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) -target_include_directories(metrics_registry_test PRIVATE ${CMAKE_SOURCE_DIR}/servatrice/src) -target_link_libraries(metrics_registry_test ${TEST_QT_MODULES} Threads::Threads ${GTEST_BOTH_LIBRARIES}) add_subdirectory(card_zone_algorithms) add_subdirectory(carddatabase) diff --git a/tests/metrics_registry_test.cpp b/tests/metrics_registry_test.cpp deleted file mode 100644 index 1abef58e1..000000000 --- a/tests/metrics_registry_test.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include - -TEST(MetricsRegistryTest, EmptyRegistryHasZeroedCounters) -{ - MetricsRegistry registry; - - EXPECT_EQ(0, registry.totalCommands()); - EXPECT_EQ(0, registry.totalTimeMs()); - EXPECT_EQ(0, registry.activeTypeCount()); - EXPECT_EQ(0, registry.getGameStartSnapshot().count); -} - -TEST(MetricsRegistryTest, SampleIsRecordedInTotalsAndSlot) -{ - MetricsRegistry registry; - registry.observeCommand(MetricsRegistry::typeIdFor(0, 1000), 7); - - EXPECT_EQ(1, registry.totalCommands()); - EXPECT_EQ(7, registry.totalTimeMs()); - EXPECT_EQ(1, registry.activeTypeCount()); - - const auto stats = registry.collectActiveStats(); - ASSERT_EQ(1, stats.size()); - EXPECT_EQ(MetricsRegistry::typeIdFor(0, 1000), stats[0].typeId); - EXPECT_EQ(1, stats[0].count); - EXPECT_EQ(7, stats[0].totalMs); -} - -TEST(MetricsRegistryTest, KindEncodingSeparatesSameExtensionNumber) -{ - MetricsRegistry registry; - const int sessionPing = MetricsRegistry::typeIdFor(0, 1000); - const int roomLeaveRoom = MetricsRegistry::typeIdFor(1, 1000); - ASSERT_NE(sessionPing, roomLeaveRoom); - - registry.observeCommand(sessionPing, 1); - registry.observeCommand(roomLeaveRoom, 5000); - - EXPECT_EQ(2, registry.activeTypeCount()); -} - -TEST(MetricsRegistryTest, OutOfRangeIdsLandInOverflowSlot) -{ - MetricsRegistry registry; - registry.observeCommand(-1, 4); - registry.observeCommand(MetricsRegistry::MaxTypes + 12345, 4); - - EXPECT_EQ(2, registry.totalCommands()); - EXPECT_EQ(1, registry.activeTypeCount()); // both collapsed into one slot - EXPECT_EQ(8, registry.totalTimeMs()); -} - -TEST(MetricsRegistryTest, NegativeDurationsAreClamped) -{ - MetricsRegistry registry; - registry.observeCommand(MetricsRegistry::typeIdFor(0, 1000), -50); - - EXPECT_EQ(0, registry.totalTimeMs()); -} - -TEST(MetricsRegistryTest, GameStartTrackedSeparatelyFromCommands) -{ - MetricsRegistry registry; - registry.observeGameStartDurationMs(120); - - EXPECT_EQ(0, registry.totalCommands()); - EXPECT_EQ(0, registry.totalTimeMs()); - EXPECT_EQ(0, registry.activeTypeCount()); - - const auto snapshot = registry.getGameStartSnapshot(); - EXPECT_EQ(1, snapshot.count); - EXPECT_EQ(120, snapshot.totalMs); -} - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/server_developer_role_test.cpp b/tests/server_developer_role_test.cpp deleted file mode 100644 index 127f606a8..000000000 --- a/tests/server_developer_role_test.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/** @file server_developer_role_test.cpp - * @brief Tests for the developer staff role authorization and dispatch. - * @ingroup Tests - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -// The server_remote library references the global RNG, which is normally -// defined by the servatrice/client executable main(). Provide a stub so the -// unit test can link against it. -RNG_Abstract *rng = nullptr; - -namespace -{ - -class TestDeveloperHandler : public Server_ProtocolHandler -{ -public: - explicit TestDeveloperHandler(Server *_server) : Server_ProtocolHandler(_server, nullptr) - { - } - - QString getAddress() const override - { - return {}; - } - QString getConnectionType() const override - { - return {}; - } - - // Buffer the last response code sent to the client so tests can assert on - // the outcome of processCommandContainer(). - Response::ResponseCode lastResponseCode = Response::RespNothing; - int dispatchCount = 0; - -protected: - void transmitProtocolItem(const ServerMessage &item) override - { - if (item.message_type() == ServerMessage::RESPONSE) { - lastResponseCode = item.response().response_code(); - } - } - - Response::ResponseCode - processExtendedDeveloperCommand(int cmdType, const DeveloperCommand &, ResponseContainer &) override - { - ++dispatchCount; - // Fail closed for anything not explicitly handled. - if (cmdType != DeveloperCommand::GET_SERVER_STATS) { - return Response::RespFunctionNotAllowed; - } - return Response::RespOk; - } -}; - -class DeveloperRoleTest : public ::testing::Test -{ -protected: - Server server; - TestDeveloperHandler handler{&server}; - - void setUserLevel(uint32_t level) - { - ServerInfo_User user; - user.set_user_level(level); - handler.setUserInfo(user); - } -}; - -TEST_F(DeveloperRoleTest, RejectsWhenNotLoggedIn) -{ - CommandContainer cont; - cont.add_developer_command(); - handler.processCommandContainer(cont); - EXPECT_EQ(handler.lastResponseCode, Response::RespLoginNeeded); - EXPECT_EQ(handler.dispatchCount, 0); -} - -TEST_F(DeveloperRoleTest, RejectsPlainUser) -{ - setUserLevel(ServerInfo_User::IsUser | ServerInfo_User::IsRegistered); - - CommandContainer cont; - cont.add_developer_command(); - handler.processCommandContainer(cont); - EXPECT_EQ(handler.lastResponseCode, Response::RespLoginNeeded); - EXPECT_EQ(handler.dispatchCount, 0); -} - -TEST_F(DeveloperRoleTest, RejectsModeratorThatIsNotDeveloper) -{ - setUserLevel(ServerInfo_User::IsModerator); - - CommandContainer cont; - cont.add_developer_command(); - handler.processCommandContainer(cont); - EXPECT_EQ(handler.lastResponseCode, Response::RespLoginNeeded); -} - -TEST_F(DeveloperRoleTest, DispatchesToDeveloperCommandForDeveloper) -{ - setUserLevel(ServerInfo_User::IsDeveloper); - - CommandContainer cont; - DeveloperCommand *cmd = cont.add_developer_command(); - cmd->MutableExtension(Command_GetServerStats::ext); - handler.processCommandContainer(cont); - EXPECT_EQ(handler.lastResponseCode, Response::RespOk); - EXPECT_EQ(handler.dispatchCount, 1); -} - -TEST_F(DeveloperRoleTest, FailClosedForUnknownDeveloperCommand) -{ - setUserLevel(ServerInfo_User::IsDeveloper); - - CommandContainer cont; - cont.add_developer_command(); // no extension set -> getPbExtension() returns -1 - handler.processCommandContainer(cont); - EXPECT_EQ(handler.lastResponseCode, Response::RespFunctionNotAllowed); - EXPECT_EQ(handler.dispatchCount, 1); -} - -} // namespace - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}