From 6dc974a05d7bdff5c088044ee28b5fc3427a8c7c Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sat, 27 Jun 2026 11:23:55 -0400 Subject: [PATCH] [UserListDelegate] Consider providerId (#7018) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lukas BrĂ¼bach --- .../server/user/user_card_art_provider.cpp | 13 +-- .../server/user/user_card_art_provider.h | 2 +- .../server/user/user_card_settings_dialog.cpp | 91 +++++++++++++++++-- .../server/user/user_card_settings_dialog.h | 7 ++ .../widgets/server/user/user_info_popup.cpp | 2 +- .../widgets/server/user/user_list_painter.cpp | 6 +- .../widgets/server/user/user_list_painter.h | 1 + .../widgets/server/user/user_list_widget.cpp | 3 +- .../widgets/tabs/tab_card_art_rules.cpp | 59 ++++++++++-- .../widgets/tabs/tab_card_art_rules.h | 4 + .../protocol/pb/moderator_commands.proto | 6 +- .../pb/response_card_art_rule_entry.proto | 5 +- .../protocol/pb/serverinfo_user.proto | 9 +- .../protocol/pb/session_commands.proto | 9 +- .../migrations/servatrice_0034_to_0035.sql | 3 +- servatrice/servatrice.sql | 3 +- .../src/servatrice_database_interface.cpp | 3 + servatrice/src/serversocketinterface.cpp | 36 +++++--- servatrice/src/serversocketinterface.h | 2 +- 19 files changed, 211 insertions(+), 53 deletions(-) diff --git a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp b/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp index 70a56375e..67fb4f684 100644 --- a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp @@ -5,9 +5,9 @@ #include #include -static QString makeKey(const QString &user, const QString &card) +static QString makeKey(const QString &user, const QString &card, const QString &providerId) { - return user + u'|' + card; + return user + u'|' + card + u'|' + providerId; } UserCardArtProvider::UserCardArtProvider(QObject *parent) : QObject(parent) @@ -31,13 +31,13 @@ const QMap &UserCardArtProvider::cache() const return cardArtCache; } -void UserCardArtProvider::requestCardArt(const QString &userName, const QString &cardName) +void UserCardArtProvider::requestCardArt(const QString &userName, const QString &cardName, const QString &providerId) { if (cardName.isEmpty()) { return; } - const QString key = makeKey(userName, cardName); + const QString key = makeKey(userName, cardName, providerId); if (cardArtCache.contains(key) || pending.contains(key)) { return; @@ -83,15 +83,16 @@ void UserCardArtProvider::processQueue() const QString key = queue.dequeue(); const QStringList parts = key.split(u'|'); - if (parts.size() != 2) { + if (parts.size() != 3) { pending.remove(key); continue; } const QString userName = parts.at(0); const QString cardName = parts.at(1); + const QString providerId = parts.at(2); - ExactCard card = CardDatabaseManager::query()->getCard({cardName}); + ExactCard card = CardDatabaseManager::query()->getCard({cardName, providerId}); if (!card) { pending.remove(key); diff --git a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h b/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h index a3ab874b7..fb2f37812 100644 --- a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h +++ b/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h @@ -14,7 +14,7 @@ class UserCardArtProvider : public QObject public: explicit UserCardArtProvider(QObject *parent = nullptr); - void requestCardArt(const QString &userName, const QString &cardName); + void requestCardArt(const QString &userName, const QString &cardName, const QString &providerId); const QMap &cache() const; static QPixmap cropCardArt(const QPixmap &fullRes); diff --git a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp b/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp index 335ee097e..56c9600a0 100644 --- a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp @@ -70,7 +70,7 @@ void CardArtPreviewWidget::paintEvent(QPaintEvent *) QString(), // userName not needed for override path nullptr, // no cache params, - &sourcePixmap // đŸ‘ˆ direct pixmap + &sourcePixmap // direct pixmap ); // Avatar placeholder so the left-margin interaction is visible @@ -174,6 +174,13 @@ void UserCardArtSettingsDialog::setupUi() { initializeSearchBar(); + providerComboBox = new QComboBox; + connect(providerComboBox, &QComboBox::currentIndexChanged, this, [this]() { + currentParams.cardProviderId = providerComboBox->currentData().toString(); + reloadPreview(); + onParamChanged(); + }); + marginLSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctL, 0.01); marginRSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctR, 0.01); verticalOffsetSpin = makeSpinBox(0.0, 1.0, currentParams.verticalOffset, 0.01); @@ -181,6 +188,7 @@ void UserCardArtSettingsDialog::setupUi() auto *form = new QFormLayout; form->addRow(tr("Card name:"), searchBar); + form->addRow(tr("Card ProviderId:"), providerComboBox); form->addRow(tr("Left margin (%):"), marginLSpin); form->addRow(tr("Right margin (%):"), marginRSpin); form->addRow(tr("Vertical offset:"), verticalOffsetSpin); @@ -219,6 +227,32 @@ void UserCardArtSettingsDialog::setupUi() connect(zoomSpin, &QDoubleSpinBox::valueChanged, this, &UserCardArtSettingsDialog::onParamChanged); } +void UserCardArtSettingsDialog::populateProviderCombo(const QString &cardName) +{ + providerComboBox->clear(); + + auto card = CardDatabaseManager::query()->getCard({cardName}); + + const auto &sets = card.getInfo().getSets(); + + for (const auto &printings : sets) { + for (const auto &p : printings) { + + QString setName = p.getSet()->getLongName(); + QString collector = p.getProperty("num"); + QString uuid = p.getUuid(); + + QString label = setName; + + if (!collector.isEmpty()) { + label += " #" + collector; + } + + providerComboBox->addItem(label, uuid); + } + } +} + void UserCardArtSettingsDialog::onCardNameChanged(const QString &name) { if (name.isEmpty()) { @@ -231,27 +265,68 @@ void UserCardArtSettingsDialog::onCardNameChanged(const QString &name) if (!card) { currentPixmap = QPixmap(); preview->setPixmap(currentPixmap); + providerComboBox->clear(); return; } currentParams.cardName = name; + populateProviderCombo(name); + + if (providerComboBox->count() == 0) { + // No printings found for this card; nothing to preview. + currentPixmap = QPixmap(); + preview->setPixmap(currentPixmap); + currentParams.cardProviderId.clear(); + return; + } + + currentParams.cardProviderId = providerComboBox->currentData().toString(); + reloadPreview(); +} + +void UserCardArtSettingsDialog::reloadPreview() +{ + if (currentParams.cardName.isEmpty()) { + return; + } + + ExactCard card = CardDatabaseManager::query()->getCard({currentParams.cardName, currentParams.cardProviderId}); + if (!card) { + return; + } + + // CardPictureLoader::getPixmap() is async on a cache miss: it enqueues a + // background download and returns a null pixmap immediately. When that + // download finishes, CardPictureLoader::imageLoaded() caches the result + // and calls card.emitPixmapUpdated(), which emits pixmapUpdated() on the + // underlying CardInfo (see exact_card.h). Listen for that, scoped to + // whichever CardInfo we just asked for, so the preview catches up once + // the image actually arrives instead of staying on the placeholder. + // + // Disconnect any previous listener first -- otherwise switching cards + // repeatedly stacks up connections to old CardInfo objects, each of + // which would still fire reloadPreview() (harmlessly, but wastefully) + // whenever ITS art finishes loading later. + disconnect(pixmapUpdatedConnection); + QPixmap fullRes; CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040)); if (fullRes.isNull()) { - connect(card.getCardPtr().data(), &CardInfo::pixmapUpdated, this, [this, card](const PrintingInfo &) { - disconnect(card.getCardPtr().data(), &CardInfo::pixmapUpdated, this, nullptr); - QPixmap loaded; - CardPictureLoader::getPixmap(loaded, card, QSize(745, 1040)); - currentPixmap = UserCardArtProvider::cropCardArt(loaded); - preview->setPixmap(currentPixmap); - }); + // Not loaded yet -- wait for the signal instead of giving up. + // card.getCardPtr() is a CardInfoPtr (QSharedPointer); + // .data() gives the raw QObject* needed for connect(). + CardInfo *cardInfo = card.getCardPtr().data(); + if (cardInfo) { + pixmapUpdatedConnection = connect(cardInfo, &CardInfo::pixmapUpdated, this, [this]() { reloadPreview(); }); + } return; } currentPixmap = UserCardArtProvider::cropCardArt(fullRes); preview->setPixmap(currentPixmap); + preview->setParams(currentParams); } void UserCardArtSettingsDialog::onParamChanged() diff --git a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h b/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h index cac26c919..018043278 100644 --- a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h +++ b/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h @@ -3,6 +3,7 @@ #include "user_list_painter.h" +#include #include #include @@ -43,10 +44,12 @@ public: private slots: void onCardNameChanged(const QString &name); + void reloadPreview(); void onParamChanged(); private: void setupUi(); + void populateProviderCombo(const QString &cardName); void initializeSearchBar(); QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step); @@ -57,6 +60,10 @@ private: CardSearchModel *searchModel; CardCompleterProxyModel *proxyModel; + QComboBox *providerComboBox; + + QMetaObject::Connection pixmapUpdatedConnection; + QDoubleSpinBox *marginLSpin; QDoubleSpinBox *marginRSpin; QDoubleSpinBox *verticalOffsetSpin; 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 fd62d5ddf..2b4dcb8ed 100644 --- a/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp @@ -542,7 +542,7 @@ void UserInfoPopup::showForUser(const QString &userName, const CardArtParams params = (m_cardArtParamsMap && m_cardArtParamsMap->contains(userName)) ? m_cardArtParamsMap->value(userName) : CardArtParams{}; - const QString artKey = userName + u'|' + params.cardName; + const QString artKey = userName + u'|' + params.cardName + u'|' + params.cardProviderId; const QPixmap cardArt = (m_cardArtCache && !params.cardName.isEmpty()) ? m_cardArtCache->value(artKey) : QPixmap{}; m_header->setUserData(userInfo, online, avatar, cardArt, params); 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 b5541b692..8891ff268 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp @@ -73,9 +73,9 @@ void UserListPainter::drawBackground(QPainter *painter, painter->drawRoundedRect(QRectF(cardRect.left(), cardRect.top(), 3, cardRect.height()), 2, 2); } -static QString makeKey(const QString &user, const QString &card) +static QString makeKey(const QString &user, const QString &card, const QString &providerId) { - return user + u'|' + card; + return user + u'|' + card + u'|' + providerId; } void UserListPainter::drawCardArt(QPainter *painter, @@ -95,7 +95,7 @@ void UserListPainter::drawCardArt(QPainter *painter, return; } - const QString key = makeKey(userName, params.cardName); + const QString key = makeKey(userName, params.cardName, params.cardProviderId); if (!cardArtCache->contains(key)) { return; diff --git a/cockatrice/src/interface/widgets/server/user/user_list_painter.h b/cockatrice/src/interface/widgets/server/user/user_list_painter.h index 95486b75e..28cab9675 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_painter.h +++ b/cockatrice/src/interface/widgets/server/user/user_list_painter.h @@ -18,6 +18,7 @@ class ServerInfo_User; struct CardArtParams { QString cardName = ""; + QString cardProviderId = ""; double marginPctL = 0.33; double marginPctR = 0.02; double verticalOffset = 0.35; 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 ed06ea941..a48d95cb5 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp @@ -904,12 +904,13 @@ void UserListWidget::processUserInfo(const ServerInfo_User &user, bool online) const auto &cap = user.card_art_params(); CardArtParams params; params.cardName = QString::fromStdString(cap.card_name()); + params.cardProviderId = QString::fromStdString(cap.card_provider_id()); params.marginPctL = cap.margin_pct_l(); params.marginPctR = cap.margin_pct_r(); params.verticalOffset = cap.vertical_offset(); params.zoom = cap.zoom(); cardArtParamsMap.insert(userName, params); - cardArtProvider->requestCardArt(userName, params.cardName); + cardArtProvider->requestCardArt(userName, params.cardName, params.cardProviderId); } else { cardArtParamsMap.remove(userName); // clear stale params on removal } diff --git a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp b/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp index 6e8ab752a..3dc4de15f 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp @@ -27,7 +27,7 @@ int CardArtRulesModel::rowCount(const QModelIndex &parent) const int CardArtRulesModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 3; + return 4; } QVariant CardArtRulesModel::data(const QModelIndex &index, int role) const @@ -43,8 +43,10 @@ QVariant CardArtRulesModel::data(const QModelIndex &index, int role) const case 0: return e.cardName; case 1: - return e.mode; + return e.cardProviderId; case 2: + return e.mode; + case 3: return e.reason; } } @@ -62,8 +64,10 @@ QVariant CardArtRulesModel::headerData(int section, Qt::Orientation orientation, case 0: return tr("Card"); case 1: - return tr("Mode"); + return tr("ProviderId"); case 2: + return tr("Mode"); + case 3: return tr("Reason"); default: return {}; @@ -97,6 +101,15 @@ QString CardArtRulesModel::cardAt(int row) const return entries[row].cardName; } +const CardArtRulesModel::Entry *CardArtRulesModel::entryAt(int row) const +{ + if (row < 0 || row >= static_cast(entries.size())) { + return nullptr; + } + + return &entries[row]; +} + void CardArtRulesModel::onRefreshFinished(const Response &r) { if (r.response_code() != Response::RespOk) { @@ -109,8 +122,8 @@ void CardArtRulesModel::onRefreshFinished(const Response &r) entries.clear(); for (const auto &e : resp.entries()) { - entries.push_back({QString::fromStdString(e.card_name()), QString::fromStdString(e.mode()), - QString::fromStdString(e.reason())}); + entries.push_back({QString::fromStdString(e.card_name()), QString::fromStdString(e.card_provider_id()), + QString::fromStdString(e.mode()), QString::fromStdString(e.reason())}); } endResetModel(); @@ -128,6 +141,7 @@ void TabCardArtRules::setupUi() initSearchBar(); + providerComboBox = new QComboBox; modeBox = new QComboBox; reasonEdit = new QLineEdit; @@ -146,6 +160,7 @@ void TabCardArtRules::setupUi() auto *form = new QFormLayout; form->addRow(tr("Card:"), searchEdit); + form->addRow(tr("ProviderId:"), providerComboBox); form->addRow(tr("Mode:"), modeBox); form->addRow(tr("Reason:"), reasonEdit); @@ -204,6 +219,34 @@ void TabCardArtRules::initSearchBar() }); connect(searchCompleter, static_cast(&QCompleter::activated), this, [this](const QString &name) { searchEdit->setText(name); }); + connect(searchEdit, &QLineEdit::editingFinished, this, + [this]() { populateProviderCombo(searchEdit->text().trimmed()); }); +} + +void TabCardArtRules::populateProviderCombo(const QString &cardName) +{ + providerComboBox->clear(); + + auto card = CardDatabaseManager::query()->getCard({cardName}); + + const auto &sets = card.getInfo().getSets(); + + for (const auto &printings : sets) { + for (const auto &p : printings) { + + QString setName = p.getSet()->getLongName(); + QString collector = p.getProperty("num"); + QString uuid = p.getUuid(); + + QString label = setName; + + if (!collector.isEmpty()) { + label += " #" + collector; + } + + providerComboBox->addItem(label, uuid); + } + } } void TabCardArtRules::retranslateUi() @@ -222,6 +265,7 @@ void TabCardArtRules::addRule() { Command_AddCardArtRule cmd; cmd.set_card_name(searchEdit->text().toStdString()); + cmd.set_card_provider_id(providerComboBox->currentData().toString().toStdString()); cmd.set_mode(modeBox->currentText().toStdString()); cmd.set_reason(reasonEdit->text().toStdString()); @@ -238,7 +282,10 @@ void TabCardArtRules::removeSelected() } Command_RemoveCardArtRule cmd; - cmd.set_card_name(tableModel->cardAt(idx.row()).toStdString()); + const auto e = tableModel->entryAt(idx.row()); + + cmd.set_card_name(e->cardName.toStdString()); + cmd.set_card_provider_id(e->cardProviderId.toStdString()); client->sendCommand(client->prepareModeratorCommand(cmd)); diff --git a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h b/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h index a47f1267d..b9ea2ca83 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h +++ b/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h @@ -20,6 +20,7 @@ public: struct Entry { QString cardName; + QString cardProviderId; QString mode; QString reason; }; @@ -35,6 +36,7 @@ public: void clear(); QString cardAt(int row) const; + const Entry *entryAt(int row) const; private slots: void onRefreshFinished(const Response &r); @@ -70,11 +72,13 @@ private: QLineEdit *searchEdit; void initSearchBar(); + void populateProviderCombo(const QString &cardName); QCompleter *searchCompleter; CardDatabaseModel *cardDbModel; CardDatabaseDisplayModel *cardDbDisplayModel; CardSearchModel *cardSearchModel; CardCompleterProxyModel *cardProxyModel; + QComboBox *providerComboBox; QComboBox *modeBox; QLineEdit *reasonEdit; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto index c10b9de22..ca46e4dd7 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto @@ -116,8 +116,9 @@ message Command_AddCardArtRule { } optional string card_name = 1; - optional string mode = 2; // "ALLOW" or "DENY" - optional string reason = 3; + optional string card_provider_id = 2; + optional string mode = 3; // "ALLOW" or "DENY" + optional string reason = 4; } message Command_RemoveCardArtRule { @@ -126,6 +127,7 @@ message Command_RemoveCardArtRule { } optional string card_name = 1; + optional string card_provider_id = 2; } message Command_ListCardArtRules { diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto index b13c79742..25b76e09f 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto @@ -3,8 +3,9 @@ import "response.proto"; message Response_CardArtRuleEntry { optional string card_name = 1; - optional string mode = 2; - optional string reason = 3; + optional string card_provider_id = 2; + optional string mode = 3; + optional string reason = 4; } message Response_ListCardArtRules { diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto index 4bbbe46bb..98cc3ce6a 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto @@ -15,10 +15,11 @@ message ServerInfo_User { }; message CardArtParams { optional string card_name = 1; - optional double margin_pct_l = 2 [default = 0.33]; - optional double margin_pct_r = 3 [default = 0.02]; - optional double vertical_offset = 4 [default = 0.35]; - optional double zoom = 5 [default = 1.0]; + optional string card_provider_id = 2; + optional double margin_pct_l = 3 [default = 0.33]; + optional double margin_pct_r = 4 [default = 0.02]; + optional double vertical_offset = 5 [default = 0.35]; + optional double zoom = 6 [default = 1.0]; }; optional string name = 1; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto index ff5dbff32..9d207c711 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto @@ -212,8 +212,9 @@ message Command_SetCardArtParams { optional Command_SetCardArtParams ext = 1025; } optional string card_name = 1; - optional double margin_pct_l = 2; - optional double margin_pct_r = 3; - optional double vertical_offset = 4; - optional double zoom = 5; + optional string card_provider_id = 2; + optional double margin_pct_l = 3; + optional double margin_pct_r = 4; + optional double vertical_offset = 5; + optional double zoom = 6; } diff --git a/servatrice/migrations/servatrice_0034_to_0035.sql b/servatrice/migrations/servatrice_0034_to_0035.sql index 83502a949..acaad9c8b 100644 --- a/servatrice/migrations/servatrice_0034_to_0035.sql +++ b/servatrice/migrations/servatrice_0034_to_0035.sql @@ -3,12 +3,13 @@ ALTER TABLE `cockatrice_users` ADD COLUMN `card_art_params` TEXT DEFAULT NULL, A CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `card_name` varchar(255) NOT NULL, + `card_provider_id` varchar(255) NOT NULL, `mode` enum('ALLOW','DENY') NOT NULL, `reason` varchar(255) DEFAULT NULL, `created_by` int(7) unsigned DEFAULT NULL, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), - UNIQUE KEY `uniq_card_name` (`card_name`), + UNIQUE KEY `uniq_provider_card_name` (`card_provider_id`, `card_name`), KEY `idx_mode` (`mode`), FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index badd82d6d..7f530063c 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -305,12 +305,13 @@ CREATE TABLE IF NOT EXISTS `cockatrice_audit` ( CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `card_name` varchar(255) NOT NULL, + `card_provider_id` varchar(255) NOT NULL, `mode` enum('ALLOW','DENY') NOT NULL, `reason` varchar(255) DEFAULT NULL, `created_by` int(7) unsigned DEFAULT NULL, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), - UNIQUE KEY `uniq_card_name` (`card_name`), + UNIQUE KEY `uniq_provider_card_name` (`card_provider_id`, `card_name`), KEY `idx_mode` (`mode`), FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`) ON DELETE SET NULL diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index 92ff80755..d5e1f13ef 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -693,6 +693,9 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer if (obj.contains("card_name")) { cap->set_card_name(obj["card_name"].toString().toStdString()); } + if (obj.contains("card_provider_id")) { + cap->set_card_provider_id(obj["card_provider_id"].toString().toStdString()); + } if (obj.contains("marginPctL")) { cap->set_margin_pct_l(obj["marginPctL"].toDouble(0.33)); } diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index f9d276941..55f779468 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -1577,11 +1577,13 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Comm return Response::RespOk; } -bool AbstractServerSocketInterface::isCardNameAllowed(const QString &cardName) +bool AbstractServerSocketInterface::isCardNameAllowed(const QString &cardName, const QString &cardProviderId) { - QSqlQuery *q = sqlInterface->prepareQuery("SELECT mode FROM {prefix}_card_art_name_rules WHERE card_name = :name"); + QSqlQuery *q = sqlInterface->prepareQuery( + "SELECT mode FROM {prefix}_card_art_name_rules WHERE card_name = :name AND card_provider_id = :provider"); q->bindValue(":name", cardName); + q->bindValue(":provider", cardProviderId); if (!sqlInterface->execSqlQuery(q)) { qWarning() << "Card art rule lookup failed; failing open for" << cardName; @@ -1603,8 +1605,9 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const } const QString cardName = QString::fromStdString(cmd.card_name()); + const QString cardProviderId = QString::fromStdString(cmd.card_provider_id()); - if (cardName.length() > MAX_NAME_LENGTH) { + if (cardName.length() > MAX_NAME_LENGTH || cardProviderId.length() > MAX_NAME_LENGTH) { return Response::RespInvalidData; } @@ -1620,7 +1623,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const return Response::RespOk; } - if (!isCardNameAllowed(cardName)) { + if (!isCardNameAllowed(cardName, cardProviderId)) { return Response::RespFunctionNotAllowed; } @@ -1633,6 +1636,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const QJsonObject obj; obj["card_name"] = cardName; + obj["card_provider_id"] = cardProviderId; obj["marginPctL"] = marginPctL; obj["marginPctR"] = marginPctR; obj["verticalOffset"] = verticalOffset; @@ -1649,6 +1653,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const // Keep the in-memory userInfo in sync auto *cap = userInfo->mutable_card_art_params(); cap->set_card_name(cmd.card_name()); + cap->set_card_provider_id(cmd.card_provider_id()); cap->set_margin_pct_l(marginPctL); cap->set_margin_pct_r(marginPctR); cap->set_vertical_offset(verticalOffset); @@ -1664,21 +1669,23 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAddCardArtRule(const Co ResponseContainer &) { const QString cardName = QString::fromStdString(cmd.card_name()); + const QString cardProviderId = QString::fromStdString(cmd.card_provider_id()); const QString mode = QString::fromStdString(cmd.mode()); if (mode != "ALLOW" && mode != "DENY") { return Response::RespInvalidData; } - if (cardName.isEmpty() || cardName.length() > MAX_NAME_LENGTH) { + if (cardName.isEmpty() || cardName.length() > MAX_NAME_LENGTH || cardProviderId.length() > MAX_NAME_LENGTH) { return Response::RespInvalidData; } QSqlQuery *q = sqlInterface->prepareQuery("INSERT INTO {prefix}_card_art_name_rules " - "(card_name, mode, reason, created_by) " - "VALUES (:name, :mode, :reason, :uid) " + "(card_name, card_provider_id, mode, reason, created_by) " + "VALUES (:name, :provider, :mode, :reason, :uid) " "ON DUPLICATE KEY UPDATE mode=:mode2, reason=:reason2"); q->bindValue(":name", cardName); + q->bindValue(":provider", cardProviderId); q->bindValue(":mode", mode); q->bindValue(":mode2", mode); q->bindValue(":reason", QString::fromStdString(cmd.reason())); @@ -1696,12 +1703,15 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRemoveCardArtRule(const ResponseContainer &) { auto cardName = QString::fromStdString(cmd.card_name()); - if (cardName.length() > MAX_NAME_LENGTH) { + auto cardProviderId = QString::fromStdString(cmd.card_provider_id()); + if (cardName.length() > MAX_NAME_LENGTH || cardProviderId.length() > MAX_NAME_LENGTH) { return Response::RespInvalidData; } - QSqlQuery *q = sqlInterface->prepareQuery("DELETE FROM {prefix}_card_art_name_rules WHERE card_name=:name"); + QSqlQuery *q = sqlInterface->prepareQuery( + "DELETE FROM {prefix}_card_art_name_rules WHERE card_name=:name AND card_provider_id=:provider"); q->bindValue(":name", cardName); + q->bindValue(":provider", cardProviderId); if (!sqlInterface->execSqlQuery(q)) { return Response::RespInternalError; @@ -1713,7 +1723,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRemoveCardArtRule(const Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const Command_ListCardArtRules &, ResponseContainer &rc) { - QSqlQuery *q = sqlInterface->prepareQuery("SELECT card_name, mode, reason FROM {prefix}_card_art_name_rules"); + QSqlQuery *q = sqlInterface->prepareQuery( + "SELECT card_name, card_provider_id, mode, reason FROM {prefix}_card_art_name_rules"); if (!sqlInterface->execSqlQuery(q)) { return Response::RespInternalError; @@ -1724,8 +1735,9 @@ Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const while (q->next()) { auto *entry = re->add_entries(); entry->set_card_name(q->value(0).toString().toStdString()); - entry->set_mode(q->value(1).toString().toStdString()); - entry->set_reason(q->value(2).toString().toStdString()); + entry->set_card_provider_id(q->value(1).toString().toStdString()); + entry->set_mode(q->value(2).toString().toStdString()); + entry->set_reason(q->value(3).toString().toStdString()); } rc.setResponseExtension(re); diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index c0732ccd9..0d66ae78f 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -129,7 +129,7 @@ private: Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); - bool isCardNameAllowed(const QString &cardName); + bool isCardNameAllowed(const QString &cardName, const QString &cardProviderId); Response::ResponseCode cmdSetCardArtParams(const Command_SetCardArtParams &cmd, ResponseContainer &); Response::ResponseCode cmdAddCardArtRule(const Command_AddCardArtRule &cmd, ResponseContainer &); Response::ResponseCode cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, ResponseContainer &);