mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-27 17:13:54 -07:00
[UserListDelegate] Consider providerId (#7018)
Co-authored-by: Lukas Brübach <lukas.bruebach@bdosecurity.de>
This commit is contained in:
parent
2914874720
commit
6dc974a05d
19 changed files with 211 additions and 53 deletions
|
|
@ -5,9 +5,9 @@
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
|
|
||||||
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)
|
UserCardArtProvider::UserCardArtProvider(QObject *parent) : QObject(parent)
|
||||||
|
|
@ -31,13 +31,13 @@ const QMap<QString, QPixmap> &UserCardArtProvider::cache() const
|
||||||
return cardArtCache;
|
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()) {
|
if (cardName.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString key = makeKey(userName, cardName);
|
const QString key = makeKey(userName, cardName, providerId);
|
||||||
|
|
||||||
if (cardArtCache.contains(key) || pending.contains(key)) {
|
if (cardArtCache.contains(key) || pending.contains(key)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -83,15 +83,16 @@ void UserCardArtProvider::processQueue()
|
||||||
const QString key = queue.dequeue();
|
const QString key = queue.dequeue();
|
||||||
|
|
||||||
const QStringList parts = key.split(u'|');
|
const QStringList parts = key.split(u'|');
|
||||||
if (parts.size() != 2) {
|
if (parts.size() != 3) {
|
||||||
pending.remove(key);
|
pending.remove(key);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString userName = parts.at(0);
|
const QString userName = parts.at(0);
|
||||||
const QString cardName = parts.at(1);
|
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) {
|
if (!card) {
|
||||||
pending.remove(key);
|
pending.remove(key);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class UserCardArtProvider : public QObject
|
||||||
public:
|
public:
|
||||||
explicit UserCardArtProvider(QObject *parent = nullptr);
|
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<QString, QPixmap> &cache() const;
|
const QMap<QString, QPixmap> &cache() const;
|
||||||
static QPixmap cropCardArt(const QPixmap &fullRes);
|
static QPixmap cropCardArt(const QPixmap &fullRes);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ void CardArtPreviewWidget::paintEvent(QPaintEvent *)
|
||||||
QString(), // userName not needed for override path
|
QString(), // userName not needed for override path
|
||||||
nullptr, // no cache
|
nullptr, // no cache
|
||||||
params,
|
params,
|
||||||
&sourcePixmap // 👈 direct pixmap
|
&sourcePixmap // direct pixmap
|
||||||
);
|
);
|
||||||
|
|
||||||
// Avatar placeholder so the left-margin interaction is visible
|
// Avatar placeholder so the left-margin interaction is visible
|
||||||
|
|
@ -174,6 +174,13 @@ void UserCardArtSettingsDialog::setupUi()
|
||||||
{
|
{
|
||||||
initializeSearchBar();
|
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);
|
marginLSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctL, 0.01);
|
||||||
marginRSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctR, 0.01);
|
marginRSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctR, 0.01);
|
||||||
verticalOffsetSpin = makeSpinBox(0.0, 1.0, currentParams.verticalOffset, 0.01);
|
verticalOffsetSpin = makeSpinBox(0.0, 1.0, currentParams.verticalOffset, 0.01);
|
||||||
|
|
@ -181,6 +188,7 @@ void UserCardArtSettingsDialog::setupUi()
|
||||||
|
|
||||||
auto *form = new QFormLayout;
|
auto *form = new QFormLayout;
|
||||||
form->addRow(tr("Card name:"), searchBar);
|
form->addRow(tr("Card name:"), searchBar);
|
||||||
|
form->addRow(tr("Card ProviderId:"), providerComboBox);
|
||||||
form->addRow(tr("Left margin (%):"), marginLSpin);
|
form->addRow(tr("Left margin (%):"), marginLSpin);
|
||||||
form->addRow(tr("Right margin (%):"), marginRSpin);
|
form->addRow(tr("Right margin (%):"), marginRSpin);
|
||||||
form->addRow(tr("Vertical offset:"), verticalOffsetSpin);
|
form->addRow(tr("Vertical offset:"), verticalOffsetSpin);
|
||||||
|
|
@ -219,6 +227,32 @@ void UserCardArtSettingsDialog::setupUi()
|
||||||
connect(zoomSpin, &QDoubleSpinBox::valueChanged, this, &UserCardArtSettingsDialog::onParamChanged);
|
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)
|
void UserCardArtSettingsDialog::onCardNameChanged(const QString &name)
|
||||||
{
|
{
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
|
|
@ -231,27 +265,68 @@ void UserCardArtSettingsDialog::onCardNameChanged(const QString &name)
|
||||||
if (!card) {
|
if (!card) {
|
||||||
currentPixmap = QPixmap();
|
currentPixmap = QPixmap();
|
||||||
preview->setPixmap(currentPixmap);
|
preview->setPixmap(currentPixmap);
|
||||||
|
providerComboBox->clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentParams.cardName = name;
|
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;
|
QPixmap fullRes;
|
||||||
CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040));
|
CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040));
|
||||||
|
|
||||||
if (fullRes.isNull()) {
|
if (fullRes.isNull()) {
|
||||||
connect(card.getCardPtr().data(), &CardInfo::pixmapUpdated, this, [this, card](const PrintingInfo &) {
|
// Not loaded yet -- wait for the signal instead of giving up.
|
||||||
disconnect(card.getCardPtr().data(), &CardInfo::pixmapUpdated, this, nullptr);
|
// card.getCardPtr() is a CardInfoPtr (QSharedPointer<CardInfo>);
|
||||||
QPixmap loaded;
|
// .data() gives the raw QObject* needed for connect().
|
||||||
CardPictureLoader::getPixmap(loaded, card, QSize(745, 1040));
|
CardInfo *cardInfo = card.getCardPtr().data();
|
||||||
currentPixmap = UserCardArtProvider::cropCardArt(loaded);
|
if (cardInfo) {
|
||||||
preview->setPixmap(currentPixmap);
|
pixmapUpdatedConnection = connect(cardInfo, &CardInfo::pixmapUpdated, this, [this]() { reloadPreview(); });
|
||||||
});
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPixmap = UserCardArtProvider::cropCardArt(fullRes);
|
currentPixmap = UserCardArtProvider::cropCardArt(fullRes);
|
||||||
preview->setPixmap(currentPixmap);
|
preview->setPixmap(currentPixmap);
|
||||||
|
preview->setParams(currentParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserCardArtSettingsDialog::onParamChanged()
|
void UserCardArtSettingsDialog::onParamChanged()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "user_list_painter.h"
|
#include "user_list_painter.h"
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
|
||||||
|
|
@ -43,10 +44,12 @@ public:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onCardNameChanged(const QString &name);
|
void onCardNameChanged(const QString &name);
|
||||||
|
void reloadPreview();
|
||||||
void onParamChanged();
|
void onParamChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUi();
|
void setupUi();
|
||||||
|
void populateProviderCombo(const QString &cardName);
|
||||||
void initializeSearchBar();
|
void initializeSearchBar();
|
||||||
QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step);
|
QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step);
|
||||||
|
|
||||||
|
|
@ -57,6 +60,10 @@ private:
|
||||||
CardSearchModel *searchModel;
|
CardSearchModel *searchModel;
|
||||||
CardCompleterProxyModel *proxyModel;
|
CardCompleterProxyModel *proxyModel;
|
||||||
|
|
||||||
|
QComboBox *providerComboBox;
|
||||||
|
|
||||||
|
QMetaObject::Connection pixmapUpdatedConnection;
|
||||||
|
|
||||||
QDoubleSpinBox *marginLSpin;
|
QDoubleSpinBox *marginLSpin;
|
||||||
QDoubleSpinBox *marginRSpin;
|
QDoubleSpinBox *marginRSpin;
|
||||||
QDoubleSpinBox *verticalOffsetSpin;
|
QDoubleSpinBox *verticalOffsetSpin;
|
||||||
|
|
|
||||||
|
|
@ -542,7 +542,7 @@ void UserInfoPopup::showForUser(const QString &userName,
|
||||||
const CardArtParams params = (m_cardArtParamsMap && m_cardArtParamsMap->contains(userName))
|
const CardArtParams params = (m_cardArtParamsMap && m_cardArtParamsMap->contains(userName))
|
||||||
? m_cardArtParamsMap->value(userName)
|
? m_cardArtParamsMap->value(userName)
|
||||||
: CardArtParams{};
|
: 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{};
|
const QPixmap cardArt = (m_cardArtCache && !params.cardName.isEmpty()) ? m_cardArtCache->value(artKey) : QPixmap{};
|
||||||
m_header->setUserData(userInfo, online, avatar, cardArt, params);
|
m_header->setUserData(userInfo, online, avatar, cardArt, params);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,9 @@ void UserListPainter::drawBackground(QPainter *painter,
|
||||||
painter->drawRoundedRect(QRectF(cardRect.left(), cardRect.top(), 3, cardRect.height()), 2, 2);
|
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,
|
void UserListPainter::drawCardArt(QPainter *painter,
|
||||||
|
|
@ -95,7 +95,7 @@ void UserListPainter::drawCardArt(QPainter *painter,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString key = makeKey(userName, params.cardName);
|
const QString key = makeKey(userName, params.cardName, params.cardProviderId);
|
||||||
|
|
||||||
if (!cardArtCache->contains(key)) {
|
if (!cardArtCache->contains(key)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ class ServerInfo_User;
|
||||||
struct CardArtParams
|
struct CardArtParams
|
||||||
{
|
{
|
||||||
QString cardName = "";
|
QString cardName = "";
|
||||||
|
QString cardProviderId = "";
|
||||||
double marginPctL = 0.33;
|
double marginPctL = 0.33;
|
||||||
double marginPctR = 0.02;
|
double marginPctR = 0.02;
|
||||||
double verticalOffset = 0.35;
|
double verticalOffset = 0.35;
|
||||||
|
|
|
||||||
|
|
@ -904,12 +904,13 @@ void UserListWidget::processUserInfo(const ServerInfo_User &user, bool online)
|
||||||
const auto &cap = user.card_art_params();
|
const auto &cap = user.card_art_params();
|
||||||
CardArtParams params;
|
CardArtParams params;
|
||||||
params.cardName = QString::fromStdString(cap.card_name());
|
params.cardName = QString::fromStdString(cap.card_name());
|
||||||
|
params.cardProviderId = QString::fromStdString(cap.card_provider_id());
|
||||||
params.marginPctL = cap.margin_pct_l();
|
params.marginPctL = cap.margin_pct_l();
|
||||||
params.marginPctR = cap.margin_pct_r();
|
params.marginPctR = cap.margin_pct_r();
|
||||||
params.verticalOffset = cap.vertical_offset();
|
params.verticalOffset = cap.vertical_offset();
|
||||||
params.zoom = cap.zoom();
|
params.zoom = cap.zoom();
|
||||||
cardArtParamsMap.insert(userName, params);
|
cardArtParamsMap.insert(userName, params);
|
||||||
cardArtProvider->requestCardArt(userName, params.cardName);
|
cardArtProvider->requestCardArt(userName, params.cardName, params.cardProviderId);
|
||||||
} else {
|
} else {
|
||||||
cardArtParamsMap.remove(userName); // clear stale params on removal
|
cardArtParamsMap.remove(userName); // clear stale params on removal
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ int CardArtRulesModel::rowCount(const QModelIndex &parent) const
|
||||||
int CardArtRulesModel::columnCount(const QModelIndex &parent) const
|
int CardArtRulesModel::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
Q_UNUSED(parent);
|
||||||
return 3;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CardArtRulesModel::data(const QModelIndex &index, int role) const
|
QVariant CardArtRulesModel::data(const QModelIndex &index, int role) const
|
||||||
|
|
@ -43,8 +43,10 @@ QVariant CardArtRulesModel::data(const QModelIndex &index, int role) const
|
||||||
case 0:
|
case 0:
|
||||||
return e.cardName;
|
return e.cardName;
|
||||||
case 1:
|
case 1:
|
||||||
return e.mode;
|
return e.cardProviderId;
|
||||||
case 2:
|
case 2:
|
||||||
|
return e.mode;
|
||||||
|
case 3:
|
||||||
return e.reason;
|
return e.reason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,8 +64,10 @@ QVariant CardArtRulesModel::headerData(int section, Qt::Orientation orientation,
|
||||||
case 0:
|
case 0:
|
||||||
return tr("Card");
|
return tr("Card");
|
||||||
case 1:
|
case 1:
|
||||||
return tr("Mode");
|
return tr("ProviderId");
|
||||||
case 2:
|
case 2:
|
||||||
|
return tr("Mode");
|
||||||
|
case 3:
|
||||||
return tr("Reason");
|
return tr("Reason");
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -97,6 +101,15 @@ QString CardArtRulesModel::cardAt(int row) const
|
||||||
return entries[row].cardName;
|
return entries[row].cardName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CardArtRulesModel::Entry *CardArtRulesModel::entryAt(int row) const
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= static_cast<int>(entries.size())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &entries[row];
|
||||||
|
}
|
||||||
|
|
||||||
void CardArtRulesModel::onRefreshFinished(const Response &r)
|
void CardArtRulesModel::onRefreshFinished(const Response &r)
|
||||||
{
|
{
|
||||||
if (r.response_code() != Response::RespOk) {
|
if (r.response_code() != Response::RespOk) {
|
||||||
|
|
@ -109,8 +122,8 @@ void CardArtRulesModel::onRefreshFinished(const Response &r)
|
||||||
entries.clear();
|
entries.clear();
|
||||||
|
|
||||||
for (const auto &e : resp.entries()) {
|
for (const auto &e : resp.entries()) {
|
||||||
entries.push_back({QString::fromStdString(e.card_name()), QString::fromStdString(e.mode()),
|
entries.push_back({QString::fromStdString(e.card_name()), QString::fromStdString(e.card_provider_id()),
|
||||||
QString::fromStdString(e.reason())});
|
QString::fromStdString(e.mode()), QString::fromStdString(e.reason())});
|
||||||
}
|
}
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
@ -128,6 +141,7 @@ void TabCardArtRules::setupUi()
|
||||||
|
|
||||||
initSearchBar();
|
initSearchBar();
|
||||||
|
|
||||||
|
providerComboBox = new QComboBox;
|
||||||
modeBox = new QComboBox;
|
modeBox = new QComboBox;
|
||||||
reasonEdit = new QLineEdit;
|
reasonEdit = new QLineEdit;
|
||||||
|
|
||||||
|
|
@ -146,6 +160,7 @@ void TabCardArtRules::setupUi()
|
||||||
|
|
||||||
auto *form = new QFormLayout;
|
auto *form = new QFormLayout;
|
||||||
form->addRow(tr("Card:"), searchEdit);
|
form->addRow(tr("Card:"), searchEdit);
|
||||||
|
form->addRow(tr("ProviderId:"), providerComboBox);
|
||||||
form->addRow(tr("Mode:"), modeBox);
|
form->addRow(tr("Mode:"), modeBox);
|
||||||
form->addRow(tr("Reason:"), reasonEdit);
|
form->addRow(tr("Reason:"), reasonEdit);
|
||||||
|
|
||||||
|
|
@ -204,6 +219,34 @@ void TabCardArtRules::initSearchBar()
|
||||||
});
|
});
|
||||||
connect(searchCompleter, static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated), this,
|
connect(searchCompleter, static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated), this,
|
||||||
[this](const QString &name) { searchEdit->setText(name); });
|
[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()
|
void TabCardArtRules::retranslateUi()
|
||||||
|
|
@ -222,6 +265,7 @@ void TabCardArtRules::addRule()
|
||||||
{
|
{
|
||||||
Command_AddCardArtRule cmd;
|
Command_AddCardArtRule cmd;
|
||||||
cmd.set_card_name(searchEdit->text().toStdString());
|
cmd.set_card_name(searchEdit->text().toStdString());
|
||||||
|
cmd.set_card_provider_id(providerComboBox->currentData().toString().toStdString());
|
||||||
cmd.set_mode(modeBox->currentText().toStdString());
|
cmd.set_mode(modeBox->currentText().toStdString());
|
||||||
cmd.set_reason(reasonEdit->text().toStdString());
|
cmd.set_reason(reasonEdit->text().toStdString());
|
||||||
|
|
||||||
|
|
@ -238,7 +282,10 @@ void TabCardArtRules::removeSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
Command_RemoveCardArtRule cmd;
|
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));
|
client->sendCommand(client->prepareModeratorCommand(cmd));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public:
|
||||||
struct Entry
|
struct Entry
|
||||||
{
|
{
|
||||||
QString cardName;
|
QString cardName;
|
||||||
|
QString cardProviderId;
|
||||||
QString mode;
|
QString mode;
|
||||||
QString reason;
|
QString reason;
|
||||||
};
|
};
|
||||||
|
|
@ -35,6 +36,7 @@ public:
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
QString cardAt(int row) const;
|
QString cardAt(int row) const;
|
||||||
|
const Entry *entryAt(int row) const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRefreshFinished(const Response &r);
|
void onRefreshFinished(const Response &r);
|
||||||
|
|
@ -70,11 +72,13 @@ private:
|
||||||
|
|
||||||
QLineEdit *searchEdit;
|
QLineEdit *searchEdit;
|
||||||
void initSearchBar();
|
void initSearchBar();
|
||||||
|
void populateProviderCombo(const QString &cardName);
|
||||||
QCompleter *searchCompleter;
|
QCompleter *searchCompleter;
|
||||||
CardDatabaseModel *cardDbModel;
|
CardDatabaseModel *cardDbModel;
|
||||||
CardDatabaseDisplayModel *cardDbDisplayModel;
|
CardDatabaseDisplayModel *cardDbDisplayModel;
|
||||||
CardSearchModel *cardSearchModel;
|
CardSearchModel *cardSearchModel;
|
||||||
CardCompleterProxyModel *cardProxyModel;
|
CardCompleterProxyModel *cardProxyModel;
|
||||||
|
QComboBox *providerComboBox;
|
||||||
QComboBox *modeBox;
|
QComboBox *modeBox;
|
||||||
QLineEdit *reasonEdit;
|
QLineEdit *reasonEdit;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,9 @@ message Command_AddCardArtRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
optional string card_name = 1;
|
optional string card_name = 1;
|
||||||
optional string mode = 2; // "ALLOW" or "DENY"
|
optional string card_provider_id = 2;
|
||||||
optional string reason = 3;
|
optional string mode = 3; // "ALLOW" or "DENY"
|
||||||
|
optional string reason = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Command_RemoveCardArtRule {
|
message Command_RemoveCardArtRule {
|
||||||
|
|
@ -126,6 +127,7 @@ message Command_RemoveCardArtRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
optional string card_name = 1;
|
optional string card_name = 1;
|
||||||
|
optional string card_provider_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Command_ListCardArtRules {
|
message Command_ListCardArtRules {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ import "response.proto";
|
||||||
|
|
||||||
message Response_CardArtRuleEntry {
|
message Response_CardArtRuleEntry {
|
||||||
optional string card_name = 1;
|
optional string card_name = 1;
|
||||||
optional string mode = 2;
|
optional string card_provider_id = 2;
|
||||||
optional string reason = 3;
|
optional string mode = 3;
|
||||||
|
optional string reason = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Response_ListCardArtRules {
|
message Response_ListCardArtRules {
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@ message ServerInfo_User {
|
||||||
};
|
};
|
||||||
message CardArtParams {
|
message CardArtParams {
|
||||||
optional string card_name = 1;
|
optional string card_name = 1;
|
||||||
optional double margin_pct_l = 2 [default = 0.33];
|
optional string card_provider_id = 2;
|
||||||
optional double margin_pct_r = 3 [default = 0.02];
|
optional double margin_pct_l = 3 [default = 0.33];
|
||||||
optional double vertical_offset = 4 [default = 0.35];
|
optional double margin_pct_r = 4 [default = 0.02];
|
||||||
optional double zoom = 5 [default = 1.0];
|
optional double vertical_offset = 5 [default = 0.35];
|
||||||
|
optional double zoom = 6 [default = 1.0];
|
||||||
};
|
};
|
||||||
|
|
||||||
optional string name = 1;
|
optional string name = 1;
|
||||||
|
|
|
||||||
|
|
@ -212,8 +212,9 @@ message Command_SetCardArtParams {
|
||||||
optional Command_SetCardArtParams ext = 1025;
|
optional Command_SetCardArtParams ext = 1025;
|
||||||
}
|
}
|
||||||
optional string card_name = 1;
|
optional string card_name = 1;
|
||||||
optional double margin_pct_l = 2;
|
optional string card_provider_id = 2;
|
||||||
optional double margin_pct_r = 3;
|
optional double margin_pct_l = 3;
|
||||||
optional double vertical_offset = 4;
|
optional double margin_pct_r = 4;
|
||||||
optional double zoom = 5;
|
optional double vertical_offset = 5;
|
||||||
|
optional double zoom = 6;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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` (
|
CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` (
|
||||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`card_name` varchar(255) NOT NULL,
|
`card_name` varchar(255) NOT NULL,
|
||||||
|
`card_provider_id` varchar(255) NOT NULL,
|
||||||
`mode` enum('ALLOW','DENY') NOT NULL,
|
`mode` enum('ALLOW','DENY') NOT NULL,
|
||||||
`reason` varchar(255) DEFAULT NULL,
|
`reason` varchar(255) DEFAULT NULL,
|
||||||
`created_by` int(7) unsigned DEFAULT NULL,
|
`created_by` int(7) unsigned DEFAULT NULL,
|
||||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`id`),
|
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`),
|
KEY `idx_mode` (`mode`),
|
||||||
FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`)
|
FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`)
|
||||||
ON DELETE SET NULL
|
ON DELETE SET NULL
|
||||||
|
|
|
||||||
|
|
@ -305,12 +305,13 @@ CREATE TABLE IF NOT EXISTS `cockatrice_audit` (
|
||||||
CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` (
|
CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` (
|
||||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`card_name` varchar(255) NOT NULL,
|
`card_name` varchar(255) NOT NULL,
|
||||||
|
`card_provider_id` varchar(255) NOT NULL,
|
||||||
`mode` enum('ALLOW','DENY') NOT NULL,
|
`mode` enum('ALLOW','DENY') NOT NULL,
|
||||||
`reason` varchar(255) DEFAULT NULL,
|
`reason` varchar(255) DEFAULT NULL,
|
||||||
`created_by` int(7) unsigned DEFAULT NULL,
|
`created_by` int(7) unsigned DEFAULT NULL,
|
||||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`id`),
|
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`),
|
KEY `idx_mode` (`mode`),
|
||||||
FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`)
|
FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`)
|
||||||
ON DELETE SET NULL
|
ON DELETE SET NULL
|
||||||
|
|
|
||||||
|
|
@ -693,6 +693,9 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer
|
||||||
if (obj.contains("card_name")) {
|
if (obj.contains("card_name")) {
|
||||||
cap->set_card_name(obj["card_name"].toString().toStdString());
|
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")) {
|
if (obj.contains("marginPctL")) {
|
||||||
cap->set_margin_pct_l(obj["marginPctL"].toDouble(0.33));
|
cap->set_margin_pct_l(obj["marginPctL"].toDouble(0.33));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1577,11 +1577,13 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Comm
|
||||||
return Response::RespOk;
|
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(":name", cardName);
|
||||||
|
q->bindValue(":provider", cardProviderId);
|
||||||
|
|
||||||
if (!sqlInterface->execSqlQuery(q)) {
|
if (!sqlInterface->execSqlQuery(q)) {
|
||||||
qWarning() << "Card art rule lookup failed; failing open for" << cardName;
|
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 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;
|
return Response::RespInvalidData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1620,7 +1623,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isCardNameAllowed(cardName)) {
|
if (!isCardNameAllowed(cardName, cardProviderId)) {
|
||||||
return Response::RespFunctionNotAllowed;
|
return Response::RespFunctionNotAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1633,6 +1636,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const
|
||||||
|
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
obj["card_name"] = cardName;
|
obj["card_name"] = cardName;
|
||||||
|
obj["card_provider_id"] = cardProviderId;
|
||||||
obj["marginPctL"] = marginPctL;
|
obj["marginPctL"] = marginPctL;
|
||||||
obj["marginPctR"] = marginPctR;
|
obj["marginPctR"] = marginPctR;
|
||||||
obj["verticalOffset"] = verticalOffset;
|
obj["verticalOffset"] = verticalOffset;
|
||||||
|
|
@ -1649,6 +1653,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const
|
||||||
// Keep the in-memory userInfo in sync
|
// Keep the in-memory userInfo in sync
|
||||||
auto *cap = userInfo->mutable_card_art_params();
|
auto *cap = userInfo->mutable_card_art_params();
|
||||||
cap->set_card_name(cmd.card_name());
|
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_l(marginPctL);
|
||||||
cap->set_margin_pct_r(marginPctR);
|
cap->set_margin_pct_r(marginPctR);
|
||||||
cap->set_vertical_offset(verticalOffset);
|
cap->set_vertical_offset(verticalOffset);
|
||||||
|
|
@ -1664,21 +1669,23 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAddCardArtRule(const Co
|
||||||
ResponseContainer &)
|
ResponseContainer &)
|
||||||
{
|
{
|
||||||
const QString cardName = QString::fromStdString(cmd.card_name());
|
const QString cardName = QString::fromStdString(cmd.card_name());
|
||||||
|
const QString cardProviderId = QString::fromStdString(cmd.card_provider_id());
|
||||||
const QString mode = QString::fromStdString(cmd.mode());
|
const QString mode = QString::fromStdString(cmd.mode());
|
||||||
|
|
||||||
if (mode != "ALLOW" && mode != "DENY") {
|
if (mode != "ALLOW" && mode != "DENY") {
|
||||||
return Response::RespInvalidData;
|
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;
|
return Response::RespInvalidData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *q = sqlInterface->prepareQuery("INSERT INTO {prefix}_card_art_name_rules "
|
QSqlQuery *q = sqlInterface->prepareQuery("INSERT INTO {prefix}_card_art_name_rules "
|
||||||
"(card_name, mode, reason, created_by) "
|
"(card_name, card_provider_id, mode, reason, created_by) "
|
||||||
"VALUES (:name, :mode, :reason, :uid) "
|
"VALUES (:name, :provider, :mode, :reason, :uid) "
|
||||||
"ON DUPLICATE KEY UPDATE mode=:mode2, reason=:reason2");
|
"ON DUPLICATE KEY UPDATE mode=:mode2, reason=:reason2");
|
||||||
|
|
||||||
q->bindValue(":name", cardName);
|
q->bindValue(":name", cardName);
|
||||||
|
q->bindValue(":provider", cardProviderId);
|
||||||
q->bindValue(":mode", mode);
|
q->bindValue(":mode", mode);
|
||||||
q->bindValue(":mode2", mode);
|
q->bindValue(":mode2", mode);
|
||||||
q->bindValue(":reason", QString::fromStdString(cmd.reason()));
|
q->bindValue(":reason", QString::fromStdString(cmd.reason()));
|
||||||
|
|
@ -1696,12 +1703,15 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRemoveCardArtRule(const
|
||||||
ResponseContainer &)
|
ResponseContainer &)
|
||||||
{
|
{
|
||||||
auto cardName = QString::fromStdString(cmd.card_name());
|
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;
|
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(":name", cardName);
|
||||||
|
q->bindValue(":provider", cardProviderId);
|
||||||
|
|
||||||
if (!sqlInterface->execSqlQuery(q)) {
|
if (!sqlInterface->execSqlQuery(q)) {
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
|
|
@ -1713,7 +1723,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRemoveCardArtRule(const
|
||||||
Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const Command_ListCardArtRules &,
|
Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const Command_ListCardArtRules &,
|
||||||
ResponseContainer &rc)
|
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)) {
|
if (!sqlInterface->execSqlQuery(q)) {
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
|
|
@ -1724,8 +1735,9 @@ Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const
|
||||||
while (q->next()) {
|
while (q->next()) {
|
||||||
auto *entry = re->add_entries();
|
auto *entry = re->add_entries();
|
||||||
entry->set_card_name(q->value(0).toString().toStdString());
|
entry->set_card_name(q->value(0).toString().toStdString());
|
||||||
entry->set_mode(q->value(1).toString().toStdString());
|
entry->set_card_provider_id(q->value(1).toString().toStdString());
|
||||||
entry->set_reason(q->value(2).toString().toStdString());
|
entry->set_mode(q->value(2).toString().toStdString());
|
||||||
|
entry->set_reason(q->value(3).toString().toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.setResponseExtension(re);
|
rc.setResponseExtension(re);
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ private:
|
||||||
|
|
||||||
Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc);
|
Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc);
|
||||||
Response::ResponseCode cmdAccountImage(const Command_AccountImage &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 cmdSetCardArtParams(const Command_SetCardArtParams &cmd, ResponseContainer &);
|
||||||
Response::ResponseCode cmdAddCardArtRule(const Command_AddCardArtRule &cmd, ResponseContainer &);
|
Response::ResponseCode cmdAddCardArtRule(const Command_AddCardArtRule &cmd, ResponseContainer &);
|
||||||
Response::ResponseCode cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, ResponseContainer &);
|
Response::ResponseCode cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, ResponseContainer &);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue