From 27bd00bec835988b3f5631bf8cf9e97048aad026 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 29 Jun 2014 18:06:39 +0200 Subject: [PATCH 01/82] Direct chat: ensure focus is on the input line; fixes #113 --- cockatrice/src/tab_message.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index 80a56154b..eec46bb96 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -34,6 +34,10 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, c retranslateUi(); setLayout(vbox); + + setFocusProxy(sayEdit); + chatView->setFocusProxy(sayEdit); + sayEdit->setFocus(); } TabMessage::~TabMessage() From d86fa410df8cdcca08fb00c5a48efecfbe616391 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 29 Jun 2014 19:16:31 +0200 Subject: [PATCH 02/82] Chat room: ensure focus on the input line --- cockatrice/src/tab_room.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index 79673e0ff..2af3dd1e3 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -97,6 +97,10 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI const int gameListSize = info.game_list_size(); for (int i = 0; i < gameListSize; ++i) gameSelector->processGameInfo(info.game_list(i)); + + setFocusProxy(sayEdit); + chatView->setFocusProxy(sayEdit); + sayEdit->setFocus(); } TabRoom::~TabRoom() From 4d6f46b06ec766d5135180abbb468eaa51668398 Mon Sep 17 00:00:00 2001 From: Daenyth Date: Sun, 29 Jun 2014 14:46:06 -0400 Subject: [PATCH 03/82] Change client ping send interval to 9 sec --- cockatrice/src/remoteclient.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 0eae13abe..e0d3d8604 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -15,7 +15,7 @@ RemoteClient::RemoteClient(QObject *parent) : AbstractClient(parent), timeRunning(0), lastDataReceived(0), messageInProgress(false), handshakeStarted(false), messageLength(0) { timer = new QTimer(this); - timer->setInterval(1000); + timer->setInterval(9000); connect(timer, SIGNAL(timeout()), this, SLOT(ping())); socket = new QTcpSocket(this); @@ -23,7 +23,7 @@ RemoteClient::RemoteClient(QObject *parent) connect(socket, SIGNAL(connected()), this, SLOT(slotConnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError))); - + connect(this, SIGNAL(serverIdentificationEventReceived(const Event_ServerIdentification &)), this, SLOT(processServerIdentificationEvent(const Event_ServerIdentification &))); connect(this, SIGNAL(connectionClosedEventReceived(Event_ConnectionClosed)), this, SLOT(processConnectionClosedEvent(Event_ConnectionClosed))); connect(this, SIGNAL(sigConnectToServer(QString, unsigned int, QString, QString)), this, SLOT(doConnectToServer(QString, unsigned int, QString, QString))); @@ -47,12 +47,12 @@ void RemoteClient::slotConnected() { timeRunning = lastDataReceived = 0; timer->start(); - + // dirty hack to be compatible with v14 server sendCommandContainer(CommandContainer()); getNewCmdId(); // end of hack - + setStatus(StatusAwaitingWelcome); } @@ -208,7 +208,7 @@ void RemoteClient::ping() pend->deleteLater(); } } - + int maxTime = timeRunning - lastDataReceived; emit maxPingTime(maxTime, maxTimeout); if (maxTime >= maxTimeout) { From 922e98af67f25151e2dbcb377742c21ef401caf8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 29 Jun 2014 23:30:32 +0200 Subject: [PATCH 04/82] Add pricing from deckbrew.com * the previous PriceUpdater class has become abstract * BLPPriceUpdater inherits the old code for blacklotusproject.com * DBPriceUpdater is a new implementation for deckbrew.com * add a setting to choose between the two --- cockatrice/src/dlg_settings.cpp | 39 ++++++++- cockatrice/src/dlg_settings.h | 4 + cockatrice/src/priceupdater.cpp | 136 ++++++++++++++++++++++++++++- cockatrice/src/priceupdater.h | 32 +++++-- cockatrice/src/settingscache.cpp | 7 ++ cockatrice/src/settingscache.h | 3 + cockatrice/src/tab_deck_editor.cpp | 13 ++- 7 files changed, 222 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 141158240..cf8fa846b 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "carddatabase.h" #include "dlg_settings.h" @@ -531,9 +532,27 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() priceTagsCheckBox = new QCheckBox; priceTagsCheckBox->setChecked(settingsCache->getPriceTagFeature()); connect(priceTagsCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPriceTagFeature(int))); - + + priceTagSource0 = new QRadioButton; + priceTagSource1 = new QRadioButton; + + switch(settingsCache->getPriceTagSource()) + { + case 0: + priceTagSource0->setChecked(true); + break; + case 1: + priceTagSource1->setChecked(true); + break; + } + + connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); + connect(priceTagSource1, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); + QGridLayout *generalGrid = new QGridLayout; generalGrid->addWidget(priceTagsCheckBox, 0, 0); + generalGrid->addWidget(priceTagSource0, 1, 0); + generalGrid->addWidget(priceTagSource1, 2, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -546,10 +565,26 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() void DeckEditorSettingsPage::retranslateUi() { - priceTagsCheckBox->setText(tr("Enable &price tag feature (using data from blacklotusproject.com)")); + priceTagsCheckBox->setText(tr("Enable &price tag feature")); + priceTagSource0->setText(tr("using data from blacklotusproject.com")); + priceTagSource1->setText(tr("using data from deckbrew.com")); generalGroupBox->setTitle(tr("General")); } +void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked) +{ + if(!checked) + return; + + int source=0; + if(priceTagSource0->isChecked()) + source=0; + if(priceTagSource1->isChecked()) + source=1; + + QMetaObject::invokeMethod( settingsCache, "setPriceTagSource", Qt::QueuedConnection, Q_ARG(int, source)); +} + MessagesSettingsPage::MessagesSettingsPage() { aAdd = new QAction(this); diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 8462f612e..803e0dfc3 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -15,6 +15,7 @@ class QCheckBox; class QLabel; class QCloseEvent; class QSpinBox; +class QRadioButton; class AbstractSettingsPage : public QWidget { public: @@ -100,8 +101,11 @@ class DeckEditorSettingsPage : public AbstractSettingsPage { public: DeckEditorSettingsPage(); void retranslateUi(); +private slots: + void radioPriceTagSourceClicked(bool checked); private: QCheckBox *priceTagsCheckBox; + QRadioButton *priceTagSource0, *priceTagSource1; QGroupBox *generalGroupBox; }; diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index a0d32a324..e5b359df9 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -4,25 +4,43 @@ */ #include #include +#include #include "qt-json/json.h" #include "priceupdater.h" +#if QT_VERSION < 0x050000 + // for Qt::escape() + #include +#endif + /** * Constructor. * * @param _deck deck. */ -PriceUpdater::PriceUpdater(const DeckList *_deck) +AbstractPriceUpdater::AbstractPriceUpdater(const DeckList *_deck) { nam = new QNetworkAccessManager(this); deck = _deck; } +// blacklotusproject.com + +/** + * Constructor. + * + * @param _deck deck. + */ +BLPPriceUpdater::BLPPriceUpdater(const DeckList *_deck) +: AbstractPriceUpdater(_deck) +{ +} + /** * Update the prices of the cards in deckList. */ -void PriceUpdater::updatePrices() +void BLPPriceUpdater::updatePrices() { QString q = "http://blacklotusproject.com/json/?cards="; QStringList cards = deck->getCardList(); @@ -38,7 +56,7 @@ void PriceUpdater::updatePrices() /** * Called when the download of the json file with the prices is finished. */ -void PriceUpdater::downloadFinished() +void BLPPriceUpdater::downloadFinished() { QNetworkReply *reply = static_cast(sender()); bool ok; @@ -82,3 +100,115 @@ void PriceUpdater::downloadFinished() deleteLater(); emit finishedUpdate(); } + +// deckbrew.com + +/** + * Constructor. + * + * @param _deck deck. + */ +DBPriceUpdater::DBPriceUpdater(const DeckList *_deck) +: AbstractPriceUpdater(_deck) +{ +} + +/** + * Update the prices of the cards in deckList. + */ +void DBPriceUpdater::updatePrices() +{ + QString q = "https://api.deckbrew.com/mtg/cards"; + QStringList cards = deck->getCardList(); + for (int i = 0; i < cards.size(); ++i) { + q += (i ? "&" : "?") + QString("name=") + cards[i].toLower(); + } + QUrl url(q.replace(' ', '+')); + + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); +} + +/** + * Called when the download of the json file with the prices is finished. + */ +void DBPriceUpdater::downloadFinished() +{ + QNetworkReply *reply = static_cast(sender()); + bool ok; + QString tmp = QString(reply->readAll()); + + // Errors are incapsulated in an object, check for them first + QVariantMap resultMap = QtJson::Json::parse(tmp, ok).toMap(); + if (!ok) { + QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); + reply->deleteLater(); + deleteLater(); + return; + } + + if(resultMap.contains("errors")) + { + QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices:") + + "
" + +#if QT_VERSION < 0x050000 + Qt::escape(resultMap["errors"].toList().first().toString()) +#else + resultMap["errors"].toList().first().toString().toHtmlEscaped() +#endif + ); + reply->deleteLater(); + deleteLater(); + return; + } + + // Good results are a list + QVariantList resultList = QtJson::Json::parse(tmp, ok).toList(); + if (!ok) { + QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); + reply->deleteLater(); + deleteLater(); + return; + } + + QMap cardsPrice; + + QListIterator it(resultList); + while (it.hasNext()) { + QVariantMap map = it.next().toMap(); + QString name = map.value("name").toString().toLower(); + + QList editions = map.value("editions").toList(); + foreach (QVariant ed, editions) + { + QVariantMap edition = ed.toMap(); + QString set = edition.value("set_id").toString(); + // Prices are in USD cents + float price = edition.value("price").toMap().value("median").toString().toFloat() / 100; + //qDebug() << "card " << name << " set " << set << " price " << price << endl; + + /** + * Make sure Masters Edition (MED) isn't the set, as it doesn't + * physically exist. Also check the price to see that the cheapest set + * ends up as the final price. + */ + if (set != "MED" && (!cardsPrice.contains(name) || cardsPrice.value(name) > price)) + cardsPrice.insert(name, price); + } + } + + InnerDecklistNode *listRoot = deck->getRoot(); + for (int i = 0; i < listRoot->size(); i++) { + InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); + for (int j = 0; j < currentZone->size(); j++) { + DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); + if (!currentCard) + continue; + currentCard->setPrice(cardsPrice[currentCard->getName().toLower()]); + } + } + + reply->deleteLater(); + deleteLater(); + emit finishedUpdate(); +} diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index a95252eb8..901471f55 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -11,18 +11,38 @@ class QNetworkAccessManager; * * @author Marcio Ribeiro */ -class PriceUpdater : public QObject +class AbstractPriceUpdater : public QWidget { Q_OBJECT -private: +protected: const DeckList *deck; QNetworkAccessManager *nam; signals: void finishedUpdate(); -private slots: - void downloadFinished(); +protected slots: + virtual void downloadFinished() = 0; public: - PriceUpdater(const DeckList *deck); - void updatePrices(); + AbstractPriceUpdater(const DeckList *deck); + virtual void updatePrices() = 0; +}; + +class BLPPriceUpdater : public AbstractPriceUpdater +{ + Q_OBJECT +protected: + virtual void downloadFinished(); +public: + BLPPriceUpdater(const DeckList *deck); + virtual void updatePrices(); +}; + +class DBPriceUpdater : public AbstractPriceUpdater +{ + Q_OBJECT +protected: + virtual void downloadFinished(); +public: + DBPriceUpdater(const DeckList *deck); + virtual void updatePrices(); }; #endif diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 892630f53..5f504aa34 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -45,6 +45,7 @@ SettingsCache::SettingsCache() soundPath = settings->value("sound/path").toString(); priceTagFeature = settings->value("deckeditor/pricetags", false).toBool(); + priceTagSource = settings->value("deckeditor/pricetagsource", 0).toInt(); ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool(); } @@ -247,6 +248,12 @@ void SettingsCache::setPriceTagFeature(int _priceTagFeature) emit priceTagFeatureChanged(priceTagFeature); } +void SettingsCache::setPriceTagSource(int _priceTagSource) +{ + priceTagSource = _priceTagSource; + settings->setValue("deckeditor/pricetagsource", priceTagSource); +} + void SettingsCache::setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers) { ignoreUnregisteredUsers = _ignoreUnregisteredUsers; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 68b5129e6..3b4d612d5 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -53,6 +53,7 @@ private: bool soundEnabled; QString soundPath; bool priceTagFeature; + int priceTagSource; bool ignoreUnregisteredUsers; QString picUrl; QString picUrlHq; @@ -87,6 +88,7 @@ public: bool getSoundEnabled() const { return soundEnabled; } QString getSoundPath() const { return soundPath; } bool getPriceTagFeature() const { return priceTagFeature; } + int getPriceTagSource() const { return priceTagSource; } bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } QString getPicUrl() const { return picUrl; } QString getPicUrlHq() const { return picUrlHq; } @@ -121,6 +123,7 @@ public slots: void setSoundEnabled(int _soundEnabled); void setSoundPath(const QString &_soundPath); void setPriceTagFeature(int _priceTagFeature); + void setPriceTagSource(int _priceTagSource); void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers); void setPicUrl(const QString &_picUrl); void setPicUrlHq(const QString &_picUrlHq); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 0c1d44e14..5706ffd6c 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -644,7 +644,18 @@ void TabDeckEditor::setPriceTagFeatureEnabled(int enabled) void TabDeckEditor::actUpdatePrices() { aUpdatePrices->setDisabled(true); - PriceUpdater *up = new PriceUpdater(deckModel->getDeckList()); + AbstractPriceUpdater *up; + + switch(settingsCache->getPriceTagSource()) + { + case 0: + up = new BLPPriceUpdater(deckModel->getDeckList()); + break; + case 1: + up = new DBPriceUpdater(deckModel->getDeckList()); + break; + } + connect(up, SIGNAL(finishedUpdate()), this, SLOT(finishedUpdatingPrices())); up->updatePrices(); } From 7cbe4101728123e6deb2cf1795fa2babb004ee53 Mon Sep 17 00:00:00 2001 From: arxanas Date: Mon, 30 Jun 2014 01:18:05 -0400 Subject: [PATCH 05/82] Fix #45: don't send tokens to deckstats. --- cockatrice/src/carddatabase.cpp | 35 ++--------------------- cockatrice/src/carddatabase.h | 2 +- cockatrice/src/deckstats_interface.cpp | 39 ++++++++++++++++++++++++-- cockatrice/src/deckstats_interface.h | 14 ++++++++- cockatrice/src/tab_deck_editor.cpp | 5 +++- common/decklist.cpp | 30 ++++++++++++++------ common/decklist.h | 23 +++++++++++++++ oracle/src/oracleimporter.cpp | 4 ++- 8 files changed, 105 insertions(+), 47 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f94..c57ac792b 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -652,41 +652,13 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) } } -LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) +LoadStatus CardDatabase::loadFromFile(const QString &fileName) { QFile file(fileName); file.open(QIODevice::ReadOnly); if (!file.isOpen()) return FileError; - if (tokens) { - QMutableHashIterator i(cardHash); - while (i.hasNext()) { - i.next(); - if (i.value()->getIsToken()) { - delete i.value(); - i.remove(); - } - } - } else { - QHashIterator setIt(setHash); - while (setIt.hasNext()) { - setIt.next(); - delete setIt.value(); - } - setHash.clear(); - - QMutableHashIterator i(cardHash); - while (i.hasNext()) { - i.next(); - if (!i.value()->getIsToken()) { - delete i.value(); - i.remove(); - } - } - cardHash.clear(); - } - QXmlStreamReader xml(&file); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { @@ -738,8 +710,7 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) QHashIterator cardIterator(cardHash); while (cardIterator.hasNext()) { CardInfo *card = cardIterator.next().value(); - if (card->getIsToken() == tokens) - xml << card; + xml << card; } xml.writeEndElement(); // cards @@ -773,7 +744,7 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) { LoadStatus tempLoadStatus = NotLoaded; if (!path.isEmpty()) - tempLoadStatus = loadFromFile(path, tokens); + tempLoadStatus = loadFromFile(path); if (tempLoadStatus == Ok) { SetList allSets; diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 09edeff4a..be82099dd 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -186,7 +186,7 @@ public: CardSet *getSet(const QString &setName); QList getCardList() const { return cardHash.values(); } SetList getSetList() const; - LoadStatus loadFromFile(const QString &fileName, bool tokens = false); + LoadStatus loadFromFile(const QString &fileName); bool saveToFile(const QString &fileName, bool tokens = false); QStringList getAllColors() const; QStringList getAllMainCardTypes() const; diff --git a/cockatrice/src/deckstats_interface.cpp b/cockatrice/src/deckstats_interface.cpp index eae4caae8..d3c96d8b0 100644 --- a/cockatrice/src/deckstats_interface.cpp +++ b/cockatrice/src/deckstats_interface.cpp @@ -7,8 +7,10 @@ #include #include -DeckStatsInterface::DeckStatsInterface(QObject *parent) - : QObject(parent) +DeckStatsInterface::DeckStatsInterface( + CardDatabase &_cardDatabase, + QObject *parent +) : QObject(parent), cardDatabase(_cardDatabase) { manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *))); @@ -42,7 +44,11 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply) void DeckStatsInterface::analyzeDeck(DeckList *deck) { QUrl params; - params.addQueryItem("deck", deck->writeToString_Plain()); + + DeckList deckWithoutTokens; + copyDeckWithoutTokens(*deck, deckWithoutTokens); + + params.addQueryItem("deck", deckWithoutTokens.writeToString_Plain()); QByteArray data; data.append(params.encodedQuery()); @@ -51,3 +57,30 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck) manager->post(request, data); } + +struct CopyIfNotAToken { + CardDatabase &cardDatabase; + DeckList &destination; + + CopyIfNotAToken( + CardDatabase &_cardDatabase, + DeckList &_destination + ) : cardDatabase(_cardDatabase), destination(_destination) {}; + + void operator()( + const InnerDecklistNode *node, + const DecklistCardNode *card + ) const { + if (!cardDatabase.getCard(card->getName())->getIsToken()) { + destination.addCard(card->getName(), node->getName()); + } + } +}; + +void DeckStatsInterface::copyDeckWithoutTokens( + const DeckList &source, + DeckList &destination +) { + CopyIfNotAToken copyIfNotAToken(cardDatabase, destination); + source.forEachCard(copyIfNotAToken); +} diff --git a/cockatrice/src/deckstats_interface.h b/cockatrice/src/deckstats_interface.h index e8454dd5a..928d52208 100644 --- a/cockatrice/src/deckstats_interface.h +++ b/cockatrice/src/deckstats_interface.h @@ -1,6 +1,8 @@ #ifndef DECKSTATS_INTERFACE_H #define DECKSTATS_INTERFACE_H +#include "carddatabase.h" +#include "decklist.h" #include class QNetworkAccessManager; @@ -11,10 +13,20 @@ class DeckStatsInterface : public QObject { Q_OBJECT private: QNetworkAccessManager *manager; + + CardDatabase &cardDatabase; + + /** + * Deckstats doesn't recognize token cards, and instead tries to find the + * closest non-token card instead. So we construct a new deck which has no + * tokens. + */ + void copyDeckWithoutTokens(const DeckList &source, DeckList& destination); + private slots: void queryFinished(QNetworkReply *reply); public: - DeckStatsInterface(QObject *parent = 0); + DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0); void analyzeDeck(DeckList *deck); }; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 0c1d44e14..96fb9a900 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -508,7 +508,10 @@ void TabDeckEditor::actPrintDeck() void TabDeckEditor::actAnalyzeDeck() { - DeckStatsInterface *interface = new DeckStatsInterface(this); // it deletes itself when done + DeckStatsInterface *interface = new DeckStatsInterface( + *databaseModel->getDatabase(), + this + ); // it deletes itself when done interface->analyzeDeck(deckModel->getDeckList()); } diff --git a/common/decklist.cpp b/common/decklist.cpp index 90b5cb7e6..1dd9acf36 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -579,16 +579,30 @@ bool DeckList::loadFromFile_Plain(QIODevice *device) return loadFromStream_Plain(in); } +struct WriteToStream { + QTextStream &stream; + + WriteToStream(QTextStream &_stream) : stream(_stream) {} + + void operator()( + const InnerDecklistNode *node, + const DecklistCardNode *card + ) { + if (node->getName() == "side") { + stream << "SB: "; + } + stream << QString("%1 %2\n").arg( + card->getNumber() + ).arg( + card->getName() + ); + } +}; + bool DeckList::saveToStream_Plain(QTextStream &out) { - // Support for this is only possible if the internal structure doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); - } - } + WriteToStream writeToStream(out); + forEachCard(writeToStream); return true; } diff --git a/common/decklist.h b/common/decklist.h index 2eed52fc6..4dfd2bbd6 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -117,6 +117,7 @@ public: QString getName() const { return name; } void setName(const QString &_name) { name = _name; } float getPrice() const { return price; } + void setPrice(const float _price) { price = _price; } }; @@ -169,6 +170,28 @@ public: InnerDecklistNode *getRoot() const { return root; } DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0); + + /** + * Calls a given function object for each card in the deck. It must + * take a InnerDecklistNode* as its first argument and a + * DecklistCardNode* as its second. + */ + template + void forEachCard(Callback &callback) const { + // Support for this is only possible if the internal structure + // doesn't get more complicated. + for (int i = 0; i < root->size(); i++) { + const InnerDecklistNode *node = + dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + const DecklistCardNode *card = + dynamic_cast( + node->at(j) + ); + callback(node, card); + } + } + } }; #endif diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 70972d92d..0da35abe2 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -137,6 +137,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardText; int cardId; int cardLoyalty; + bool cardIsToken = false; QMap splitCards; while (it.hasNext()) { @@ -197,9 +198,10 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) cardText = map.contains("text") ? map.value("text").toString() : QString(""); cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; + cardIsToken = map.value("layout") == "token"; } - CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); + CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); if (!set->contains(card)) { card->addToSet(set); From f730dca14c84d9f764f66c825418d973058c1f7a Mon Sep 17 00:00:00 2001 From: arxanas Date: Tue, 1 Jul 2014 02:29:25 -0400 Subject: [PATCH 06/82] Fix #155: Vanguard card names no longer conflict with regular card names. --- oracle/src/oracleimporter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 70972d92d..241924e3b 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -197,6 +197,11 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) cardText = map.contains("text") ? map.value("text").toString() : QString(""); cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; + + // Distinguish Vanguard cards from regular cards of the same name. + if (map.value("layout") == "vanguard") { + cardName += " Avatar"; + } } CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); From fb4a7b327478056aba254e71e25baa908139a4b0 Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Tue, 1 Jul 2014 12:04:33 -0400 Subject: [PATCH 07/82] Give more friendly login error message --- cockatrice/src/window_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index f5dc26843..7890d63af 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -241,7 +241,7 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 { switch (r) { case Response::RespWrongPassword: - QMessageBox::critical(this, tr("Error"), tr("Invalid login data.")); + QMessageBox::critical(this, tr("Error"), tr("Incorrect username or password. Please check your authentication information and try again.")); break; case Response::RespWouldOverwriteOldSession: QMessageBox::critical(this, tr("Error"), tr("There is already an active session using this user name.\nPlease close that session first and re-login.")); From 2b55170110ea9162dcf3695b50cd9d1128a24a99 Mon Sep 17 00:00:00 2001 From: woogerboy21 Date: Tue, 1 Jul 2014 13:11:52 -0400 Subject: [PATCH 08/82] DB Maint Scripts External shell script to help maintain the cockatrice database tables. --- servatrice/scripts/maint_replays | 6 ++++++ servatrice/scripts/maint_sessions | 7 +++++++ servatrice/scripts/mysql.cnf | 3 +++ 3 files changed, 16 insertions(+) create mode 100644 servatrice/scripts/maint_replays create mode 100644 servatrice/scripts/maint_sessions create mode 100644 servatrice/scripts/mysql.cnf diff --git a/servatrice/scripts/maint_replays b/servatrice/scripts/maint_replays new file mode 100644 index 000000000..b1326d9b2 --- /dev/null +++ b/servatrice/scripts/maint_replays @@ -0,0 +1,6 @@ +#!/bin/bash +#RUN THIS SCRIPT TO REMOVE ALL COCKATRICE REPLAYS FROM THE DATABASE +#--CAN BE SCHEDULED WITH CRONTAB + +mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays' +mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays_access' diff --git a/servatrice/scripts/maint_sessions b/servatrice/scripts/maint_sessions new file mode 100644 index 000000000..1fabd68f1 --- /dev/null +++ b/servatrice/scripts/maint_sessions @@ -0,0 +1,7 @@ +#!/bin/bash +#REPLACE THE --date="1 month ago" WITH THE TIME FRAME TO CLEAR UP +#EX: --date="1 day ago" (FOR SINGLE DAY) +#--CAN BE SCHEDULED WITH CRONTAB + +DATE=$(date +%Y-%m --date="1 month ago") +mysql --defaults-file=./mysql.cnf -h localhost -e "delete from servatrice.cockatrice_sessions where start_time like \"${DATE}%\"" diff --git a/servatrice/scripts/mysql.cnf b/servatrice/scripts/mysql.cnf new file mode 100644 index 000000000..e5d901be6 --- /dev/null +++ b/servatrice/scripts/mysql.cnf @@ -0,0 +1,3 @@ +[client] +user={db_username} +password={db_password} From b27873667b87e55a4df1dc27aa7cf71cafbbc820 Mon Sep 17 00:00:00 2001 From: woogerboy21 Date: Wed, 2 Jul 2014 11:11:17 -0400 Subject: [PATCH 09/82] Optimization Updates for performance improvements per request --- servatrice/scripts/maint_replays | 7 ++----- servatrice/scripts/maint_sessions | 8 ++------ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/servatrice/scripts/maint_replays b/servatrice/scripts/maint_replays index b1326d9b2..1ce1b400d 100644 --- a/servatrice/scripts/maint_replays +++ b/servatrice/scripts/maint_replays @@ -1,6 +1,3 @@ #!/bin/bash -#RUN THIS SCRIPT TO REMOVE ALL COCKATRICE REPLAYS FROM THE DATABASE -#--CAN BE SCHEDULED WITH CRONTAB - -mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays' -mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays_access' +# SCHEDULE WITH CRONTAB BASED ON TIME PERIOD REPLAYS SHOULD BE SAVED UNTIL (EX: SCHEDULE ONCE A WEEK TO KEEP A WEEKS WORTH OF REPLAYS IN THE DB) +mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays;truncate table servatrice.cockatrice_replays_access' diff --git a/servatrice/scripts/maint_sessions b/servatrice/scripts/maint_sessions index 1fabd68f1..2c41da6da 100644 --- a/servatrice/scripts/maint_sessions +++ b/servatrice/scripts/maint_sessions @@ -1,7 +1,3 @@ #!/bin/bash -#REPLACE THE --date="1 month ago" WITH THE TIME FRAME TO CLEAR UP -#EX: --date="1 day ago" (FOR SINGLE DAY) -#--CAN BE SCHEDULED WITH CRONTAB - -DATE=$(date +%Y-%m --date="1 month ago") -mysql --defaults-file=./mysql.cnf -h localhost -e "delete from servatrice.cockatrice_sessions where start_time like \"${DATE}%\"" +# SCHEDULE WITH CRONTAB TO RUN ONCE A MONTH +mysql --defaults-file=./mysql.cnf -h localhost -e "delete from servatrice.cockatrice_sessions where start_time < DATE_SUB(now(), INTERVAL 1 MONTH)" From 9ba74fee5c16b0e1d625947066bd4a64c31afa2c Mon Sep 17 00:00:00 2001 From: woogerboy21 Date: Wed, 2 Jul 2014 13:23:50 -0400 Subject: [PATCH 10/82] Example Update Renamed mysql configuration file to include example extension and set git to ignore mysql.cnf per request. --- .gitignore | 1 + servatrice/scripts/{mysql.cnf => mysql.cnf.example} | 0 2 files changed, 1 insertion(+) rename servatrice/scripts/{mysql.cnf => mysql.cnf.example} (100%) diff --git a/.gitignore b/.gitignore index 1753f78c6..df1a5c9fe 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ tags build* *.qm .directory +mysql.cnf diff --git a/servatrice/scripts/mysql.cnf b/servatrice/scripts/mysql.cnf.example similarity index 100% rename from servatrice/scripts/mysql.cnf rename to servatrice/scripts/mysql.cnf.example From a9ffbf5d2db104aabbc7ea7ec27cfe45813ccafb Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 19:03:12 +0200 Subject: [PATCH 11/82] use a signal instead of calling QMetaObject::invokeMethod --- cockatrice/src/dlg_settings.cpp | 4 +++- cockatrice/src/dlg_settings.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index cf8fa846b..0d36dd185 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -549,6 +549,8 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); connect(priceTagSource1, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); + connect(this, SIGNAL(priceTagSourceChanged(int)), settingsCache, SLOT(setPriceTagSource(int))); + QGridLayout *generalGrid = new QGridLayout; generalGrid->addWidget(priceTagsCheckBox, 0, 0); generalGrid->addWidget(priceTagSource0, 1, 0); @@ -582,7 +584,7 @@ void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked) if(priceTagSource1->isChecked()) source=1; - QMetaObject::invokeMethod( settingsCache, "setPriceTagSource", Qt::QueuedConnection, Q_ARG(int, source)); + emit priceTagSourceChanged(source); } MessagesSettingsPage::MessagesSettingsPage() diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 803e0dfc3..55685e1e9 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -103,6 +103,8 @@ public: void retranslateUi(); private slots: void radioPriceTagSourceClicked(bool checked); +signals: + void priceTagSourceChanged(int _priceTagSource); private: QCheckBox *priceTagsCheckBox; QRadioButton *priceTagSource0, *priceTagSource1; From 9ca5908de5507de5a7c30e767d80c4dca6994844 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 19:13:05 +0200 Subject: [PATCH 12/82] Create an enum for price sources Probably settingscache would have been the correct place for the enum, but keeping it in the same class/file where price sources are defined sounds cleaner to me --- cockatrice/src/dlg_settings.cpp | 11 ++++++----- cockatrice/src/priceupdater.h | 2 ++ cockatrice/src/tab_deck_editor.cpp | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 0d36dd185..757c719ad 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -24,6 +24,7 @@ #include "dlg_settings.h" #include "main.h" #include "settingscache.h" +#include "priceupdater.h" GeneralSettingsPage::GeneralSettingsPage() { @@ -538,10 +539,10 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() switch(settingsCache->getPriceTagSource()) { - case 0: + case AbstractPriceUpdater::BLPPriceSource: priceTagSource0->setChecked(true); break; - case 1: + case AbstractPriceUpdater::DBPriceSource: priceTagSource1->setChecked(true); break; } @@ -578,11 +579,11 @@ void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked) if(!checked) return; - int source=0; + int source=AbstractPriceUpdater::BLPPriceSource; if(priceTagSource0->isChecked()) - source=0; + source=AbstractPriceUpdater::BLPPriceSource; if(priceTagSource1->isChecked()) - source=1; + source=AbstractPriceUpdater::DBPriceSource; emit priceTagSourceChanged(source); } diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index 901471f55..642c75c75 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -14,6 +14,8 @@ class QNetworkAccessManager; class AbstractPriceUpdater : public QWidget { Q_OBJECT +public: + enum PriceSource { BLPPriceSource, DBPriceSource }; protected: const DeckList *deck; QNetworkAccessManager *nam; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 5706ffd6c..71fd9cbd4 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -648,10 +648,10 @@ void TabDeckEditor::actUpdatePrices() switch(settingsCache->getPriceTagSource()) { - case 0: + case AbstractPriceUpdater::BLPPriceSource: up = new BLPPriceUpdater(deckModel->getDeckList()); break; - case 1: + case AbstractPriceUpdater::DBPriceSource: up = new DBPriceUpdater(deckModel->getDeckList()); break; } From d102733826b636d3683395a12ab1b91807adf50d Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 19:24:51 +0200 Subject: [PATCH 13/82] Added default cases to switch --- cockatrice/src/dlg_settings.cpp | 7 ++++--- cockatrice/src/tab_deck_editor.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 757c719ad..0950ec601 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -539,12 +539,13 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() switch(settingsCache->getPriceTagSource()) { - case AbstractPriceUpdater::BLPPriceSource: - priceTagSource0->setChecked(true); - break; case AbstractPriceUpdater::DBPriceSource: priceTagSource1->setChecked(true); break; + case AbstractPriceUpdater::BLPPriceSource: + default: + priceTagSource0->setChecked(true); + break; } connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 71fd9cbd4..5a3d5e779 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -648,12 +648,13 @@ void TabDeckEditor::actUpdatePrices() switch(settingsCache->getPriceTagSource()) { - case AbstractPriceUpdater::BLPPriceSource: - up = new BLPPriceUpdater(deckModel->getDeckList()); - break; case AbstractPriceUpdater::DBPriceSource: up = new DBPriceUpdater(deckModel->getDeckList()); break; + case AbstractPriceUpdater::BLPPriceSource: + default: + up = new BLPPriceUpdater(deckModel->getDeckList()); + break; } connect(up, SIGNAL(finishedUpdate()), this, SLOT(finishedUpdatingPrices())); From 06def7f46ae74fbce5020dc7317e9459ef28cada Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 21:40:41 +0200 Subject: [PATCH 14/82] Ensure all card prices are retrieved correctly Query card database to get a multiverseis for a card. Get card price using that id instead of card name. Tested Aether, split cards, question elemental?, _____ --- cockatrice/src/priceupdater.cpp | 32 ++++++++++++++++++++++++-------- cockatrice/src/priceupdater.h | 5 +++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index e5b359df9..1cf2b0642 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -9,6 +9,9 @@ #include "qt-json/json.h" #include "priceupdater.h" +#include "main.h" +#include "carddatabase.h" + #if QT_VERSION < 0x050000 // for Qt::escape() #include @@ -120,10 +123,16 @@ void DBPriceUpdater::updatePrices() { QString q = "https://api.deckbrew.com/mtg/cards"; QStringList cards = deck->getCardList(); + muidMap.clear(); + CardInfo * card; + int muid; for (int i = 0; i < cards.size(); ++i) { - q += (i ? "&" : "?") + QString("name=") + cards[i].toLower(); + card = db->getCard(cards[i], false); + muid=card->getPreferredMuId(); + q += (i ? "&" : "?") + QString("multiverseid=%1").arg(muid); + muidMap.insert(muid, cards[i]); } - QUrl url(q.replace(' ', '+')); + QUrl url(q); QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); @@ -172,17 +181,24 @@ void DBPriceUpdater::downloadFinished() } QMap cardsPrice; - QListIterator it(resultList); while (it.hasNext()) { - QVariantMap map = it.next().toMap(); - QString name = map.value("name").toString().toLower(); + QVariantMap cardMap = it.next().toMap(); - QList editions = map.value("editions").toList(); + + // get sets list + QList editions = cardMap.value("editions").toList(); foreach (QVariant ed, editions) { + // retrieve card name "as we know it" from the muid QVariantMap edition = ed.toMap(); QString set = edition.value("set_id").toString(); + + int muid = edition.value("multiverse_id").toString().toInt(); + if(!muidMap.contains(muid)) + continue; + + QString name=muidMap.value(muid); // Prices are in USD cents float price = edition.value("price").toMap().value("median").toString().toFloat() / 100; //qDebug() << "card " << name << " set " << set << " price " << price << endl; @@ -196,7 +212,7 @@ void DBPriceUpdater::downloadFinished() cardsPrice.insert(name, price); } } - + InnerDecklistNode *listRoot = deck->getRoot(); for (int i = 0; i < listRoot->size(); i++) { InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); @@ -204,7 +220,7 @@ void DBPriceUpdater::downloadFinished() DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); if (!currentCard) continue; - currentCard->setPrice(cardsPrice[currentCard->getName().toLower()]); + currentCard->setPrice(cardsPrice[currentCard->getName()]); } } diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index 642c75c75..076d9663d 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -6,6 +6,9 @@ class QNetworkAccessManager; +// If we don't typedef this, won't compile on OS X < 10.9 +typedef QMap MuidStringMap; + /** * Price Updater. * @@ -41,6 +44,8 @@ public: class DBPriceUpdater : public AbstractPriceUpdater { Q_OBJECT +protected: + MuidStringMap muidMap; protected: virtual void downloadFinished(); public: From 6f18e3692dbddb2f4efca9f02374a76e3a51fa6b Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 3 Jul 2014 15:45:13 +0200 Subject: [PATCH 15/82] Chat room: ensure the line input has focus when the window gets created --- cockatrice/src/tab_room.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index 2af3dd1e3..a7514e792 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" @@ -100,7 +101,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI setFocusProxy(sayEdit); chatView->setFocusProxy(sayEdit); - sayEdit->setFocus(); + QTimer::singleShot(0, sayEdit, SLOT(setFocus())); } TabRoom::~TabRoom() From aead0843af9207a170282cd21a84df9c9870e6a6 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 6 Jul 2014 14:27:35 +0200 Subject: [PATCH 16/82] Room and Message tabs: focus input line when convenient; Fix #180 As suggest by Daenyth in #185 --- cockatrice/src/tab.h | 1 + cockatrice/src/tab_message.cpp | 10 ++++++---- cockatrice/src/tab_message.h | 1 + cockatrice/src/tab_room.cpp | 11 ++++++----- cockatrice/src/tab_room.h | 1 + cockatrice/src/tab_supervisor.cpp | 1 + 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/tab.h b/cockatrice/src/tab.h index 654731212..1b8b77049 100644 --- a/cockatrice/src/tab.h +++ b/cockatrice/src/tab.h @@ -31,6 +31,7 @@ public: virtual QString getTabText() const = 0; virtual void retranslateUi() = 0; virtual void closeRequest() { } + virtual void tabActivated() { } }; #endif diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index eec46bb96..6369f2a66 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -34,10 +34,6 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, c retranslateUi(); setLayout(vbox); - - setFocusProxy(sayEdit); - chatView->setFocusProxy(sayEdit); - sayEdit->setFocus(); } TabMessage::~TabMessage() @@ -53,6 +49,12 @@ void TabMessage::retranslateUi() aLeave->setText(tr("&Leave")); } +void TabMessage::tabActivated() +{ + if(!sayEdit->hasFocus()) + sayEdit->setFocus(); +} + QString TabMessage::getUserName() const { return QString::fromStdString(otherUserInfo->name()); diff --git a/cockatrice/src/tab_message.h b/cockatrice/src/tab_message.h index b8f989599..80263975b 100644 --- a/cockatrice/src/tab_message.h +++ b/cockatrice/src/tab_message.h @@ -34,6 +34,7 @@ public: ~TabMessage(); void retranslateUi(); void closeRequest(); + void tabActivated(); QString getUserName() const; QString getTabText() const; diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index a7514e792..a4bb9ce0e 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" @@ -98,10 +97,6 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI const int gameListSize = info.game_list_size(); for (int i = 0; i < gameListSize; ++i) gameSelector->processGameInfo(info.game_list(i)); - - setFocusProxy(sayEdit); - chatView->setFocusProxy(sayEdit); - QTimer::singleShot(0, sayEdit, SLOT(setFocus())); } TabRoom::~TabRoom() @@ -126,6 +121,12 @@ void TabRoom::closeRequest() actLeaveRoom(); } +void TabRoom::tabActivated() +{ + if(!sayEdit->hasFocus()) + sayEdit->setFocus(); +} + QString TabRoom::sanitizeHtml(QString dirty) const { return dirty diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index ada3aca88..857f37a25 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -64,6 +64,7 @@ public: ~TabRoom(); void retranslateUi(); void closeRequest(); + void tabActivated(); void processRoomEvent(const RoomEvent &event); int getRoomId() const { return roomId; } const QMap &getGameTypes() const { return gameTypes; } diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index e85efc6ad..dda918a04 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -479,6 +479,7 @@ void TabSupervisor::updateCurrent(int index) tab->setContentsChanged(false); } emit setMenu(static_cast(widget(index))->getTabMenus()); + tab->tabActivated(); } else emit setMenu(); } From 04ad73fcdc5f52e523743c95145f4b0bf03e9609 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 6 Jul 2014 23:25:44 +0200 Subject: [PATCH 17/82] Workaround for qt5 version compiled with -reduce-relocations --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cb173173..825ed5cb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,11 @@ ENDIF() IF(Qt5Widgets_FOUND) MESSAGE(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}") + + if(UNIX AND NOT APPLE AND "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + # FIX: Qt was built with -reduce-relocations + add_definitions(-fPIC) + endif() ELSE() FIND_PACKAGE(Qt4 4.8.0 REQUIRED) IF(QT4_FOUND) From 810029ce15cf1854b28fd99df150303ccdefe659 Mon Sep 17 00:00:00 2001 From: arxanas Date: Sun, 6 Jul 2014 20:04:09 -0400 Subject: [PATCH 18/82] Fix #117: Card tags now match disregarding case and punctuation. --- cockatrice/src/carddatabase.cpp | 110 ++++++++++++++++++------------ cockatrice/src/carddatabase.h | 44 +++++++++++- cockatrice/src/cardinfowidget.cpp | 12 ++-- cockatrice/src/cardinfowidget.h | 5 ++ oracle/src/oracleimporter.cpp | 10 +-- 5 files changed, 127 insertions(+), 54 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f94..c55148aa0 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -434,6 +434,13 @@ int CardInfo::getPreferredMuId() return muIds[getPreferredSet()->getShortName()]; } +QString CardInfo::simplifyName(const QString &name) { + QString simpleName(name); + simpleName.remove(QRegExp("[^a-zA-Z0-9 ]")); + simpleName = simpleName.toLower(); + return simpleName; +} + static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) { xml.writeStartElement("card"); @@ -507,55 +514,57 @@ CardDatabase::~CardDatabase() void CardDatabase::clear() { - QHashIterator setIt(setHash); + QHashIterator setIt(sets); while (setIt.hasNext()) { setIt.next(); delete setIt.value(); } - setHash.clear(); + sets.clear(); - QHashIterator i(cardHash); + QHashIterator i(cards); while (i.hasNext()) { i.next(); delete i.value(); } - cardHash.clear(); + cards.clear(); + + // The pointers themselves were already deleted, so we don't delete them + // again. + simpleNameCards.clear(); } void CardDatabase::addCard(CardInfo *card) { - cardHash.insert(card->getName(), card); + cards.insert(card->getName(), card); + simpleNameCards.insert(CardInfo::simplifyName(card->getName()), card); emit cardAdded(card); } void CardDatabase::removeCard(CardInfo *card) { - cardHash.remove(card->getName()); + cards.remove(card->getName()); + simpleNameCards.remove(CardInfo::simplifyName(card->getName())); emit cardRemoved(card); } -CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) -{ - if (cardName.isEmpty()) - return noCard; - else if (cardHash.contains(cardName)) - return cardHash.value(cardName); - else if (createIfNotFound) { - CardInfo *newCard = new CardInfo(this, cardName, true); - newCard->addToSet(getSet("TK")); - cardHash.insert(cardName, newCard); - return newCard; - } else - return 0; +CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) { + return getCardFromMap(cards, cardName, createIfNotFound); +} + +CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { + QString simpleName = CardInfo::simplifyName(cardName); + qDebug() << "Getting card by name " << simpleName << "\n"; + qDebug() << "Cards available: " << simpleNameCards.size() << "\n"; + return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); } CardSet *CardDatabase::getSet(const QString &setName) { - if (setHash.contains(setName)) - return setHash.value(setName); + if (sets.contains(setName)) + return sets.value(setName); else { CardSet *newSet = new CardSet(setName); - setHash.insert(setName, newSet); + sets.insert(setName, newSet); return newSet; } } @@ -563,7 +572,7 @@ CardSet *CardDatabase::getSet(const QString &setName) SetList CardDatabase::getSetList() const { SetList result; - QHashIterator i(setHash); + QHashIterator i(sets); while (i.hasNext()) { i.next(); result << i.value(); @@ -573,7 +582,9 @@ SetList CardDatabase::getSetList() const void CardDatabase::clearPixmapCache() { - QHashIterator i(cardHash); + // This also clears the cards in simpleNameCards since they point to the + // same object. + QHashIterator i(cards); while (i.hasNext()) { i.next(); i.value()->clearPixmapCache(); @@ -597,7 +608,7 @@ void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) else if (xml.name() == "longname") longName = xml.readElementText(); } - setHash.insert(shortName, new CardSet(shortName, longName)); + sets.insert(shortName, new CardSet(shortName, longName)); } } } @@ -647,11 +658,25 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) else if (xml.name() == "token") isToken = xml.readElementText().toInt(); } - cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids)); + addCard(new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids)); } } } +CardInfo *CardDatabase::getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound) { + if (cardName.isEmpty()) + return noCard; + else if (cardMap.contains(cardName)) + return cardMap.value(cardName); + else if (createIfNotFound) { + CardInfo *newCard = new CardInfo(this, cardName, true); + newCard->addToSet(getSet("TK")); + cardMap.insert(cardName, newCard); + return newCard; + } else + return 0; +} + LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) { QFile file(fileName); @@ -660,31 +685,32 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) return FileError; if (tokens) { - QMutableHashIterator i(cardHash); + QMutableHashIterator i(cards); while (i.hasNext()) { i.next(); if (i.value()->getIsToken()) { + removeCard(i.value()); delete i.value(); - i.remove(); } } } else { - QHashIterator setIt(setHash); + QHashIterator setIt(sets); while (setIt.hasNext()) { setIt.next(); delete setIt.value(); } - setHash.clear(); + sets.clear(); - QMutableHashIterator i(cardHash); + QMutableHashIterator i(cards); while (i.hasNext()) { i.next(); if (!i.value()->getIsToken()) { + removeCard(i.value()); delete i.value(); - i.remove(); } } - cardHash.clear(); + cards.clear(); + simpleNameCards.clear(); } QXmlStreamReader xml(&file); @@ -707,9 +733,9 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) } } } - qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded"; + qDebug() << cards.size() << "cards in" << sets.size() << "sets loaded"; - if (cardHash.isEmpty()) return NoCards; + if (cards.isEmpty()) return NoCards; return Ok; } @@ -728,14 +754,14 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) if (!tokens) { xml.writeStartElement("sets"); - QHashIterator setIterator(setHash); + QHashIterator setIterator(sets); while (setIterator.hasNext()) xml << setIterator.next().value(); xml.writeEndElement(); // sets } xml.writeStartElement("cards"); - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) { CardInfo *card = cardIterator.next().value(); if (card->getIsToken() == tokens) @@ -753,7 +779,7 @@ void CardDatabase::picDownloadChanged() { pictureLoader->setPicDownload(settingsCache->getPicDownload()); if (settingsCache->getPicDownload()) { - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } @@ -763,7 +789,7 @@ void CardDatabase::picDownloadHqChanged() { pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); if (settingsCache->getPicDownloadHq()) { - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } @@ -777,7 +803,7 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) if (tempLoadStatus == Ok) { SetList allSets; - QHashIterator setsIterator(setHash); + QHashIterator setsIterator(sets); while (setsIterator.hasNext()) allSets.append(setsIterator.next().value()); allSets.sortByKey(); @@ -809,7 +835,7 @@ void CardDatabase::loadTokenDatabase() QStringList CardDatabase::getAllColors() const { QSet colors; - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) { const QStringList &cardColors = cardIterator.next().value()->getColors(); if (cardColors.isEmpty()) @@ -824,7 +850,7 @@ QStringList CardDatabase::getAllColors() const QStringList CardDatabase::getAllMainCardTypes() const { QSet types; - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) types.insert(cardIterator.next().value()->getMainCardType()); return types.toList(); diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 09edeff4a..50a4d1b7c 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -95,6 +95,13 @@ private: CardDatabase *db; QString name; + + /* + * The name without punctuation or capitalization, for better card tag name + * recognition. + */ + QString simpleName; + bool isToken; SetList sets; QString manacost; @@ -153,6 +160,12 @@ public: void imageLoaded(const QImage &image); CardSet *getPreferredSet(); int getPreferredMuId(); + + /** + * Simplify a name to have no punctuation and lowercase all letters, for + * less strict name-matching. + */ + static QString simplifyName(const QString &name); public slots: void updatePixmapCache(); signals: @@ -162,11 +175,27 @@ signals: enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards }; +typedef QHash CardNameMap; +typedef QHash SetNameMap; + class CardDatabase : public QObject { Q_OBJECT protected: - QHash cardHash; - QHash setHash; + /* + * The cards, indexed by name. + */ + CardNameMap cards; + + /** + * The cards, indexed by their simple name. + */ + CardNameMap simpleNameCards; + + /* + * The sets, indexed by short name. + */ + SetNameMap sets; + CardInfo *noCard; QThread *pictureLoaderThread; @@ -176,6 +205,8 @@ private: static const int versionNeeded; void loadCardsFromXml(QXmlStreamReader &xml); void loadSetsFromXml(QXmlStreamReader &xml); + + CardInfo *getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound); public: CardDatabase(QObject *parent = 0); ~CardDatabase(); @@ -183,8 +214,15 @@ public: void addCard(CardInfo *card); void removeCard(CardInfo *card); CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = true); + + /* + * Get a card by its simple name. The name will be simplified in this + * function, so you don't need to simplify it beforehand. + */ + CardInfo *getCardBySimpleName(const QString &cardName = QString(), bool createIfNotFound = true); + CardSet *getSet(const QString &setName); - QList getCardList() const { return cardHash.values(); } + QList getCardList() const { return cards.values(); } SetList getSetList() const; LoadStatus loadFromFile(const QString &fileName, bool tokens = false); bool saveToFile(const QString &fileName, bool tokens = false); diff --git a/cockatrice/src/cardinfowidget.cpp b/cockatrice/src/cardinfowidget.cpp index 6b5e995a0..21b00a131 100644 --- a/cockatrice/src/cardinfowidget.cpp +++ b/cockatrice/src/cardinfowidget.cpp @@ -81,7 +81,7 @@ CardInfoWidget::CardInfoWidget(ResizeMode _mode, const QString &cardName, QWidge } else setFixedWidth(250); - setCard(db->getCard(cardName)); + setCard(getCard(cardName)); setMinimized(settingsCache->getCardInfoMinimized()); } @@ -166,7 +166,7 @@ void CardInfoWidget::setCard(CardInfo *card) void CardInfoWidget::setCard(const QString &cardName) { - setCard(db->getCard(cardName)); + setCard(getCard(cardName)); } void CardInfoWidget::setCard(AbstractCardItem *card) @@ -176,7 +176,11 @@ void CardInfoWidget::setCard(AbstractCardItem *card) void CardInfoWidget::clear() { - setCard(db->getCard()); + setCard(getCard()); +} + +CardInfo *CardInfoWidget::getCard(const QString &cardName) { + return db->getCardBySimpleName(cardName); } void CardInfoWidget::updatePixmap() @@ -188,7 +192,7 @@ void CardInfoWidget::updatePixmap() if (resizedPixmap) cardPicture->setPixmap(*resizedPixmap); else - cardPicture->setPixmap(*(db->getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); + cardPicture->setPixmap(*(getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); } void CardInfoWidget::retranslateUi() diff --git a/cockatrice/src/cardinfowidget.h b/cockatrice/src/cardinfowidget.h index ecfd6eca7..b9988a7a1 100644 --- a/cockatrice/src/cardinfowidget.h +++ b/cockatrice/src/cardinfowidget.h @@ -42,6 +42,11 @@ private: CardInfo *info; void setMinimized(int _minimized); + /* + * Wrapper around db->getCardBySimpleName. + */ + CardInfo *getCard(const QString &cardName = QString()); + public: CardInfoWidget(ResizeMode _mode, const QString &cardName = QString(), QWidget *parent = 0, Qt::WindowFlags f = 0); void retranslateUi(); diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 70972d92d..cdc510957 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -76,8 +76,8 @@ CardInfo *OracleImporter::addCard(const QString &setName, cardCost.remove(QChar('}')); CardInfo *card; - if (cardHash.contains(cardName)) { - card = cardHash.value(cardName); + if (cards.contains(cardName)) { + card = cards.value(cardName); if (splitCard && !card->getText().contains(fullCardText)) card->setText(card->getText() + "\n---\n" + fullCardText); } else { @@ -117,7 +117,7 @@ CardInfo *OracleImporter::addCard(const QString &setName, tableRow = 2; card->setTableRow(tableRow); - cardHash.insert(cardName, card); + cards.insert(cardName, card); } card->setMuId(setName, cardId); @@ -225,8 +225,8 @@ int OracleImporter::startImport() continue; CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName()); - if (!setHash.contains(set->getShortName())) - setHash.insert(set->getShortName(), set); + if (!sets.contains(set->getShortName())) + sets.insert(set->getShortName(), set); int setCards = importTextSpoiler(set, curSet->getCards()); From 8141901679fe8c66d8ea079f441546c0a81693ef Mon Sep 17 00:00:00 2001 From: arxanas Date: Mon, 7 Jul 2014 17:31:42 -0400 Subject: [PATCH 19/82] Fix #137: Game filter preferences are saved. --- cockatrice/src/dlg_filter_games.cpp | 76 ++++++++++++++++++++++++++--- cockatrice/src/dlg_filter_games.h | 4 ++ cockatrice/src/gameselector.cpp | 6 --- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/dlg_filter_games.cpp b/cockatrice/src/dlg_filter_games.cpp index 5e4022945..b4ce11c83 100644 --- a/cockatrice/src/dlg_filter_games.cpp +++ b/cockatrice/src/dlg_filter_games.cpp @@ -9,26 +9,52 @@ #include #include #include +#include -DlgFilterGames::DlgFilterGames(const QMap &allGameTypes, QWidget *parent) - : QDialog(parent) +DlgFilterGames::DlgFilterGames(const QMap &_allGameTypes, QWidget *parent) + : QDialog(parent), + allGameTypes(_allGameTypes) { + QSettings settings; + settings.beginGroup("filter_games"); + unavailableGamesVisibleCheckBox = new QCheckBox(tr("Show &unavailable games")); + unavailableGamesVisibleCheckBox->setChecked( + settings.value("unavailable_games_visible", false).toBool() + ); + passwordProtectedGamesVisibleCheckBox = new QCheckBox(tr("Show &password protected games")); + passwordProtectedGamesVisibleCheckBox->setChecked( + settings.value("password_protected_games_visible", false).toBool() + ); - QLabel *gameNameFilterLabel = new QLabel(tr("Game &description:")); gameNameFilterEdit = new QLineEdit; + gameNameFilterEdit->setText( + settings.value("game_name_filter", "").toString() + ); + QLabel *gameNameFilterLabel = new QLabel(tr("Game &description:")); gameNameFilterLabel->setBuddy(gameNameFilterEdit); - QLabel *creatorNameFilterLabel = new QLabel(tr("&Creator name:")); creatorNameFilterEdit = new QLineEdit; + creatorNameFilterEdit->setText( + settings.value("creator_name_filter", "").toString() + ); + QLabel *creatorNameFilterLabel = new QLabel(tr("&Creator name:")); creatorNameFilterLabel->setBuddy(creatorNameFilterEdit); QVBoxLayout *gameTypeFilterLayout = new QVBoxLayout; QMapIterator gameTypesIterator(allGameTypes); while (gameTypesIterator.hasNext()) { gameTypesIterator.next(); + QCheckBox *temp = new QCheckBox(gameTypesIterator.value()); + temp->setChecked( + settings.value( + "game_type/" + gameTypesIterator.value(), + false + ).toBool() + ); + gameTypeFilterCheckBoxes.insert(gameTypesIterator.key(), temp); gameTypeFilterLayout->addWidget(temp); } @@ -43,14 +69,18 @@ DlgFilterGames::DlgFilterGames(const QMap &allGameTypes, QWidget * maxPlayersFilterMinSpinBox = new QSpinBox; maxPlayersFilterMinSpinBox->setMinimum(1); maxPlayersFilterMinSpinBox->setMaximum(99); - maxPlayersFilterMinSpinBox->setValue(1); + maxPlayersFilterMinSpinBox->setValue( + settings.value("min_players", 1).toInt() + ); maxPlayersFilterMinLabel->setBuddy(maxPlayersFilterMinSpinBox); QLabel *maxPlayersFilterMaxLabel = new QLabel(tr("at &most:")); maxPlayersFilterMaxSpinBox = new QSpinBox; maxPlayersFilterMaxSpinBox->setMinimum(1); maxPlayersFilterMaxSpinBox->setMaximum(99); - maxPlayersFilterMaxSpinBox->setValue(99); + maxPlayersFilterMaxSpinBox->setValue( + settings.value("max_players", 99).toInt() + ); maxPlayersFilterMaxLabel->setBuddy(maxPlayersFilterMaxSpinBox); QGridLayout *maxPlayersFilterLayout = new QGridLayout; @@ -83,7 +113,7 @@ DlgFilterGames::DlgFilterGames(const QMap &allGameTypes, QWidget * hbox->addLayout(rightColumn); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout *mainLayout = new QVBoxLayout; @@ -94,6 +124,38 @@ DlgFilterGames::DlgFilterGames(const QMap &allGameTypes, QWidget * setWindowTitle(tr("Filter games")); } +void DlgFilterGames::actOk() { + QSettings settings; + settings.beginGroup("filter_games"); + settings.setValue( + "unavailable_games_visible", + unavailableGamesVisibleCheckBox->isChecked() + ); + settings.setValue( + "password_protected_games_visible", + passwordProtectedGamesVisibleCheckBox->isChecked() + ); + settings.setValue("game_name_filter", gameNameFilterEdit->text()); + settings.setValue("creator_name_filter", creatorNameFilterEdit->text()); + + QMapIterator gameTypeIterator(allGameTypes); + QMapIterator checkboxIterator(gameTypeFilterCheckBoxes); + while (gameTypeIterator.hasNext()) { + gameTypeIterator.next(); + checkboxIterator.next(); + + settings.setValue( + "game_type/" + gameTypeIterator.value(), + checkboxIterator.value()->isChecked() + ); + } + + settings.setValue("min_players", maxPlayersFilterMinSpinBox->value()); + settings.setValue("max_players", maxPlayersFilterMaxSpinBox->value()); + + accept(); +} + bool DlgFilterGames::getUnavailableGamesVisible() const { return unavailableGamesVisibleCheckBox->isChecked(); diff --git a/cockatrice/src/dlg_filter_games.h b/cockatrice/src/dlg_filter_games.h index 8059da456..ac727d19b 100644 --- a/cockatrice/src/dlg_filter_games.h +++ b/cockatrice/src/dlg_filter_games.h @@ -19,6 +19,10 @@ private: QMap gameTypeFilterCheckBoxes; QSpinBox *maxPlayersFilterMinSpinBox; QSpinBox *maxPlayersFilterMaxSpinBox; + + const QMap &allGameTypes; +private slots: + void actOk(); public: DlgFilterGames(const QMap &allGameTypes, QWidget *parent = 0); diff --git a/cockatrice/src/gameselector.cpp b/cockatrice/src/gameselector.cpp index 6a05d9058..f5ef9ec87 100644 --- a/cockatrice/src/gameselector.cpp +++ b/cockatrice/src/gameselector.cpp @@ -81,12 +81,6 @@ void GameSelector::actSetFilter() if (room) gameTypeMap = gameListModel->getGameTypes().value(room->getRoomId()); DlgFilterGames dlg(gameTypeMap, this); - dlg.setUnavailableGamesVisible(gameListProxyModel->getUnavailableGamesVisible()); - dlg.setPasswordProtectedGamesVisible(gameListProxyModel->getPasswordProtectedGamesVisible()); - dlg.setGameNameFilter(gameListProxyModel->getGameNameFilter()); - dlg.setCreatorNameFilter(gameListProxyModel->getCreatorNameFilter()); - dlg.setGameTypeFilter(gameListProxyModel->getGameTypeFilter()); - dlg.setMaxPlayersFilter(gameListProxyModel->getMaxPlayersFilterMin(), gameListProxyModel->getMaxPlayersFilterMax()); if (!dlg.exec()) return; From 1217689ed63f5721f5ab6310f0f31cb933790a1a Mon Sep 17 00:00:00 2001 From: arxanas Date: Mon, 7 Jul 2014 19:04:01 -0400 Subject: [PATCH 20/82] Hash the game type. --- cockatrice/src/dlg_filter_games.cpp | 9 +++++++-- cockatrice/src/dlg_filter_games.h | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/dlg_filter_games.cpp b/cockatrice/src/dlg_filter_games.cpp index b4ce11c83..eb2e8f78e 100644 --- a/cockatrice/src/dlg_filter_games.cpp +++ b/cockatrice/src/dlg_filter_games.cpp @@ -10,6 +10,7 @@ #include #include #include +#include DlgFilterGames::DlgFilterGames(const QMap &_allGameTypes, QWidget *parent) : QDialog(parent), @@ -50,7 +51,7 @@ DlgFilterGames::DlgFilterGames(const QMap &_allGameTypes, QWidget QCheckBox *temp = new QCheckBox(gameTypesIterator.value()); temp->setChecked( settings.value( - "game_type/" + gameTypesIterator.value(), + "game_type/" + hashGameType(gameTypesIterator.value()), false ).toBool() ); @@ -145,7 +146,7 @@ void DlgFilterGames::actOk() { checkboxIterator.next(); settings.setValue( - "game_type/" + gameTypeIterator.value(), + "game_type/" + hashGameType(gameTypeIterator.value()), checkboxIterator.value()->isChecked() ); } @@ -156,6 +157,10 @@ void DlgFilterGames::actOk() { accept(); } +QString DlgFilterGames::hashGameType(const QString &gameType) const { + return QCryptographicHash::hash(gameType.toUtf8(), QCryptographicHash::Md5).toHex(); +} + bool DlgFilterGames::getUnavailableGamesVisible() const { return unavailableGamesVisibleCheckBox->isChecked(); diff --git a/cockatrice/src/dlg_filter_games.h b/cockatrice/src/dlg_filter_games.h index ac727d19b..2480e642f 100644 --- a/cockatrice/src/dlg_filter_games.h +++ b/cockatrice/src/dlg_filter_games.h @@ -21,6 +21,12 @@ private: QSpinBox *maxPlayersFilterMaxSpinBox; const QMap &allGameTypes; + + /* + * The game type might contain special characters, so to use it in + * QSettings we just hash it. + */ + QString hashGameType(const QString &gameType) const; private slots: void actOk(); public: From d894c5326566d5ba56925cfed0358c8cd787da3c Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 8 Jul 2014 22:05:06 +0200 Subject: [PATCH 21/82] Servatrice: fix handling of incoming connections on qt5 --- servatrice/src/servatrice.cpp | 4 ++++ servatrice/src/servatrice.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 87eada780..30c88cfdb 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -78,7 +78,11 @@ Servatrice_GameServer::~Servatrice_GameServer() } } +#if QT_VERSION < 0x050000 void Servatrice_GameServer::incomingConnection(int socketDescriptor) +#else +void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor) +#endif { // Determine connection pool with smallest client count int minClientCount = -1; diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index fbfac0350..2b991512f 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -52,7 +52,11 @@ public: Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent = 0); ~Servatrice_GameServer(); protected: +#if QT_VERSION < 0x050000 void incomingConnection(int socketDescriptor); +#else + void incomingConnection(qintptr socketDescriptor); +#endif }; class Servatrice_IslServer : public QTcpServer { From 1a601d9509f191bda6d60371990c65f619bb273c Mon Sep 17 00:00:00 2001 From: pliu037 Date: Tue, 8 Jul 2014 16:24:51 -0400 Subject: [PATCH 22/82] Added better support for custom card images by prioritizing a check for card images in / before all other checks. --- cockatrice/src/carddatabase.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f94..98e506b5b 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -129,21 +129,22 @@ void PictureLoader::processLoadQueue() QString setName = ptl.getSetName(); QImage image; - if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) - if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1))) - if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { - if (picDownload) { - cardsToDownload.append(ptl); - if (!downloadRunning) - startNextPicDownload(); - } else { - if (ptl.nextSet()) - loadQueue.prepend(ptl); - else - emit imageLoaded(ptl.getCard(), QImage()); + if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg("CUSTOM").arg(correctedName))) + if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) + //if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1))) + if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { + if (picDownload) { + cardsToDownload.append(ptl); + if (!downloadRunning) + startNextPicDownload(); + } else { + if (ptl.nextSet()) + loadQueue.prepend(ptl); + else + emit imageLoaded(ptl.getCard(), QImage()); + } + continue; } - continue; - } emit imageLoaded(ptl.getCard(), image); } From 4e8ba699d2a003db734b134a51049fce3baecd39 Mon Sep 17 00:00:00 2001 From: arxanas Date: Tue, 8 Jul 2014 22:53:02 -0400 Subject: [PATCH 23/82] Tabs to spaces. --- common/color.h | 22 +- common/decklist.cpp | 870 +++---- common/decklist.h | 196 +- common/get_pb_extension.cpp | 12 +- common/get_pb_extension.h | 6 +- common/rng_abstract.cpp | 38 +- common/rng_abstract.h | 10 +- common/rng_sfmt.cpp | 74 +- common/rng_sfmt.h | 14 +- common/server.cpp | 842 +++---- common/server.h | 152 +- common/server_abstractuserinterface.cpp | 116 +- common/server_abstractuserinterface.h | 46 +- common/server_arrow.cpp | 24 +- common/server_arrow.h | 22 +- common/server_arrowtarget.cpp | 2 +- common/server_arrowtarget.h | 2 +- common/server_card.cpp | 200 +- common/server_card.h | 118 +- common/server_cardzone.h | 80 +- common/server_counter.cpp | 10 +- common/server_counter.h | 30 +- common/server_database_interface.h | 46 +- common/server_game.cpp | 1124 ++++----- common/server_game.h | 150 +- common/server_player.cpp | 2782 +++++++++++------------ common/server_player.h | 188 +- common/server_player_reference.h | 16 +- common/server_protocolhandler.cpp | 900 ++++---- common/server_protocolhandler.h | 102 +- common/server_remoteuserinterface.cpp | 8 +- common/server_remoteuserinterface.h | 16 +- common/server_response_containers.cpp | 76 +- common/server_response_containers.h | 70 +- common/server_room.cpp | 500 ++-- common/server_room.h | 96 +- common/serverinfo_user_container.cpp | 42 +- common/serverinfo_user_container.h | 18 +- 38 files changed, 4510 insertions(+), 4510 deletions(-) diff --git a/common/color.h b/common/color.h index c96eda07e..1ea3e33df 100644 --- a/common/color.h +++ b/common/color.h @@ -10,26 +10,26 @@ #ifdef QT_GUI_LIB inline QColor convertColorToQColor(const color &c) { - return QColor(c.r(), c.g(), c.b()); + return QColor(c.r(), c.g(), c.b()); } inline color convertQColorToColor(const QColor &c) { - color result; - result.set_r(c.red()); - result.set_g(c.green()); - result.set_b(c.blue()); - return result; + color result; + result.set_r(c.red()); + result.set_g(c.green()); + result.set_b(c.blue()); + return result; } #endif inline color makeColor(int r, int g, int b) { - color result; - result.set_r(r); - result.set_g(g); - result.set_b(b); - return result; + color result; + result.set_r(r); + result.set_g(g); + result.set_b(b); + return result; } #endif diff --git a/common/decklist.cpp b/common/decklist.cpp index 90b5cb7e6..8e877ba9a 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -6,155 +6,155 @@ #include "decklist.h" SideboardPlan::SideboardPlan(const QString &_name, const QList &_moveList) - : name(_name), moveList(_moveList) + : name(_name), moveList(_moveList) { } void SideboardPlan::setMoveList(const QList &_moveList) { - moveList = _moveList; + moveList = _moveList; } bool SideboardPlan::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "name") - name = xml->readElementText(); - else if (childName == "move_card_to_zone") { - MoveCard_ToZone m; - while (!xml->atEnd()) { - xml->readNext(); - const QString childName2 = xml->name().toString(); - if (xml->isStartElement()) { - if (childName2 == "card_name") - m.set_card_name(xml->readElementText().toStdString()); - else if (childName2 == "start_zone") - m.set_start_zone(xml->readElementText().toStdString()); - else if (childName2 == "target_zone") - m.set_target_zone(xml->readElementText().toStdString()); - } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { - moveList.append(m); - break; - } - } - } - } else if (xml->isEndElement() && (childName == "sideboard_plan")) - return true; - } - return false; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "name") + name = xml->readElementText(); + else if (childName == "move_card_to_zone") { + MoveCard_ToZone m; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName2 = xml->name().toString(); + if (xml->isStartElement()) { + if (childName2 == "card_name") + m.set_card_name(xml->readElementText().toStdString()); + else if (childName2 == "start_zone") + m.set_start_zone(xml->readElementText().toStdString()); + else if (childName2 == "target_zone") + m.set_target_zone(xml->readElementText().toStdString()); + } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { + moveList.append(m); + break; + } + } + } + } else if (xml->isEndElement() && (childName == "sideboard_plan")) + return true; + } + return false; } void SideboardPlan::write(QXmlStreamWriter *xml) { - xml->writeStartElement("sideboard_plan"); - xml->writeTextElement("name", name); - for (int i = 0; i < moveList.size(); ++i) { - xml->writeStartElement("move_card_to_zone"); - xml->writeTextElement("card_name", QString::fromStdString(moveList[i].card_name())); - xml->writeTextElement("start_zone", QString::fromStdString(moveList[i].start_zone())); - xml->writeTextElement("target_zone", QString::fromStdString(moveList[i].target_zone())); - xml->writeEndElement(); - } - xml->writeEndElement(); + xml->writeStartElement("sideboard_plan"); + xml->writeTextElement("name", name); + for (int i = 0; i < moveList.size(); ++i) { + xml->writeStartElement("move_card_to_zone"); + xml->writeTextElement("card_name", QString::fromStdString(moveList[i].card_name())); + xml->writeTextElement("start_zone", QString::fromStdString(moveList[i].start_zone())); + xml->writeTextElement("target_zone", QString::fromStdString(moveList[i].target_zone())); + xml->writeEndElement(); + } + xml->writeEndElement(); } AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent) - : parent(_parent) + : parent(_parent) { - if (parent) - parent->append(this); + if (parent) + parent->append(this); } int AbstractDecklistNode::depth() const { - if (parent) - return parent->depth() + 1; - else - return 0; + if (parent) + return parent->depth() + 1; + else + return 0; } InnerDecklistNode::InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent) - : AbstractDecklistNode(_parent), name(other->getName()) + : AbstractDecklistNode(_parent), name(other->getName()) { - for (int i = 0; i < other->size(); ++i) { - InnerDecklistNode *inner = dynamic_cast(other->at(i)); - if (inner) - new InnerDecklistNode(inner, this); - else - new DecklistCardNode(dynamic_cast(other->at(i)), this); - } + for (int i = 0; i < other->size(); ++i) { + InnerDecklistNode *inner = dynamic_cast(other->at(i)); + if (inner) + new InnerDecklistNode(inner, this); + else + new DecklistCardNode(dynamic_cast(other->at(i)), this); + } } InnerDecklistNode::~InnerDecklistNode() { - clearTree(); + clearTree(); } QString InnerDecklistNode::visibleNameFromName(const QString &_name) { - if (_name == "main") - return QObject::tr("Maindeck"); - else if (_name == "side") - return QObject::tr("Sideboard"); - else if (_name == "tokens") - return QObject::tr("Tokens"); - else - return _name; + if (_name == "main") + return QObject::tr("Maindeck"); + else if (_name == "side") + return QObject::tr("Sideboard"); + else if (_name == "tokens") + return QObject::tr("Tokens"); + else + return _name; } void InnerDecklistNode::setSortMethod(DeckSortMethod method) { - sortMethod = method; - for (int i = 0; i < size(); i++) - at(i)->setSortMethod(method); + sortMethod = method; + for (int i = 0; i < size(); i++) + at(i)->setSortMethod(method); } QString InnerDecklistNode::getVisibleName() const { - return visibleNameFromName(name); + return visibleNameFromName(name); } void InnerDecklistNode::clearTree() { - for (int i = 0; i < size(); i++) - delete at(i); - clear(); + for (int i = 0; i < size(); i++) + delete at(i); + clear(); } DecklistCardNode::DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent) - : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), price(other->getPrice()) + : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), price(other->getPrice()) { } AbstractDecklistNode *InnerDecklistNode::findChild(const QString &name) { - for (int i = 0; i < size(); i++) - if (at(i)->getName() == name) - return at(i); - return 0; + for (int i = 0; i < size(); i++) + if (at(i)->getName() == name) + return at(i); + return 0; } int InnerDecklistNode::height() const { - return at(0)->height() + 1; + return at(0)->height() + 1; } int InnerDecklistNode::recursiveCount(bool countTotalCards) const { - int result = 0; - for (int i = 0; i < size(); i++) { - InnerDecklistNode *node = dynamic_cast(at(i)); - if (node) - result += node->recursiveCount(countTotalCards); - else if (countTotalCards) - result += dynamic_cast(at(i))->getNumber(); - else - result += 1; - } - return result; + int result = 0; + for (int i = 0; i < size(); i++) { + InnerDecklistNode *node = dynamic_cast(at(i)); + if (node) + result += node->recursiveCount(countTotalCards); + else if (countTotalCards) + result += dynamic_cast(at(i))->getNumber(); + else + result += 1; + } + return result; } float InnerDecklistNode::recursivePrice(bool countTotalCards) const @@ -172,275 +172,275 @@ float InnerDecklistNode::recursivePrice(bool countTotalCards) const bool InnerDecklistNode::compare(AbstractDecklistNode *other) const { - switch (sortMethod) { - case 0: - return compareNumber(other); - case 1: - return compareName(other); - case 2: - return comparePrice(other); - } + switch (sortMethod) { + case 0: + return compareNumber(other); + case 1: + return compareName(other); + case 2: + return comparePrice(other); + } return 0; } bool InnerDecklistNode::compareNumber(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - int n1 = recursiveCount(true); - int n2 = other2->recursiveCount(true); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + int n1 = recursiveCount(true); + int n2 = other2->recursiveCount(true); + return (n1 != n2) ? (n1 > n2) : compareName(other); + } else { + return false; + } } bool InnerDecklistNode::compareName(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + return (getName() > other2->getName()); + } else { + return false; + } } bool InnerDecklistNode::comparePrice(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - int p1 = 100*recursivePrice(true); - int p2 = 100*other2->recursivePrice(true); - return (p1 != p2) ? (p1 > p2) : compareName(other); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + int p1 = 100*recursivePrice(true); + int p2 = 100*other2->recursivePrice(true); + return (p1 != p2) ? (p1 > p2) : compareName(other); + } else { + return false; + } } bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const { - switch (sortMethod) { - case ByNumber: - return compareNumber(other); - case ByName: - return compareName(other); - case ByPrice: - return compareTotalPrice(other); - } + switch (sortMethod) { + case ByNumber: + return compareNumber(other); + case ByName: + return compareName(other); + case ByPrice: + return compareTotalPrice(other); + } return 0; } bool AbstractDecklistCardNode::compareNumber(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - int n1 = getNumber(); - int n2 = other2->getNumber(); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + int n1 = getNumber(); + int n2 = other2->getNumber(); + return (n1 != n2) ? (n1 > n2) : compareName(other); + } else { + return true; + } } bool AbstractDecklistCardNode::compareName(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + return (getName() > other2->getName()); + } else { + return true; + } } bool AbstractDecklistCardNode::compareTotalPrice(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - int p1 = 100*getTotalPrice(); - int p2 = 100*other2->getTotalPrice(); - return (p1 != p2) ? (p1 > p2) : compareName(other); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + int p1 = 100*getTotalPrice(); + int p2 = 100*other2->getTotalPrice(); + return (p1 != p2) ? (p1 > p2) : compareName(other); + } else { + return true; + } } class InnerDecklistNode::compareFunctor { private: - Qt::SortOrder order; + Qt::SortOrder order; public: - compareFunctor(Qt::SortOrder _order) : order(_order) { } - inline bool operator()(QPair a, QPair b) const - { - return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second)); - } + compareFunctor(Qt::SortOrder _order) : order(_order) { } + inline bool operator()(QPair a, QPair b) const + { + return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second)); + } }; bool InnerDecklistNode::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "zone") { - InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); - newZone->readElement(xml); - } else if (childName == "card") { - float price = (xml->attributes().value("price") != NULL) ? xml->attributes().value("price").toString().toFloat() : 0; - DecklistCardNode *newCard = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), price, this); - newCard->readElement(xml); - } - } else if (xml->isEndElement() && (childName == "zone")) - return false; - } - return true; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "zone") { + InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); + newZone->readElement(xml); + } else if (childName == "card") { + float price = (xml->attributes().value("price") != NULL) ? xml->attributes().value("price").toString().toFloat() : 0; + DecklistCardNode *newCard = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), price, this); + newCard->readElement(xml); + } + } else if (xml->isEndElement() && (childName == "zone")) + return false; + } + return true; } void InnerDecklistNode::writeElement(QXmlStreamWriter *xml) { - xml->writeStartElement("zone"); - xml->writeAttribute("name", name); - for (int i = 0; i < size(); i++) - at(i)->writeElement(xml); - xml->writeEndElement(); // zone + xml->writeStartElement("zone"); + xml->writeAttribute("name", name); + for (int i = 0; i < size(); i++) + at(i)->writeElement(xml); + xml->writeEndElement(); // zone } bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isEndElement() && xml->name() == "card") - return false; - } - return true; + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isEndElement() && xml->name() == "card") + return false; + } + return true; } void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml) { - xml->writeEmptyElement("card"); - xml->writeAttribute("number", QString::number(getNumber())); + xml->writeEmptyElement("card"); + xml->writeAttribute("number", QString::number(getNumber())); xml->writeAttribute("price", QString::number(getPrice())); - xml->writeAttribute("name", getName()); + xml->writeAttribute("name", getName()); } QVector > InnerDecklistNode::sort(Qt::SortOrder order) { - QVector > result(size()); - - // Initialize temporary list with contents of current list - QVector > tempList(size()); - for (int i = size() - 1; i >= 0; --i) { - tempList[i].first = i; - tempList[i].second = at(i); - } - - // Sort temporary list - compareFunctor cmp(order); - qSort(tempList.begin(), tempList.end(), cmp); - - // Map old indexes to new indexes and - // copy temporary list to the current one - for (int i = size() - 1; i >= 0; --i) { - result[i].first = tempList[i].first; - result[i].second = i; - replace(i, tempList[i].second); - } + QVector > result(size()); + + // Initialize temporary list with contents of current list + QVector > tempList(size()); + for (int i = size() - 1; i >= 0; --i) { + tempList[i].first = i; + tempList[i].second = at(i); + } + + // Sort temporary list + compareFunctor cmp(order); + qSort(tempList.begin(), tempList.end(), cmp); + + // Map old indexes to new indexes and + // copy temporary list to the current one + for (int i = size() - 1; i >= 0; --i) { + result[i].first = tempList[i].first; + result[i].second = i; + replace(i, tempList[i].second); + } - return result; + return result; } DeckList::DeckList() { - root = new InnerDecklistNode; + root = new InnerDecklistNode; } // TODO: http://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator DeckList::DeckList(const DeckList &other) - : name(other.name), - comments(other.comments), - deckHash(other.deckHash) + : name(other.name), + comments(other.comments), + deckHash(other.deckHash) { - root = new InnerDecklistNode(other.getRoot()); - - QMapIterator spIterator(other.getSideboardPlans()); - while (spIterator.hasNext()) { - spIterator.next(); - sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); - } - updateDeckHash(); + root = new InnerDecklistNode(other.getRoot()); + + QMapIterator spIterator(other.getSideboardPlans()); + while (spIterator.hasNext()) { + spIterator.next(); + sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); + } + updateDeckHash(); } DeckList::DeckList(const QString &nativeString) { - root = new InnerDecklistNode; - loadFromString_Native(nativeString); + root = new InnerDecklistNode; + loadFromString_Native(nativeString); } DeckList::~DeckList() { - delete root; - - QMapIterator i(sideboardPlans); - while (i.hasNext()) - delete i.next().value(); + delete root; + + QMapIterator i(sideboardPlans); + while (i.hasNext()) + delete i.next().value(); } QList DeckList::getCurrentSideboardPlan() { - SideboardPlan *current = sideboardPlans.value(QString(), 0); - if (!current) - return QList(); - else - return current->getMoveList(); + SideboardPlan *current = sideboardPlans.value(QString(), 0); + if (!current) + return QList(); + else + return current->getMoveList(); } void DeckList::setCurrentSideboardPlan(const QList &plan) { - SideboardPlan *current = sideboardPlans.value(QString(), 0); - if (!current) { - current = new SideboardPlan; - sideboardPlans.insert(QString(), current); - } - - current->setMoveList(plan); + SideboardPlan *current = sideboardPlans.value(QString(), 0); + if (!current) { + current = new SideboardPlan; + sideboardPlans.insert(QString(), current); + } + + current->setMoveList(plan); } bool DeckList::readElement(QXmlStreamReader *xml) { - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "deckname") - name = xml->readElementText(); - else if (childName == "comments") - comments = xml->readElementText(); - else if (childName == "zone") { - InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root); - newZone->readElement(xml); - } else if (childName == "sideboard_plan") { - SideboardPlan *newSideboardPlan = new SideboardPlan; - if (newSideboardPlan->readElement(xml)) - sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan); - else - delete newSideboardPlan; - } - } else if (xml->isEndElement() && (childName == "cockatrice_deck")) - return false; - return true; + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "deckname") + name = xml->readElementText(); + else if (childName == "comments") + comments = xml->readElementText(); + else if (childName == "zone") { + InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root); + newZone->readElement(xml); + } else if (childName == "sideboard_plan") { + SideboardPlan *newSideboardPlan = new SideboardPlan; + if (newSideboardPlan->readElement(xml)) + sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan); + else + delete newSideboardPlan; + } + } else if (xml->isEndElement() && (childName == "cockatrice_deck")) + return false; + return true; } void DeckList::write(QXmlStreamWriter *xml) { - xml->writeStartElement("cockatrice_deck"); - xml->writeAttribute("version", "1"); - xml->writeTextElement("deckname", name); - xml->writeTextElement("comments", comments); + xml->writeStartElement("cockatrice_deck"); + xml->writeAttribute("version", "1"); + xml->writeTextElement("deckname", name); + xml->writeTextElement("comments", comments); - for (int i = 0; i < root->size(); i++) - root->at(i)->writeElement(xml); - - QMapIterator i(sideboardPlans); - while (i.hasNext()) - i.next().value()->write(xml); - xml->writeEndElement(); + for (int i = 0; i < root->size(); i++) + root->at(i)->writeElement(xml); + + QMapIterator i(sideboardPlans); + while (i.hasNext()) + i.next().value()->write(xml); + xml->writeEndElement(); } bool DeckList::loadFromXml(QXmlStreamReader *xml) @@ -450,187 +450,187 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml) return false; } - cleanList(); - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isStartElement()) { - if (xml->name() != "cockatrice_deck") - return false; - while (!xml->atEnd()) { - xml->readNext(); - if (!readElement(xml)) - break; - } - } - } - updateDeckHash(); + cleanList(); + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isStartElement()) { + if (xml->name() != "cockatrice_deck") + return false; + while (!xml->atEnd()) { + xml->readNext(); + if (!readElement(xml)) + break; + } + } + } + updateDeckHash(); if (xml->error()) { qDebug() << "Error loading deck from xml: " << xml->errorString(); return false; } - return true; + return true; } bool DeckList::loadFromString_Native(const QString &nativeString) { - QXmlStreamReader xml(nativeString); - return loadFromXml(&xml); + QXmlStreamReader xml(nativeString); + return loadFromXml(&xml); } QString DeckList::writeToString_Native() { - QString result; - QXmlStreamWriter xml(&result); - xml.writeStartDocument(); - write(&xml); - xml.writeEndDocument(); - return result; + QString result; + QXmlStreamWriter xml(&result); + xml.writeStartDocument(); + write(&xml); + xml.writeEndDocument(); + return result; } bool DeckList::loadFromFile_Native(QIODevice *device) { - QXmlStreamReader xml(device); - return loadFromXml(&xml); + QXmlStreamReader xml(device); + return loadFromXml(&xml); } bool DeckList::saveToFile_Native(QIODevice *device) { - QXmlStreamWriter xml(device); - xml.setAutoFormatting(true); - xml.writeStartDocument(); + QXmlStreamWriter xml(device); + xml.setAutoFormatting(true); + xml.writeStartDocument(); - write(&xml); + write(&xml); - xml.writeEndDocument(); - return true; + xml.writeEndDocument(); + return true; } bool DeckList::loadFromStream_Plain(QTextStream &in) { - cleanList(); + cleanList(); - InnerDecklistNode *main = 0, *side = 0; - bool inSideboard = false; + InnerDecklistNode *main = 0, *side = 0; + bool inSideboard = false; - int okRows = 0; - while (!in.atEnd()) { - QString line = in.readLine().simplified(); - if (line.startsWith("//")) - continue; + int okRows = 0; + while (!in.atEnd()) { + QString line = in.readLine().simplified(); + if (line.startsWith("//")) + continue; - InnerDecklistNode *zone; - if (line.startsWith("Sideboard", Qt::CaseInsensitive)) { - inSideboard = true; - continue; - } else if (line.startsWith("SB:", Qt::CaseInsensitive)) { - line = line.mid(3).trimmed(); - if (!side) - side = new InnerDecklistNode("side", root); - zone = side; - } else if (inSideboard) { - if (!side) - side = new InnerDecklistNode("side", root); - zone = side; - } else { - if (!main) - main = new InnerDecklistNode("main", root); - zone = main; - } + InnerDecklistNode *zone; + if (line.startsWith("Sideboard", Qt::CaseInsensitive)) { + inSideboard = true; + continue; + } else if (line.startsWith("SB:", Qt::CaseInsensitive)) { + line = line.mid(3).trimmed(); + if (!side) + side = new InnerDecklistNode("side", root); + zone = side; + } else if (inSideboard) { + if (!side) + side = new InnerDecklistNode("side", root); + zone = side; + } else { + if (!main) + main = new InnerDecklistNode("main", root); + zone = main; + } - // Filter out MWS edition symbols and basic land extras - QRegExp rx("\\[.*\\]"); - line.remove(rx); - rx.setPattern("\\(.*\\)"); - line.remove(rx); - //Filter out post card name editions - rx.setPattern("\\|.*$"); - line.remove(rx); - line = line.simplified(); + // Filter out MWS edition symbols and basic land extras + QRegExp rx("\\[.*\\]"); + line.remove(rx); + rx.setPattern("\\(.*\\)"); + line.remove(rx); + //Filter out post card name editions + rx.setPattern("\\|.*$"); + line.remove(rx); + line = line.simplified(); - int i = line.indexOf(' '); - bool ok; - int number = line.left(i).toInt(&ok); - if (!ok) - continue; + int i = line.indexOf(' '); + bool ok; + int number = line.left(i).toInt(&ok); + if (!ok) + continue; - QString cardName = line.mid(i + 1); + QString cardName = line.mid(i + 1); // Common differences between cockatrice's card names // and what's commonly used in decklists - rx.setPattern("’"); + rx.setPattern("’"); cardName.replace(rx, "'"); - rx.setPattern("Æ"); - cardName.replace(rx, "AE"); - rx.setPattern("^Aether"); - cardName.replace(rx, "AEther"); - rx.setPattern("\\s*[&|/]{1,2}\\s*"); - cardName.replace(rx, " // "); + rx.setPattern("Æ"); + cardName.replace(rx, "AE"); + rx.setPattern("^Aether"); + cardName.replace(rx, "AEther"); + rx.setPattern("\\s*[&|/]{1,2}\\s*"); + cardName.replace(rx, " // "); - ++okRows; - new DecklistCardNode(cardName, number, zone); - } - updateDeckHash(); - return (okRows > 0); + ++okRows; + new DecklistCardNode(cardName, number, zone); + } + updateDeckHash(); + return (okRows > 0); } bool DeckList::loadFromFile_Plain(QIODevice *device) { - QTextStream in(device); - return loadFromStream_Plain(in); + QTextStream in(device); + return loadFromStream_Plain(in); } bool DeckList::saveToStream_Plain(QTextStream &out) { - // Support for this is only possible if the internal structure doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); - } - } - return true; + // Support for this is only possible if the internal structure doesn't get more complicated. + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); + } + } + return true; } bool DeckList::saveToFile_Plain(QIODevice *device) { - QTextStream out(device); - return saveToStream_Plain(out); + QTextStream out(device); + return saveToStream_Plain(out); } QString DeckList::writeToString_Plain() { - QString result; - QTextStream out(&result); - saveToStream_Plain(out); - return result; + QString result; + QTextStream out(&result); + saveToStream_Plain(out); + return result; } void DeckList::cleanList() { - root->clearTree(); - setName(); - setComments(); - deckHash = QString(); - emit deckHashChanged(); + root->clearTree(); + setName(); + setComments(); + deckHash = QString(); + emit deckHashChanged(); } void DeckList::getCardListHelper(InnerDecklistNode *item, QSet &result) const { - for (int i = 0; i < item->size(); ++i) { - DecklistCardNode *node = dynamic_cast(item->at(i)); - if (node) - result.insert(node->getName()); - else - getCardListHelper(dynamic_cast(item->at(i)), result); - } + for (int i = 0; i < item->size(); ++i) { + DecklistCardNode *node = dynamic_cast(item->at(i)); + if (node) + result.insert(node->getName()); + else + getCardListHelper(dynamic_cast(item->at(i)), result); + } } QStringList DeckList::getCardList() const { - QSet result; - getCardListHelper(root, result); - return result.toList(); + QSet result; + getCardListHelper(root, result); + return result.toList(); } int DeckList::getSideboardSize() const @@ -640,7 +640,7 @@ int DeckList::getSideboardSize() const InnerDecklistNode *node = dynamic_cast(root->at(i)); if (node->getName() != "side") continue; - for (int j = 0; j < node->size(); j++) { + for (int j = 0; j < node->size(); j++) { DecklistCardNode *card = dynamic_cast(node->at(j)); size += card->getNumber(); } @@ -650,65 +650,65 @@ int DeckList::getSideboardSize() const DecklistCardNode *DeckList::addCard(const QString &cardName, const QString &zoneName) { - InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); - if (!zoneNode) - zoneNode = new InnerDecklistNode(zoneName, root); + InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); + if (!zoneNode) + zoneNode = new InnerDecklistNode(zoneName, root); - DecklistCardNode *node = new DecklistCardNode(cardName, 1, zoneNode); - updateDeckHash(); - return node; + DecklistCardNode *node = new DecklistCardNode(cardName, 1, zoneNode); + updateDeckHash(); + return node; } bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode) { - if (node == root) - return true; - bool updateHash = false; - if (!rootNode) { - rootNode = root; - updateHash = true; - } - - int index = rootNode->indexOf(node); - if (index != -1) { - delete rootNode->takeAt(index); - if (!rootNode->size()) - deleteNode(rootNode, rootNode->getParent()); - if (updateHash) - updateDeckHash(); - return true; - } - for (int i = 0; i < rootNode->size(); i++) { - InnerDecklistNode *inner = dynamic_cast(rootNode->at(i)); - if (inner) - if (deleteNode(node, inner)) { - if (updateHash) - updateDeckHash(); - return true; - } - } - return false; + if (node == root) + return true; + bool updateHash = false; + if (!rootNode) { + rootNode = root; + updateHash = true; + } + + int index = rootNode->indexOf(node); + if (index != -1) { + delete rootNode->takeAt(index); + if (!rootNode->size()) + deleteNode(rootNode, rootNode->getParent()); + if (updateHash) + updateDeckHash(); + return true; + } + for (int i = 0; i < rootNode->size(); i++) { + InnerDecklistNode *inner = dynamic_cast(rootNode->at(i)); + if (inner) + if (deleteNode(node, inner)) { + if (updateHash) + updateDeckHash(); + return true; + } + } + return false; } void DeckList::updateDeckHash() { - QStringList cardList; - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - for (int k = 0; k < card->getNumber(); ++k) - cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); - } - } - cardList.sort(); - QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); - quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) - + (((quint64) (unsigned char) deckHashArray[1]) << 24) - + (((quint64) (unsigned char) deckHashArray[2] << 16)) - + (((quint64) (unsigned char) deckHashArray[3]) << 8) - + (quint64) (unsigned char) deckHashArray[4]; - deckHash = QString::number(number, 32).rightJustified(8, '0'); - - emit deckHashChanged(); + QStringList cardList; + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + for (int k = 0; k < card->getNumber(); ++k) + cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); + } + } + cardList.sort(); + QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); + quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) + + (((quint64) (unsigned char) deckHashArray[1]) << 24) + + (((quint64) (unsigned char) deckHashArray[2] << 16)) + + (((quint64) (unsigned char) deckHashArray[3]) << 8) + + (quint64) (unsigned char) deckHashArray[4]; + deckHash = QString::number(number, 32).rightJustified(8, '0'); + + emit deckHashChanged(); } diff --git a/common/decklist.h b/common/decklist.h index 2eed52fc6..8b71a4b72 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -23,152 +23,152 @@ class InnerDecklistNode; class SideboardPlan { private: - QString name; - QList moveList; + QString name; + QList moveList; public: - SideboardPlan(const QString &_name = QString(), const QList &_moveList = QList()); - bool readElement(QXmlStreamReader *xml); - void write(QXmlStreamWriter *xml); + SideboardPlan(const QString &_name = QString(), const QList &_moveList = QList()); + bool readElement(QXmlStreamReader *xml); + void write(QXmlStreamWriter *xml); - QString getName() const { return name; } - const QList &getMoveList() const { return moveList; } - void setMoveList(const QList &_moveList); + QString getName() const { return name; } + const QList &getMoveList() const { return moveList; } + void setMoveList(const QList &_moveList); }; enum DeckSortMethod { ByNumber, ByName, ByPrice }; class AbstractDecklistNode { protected: - InnerDecklistNode *parent; - DeckSortMethod sortMethod; + InnerDecklistNode *parent; + DeckSortMethod sortMethod; public: - AbstractDecklistNode(InnerDecklistNode *_parent = 0); - virtual ~AbstractDecklistNode() { } - virtual void setSortMethod(DeckSortMethod method) { sortMethod = method; } - virtual QString getName() const = 0; - InnerDecklistNode *getParent() const { return parent; } - int depth() const; - virtual int height() const = 0; - virtual bool compare(AbstractDecklistNode *other) const = 0; + AbstractDecklistNode(InnerDecklistNode *_parent = 0); + virtual ~AbstractDecklistNode() { } + virtual void setSortMethod(DeckSortMethod method) { sortMethod = method; } + virtual QString getName() const = 0; + InnerDecklistNode *getParent() const { return parent; } + int depth() const; + virtual int height() const = 0; + virtual bool compare(AbstractDecklistNode *other) const = 0; - virtual bool readElement(QXmlStreamReader *xml) = 0; - virtual void writeElement(QXmlStreamWriter *xml) = 0; + virtual bool readElement(QXmlStreamReader *xml) = 0; + virtual void writeElement(QXmlStreamWriter *xml) = 0; }; class InnerDecklistNode : public AbstractDecklistNode, public QList { private: - QString name; - class compareFunctor; + QString name; + class compareFunctor; public: - InnerDecklistNode(const QString &_name = QString(), InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent), name(_name) { } - InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = 0); - virtual ~InnerDecklistNode(); - void setSortMethod(DeckSortMethod method); - QString getName() const { return name; } - void setName(const QString &_name) { name = _name; } - static QString visibleNameFromName(const QString &_name); - virtual QString getVisibleName() const; - void clearTree(); - AbstractDecklistNode *findChild(const QString &name); - int height() const; - int recursiveCount(bool countTotalCards = false) const; + InnerDecklistNode(const QString &_name = QString(), InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent), name(_name) { } + InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = 0); + virtual ~InnerDecklistNode(); + void setSortMethod(DeckSortMethod method); + QString getName() const { return name; } + void setName(const QString &_name) { name = _name; } + static QString visibleNameFromName(const QString &_name); + virtual QString getVisibleName() const; + void clearTree(); + AbstractDecklistNode *findChild(const QString &name); + int height() const; + int recursiveCount(bool countTotalCards = false) const; float recursivePrice(bool countTotalCards = false) const; - bool compare(AbstractDecklistNode *other) const; - bool compareNumber(AbstractDecklistNode *other) const; - bool compareName(AbstractDecklistNode *other) const; - bool comparePrice(AbstractDecklistNode *other) const; - QVector > sort(Qt::SortOrder order = Qt::AscendingOrder); + bool compare(AbstractDecklistNode *other) const; + bool compareNumber(AbstractDecklistNode *other) const; + bool compareName(AbstractDecklistNode *other) const; + bool comparePrice(AbstractDecklistNode *other) const; + QVector > sort(Qt::SortOrder order = Qt::AscendingOrder); - bool readElement(QXmlStreamReader *xml); - void writeElement(QXmlStreamWriter *xml); + bool readElement(QXmlStreamReader *xml); + void writeElement(QXmlStreamWriter *xml); }; class AbstractDecklistCardNode : public AbstractDecklistNode { public: - AbstractDecklistCardNode(InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent) { } - virtual int getNumber() const = 0; - virtual void setNumber(int _number) = 0; - virtual QString getName() const = 0; - virtual void setName(const QString &_name) = 0; + AbstractDecklistCardNode(InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent) { } + virtual int getNumber() const = 0; + virtual void setNumber(int _number) = 0; + virtual QString getName() const = 0; + virtual void setName(const QString &_name) = 0; virtual float getPrice() const = 0; virtual void setPrice(const float _price) = 0; float getTotalPrice() const { return getNumber() * getPrice(); } - int height() const { return 0; } - bool compare(AbstractDecklistNode *other) const; - bool compareNumber(AbstractDecklistNode *other) const; - bool compareName(AbstractDecklistNode *other) const; - bool compareTotalPrice(AbstractDecklistNode *other) const; + int height() const { return 0; } + bool compare(AbstractDecklistNode *other) const; + bool compareNumber(AbstractDecklistNode *other) const; + bool compareName(AbstractDecklistNode *other) const; + bool compareTotalPrice(AbstractDecklistNode *other) const; - bool readElement(QXmlStreamReader *xml); - void writeElement(QXmlStreamWriter *xml); + bool readElement(QXmlStreamReader *xml); + void writeElement(QXmlStreamWriter *xml); }; class DecklistCardNode : public AbstractDecklistCardNode { private: - QString name; - int number; + QString name; + int number; float price; public: DecklistCardNode(const QString &_name = QString(), int _number = 1, float _price = 0, InnerDecklistNode *_parent = 0) : AbstractDecklistCardNode(_parent), name(_name), number(_number), price(_price) { } DecklistCardNode(const QString &_name = QString(), int _number = 1, InnerDecklistNode *_parent = 0) : AbstractDecklistCardNode(_parent), name(_name), number(_number), price(0) { } - DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent); - int getNumber() const { return number; } - void setNumber(int _number) { number = _number; } - QString getName() const { return name; } - void setName(const QString &_name) { name = _name; } + DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent); + int getNumber() const { return number; } + void setNumber(int _number) { number = _number; } + QString getName() const { return name; } + void setName(const QString &_name) { name = _name; } float getPrice() const { return price; } void setPrice(const float _price) { price = _price; } }; class DeckList : public QObject { - Q_OBJECT + Q_OBJECT private: - QString name, comments; - QString deckHash; - QMap sideboardPlans; - InnerDecklistNode *root; - void getCardListHelper(InnerDecklistNode *node, QSet &result) const; + QString name, comments; + QString deckHash; + QMap sideboardPlans; + InnerDecklistNode *root; + void getCardListHelper(InnerDecklistNode *node, QSet &result) const; signals: - void deckHashChanged(); + void deckHashChanged(); public slots: - void setName(const QString &_name = QString()) { name = _name; } - void setComments(const QString &_comments = QString()) { comments = _comments; } + void setName(const QString &_name = QString()) { name = _name; } + void setComments(const QString &_comments = QString()) { comments = _comments; } public: - DeckList(); - DeckList(const DeckList &other); - DeckList(const QString &nativeString); - ~DeckList(); - QString getName() const { return name; } - QString getComments() const { return comments; } - QList getCurrentSideboardPlan(); - void setCurrentSideboardPlan(const QList &plan); - const QMap &getSideboardPlans() const { return sideboardPlans; } + DeckList(); + DeckList(const DeckList &other); + DeckList(const QString &nativeString); + ~DeckList(); + QString getName() const { return name; } + QString getComments() const { return comments; } + QList getCurrentSideboardPlan(); + void setCurrentSideboardPlan(const QList &plan); + const QMap &getSideboardPlans() const { return sideboardPlans; } - bool readElement(QXmlStreamReader *xml); - void write(QXmlStreamWriter *xml); - bool loadFromXml(QXmlStreamReader *xml); - bool loadFromString_Native(const QString &nativeString); - QString writeToString_Native(); - bool loadFromFile_Native(QIODevice *device); - bool saveToFile_Native(QIODevice *device); - bool loadFromStream_Plain(QTextStream &stream); - bool loadFromFile_Plain(QIODevice *device); - bool saveToStream_Plain(QTextStream &stream); - bool saveToFile_Plain(QIODevice *device); - QString writeToString_Plain(); + bool readElement(QXmlStreamReader *xml); + void write(QXmlStreamWriter *xml); + bool loadFromXml(QXmlStreamReader *xml); + bool loadFromString_Native(const QString &nativeString); + QString writeToString_Native(); + bool loadFromFile_Native(QIODevice *device); + bool saveToFile_Native(QIODevice *device); + bool loadFromStream_Plain(QTextStream &stream); + bool loadFromFile_Plain(QIODevice *device); + bool saveToStream_Plain(QTextStream &stream); + bool saveToFile_Plain(QIODevice *device); + QString writeToString_Plain(); - void cleanList(); - bool isEmpty() const { return root->isEmpty() && name.isEmpty() && comments.isEmpty() && sideboardPlans.isEmpty(); } - QStringList getCardList() const; + void cleanList(); + bool isEmpty() const { return root->isEmpty() && name.isEmpty() && comments.isEmpty() && sideboardPlans.isEmpty(); } + QStringList getCardList() const; int getSideboardSize() const; - QString getDeckHash() const { return deckHash; } - void updateDeckHash(); + QString getDeckHash() const { return deckHash; } + void updateDeckHash(); - InnerDecklistNode *getRoot() const { return root; } - DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); - bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0); + InnerDecklistNode *getRoot() const { return root; } + DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); + bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0); }; #endif diff --git a/common/get_pb_extension.cpp b/common/get_pb_extension.cpp index d561f0c85..1e980f4b6 100644 --- a/common/get_pb_extension.cpp +++ b/common/get_pb_extension.cpp @@ -4,10 +4,10 @@ int getPbExtension(const ::google::protobuf::Message &message) { - std::vector< const ::google::protobuf::FieldDescriptor * > fieldList; - message.GetReflection()->ListFields(message, &fieldList); - for (unsigned int j = 0; j < fieldList.size(); ++j) - if (fieldList[j]->is_extension()) - return fieldList[j]->number(); - return -1; + std::vector< const ::google::protobuf::FieldDescriptor * > fieldList; + message.GetReflection()->ListFields(message, &fieldList); + for (unsigned int j = 0; j < fieldList.size(); ++j) + if (fieldList[j]->is_extension()) + return fieldList[j]->number(); + return -1; } diff --git a/common/get_pb_extension.h b/common/get_pb_extension.h index c55aa937f..bebef39ee 100644 --- a/common/get_pb_extension.h +++ b/common/get_pb_extension.h @@ -2,9 +2,9 @@ #define GET_PB_EXTENSION_H namespace google { - namespace protobuf { - class Message; - } + namespace protobuf { + class Message; + } } int getPbExtension(const ::google::protobuf::Message &message); diff --git a/common/rng_abstract.cpp b/common/rng_abstract.cpp index 4c51143a6..790549086 100644 --- a/common/rng_abstract.cpp +++ b/common/rng_abstract.cpp @@ -4,27 +4,27 @@ QVector RNG_Abstract::makeNumbersVector(int n, int min, int max) { - const int bins = max - min + 1; - QVector result(bins); - for (int i = 0; i < n; ++i) { - int number = rand(min, max); - if ((number < min) || (number > max)) - qDebug() << "rand(" << min << "," << max << ") returned " << number; - else - result[number - min]++; - } - return result; + const int bins = max - min + 1; + QVector result(bins); + for (int i = 0; i < n; ++i) { + int number = rand(min, max); + if ((number < min) || (number > max)) + qDebug() << "rand(" << min << "," << max << ") returned " << number; + else + result[number - min]++; + } + return result; } double RNG_Abstract::testRandom(const QVector &numbers) const { - int n = 0; - for (int i = 0; i < numbers.size(); ++i) - n += numbers[i]; - double expected = (double) n / (double) numbers.size(); - double chisq = 0; - for (int i = 0; i < numbers.size(); ++i) - chisq += ((double) numbers[i] - expected) * ((double) numbers[i] - expected) / expected; - - return chisq; + int n = 0; + for (int i = 0; i < numbers.size(); ++i) + n += numbers[i]; + double expected = (double) n / (double) numbers.size(); + double chisq = 0; + for (int i = 0; i < numbers.size(); ++i) + chisq += ((double) numbers[i] - expected) * ((double) numbers[i] - expected) / expected; + + return chisq; } diff --git a/common/rng_abstract.h b/common/rng_abstract.h index f20178195..0423930cd 100644 --- a/common/rng_abstract.h +++ b/common/rng_abstract.h @@ -5,12 +5,12 @@ #include class RNG_Abstract : public QObject { - Q_OBJECT + Q_OBJECT public: - RNG_Abstract(QObject *parent = 0) : QObject(parent) { } - virtual unsigned int rand(int min, int max) = 0; - QVector makeNumbersVector(int n, int min, int max); - double testRandom(const QVector &numbers) const; + RNG_Abstract(QObject *parent = 0) : QObject(parent) { } + virtual unsigned int rand(int min, int max) = 0; + QVector makeNumbersVector(int n, int min, int max); + double testRandom(const QVector &numbers) const; }; extern RNG_Abstract *rng; diff --git a/common/rng_sfmt.cpp b/common/rng_sfmt.cpp index d3d55d1c5..783594a04 100644 --- a/common/rng_sfmt.cpp +++ b/common/rng_sfmt.cpp @@ -11,10 +11,10 @@ #endif RNG_SFMT::RNG_SFMT(QObject *parent) - : RNG_Abstract(parent) + : RNG_Abstract(parent) { - // initialize the random number generator with a 32bit integer seed (timestamp) - sfmt_init_gen_rand(&sfmt, QDateTime::currentDateTime().toTime_t()); + // initialize the random number generator with a 32bit integer seed (timestamp) + sfmt_init_gen_rand(&sfmt, QDateTime::currentDateTime().toTime_t()); } /** @@ -32,11 +32,11 @@ unsigned int RNG_SFMT::rand(int min, int max) { * There has been no use for negative random numbers with rand() though, so it's treated as error. */ if(min < 0) { - throw std::invalid_argument( - QString("Invalid bounds for RNG: Got min " + - QString::number(min) + " < 0!\n").toStdString()); - // at this point, the method exits. No return value is needed, because - // basically the exception itself is returned. + throw std::invalid_argument( + QString("Invalid bounds for RNG: Got min " + + QString::number(min) + " < 0!\n").toStdString()); + // at this point, the method exits. No return value is needed, because + // basically the exception itself is returned. } // For complete fairness and equal timing, this should be a roll, but let's skip it anyway @@ -99,38 +99,38 @@ unsigned int RNG_SFMT::rand(int min, int max) { */ unsigned int RNG_SFMT::cdf(unsigned int min, unsigned int max) { - // This all makes no sense if min > max, which should never happen. - if(min > max) { - throw std::invalid_argument( - QString("Invalid bounds for RNG: min > max! Values were: min = " + - QString::number(min) + ", max = " + - QString::number(max)).toStdString()); - // at this point, the method exits. No return value is needed, because - // basically the exception itself is returned. - } + // This all makes no sense if min > max, which should never happen. + if(min > max) { + throw std::invalid_argument( + QString("Invalid bounds for RNG: min > max! Values were: min = " + + QString::number(min) + ", max = " + + QString::number(max)).toStdString()); + // at this point, the method exits. No return value is needed, because + // basically the exception itself is returned. + } - // First compute the diameter (aka size, length) of the [min, max] interval - const unsigned int diameter = max - min + 1; + // First compute the diameter (aka size, length) of the [min, max] interval + const unsigned int diameter = max - min + 1; - // Compute how many buckets (each in size of the diameter) will fit into the - // universe. - // If the division has a remainder, the result is floored automatically. - const uint64_t buckets = UINT64_MAX / diameter; + // Compute how many buckets (each in size of the diameter) will fit into the + // universe. + // If the division has a remainder, the result is floored automatically. + const uint64_t buckets = UINT64_MAX / diameter; - // Compute the last valid random number. All numbers beyond have to be ignored. - // If there was no remainder in the previous step, limit is equal to UINT64_MAX. - const uint64_t limit = diameter * buckets; + // Compute the last valid random number. All numbers beyond have to be ignored. + // If there was no remainder in the previous step, limit is equal to UINT64_MAX. + const uint64_t limit = diameter * buckets; - uint64_t rand; - // To make the random number generation thread-safe, a mutex is created around - // the generation. Outside of the loop of course, to avoid lock/unlock overhead. - mutex.lock(); - do { - rand = sfmt_genrand_uint64(&sfmt); - } while (rand >= limit); - mutex.unlock(); + uint64_t rand; + // To make the random number generation thread-safe, a mutex is created around + // the generation. Outside of the loop of course, to avoid lock/unlock overhead. + mutex.lock(); + do { + rand = sfmt_genrand_uint64(&sfmt); + } while (rand >= limit); + mutex.unlock(); - // Now determine the bucket containing the SFMT() random number and after adding - // the lower bound, a random number from [min, max] can be returned. - return (unsigned int) (rand / buckets + min); + // Now determine the bucket containing the SFMT() random number and after adding + // the lower bound, a random number from [min, max] can be returned. + return (unsigned int) (rand / buckets + min); } diff --git a/common/rng_sfmt.h b/common/rng_sfmt.h index d35ce97df..ec7a75c97 100644 --- a/common/rng_sfmt.h +++ b/common/rng_sfmt.h @@ -26,15 +26,15 @@ */ class RNG_SFMT : public RNG_Abstract { - Q_OBJECT + Q_OBJECT private: - QMutex mutex; - sfmt_t sfmt; - // The discrete cumulative distribution function for the RNG - unsigned int cdf(unsigned int min, unsigned int max); + QMutex mutex; + sfmt_t sfmt; + // The discrete cumulative distribution function for the RNG + unsigned int cdf(unsigned int min, unsigned int max); public: - RNG_SFMT(QObject *parent = 0); - unsigned int rand(int min, int max); + RNG_SFMT(QObject *parent = 0); + unsigned int rand(int min, int max); }; #endif diff --git a/common/server.cpp b/common/server.cpp index f05304975..132cba945 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -36,18 +36,18 @@ #include Server::Server(bool _threaded, QObject *parent) - : QObject(parent), threaded(_threaded), nextLocalGameId(0) + : QObject(parent), threaded(_threaded), nextLocalGameId(0) { - qRegisterMetaType("ServerInfo_Game"); - qRegisterMetaType("ServerInfo_Room"); - qRegisterMetaType("ServerInfo_User"); - qRegisterMetaType("CommandContainer"); - qRegisterMetaType("Response"); - qRegisterMetaType("GameEventContainer"); - qRegisterMetaType("IslMessage"); - qRegisterMetaType("Command_JoinGame"); - - connect(this, SIGNAL(sigSendIslMessage(IslMessage, int)), this, SLOT(doSendIslMessage(IslMessage, int)), Qt::QueuedConnection); + qRegisterMetaType("ServerInfo_Game"); + qRegisterMetaType("ServerInfo_Room"); + qRegisterMetaType("ServerInfo_User"); + qRegisterMetaType("CommandContainer"); + qRegisterMetaType("Response"); + qRegisterMetaType("GameEventContainer"); + qRegisterMetaType("IslMessage"); + qRegisterMetaType("Command_JoinGame"); + + connect(this, SIGNAL(sigSendIslMessage(IslMessage, int)), this, SLOT(doSendIslMessage(IslMessage, int)), Qt::QueuedConnection); } Server::~Server() @@ -56,532 +56,532 @@ Server::~Server() void Server::prepareDestroy() { - // dirty :( - if (threaded) { - clientsLock.lockForRead(); - for (int i = 0; i < clients.size(); ++i) - QMetaObject::invokeMethod(clients.at(i), "prepareDestroy", Qt::QueuedConnection); - clientsLock.unlock(); - - bool done = false; - - class SleeperThread : public QThread - { - public: - static void msleep(unsigned long msecs) { QThread::usleep(msecs); } - }; + // dirty :( + if (threaded) { + clientsLock.lockForRead(); + for (int i = 0; i < clients.size(); ++i) + QMetaObject::invokeMethod(clients.at(i), "prepareDestroy", Qt::QueuedConnection); + clientsLock.unlock(); + + bool done = false; + + class SleeperThread : public QThread + { + public: + static void msleep(unsigned long msecs) { QThread::usleep(msecs); } + }; - do { - SleeperThread::msleep(10); - clientsLock.lockForRead(); - if (clients.isEmpty()) - done = true; - clientsLock.unlock(); - } while (!done); - } else { - // no locking is needed in unthreaded mode - while (!clients.isEmpty()) - clients.first()->prepareDestroy(); - } - - roomsLock.lockForWrite(); - QMapIterator roomIterator(rooms); - while (roomIterator.hasNext()) - delete roomIterator.next().value(); - rooms.clear(); - roomsLock.unlock(); + do { + SleeperThread::msleep(10); + clientsLock.lockForRead(); + if (clients.isEmpty()) + done = true; + clientsLock.unlock(); + } while (!done); + } else { + // no locking is needed in unthreaded mode + while (!clients.isEmpty()) + clients.first()->prepareDestroy(); + } + + roomsLock.lockForWrite(); + QMapIterator roomIterator(rooms); + while (roomIterator.hasNext()) + delete roomIterator.next().value(); + rooms.clear(); + roomsLock.unlock(); } void Server::setDatabaseInterface(Server_DatabaseInterface *_databaseInterface) { - connect(this, SIGNAL(endSession(qint64)), _databaseInterface, SLOT(endSession(qint64))); - databaseInterfaces.insert(QThread::currentThread(), _databaseInterface); + connect(this, SIGNAL(endSession(qint64)), _databaseInterface, SLOT(endSession(qint64))); + databaseInterfaces.insert(QThread::currentThread(), _databaseInterface); } Server_DatabaseInterface *Server::getDatabaseInterface() const { - return databaseInterfaces.value(QThread::currentThread()); + return databaseInterfaces.value(QThread::currentThread()); } AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reasonStr, int &secondsLeft) { - if (name.size() > 35) - name = name.left(35); - - Server_DatabaseInterface *databaseInterface = getDatabaseInterface(); - - QWriteLocker locker(&clientsLock); - - AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, reasonStr, secondsLeft); - if ((authState == NotLoggedIn) || (authState == UserIsBanned || authState == UsernameInvalid)) - return authState; - - ServerInfo_User data = databaseInterface->getUserData(name, true); - data.set_address(session->getAddress().toStdString()); - name = QString::fromStdString(data.name()); // Compensate for case indifference - - databaseInterface->lockSessionTables(); - - if (authState == PasswordRight) { - if (users.contains(name) || databaseInterface->userSessionExists(name)) { - qDebug("Login denied: would overwrite old session"); - databaseInterface->unlockSessionTables(); - return WouldOverwriteOldSession; - } - } else if (authState == UnknownUser) { - // Change user name so that no two users have the same names, - // don't interfere with registered user names though. - QString tempName = name; - int i = 0; - while (users.contains(tempName) || databaseInterface->userExists(tempName) || databaseInterface->userSessionExists(tempName)) - tempName = name + "_" + QString::number(++i); - name = tempName; - data.set_name(name.toStdString()); - } - - users.insert(name, session); - qDebug() << "Server::loginUser:" << session << "name=" << name; - - data.set_session_id(databaseInterface->startSession(name, session->getAddress())); - databaseInterface->unlockSessionTables(); - - usersBySessionId.insert(data.session_id(), session); - - qDebug() << "session id:" << data.session_id(); - session->setUserInfo(data); - - Event_UserJoined event; - event.mutable_user_info()->CopyFrom(session->copyUserInfo(false)); - SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - for (int i = 0; i < clients.size(); ++i) - if (clients[i]->getAcceptsUserListChanges()) - clients[i]->sendProtocolItem(*se); - delete se; - - event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true)); - locker.unlock(); - - se = Server_ProtocolHandler::prepareSessionEvent(event); - sendIsl_SessionEvent(*se); - delete se; - - return authState; + if (name.size() > 35) + name = name.left(35); + + Server_DatabaseInterface *databaseInterface = getDatabaseInterface(); + + QWriteLocker locker(&clientsLock); + + AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, reasonStr, secondsLeft); + if ((authState == NotLoggedIn) || (authState == UserIsBanned || authState == UsernameInvalid)) + return authState; + + ServerInfo_User data = databaseInterface->getUserData(name, true); + data.set_address(session->getAddress().toStdString()); + name = QString::fromStdString(data.name()); // Compensate for case indifference + + databaseInterface->lockSessionTables(); + + if (authState == PasswordRight) { + if (users.contains(name) || databaseInterface->userSessionExists(name)) { + qDebug("Login denied: would overwrite old session"); + databaseInterface->unlockSessionTables(); + return WouldOverwriteOldSession; + } + } else if (authState == UnknownUser) { + // Change user name so that no two users have the same names, + // don't interfere with registered user names though. + QString tempName = name; + int i = 0; + while (users.contains(tempName) || databaseInterface->userExists(tempName) || databaseInterface->userSessionExists(tempName)) + tempName = name + "_" + QString::number(++i); + name = tempName; + data.set_name(name.toStdString()); + } + + users.insert(name, session); + qDebug() << "Server::loginUser:" << session << "name=" << name; + + data.set_session_id(databaseInterface->startSession(name, session->getAddress())); + databaseInterface->unlockSessionTables(); + + usersBySessionId.insert(data.session_id(), session); + + qDebug() << "session id:" << data.session_id(); + session->setUserInfo(data); + + Event_UserJoined event; + event.mutable_user_info()->CopyFrom(session->copyUserInfo(false)); + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsUserListChanges()) + clients[i]->sendProtocolItem(*se); + delete se; + + event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true)); + locker.unlock(); + + se = Server_ProtocolHandler::prepareSessionEvent(event); + sendIsl_SessionEvent(*se); + delete se; + + return authState; } void Server::addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId) { - QWriteLocker locker(&persistentPlayersLock); - persistentPlayers.insert(userName, PlayerReference(roomId, gameId, playerId)); + QWriteLocker locker(&persistentPlayersLock); + persistentPlayers.insert(userName, PlayerReference(roomId, gameId, playerId)); } void Server::removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId) { - QWriteLocker locker(&persistentPlayersLock); - persistentPlayers.remove(userName, PlayerReference(roomId, gameId, playerId)); + QWriteLocker locker(&persistentPlayersLock); + persistentPlayers.remove(userName, PlayerReference(roomId, gameId, playerId)); } QList Server::getPersistentPlayerReferences(const QString &userName) const { - QReadLocker locker(&persistentPlayersLock); - return persistentPlayers.values(userName); + QReadLocker locker(&persistentPlayersLock); + return persistentPlayers.values(userName); } Server_AbstractUserInterface *Server::findUser(const QString &userName) const { - // Call this only with clientsLock set. - - Server_AbstractUserInterface *userHandler = users.value(userName); - if (userHandler) - return userHandler; - else - return externalUsers.value(userName); + // Call this only with clientsLock set. + + Server_AbstractUserInterface *userHandler = users.value(userName); + if (userHandler) + return userHandler; + else + return externalUsers.value(userName); } void Server::addClient(Server_ProtocolHandler *client) { - QWriteLocker locker(&clientsLock); - clients << client; + QWriteLocker locker(&clientsLock); + clients << client; } void Server::removeClient(Server_ProtocolHandler *client) { - QWriteLocker locker(&clientsLock); - clients.removeAt(clients.indexOf(client)); - ServerInfo_User *data = client->getUserInfo(); - if (data) { - Event_UserLeft event; - event.set_name(data->name()); - SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - for (int i = 0; i < clients.size(); ++i) - if (clients[i]->getAcceptsUserListChanges()) - clients[i]->sendProtocolItem(*se); - sendIsl_SessionEvent(*se); - delete se; - - users.remove(QString::fromStdString(data->name())); - qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name()); - - if (data->has_session_id()) { - const qint64 sessionId = data->session_id(); - usersBySessionId.remove(sessionId); - emit endSession(sessionId); - qDebug() << "closed session id:" << sessionId; - } - } - qDebug() << "Server::removeClient: removed" << (void *) client << ";" << clients.size() << "clients; " << users.size() << "users left"; + QWriteLocker locker(&clientsLock); + clients.removeAt(clients.indexOf(client)); + ServerInfo_User *data = client->getUserInfo(); + if (data) { + Event_UserLeft event; + event.set_name(data->name()); + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsUserListChanges()) + clients[i]->sendProtocolItem(*se); + sendIsl_SessionEvent(*se); + delete se; + + users.remove(QString::fromStdString(data->name())); + qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name()); + + if (data->has_session_id()) { + const qint64 sessionId = data->session_id(); + usersBySessionId.remove(sessionId); + emit endSession(sessionId); + qDebug() << "closed session id:" << sessionId; + } + } + qDebug() << "Server::removeClient: removed" << (void *) client << ";" << clients.size() << "clients; " << users.size() << "users left"; } void Server::externalUserJoined(const ServerInfo_User &userInfo) { - // This function is always called from the main thread via signal/slot. - clientsLock.lockForWrite(); - - Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo)); - externalUsers.insert(QString::fromStdString(userInfo.name()), newUser); - externalUsersBySessionId.insert(userInfo.session_id(), newUser); - - Event_UserJoined event; - event.mutable_user_info()->CopyFrom(userInfo); - - SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - for (int i = 0; i < clients.size(); ++i) - if (clients[i]->getAcceptsUserListChanges()) - clients[i]->sendProtocolItem(*se); - delete se; - clientsLock.unlock(); - - ResponseContainer rc(-1); - newUser->joinPersistentGames(rc); - newUser->sendResponseContainer(rc, Response::RespNothing); + // This function is always called from the main thread via signal/slot. + clientsLock.lockForWrite(); + + Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo)); + externalUsers.insert(QString::fromStdString(userInfo.name()), newUser); + externalUsersBySessionId.insert(userInfo.session_id(), newUser); + + Event_UserJoined event; + event.mutable_user_info()->CopyFrom(userInfo); + + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsUserListChanges()) + clients[i]->sendProtocolItem(*se); + delete se; + clientsLock.unlock(); + + ResponseContainer rc(-1); + newUser->joinPersistentGames(rc); + newUser->sendResponseContainer(rc, Response::RespNothing); } void Server::externalUserLeft(const QString &userName) { - // This function is always called from the main thread via signal/slot. - - clientsLock.lockForWrite(); - Server_AbstractUserInterface *user = externalUsers.take(userName); - externalUsersBySessionId.remove(user->getUserInfo()->session_id()); - clientsLock.unlock(); - - QMap > userGames(user->getGames()); - QMapIterator > userGamesIterator(userGames); - roomsLock.lockForRead(); - while (userGamesIterator.hasNext()) { - userGamesIterator.next(); - Server_Room *room = rooms.value(userGamesIterator.value().first); - if (!room) - continue; - - QReadLocker roomGamesLocker(&room->gamesLock); - Server_Game *game = room->getGames().value(userGamesIterator.key()); - if (!game) - continue; - - QMutexLocker gameLocker(&game->gameMutex); - Server_Player *player = game->getPlayers().value(userGamesIterator.value().second); - if (!player) - continue; - - player->disconnectClient(); - } - roomsLock.unlock(); - - delete user; - - Event_UserLeft event; - event.set_name(userName.toStdString()); - - SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - clientsLock.lockForRead(); - for (int i = 0; i < clients.size(); ++i) - if (clients[i]->getAcceptsUserListChanges()) - clients[i]->sendProtocolItem(*se); - clientsLock.unlock(); - delete se; + // This function is always called from the main thread via signal/slot. + + clientsLock.lockForWrite(); + Server_AbstractUserInterface *user = externalUsers.take(userName); + externalUsersBySessionId.remove(user->getUserInfo()->session_id()); + clientsLock.unlock(); + + QMap > userGames(user->getGames()); + QMapIterator > userGamesIterator(userGames); + roomsLock.lockForRead(); + while (userGamesIterator.hasNext()) { + userGamesIterator.next(); + Server_Room *room = rooms.value(userGamesIterator.value().first); + if (!room) + continue; + + QReadLocker roomGamesLocker(&room->gamesLock); + Server_Game *game = room->getGames().value(userGamesIterator.key()); + if (!game) + continue; + + QMutexLocker gameLocker(&game->gameMutex); + Server_Player *player = game->getPlayers().value(userGamesIterator.value().second); + if (!player) + continue; + + player->disconnectClient(); + } + roomsLock.unlock(); + + delete user; + + Event_UserLeft event; + event.set_name(userName.toStdString()); + + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); + clientsLock.lockForRead(); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsUserListChanges()) + clients[i]->sendProtocolItem(*se); + clientsLock.unlock(); + delete se; } void Server::externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo) { - // This function is always called from the main thread via signal/slot. - QReadLocker locker(&roomsLock); - - Server_Room *room = rooms.value(roomId); - if (!room) { - qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found"; - return; - } - room->addExternalUser(userInfo); + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found"; + return; + } + room->addExternalUser(userInfo); } void Server::externalRoomUserLeft(int roomId, const QString &userName) { - // This function is always called from the main thread via signal/slot. - QReadLocker locker(&roomsLock); - - Server_Room *room = rooms.value(roomId); - if (!room) { - qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found"; - return; - } - room->removeExternalUser(userName); + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found"; + return; + } + room->removeExternalUser(userName); } void Server::externalRoomSay(int roomId, const QString &userName, const QString &message) { - // This function is always called from the main thread via signal/slot. - QReadLocker locker(&roomsLock); - - Server_Room *room = rooms.value(roomId); - if (!room) { - qDebug() << "externalRoomSay: room id=" << roomId << "not found"; - return; - } - room->say(userName, message, false); + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomSay: room id=" << roomId << "not found"; + return; + } + room->say(userName, message, false); } void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo) { - // This function is always called from the main thread via signal/slot. - QReadLocker locker(&roomsLock); - - Server_Room *room = rooms.value(roomId); - if (!room) { - qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found"; - return; - } - room->updateExternalGameList(gameInfo); + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found"; + return; + } + room->updateExternalGameList(gameInfo); } void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId) { - // This function is always called from the main thread via signal/slot. - - try { - QReadLocker roomsLocker(&roomsLock); - QReadLocker clientsLocker(&clientsLock); - - Server_Room *room = rooms.value(roomId); - if (!room) { - qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found"; - throw Response::RespNotInRoom; - } - Server_AbstractUserInterface *userInterface = externalUsersBySessionId.value(sessionId); - if (!userInterface) { - qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found"; - throw Response::RespNotInRoom; - } - - ResponseContainer responseContainer(cmdId); - Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface); - userInterface->sendResponseContainer(responseContainer, responseCode); - } catch (Response::ResponseCode code) { - Response response; - response.set_cmd_id(cmdId); - response.set_response_code(code); - - sendIsl_Response(response, serverId, sessionId); - } + // This function is always called from the main thread via signal/slot. + + try { + QReadLocker roomsLocker(&roomsLock); + QReadLocker clientsLocker(&clientsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found"; + throw Response::RespNotInRoom; + } + Server_AbstractUserInterface *userInterface = externalUsersBySessionId.value(sessionId); + if (!userInterface) { + qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found"; + throw Response::RespNotInRoom; + } + + ResponseContainer responseContainer(cmdId); + Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface); + userInterface->sendResponseContainer(responseContainer, responseCode); + } catch (Response::ResponseCode code) { + Response response; + response.set_cmd_id(cmdId); + response.set_response_code(code); + + sendIsl_Response(response, serverId, sessionId); + } } void Server::externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId) { - // This function is always called from the main thread via signal/slot. - - try { - ResponseContainer responseContainer(cont.cmd_id()); - Response::ResponseCode finalResponseCode = Response::RespOk; - - QReadLocker roomsLocker(&roomsLock); - Server_Room *room = rooms.value(cont.room_id()); - if (!room) { - qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found"; - throw Response::RespNotInRoom; - } - - QReadLocker roomGamesLocker(&room->gamesLock); - Server_Game *game = room->getGames().value(cont.game_id()); - if (!game) { - qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found"; - throw Response::RespNotInRoom; - } - - QMutexLocker gameLocker(&game->gameMutex); - Server_Player *player = game->getPlayers().value(playerId); - if (!player) { - qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found"; - throw Response::RespNotInRoom; - } - - GameEventStorage ges; - for (int i = cont.game_command_size() - 1; i >= 0; --i) { - const GameCommand &sc = cont.game_command(i); - qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString()); - - Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges); - - if (resp != Response::RespOk) - finalResponseCode = resp; - } - ges.sendToGame(game); - - if (finalResponseCode != Response::RespNothing) { - player->playerMutex.lock(); - player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode); - player->playerMutex.unlock(); - } - } catch (Response::ResponseCode code) { - Response response; - response.set_cmd_id(cont.cmd_id()); - response.set_response_code(code); - - sendIsl_Response(response, serverId, sessionId); - } + // This function is always called from the main thread via signal/slot. + + try { + ResponseContainer responseContainer(cont.cmd_id()); + Response::ResponseCode finalResponseCode = Response::RespOk; + + QReadLocker roomsLocker(&roomsLock); + Server_Room *room = rooms.value(cont.room_id()); + if (!room) { + qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found"; + throw Response::RespNotInRoom; + } + + QReadLocker roomGamesLocker(&room->gamesLock); + Server_Game *game = room->getGames().value(cont.game_id()); + if (!game) { + qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found"; + throw Response::RespNotInRoom; + } + + QMutexLocker gameLocker(&game->gameMutex); + Server_Player *player = game->getPlayers().value(playerId); + if (!player) { + qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found"; + throw Response::RespNotInRoom; + } + + GameEventStorage ges; + for (int i = cont.game_command_size() - 1; i >= 0; --i) { + const GameCommand &sc = cont.game_command(i); + qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString()); + + Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges); + + if (resp != Response::RespOk) + finalResponseCode = resp; + } + ges.sendToGame(game); + + if (finalResponseCode != Response::RespNothing) { + player->playerMutex.lock(); + player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode); + player->playerMutex.unlock(); + } + } catch (Response::ResponseCode code) { + Response response; + response.set_cmd_id(cont.cmd_id()); + response.set_response_code(code); + + sendIsl_Response(response, serverId, sessionId); + } } void Server::externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId) { - // This function is always called from the main thread via signal/slot. - - QReadLocker usersLocker(&clientsLock); - - Server_ProtocolHandler *client = usersBySessionId.value(sessionId); - if (!client) { - qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found"; - return; - } - client->sendProtocolItem(cont); + // This function is always called from the main thread via signal/slot. + + QReadLocker usersLocker(&clientsLock); + + Server_ProtocolHandler *client = usersBySessionId.value(sessionId); + if (!client) { + qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found"; + return; + } + client->sendProtocolItem(cont); } void Server::externalResponseReceived(const Response &resp, qint64 sessionId) { - // This function is always called from the main thread via signal/slot. - - QReadLocker usersLocker(&clientsLock); - - Server_ProtocolHandler *client = usersBySessionId.value(sessionId); - if (!client) { - qDebug() << "externalResponseReceived: session" << sessionId << "not found"; - return; - } - client->sendProtocolItem(resp); + // This function is always called from the main thread via signal/slot. + + QReadLocker usersLocker(&clientsLock); + + Server_ProtocolHandler *client = usersBySessionId.value(sessionId); + if (!client) { + qDebug() << "externalResponseReceived: session" << sessionId << "not found"; + return; + } + client->sendProtocolItem(resp); } void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl) { - // This function is always called from the main thread via signal/slot. - - Event_ListRooms event; - event.add_room_list()->CopyFrom(roomInfo); - - SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); + // This function is always called from the main thread via signal/slot. + + Event_ListRooms event; + event.add_room_list()->CopyFrom(roomInfo); + + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - clientsLock.lockForRead(); - for (int i = 0; i < clients.size(); ++i) - if (clients[i]->getAcceptsRoomListChanges()) - clients[i]->sendProtocolItem(*se); - clientsLock.unlock(); - - if (sendToIsl) - sendIsl_SessionEvent(*se); - - delete se; + clientsLock.lockForRead(); + for (int i = 0; i < clients.size(); ++i) + if (clients[i]->getAcceptsRoomListChanges()) + clients[i]->sendProtocolItem(*se); + clientsLock.unlock(); + + if (sendToIsl) + sendIsl_SessionEvent(*se); + + delete se; } void Server::addRoom(Server_Room *newRoom) { - QWriteLocker locker(&roomsLock); - qDebug() << "Adding room: ID=" << newRoom->getId() << "name=" << newRoom->getName(); - rooms.insert(newRoom->getId(), newRoom); - connect(newRoom, SIGNAL(roomInfoChanged(ServerInfo_Room)), this, SLOT(broadcastRoomUpdate(const ServerInfo_Room &)), Qt::QueuedConnection); + QWriteLocker locker(&roomsLock); + qDebug() << "Adding room: ID=" << newRoom->getId() << "name=" << newRoom->getName(); + rooms.insert(newRoom->getId(), newRoom); + connect(newRoom, SIGNAL(roomInfoChanged(ServerInfo_Room)), this, SLOT(broadcastRoomUpdate(const ServerInfo_Room &)), Qt::QueuedConnection); } int Server::getUsersCount() const { - QReadLocker locker(&clientsLock); - return users.size(); + QReadLocker locker(&clientsLock); + return users.size(); } int Server::getGamesCount() const { - int result = 0; - QReadLocker locker(&roomsLock); - QMapIterator roomIterator(rooms); - while (roomIterator.hasNext()) { - Server_Room *room = roomIterator.next().value(); - QReadLocker roomLocker(&room->gamesLock); - result += room->getGames().size(); - } - return result; + int result = 0; + QReadLocker locker(&roomsLock); + QMapIterator roomIterator(rooms); + while (roomIterator.hasNext()) { + Server_Room *room = roomIterator.next().value(); + QReadLocker roomLocker(&room->gamesLock); + result += room->getGames().size(); + } + return result; } void Server::sendIsl_Response(const Response &item, int serverId, qint64 sessionId) { - IslMessage msg; - msg.set_message_type(IslMessage::RESPONSE); - if (sessionId != -1) - msg.set_session_id(sessionId); - msg.mutable_response()->CopyFrom(item); - - emit sigSendIslMessage(msg, serverId); + IslMessage msg; + msg.set_message_type(IslMessage::RESPONSE); + if (sessionId != -1) + msg.set_session_id(sessionId); + msg.mutable_response()->CopyFrom(item); + + emit sigSendIslMessage(msg, serverId); } void Server::sendIsl_SessionEvent(const SessionEvent &item, int serverId, qint64 sessionId) { - IslMessage msg; - msg.set_message_type(IslMessage::SESSION_EVENT); - if (sessionId != -1) - msg.set_session_id(sessionId); - msg.mutable_session_event()->CopyFrom(item); - - emit sigSendIslMessage(msg, serverId); + IslMessage msg; + msg.set_message_type(IslMessage::SESSION_EVENT); + if (sessionId != -1) + msg.set_session_id(sessionId); + msg.mutable_session_event()->CopyFrom(item); + + emit sigSendIslMessage(msg, serverId); } void Server::sendIsl_GameEventContainer(const GameEventContainer &item, int serverId, qint64 sessionId) { - IslMessage msg; - msg.set_message_type(IslMessage::GAME_EVENT_CONTAINER); - if (sessionId != -1) - msg.set_session_id(sessionId); - msg.mutable_game_event_container()->CopyFrom(item); - - emit sigSendIslMessage(msg, serverId); + IslMessage msg; + msg.set_message_type(IslMessage::GAME_EVENT_CONTAINER); + if (sessionId != -1) + msg.set_session_id(sessionId); + msg.mutable_game_event_container()->CopyFrom(item); + + emit sigSendIslMessage(msg, serverId); } void Server::sendIsl_RoomEvent(const RoomEvent &item, int serverId, qint64 sessionId) { - IslMessage msg; - msg.set_message_type(IslMessage::ROOM_EVENT); - if (sessionId != -1) - msg.set_session_id(sessionId); - msg.mutable_room_event()->CopyFrom(item); - - emit sigSendIslMessage(msg, serverId); + IslMessage msg; + msg.set_message_type(IslMessage::ROOM_EVENT); + if (sessionId != -1) + msg.set_session_id(sessionId); + msg.mutable_room_event()->CopyFrom(item); + + emit sigSendIslMessage(msg, serverId); } void Server::sendIsl_GameCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId, int playerId) { - IslMessage msg; - msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER); - msg.set_session_id(sessionId); - msg.set_player_id(playerId); - - CommandContainer *cont = msg.mutable_game_command(); - cont->CopyFrom(item); - cont->set_room_id(roomId); - - emit sigSendIslMessage(msg, serverId); + IslMessage msg; + msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER); + msg.set_session_id(sessionId); + msg.set_player_id(playerId); + + CommandContainer *cont = msg.mutable_game_command(); + cont->CopyFrom(item); + cont->set_room_id(roomId); + + emit sigSendIslMessage(msg, serverId); } void Server::sendIsl_RoomCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId) { - IslMessage msg; - msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER); - msg.set_session_id(sessionId); - - CommandContainer *cont = msg.mutable_room_command(); - cont->CopyFrom(item); - cont->set_room_id(roomId); - - emit sigSendIslMessage(msg, serverId); + IslMessage msg; + msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER); + msg.set_session_id(sessionId); + + CommandContainer *cont = msg.mutable_room_command(); + cont->CopyFrom(item); + cont->set_room_id(roomId); + + emit sigSendIslMessage(msg, serverId); } diff --git a/common/server.h b/common/server.h index 631d2d2e8..6742f6e2b 100644 --- a/common/server.h +++ b/common/server.h @@ -31,87 +31,87 @@ enum AuthenticationResult { NotLoggedIn = 0, PasswordRight = 1, UnknownUser = 2, class Server : public QObject { - Q_OBJECT + Q_OBJECT signals: - void pingClockTimeout(); - void sigSendIslMessage(const IslMessage &message, int serverId); - void endSession(qint64 sessionId); + void pingClockTimeout(); + void sigSendIslMessage(const IslMessage &message, int serverId); + void endSession(qint64 sessionId); private slots: - void broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl = false); + void broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl = false); public: - mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock - Server(bool _threaded, QObject *parent = 0); - ~Server(); - void setThreaded(bool _threaded) { threaded = _threaded; } - AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft); - const QMap &getRooms() { return rooms; } - - Server_AbstractUserInterface *findUser(const QString &userName) const; - const QMap &getUsers() const { return users; } - const QMap &getUsersBySessionId() const { return usersBySessionId; } - void addClient(Server_ProtocolHandler *player); - void removeClient(Server_ProtocolHandler *player); - virtual QString getLoginMessage() const { return QString(); } - - virtual bool getGameShouldPing() const { return false; } - virtual int getMaxGameInactivityTime() const { return 9999999; } - virtual int getMaxPlayerInactivityTime() const { return 9999999; } - virtual int getMessageCountingInterval() const { return 0; } - virtual int getMaxMessageCountPerInterval() const { return 0; } - virtual int getMaxMessageSizePerInterval() const { return 0; } - virtual int getMaxGamesPerUser() const { return 0; } - virtual bool getThreaded() const { return false; } - - Server_DatabaseInterface *getDatabaseInterface() const; - int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; } - - void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1); - void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1); - void sendIsl_GameEventContainer(const GameEventContainer &item, int serverId = -1, qint64 sessionId = -1); - void sendIsl_RoomEvent(const RoomEvent &item, int serverId = -1, qint64 sessionId = -1); - void sendIsl_GameCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId, int playerId); - void sendIsl_RoomCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId); - - void addExternalUser(const ServerInfo_User &userInfo); - void removeExternalUser(const QString &userName); - const QMap &getExternalUsers() const { return externalUsers; } - - void addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); - void removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); - QList getPersistentPlayerReferences(const QString &userName) const; + mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock + Server(bool _threaded, QObject *parent = 0); + ~Server(); + void setThreaded(bool _threaded) { threaded = _threaded; } + AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft); + const QMap &getRooms() { return rooms; } + + Server_AbstractUserInterface *findUser(const QString &userName) const; + const QMap &getUsers() const { return users; } + const QMap &getUsersBySessionId() const { return usersBySessionId; } + void addClient(Server_ProtocolHandler *player); + void removeClient(Server_ProtocolHandler *player); + virtual QString getLoginMessage() const { return QString(); } + + virtual bool getGameShouldPing() const { return false; } + virtual int getMaxGameInactivityTime() const { return 9999999; } + virtual int getMaxPlayerInactivityTime() const { return 9999999; } + virtual int getMessageCountingInterval() const { return 0; } + virtual int getMaxMessageCountPerInterval() const { return 0; } + virtual int getMaxMessageSizePerInterval() const { return 0; } + virtual int getMaxGamesPerUser() const { return 0; } + virtual bool getThreaded() const { return false; } + + Server_DatabaseInterface *getDatabaseInterface() const; + int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; } + + void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1); + void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1); + void sendIsl_GameEventContainer(const GameEventContainer &item, int serverId = -1, qint64 sessionId = -1); + void sendIsl_RoomEvent(const RoomEvent &item, int serverId = -1, qint64 sessionId = -1); + void sendIsl_GameCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId, int playerId); + void sendIsl_RoomCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId); + + void addExternalUser(const ServerInfo_User &userInfo); + void removeExternalUser(const QString &userName); + const QMap &getExternalUsers() const { return externalUsers; } + + void addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); + void removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); + QList getPersistentPlayerReferences(const QString &userName) const; private: - bool threaded; - QMultiMap persistentPlayers; - mutable QReadWriteLock persistentPlayersLock; - int nextLocalGameId; - QMutex nextLocalGameIdMutex; -protected slots: - void externalUserJoined(const ServerInfo_User &userInfo); - void externalUserLeft(const QString &userName); - void externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo); - void externalRoomUserLeft(int roomId, const QString &userName); - void externalRoomSay(int roomId, const QString &userName, const QString &message); - void externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo); - void externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId); - void externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId); - void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); - void externalResponseReceived(const Response &resp, qint64 sessionId); - - virtual void doSendIslMessage(const IslMessage &msg, int serverId) { } + bool threaded; + QMultiMap persistentPlayers; + mutable QReadWriteLock persistentPlayersLock; + int nextLocalGameId; + QMutex nextLocalGameIdMutex; +protected slots: + void externalUserJoined(const ServerInfo_User &userInfo); + void externalUserLeft(const QString &userName); + void externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo); + void externalRoomUserLeft(int roomId, const QString &userName); + void externalRoomSay(int roomId, const QString &userName, const QString &message); + void externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo); + void externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId); + void externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId); + void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); + void externalResponseReceived(const Response &resp, qint64 sessionId); + + virtual void doSendIslMessage(const IslMessage &msg, int serverId) { } protected: - void prepareDestroy(); - void setDatabaseInterface(Server_DatabaseInterface *_databaseInterface); - QList clients; - QMap usersBySessionId; - QMap users; - QMap externalUsersBySessionId; - QMap externalUsers; - QMap rooms; - QMap databaseInterfaces; - - int getUsersCount() const; - int getGamesCount() const; - void addRoom(Server_Room *newRoom); + void prepareDestroy(); + void setDatabaseInterface(Server_DatabaseInterface *_databaseInterface); + QList clients; + QMap usersBySessionId; + QMap users; + QMap externalUsersBySessionId; + QMap externalUsers; + QMap rooms; + QMap databaseInterfaces; + + int getUsersCount() const; + int getGamesCount() const; + void addRoom(Server_Room *newRoom); }; #endif diff --git a/common/server_abstractuserinterface.cpp b/common/server_abstractuserinterface.cpp index ea4b0f6b7..d00c5034b 100644 --- a/common/server_abstractuserinterface.cpp +++ b/common/server_abstractuserinterface.cpp @@ -15,82 +15,82 @@ void Server_AbstractUserInterface::sendProtocolItemByType(ServerMessage::MessageType type, const ::google::protobuf::Message &item) { - switch (type) { - case ServerMessage::RESPONSE: sendProtocolItem(static_cast(item)); break; - case ServerMessage::SESSION_EVENT: sendProtocolItem(static_cast(item)); break; - case ServerMessage::GAME_EVENT_CONTAINER: sendProtocolItem(static_cast(item)); break; - case ServerMessage::ROOM_EVENT: sendProtocolItem(static_cast(item)); break; - } + switch (type) { + case ServerMessage::RESPONSE: sendProtocolItem(static_cast(item)); break; + case ServerMessage::SESSION_EVENT: sendProtocolItem(static_cast(item)); break; + case ServerMessage::GAME_EVENT_CONTAINER: sendProtocolItem(static_cast(item)); break; + case ServerMessage::ROOM_EVENT: sendProtocolItem(static_cast(item)); break; + } } SessionEvent *Server_AbstractUserInterface::prepareSessionEvent(const ::google::protobuf::Message &sessionEvent) { - SessionEvent *event = new SessionEvent; - event->GetReflection()->MutableMessage(event, sessionEvent.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(sessionEvent); - return event; + SessionEvent *event = new SessionEvent; + event->GetReflection()->MutableMessage(event, sessionEvent.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(sessionEvent); + return event; } void Server_AbstractUserInterface::sendResponseContainer(const ResponseContainer &responseContainer, Response::ResponseCode responseCode) { - const QList > &preResponseQueue = responseContainer.getPreResponseQueue(); - for (int i = 0; i < preResponseQueue.size(); ++i) - sendProtocolItemByType(preResponseQueue[i].first, *preResponseQueue[i].second); - - if (responseCode != Response::RespNothing) { - Response response; - response.set_cmd_id(responseContainer.getCmdId()); - response.set_response_code(responseCode); - ::google::protobuf::Message *responseExtension = responseContainer.getResponseExtension(); - if (responseExtension) - response.GetReflection()->MutableMessage(&response, responseExtension->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*responseExtension); - sendProtocolItem(response); - } - - const QList > &postResponseQueue = responseContainer.getPostResponseQueue(); - for (int i = 0; i < postResponseQueue.size(); ++i) - sendProtocolItemByType(postResponseQueue[i].first, *postResponseQueue[i].second); + const QList > &preResponseQueue = responseContainer.getPreResponseQueue(); + for (int i = 0; i < preResponseQueue.size(); ++i) + sendProtocolItemByType(preResponseQueue[i].first, *preResponseQueue[i].second); + + if (responseCode != Response::RespNothing) { + Response response; + response.set_cmd_id(responseContainer.getCmdId()); + response.set_response_code(responseCode); + ::google::protobuf::Message *responseExtension = responseContainer.getResponseExtension(); + if (responseExtension) + response.GetReflection()->MutableMessage(&response, responseExtension->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*responseExtension); + sendProtocolItem(response); + } + + const QList > &postResponseQueue = responseContainer.getPostResponseQueue(); + for (int i = 0; i < postResponseQueue.size(); ++i) + sendProtocolItemByType(postResponseQueue[i].first, *postResponseQueue[i].second); } void Server_AbstractUserInterface::playerRemovedFromGame(Server_Game *game) { - qDebug() << "Server_AbstractUserInterface::playerRemovedFromGame(): gameId =" << game->getGameId(); - - QMutexLocker locker(&gameListMutex); - games.remove(game->getGameId()); + qDebug() << "Server_AbstractUserInterface::playerRemovedFromGame(): gameId =" << game->getGameId(); + + QMutexLocker locker(&gameListMutex); + games.remove(game->getGameId()); } void Server_AbstractUserInterface::playerAddedToGame(int gameId, int roomId, int playerId) { - qDebug() << "Server_AbstractUserInterface::playerAddedToGame(): gameId =" << gameId; - - QMutexLocker locker(&gameListMutex); - games.insert(gameId, QPair(roomId, playerId)); + qDebug() << "Server_AbstractUserInterface::playerAddedToGame(): gameId =" << gameId; + + QMutexLocker locker(&gameListMutex); + games.insert(gameId, QPair(roomId, playerId)); } void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc) { - QList gamesToJoin = server->getPersistentPlayerReferences(QString::fromStdString(userInfo->name())); - - server->roomsLock.lockForRead(); - for (int i = 0; i < gamesToJoin.size(); ++i) { - const PlayerReference &pr = gamesToJoin.at(i); - - Server_Room *room = server->getRooms().value(pr.getRoomId()); - if (!room) - continue; - QReadLocker roomGamesLocker(&room->gamesLock); - - Server_Game *game = room->getGames().value(pr.getGameId()); - if (!game) - continue; - QMutexLocker gameLocker(&game->gameMutex); - - Server_Player *player = game->getPlayers().value(pr.getPlayerId()); - - player->setUserInterface(this); - playerAddedToGame(game->getGameId(), room->getId(), player->getPlayerId()); - - game->createGameJoinedEvent(player, rc, true); - } - server->roomsLock.unlock(); + QList gamesToJoin = server->getPersistentPlayerReferences(QString::fromStdString(userInfo->name())); + + server->roomsLock.lockForRead(); + for (int i = 0; i < gamesToJoin.size(); ++i) { + const PlayerReference &pr = gamesToJoin.at(i); + + Server_Room *room = server->getRooms().value(pr.getRoomId()); + if (!room) + continue; + QReadLocker roomGamesLocker(&room->gamesLock); + + Server_Game *game = room->getGames().value(pr.getGameId()); + if (!game) + continue; + QMutexLocker gameLocker(&game->gameMutex); + + Server_Player *player = game->getPlayers().value(pr.getPlayerId()); + + player->setUserInterface(this); + playerAddedToGame(game->getGameId(), room->getId(), player->getPlayerId()); + + game->createGameJoinedEvent(player, rc, true); + } + server->roomsLock.unlock(); } diff --git a/common/server_abstractuserinterface.h b/common/server_abstractuserinterface.h index 485162e06..1253c0f4d 100644 --- a/common/server_abstractuserinterface.h +++ b/common/server_abstractuserinterface.h @@ -18,31 +18,31 @@ class Server_Game; class Server_AbstractUserInterface : public ServerInfo_User_Container { private: - mutable QMutex gameListMutex; - QMap > games; // gameId -> (roomId, playerId) + mutable QMutex gameListMutex; + QMap > games; // gameId -> (roomId, playerId) protected: - Server *server; + Server *server; public: - Server_AbstractUserInterface(Server *_server) : server(_server) { } - Server_AbstractUserInterface(Server *_server, const ServerInfo_User_Container &other) : ServerInfo_User_Container(other), server(_server) { } - virtual ~Server_AbstractUserInterface() { } - - virtual int getLastCommandTime() const = 0; - - void playerRemovedFromGame(Server_Game *game); - void playerAddedToGame(int gameId, int roomId, int playerId); - void joinPersistentGames(ResponseContainer &rc); - - QMap > getGames() const { QMutexLocker locker(&gameListMutex); return games; } - - virtual void sendProtocolItem(const Response &item) = 0; - virtual void sendProtocolItem(const SessionEvent &item) = 0; - virtual void sendProtocolItem(const GameEventContainer &item) = 0; - virtual void sendProtocolItem(const RoomEvent &item) = 0; - void sendProtocolItemByType(ServerMessage::MessageType type, const ::google::protobuf::Message &item); - - static SessionEvent *prepareSessionEvent(const ::google::protobuf::Message &sessionEvent); - void sendResponseContainer(const ResponseContainer &responseContainer, Response::ResponseCode responseCode); + Server_AbstractUserInterface(Server *_server) : server(_server) { } + Server_AbstractUserInterface(Server *_server, const ServerInfo_User_Container &other) : ServerInfo_User_Container(other), server(_server) { } + virtual ~Server_AbstractUserInterface() { } + + virtual int getLastCommandTime() const = 0; + + void playerRemovedFromGame(Server_Game *game); + void playerAddedToGame(int gameId, int roomId, int playerId); + void joinPersistentGames(ResponseContainer &rc); + + QMap > getGames() const { QMutexLocker locker(&gameListMutex); return games; } + + virtual void sendProtocolItem(const Response &item) = 0; + virtual void sendProtocolItem(const SessionEvent &item) = 0; + virtual void sendProtocolItem(const GameEventContainer &item) = 0; + virtual void sendProtocolItem(const RoomEvent &item) = 0; + void sendProtocolItemByType(ServerMessage::MessageType type, const ::google::protobuf::Message &item); + + static SessionEvent *prepareSessionEvent(const ::google::protobuf::Message &sessionEvent); + void sendResponseContainer(const ResponseContainer &responseContainer, Response::ResponseCode responseCode); }; #endif diff --git a/common/server_arrow.cpp b/common/server_arrow.cpp index 9cbb2c384..a668cc25d 100644 --- a/common/server_arrow.cpp +++ b/common/server_arrow.cpp @@ -14,17 +14,17 @@ Server_Arrow::Server_Arrow(int _id, Server_Card *_startCard, Server_ArrowTarget void Server_Arrow::getInfo(ServerInfo_Arrow *info) { - info->set_id(id); - info->set_start_player_id(startCard->getZone()->getPlayer()->getPlayerId()); - info->set_start_zone(startCard->getZone()->getName().toStdString()); - info->set_start_card_id(startCard->getId()); - info->mutable_arrow_color()->CopyFrom(arrowColor); + info->set_id(id); + info->set_start_player_id(startCard->getZone()->getPlayer()->getPlayerId()); + info->set_start_zone(startCard->getZone()->getName().toStdString()); + info->set_start_card_id(startCard->getId()); + info->mutable_arrow_color()->CopyFrom(arrowColor); - Server_Card *targetCard = qobject_cast(targetItem); - if (targetCard) { - info->set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerId()); - info->set_target_zone(targetCard->getZone()->getName().toStdString()); - info->set_target_card_id(targetCard->getId()); - } else - info->set_target_player_id(static_cast(targetItem)->getPlayerId()); + Server_Card *targetCard = qobject_cast(targetItem); + if (targetCard) { + info->set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerId()); + info->set_target_zone(targetCard->getZone()->getName().toStdString()); + info->set_target_card_id(targetCard->getId()); + } else + info->set_target_player_id(static_cast(targetItem)->getPlayerId()); } diff --git a/common/server_arrow.h b/common/server_arrow.h index 1d171ef30..01c472ef3 100644 --- a/common/server_arrow.h +++ b/common/server_arrow.h @@ -9,18 +9,18 @@ class ServerInfo_Arrow; class Server_Arrow { private: - int id; - Server_Card *startCard; - Server_ArrowTarget *targetItem; - color arrowColor; + int id; + Server_Card *startCard; + Server_ArrowTarget *targetItem; + color arrowColor; public: - Server_Arrow(int _id, Server_Card *_startCard, Server_ArrowTarget *_targetItem, const color &_arrowColor); - int getId() const { return id; } - Server_Card *getStartCard() const { return startCard; } - Server_ArrowTarget *getTargetItem() const { return targetItem; } - const color &getColor() const { return arrowColor; } - - void getInfo(ServerInfo_Arrow *info); + Server_Arrow(int _id, Server_Card *_startCard, Server_ArrowTarget *_targetItem, const color &_arrowColor); + int getId() const { return id; } + Server_Card *getStartCard() const { return startCard; } + Server_ArrowTarget *getTargetItem() const { return targetItem; } + const color &getColor() const { return arrowColor; } + + void getInfo(ServerInfo_Arrow *info); }; #endif diff --git a/common/server_arrowtarget.cpp b/common/server_arrowtarget.cpp index aa7789ddf..1b3385d58 100644 --- a/common/server_arrowtarget.cpp +++ b/common/server_arrowtarget.cpp @@ -1,2 +1,2 @@ -#include "server_arrowtarget.h" \ No newline at end of file +#include "server_arrowtarget.h" diff --git a/common/server_arrowtarget.h b/common/server_arrowtarget.h index f5783d5b8..d94fc4b71 100644 --- a/common/server_arrowtarget.h +++ b/common/server_arrowtarget.h @@ -4,7 +4,7 @@ #include class Server_ArrowTarget : public QObject { - Q_OBJECT + Q_OBJECT }; #endif diff --git a/common/server_card.cpp b/common/server_card.cpp index 10c465b83..39e4aadcf 100644 --- a/common/server_card.cpp +++ b/common/server_card.cpp @@ -23,138 +23,138 @@ #include "pb/serverinfo_card.pb.h" Server_Card::Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone) - : zone(_zone), id(_id), coord_x(_coord_x), coord_y(_coord_y), name(_name), tapped(false), attacking(false), facedown(false), color(QString()), power(-1), toughness(-1), annotation(QString()), destroyOnZoneChange(false), doesntUntap(false), parentCard(0) + : zone(_zone), id(_id), coord_x(_coord_x), coord_y(_coord_y), name(_name), tapped(false), attacking(false), facedown(false), color(QString()), power(-1), toughness(-1), annotation(QString()), destroyOnZoneChange(false), doesntUntap(false), parentCard(0) { } Server_Card::~Server_Card() { - // setParentCard(0) leads to the item being removed from our list, so we can't iterate properly - while (!attachedCards.isEmpty()) - attachedCards.first()->setParentCard(0); - - if (parentCard) - parentCard->removeAttachedCard(this); + // setParentCard(0) leads to the item being removed from our list, so we can't iterate properly + while (!attachedCards.isEmpty()) + attachedCards.first()->setParentCard(0); + + if (parentCard) + parentCard->removeAttachedCard(this); } void Server_Card::resetState() { - counters.clear(); - setTapped(false); - setAttacking(false); - power = -1; - toughness = -1; - setAnnotation(QString()); - setDoesntUntap(false); + counters.clear(); + setTapped(false); + setAttacking(false); + power = -1; + toughness = -1; + setAnnotation(QString()); + setDoesntUntap(false); } QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue, bool allCards) { - switch (attribute) { - case AttrTapped: { - bool value = avalue == "1"; - if (!(!value && allCards && doesntUntap)) - setTapped(value); - break; - } - case AttrAttacking: setAttacking(avalue == "1"); break; - case AttrFaceDown: setFaceDown(avalue == "1"); break; - case AttrColor: setColor(avalue); break; - case AttrPT: setPT(avalue); return getPT(); - case AttrAnnotation: setAnnotation(avalue); break; - case AttrDoesntUntap: setDoesntUntap(avalue == "1"); break; - } - return avalue; + switch (attribute) { + case AttrTapped: { + bool value = avalue == "1"; + if (!(!value && allCards && doesntUntap)) + setTapped(value); + break; + } + case AttrAttacking: setAttacking(avalue == "1"); break; + case AttrFaceDown: setFaceDown(avalue == "1"); break; + case AttrColor: setColor(avalue); break; + case AttrPT: setPT(avalue); return getPT(); + case AttrAnnotation: setAnnotation(avalue); break; + case AttrDoesntUntap: setDoesntUntap(avalue == "1"); break; + } + return avalue; } void Server_Card::setCounter(int id, int value) { - if (value) - counters.insert(id, value); - else - counters.remove(id); + if (value) + counters.insert(id, value); + else + counters.remove(id); } void Server_Card::setPT(const QString &_pt) { - if (_pt.isEmpty()) { - power = 0; - toughness = -1; - } else { - int sep = _pt.indexOf('/'); - QString p1 = _pt.left(sep); - QString p2 = _pt.mid(sep + 1); - if (p1.isEmpty() || p2.isEmpty()) - return; - if ((p1[0] == '+') || (p2[0] == '+')) { - if (power < 0) - power = 0; - if (toughness < 0) - toughness = 0; - } - if (p1[0] == '+') - power += p1.mid(1).toInt(); - else - power = p1.toInt(); - if (p2[0] == '+') - toughness += p2.mid(1).toInt(); - else - toughness = p2.toInt(); - } + if (_pt.isEmpty()) { + power = 0; + toughness = -1; + } else { + int sep = _pt.indexOf('/'); + QString p1 = _pt.left(sep); + QString p2 = _pt.mid(sep + 1); + if (p1.isEmpty() || p2.isEmpty()) + return; + if ((p1[0] == '+') || (p2[0] == '+')) { + if (power < 0) + power = 0; + if (toughness < 0) + toughness = 0; + } + if (p1[0] == '+') + power += p1.mid(1).toInt(); + else + power = p1.toInt(); + if (p2[0] == '+') + toughness += p2.mid(1).toInt(); + else + toughness = p2.toInt(); + } } QString Server_Card::getPT() const { - if (toughness < 0) - return QString(""); - return QString::number(power) + "/" + QString::number(toughness); + if (toughness < 0) + return QString(""); + return QString::number(power) + "/" + QString::number(toughness); } void Server_Card::setParentCard(Server_Card *_parentCard) { - if (parentCard) - parentCard->removeAttachedCard(this); - parentCard = _parentCard; - if (parentCard) - parentCard->addAttachedCard(this); + if (parentCard) + parentCard->removeAttachedCard(this); + parentCard = _parentCard; + if (parentCard) + parentCard->addAttachedCard(this); } void Server_Card::getInfo(ServerInfo_Card *info) { - QString displayedName = facedown ? QString() : name; - - info->set_id(id); - info->set_name(displayedName.toStdString()); - info->set_x(coord_x); - info->set_y(coord_y); - if (facedown) - info->set_face_down(true); - info->set_tapped(tapped); - if (attacking) - info->set_attacking(true); - if (!color.isEmpty()) - info->set_color(color.toStdString()); - const QString ptStr = getPT(); - if (!ptStr.isEmpty()) - info->set_pt(ptStr.toStdString()); - if (!annotation.isEmpty()) - info->set_annotation(annotation.toStdString()); - if (destroyOnZoneChange) - info->set_destroy_on_zone_change(true); - if (doesntUntap) - info->set_doesnt_untap(true); - - QMapIterator cardCounterIterator(counters); - while (cardCounterIterator.hasNext()) { - cardCounterIterator.next(); - ServerInfo_CardCounter *counterInfo = info->add_counter_list(); - counterInfo->set_id(cardCounterIterator.key()); - counterInfo->set_value(cardCounterIterator.value()); - } + QString displayedName = facedown ? QString() : name; + + info->set_id(id); + info->set_name(displayedName.toStdString()); + info->set_x(coord_x); + info->set_y(coord_y); + if (facedown) + info->set_face_down(true); + info->set_tapped(tapped); + if (attacking) + info->set_attacking(true); + if (!color.isEmpty()) + info->set_color(color.toStdString()); + const QString ptStr = getPT(); + if (!ptStr.isEmpty()) + info->set_pt(ptStr.toStdString()); + if (!annotation.isEmpty()) + info->set_annotation(annotation.toStdString()); + if (destroyOnZoneChange) + info->set_destroy_on_zone_change(true); + if (doesntUntap) + info->set_doesnt_untap(true); + + QMapIterator cardCounterIterator(counters); + while (cardCounterIterator.hasNext()) { + cardCounterIterator.next(); + ServerInfo_CardCounter *counterInfo = info->add_counter_list(); + counterInfo->set_id(cardCounterIterator.key()); + counterInfo->set_value(cardCounterIterator.value()); + } - if (parentCard) { - info->set_attach_player_id(parentCard->getZone()->getPlayer()->getPlayerId()); - info->set_attach_zone(parentCard->getZone()->getName().toStdString()); - info->set_attach_card_id(parentCard->getId()); - } + if (parentCard) { + info->set_attach_player_id(parentCard->getZone()->getPlayer()->getPlayerId()); + info->set_attach_zone(parentCard->getZone()->getName().toStdString()); + info->set_attach_card_id(parentCard->getId()); + } } diff --git a/common/server_card.h b/common/server_card.h index 2af8aa6d5..e3c76f05a 100644 --- a/common/server_card.h +++ b/common/server_card.h @@ -29,68 +29,68 @@ class Server_CardZone; class ServerInfo_Card; class Server_Card : public Server_ArrowTarget { - Q_OBJECT + Q_OBJECT private: - Server_CardZone *zone; - int id; - int coord_x, coord_y; - QString name; - QMap counters; - bool tapped; - bool attacking; - bool facedown; - QString color; - int power, toughness; - QString annotation; - bool destroyOnZoneChange; - bool doesntUntap; - - Server_Card *parentCard; - QList attachedCards; + Server_CardZone *zone; + int id; + int coord_x, coord_y; + QString name; + QMap counters; + bool tapped; + bool attacking; + bool facedown; + QString color; + int power, toughness; + QString annotation; + bool destroyOnZoneChange; + bool doesntUntap; + + Server_Card *parentCard; + QList attachedCards; public: - Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = 0); - ~Server_Card(); - - Server_CardZone *getZone() const { return zone; } - void setZone(Server_CardZone *_zone) { zone = _zone; } - - int getId() const { return id; } - int getX() const { return coord_x; } - int getY() const { return coord_y; } - QString getName() const { return name; } - const QMap &getCounters() const { return counters; } - int getCounter(int id) const { return counters.value(id, 0); } - bool getTapped() const { return tapped; } - bool getAttacking() const { return attacking; } - bool getFaceDown() const { return facedown; } - QString getColor() const { return color; } - QString getPT() const; - QString getAnnotation() const { return annotation; } - bool getDoesntUntap() const { return doesntUntap; } - bool getDestroyOnZoneChange() const { return destroyOnZoneChange; } - Server_Card *getParentCard() const { return parentCard; } - const QList &getAttachedCards() const { return attachedCards; } + Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = 0); + ~Server_Card(); + + Server_CardZone *getZone() const { return zone; } + void setZone(Server_CardZone *_zone) { zone = _zone; } + + int getId() const { return id; } + int getX() const { return coord_x; } + int getY() const { return coord_y; } + QString getName() const { return name; } + const QMap &getCounters() const { return counters; } + int getCounter(int id) const { return counters.value(id, 0); } + bool getTapped() const { return tapped; } + bool getAttacking() const { return attacking; } + bool getFaceDown() const { return facedown; } + QString getColor() const { return color; } + QString getPT() const; + QString getAnnotation() const { return annotation; } + bool getDoesntUntap() const { return doesntUntap; } + bool getDestroyOnZoneChange() const { return destroyOnZoneChange; } + Server_Card *getParentCard() const { return parentCard; } + const QList &getAttachedCards() const { return attachedCards; } - void setId(int _id) { id = _id; } - void setCoords(int x, int y) { coord_x = x; coord_y = y; } - void setName(const QString &_name) { name = _name; } - void setCounter(int id, int value); - void setTapped(bool _tapped) { tapped = _tapped; } - void setAttacking(bool _attacking) { attacking = _attacking; } - void setFaceDown(bool _facedown) { facedown = _facedown; } - void setColor(const QString &_color) { color = _color; } - void setPT(const QString &_pt); - void setAnnotation(const QString &_annotation) { annotation = _annotation; } - void setDestroyOnZoneChange(bool _destroy) { destroyOnZoneChange = _destroy; } - void setDoesntUntap(bool _doesntUntap) { doesntUntap = _doesntUntap; } - void setParentCard(Server_Card *_parentCard); - void addAttachedCard(Server_Card *card) { attachedCards.append(card); } - void removeAttachedCard(Server_Card *card) { attachedCards.removeAt(attachedCards.indexOf(card)); } - - void resetState(); - QString setAttribute(CardAttribute attribute, const QString &avalue, bool allCards); - - void getInfo(ServerInfo_Card *info); + void setId(int _id) { id = _id; } + void setCoords(int x, int y) { coord_x = x; coord_y = y; } + void setName(const QString &_name) { name = _name; } + void setCounter(int id, int value); + void setTapped(bool _tapped) { tapped = _tapped; } + void setAttacking(bool _attacking) { attacking = _attacking; } + void setFaceDown(bool _facedown) { facedown = _facedown; } + void setColor(const QString &_color) { color = _color; } + void setPT(const QString &_pt); + void setAnnotation(const QString &_annotation) { annotation = _annotation; } + void setDestroyOnZoneChange(bool _destroy) { destroyOnZoneChange = _destroy; } + void setDoesntUntap(bool _doesntUntap) { doesntUntap = _doesntUntap; } + void setParentCard(Server_Card *_parentCard); + void addAttachedCard(Server_Card *card) { attachedCards.append(card); } + void removeAttachedCard(Server_Card *card) { attachedCards.removeAt(attachedCards.indexOf(card)); } + + void resetState(); + QString setAttribute(CardAttribute attribute, const QString &avalue, bool allCards); + + void getInfo(ServerInfo_Card *info); }; #endif diff --git a/common/server_cardzone.h b/common/server_cardzone.h index 1eeb90089..a00b367a8 100644 --- a/common/server_cardzone.h +++ b/common/server_cardzone.h @@ -33,48 +33,48 @@ class GameEventStorage; class Server_CardZone { private: - Server_Player *player; - QString name; - bool has_coords; - ServerInfo_Zone::ZoneType type; - int cardsBeingLookedAt; - QSet playersWithWritePermission; - bool alwaysRevealTopCard; - QList cards; - QMap > coordinateMap; // y -> (x -> card) - QMap > freePilesMap; // y -> (cardName -> x) - QMap freeSpaceMap; // y -> x - void removeCardFromCoordMap(Server_Card *card, int oldX, int oldY); - void insertCardIntoCoordMap(Server_Card *card, int x, int y); + Server_Player *player; + QString name; + bool has_coords; + ServerInfo_Zone::ZoneType type; + int cardsBeingLookedAt; + QSet playersWithWritePermission; + bool alwaysRevealTopCard; + QList cards; + QMap > coordinateMap; // y -> (x -> card) + QMap > freePilesMap; // y -> (cardName -> x) + QMap freeSpaceMap; // y -> x + void removeCardFromCoordMap(Server_Card *card, int oldX, int oldY); + void insertCardIntoCoordMap(Server_Card *card, int x, int y); public: - Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type); - ~Server_CardZone(); - - const QList &getCards() const { return cards; } - int removeCard(Server_Card *card); - Server_Card *getCard(int id, int *position = NULL, bool remove = false); + Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type); + ~Server_CardZone(); + + const QList &getCards() const { return cards; } + int removeCard(Server_Card *card); + Server_Card *getCard(int id, int *position = NULL, bool remove = false); - int getCardsBeingLookedAt() const { return cardsBeingLookedAt; } - void setCardsBeingLookedAt(int _cardsBeingLookedAt) { cardsBeingLookedAt = _cardsBeingLookedAt; } - bool hasCoords() const { return has_coords; } - ServerInfo_Zone::ZoneType getType() const { return type; } - QString getName() const { return name; } - Server_Player *getPlayer() const { return player; } - void getInfo(ServerInfo_Zone *info, Server_Player *playerWhosAsking, bool omniscient); - - int getFreeGridColumn(int x, int y, const QString &cardName) const; - bool isColumnEmpty(int x, int y) const; - bool isColumnStacked(int x, int y) const; - void fixFreeSpaces(GameEventStorage &ges); - void moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y); - void insertCard(Server_Card *card, int x, int y); - void updateCardCoordinates(Server_Card *card, int oldX, int oldY); - void shuffle(); - void clear(); - void addWritePermission(int playerId); - const QSet &getPlayersWithWritePermission() const { return playersWithWritePermission; } - bool getAlwaysRevealTopCard() const { return alwaysRevealTopCard; } - void setAlwaysRevealTopCard(bool _alwaysRevealTopCard) { alwaysRevealTopCard = _alwaysRevealTopCard; } + int getCardsBeingLookedAt() const { return cardsBeingLookedAt; } + void setCardsBeingLookedAt(int _cardsBeingLookedAt) { cardsBeingLookedAt = _cardsBeingLookedAt; } + bool hasCoords() const { return has_coords; } + ServerInfo_Zone::ZoneType getType() const { return type; } + QString getName() const { return name; } + Server_Player *getPlayer() const { return player; } + void getInfo(ServerInfo_Zone *info, Server_Player *playerWhosAsking, bool omniscient); + + int getFreeGridColumn(int x, int y, const QString &cardName) const; + bool isColumnEmpty(int x, int y) const; + bool isColumnStacked(int x, int y) const; + void fixFreeSpaces(GameEventStorage &ges); + void moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y); + void insertCard(Server_Card *card, int x, int y); + void updateCardCoordinates(Server_Card *card, int oldX, int oldY); + void shuffle(); + void clear(); + void addWritePermission(int playerId); + const QSet &getPlayersWithWritePermission() const { return playersWithWritePermission; } + bool getAlwaysRevealTopCard() const { return alwaysRevealTopCard; } + void setAlwaysRevealTopCard(bool _alwaysRevealTopCard) { alwaysRevealTopCard = _alwaysRevealTopCard; } }; #endif diff --git a/common/server_counter.cpp b/common/server_counter.cpp index 2e0ffde80..df8dcd03c 100644 --- a/common/server_counter.cpp +++ b/common/server_counter.cpp @@ -12,9 +12,9 @@ Server_Counter::Server_Counter(int _id, const QString &_name, const color &_coun void Server_Counter::getInfo(ServerInfo_Counter *info) { - info->set_id(id); - info->set_name(name.toStdString()); - info->mutable_counter_color()->CopyFrom(counterColor); - info->set_radius(radius); - info->set_count(count); + info->set_id(id); + info->set_name(name.toStdString()); + info->mutable_counter_color()->CopyFrom(counterColor); + info->set_radius(radius); + info->set_count(count); } diff --git a/common/server_counter.h b/common/server_counter.h index 42483b9da..60b70e699 100644 --- a/common/server_counter.h +++ b/common/server_counter.h @@ -27,22 +27,22 @@ class ServerInfo_Counter; class Server_Counter { protected: - int id; - QString name; - color counterColor; - int radius; - int count; + int id; + QString name; + color counterColor; + int radius; + int count; public: - Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count = 0); - ~Server_Counter() { } - int getId() const { return id; } - QString getName() const { return name; } - const color &getColor() const { return counterColor; } - int getRadius() const { return radius; } - int getCount() const { return count; } - void setCount(int _count) { count = _count; } - - void getInfo(ServerInfo_Counter *info); + Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count = 0); + ~Server_Counter() { } + int getId() const { return id; } + QString getName() const { return name; } + const color &getColor() const { return counterColor; } + int getRadius() const { return radius; } + int getCount() const { return count; } + void setCount(int _count) { count = _count; } + + void getInfo(ServerInfo_Counter *info); }; #endif diff --git a/common/server_database_interface.h b/common/server_database_interface.h index 10ce082ab..df75cd63a 100644 --- a/common/server_database_interface.h +++ b/common/server_database_interface.h @@ -6,32 +6,32 @@ #include "server.h" class Server_DatabaseInterface : public QObject { - Q_OBJECT + Q_OBJECT public: - Server_DatabaseInterface(QObject *parent = 0) - : QObject(parent) { } - - virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0; - virtual bool userExists(const QString &user) { return false; } - virtual QMap getBuddyList(const QString &name) { return QMap(); } - virtual QMap getIgnoreList(const QString &name) { return QMap(); } - virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; } - virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; } - virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; - virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList) { } - virtual DeckList *getDeckFromDatabase(int deckId, int userId) { return 0; } - - virtual qint64 startSession(const QString &userName, const QString &address) { return 0; } + Server_DatabaseInterface(QObject *parent = 0) + : QObject(parent) { } + + virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0; + virtual bool userExists(const QString &user) { return false; } + virtual QMap getBuddyList(const QString &name) { return QMap(); } + virtual QMap getIgnoreList(const QString &name) { return QMap(); } + virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; } + virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; } + virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; + virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList) { } + virtual DeckList *getDeckFromDatabase(int deckId, int userId) { return 0; } + + virtual qint64 startSession(const QString &userName, const QString &address) { return 0; } public slots: - virtual void endSession(qint64 sessionId) { } + virtual void endSession(qint64 sessionId) { } public: - virtual int getNextGameId() = 0; - virtual int getNextReplayId() = 0; - - virtual void clearSessionTables() { } - virtual void lockSessionTables() { } - virtual void unlockSessionTables() { } - virtual bool userSessionExists(const QString &userName) { return false; } + virtual int getNextGameId() = 0; + virtual int getNextReplayId() = 0; + + virtual void clearSessionTables() { } + virtual void lockSessionTables() { } + virtual void unlockSessionTables() { } + virtual bool userSessionExists(const QString &userName) { return false; } }; #endif diff --git a/common/server_game.cpp b/common/server_game.cpp index 94eb03aeb..647aa22e3 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -48,7 +48,7 @@ #include Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const QString &_description, const QString &_password, int _maxPlayers, const QList &_gameTypes, bool _onlyBuddies, bool _onlyRegistered, bool _spectatorsAllowed, bool _spectatorsNeedPassword, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, Server_Room *_room) - : QObject(), + : QObject(), room(_room), nextPlayerId(0), hostId(0), @@ -75,667 +75,667 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const startTime(QDateTime::currentDateTime()), gameMutex(QMutex::Recursive) { - currentReplay = new GameReplay; - currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId()); - - connect(this, SIGNAL(sigStartGameIfReady()), this, SLOT(doStartGameIfReady()), Qt::QueuedConnection); - - getInfo(*currentReplay->mutable_game_info()); + currentReplay = new GameReplay; + currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId()); + + connect(this, SIGNAL(sigStartGameIfReady()), this, SLOT(doStartGameIfReady()), Qt::QueuedConnection); + + getInfo(*currentReplay->mutable_game_info()); - if (room->getServer()->getGameShouldPing()) { - pingClock = new QTimer(this); - connect(pingClock, SIGNAL(timeout()), this, SLOT(pingClockTimeout())); - pingClock->start(1000); - } + if (room->getServer()->getGameShouldPing()) { + pingClock = new QTimer(this); + connect(pingClock, SIGNAL(timeout()), this, SLOT(pingClockTimeout())); + pingClock->start(1000); + } } Server_Game::~Server_Game() { - room->gamesLock.lockForWrite(); - gameMutex.lock(); - - gameClosed = true; - sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); - - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - playerIterator.next().value()->prepareDestroy(); - players.clear(); - - room->removeGame(this); - delete creatorInfo; - creatorInfo = 0; - - gameMutex.unlock(); - room->gamesLock.unlock(); - - currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); - replayList.append(currentReplay); - storeGameInformation(); - - for (int i = 0; i < replayList.size(); ++i) - delete replayList[i]; - - qDebug() << "Server_Game destructor: gameId=" << gameId; + room->gamesLock.lockForWrite(); + gameMutex.lock(); + + gameClosed = true; + sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); + + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) + playerIterator.next().value()->prepareDestroy(); + players.clear(); + + room->removeGame(this); + delete creatorInfo; + creatorInfo = 0; + + gameMutex.unlock(); + room->gamesLock.unlock(); + + currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); + replayList.append(currentReplay); + storeGameInformation(); + + for (int i = 0; i < replayList.size(); ++i) + delete replayList[i]; + + qDebug() << "Server_Game destructor: gameId=" << gameId; } void Server_Game::storeGameInformation() { - const ServerInfo_Game &gameInfo = replayList.first()->game_info(); - - Event_ReplayAdded replayEvent; - ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info(); - replayMatchInfo->set_game_id(gameInfo.game_id()); - replayMatchInfo->set_room_name(room->getName().toStdString()); - replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toTime_t()); - replayMatchInfo->set_length(secondsElapsed); - replayMatchInfo->set_game_name(gameInfo.description()); - - const QStringList &allGameTypes = room->getGameTypes(); - QStringList gameTypes; - for (int i = gameInfo.game_types_size() - 1; i >= 0; --i) - gameTypes.append(allGameTypes[gameInfo.game_types(i)]); - - QSetIterator playerIterator(allPlayersEver); - while (playerIterator.hasNext()) - replayMatchInfo->add_player_names(playerIterator.next().toStdString()); - - for (int i = 0; i < replayList.size(); ++i) { - ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list(); - replayInfo->set_replay_id(replayList[i]->replay_id()); - replayInfo->set_replay_name(gameInfo.description()); - replayInfo->set_duration(replayList[i]->duration_seconds()); - } - - QSet allUsersInGame = allPlayersEver + allSpectatorsEver; - QSetIterator allUsersIterator(allUsersInGame); - - SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent); - Server *server = room->getServer(); - server->clientsLock.lockForRead(); - while (allUsersIterator.hasNext()) { - Server_AbstractUserInterface *userHandler = server->findUser(allUsersIterator.next()); - if (userHandler) - userHandler->sendProtocolItem(*sessionEvent); - } - server->clientsLock.unlock(); - delete sessionEvent; - - server->getDatabaseInterface()->storeGameInformation(room->getName(), gameTypes, gameInfo, allPlayersEver, allSpectatorsEver, replayList); + const ServerInfo_Game &gameInfo = replayList.first()->game_info(); + + Event_ReplayAdded replayEvent; + ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info(); + replayMatchInfo->set_game_id(gameInfo.game_id()); + replayMatchInfo->set_room_name(room->getName().toStdString()); + replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toTime_t()); + replayMatchInfo->set_length(secondsElapsed); + replayMatchInfo->set_game_name(gameInfo.description()); + + const QStringList &allGameTypes = room->getGameTypes(); + QStringList gameTypes; + for (int i = gameInfo.game_types_size() - 1; i >= 0; --i) + gameTypes.append(allGameTypes[gameInfo.game_types(i)]); + + QSetIterator playerIterator(allPlayersEver); + while (playerIterator.hasNext()) + replayMatchInfo->add_player_names(playerIterator.next().toStdString()); + + for (int i = 0; i < replayList.size(); ++i) { + ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list(); + replayInfo->set_replay_id(replayList[i]->replay_id()); + replayInfo->set_replay_name(gameInfo.description()); + replayInfo->set_duration(replayList[i]->duration_seconds()); + } + + QSet allUsersInGame = allPlayersEver + allSpectatorsEver; + QSetIterator allUsersIterator(allUsersInGame); + + SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent); + Server *server = room->getServer(); + server->clientsLock.lockForRead(); + while (allUsersIterator.hasNext()) { + Server_AbstractUserInterface *userHandler = server->findUser(allUsersIterator.next()); + if (userHandler) + userHandler->sendProtocolItem(*sessionEvent); + } + server->clientsLock.unlock(); + delete sessionEvent; + + server->getDatabaseInterface()->storeGameInformation(room->getName(), gameTypes, gameInfo, allPlayersEver, allSpectatorsEver, replayList); } void Server_Game::pingClockTimeout() { - QMutexLocker locker(&gameMutex); - ++secondsElapsed; - - GameEventStorage ges; - ges.setGameEventContext(Context_PingChanged()); - - QList pingList; - QMapIterator playerIterator(players); - bool allPlayersInactive = true; - int playerCount = 0; - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); - if (!player->getSpectator()) - ++playerCount; - - const int oldPingTime = player->getPingTime(); - player->playerMutex.lock(); - int newPingTime; - if (player->getUserInterface()) - newPingTime = player->getUserInterface()->getLastCommandTime(); - else - newPingTime = -1; - player->playerMutex.unlock(); - - if ((newPingTime != -1) && !player->getSpectator()) - allPlayersInactive = false; - - if ((abs(oldPingTime - newPingTime) > 1) || ((newPingTime == -1) && (oldPingTime != -1)) || ((newPingTime != -1) && (oldPingTime == -1))) { - player->setPingTime(newPingTime); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_ping_seconds(newPingTime); - ges.enqueueGameEvent(event, player->getPlayerId()); - } - } - ges.sendToGame(this); - - const int maxTime = room->getServer()->getMaxGameInactivityTime(); - if (allPlayersInactive) { - if (((++inactivityCounter >= maxTime) && (maxTime > 0)) || (playerCount < maxPlayers)) - deleteLater(); - } else - inactivityCounter = 0; + QMutexLocker locker(&gameMutex); + ++secondsElapsed; + + GameEventStorage ges; + ges.setGameEventContext(Context_PingChanged()); + + QList pingList; + QMapIterator playerIterator(players); + bool allPlayersInactive = true; + int playerCount = 0; + while (playerIterator.hasNext()) { + Server_Player *player = playerIterator.next().value(); + if (!player->getSpectator()) + ++playerCount; + + const int oldPingTime = player->getPingTime(); + player->playerMutex.lock(); + int newPingTime; + if (player->getUserInterface()) + newPingTime = player->getUserInterface()->getLastCommandTime(); + else + newPingTime = -1; + player->playerMutex.unlock(); + + if ((newPingTime != -1) && !player->getSpectator()) + allPlayersInactive = false; + + if ((abs(oldPingTime - newPingTime) > 1) || ((newPingTime == -1) && (oldPingTime != -1)) || ((newPingTime != -1) && (oldPingTime == -1))) { + player->setPingTime(newPingTime); + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_ping_seconds(newPingTime); + ges.enqueueGameEvent(event, player->getPlayerId()); + } + } + ges.sendToGame(this); + + const int maxTime = room->getServer()->getMaxGameInactivityTime(); + if (allPlayersInactive) { + if (((++inactivityCounter >= maxTime) && (maxTime > 0)) || (playerCount < maxPlayers)) + deleteLater(); + } else + inactivityCounter = 0; } int Server_Game::getPlayerCount() const { - QMutexLocker locker(&gameMutex); - - QMapIterator playerIterator(players); - int result = 0; - while (playerIterator.hasNext()) - if (!playerIterator.next().value()->getSpectator()) - ++result; - return result; + QMutexLocker locker(&gameMutex); + + QMapIterator playerIterator(players); + int result = 0; + while (playerIterator.hasNext()) + if (!playerIterator.next().value()->getSpectator()) + ++result; + return result; } int Server_Game::getSpectatorCount() const { - QMutexLocker locker(&gameMutex); - - QMapIterator playerIterator(players); - int result = 0; - while (playerIterator.hasNext()) - if (playerIterator.next().value()->getSpectator()) - ++result; - return result; + QMutexLocker locker(&gameMutex); + + QMapIterator playerIterator(players); + int result = 0; + while (playerIterator.hasNext()) + if (playerIterator.next().value()->getSpectator()) + ++result; + return result; } void Server_Game::createGameStateChangedEvent(Event_GameStateChanged *event, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo) { - event->set_seconds_elapsed(secondsElapsed); - if (gameStarted) { - event->set_game_started(true); - event->set_active_player_id(0); - event->set_active_phase(0); - } else - event->set_game_started(false); - - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - playerIterator.next().value()->getInfo(event->add_player_list(), playerWhosAsking, omniscient, withUserInfo); + event->set_seconds_elapsed(secondsElapsed); + if (gameStarted) { + event->set_game_started(true); + event->set_active_player_id(0); + event->set_active_phase(0); + } else + event->set_game_started(false); + + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) + playerIterator.next().value()->getInfo(event->add_player_list(), playerWhosAsking, omniscient, withUserInfo); } void Server_Game::sendGameStateToPlayers() { - // game state information for replay and omniscient spectators - Event_GameStateChanged omniscientEvent; - createGameStateChangedEvent(&omniscientEvent, 0, true, false); - - GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); - replayCont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); - replayCont->clear_game_id(); - currentReplay->add_event_list()->CopyFrom(*replayCont); - delete replayCont; - - // If spectators are not omniscient, we need an additional createGameStateChangedEvent call, otherwise we can use the data we used for the replay. - // All spectators are equal, so we don't need to make a createGameStateChangedEvent call for each one. - Event_GameStateChanged spectatorEvent; - if (spectatorsSeeEverything) - spectatorEvent = omniscientEvent; - else - createGameStateChangedEvent(&spectatorEvent, 0, false, false); - - // send game state info to clients according to their role in the game - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); - GameEventContainer *gec; - if (player->getSpectator()) - gec = prepareGameEvent(spectatorEvent, -1); - else { - Event_GameStateChanged event; - createGameStateChangedEvent(&event, player, false, false); - - gec = prepareGameEvent(event, -1); - } - player->sendGameEvent(*gec); - delete gec; - } + // game state information for replay and omniscient spectators + Event_GameStateChanged omniscientEvent; + createGameStateChangedEvent(&omniscientEvent, 0, true, false); + + GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); + replayCont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); + replayCont->clear_game_id(); + currentReplay->add_event_list()->CopyFrom(*replayCont); + delete replayCont; + + // If spectators are not omniscient, we need an additional createGameStateChangedEvent call, otherwise we can use the data we used for the replay. + // All spectators are equal, so we don't need to make a createGameStateChangedEvent call for each one. + Event_GameStateChanged spectatorEvent; + if (spectatorsSeeEverything) + spectatorEvent = omniscientEvent; + else + createGameStateChangedEvent(&spectatorEvent, 0, false, false); + + // send game state info to clients according to their role in the game + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Server_Player *player = playerIterator.next().value(); + GameEventContainer *gec; + if (player->getSpectator()) + gec = prepareGameEvent(spectatorEvent, -1); + else { + Event_GameStateChanged event; + createGameStateChangedEvent(&event, player, false, false); + + gec = prepareGameEvent(event, -1); + } + player->sendGameEvent(*gec); + delete gec; + } } void Server_Game::doStartGameIfReady() { - Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); - QMutexLocker locker(&gameMutex); - - if (getPlayerCount() < maxPlayers) - return; - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getReadyStart() && !p->getSpectator()) - return; - } - playerIterator.toFront(); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getSpectator()) - p->setupZones(); - } + Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); + QMutexLocker locker(&gameMutex); + + if (getPlayerCount() < maxPlayers) + return; + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + if (!p->getReadyStart() && !p->getSpectator()) + return; + } + playerIterator.toFront(); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + if (!p->getSpectator()) + p->setupZones(); + } - gameStarted = true; - playerIterator.toFront(); - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); - player->setConceded(false); - player->setReadyStart(false); - } - - if (firstGameStarted) { - currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); - replayList.append(currentReplay); - currentReplay = new GameReplay; - currentReplay->set_replay_id(databaseInterface->getNextReplayId()); - ServerInfo_Game *gameInfo = currentReplay->mutable_game_info(); - getInfo(*gameInfo); - gameInfo->set_started(false); - - Event_GameStateChanged omniscientEvent; - createGameStateChangedEvent(&omniscientEvent, 0, true, true); - - GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); - replayCont->set_seconds_elapsed(0); - replayCont->clear_game_id(); - currentReplay->add_event_list()->CopyFrom(*replayCont); - delete replayCont; - - startTimeOfThisGame = secondsElapsed; - } else - firstGameStarted = true; - - sendGameStateToPlayers(); - - activePlayer = -1; - nextTurn(); - - locker.unlock(); - - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_started(true); - emit gameInfoChanged(gameInfo); + gameStarted = true; + playerIterator.toFront(); + while (playerIterator.hasNext()) { + Server_Player *player = playerIterator.next().value(); + player->setConceded(false); + player->setReadyStart(false); + } + + if (firstGameStarted) { + currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); + replayList.append(currentReplay); + currentReplay = new GameReplay; + currentReplay->set_replay_id(databaseInterface->getNextReplayId()); + ServerInfo_Game *gameInfo = currentReplay->mutable_game_info(); + getInfo(*gameInfo); + gameInfo->set_started(false); + + Event_GameStateChanged omniscientEvent; + createGameStateChangedEvent(&omniscientEvent, 0, true, true); + + GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); + replayCont->set_seconds_elapsed(0); + replayCont->clear_game_id(); + currentReplay->add_event_list()->CopyFrom(*replayCont); + delete replayCont; + + startTimeOfThisGame = secondsElapsed; + } else + firstGameStarted = true; + + sendGameStateToPlayers(); + + activePlayer = -1; + nextTurn(); + + locker.unlock(); + + ServerInfo_Game gameInfo; + gameInfo.set_room_id(room->getId()); + gameInfo.set_game_id(gameId); + gameInfo.set_started(true); + emit gameInfoChanged(gameInfo); } void Server_Game::startGameIfReady() { - emit sigStartGameIfReady(); + emit sigStartGameIfReady(); } void Server_Game::stopGameIfFinished() { - QMutexLocker locker(&gameMutex); - - QMapIterator playerIterator(players); - int playing = 0; - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getConceded() && !p->getSpectator()) - ++playing; - } - if (playing > 1) - return; + QMutexLocker locker(&gameMutex); + + QMapIterator playerIterator(players); + int playing = 0; + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + if (!p->getConceded() && !p->getSpectator()) + ++playing; + } + if (playing > 1) + return; - gameStarted = false; + gameStarted = false; - playerIterator.toFront(); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - p->clearZones(); - p->setConceded(false); - } + playerIterator.toFront(); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + p->clearZones(); + p->setConceded(false); + } - sendGameStateToPlayers(); - - locker.unlock(); - - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_started(false); - emit gameInfoChanged(gameInfo); + sendGameStateToPlayers(); + + locker.unlock(); + + ServerInfo_Game gameInfo; + gameInfo.set_room_id(room->getId()); + gameInfo.set_game_id(gameId); + gameInfo.set_started(false); + emit gameInfoChanged(gameInfo); } Response::ResponseCode Server_Game::checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions) { - Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); - { - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - if (playerIterator.next().value()->getUserInfo()->name() == user->name()) - return Response::RespContextError; - } - if (!(overrideRestrictions && (user->user_level() & ServerInfo_User::IsModerator))) { - if ((_password != password) && !(spectator && !spectatorsNeedPassword)) - return Response::RespWrongPassword; - if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered) - return Response::RespUserLevelTooLow; - if (onlyBuddies && (user->name() != creatorInfo->name())) - if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name()))) - return Response::RespOnlyBuddies; - if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name()))) - return Response::RespInIgnoreList; - if (spectator) { - if (!spectatorsAllowed) - return Response::RespSpectatorsNotAllowed; - } - } - if (!spectator && (gameStarted || (getPlayerCount() >= getMaxPlayers()))) - return Response::RespGameFull; - - return Response::RespOk; + Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); + { + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) + if (playerIterator.next().value()->getUserInfo()->name() == user->name()) + return Response::RespContextError; + } + if (!(overrideRestrictions && (user->user_level() & ServerInfo_User::IsModerator))) { + if ((_password != password) && !(spectator && !spectatorsNeedPassword)) + return Response::RespWrongPassword; + if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered) + return Response::RespUserLevelTooLow; + if (onlyBuddies && (user->name() != creatorInfo->name())) + if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name()))) + return Response::RespOnlyBuddies; + if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name()))) + return Response::RespInIgnoreList; + if (spectator) { + if (!spectatorsAllowed) + return Response::RespSpectatorsNotAllowed; + } + } + if (!spectator && (gameStarted || (getPlayerCount() >= getMaxPlayers()))) + return Response::RespGameFull; + + return Response::RespOk; } bool Server_Game::containsUser(const QString &userName) const { - QMutexLocker locker(&gameMutex); - - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - if (playerIterator.next().value()->getUserInfo()->name() == userName.toStdString()) - return true; - return false; + QMutexLocker locker(&gameMutex); + + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) + if (playerIterator.next().value()->getUserInfo()->name() == userName.toStdString()) + return true; + return false; } void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, ResponseContainer &rc, bool spectator, bool broadcastUpdate) { - QMutexLocker locker(&gameMutex); - - Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true, true), spectator, userInterface); - newPlayer->moveToThread(thread()); - - Event_Join joinEvent; - newPlayer->getProperties(*joinEvent.mutable_player_properties(), true); - sendGameEventContainer(prepareGameEvent(joinEvent, -1)); - - const QString playerName = QString::fromStdString(newPlayer->getUserInfo()->name()); - if (spectator) - allSpectatorsEver.insert(playerName); - else - allPlayersEver.insert(playerName); - players.insert(newPlayer->getPlayerId(), newPlayer); - if (newPlayer->getUserInfo()->name() == creatorInfo->name()) { - hostId = newPlayer->getPlayerId(); - sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); - } - - if (broadcastUpdate) { - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_player_count(getPlayerCount()); - gameInfo.set_spectators_count(getSpectatorCount()); - emit gameInfoChanged(gameInfo); - } - - if ((newPlayer->getUserInfo()->user_level() & ServerInfo_User::IsRegistered) && !spectator) - room->getServer()->addPersistentPlayer(playerName, room->getId(), gameId, newPlayer->getPlayerId()); - - userInterface->playerAddedToGame(gameId, room->getId(), newPlayer->getPlayerId()); - - createGameJoinedEvent(newPlayer, rc, false); + QMutexLocker locker(&gameMutex); + + Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true, true), spectator, userInterface); + newPlayer->moveToThread(thread()); + + Event_Join joinEvent; + newPlayer->getProperties(*joinEvent.mutable_player_properties(), true); + sendGameEventContainer(prepareGameEvent(joinEvent, -1)); + + const QString playerName = QString::fromStdString(newPlayer->getUserInfo()->name()); + if (spectator) + allSpectatorsEver.insert(playerName); + else + allPlayersEver.insert(playerName); + players.insert(newPlayer->getPlayerId(), newPlayer); + if (newPlayer->getUserInfo()->name() == creatorInfo->name()) { + hostId = newPlayer->getPlayerId(); + sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); + } + + if (broadcastUpdate) { + ServerInfo_Game gameInfo; + gameInfo.set_room_id(room->getId()); + gameInfo.set_game_id(gameId); + gameInfo.set_player_count(getPlayerCount()); + gameInfo.set_spectators_count(getSpectatorCount()); + emit gameInfoChanged(gameInfo); + } + + if ((newPlayer->getUserInfo()->user_level() & ServerInfo_User::IsRegistered) && !spectator) + room->getServer()->addPersistentPlayer(playerName, room->getId(), gameId, newPlayer->getPlayerId()); + + userInterface->playerAddedToGame(gameId, room->getId(), newPlayer->getPlayerId()); + + createGameJoinedEvent(newPlayer, rc, false); } void Server_Game::removePlayer(Server_Player *player) { - room->getServer()->removePersistentPlayer(QString::fromStdString(player->getUserInfo()->name()), room->getId(), gameId, player->getPlayerId()); - players.remove(player->getPlayerId()); - - GameEventStorage ges; - removeArrowsRelatedToPlayer(ges, player); - unattachCards(ges, player); - ges.enqueueGameEvent(Event_Leave(), player->getPlayerId()); - ges.sendToGame(this); - - bool playerActive = activePlayer == player->getPlayerId(); - bool playerHost = hostId == player->getPlayerId(); - bool spectator = player->getSpectator(); - player->prepareDestroy(); - - if (!getPlayerCount()) { - gameClosed = true; - deleteLater(); - return; - } else if (!spectator) { - if (playerHost) { - int newHostId = -1; - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getSpectator()) { - newHostId = p->getPlayerId(); - break; - } - } - if (newHostId != -1) { - hostId = newHostId; - sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); - } - } - stopGameIfFinished(); - if (gameStarted && playerActive) - nextTurn(); - } - - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_player_count(getPlayerCount()); - gameInfo.set_spectators_count(getSpectatorCount()); - emit gameInfoChanged(gameInfo); + room->getServer()->removePersistentPlayer(QString::fromStdString(player->getUserInfo()->name()), room->getId(), gameId, player->getPlayerId()); + players.remove(player->getPlayerId()); + + GameEventStorage ges; + removeArrowsRelatedToPlayer(ges, player); + unattachCards(ges, player); + ges.enqueueGameEvent(Event_Leave(), player->getPlayerId()); + ges.sendToGame(this); + + bool playerActive = activePlayer == player->getPlayerId(); + bool playerHost = hostId == player->getPlayerId(); + bool spectator = player->getSpectator(); + player->prepareDestroy(); + + if (!getPlayerCount()) { + gameClosed = true; + deleteLater(); + return; + } else if (!spectator) { + if (playerHost) { + int newHostId = -1; + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + if (!p->getSpectator()) { + newHostId = p->getPlayerId(); + break; + } + } + if (newHostId != -1) { + hostId = newHostId; + sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); + } + } + stopGameIfFinished(); + if (gameStarted && playerActive) + nextTurn(); + } + + ServerInfo_Game gameInfo; + gameInfo.set_room_id(room->getId()); + gameInfo.set_game_id(gameId); + gameInfo.set_player_count(getPlayerCount()); + gameInfo.set_spectators_count(getSpectatorCount()); + emit gameInfoChanged(gameInfo); } void Server_Game::removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Player *player) { - QMutexLocker locker(&gameMutex); - - // Remove all arrows of other players pointing to the player being removed or to one of his cards. - // Also remove all arrows starting at one of his cards. This is necessary since players can create - // arrows that start at another person's cards. - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - QList arrows = p->getArrows().values(); - QList toDelete; - for (int i = 0; i < arrows.size(); ++i) { - Server_Arrow *a = arrows[i]; - Server_Card *targetCard = qobject_cast(a->getTargetItem()); - if (targetCard) { - if (targetCard->getZone()->getPlayer() == player) - toDelete.append(a); - } else if (static_cast(a->getTargetItem()) == player) - toDelete.append(a); - - // Don't use else here! It has to happen regardless of whether targetCard == 0. - if (a->getStartCard()->getZone()->getPlayer() == player) - toDelete.append(a); - } - for (int i = 0; i < toDelete.size(); ++i) { - Event_DeleteArrow event; - event.set_arrow_id(toDelete[i]->getId()); - ges.enqueueGameEvent(event, p->getPlayerId()); - - p->deleteArrow(toDelete[i]->getId()); - } - } + QMutexLocker locker(&gameMutex); + + // Remove all arrows of other players pointing to the player being removed or to one of his cards. + // Also remove all arrows starting at one of his cards. This is necessary since players can create + // arrows that start at another person's cards. + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + QList arrows = p->getArrows().values(); + QList toDelete; + for (int i = 0; i < arrows.size(); ++i) { + Server_Arrow *a = arrows[i]; + Server_Card *targetCard = qobject_cast(a->getTargetItem()); + if (targetCard) { + if (targetCard->getZone()->getPlayer() == player) + toDelete.append(a); + } else if (static_cast(a->getTargetItem()) == player) + toDelete.append(a); + + // Don't use else here! It has to happen regardless of whether targetCard == 0. + if (a->getStartCard()->getZone()->getPlayer() == player) + toDelete.append(a); + } + for (int i = 0; i < toDelete.size(); ++i) { + Event_DeleteArrow event; + event.set_arrow_id(toDelete[i]->getId()); + ges.enqueueGameEvent(event, p->getPlayerId()); + + p->deleteArrow(toDelete[i]->getId()); + } + } } void Server_Game::unattachCards(GameEventStorage &ges, Server_Player *player) { - QMutexLocker locker(&gameMutex); - - QMapIterator zoneIterator(player->getZones()); - while (zoneIterator.hasNext()) { - Server_CardZone *zone = zoneIterator.next().value(); - for (int i = 0; i < zone->getCards().size(); ++i) { - Server_Card *card = zone->getCards().at(i); - - // Make a copy of the list because the original one gets modified during the loop - QList attachedCards = card->getAttachedCards(); - for (int i = 0; i < attachedCards.size(); ++i) - attachedCards[i]->getZone()->getPlayer()->unattachCard(ges, attachedCards[i]); - } - } + QMutexLocker locker(&gameMutex); + + QMapIterator zoneIterator(player->getZones()); + while (zoneIterator.hasNext()) { + Server_CardZone *zone = zoneIterator.next().value(); + for (int i = 0; i < zone->getCards().size(); ++i) { + Server_Card *card = zone->getCards().at(i); + + // Make a copy of the list because the original one gets modified during the loop + QList attachedCards = card->getAttachedCards(); + for (int i = 0; i < attachedCards.size(); ++i) + attachedCards[i]->getZone()->getPlayer()->unattachCard(ges, attachedCards[i]); + } + } } bool Server_Game::kickPlayer(int playerId) { - QMutexLocker locker(&gameMutex); - - Server_Player *playerToKick = players.value(playerId); - if (!playerToKick) - return false; - - GameEventContainer *gec = prepareGameEvent(Event_Kicked(), -1); - playerToKick->sendGameEvent(*gec); - delete gec; - - removePlayer(playerToKick); - - return true; + QMutexLocker locker(&gameMutex); + + Server_Player *playerToKick = players.value(playerId); + if (!playerToKick) + return false; + + GameEventContainer *gec = prepareGameEvent(Event_Kicked(), -1); + playerToKick->sendGameEvent(*gec); + delete gec; + + removePlayer(playerToKick); + + return true; } void Server_Game::setActivePlayer(int _activePlayer) { - QMutexLocker locker(&gameMutex); - - activePlayer = _activePlayer; - - Event_SetActivePlayer event; - event.set_active_player_id(activePlayer); - sendGameEventContainer(prepareGameEvent(event, -1)); - - setActivePhase(0); + QMutexLocker locker(&gameMutex); + + activePlayer = _activePlayer; + + Event_SetActivePlayer event; + event.set_active_player_id(activePlayer); + sendGameEventContainer(prepareGameEvent(event, -1)); + + setActivePhase(0); } void Server_Game::setActivePhase(int _activePhase) { - QMutexLocker locker(&gameMutex); - - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); - QList toDelete = player->getArrows().values(); - for (int i = 0; i < toDelete.size(); ++i) { - Server_Arrow *a = toDelete[i]; - - Event_DeleteArrow event; - event.set_arrow_id(a->getId()); - sendGameEventContainer(prepareGameEvent(event, player->getPlayerId())); - - player->deleteArrow(a->getId()); - } - } - - activePhase = _activePhase; - - Event_SetActivePhase event; - event.set_phase(activePhase); - sendGameEventContainer(prepareGameEvent(event, -1)); + QMutexLocker locker(&gameMutex); + + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Server_Player *player = playerIterator.next().value(); + QList toDelete = player->getArrows().values(); + for (int i = 0; i < toDelete.size(); ++i) { + Server_Arrow *a = toDelete[i]; + + Event_DeleteArrow event; + event.set_arrow_id(a->getId()); + sendGameEventContainer(prepareGameEvent(event, player->getPlayerId())); + + player->deleteArrow(a->getId()); + } + } + + activePhase = _activePhase; + + Event_SetActivePhase event; + event.set_phase(activePhase); + sendGameEventContainer(prepareGameEvent(event, -1)); } void Server_Game::nextTurn() { - QMutexLocker locker(&gameMutex); - - const QList keys = players.keys(); - int listPos = -1; - if (activePlayer != -1) - listPos = keys.indexOf(activePlayer); - do { - ++listPos; - if (listPos == keys.size()) - listPos = 0; - } while (players.value(keys[listPos])->getSpectator() || players.value(keys[listPos])->getConceded()); - - setActivePlayer(keys[listPos]); + QMutexLocker locker(&gameMutex); + + const QList keys = players.keys(); + int listPos = -1; + if (activePlayer != -1) + listPos = keys.indexOf(activePlayer); + do { + ++listPos; + if (listPos == keys.size()) + listPos = 0; + } while (players.value(keys[listPos])->getSpectator() || players.value(keys[listPos])->getConceded()); + + setActivePlayer(keys[listPos]); } void Server_Game::createGameJoinedEvent(Server_Player *player, ResponseContainer &rc, bool resuming) { - Event_GameJoined event1; - getInfo(*event1.mutable_game_info()); - event1.set_host_id(hostId); - event1.set_player_id(player->getPlayerId()); - event1.set_spectator(player->getSpectator()); - event1.set_resuming(resuming); - if (resuming) { - const QStringList &allGameTypes = room->getGameTypes(); - for (int i = 0; i < allGameTypes.size(); ++i) { - ServerInfo_GameType *newGameType = event1.add_game_types(); - newGameType->set_game_type_id(i); - newGameType->set_description(allGameTypes[i].toStdString()); - } - } - rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1)); - - Event_GameStateChanged event2; - event2.set_seconds_elapsed(secondsElapsed); - event2.set_game_started(gameStarted); - event2.set_active_player_id(activePlayer); - event2.set_active_phase(activePhase); - - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - playerIterator.next().value()->getInfo(event2.add_player_list(), player, player->getSpectator() && spectatorsSeeEverything, true); + Event_GameJoined event1; + getInfo(*event1.mutable_game_info()); + event1.set_host_id(hostId); + event1.set_player_id(player->getPlayerId()); + event1.set_spectator(player->getSpectator()); + event1.set_resuming(resuming); + if (resuming) { + const QStringList &allGameTypes = room->getGameTypes(); + for (int i = 0; i < allGameTypes.size(); ++i) { + ServerInfo_GameType *newGameType = event1.add_game_types(); + newGameType->set_game_type_id(i); + newGameType->set_description(allGameTypes[i].toStdString()); + } + } + rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1)); + + Event_GameStateChanged event2; + event2.set_seconds_elapsed(secondsElapsed); + event2.set_game_started(gameStarted); + event2.set_active_player_id(activePlayer); + event2.set_active_phase(activePhase); + + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) + playerIterator.next().value()->getInfo(event2.add_player_list(), player, player->getSpectator() && spectatorsSeeEverything, true); - rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1)); + rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1)); } void Server_Game::sendGameEventContainer(GameEventContainer *cont, GameEventStorageItem::EventRecipients recipients, int privatePlayerId) { - QMutexLocker locker(&gameMutex); - - cont->set_game_id(gameId); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - const bool playerPrivate = (p->getPlayerId() == privatePlayerId) || (p->getSpectator() && spectatorsSeeEverything); - if ((recipients.testFlag(GameEventStorageItem::SendToPrivate) && playerPrivate) || (recipients.testFlag(GameEventStorageItem::SendToOthers) && !playerPrivate)) - p->sendGameEvent(*cont); - } - if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) { - cont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); - cont->clear_game_id(); - currentReplay->add_event_list()->CopyFrom(*cont); - } - - delete cont; + QMutexLocker locker(&gameMutex); + + cont->set_game_id(gameId); + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + const bool playerPrivate = (p->getPlayerId() == privatePlayerId) || (p->getSpectator() && spectatorsSeeEverything); + if ((recipients.testFlag(GameEventStorageItem::SendToPrivate) && playerPrivate) || (recipients.testFlag(GameEventStorageItem::SendToOthers) && !playerPrivate)) + p->sendGameEvent(*cont); + } + if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) { + cont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); + cont->clear_game_id(); + currentReplay->add_event_list()->CopyFrom(*cont); + } + + delete cont; } GameEventContainer *Server_Game::prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context) { - GameEventContainer *cont = new GameEventContainer; - cont->set_game_id(gameId); - if (context) - cont->mutable_context()->CopyFrom(*context); - GameEvent *event = cont->add_event_list(); - if (playerId != -1) - event->set_player_id(playerId); - event->GetReflection()->MutableMessage(event, gameEvent.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(gameEvent); - return cont; + GameEventContainer *cont = new GameEventContainer; + cont->set_game_id(gameId); + if (context) + cont->mutable_context()->CopyFrom(*context); + GameEvent *event = cont->add_event_list(); + if (playerId != -1) + event->set_player_id(playerId); + event->GetReflection()->MutableMessage(event, gameEvent.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(gameEvent); + return cont; } void Server_Game::getInfo(ServerInfo_Game &result) const { - QMutexLocker locker(&gameMutex); - - result.set_room_id(room->getId()); - result.set_game_id(gameId); - if (gameClosed) - result.set_closed(true); - else { - for (int i = 0; i < gameTypes.size(); ++i) - result.add_game_types(gameTypes[i]); - - result.set_max_players(getMaxPlayers()); - result.set_description(getDescription().toStdString()); - result.set_with_password(!getPassword().isEmpty()); - result.set_player_count(getPlayerCount()); - result.set_started(gameStarted); - result.mutable_creator_info()->CopyFrom(*getCreatorInfo()); - result.set_only_buddies(onlyBuddies); - result.set_only_registered(onlyRegistered); - result.set_spectators_allowed(getSpectatorsAllowed()); - result.set_spectators_need_password(getSpectatorsNeedPassword()); - result.set_spectators_can_chat(spectatorsCanTalk); - result.set_spectators_omniscient(spectatorsSeeEverything); - result.set_spectators_count(getSpectatorCount()); - result.set_start_time(startTime.toTime_t()); - } + QMutexLocker locker(&gameMutex); + + result.set_room_id(room->getId()); + result.set_game_id(gameId); + if (gameClosed) + result.set_closed(true); + else { + for (int i = 0; i < gameTypes.size(); ++i) + result.add_game_types(gameTypes[i]); + + result.set_max_players(getMaxPlayers()); + result.set_description(getDescription().toStdString()); + result.set_with_password(!getPassword().isEmpty()); + result.set_player_count(getPlayerCount()); + result.set_started(gameStarted); + result.mutable_creator_info()->CopyFrom(*getCreatorInfo()); + result.set_only_buddies(onlyBuddies); + result.set_only_registered(onlyRegistered); + result.set_spectators_allowed(getSpectatorsAllowed()); + result.set_spectators_need_password(getSpectatorsNeedPassword()); + result.set_spectators_can_chat(spectatorsCanTalk); + result.set_spectators_omniscient(spectatorsSeeEverything); + result.set_spectators_count(getSpectatorCount()); + result.set_start_time(startTime.toTime_t()); + } } diff --git a/common/server_game.h b/common/server_game.h index f6ddbf927..cf7eb0862 100644 --- a/common/server_game.h +++ b/common/server_game.h @@ -42,86 +42,86 @@ class Server_AbstractUserInterface; class Event_GameStateChanged; class Server_Game : public QObject { - Q_OBJECT + Q_OBJECT private: - Server_Room *room; - int nextPlayerId; - int hostId; - ServerInfo_User *creatorInfo; - QMap players; - QSet allPlayersEver, allSpectatorsEver; - bool gameStarted; - bool gameClosed; - int gameId; - QString description; - QString password; - int maxPlayers; - QList gameTypes; - int activePlayer, activePhase; - bool onlyBuddies, onlyRegistered; - bool spectatorsAllowed; - bool spectatorsNeedPassword; - bool spectatorsCanTalk; - bool spectatorsSeeEverything; - int inactivityCounter; - int startTimeOfThisGame, secondsElapsed; - bool firstGameStarted; - QDateTime startTime; - QTimer *pingClock; - QList replayList; - GameReplay *currentReplay; - - void createGameStateChangedEvent(Event_GameStateChanged *event, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo); - void sendGameStateToPlayers(); - void storeGameInformation(); + Server_Room *room; + int nextPlayerId; + int hostId; + ServerInfo_User *creatorInfo; + QMap players; + QSet allPlayersEver, allSpectatorsEver; + bool gameStarted; + bool gameClosed; + int gameId; + QString description; + QString password; + int maxPlayers; + QList gameTypes; + int activePlayer, activePhase; + bool onlyBuddies, onlyRegistered; + bool spectatorsAllowed; + bool spectatorsNeedPassword; + bool spectatorsCanTalk; + bool spectatorsSeeEverything; + int inactivityCounter; + int startTimeOfThisGame, secondsElapsed; + bool firstGameStarted; + QDateTime startTime; + QTimer *pingClock; + QList replayList; + GameReplay *currentReplay; + + void createGameStateChangedEvent(Event_GameStateChanged *event, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo); + void sendGameStateToPlayers(); + void storeGameInformation(); signals: - void sigStartGameIfReady(); - void gameInfoChanged(ServerInfo_Game gameInfo); + void sigStartGameIfReady(); + void gameInfoChanged(ServerInfo_Game gameInfo); private slots: - void pingClockTimeout(); - void doStartGameIfReady(); + void pingClockTimeout(); + void doStartGameIfReady(); public: - mutable QMutex gameMutex; - Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const QString &_description, const QString &_password, int _maxPlayers, const QList &_gameTypes, bool _onlyBuddies, bool _onlyRegistered, bool _spectatorsAllowed, bool _spectatorsNeedPassword, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, Server_Room *parent); - ~Server_Game(); - Server_Room *getRoom() const { return room; } - void getInfo(ServerInfo_Game &result) const; - int getHostId() const { return hostId; } - ServerInfo_User *getCreatorInfo() const { return creatorInfo; } - bool getGameStarted() const { return gameStarted; } - int getPlayerCount() const; - int getSpectatorCount() const; - const QMap &getPlayers() const { return players; } - int getGameId() const { return gameId; } - QString getDescription() const { return description; } - QString getPassword() const { return password; } - int getMaxPlayers() const { return maxPlayers; } - bool getSpectatorsAllowed() const { return spectatorsAllowed; } - bool getSpectatorsNeedPassword() const { return spectatorsNeedPassword; } - bool getSpectatorsCanTalk() const { return spectatorsCanTalk; } - bool getSpectatorsSeeEverything() const { return spectatorsSeeEverything; } - Response::ResponseCode checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions); - bool containsUser(const QString &userName) const; - void addPlayer(Server_AbstractUserInterface *userInterface, ResponseContainer &rc, bool spectator, bool broadcastUpdate = true); - void removePlayer(Server_Player *player); - void removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Player *player); - void unattachCards(GameEventStorage &ges, Server_Player *player); - bool kickPlayer(int playerId); - void startGameIfReady(); - void stopGameIfFinished(); - int getActivePlayer() const { return activePlayer; } - int getActivePhase() const { return activePhase; } - void setActivePlayer(int _activePlayer); - void setActivePhase(int _activePhase); - void nextTurn(); - int getSecondsElapsed() const { return secondsElapsed; } + mutable QMutex gameMutex; + Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const QString &_description, const QString &_password, int _maxPlayers, const QList &_gameTypes, bool _onlyBuddies, bool _onlyRegistered, bool _spectatorsAllowed, bool _spectatorsNeedPassword, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, Server_Room *parent); + ~Server_Game(); + Server_Room *getRoom() const { return room; } + void getInfo(ServerInfo_Game &result) const; + int getHostId() const { return hostId; } + ServerInfo_User *getCreatorInfo() const { return creatorInfo; } + bool getGameStarted() const { return gameStarted; } + int getPlayerCount() const; + int getSpectatorCount() const; + const QMap &getPlayers() const { return players; } + int getGameId() const { return gameId; } + QString getDescription() const { return description; } + QString getPassword() const { return password; } + int getMaxPlayers() const { return maxPlayers; } + bool getSpectatorsAllowed() const { return spectatorsAllowed; } + bool getSpectatorsNeedPassword() const { return spectatorsNeedPassword; } + bool getSpectatorsCanTalk() const { return spectatorsCanTalk; } + bool getSpectatorsSeeEverything() const { return spectatorsSeeEverything; } + Response::ResponseCode checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions); + bool containsUser(const QString &userName) const; + void addPlayer(Server_AbstractUserInterface *userInterface, ResponseContainer &rc, bool spectator, bool broadcastUpdate = true); + void removePlayer(Server_Player *player); + void removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Player *player); + void unattachCards(GameEventStorage &ges, Server_Player *player); + bool kickPlayer(int playerId); + void startGameIfReady(); + void stopGameIfFinished(); + int getActivePlayer() const { return activePlayer; } + int getActivePhase() const { return activePhase; } + void setActivePlayer(int _activePlayer); + void setActivePhase(int _activePhase); + void nextTurn(); + int getSecondsElapsed() const { return secondsElapsed; } - void createGameJoinedEvent(Server_Player *player, ResponseContainer &rc, bool resuming); - - GameEventContainer *prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context = 0); - GameEventContext prepareGameEventContext(const ::google::protobuf::Message &gameEventContext); - - void sendGameEventContainer(GameEventContainer *cont, GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate | GameEventStorageItem::SendToOthers, int privatePlayerId = -1); + void createGameJoinedEvent(Server_Player *player, ResponseContainer &rc, bool resuming); + + GameEventContainer *prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context = 0); + GameEventContext prepareGameEventContext(const ::google::protobuf::Message &gameEventContext); + + void sendGameEventContainer(GameEventContainer *cont, GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate | GameEventStorageItem::SendToOthers, int privatePlayerId = -1); }; #endif diff --git a/common/server_player.cpp b/common/server_player.cpp index 76cd260d0..c25151e8d 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -84,7 +84,7 @@ #include Server_Player::Server_Player(Server_Game *_game, int _playerId, const ServerInfo_User &_userInfo, bool _spectator, Server_AbstractUserInterface *_userInterface) - : ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), deck(0), pingTime(0), playerId(_playerId), spectator(_spectator), nextCardId(0), readyStart(false), conceded(false), sideboardLocked(true) + : ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), deck(0), pingTime(0), playerId(_playerId), spectator(_spectator), nextCardId(0), readyStart(false), conceded(false), sideboardLocked(true) { } @@ -94,1624 +94,1624 @@ Server_Player::~Server_Player() void Server_Player::prepareDestroy() { - delete deck; - - playerMutex.lock(); - if (userInterface) - userInterface->playerRemovedFromGame(game); - playerMutex.unlock(); - - clearZones(); - - deleteLater(); + delete deck; + + playerMutex.lock(); + if (userInterface) + userInterface->playerRemovedFromGame(game); + playerMutex.unlock(); + + clearZones(); + + deleteLater(); } int Server_Player::newCardId() { - return nextCardId++; + return nextCardId++; } int Server_Player::newCounterId() const { - int id = 0; - QMapIterator i(counters); - while (i.hasNext()) { - Server_Counter *c = i.next().value(); - if (c->getId() > id) - id = c->getId(); - } - return id + 1; + int id = 0; + QMapIterator i(counters); + while (i.hasNext()) { + Server_Counter *c = i.next().value(); + if (c->getId() > id) + id = c->getId(); + } + return id + 1; } int Server_Player::newArrowId() const { - int id = 0; - QMapIterator i(arrows); - while (i.hasNext()) { - Server_Arrow *a = i.next().value(); - if (a->getId() > id) - id = a->getId(); - } - return id + 1; + int id = 0; + QMapIterator i(arrows); + while (i.hasNext()) { + Server_Arrow *a = i.next().value(); + if (a->getId() > id) + id = a->getId(); + } + return id + 1; } void Server_Player::setupZones() { - // This may need to be customized according to the game rules. - // ------------------------------------------------------------------ + // This may need to be customized according to the game rules. + // ------------------------------------------------------------------ - // Create zones - Server_CardZone *deckZone = new Server_CardZone(this, "deck", false, ServerInfo_Zone::HiddenZone); - addZone(deckZone); - Server_CardZone *sbZone = new Server_CardZone(this, "sb", false, ServerInfo_Zone::HiddenZone); - addZone(sbZone); - addZone(new Server_CardZone(this, "table", true, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "hand", false, ServerInfo_Zone::PrivateZone)); - addZone(new Server_CardZone(this, "stack", false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone)); + // Create zones + Server_CardZone *deckZone = new Server_CardZone(this, "deck", false, ServerInfo_Zone::HiddenZone); + addZone(deckZone); + Server_CardZone *sbZone = new Server_CardZone(this, "sb", false, ServerInfo_Zone::HiddenZone); + addZone(sbZone); + addZone(new Server_CardZone(this, "table", true, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, "hand", false, ServerInfo_Zone::PrivateZone)); + addZone(new Server_CardZone(this, "stack", false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone)); - addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, 20)); - addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); - addCounter(new Server_Counter(2, "u", makeColor(150, 150, 255), 20, 0)); - addCounter(new Server_Counter(3, "b", makeColor(150, 150, 150), 20, 0)); - addCounter(new Server_Counter(4, "r", makeColor(250, 150, 150), 20, 0)); - addCounter(new Server_Counter(5, "g", makeColor(150, 255, 150), 20, 0)); - addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0)); - addCounter(new Server_Counter(7, "storm", makeColor(255, 255, 255), 20, 0)); + addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, 20)); + addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); + addCounter(new Server_Counter(2, "u", makeColor(150, 150, 255), 20, 0)); + addCounter(new Server_Counter(3, "b", makeColor(150, 150, 150), 20, 0)); + addCounter(new Server_Counter(4, "r", makeColor(250, 150, 150), 20, 0)); + addCounter(new Server_Counter(5, "g", makeColor(150, 255, 150), 20, 0)); + addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0)); + addCounter(new Server_Counter(7, "storm", makeColor(255, 255, 255), 20, 0)); - initialCards = 7; + initialCards = 7; - // ------------------------------------------------------------------ + // ------------------------------------------------------------------ - // Assign card ids and create deck from decklist - InnerDecklistNode *listRoot = deck->getRoot(); - nextCardId = 0; - for (int i = 0; i < listRoot->size(); ++i) { - InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); - Server_CardZone *z; - if (currentZone->getName() == "main") - z = deckZone; - else if (currentZone->getName() == "side") - z = sbZone; - else - continue; - - for (int j = 0; j < currentZone->size(); ++j) { - DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); - if (!currentCard) - continue; - for (int k = 0; k < currentCard->getNumber(); ++k) - z->insertCard(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z), -1, 0); - } - } - - const QList &sideboardPlan = deck->getCurrentSideboardPlan(); - for (int i = 0; i < sideboardPlan.size(); ++i) { - const MoveCard_ToZone &m = sideboardPlan[i]; - const QString startZone = QString::fromStdString(m.start_zone()); - const QString targetZone = QString::fromStdString(m.target_zone()); - - Server_CardZone *start, *target; - if (startZone == "main") - start = deckZone; - else if (startZone == "side") - start = sbZone; - else - continue; - if (targetZone == "main") - target = deckZone; - else if (targetZone == "side") - target = sbZone; - else - continue; - - for (int j = 0; j < start->getCards().size(); ++j) - if (start->getCards()[j]->getName() == QString::fromStdString(m.card_name())) { - Server_Card *card = start->getCard(j, NULL, true); - target->insertCard(card, -1, 0); - break; - } - } - - deckZone->shuffle(); + // Assign card ids and create deck from decklist + InnerDecklistNode *listRoot = deck->getRoot(); + nextCardId = 0; + for (int i = 0; i < listRoot->size(); ++i) { + InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); + Server_CardZone *z; + if (currentZone->getName() == "main") + z = deckZone; + else if (currentZone->getName() == "side") + z = sbZone; + else + continue; + + for (int j = 0; j < currentZone->size(); ++j) { + DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); + if (!currentCard) + continue; + for (int k = 0; k < currentCard->getNumber(); ++k) + z->insertCard(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z), -1, 0); + } + } + + const QList &sideboardPlan = deck->getCurrentSideboardPlan(); + for (int i = 0; i < sideboardPlan.size(); ++i) { + const MoveCard_ToZone &m = sideboardPlan[i]; + const QString startZone = QString::fromStdString(m.start_zone()); + const QString targetZone = QString::fromStdString(m.target_zone()); + + Server_CardZone *start, *target; + if (startZone == "main") + start = deckZone; + else if (startZone == "side") + start = sbZone; + else + continue; + if (targetZone == "main") + target = deckZone; + else if (targetZone == "side") + target = sbZone; + else + continue; + + for (int j = 0; j < start->getCards().size(); ++j) + if (start->getCards()[j]->getName() == QString::fromStdString(m.card_name())) { + Server_Card *card = start->getCard(j, NULL, true); + target->insertCard(card, -1, 0); + break; + } + } + + deckZone->shuffle(); } void Server_Player::clearZones() { - QMapIterator zoneIterator(zones); - while (zoneIterator.hasNext()) - delete zoneIterator.next().value(); - zones.clear(); + QMapIterator zoneIterator(zones); + while (zoneIterator.hasNext()) + delete zoneIterator.next().value(); + zones.clear(); - QMapIterator counterIterator(counters); - while (counterIterator.hasNext()) - delete counterIterator.next().value(); - counters.clear(); - - QMapIterator arrowIterator(arrows); - while (arrowIterator.hasNext()) - delete arrowIterator.next().value(); - arrows.clear(); + QMapIterator counterIterator(counters); + while (counterIterator.hasNext()) + delete counterIterator.next().value(); + counters.clear(); + + QMapIterator arrowIterator(arrows); + while (arrowIterator.hasNext()) + delete arrowIterator.next().value(); + arrows.clear(); - lastDrawList.clear(); + lastDrawList.clear(); } void Server_Player::getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo) { - result.set_player_id(playerId); - if (withUserInfo) - copyUserInfo(*(result.mutable_user_info()), true); - result.set_spectator(spectator); - if (!spectator) { - result.set_conceded(conceded); - result.set_sideboard_locked(sideboardLocked); - result.set_ready_start(readyStart); - } - if (deck) - result.set_deck_hash(deck->getDeckHash().toStdString()); - result.set_ping_seconds(pingTime); + result.set_player_id(playerId); + if (withUserInfo) + copyUserInfo(*(result.mutable_user_info()), true); + result.set_spectator(spectator); + if (!spectator) { + result.set_conceded(conceded); + result.set_sideboard_locked(sideboardLocked); + result.set_ready_start(readyStart); + } + if (deck) + result.set_deck_hash(deck->getDeckHash().toStdString()); + result.set_ping_seconds(pingTime); } void Server_Player::addZone(Server_CardZone *zone) { - zones.insert(zone->getName(), zone); + zones.insert(zone->getName(), zone); } void Server_Player::addArrow(Server_Arrow *arrow) { - arrows.insert(arrow->getId(), arrow); + arrows.insert(arrow->getId(), arrow); } bool Server_Player::deleteArrow(int arrowId) { - Server_Arrow *arrow = arrows.value(arrowId, 0); - if (!arrow) - return false; - arrows.remove(arrowId); - delete arrow; - return true; + Server_Arrow *arrow = arrows.value(arrowId, 0); + if (!arrow) + return false; + arrows.remove(arrowId); + delete arrow; + return true; } void Server_Player::addCounter(Server_Counter *counter) { - counters.insert(counter->getId(), counter); + counters.insert(counter->getId(), counter); } Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int number) { - Server_CardZone *deckZone = zones.value("deck"); - Server_CardZone *handZone = zones.value("hand"); - if (deckZone->getCards().size() < number) - number = deckZone->getCards().size(); - - Event_DrawCards eventOthers; - eventOthers.set_number(number); - Event_DrawCards eventPrivate(eventOthers); - - for (int i = 0; i < number; ++i) { - Server_Card *card = deckZone->getCard(0, NULL, true); - handZone->insertCard(card, -1, 0); - lastDrawList.append(card->getId()); - - ServerInfo_Card *cardInfo = eventPrivate.add_cards(); - cardInfo->set_id(card->getId()); - cardInfo->set_name(card->getName().toStdString()); - } - - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - - if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) { - Event_RevealCards revealEvent; - revealEvent.set_zone_name(deckZone->getName().toStdString()); - revealEvent.set_card_id(0); - deckZone->getCards().first()->getInfo(revealEvent.add_cards()); - - ges.enqueueGameEvent(revealEvent, playerId); - } - - return Response::RespOk; + Server_CardZone *deckZone = zones.value("deck"); + Server_CardZone *handZone = zones.value("hand"); + if (deckZone->getCards().size() < number) + number = deckZone->getCards().size(); + + Event_DrawCards eventOthers; + eventOthers.set_number(number); + Event_DrawCards eventPrivate(eventOthers); + + for (int i = 0; i < number; ++i) { + Server_Card *card = deckZone->getCard(0, NULL, true); + handZone->insertCard(card, -1, 0); + lastDrawList.append(card->getId()); + + ServerInfo_Card *cardInfo = eventPrivate.add_cards(); + cardInfo->set_id(card->getId()); + cardInfo->set_name(card->getName().toStdString()); + } + + ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); + ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); + + if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) { + Event_RevealCards revealEvent; + revealEvent.set_zone_name(deckZone->getName().toStdString()); + revealEvent.set_card_id(0); + deckZone->getCards().first()->getInfo(revealEvent.add_cards()); + + ges.enqueueGameEvent(revealEvent, playerId); + } + + return Response::RespOk; } class Server_Player::MoveCardCompareFunctor { private: - int x; + int x; public: - MoveCardCompareFunctor(int _x) : x(_x) { } - inline bool operator()(QPair a, QPair b) - { - if (a.second < x) { - if (b.second >= x) - return false; - else - return (a.second > b.second); - } else { - if (b.second < x) - return true; - else - return (a.second < b.second); - } - } + MoveCardCompareFunctor(int _x) : x(_x) { } + inline bool operator()(QPair a, QPair b) + { + if (a.second < x) { + if (b.second >= x) + return false; + else + return (a.second > b.second); + } else { + if (b.second < x) + return true; + else + return (a.second < b.second); + } + } }; Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_CardZone *startzone, const QList &_cards, Server_CardZone *targetzone, int x, int y, bool fixFreeSpaces, bool undoingDraw) { - // Disallow controller change to other zones than the table. - if (((targetzone->getType() != ServerInfo_Zone::PublicZone) || !targetzone->hasCoords()) && (startzone->getPlayer() != targetzone->getPlayer())) - return Response::RespContextError; - - if (!targetzone->hasCoords() && (x == -1)) - x = targetzone->getCards().size(); - - QList > cardsToMove; - QMap cardProperties; - QSet cardIdsToMove; - for (int i = 0; i < _cards.size(); ++i) { - // The same card being moved twice would lead to undefined behaviour. - if (cardIdsToMove.contains(_cards[i]->card_id())) - continue; - cardIdsToMove.insert(_cards[i]->card_id()); - - // Consistency checks. In case the command contains illegal moves, try to resolve the legal ones still. - int position; - Server_Card *card = startzone->getCard(_cards[i]->card_id(), &position); - if (!card) - return Response::RespNameNotFound; - if (card->getParentCard()) - continue; - if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(x, y)) - continue; - cardsToMove.append(QPair(card, position)); - cardProperties.insert(card, _cards[i]); - } - // In case all moves were filtered out, abort. - if (cardsToMove.isEmpty()) - return Response::RespContextError; - - MoveCardCompareFunctor cmp(startzone == targetzone ? -1 : x); - qSort(cardsToMove.begin(), cardsToMove.end(), cmp); - - bool secondHalf = false; - int xIndex = -1; - for (int cardIndex = 0; cardIndex < cardsToMove.size(); ++cardIndex) { - Server_Card *card = cardsToMove[cardIndex].first; - const CardToMove *thisCardProperties = cardProperties.value(card); - bool faceDown = thisCardProperties->has_face_down() ? thisCardProperties->face_down() : card->getFaceDown(); - if (!targetzone->hasCoords()) - faceDown = false; - - int originalPosition = cardsToMove[cardIndex].second; - int position = startzone->removeCard(card); - if (startzone->getName() == "hand") { - if (undoingDraw) - lastDrawList.removeAt(lastDrawList.indexOf(card->getId())); - else if (lastDrawList.contains(card->getId())) - lastDrawList.clear(); - } - - if ((startzone == targetzone) && !startzone->hasCoords()) { - if (!secondHalf && (originalPosition < x)) { - xIndex = -1; - secondHalf = true; - } else if (secondHalf) - --xIndex; - else - ++xIndex; - } else - ++xIndex; - int newX = x + xIndex; - - // Attachment relationships can be retained when moving a card onto the opponent's table - if (startzone->getName() != targetzone->getName()) { - // Delete all attachment relationships - if (card->getParentCard()) - card->setParentCard(0); - - // Make a copy of the list because the original one gets modified during the loop - QList attachedCards = card->getAttachedCards(); - for (int i = 0; i < attachedCards.size(); ++i) - attachedCards[i]->getZone()->getPlayer()->unattachCard(ges, attachedCards[i]); - } - - if (startzone != targetzone) { - // Delete all arrows from and to the card - const QList &players = game->getPlayers().values(); - for (int i = 0; i < players.size(); ++i) { - QList arrowsToDelete; - QMapIterator arrowIterator(players[i]->getArrows()); - while (arrowIterator.hasNext()) { - Server_Arrow *arrow = arrowIterator.next().value(); - if ((arrow->getStartCard() == card) || (arrow->getTargetItem() == card)) - arrowsToDelete.append(arrow->getId()); - } - for (int j = 0; j < arrowsToDelete.size(); ++j) - players[i]->deleteArrow(arrowsToDelete[j]); - } - } - - int publicNewX; - if (card->getDestroyOnZoneChange() && (startzone->getName() != targetzone->getName())) { - Event_DestroyCard event; - event.set_zone_name(startzone->getName().toStdString()); - event.set_card_id(card->getId()); - ges.enqueueGameEvent(event, playerId); - - card->deleteLater(); - } else { - if (!targetzone->hasCoords()) { - y = 0; - card->resetState(); - } else - newX = targetzone->getFreeGridColumn(newX, y, card->getName()); - - targetzone->insertCard(card, newX, y); - - bool targetBeingLookedAt = (targetzone->getType() != ServerInfo_Zone::HiddenZone) || (targetzone->getCardsBeingLookedAt() > newX) || (targetzone->getCardsBeingLookedAt() == -1); - bool sourceBeingLookedAt = (startzone->getType() != ServerInfo_Zone::HiddenZone) || (startzone->getCardsBeingLookedAt() > position) || (startzone->getCardsBeingLookedAt() == -1); - - bool targetHiddenToPlayer = faceDown || !targetBeingLookedAt; - bool targetHiddenToOthers = faceDown || (targetzone->getType() != ServerInfo_Zone::PublicZone); - bool sourceHiddenToPlayer = card->getFaceDown() || !sourceBeingLookedAt; - bool sourceHiddenToOthers = card->getFaceDown() || (startzone->getType() != ServerInfo_Zone::PublicZone); - - QString privateCardName, publicCardName; - if (!(sourceHiddenToPlayer && targetHiddenToPlayer)) - privateCardName = card->getName(); - if (!(sourceHiddenToOthers && targetHiddenToOthers)) - publicCardName = card->getName(); - - int oldCardId = card->getId(); - if ((faceDown && (startzone != targetzone)) || (targetzone->getPlayer() != startzone->getPlayer())) - card->setId(targetzone->getPlayer()->newCardId()); - card->setFaceDown(faceDown); - - // The player does not get to see which card he moved if it moves between two parts of hidden zones which - // are not being looked at. - int privateNewCardId = card->getId(); - int privateOldCardId = oldCardId; - if (!targetBeingLookedAt && !sourceBeingLookedAt) { - privateOldCardId = -1; - privateNewCardId = -1; - privateCardName = QString(); - } - int privatePosition = -1; - if (startzone->getType() == ServerInfo_Zone::HiddenZone) - privatePosition = position; - - publicNewX = newX; - - Event_MoveCard eventOthers; - eventOthers.set_start_player_id(startzone->getPlayer()->getPlayerId()); - eventOthers.set_start_zone(startzone->getName().toStdString()); - eventOthers.set_target_player_id(targetzone->getPlayer()->getPlayerId()); - if (startzone != targetzone) - eventOthers.set_target_zone(targetzone->getName().toStdString()); - eventOthers.set_y(y); - eventOthers.set_face_down(faceDown); - - Event_MoveCard eventPrivate(eventOthers); - eventPrivate.set_card_id(privateOldCardId); - if (!privateCardName.isEmpty()) - eventPrivate.set_card_name(privateCardName.toStdString()); - eventPrivate.set_position(privatePosition); - eventPrivate.set_new_card_id(privateNewCardId); - eventPrivate.set_x(newX); - - // Other players do not get to see the start and/or target position of the card if the respective - // part of the zone is being looked at. The information is not needed anyway because in hidden zones, - // all cards are equal. - if ( - ((startzone->getType() == ServerInfo_Zone::HiddenZone) && ((startzone->getCardsBeingLookedAt() > position) || (startzone->getCardsBeingLookedAt() == -1))) - || (startzone->getType() == ServerInfo_Zone::PublicZone) - ) - position = -1; - if ((targetzone->getType() == ServerInfo_Zone::HiddenZone) && ((targetzone->getCardsBeingLookedAt() > newX) || (targetzone->getCardsBeingLookedAt() == -1))) - publicNewX = -1; - - eventOthers.set_x(publicNewX); - eventOthers.set_position(position); - if ((startzone->getType() == ServerInfo_Zone::PublicZone) || (targetzone->getType() == ServerInfo_Zone::PublicZone)) { - eventOthers.set_card_id(oldCardId); - if (!publicCardName.isEmpty()) - eventOthers.set_card_name(publicCardName.toStdString()); - eventOthers.set_new_card_id(card->getId()); - } - - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - - if (thisCardProperties->tapped()) - setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrTapped, "1"); - QString ptString = QString::fromStdString(thisCardProperties->pt()); - if (!ptString.isEmpty() && !faceDown) - setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrPT, ptString); - } - if (startzone->getAlwaysRevealTopCard() && !startzone->getCards().isEmpty() && (originalPosition == 0)) { - Event_RevealCards revealEvent; - revealEvent.set_zone_name(startzone->getName().toStdString()); - revealEvent.set_card_id(0); - startzone->getCards().first()->getInfo(revealEvent.add_cards()); - - ges.enqueueGameEvent(revealEvent, playerId); - } - if (targetzone->getAlwaysRevealTopCard() && !targetzone->getCards().isEmpty() && (newX == 0)) { - Event_RevealCards revealEvent; - revealEvent.set_zone_name(targetzone->getName().toStdString()); - revealEvent.set_card_id(0); - targetzone->getCards().first()->getInfo(revealEvent.add_cards()); - - ges.enqueueGameEvent(revealEvent, playerId); - } - } - if (undoingDraw) - ges.setGameEventContext(Context_UndoDraw()); - else - ges.setGameEventContext(Context_MoveCard()); - - if (startzone->hasCoords() && fixFreeSpaces) - startzone->fixFreeSpaces(ges); - - return Response::RespOk; + // Disallow controller change to other zones than the table. + if (((targetzone->getType() != ServerInfo_Zone::PublicZone) || !targetzone->hasCoords()) && (startzone->getPlayer() != targetzone->getPlayer())) + return Response::RespContextError; + + if (!targetzone->hasCoords() && (x == -1)) + x = targetzone->getCards().size(); + + QList > cardsToMove; + QMap cardProperties; + QSet cardIdsToMove; + for (int i = 0; i < _cards.size(); ++i) { + // The same card being moved twice would lead to undefined behaviour. + if (cardIdsToMove.contains(_cards[i]->card_id())) + continue; + cardIdsToMove.insert(_cards[i]->card_id()); + + // Consistency checks. In case the command contains illegal moves, try to resolve the legal ones still. + int position; + Server_Card *card = startzone->getCard(_cards[i]->card_id(), &position); + if (!card) + return Response::RespNameNotFound; + if (card->getParentCard()) + continue; + if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(x, y)) + continue; + cardsToMove.append(QPair(card, position)); + cardProperties.insert(card, _cards[i]); + } + // In case all moves were filtered out, abort. + if (cardsToMove.isEmpty()) + return Response::RespContextError; + + MoveCardCompareFunctor cmp(startzone == targetzone ? -1 : x); + qSort(cardsToMove.begin(), cardsToMove.end(), cmp); + + bool secondHalf = false; + int xIndex = -1; + for (int cardIndex = 0; cardIndex < cardsToMove.size(); ++cardIndex) { + Server_Card *card = cardsToMove[cardIndex].first; + const CardToMove *thisCardProperties = cardProperties.value(card); + bool faceDown = thisCardProperties->has_face_down() ? thisCardProperties->face_down() : card->getFaceDown(); + if (!targetzone->hasCoords()) + faceDown = false; + + int originalPosition = cardsToMove[cardIndex].second; + int position = startzone->removeCard(card); + if (startzone->getName() == "hand") { + if (undoingDraw) + lastDrawList.removeAt(lastDrawList.indexOf(card->getId())); + else if (lastDrawList.contains(card->getId())) + lastDrawList.clear(); + } + + if ((startzone == targetzone) && !startzone->hasCoords()) { + if (!secondHalf && (originalPosition < x)) { + xIndex = -1; + secondHalf = true; + } else if (secondHalf) + --xIndex; + else + ++xIndex; + } else + ++xIndex; + int newX = x + xIndex; + + // Attachment relationships can be retained when moving a card onto the opponent's table + if (startzone->getName() != targetzone->getName()) { + // Delete all attachment relationships + if (card->getParentCard()) + card->setParentCard(0); + + // Make a copy of the list because the original one gets modified during the loop + QList attachedCards = card->getAttachedCards(); + for (int i = 0; i < attachedCards.size(); ++i) + attachedCards[i]->getZone()->getPlayer()->unattachCard(ges, attachedCards[i]); + } + + if (startzone != targetzone) { + // Delete all arrows from and to the card + const QList &players = game->getPlayers().values(); + for (int i = 0; i < players.size(); ++i) { + QList arrowsToDelete; + QMapIterator arrowIterator(players[i]->getArrows()); + while (arrowIterator.hasNext()) { + Server_Arrow *arrow = arrowIterator.next().value(); + if ((arrow->getStartCard() == card) || (arrow->getTargetItem() == card)) + arrowsToDelete.append(arrow->getId()); + } + for (int j = 0; j < arrowsToDelete.size(); ++j) + players[i]->deleteArrow(arrowsToDelete[j]); + } + } + + int publicNewX; + if (card->getDestroyOnZoneChange() && (startzone->getName() != targetzone->getName())) { + Event_DestroyCard event; + event.set_zone_name(startzone->getName().toStdString()); + event.set_card_id(card->getId()); + ges.enqueueGameEvent(event, playerId); + + card->deleteLater(); + } else { + if (!targetzone->hasCoords()) { + y = 0; + card->resetState(); + } else + newX = targetzone->getFreeGridColumn(newX, y, card->getName()); + + targetzone->insertCard(card, newX, y); + + bool targetBeingLookedAt = (targetzone->getType() != ServerInfo_Zone::HiddenZone) || (targetzone->getCardsBeingLookedAt() > newX) || (targetzone->getCardsBeingLookedAt() == -1); + bool sourceBeingLookedAt = (startzone->getType() != ServerInfo_Zone::HiddenZone) || (startzone->getCardsBeingLookedAt() > position) || (startzone->getCardsBeingLookedAt() == -1); + + bool targetHiddenToPlayer = faceDown || !targetBeingLookedAt; + bool targetHiddenToOthers = faceDown || (targetzone->getType() != ServerInfo_Zone::PublicZone); + bool sourceHiddenToPlayer = card->getFaceDown() || !sourceBeingLookedAt; + bool sourceHiddenToOthers = card->getFaceDown() || (startzone->getType() != ServerInfo_Zone::PublicZone); + + QString privateCardName, publicCardName; + if (!(sourceHiddenToPlayer && targetHiddenToPlayer)) + privateCardName = card->getName(); + if (!(sourceHiddenToOthers && targetHiddenToOthers)) + publicCardName = card->getName(); + + int oldCardId = card->getId(); + if ((faceDown && (startzone != targetzone)) || (targetzone->getPlayer() != startzone->getPlayer())) + card->setId(targetzone->getPlayer()->newCardId()); + card->setFaceDown(faceDown); + + // The player does not get to see which card he moved if it moves between two parts of hidden zones which + // are not being looked at. + int privateNewCardId = card->getId(); + int privateOldCardId = oldCardId; + if (!targetBeingLookedAt && !sourceBeingLookedAt) { + privateOldCardId = -1; + privateNewCardId = -1; + privateCardName = QString(); + } + int privatePosition = -1; + if (startzone->getType() == ServerInfo_Zone::HiddenZone) + privatePosition = position; + + publicNewX = newX; + + Event_MoveCard eventOthers; + eventOthers.set_start_player_id(startzone->getPlayer()->getPlayerId()); + eventOthers.set_start_zone(startzone->getName().toStdString()); + eventOthers.set_target_player_id(targetzone->getPlayer()->getPlayerId()); + if (startzone != targetzone) + eventOthers.set_target_zone(targetzone->getName().toStdString()); + eventOthers.set_y(y); + eventOthers.set_face_down(faceDown); + + Event_MoveCard eventPrivate(eventOthers); + eventPrivate.set_card_id(privateOldCardId); + if (!privateCardName.isEmpty()) + eventPrivate.set_card_name(privateCardName.toStdString()); + eventPrivate.set_position(privatePosition); + eventPrivate.set_new_card_id(privateNewCardId); + eventPrivate.set_x(newX); + + // Other players do not get to see the start and/or target position of the card if the respective + // part of the zone is being looked at. The information is not needed anyway because in hidden zones, + // all cards are equal. + if ( + ((startzone->getType() == ServerInfo_Zone::HiddenZone) && ((startzone->getCardsBeingLookedAt() > position) || (startzone->getCardsBeingLookedAt() == -1))) + || (startzone->getType() == ServerInfo_Zone::PublicZone) + ) + position = -1; + if ((targetzone->getType() == ServerInfo_Zone::HiddenZone) && ((targetzone->getCardsBeingLookedAt() > newX) || (targetzone->getCardsBeingLookedAt() == -1))) + publicNewX = -1; + + eventOthers.set_x(publicNewX); + eventOthers.set_position(position); + if ((startzone->getType() == ServerInfo_Zone::PublicZone) || (targetzone->getType() == ServerInfo_Zone::PublicZone)) { + eventOthers.set_card_id(oldCardId); + if (!publicCardName.isEmpty()) + eventOthers.set_card_name(publicCardName.toStdString()); + eventOthers.set_new_card_id(card->getId()); + } + + ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); + ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); + + if (thisCardProperties->tapped()) + setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrTapped, "1"); + QString ptString = QString::fromStdString(thisCardProperties->pt()); + if (!ptString.isEmpty() && !faceDown) + setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrPT, ptString); + } + if (startzone->getAlwaysRevealTopCard() && !startzone->getCards().isEmpty() && (originalPosition == 0)) { + Event_RevealCards revealEvent; + revealEvent.set_zone_name(startzone->getName().toStdString()); + revealEvent.set_card_id(0); + startzone->getCards().first()->getInfo(revealEvent.add_cards()); + + ges.enqueueGameEvent(revealEvent, playerId); + } + if (targetzone->getAlwaysRevealTopCard() && !targetzone->getCards().isEmpty() && (newX == 0)) { + Event_RevealCards revealEvent; + revealEvent.set_zone_name(targetzone->getName().toStdString()); + revealEvent.set_card_id(0); + targetzone->getCards().first()->getInfo(revealEvent.add_cards()); + + ges.enqueueGameEvent(revealEvent, playerId); + } + } + if (undoingDraw) + ges.setGameEventContext(Context_UndoDraw()); + else + ges.setGameEventContext(Context_MoveCard()); + + if (startzone->hasCoords() && fixFreeSpaces) + startzone->fixFreeSpaces(ges); + + return Response::RespOk; } void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card) { - Server_CardZone *zone = card->getZone(); - Server_Card *parentCard = card->getParentCard(); - card->setParentCard(0); - - Event_AttachCard event; - event.set_start_zone(zone->getName().toStdString()); - event.set_card_id(card->getId()); - ges.enqueueGameEvent(event, playerId); - - CardToMove *cardToMove = new CardToMove; - cardToMove->set_card_id(card->getId()); - moveCard(ges, zone, QList() << cardToMove, zone, -1, card->getY(), card->getFaceDown()); - delete cardToMove; - - if (parentCard->getZone()) - parentCard->getZone()->updateCardCoordinates(parentCard, parentCard->getX(), parentCard->getY()); + Server_CardZone *zone = card->getZone(); + Server_Card *parentCard = card->getParentCard(); + card->setParentCard(0); + + Event_AttachCard event; + event.set_start_zone(zone->getName().toStdString()); + event.set_card_id(card->getId()); + ges.enqueueGameEvent(event, playerId); + + CardToMove *cardToMove = new CardToMove; + cardToMove->set_card_id(card->getId()); + moveCard(ges, zone, QList() << cardToMove, zone, -1, card->getY(), card->getFaceDown()); + delete cardToMove; + + if (parentCard->getZone()) + parentCard->getZone()->updateCardCoordinates(parentCard, parentCard->getX(), parentCard->getY()); } Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges, const QString &zoneName, int cardId, CardAttribute attribute, const QString &attrValue) { - Server_CardZone *zone = getZones().value(zoneName); - if (!zone) - return Response::RespNameNotFound; - if (!zone->hasCoords()) - return Response::RespContextError; + Server_CardZone *zone = getZones().value(zoneName); + if (!zone) + return Response::RespNameNotFound; + if (!zone->hasCoords()) + return Response::RespContextError; - QString result; - if (cardId == -1) { - QListIterator CardIterator(zone->getCards()); - while (CardIterator.hasNext()) { - result = CardIterator.next()->setAttribute(attribute, attrValue, true); - if (result.isNull()) - return Response::RespInvalidCommand; - } - } else { - Server_Card *card = zone->getCard(cardId); - if (!card) - return Response::RespNameNotFound; - result = card->setAttribute(attribute, attrValue, false); - if (result.isNull()) - return Response::RespInvalidCommand; - } - - Event_SetCardAttr event; - event.set_zone_name(zone->getName().toStdString()); - if (cardId != -1) - event.set_card_id(cardId); - event.set_attribute(attribute); - event.set_attr_value(result.toStdString()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + QString result; + if (cardId == -1) { + QListIterator CardIterator(zone->getCards()); + while (CardIterator.hasNext()) { + result = CardIterator.next()->setAttribute(attribute, attrValue, true); + if (result.isNull()) + return Response::RespInvalidCommand; + } + } else { + Server_Card *card = zone->getCard(cardId); + if (!card) + return Response::RespNameNotFound; + result = card->setAttribute(attribute, attrValue, false); + if (result.isNull()) + return Response::RespInvalidCommand; + } + + Event_SetCardAttr event; + event.set_zone_name(zone->getName().toStdString()); + if (cardId != -1) + event.set_card_id(cardId); + event.set_attribute(attribute); + event.set_attr_value(result.toStdString()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdLeaveGame(const Command_LeaveGame & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) { - game->removePlayer(this); - return Response::RespOk; + game->removePlayer(this); + return Response::RespOk; } Response::ResponseCode Server_Player::cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) { - if ((game->getHostId() != playerId) && !(userInfo->user_level() & ServerInfo_User::IsModerator)) - return Response::RespFunctionNotAllowed; - - if (!game->kickPlayer(cmd.player_id())) - return Response::RespNameNotFound; - - return Response::RespOk; + if ((game->getHostId() != playerId) && !(userInfo->user_level() & ServerInfo_User::IsModerator)) + return Response::RespFunctionNotAllowed; + + if (!game->kickPlayer(cmd.player_id())) + return Response::RespNameNotFound; + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - DeckList *newDeck; - if (cmd.has_deck_id()) { - try { - newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), userInfo->id()); - } catch(Response::ResponseCode r) { - return r; - } - } else - newDeck = new DeckList(QString::fromStdString(cmd.deck())); - - if (!newDeck) - return Response::RespInternalError; - - delete deck; - deck = newDeck; - sideboardLocked = true; - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_sideboard_locked(true); - event.mutable_player_properties()->set_deck_hash(deck->getDeckHash().toStdString()); - ges.enqueueGameEvent(event, playerId); - - Context_DeckSelect context; - context.set_deck_hash(deck->getDeckHash().toStdString()); + if (spectator) + return Response::RespFunctionNotAllowed; + + DeckList *newDeck; + if (cmd.has_deck_id()) { + try { + newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), userInfo->id()); + } catch(Response::ResponseCode r) { + return r; + } + } else + newDeck = new DeckList(QString::fromStdString(cmd.deck())); + + if (!newDeck) + return Response::RespInternalError; + + delete deck; + deck = newDeck; + sideboardLocked = true; + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_sideboard_locked(true); + event.mutable_player_properties()->set_deck_hash(deck->getDeckHash().toStdString()); + ges.enqueueGameEvent(event, playerId); + + Context_DeckSelect context; + context.set_deck_hash(deck->getDeckHash().toStdString()); context.set_sideboard_size(deck->getSideboardSize()); - ges.setGameEventContext(context); - - Response_DeckDownload *re = new Response_DeckDownload; - re->set_deck(deck->writeToString_Native().toStdString()); - - rc.setResponseExtension(re); - return Response::RespOk; + ges.setGameEventContext(context); + + Response_DeckDownload *re = new Response_DeckDownload; + re->set_deck(deck->writeToString_Native().toStdString()); + + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_Player::cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) { - if (spectator) - return Response::RespFunctionNotAllowed; - if (readyStart) - return Response::RespContextError; - if (!deck) - return Response::RespContextError; - if (sideboardLocked) - return Response::RespContextError; - - QList sideboardPlan; - for (int i = 0; i < cmd.move_list_size(); ++i) - sideboardPlan.append(cmd.move_list(i)); - deck->setCurrentSideboardPlan(sideboardPlan); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + if (readyStart) + return Response::RespContextError; + if (!deck) + return Response::RespContextError; + if (sideboardLocked) + return Response::RespContextError; + + QList sideboardPlan; + for (int i = 0; i < cmd.move_list_size(); ++i) + sideboardPlan.append(cmd.move_list(i)); + deck->setCurrentSideboardPlan(sideboardPlan); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - if (readyStart) - return Response::RespContextError; - if (!deck) - return Response::RespContextError; - if (sideboardLocked == cmd.locked()) - return Response::RespContextError; - - sideboardLocked = cmd.locked(); - if (sideboardLocked) - deck->setCurrentSideboardPlan(QList()); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_sideboard_locked(sideboardLocked); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_SetSideboardLock()); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + if (readyStart) + return Response::RespContextError; + if (!deck) + return Response::RespContextError; + if (sideboardLocked == cmd.locked()) + return Response::RespContextError; + + sideboardLocked = cmd.locked(); + if (sideboardLocked) + deck->setCurrentSideboardPlan(QList()); + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_sideboard_locked(sideboardLocked); + ges.enqueueGameEvent(event, playerId); + ges.setGameEventContext(Context_SetSideboardLock()); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdConcede(const Command_Concede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - setConceded(true); - game->removeArrowsRelatedToPlayer(ges, this); - game->unattachCards(ges, this); - clearZones(); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_conceded(true); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_Concede()); - - game->stopGameIfFinished(); - if (game->getGameStarted() && (game->getActivePlayer() == playerId)) - game->nextTurn(); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + setConceded(true); + game->removeArrowsRelatedToPlayer(ges, this); + game->unattachCards(ges, this); + clearZones(); + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_conceded(true); + ges.enqueueGameEvent(event, playerId); + ges.setGameEventContext(Context_Concede()); + + game->stopGameIfFinished(); + if (game->getGameStarted() && (game->getActivePlayer() == playerId)) + game->nextTurn(); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!deck || game->getGameStarted()) - return Response::RespContextError; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!deck || game->getGameStarted()) + return Response::RespContextError; - if (readyStart == cmd.ready()) - return Response::RespContextError; - - setReadyStart(cmd.ready()); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_ready_start(cmd.ready()); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_ReadyStart()); - - if (cmd.ready()) - game->startGameIfReady(); - - return Response::RespOk; + if (readyStart == cmd.ready()) + return Response::RespContextError; + + setReadyStart(cmd.ready()); + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_ready_start(cmd.ready()); + ges.enqueueGameEvent(event, playerId); + ges.setGameEventContext(Context_ReadyStart()); + + if (cmd.ready()) + game->startGameIfReady(); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdGameSay(const Command_GameSay &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator && !game->getSpectatorsCanTalk() && !(userInfo->user_level() & ServerInfo_User::IsModerator)) - return Response::RespFunctionNotAllowed; - - Event_GameSay event; - event.set_message(cmd.message()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator && !game->getSpectatorsCanTalk() && !(userInfo->user_level() & ServerInfo_User::IsModerator)) + return Response::RespFunctionNotAllowed; + + Event_GameSay event; + event.set_message(cmd.message()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdShuffle(const Command_Shuffle & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *deckZone = zones.value("deck"); - deckZone->shuffle(); - - Event_Shuffle event; - event.set_zone_name("deck"); - ges.enqueueGameEvent(event, playerId); - - if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) { - Event_RevealCards revealEvent; - revealEvent.set_zone_name(deckZone->getName().toStdString()); - revealEvent.set_card_id(0); - deckZone->getCards().first()->getInfo(revealEvent.add_cards()); - - ges.enqueueGameEvent(revealEvent, playerId); - } - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *deckZone = zones.value("deck"); + deckZone->shuffle(); + + Event_Shuffle event; + event.set_zone_name("deck"); + ges.enqueueGameEvent(event, playerId); + + if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) { + Event_RevealCards revealEvent; + revealEvent.set_zone_name(deckZone->getName().toStdString()); + revealEvent.set_card_id(0); + deckZone->getCards().first()->getInfo(revealEvent.add_cards()); + + ges.enqueueGameEvent(revealEvent, playerId); + } + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdMulligan(const Command_Mulligan & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *hand = zones.value("hand"); - int number = (hand->getCards().size() <= 1) ? initialCards : hand->getCards().size() - 1; - - Server_CardZone *deck = zones.value("deck"); - while (!hand->getCards().isEmpty()) { - CardToMove *cardToMove = new CardToMove; - cardToMove->set_card_id(hand->getCards().first()->getId()); - moveCard(ges, hand, QList() << cardToMove, deck, 0, 0, false); - delete cardToMove; - } - - deck->shuffle(); - ges.enqueueGameEvent(Event_Shuffle(), playerId); + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *hand = zones.value("hand"); + int number = (hand->getCards().size() <= 1) ? initialCards : hand->getCards().size() - 1; + + Server_CardZone *deck = zones.value("deck"); + while (!hand->getCards().isEmpty()) { + CardToMove *cardToMove = new CardToMove; + cardToMove->set_card_id(hand->getCards().first()->getId()); + moveCard(ges, hand, QList() << cardToMove, deck, 0, 0, false); + delete cardToMove; + } + + deck->shuffle(); + ges.enqueueGameEvent(Event_Shuffle(), playerId); - drawCards(ges, number); - - if (number == initialCards) - number = -1; - - Context_Mulligan context; - context.set_number(number); - ges.setGameEventContext(context); + drawCards(ges, number); + + if (number == initialCards) + number = -1; + + Context_Mulligan context; + context.set_number(number); + ges.setGameEventContext(context); - return Response::RespOk; + return Response::RespOk; } Response::ResponseCode Server_Player::cmdRollDie(const Command_RollDie &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - if (conceded) - return Response::RespContextError; - - Event_RollDie event; - event.set_sides(cmd.sides()); - event.set_value(rng->rand(1, cmd.sides())); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + if (conceded) + return Response::RespContextError; + + Event_RollDie event; + event.set_sides(cmd.sides()); + event.set_value(rng->rand(1, cmd.sides())); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - return drawCards(ges, cmd.number()); + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + return drawCards(ges, cmd.number()); } Response::ResponseCode Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - if (lastDrawList.isEmpty()) - return Response::RespContextError; - - Response::ResponseCode retVal; - CardToMove *cardToMove = new CardToMove; - cardToMove->set_card_id(lastDrawList.takeLast()); - retVal = moveCard(ges, zones.value("hand"), QList() << cardToMove, zones.value("deck"), 0, 0, false, true); - delete cardToMove; - - return retVal; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + if (lastDrawList.isEmpty()) + return Response::RespContextError; + + Response::ResponseCode retVal; + CardToMove *cardToMove = new CardToMove; + cardToMove->set_card_id(lastDrawList.takeLast()); + retVal = moveCard(ges, zones.value("hand"), QList() << cardToMove, zones.value("deck"), 0, 0, false, true); + delete cardToMove; + + return retVal; } Response::ResponseCode Server_Player::cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Player *startPlayer = game->getPlayers().value(cmd.has_start_player_id() ? cmd.start_player_id() : playerId); - if (!startPlayer) - return Response::RespNameNotFound; - Server_CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(cmd.start_zone())); - if (!startZone) - return Response::RespNameNotFound; - - if ((startPlayer != this) && (!startZone->getPlayersWithWritePermission().contains(playerId))) - return Response::RespContextError; - - Server_Player *targetPlayer = game->getPlayers().value(cmd.target_player_id()); - if (!targetPlayer) - return Response::RespNameNotFound; - Server_CardZone *targetZone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone())); - if (!targetZone) - return Response::RespNameNotFound; - - if ((startPlayer != this) && (targetPlayer != this)) - return Response::RespContextError; - - QList cardsToMove; - for (int i = 0; i < cmd.cards_to_move().card_size(); ++i) - cardsToMove.append(&cmd.cards_to_move().card(i)); - - return moveCard(ges, startZone, cardsToMove, targetZone, cmd.x(), cmd.y()); + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Player *startPlayer = game->getPlayers().value(cmd.has_start_player_id() ? cmd.start_player_id() : playerId); + if (!startPlayer) + return Response::RespNameNotFound; + Server_CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(cmd.start_zone())); + if (!startZone) + return Response::RespNameNotFound; + + if ((startPlayer != this) && (!startZone->getPlayersWithWritePermission().contains(playerId))) + return Response::RespContextError; + + Server_Player *targetPlayer = game->getPlayers().value(cmd.target_player_id()); + if (!targetPlayer) + return Response::RespNameNotFound; + Server_CardZone *targetZone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone())); + if (!targetZone) + return Response::RespNameNotFound; + + if ((startPlayer != this) && (targetPlayer != this)) + return Response::RespContextError; + + QList cardsToMove; + for (int i = 0; i < cmd.cards_to_move().card_size(); ++i) + cardsToMove.append(&cmd.cards_to_move().card(i)); + + return moveCard(ges, startZone, cardsToMove, targetZone, cmd.x(), cmd.y()); } Response::ResponseCode Server_Player::cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); - if (!zone) - return Response::RespNameNotFound; - if (!zone->hasCoords()) - return Response::RespContextError; - - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) - return Response::RespNameNotFound; - - const bool faceDown = cmd.face_down(); - if (faceDown == card->getFaceDown()) - return Response::RespContextError; - - card->setFaceDown(faceDown); - - Event_FlipCard event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - if (!faceDown) - event.set_card_name(card->getName().toStdString()); - event.set_face_down(faceDown); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); + if (!zone) + return Response::RespNameNotFound; + if (!zone->hasCoords()) + return Response::RespContextError; + + Server_Card *card = zone->getCard(cmd.card_id()); + if (!card) + return Response::RespNameNotFound; + + const bool faceDown = cmd.face_down(); + if (faceDown == card->getFaceDown()) + return Response::RespContextError; + + card->setFaceDown(faceDown); + + Event_FlipCard event; + event.set_zone_name(zone->getName().toStdString()); + event.set_card_id(card->getId()); + if (!faceDown) + event.set_card_name(card->getName().toStdString()); + event.set_face_down(faceDown); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *startzone = zones.value(QString::fromStdString(cmd.start_zone())); - if (!startzone) - return Response::RespNameNotFound; - - Server_Card *card = startzone->getCard(cmd.card_id()); - if (!card) - return Response::RespNameNotFound; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *startzone = zones.value(QString::fromStdString(cmd.start_zone())); + if (!startzone) + return Response::RespNameNotFound; + + Server_Card *card = startzone->getCard(cmd.card_id()); + if (!card) + return Response::RespNameNotFound; - Server_Player *targetPlayer = 0; - Server_CardZone *targetzone = 0; - Server_Card *targetCard = 0; - - if (cmd.has_target_player_id()) { - targetPlayer = game->getPlayers().value(cmd.target_player_id()); - if (!targetPlayer) - return Response::RespNameNotFound; - } else if (!card->getParentCard()) - return Response::RespContextError; - if (targetPlayer) - targetzone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone())); - if (targetzone) { - // This is currently enough to make sure cards don't get attached to a card that is not on the table. - // Possibly a flag will have to be introduced for this sometime. - if (!targetzone->hasCoords()) - return Response::RespContextError; - if (cmd.has_target_card_id()) - targetCard = targetzone->getCard(cmd.target_card_id()); - if (targetCard) { - if (targetCard->getParentCard()) - return Response::RespContextError; - } else - return Response::RespNameNotFound; - } - if (!startzone->hasCoords()) - return Response::RespContextError; - - // Get all arrows pointing to or originating from the card being attached and delete them. - QMapIterator playerIterator(game->getPlayers()); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - QList arrows = p->getArrows().values(); - QList toDelete; - for (int i = 0; i < arrows.size(); ++i) { - Server_Arrow *a = arrows[i]; - Server_Card *tCard = qobject_cast(a->getTargetItem()); - if ((tCard == card) || (a->getStartCard() == card)) - toDelete.append(a); - } - for (int i = 0; i < toDelete.size(); ++i) { - Event_DeleteArrow event; - event.set_arrow_id(toDelete[i]->getId()); - ges.enqueueGameEvent(event, p->getPlayerId()); - p->deleteArrow(toDelete[i]->getId()); - } - } + Server_Player *targetPlayer = 0; + Server_CardZone *targetzone = 0; + Server_Card *targetCard = 0; + + if (cmd.has_target_player_id()) { + targetPlayer = game->getPlayers().value(cmd.target_player_id()); + if (!targetPlayer) + return Response::RespNameNotFound; + } else if (!card->getParentCard()) + return Response::RespContextError; + if (targetPlayer) + targetzone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone())); + if (targetzone) { + // This is currently enough to make sure cards don't get attached to a card that is not on the table. + // Possibly a flag will have to be introduced for this sometime. + if (!targetzone->hasCoords()) + return Response::RespContextError; + if (cmd.has_target_card_id()) + targetCard = targetzone->getCard(cmd.target_card_id()); + if (targetCard) { + if (targetCard->getParentCard()) + return Response::RespContextError; + } else + return Response::RespNameNotFound; + } + if (!startzone->hasCoords()) + return Response::RespContextError; + + // Get all arrows pointing to or originating from the card being attached and delete them. + QMapIterator playerIterator(game->getPlayers()); + while (playerIterator.hasNext()) { + Server_Player *p = playerIterator.next().value(); + QList arrows = p->getArrows().values(); + QList toDelete; + for (int i = 0; i < arrows.size(); ++i) { + Server_Arrow *a = arrows[i]; + Server_Card *tCard = qobject_cast(a->getTargetItem()); + if ((tCard == card) || (a->getStartCard() == card)) + toDelete.append(a); + } + for (int i = 0; i < toDelete.size(); ++i) { + Event_DeleteArrow event; + event.set_arrow_id(toDelete[i]->getId()); + ges.enqueueGameEvent(event, p->getPlayerId()); + p->deleteArrow(toDelete[i]->getId()); + } + } - if (targetCard) { - // Unattach all cards attached to the card being attached. - // Make a copy of the list because its contents change during the loop otherwise. - QList attachedList = card->getAttachedCards(); - for (int i = 0; i < attachedList.size(); ++i) - attachedList[i]->getZone()->getPlayer()->unattachCard(ges, attachedList[i]); - - card->setParentCard(targetCard); - const int oldX = card->getX(); - card->setCoords(-1, card->getY()); - startzone->updateCardCoordinates(card, oldX, card->getY()); - - if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) { - CardToMove *cardToMove = new CardToMove; - cardToMove->set_card_id(targetCard->getId()); - targetPlayer->moveCard(ges, targetzone, QList() << cardToMove, targetzone, targetzone->getFreeGridColumn(-2, targetCard->getY(), targetCard->getName()), targetCard->getY(), targetCard->getFaceDown()); - delete cardToMove; - } - - Event_AttachCard event; - event.set_start_zone(startzone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_target_player_id(targetPlayer->getPlayerId()); - event.set_target_zone(targetzone->getName().toStdString()); - event.set_target_card_id(targetCard->getId()); - ges.enqueueGameEvent(event, playerId); - - startzone->fixFreeSpaces(ges); - } else - unattachCard(ges, card); - - return Response::RespOk; + if (targetCard) { + // Unattach all cards attached to the card being attached. + // Make a copy of the list because its contents change during the loop otherwise. + QList attachedList = card->getAttachedCards(); + for (int i = 0; i < attachedList.size(); ++i) + attachedList[i]->getZone()->getPlayer()->unattachCard(ges, attachedList[i]); + + card->setParentCard(targetCard); + const int oldX = card->getX(); + card->setCoords(-1, card->getY()); + startzone->updateCardCoordinates(card, oldX, card->getY()); + + if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) { + CardToMove *cardToMove = new CardToMove; + cardToMove->set_card_id(targetCard->getId()); + targetPlayer->moveCard(ges, targetzone, QList() << cardToMove, targetzone, targetzone->getFreeGridColumn(-2, targetCard->getY(), targetCard->getName()), targetCard->getY(), targetCard->getFaceDown()); + delete cardToMove; + } + + Event_AttachCard event; + event.set_start_zone(startzone->getName().toStdString()); + event.set_card_id(card->getId()); + event.set_target_player_id(targetPlayer->getPlayerId()); + event.set_target_zone(targetzone->getName().toStdString()); + event.set_target_card_id(targetCard->getId()); + ges.enqueueGameEvent(event, playerId); + + startzone->fixFreeSpaces(ges); + } else + unattachCard(ges, card); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); - if (!zone) - return Response::RespNameNotFound; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); + if (!zone) + return Response::RespNameNotFound; - QString cardName = QString::fromStdString(cmd.card_name()); - int x = cmd.x(); - int y = cmd.y(); - if (zone->hasCoords()) - x = zone->getFreeGridColumn(x, y, cardName); - if (x < 0) - x = 0; - if (y < 0) - y = 0; + QString cardName = QString::fromStdString(cmd.card_name()); + int x = cmd.x(); + int y = cmd.y(); + if (zone->hasCoords()) + x = zone->getFreeGridColumn(x, y, cardName); + if (x < 0) + x = 0; + if (y < 0) + y = 0; - Server_Card *card = new Server_Card(cardName, newCardId(), x, y); - card->moveToThread(thread()); - card->setPT(QString::fromStdString(cmd.pt())); - card->setColor(QString::fromStdString(cmd.color())); - card->setAnnotation(QString::fromStdString(cmd.annotation())); - card->setDestroyOnZoneChange(cmd.destroy_on_zone_change()); - - zone->insertCard(card, x, y); - - Event_CreateToken event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_card_name(card->getName().toStdString()); - event.set_color(card->getColor().toStdString()); - event.set_pt(card->getPT().toStdString()); - event.set_annotation(card->getAnnotation().toStdString()); - event.set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - event.set_x(x); - event.set_y(y); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + Server_Card *card = new Server_Card(cardName, newCardId(), x, y); + card->moveToThread(thread()); + card->setPT(QString::fromStdString(cmd.pt())); + card->setColor(QString::fromStdString(cmd.color())); + card->setAnnotation(QString::fromStdString(cmd.annotation())); + card->setDestroyOnZoneChange(cmd.destroy_on_zone_change()); + + zone->insertCard(card, x, y); + + Event_CreateToken event; + event.set_zone_name(zone->getName().toStdString()); + event.set_card_id(card->getId()); + event.set_card_name(card->getName().toStdString()); + event.set_color(card->getColor().toStdString()); + event.set_pt(card->getPT().toStdString()); + event.set_annotation(card->getAnnotation().toStdString()); + event.set_destroy_on_zone_change(card->getDestroyOnZoneChange()); + event.set_x(x); + event.set_y(y); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Player *startPlayer = game->getPlayers().value(cmd.start_player_id()); - Server_Player *targetPlayer = game->getPlayers().value(cmd.target_player_id()); - if (!startPlayer || !targetPlayer) - return Response::RespNameNotFound; - QString startZoneName = QString::fromStdString(cmd.start_zone()); - Server_CardZone *startZone = startPlayer->getZones().value(startZoneName); - bool playerTarget = !cmd.has_target_zone(); - Server_CardZone *targetZone = 0; - if (!playerTarget) - targetZone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone())); - if (!startZone || (!targetZone && !playerTarget)) - return Response::RespNameNotFound; - if (startZone->getType() != ServerInfo_Zone::PublicZone) - return Response::RespContextError; - Server_Card *startCard = startZone->getCard(cmd.start_card_id()); - if (!startCard) - return Response::RespNameNotFound; - Server_Card *targetCard = 0; - if (!playerTarget) { - if (targetZone->getType() != ServerInfo_Zone::PublicZone) - return Response::RespContextError; - targetCard = targetZone->getCard(cmd.target_card_id()); - } - - Server_ArrowTarget *targetItem; - if (playerTarget) - targetItem = targetPlayer; - else - targetItem = targetCard; - if (!targetItem) - return Response::RespNameNotFound; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Player *startPlayer = game->getPlayers().value(cmd.start_player_id()); + Server_Player *targetPlayer = game->getPlayers().value(cmd.target_player_id()); + if (!startPlayer || !targetPlayer) + return Response::RespNameNotFound; + QString startZoneName = QString::fromStdString(cmd.start_zone()); + Server_CardZone *startZone = startPlayer->getZones().value(startZoneName); + bool playerTarget = !cmd.has_target_zone(); + Server_CardZone *targetZone = 0; + if (!playerTarget) + targetZone = targetPlayer->getZones().value(QString::fromStdString(cmd.target_zone())); + if (!startZone || (!targetZone && !playerTarget)) + return Response::RespNameNotFound; + if (startZone->getType() != ServerInfo_Zone::PublicZone) + return Response::RespContextError; + Server_Card *startCard = startZone->getCard(cmd.start_card_id()); + if (!startCard) + return Response::RespNameNotFound; + Server_Card *targetCard = 0; + if (!playerTarget) { + if (targetZone->getType() != ServerInfo_Zone::PublicZone) + return Response::RespContextError; + targetCard = targetZone->getCard(cmd.target_card_id()); + } + + Server_ArrowTarget *targetItem; + if (playerTarget) + targetItem = targetPlayer; + else + targetItem = targetCard; + if (!targetItem) + return Response::RespNameNotFound; - QMapIterator arrowIterator(arrows); - while (arrowIterator.hasNext()) { - Server_Arrow *temp = arrowIterator.next().value(); - if ((temp->getStartCard() == startCard) && (temp->getTargetItem() == targetItem)) - return Response::RespContextError; - } - - Server_Arrow *arrow = new Server_Arrow(newArrowId(), startCard, targetItem, cmd.arrow_color()); - addArrow(arrow); - - Event_CreateArrow event; - ServerInfo_Arrow *arrowInfo = event.mutable_arrow_info(); - arrowInfo->set_id(arrow->getId()); - arrowInfo->set_start_player_id(startPlayer->getPlayerId()); - arrowInfo->set_start_zone(startZoneName.toStdString()); - arrowInfo->set_start_card_id(startCard->getId()); - arrowInfo->set_target_player_id(targetPlayer->getPlayerId()); - if (!playerTarget) { - arrowInfo->set_target_zone(cmd.target_zone()); - arrowInfo->set_target_card_id(cmd.target_card_id()); - } - arrowInfo->mutable_arrow_color()->CopyFrom(cmd.arrow_color()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + QMapIterator arrowIterator(arrows); + while (arrowIterator.hasNext()) { + Server_Arrow *temp = arrowIterator.next().value(); + if ((temp->getStartCard() == startCard) && (temp->getTargetItem() == targetItem)) + return Response::RespContextError; + } + + Server_Arrow *arrow = new Server_Arrow(newArrowId(), startCard, targetItem, cmd.arrow_color()); + addArrow(arrow); + + Event_CreateArrow event; + ServerInfo_Arrow *arrowInfo = event.mutable_arrow_info(); + arrowInfo->set_id(arrow->getId()); + arrowInfo->set_start_player_id(startPlayer->getPlayerId()); + arrowInfo->set_start_zone(startZoneName.toStdString()); + arrowInfo->set_start_card_id(startCard->getId()); + arrowInfo->set_target_player_id(targetPlayer->getPlayerId()); + if (!playerTarget) { + arrowInfo->set_target_zone(cmd.target_zone()); + arrowInfo->set_target_card_id(cmd.target_card_id()); + } + arrowInfo->mutable_arrow_color()->CopyFrom(cmd.arrow_color()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - if (!deleteArrow(cmd.arrow_id())) - return Response::RespNameNotFound; - - Event_DeleteArrow event; - event.set_arrow_id(cmd.arrow_id()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + if (!deleteArrow(cmd.arrow_id())) + return Response::RespNameNotFound; + + Event_DeleteArrow event; + event.set_arrow_id(cmd.arrow_id()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - return setCardAttrHelper(ges, QString::fromStdString(cmd.zone()), cmd.card_id(), cmd.attribute(), QString::fromStdString(cmd.attr_value())); + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + return setCardAttrHelper(ges, QString::fromStdString(cmd.zone()), cmd.card_id(), cmd.attribute(), QString::fromStdString(cmd.attr_value())); } Response::ResponseCode Server_Player::cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); - if (!zone) - return Response::RespNameNotFound; - if (!zone->hasCoords()) - return Response::RespContextError; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); + if (!zone) + return Response::RespNameNotFound; + if (!zone->hasCoords()) + return Response::RespContextError; - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) - return Response::RespNameNotFound; - - card->setCounter(cmd.counter_id(), cmd.counter_value()); - - Event_SetCardCounter event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_counter_id(cmd.counter_id()); - event.set_counter_value(cmd.counter_value()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + Server_Card *card = zone->getCard(cmd.card_id()); + if (!card) + return Response::RespNameNotFound; + + card->setCounter(cmd.counter_id(), cmd.counter_value()); + + Event_SetCardCounter event; + event.set_zone_name(zone->getName().toStdString()); + event.set_card_id(card->getId()); + event.set_counter_id(cmd.counter_id()); + event.set_counter_value(cmd.counter_value()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &/*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); - if (!zone) - return Response::RespNameNotFound; - if (!zone->hasCoords()) - return Response::RespContextError; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone())); + if (!zone) + return Response::RespNameNotFound; + if (!zone->hasCoords()) + return Response::RespContextError; - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) - return Response::RespNameNotFound; - - int newValue = card->getCounter(cmd.counter_id()) + cmd.counter_delta(); - card->setCounter(cmd.counter_id(), newValue); - - Event_SetCardCounter event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_counter_id(cmd.counter_id()); - event.set_counter_value(newValue); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + Server_Card *card = zone->getCard(cmd.card_id()); + if (!card) + return Response::RespNameNotFound; + + int newValue = card->getCounter(cmd.counter_id()) + cmd.counter_delta(); + card->setCounter(cmd.counter_id(), newValue); + + Event_SetCardCounter event; + event.set_zone_name(zone->getName().toStdString()); + event.set_card_id(card->getId()); + event.set_counter_id(cmd.counter_id()); + event.set_counter_value(newValue); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Counter *c = counters.value(cmd.counter_id(), 0); - if (!c) - return Response::RespNameNotFound; - - c->setCount(c->getCount() + cmd.delta()); - - Event_SetCounter event; - event.set_counter_id(c->getId()); - event.set_value(c->getCount()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Counter *c = counters.value(cmd.counter_id(), 0); + if (!c) + return Response::RespNameNotFound; + + c->setCount(c->getCount() + cmd.delta()); + + Event_SetCounter event; + event.set_counter_id(c->getId()); + event.set_value(c->getCount()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Counter *c = new Server_Counter(newCounterId(), QString::fromStdString(cmd.counter_name()), cmd.counter_color(), cmd.radius(), cmd.value()); - addCounter(c); - - Event_CreateCounter event; - ServerInfo_Counter *counterInfo = event.mutable_counter_info(); - counterInfo->set_id(c->getId()); - counterInfo->set_name(c->getName().toStdString()); - counterInfo->mutable_counter_color()->CopyFrom(cmd.counter_color()); - counterInfo->set_radius(c->getRadius()); - counterInfo->set_count(c->getCount()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Counter *c = new Server_Counter(newCounterId(), QString::fromStdString(cmd.counter_name()), cmd.counter_color(), cmd.radius(), cmd.value()); + addCounter(c); + + Event_CreateCounter event; + ServerInfo_Counter *counterInfo = event.mutable_counter_info(); + counterInfo->set_id(c->getId()); + counterInfo->set_name(c->getName().toStdString()); + counterInfo->mutable_counter_color()->CopyFrom(cmd.counter_color()); + counterInfo->set_radius(c->getRadius()); + counterInfo->set_count(c->getCount()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Counter *c = counters.value(cmd.counter_id(), 0);; - if (!c) - return Response::RespNameNotFound; - - c->setCount(cmd.value()); - - Event_SetCounter event; - event.set_counter_id(c->getId()); - event.set_value(c->getCount()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Counter *c = counters.value(cmd.counter_id(), 0);; + if (!c) + return Response::RespNameNotFound; + + c->setCount(cmd.value()); + + Event_SetCounter event; + event.set_counter_id(c->getId()); + event.set_value(c->getCount()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Counter *counter = counters.value(cmd.counter_id(), 0); - if (!counter) - return Response::RespNameNotFound; - counters.remove(cmd.counter_id()); - delete counter; - - Event_DelCounter event; - event.set_counter_id(cmd.counter_id()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Counter *counter = counters.value(cmd.counter_id(), 0); + if (!counter) + return Response::RespNameNotFound; + counters.remove(cmd.counter_id()); + delete counter; + + Event_DelCounter event; + event.set_counter_id(cmd.counter_id()); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdNextTurn(const Command_NextTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - game->nextTurn(); - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + game->nextTurn(); + return Response::RespOk; } Response::ResponseCode Server_Player::cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - if (game->getActivePlayer() != playerId) - return Response::RespContextError; - game->setActivePhase(cmd.phase()); - - return Response::RespOk; + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + if (game->getActivePlayer() != playerId) + return Response::RespContextError; + game->setActivePhase(cmd.phase()); + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges) { - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - - Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id()); - if (!otherPlayer) - return Response::RespNameNotFound; - Server_CardZone *zone = otherPlayer->getZones().value(QString::fromStdString(cmd.zone_name())); - if (!zone) - return Response::RespNameNotFound; - if (!((zone->getType() == ServerInfo_Zone::PublicZone) || (this == otherPlayer))) - return Response::RespContextError; - - int numberCards = cmd.number_cards(); - const QList &cards = zone->getCards(); - - Response_DumpZone *re = new Response_DumpZone; - ServerInfo_Zone *zoneInfo = re->mutable_zone_info(); - zoneInfo->set_name(zone->getName().toStdString()); - zoneInfo->set_type(zone->getType()); - zoneInfo->set_with_coords(zone->hasCoords()); - zoneInfo->set_card_count(numberCards < cards.size() ? cards.size() : numberCards); - - for (int i = 0; (i < cards.size()) && (i < numberCards || numberCards == -1); ++i) { - Server_Card *card = cards[i]; - QString displayedName = card->getFaceDown() ? QString() : card->getName(); - ServerInfo_Card *cardInfo = zoneInfo->add_card_list(); - cardInfo->set_name(displayedName.toStdString()); - if (zone->getType() == ServerInfo_Zone::HiddenZone) - cardInfo->set_id(i); - else { - cardInfo->set_id(card->getId()); - cardInfo->set_x(card->getX()); - cardInfo->set_y(card->getY()); - cardInfo->set_face_down(card->getFaceDown()); - cardInfo->set_tapped(card->getTapped()); - cardInfo->set_attacking(card->getAttacking()); - cardInfo->set_color(card->getColor().toStdString()); - cardInfo->set_pt(card->getPT().toStdString()); - cardInfo->set_annotation(card->getAnnotation().toStdString()); - cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - cardInfo->set_doesnt_untap(card->getDoesntUntap()); - - QMapIterator cardCounterIterator(card->getCounters()); - while (cardCounterIterator.hasNext()) { - cardCounterIterator.next(); - ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list(); - counterInfo->set_id(cardCounterIterator.key()); - counterInfo->set_value(cardCounterIterator.value()); - } + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + + Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id()); + if (!otherPlayer) + return Response::RespNameNotFound; + Server_CardZone *zone = otherPlayer->getZones().value(QString::fromStdString(cmd.zone_name())); + if (!zone) + return Response::RespNameNotFound; + if (!((zone->getType() == ServerInfo_Zone::PublicZone) || (this == otherPlayer))) + return Response::RespContextError; + + int numberCards = cmd.number_cards(); + const QList &cards = zone->getCards(); + + Response_DumpZone *re = new Response_DumpZone; + ServerInfo_Zone *zoneInfo = re->mutable_zone_info(); + zoneInfo->set_name(zone->getName().toStdString()); + zoneInfo->set_type(zone->getType()); + zoneInfo->set_with_coords(zone->hasCoords()); + zoneInfo->set_card_count(numberCards < cards.size() ? cards.size() : numberCards); + + for (int i = 0; (i < cards.size()) && (i < numberCards || numberCards == -1); ++i) { + Server_Card *card = cards[i]; + QString displayedName = card->getFaceDown() ? QString() : card->getName(); + ServerInfo_Card *cardInfo = zoneInfo->add_card_list(); + cardInfo->set_name(displayedName.toStdString()); + if (zone->getType() == ServerInfo_Zone::HiddenZone) + cardInfo->set_id(i); + else { + cardInfo->set_id(card->getId()); + cardInfo->set_x(card->getX()); + cardInfo->set_y(card->getY()); + cardInfo->set_face_down(card->getFaceDown()); + cardInfo->set_tapped(card->getTapped()); + cardInfo->set_attacking(card->getAttacking()); + cardInfo->set_color(card->getColor().toStdString()); + cardInfo->set_pt(card->getPT().toStdString()); + cardInfo->set_annotation(card->getAnnotation().toStdString()); + cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange()); + cardInfo->set_doesnt_untap(card->getDoesntUntap()); + + QMapIterator cardCounterIterator(card->getCounters()); + while (cardCounterIterator.hasNext()) { + cardCounterIterator.next(); + ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list(); + counterInfo->set_id(cardCounterIterator.key()); + counterInfo->set_value(cardCounterIterator.value()); + } - if (card->getParentCard()) { - cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId()); - cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString()); - cardInfo->set_attach_card_id(card->getParentCard()->getId()); - } - } - } - if (zone->getType() == ServerInfo_Zone::HiddenZone) { - zone->setCardsBeingLookedAt(numberCards); - - Event_DumpZone event; - event.set_zone_owner_id(otherPlayer->getPlayerId()); - event.set_zone_name(zone->getName().toStdString()); - event.set_number_cards(numberCards); - ges.enqueueGameEvent(event, playerId); - } - rc.setResponseExtension(re); - return Response::RespOk; + if (card->getParentCard()) { + cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId()); + cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString()); + cardInfo->set_attach_card_id(card->getParentCard()->getId()); + } + } + } + if (zone->getType() == ServerInfo_Zone::HiddenZone) { + zone->setCardsBeingLookedAt(numberCards); + + Event_DumpZone event; + event.set_zone_owner_id(otherPlayer->getPlayerId()); + event.set_zone_name(zone->getName().toStdString()); + event.set_number_cards(numberCards); + ges.enqueueGameEvent(event, playerId); + } + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_Player::cmdStopDumpZone(const Command_StopDumpZone &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id()); - if (!otherPlayer) - return Response::RespNameNotFound; - Server_CardZone *zone = otherPlayer->getZones().value(QString::fromStdString(cmd.zone_name())); - if (!zone) - return Response::RespNameNotFound; - - if (zone->getType() == ServerInfo_Zone::HiddenZone) { - zone->setCardsBeingLookedAt(0); - - Event_StopDumpZone event; - event.set_zone_owner_id(cmd.player_id()); - event.set_zone_name(zone->getName().toStdString()); - ges.enqueueGameEvent(event, playerId); - } - return Response::RespOk; + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Player *otherPlayer = game->getPlayers().value(cmd.player_id()); + if (!otherPlayer) + return Response::RespNameNotFound; + Server_CardZone *zone = otherPlayer->getZones().value(QString::fromStdString(cmd.zone_name())); + if (!zone) + return Response::RespNameNotFound; + + if (zone->getType() == ServerInfo_Zone::HiddenZone) { + zone->setCardsBeingLookedAt(0); + + Event_StopDumpZone event; + event.set_zone_owner_id(cmd.player_id()); + event.set_zone_name(zone->getName().toStdString()); + ges.enqueueGameEvent(event, playerId); + } + return Response::RespOk; } Response::ResponseCode Server_Player::cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) { - if (spectator) - return Response::RespFunctionNotAllowed; - - if (!game->getGameStarted()) - return Response::RespGameNotStarted; - if (conceded) - return Response::RespContextError; - - Server_Player *otherPlayer = 0; - if (cmd.has_player_id()) { - otherPlayer = game->getPlayers().value(cmd.player_id()); - if (!otherPlayer) - return Response::RespNameNotFound; - } - Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name())); - if (!zone) - return Response::RespNameNotFound; - - QList cardsToReveal; - if (!cmd.has_card_id()) - cardsToReveal = zone->getCards(); - else if (cmd.card_id() == -2) { - if (zone->getCards().isEmpty()) - return Response::RespContextError; - cardsToReveal.append(zone->getCards().at(rng->rand(0, zone->getCards().size() - 1))); - } else { - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) - return Response::RespNameNotFound; - cardsToReveal.append(card); - } - - Event_RevealCards eventOthers; - eventOthers.set_grant_write_access(cmd.grant_write_access()); - eventOthers.set_zone_name(zone->getName().toStdString()); - if (cmd.has_card_id()) - eventOthers.set_card_id(cmd.card_id()); - if (cmd.has_player_id()) - eventOthers.set_other_player_id(cmd.player_id()); - - Event_RevealCards eventPrivate(eventOthers); - - for (int i = 0; i < cardsToReveal.size(); ++i) { - Server_Card *card = cardsToReveal[i]; - ServerInfo_Card *cardInfo = eventPrivate.add_cards(); + if (spectator) + return Response::RespFunctionNotAllowed; + + if (!game->getGameStarted()) + return Response::RespGameNotStarted; + if (conceded) + return Response::RespContextError; + + Server_Player *otherPlayer = 0; + if (cmd.has_player_id()) { + otherPlayer = game->getPlayers().value(cmd.player_id()); + if (!otherPlayer) + return Response::RespNameNotFound; + } + Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name())); + if (!zone) + return Response::RespNameNotFound; + + QList cardsToReveal; + if (!cmd.has_card_id()) + cardsToReveal = zone->getCards(); + else if (cmd.card_id() == -2) { + if (zone->getCards().isEmpty()) + return Response::RespContextError; + cardsToReveal.append(zone->getCards().at(rng->rand(0, zone->getCards().size() - 1))); + } else { + Server_Card *card = zone->getCard(cmd.card_id()); + if (!card) + return Response::RespNameNotFound; + cardsToReveal.append(card); + } + + Event_RevealCards eventOthers; + eventOthers.set_grant_write_access(cmd.grant_write_access()); + eventOthers.set_zone_name(zone->getName().toStdString()); + if (cmd.has_card_id()) + eventOthers.set_card_id(cmd.card_id()); + if (cmd.has_player_id()) + eventOthers.set_other_player_id(cmd.player_id()); + + Event_RevealCards eventPrivate(eventOthers); + + for (int i = 0; i < cardsToReveal.size(); ++i) { + Server_Card *card = cardsToReveal[i]; + ServerInfo_Card *cardInfo = eventPrivate.add_cards(); - cardInfo->set_id(card->getId()); - cardInfo->set_name(card->getName().toStdString()); - cardInfo->set_x(card->getX()); - cardInfo->set_y(card->getY()); - cardInfo->set_face_down(card->getFaceDown()); - cardInfo->set_tapped(card->getTapped()); - cardInfo->set_attacking(card->getAttacking()); - cardInfo->set_color(card->getColor().toStdString()); - cardInfo->set_pt(card->getPT().toStdString()); - cardInfo->set_annotation(card->getAnnotation().toStdString()); - cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - cardInfo->set_doesnt_untap(card->getDoesntUntap()); - - QMapIterator cardCounterIterator(card->getCounters()); - while (cardCounterIterator.hasNext()) { - cardCounterIterator.next(); - ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list(); - counterInfo->set_id(cardCounterIterator.key()); - counterInfo->set_value(cardCounterIterator.value()); - } - - if (card->getParentCard()) { - cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId()); - cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString()); - cardInfo->set_attach_card_id(card->getParentCard()->getId()); - } - } - - if (cmd.has_player_id()) { - if (cmd.grant_write_access()) - zone->addWritePermission(cmd.player_id()); - - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, cmd.player_id()); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - } else { - if (cmd.grant_write_access()) { - const QList &playerIds = game->getPlayers().keys(); - for (int i = 0; i < playerIds.size(); ++i) - zone->addWritePermission(playerIds[i]); - } - - ges.enqueueGameEvent(eventPrivate, playerId); - } - - return Response::RespOk; + cardInfo->set_id(card->getId()); + cardInfo->set_name(card->getName().toStdString()); + cardInfo->set_x(card->getX()); + cardInfo->set_y(card->getY()); + cardInfo->set_face_down(card->getFaceDown()); + cardInfo->set_tapped(card->getTapped()); + cardInfo->set_attacking(card->getAttacking()); + cardInfo->set_color(card->getColor().toStdString()); + cardInfo->set_pt(card->getPT().toStdString()); + cardInfo->set_annotation(card->getAnnotation().toStdString()); + cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange()); + cardInfo->set_doesnt_untap(card->getDoesntUntap()); + + QMapIterator cardCounterIterator(card->getCounters()); + while (cardCounterIterator.hasNext()) { + cardCounterIterator.next(); + ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list(); + counterInfo->set_id(cardCounterIterator.key()); + counterInfo->set_value(cardCounterIterator.value()); + } + + if (card->getParentCard()) { + cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId()); + cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString()); + cardInfo->set_attach_card_id(card->getParentCard()->getId()); + } + } + + if (cmd.has_player_id()) { + if (cmd.grant_write_access()) + zone->addWritePermission(cmd.player_id()); + + ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, cmd.player_id()); + ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); + } else { + if (cmd.grant_write_access()) { + const QList &playerIds = game->getPlayers().keys(); + for (int i = 0; i < playerIds.size(); ++i) + zone->addWritePermission(playerIds[i]); + } + + ges.enqueueGameEvent(eventPrivate, playerId); + } + + return Response::RespOk; } Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges) { - Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name())); - if (!zone) - return Response::RespNameNotFound; - - Event_ChangeZoneProperties event; - event.set_zone_name(cmd.zone_name()); - - if (cmd.has_always_reveal_top_card()) { - if (zone->getAlwaysRevealTopCard() == cmd.always_reveal_top_card()) - return Response::RespContextError; - zone->setAlwaysRevealTopCard(cmd.always_reveal_top_card()); - event.set_always_reveal_top_card(cmd.always_reveal_top_card()); - - ges.enqueueGameEvent(event, playerId); - - if (!zone->getCards().isEmpty() && cmd.always_reveal_top_card()) { - Event_RevealCards revealEvent; - revealEvent.set_zone_name(zone->getName().toStdString()); - revealEvent.set_card_id(0); - zone->getCards().first()->getInfo(revealEvent.add_cards()); - - ges.enqueueGameEvent(revealEvent, playerId); - } - return Response::RespOk; - } else - return Response::RespContextError; + Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name())); + if (!zone) + return Response::RespNameNotFound; + + Event_ChangeZoneProperties event; + event.set_zone_name(cmd.zone_name()); + + if (cmd.has_always_reveal_top_card()) { + if (zone->getAlwaysRevealTopCard() == cmd.always_reveal_top_card()) + return Response::RespContextError; + zone->setAlwaysRevealTopCard(cmd.always_reveal_top_card()); + event.set_always_reveal_top_card(cmd.always_reveal_top_card()); + + ges.enqueueGameEvent(event, playerId); + + if (!zone->getCards().isEmpty() && cmd.always_reveal_top_card()) { + Event_RevealCards revealEvent; + revealEvent.set_zone_name(zone->getName().toStdString()); + revealEvent.set_card_id(0); + zone->getCards().first()->getInfo(revealEvent.add_cards()); + + ges.enqueueGameEvent(revealEvent, playerId); + } + return Response::RespOk; + } else + return Response::RespContextError; } Response::ResponseCode Server_Player::processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges) { - switch ((GameCommand::GameCommandType) getPbExtension(command)) { - case GameCommand::KICK_FROM_GAME: return cmdKickFromGame(command.GetExtension(Command_KickFromGame::ext), rc, ges); break; - case GameCommand::LEAVE_GAME: return cmdLeaveGame(command.GetExtension(Command_LeaveGame::ext), rc, ges); break; - case GameCommand::GAME_SAY: return cmdGameSay(command.GetExtension(Command_GameSay::ext), rc, ges); break; - case GameCommand::SHUFFLE: return cmdShuffle(command.GetExtension(Command_Shuffle::ext), rc, ges); break; - case GameCommand::MULLIGAN: return cmdMulligan(command.GetExtension(Command_Mulligan::ext), rc, ges); break; - case GameCommand::ROLL_DIE: return cmdRollDie(command.GetExtension(Command_RollDie::ext), rc, ges); break; - case GameCommand::DRAW_CARDS: return cmdDrawCards(command.GetExtension(Command_DrawCards::ext), rc, ges); break; - case GameCommand::UNDO_DRAW: return cmdUndoDraw(command.GetExtension(Command_UndoDraw::ext), rc, ges); break; - case GameCommand::FLIP_CARD: return cmdFlipCard(command.GetExtension(Command_FlipCard::ext), rc, ges); break; - case GameCommand::ATTACH_CARD: return cmdAttachCard(command.GetExtension(Command_AttachCard::ext), rc, ges); break; - case GameCommand::CREATE_TOKEN: return cmdCreateToken(command.GetExtension(Command_CreateToken::ext), rc, ges); break; - case GameCommand::CREATE_ARROW: return cmdCreateArrow(command.GetExtension(Command_CreateArrow::ext), rc, ges); break; - case GameCommand::DELETE_ARROW: return cmdDeleteArrow(command.GetExtension(Command_DeleteArrow::ext), rc, ges); break; - case GameCommand::SET_CARD_ATTR: return cmdSetCardAttr(command.GetExtension(Command_SetCardAttr::ext), rc, ges); break; - case GameCommand::SET_CARD_COUNTER: return cmdSetCardCounter(command.GetExtension(Command_SetCardCounter::ext), rc, ges); break; - case GameCommand::INC_CARD_COUNTER: return cmdIncCardCounter(command.GetExtension(Command_IncCardCounter::ext), rc, ges); break; - case GameCommand::READY_START: return cmdReadyStart(command.GetExtension(Command_ReadyStart::ext), rc, ges); break; - case GameCommand::CONCEDE: return cmdConcede(command.GetExtension(Command_Concede::ext), rc, ges); break; - case GameCommand::INC_COUNTER: return cmdIncCounter(command.GetExtension(Command_IncCounter::ext), rc, ges); break; - case GameCommand::CREATE_COUNTER: return cmdCreateCounter(command.GetExtension(Command_CreateCounter::ext), rc, ges); break; - case GameCommand::SET_COUNTER: return cmdSetCounter(command.GetExtension(Command_SetCounter::ext), rc, ges); break; - case GameCommand::DEL_COUNTER: return cmdDelCounter(command.GetExtension(Command_DelCounter::ext), rc, ges); break; - case GameCommand::NEXT_TURN: return cmdNextTurn(command.GetExtension(Command_NextTurn::ext), rc, ges); break; - case GameCommand::SET_ACTIVE_PHASE: return cmdSetActivePhase(command.GetExtension(Command_SetActivePhase::ext), rc, ges); break; - case GameCommand::DUMP_ZONE: return cmdDumpZone(command.GetExtension(Command_DumpZone::ext), rc, ges); break; - case GameCommand::STOP_DUMP_ZONE: return cmdStopDumpZone(command.GetExtension(Command_StopDumpZone::ext), rc, ges); break; - case GameCommand::REVEAL_CARDS: return cmdRevealCards(command.GetExtension(Command_RevealCards::ext), rc, ges); break; - case GameCommand::MOVE_CARD: return cmdMoveCard(command.GetExtension(Command_MoveCard::ext), rc, ges); break; - case GameCommand::SET_SIDEBOARD_PLAN: return cmdSetSideboardPlan(command.GetExtension(Command_SetSideboardPlan::ext), rc, ges); break; - case GameCommand::DECK_SELECT: return cmdDeckSelect(command.GetExtension(Command_DeckSelect::ext), rc, ges); break; - case GameCommand::SET_SIDEBOARD_LOCK: return cmdSetSideboardLock(command.GetExtension(Command_SetSideboardLock::ext), rc, ges); break; - case GameCommand::CHANGE_ZONE_PROPERTIES: return cmdChangeZoneProperties(command.GetExtension(Command_ChangeZoneProperties::ext), rc, ges); break; - default: return Response::RespInvalidCommand; - } + switch ((GameCommand::GameCommandType) getPbExtension(command)) { + case GameCommand::KICK_FROM_GAME: return cmdKickFromGame(command.GetExtension(Command_KickFromGame::ext), rc, ges); break; + case GameCommand::LEAVE_GAME: return cmdLeaveGame(command.GetExtension(Command_LeaveGame::ext), rc, ges); break; + case GameCommand::GAME_SAY: return cmdGameSay(command.GetExtension(Command_GameSay::ext), rc, ges); break; + case GameCommand::SHUFFLE: return cmdShuffle(command.GetExtension(Command_Shuffle::ext), rc, ges); break; + case GameCommand::MULLIGAN: return cmdMulligan(command.GetExtension(Command_Mulligan::ext), rc, ges); break; + case GameCommand::ROLL_DIE: return cmdRollDie(command.GetExtension(Command_RollDie::ext), rc, ges); break; + case GameCommand::DRAW_CARDS: return cmdDrawCards(command.GetExtension(Command_DrawCards::ext), rc, ges); break; + case GameCommand::UNDO_DRAW: return cmdUndoDraw(command.GetExtension(Command_UndoDraw::ext), rc, ges); break; + case GameCommand::FLIP_CARD: return cmdFlipCard(command.GetExtension(Command_FlipCard::ext), rc, ges); break; + case GameCommand::ATTACH_CARD: return cmdAttachCard(command.GetExtension(Command_AttachCard::ext), rc, ges); break; + case GameCommand::CREATE_TOKEN: return cmdCreateToken(command.GetExtension(Command_CreateToken::ext), rc, ges); break; + case GameCommand::CREATE_ARROW: return cmdCreateArrow(command.GetExtension(Command_CreateArrow::ext), rc, ges); break; + case GameCommand::DELETE_ARROW: return cmdDeleteArrow(command.GetExtension(Command_DeleteArrow::ext), rc, ges); break; + case GameCommand::SET_CARD_ATTR: return cmdSetCardAttr(command.GetExtension(Command_SetCardAttr::ext), rc, ges); break; + case GameCommand::SET_CARD_COUNTER: return cmdSetCardCounter(command.GetExtension(Command_SetCardCounter::ext), rc, ges); break; + case GameCommand::INC_CARD_COUNTER: return cmdIncCardCounter(command.GetExtension(Command_IncCardCounter::ext), rc, ges); break; + case GameCommand::READY_START: return cmdReadyStart(command.GetExtension(Command_ReadyStart::ext), rc, ges); break; + case GameCommand::CONCEDE: return cmdConcede(command.GetExtension(Command_Concede::ext), rc, ges); break; + case GameCommand::INC_COUNTER: return cmdIncCounter(command.GetExtension(Command_IncCounter::ext), rc, ges); break; + case GameCommand::CREATE_COUNTER: return cmdCreateCounter(command.GetExtension(Command_CreateCounter::ext), rc, ges); break; + case GameCommand::SET_COUNTER: return cmdSetCounter(command.GetExtension(Command_SetCounter::ext), rc, ges); break; + case GameCommand::DEL_COUNTER: return cmdDelCounter(command.GetExtension(Command_DelCounter::ext), rc, ges); break; + case GameCommand::NEXT_TURN: return cmdNextTurn(command.GetExtension(Command_NextTurn::ext), rc, ges); break; + case GameCommand::SET_ACTIVE_PHASE: return cmdSetActivePhase(command.GetExtension(Command_SetActivePhase::ext), rc, ges); break; + case GameCommand::DUMP_ZONE: return cmdDumpZone(command.GetExtension(Command_DumpZone::ext), rc, ges); break; + case GameCommand::STOP_DUMP_ZONE: return cmdStopDumpZone(command.GetExtension(Command_StopDumpZone::ext), rc, ges); break; + case GameCommand::REVEAL_CARDS: return cmdRevealCards(command.GetExtension(Command_RevealCards::ext), rc, ges); break; + case GameCommand::MOVE_CARD: return cmdMoveCard(command.GetExtension(Command_MoveCard::ext), rc, ges); break; + case GameCommand::SET_SIDEBOARD_PLAN: return cmdSetSideboardPlan(command.GetExtension(Command_SetSideboardPlan::ext), rc, ges); break; + case GameCommand::DECK_SELECT: return cmdDeckSelect(command.GetExtension(Command_DeckSelect::ext), rc, ges); break; + case GameCommand::SET_SIDEBOARD_LOCK: return cmdSetSideboardLock(command.GetExtension(Command_SetSideboardLock::ext), rc, ges); break; + case GameCommand::CHANGE_ZONE_PROPERTIES: return cmdChangeZoneProperties(command.GetExtension(Command_ChangeZoneProperties::ext), rc, ges); break; + default: return Response::RespInvalidCommand; + } } void Server_Player::sendGameEvent(const GameEventContainer &cont) { - QMutexLocker locker(&playerMutex); - - if (userInterface) - userInterface->sendProtocolItem(cont); + QMutexLocker locker(&playerMutex); + + if (userInterface) + userInterface->sendProtocolItem(cont); } void Server_Player::setUserInterface(Server_AbstractUserInterface *_userInterface) { - playerMutex.lock(); - userInterface = _userInterface; - playerMutex.unlock(); - - pingTime = _userInterface ? 0 : -1; - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_ping_seconds(pingTime); - - GameEventStorage ges; - ges.setGameEventContext(Context_ConnectionStateChanged()); - ges.enqueueGameEvent(event, playerId); - ges.sendToGame(game); + playerMutex.lock(); + userInterface = _userInterface; + playerMutex.unlock(); + + pingTime = _userInterface ? 0 : -1; + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_ping_seconds(pingTime); + + GameEventStorage ges; + ges.setGameEventContext(Context_ConnectionStateChanged()); + ges.enqueueGameEvent(event, playerId); + ges.sendToGame(game); } void Server_Player::disconnectClient() { - if (!(userInfo->user_level() & ServerInfo_User::IsRegistered) || spectator) - game->removePlayer(this); - else - setUserInterface(0); + if (!(userInfo->user_level() & ServerInfo_User::IsRegistered) || spectator) + game->removePlayer(this); + else + setUserInterface(0); } void Server_Player::getInfo(ServerInfo_Player *info, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo) { - getProperties(*info->mutable_properties(), withUserInfo); - if (playerWhosAsking == this) - if (deck) - info->set_deck_list(deck->writeToString_Native().toStdString()); - - QMapIterator arrowIterator(arrows); - while (arrowIterator.hasNext()) - arrowIterator.next().value()->getInfo(info->add_arrow_list()); - - QMapIterator counterIterator(counters); - while (counterIterator.hasNext()) - counterIterator.next().value()->getInfo(info->add_counter_list()); - - QMapIterator zoneIterator(zones); - while (zoneIterator.hasNext()) - zoneIterator.next().value()->getInfo(info->add_zone_list(), playerWhosAsking, omniscient); + getProperties(*info->mutable_properties(), withUserInfo); + if (playerWhosAsking == this) + if (deck) + info->set_deck_list(deck->writeToString_Native().toStdString()); + + QMapIterator arrowIterator(arrows); + while (arrowIterator.hasNext()) + arrowIterator.next().value()->getInfo(info->add_arrow_list()); + + QMapIterator counterIterator(counters); + while (counterIterator.hasNext()) + counterIterator.next().value()->getInfo(info->add_counter_list()); + + QMapIterator zoneIterator(zones); + while (zoneIterator.hasNext()) + zoneIterator.next().value()->getInfo(info->add_zone_list(), playerWhosAsking, omniscient); } diff --git a/common/server_player.h b/common/server_player.h index 61e29bab4..873a4a761 100644 --- a/common/server_player.h +++ b/common/server_player.h @@ -62,104 +62,104 @@ class Command_SetSideboardLock; class Command_ChangeZoneProperties; class Server_Player : public Server_ArrowTarget, public ServerInfo_User_Container { - Q_OBJECT + Q_OBJECT private: - class MoveCardCompareFunctor; - Server_Game *game; - Server_AbstractUserInterface *userInterface; - DeckList *deck; - QMap zones; - QMap counters; - QMap arrows; - QList lastDrawList; - int pingTime; - int playerId; - bool spectator; - int initialCards; - int nextCardId; - bool readyStart; - bool conceded; - bool sideboardLocked; + class MoveCardCompareFunctor; + Server_Game *game; + Server_AbstractUserInterface *userInterface; + DeckList *deck; + QMap zones; + QMap counters; + QMap arrows; + QList lastDrawList; + int pingTime; + int playerId; + bool spectator; + int initialCards; + int nextCardId; + bool readyStart; + bool conceded; + bool sideboardLocked; public: - mutable QMutex playerMutex; - Server_Player(Server_Game *_game, int _playerId, const ServerInfo_User &_userInfo, bool _spectator, Server_AbstractUserInterface *_handler); - ~Server_Player(); - void prepareDestroy(); - Server_AbstractUserInterface *getUserInterface() const { return userInterface; } - void setUserInterface(Server_AbstractUserInterface *_userInterface); - void disconnectClient(); - - void setPlayerId(int _id) { playerId = _id; } - bool getReadyStart() const { return readyStart; } - void setReadyStart(bool _readyStart) { readyStart = _readyStart; } - int getPlayerId() const { return playerId; } - bool getSpectator() const { return spectator; } - bool getConceded() const { return conceded; } - void setConceded(bool _conceded) { conceded = _conceded; } - DeckList *getDeck() const { return deck; } - Server_Game *getGame() const { return game; } - const QMap &getZones() const { return zones; } - const QMap &getCounters() const { return counters; } - const QMap &getArrows() const { return arrows; } - - int getPingTime() const { return pingTime; } - void setPingTime(int _pingTime) { pingTime = _pingTime; } - void getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo); - - int newCardId(); - int newCounterId() const; - int newArrowId() const; - - void addZone(Server_CardZone *zone); - void addArrow(Server_Arrow *arrow); - bool deleteArrow(int arrowId); - void addCounter(Server_Counter *counter); - - void clearZones(); - void setupZones(); + mutable QMutex playerMutex; + Server_Player(Server_Game *_game, int _playerId, const ServerInfo_User &_userInfo, bool _spectator, Server_AbstractUserInterface *_handler); + ~Server_Player(); + void prepareDestroy(); + Server_AbstractUserInterface *getUserInterface() const { return userInterface; } + void setUserInterface(Server_AbstractUserInterface *_userInterface); + void disconnectClient(); + + void setPlayerId(int _id) { playerId = _id; } + bool getReadyStart() const { return readyStart; } + void setReadyStart(bool _readyStart) { readyStart = _readyStart; } + int getPlayerId() const { return playerId; } + bool getSpectator() const { return spectator; } + bool getConceded() const { return conceded; } + void setConceded(bool _conceded) { conceded = _conceded; } + DeckList *getDeck() const { return deck; } + Server_Game *getGame() const { return game; } + const QMap &getZones() const { return zones; } + const QMap &getCounters() const { return counters; } + const QMap &getArrows() const { return arrows; } + + int getPingTime() const { return pingTime; } + void setPingTime(int _pingTime) { pingTime = _pingTime; } + void getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo); + + int newCardId(); + int newCounterId() const; + int newArrowId() const; + + void addZone(Server_CardZone *zone); + void addArrow(Server_Arrow *arrow); + bool deleteArrow(int arrowId); + void addCounter(Server_Counter *counter); + + void clearZones(); + void setupZones(); - Response::ResponseCode drawCards(GameEventStorage &ges, int number); - Response::ResponseCode moveCard(GameEventStorage &ges, Server_CardZone *startzone, const QList &_cards, Server_CardZone *targetzone, int x, int y, bool fixFreeSpaces = true, bool undoingDraw = false); - void unattachCard(GameEventStorage &ges, Server_Card *card); - Response::ResponseCode setCardAttrHelper(GameEventStorage &ges, const QString &zone, int cardId, CardAttribute attribute, const QString &attrValue); + Response::ResponseCode drawCards(GameEventStorage &ges, int number); + Response::ResponseCode moveCard(GameEventStorage &ges, Server_CardZone *startzone, const QList &_cards, Server_CardZone *targetzone, int x, int y, bool fixFreeSpaces = true, bool undoingDraw = false); + void unattachCard(GameEventStorage &ges, Server_Card *card); + Response::ResponseCode setCardAttrHelper(GameEventStorage &ges, const QString &zone, int cardId, CardAttribute attribute, const QString &attrValue); - Response::ResponseCode cmdLeaveGame(const Command_LeaveGame &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdGameSay(const Command_GameSay &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdMulligan(const Command_Mulligan &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdRollDie(const Command_RollDie &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdUndoDraw(const Command_UndoDraw &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdStopDumpZone(const Command_StopDumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer &rc, GameEventStorage &ges); - Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges); - - Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges); - void sendGameEvent(const GameEventContainer &event); - - void getInfo(ServerInfo_Player *info, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo); + Response::ResponseCode cmdLeaveGame(const Command_LeaveGame &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdGameSay(const Command_GameSay &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdMulligan(const Command_Mulligan &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdRollDie(const Command_RollDie &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdUndoDraw(const Command_UndoDraw &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdStopDumpZone(const Command_StopDumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer &rc, GameEventStorage &ges); + Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges); + + Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges); + void sendGameEvent(const GameEventContainer &event); + + void getInfo(ServerInfo_Player *info, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo); }; #endif diff --git a/common/server_player_reference.h b/common/server_player_reference.h index fc784a1a5..868b40b6a 100644 --- a/common/server_player_reference.h +++ b/common/server_player_reference.h @@ -3,15 +3,15 @@ class PlayerReference { private: - int roomId; - int gameId; - int playerId; + int roomId; + int gameId; + int playerId; public: - PlayerReference(int _roomId, int _gameId, int _playerId) : roomId(_roomId), gameId(_gameId), playerId(_playerId) { } - int getRoomId() const { return roomId; } - int getGameId() const { return gameId; } - int getPlayerId() const { return playerId; } - bool operator==(const PlayerReference &other) { return ((roomId == other.roomId) && (gameId == other.gameId) && (playerId == other.playerId)); } + PlayerReference(int _roomId, int _gameId, int _playerId) : roomId(_roomId), gameId(_gameId), playerId(_playerId) { } + int getRoomId() const { return roomId; } + int getGameId() const { return gameId; } + int getPlayerId() const { return playerId; } + bool operator==(const PlayerReference &other) { return ((roomId == other.roomId) && (gameId == other.gameId) && (playerId == other.playerId)); } }; #endif diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index eb8322d06..f7561b2a2 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -21,17 +21,17 @@ #include Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, Server_DatabaseInterface *_databaseInterface, QObject *parent) - : QObject(parent), - Server_AbstractUserInterface(_server), - deleted(false), - databaseInterface(_databaseInterface), - authState(NotLoggedIn), - acceptsUserListChanges(false), - acceptsRoomListChanges(false), - timeRunning(0), - lastDataReceived(0) + : QObject(parent), + Server_AbstractUserInterface(_server), + deleted(false), + databaseInterface(_databaseInterface), + authState(NotLoggedIn), + acceptsUserListChanges(false), + acceptsRoomListChanges(false), + timeRunning(0), + lastDataReceived(0) { - connect(server, SIGNAL(pingClockTimeout()), this, SLOT(pingClockTimeout())); + connect(server, SIGNAL(pingClockTimeout()), this, SLOT(pingClockTimeout())); } Server_ProtocolHandler::~Server_ProtocolHandler() @@ -42,540 +42,540 @@ Server_ProtocolHandler::~Server_ProtocolHandler() // The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock). void Server_ProtocolHandler::prepareDestroy() { - if (deleted) - return; - deleted = true; - - QMapIterator roomIterator(rooms); - while (roomIterator.hasNext()) - roomIterator.next().value()->removeClient(this); - - QMap > tempGames(getGames()); - - server->roomsLock.lockForRead(); - QMapIterator > gameIterator(tempGames); - while (gameIterator.hasNext()) { - gameIterator.next(); - - Server_Room *r = server->getRooms().value(gameIterator.value().first); - if (!r) - continue; - r->gamesLock.lockForRead(); - Server_Game *g = r->getGames().value(gameIterator.key()); - if (!g) { - r->gamesLock.unlock(); - continue; - } - g->gameMutex.lock(); - Server_Player *p = g->getPlayers().value(gameIterator.value().second); - if (!p) { - g->gameMutex.unlock(); - r->gamesLock.unlock(); - continue; - } - - p->disconnectClient(); - - g->gameMutex.unlock(); - r->gamesLock.unlock(); - } - server->roomsLock.unlock(); - - server->removeClient(this); - - deleteLater(); + if (deleted) + return; + deleted = true; + + QMapIterator roomIterator(rooms); + while (roomIterator.hasNext()) + roomIterator.next().value()->removeClient(this); + + QMap > tempGames(getGames()); + + server->roomsLock.lockForRead(); + QMapIterator > gameIterator(tempGames); + while (gameIterator.hasNext()) { + gameIterator.next(); + + Server_Room *r = server->getRooms().value(gameIterator.value().first); + if (!r) + continue; + r->gamesLock.lockForRead(); + Server_Game *g = r->getGames().value(gameIterator.key()); + if (!g) { + r->gamesLock.unlock(); + continue; + } + g->gameMutex.lock(); + Server_Player *p = g->getPlayers().value(gameIterator.value().second); + if (!p) { + g->gameMutex.unlock(); + r->gamesLock.unlock(); + continue; + } + + p->disconnectClient(); + + g->gameMutex.unlock(); + r->gamesLock.unlock(); + } + server->roomsLock.unlock(); + + server->removeClient(this); + + deleteLater(); } void Server_ProtocolHandler::sendProtocolItem(const Response &item) { - ServerMessage msg; - msg.mutable_response()->CopyFrom(item); - msg.set_message_type(ServerMessage::RESPONSE); - - transmitProtocolItem(msg); + ServerMessage msg; + msg.mutable_response()->CopyFrom(item); + msg.set_message_type(ServerMessage::RESPONSE); + + transmitProtocolItem(msg); } void Server_ProtocolHandler::sendProtocolItem(const SessionEvent &item) { - ServerMessage msg; - msg.mutable_session_event()->CopyFrom(item); - msg.set_message_type(ServerMessage::SESSION_EVENT); - - transmitProtocolItem(msg); + ServerMessage msg; + msg.mutable_session_event()->CopyFrom(item); + msg.set_message_type(ServerMessage::SESSION_EVENT); + + transmitProtocolItem(msg); } void Server_ProtocolHandler::sendProtocolItem(const GameEventContainer &item) { - ServerMessage msg; - msg.mutable_game_event_container()->CopyFrom(item); - msg.set_message_type(ServerMessage::GAME_EVENT_CONTAINER); - - transmitProtocolItem(msg); + ServerMessage msg; + msg.mutable_game_event_container()->CopyFrom(item); + msg.set_message_type(ServerMessage::GAME_EVENT_CONTAINER); + + transmitProtocolItem(msg); } void Server_ProtocolHandler::sendProtocolItem(const RoomEvent &item) { - ServerMessage msg; - msg.mutable_room_event()->CopyFrom(item); - msg.set_message_type(ServerMessage::ROOM_EVENT); - - transmitProtocolItem(msg); + ServerMessage msg; + msg.mutable_room_event()->CopyFrom(item); + msg.set_message_type(ServerMessage::ROOM_EVENT); + + transmitProtocolItem(msg); } Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { - Response::ResponseCode finalResponseCode = Response::RespOk; - for (int i = cont.session_command_size() - 1; i >= 0; --i) { - Response::ResponseCode resp = Response::RespInvalidCommand; - const SessionCommand &sc = cont.session_command(i); - const int num = getPbExtension(sc); - if (num != SessionCommand::PING) { // don't log ping commands - if (num == SessionCommand::LOGIN) { // log login commands, but hide passwords - SessionCommand debugSc(sc); - debugSc.MutableExtension(Command_Login::ext)->clear_password(); - logDebugMessage(QString::fromStdString(debugSc.ShortDebugString())); - } else - logDebugMessage(QString::fromStdString(sc.ShortDebugString())); - } - switch ((SessionCommand::SessionCommandType) num) { - case SessionCommand::PING: resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); break; - case SessionCommand::LOGIN: resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); break; - case SessionCommand::MESSAGE: resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); break; - case SessionCommand::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); break; - case SessionCommand::GET_USER_INFO: resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); break; - case SessionCommand::LIST_ROOMS: resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc); break; - case SessionCommand::JOIN_ROOM: resp = cmdJoinRoom(sc.GetExtension(Command_JoinRoom::ext), rc); break; - case SessionCommand::LIST_USERS: resp = cmdListUsers(sc.GetExtension(Command_ListUsers::ext), rc); break; - default: resp = processExtendedSessionCommand(num, sc, rc); - } - if (resp != Response::RespOk) - finalResponseCode = resp; - } - return finalResponseCode; + Response::ResponseCode finalResponseCode = Response::RespOk; + for (int i = cont.session_command_size() - 1; i >= 0; --i) { + Response::ResponseCode resp = Response::RespInvalidCommand; + const SessionCommand &sc = cont.session_command(i); + const int num = getPbExtension(sc); + if (num != SessionCommand::PING) { // don't log ping commands + if (num == SessionCommand::LOGIN) { // log login commands, but hide passwords + SessionCommand debugSc(sc); + debugSc.MutableExtension(Command_Login::ext)->clear_password(); + logDebugMessage(QString::fromStdString(debugSc.ShortDebugString())); + } else + logDebugMessage(QString::fromStdString(sc.ShortDebugString())); + } + switch ((SessionCommand::SessionCommandType) num) { + case SessionCommand::PING: resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); break; + case SessionCommand::LOGIN: resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); break; + case SessionCommand::MESSAGE: resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); break; + case SessionCommand::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); break; + case SessionCommand::GET_USER_INFO: resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); break; + case SessionCommand::LIST_ROOMS: resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc); break; + case SessionCommand::JOIN_ROOM: resp = cmdJoinRoom(sc.GetExtension(Command_JoinRoom::ext), rc); break; + case SessionCommand::LIST_USERS: resp = cmdListUsers(sc.GetExtension(Command_ListUsers::ext), rc); break; + default: resp = processExtendedSessionCommand(num, sc, rc); + } + if (resp != Response::RespOk) + finalResponseCode = resp; + } + return finalResponseCode; } Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; - QReadLocker locker(&server->roomsLock); - Server_Room *room = rooms.value(cont.room_id(), 0); - if (!room) - return Response::RespNotInRoom; - - Response::ResponseCode finalResponseCode = Response::RespOk; - for (int i = cont.room_command_size() - 1; i >= 0; --i) { - Response::ResponseCode resp = Response::RespInvalidCommand; - const RoomCommand &sc = cont.room_command(i); - const int num = getPbExtension(sc); - logDebugMessage(QString::fromStdString(sc.ShortDebugString())); - switch ((RoomCommand::RoomCommandType) num) { - case RoomCommand::LEAVE_ROOM: resp = cmdLeaveRoom(sc.GetExtension(Command_LeaveRoom::ext), room, rc); break; - case RoomCommand::ROOM_SAY: resp = cmdRoomSay(sc.GetExtension(Command_RoomSay::ext), room, rc); break; - case RoomCommand::CREATE_GAME: resp = cmdCreateGame(sc.GetExtension(Command_CreateGame::ext), room, rc); break; - case RoomCommand::JOIN_GAME: resp = cmdJoinGame(sc.GetExtension(Command_JoinGame::ext), room, rc); break; - } - if (resp != Response::RespOk) - finalResponseCode = resp; - } - return finalResponseCode; + QReadLocker locker(&server->roomsLock); + Server_Room *room = rooms.value(cont.room_id(), 0); + if (!room) + return Response::RespNotInRoom; + + Response::ResponseCode finalResponseCode = Response::RespOk; + for (int i = cont.room_command_size() - 1; i >= 0; --i) { + Response::ResponseCode resp = Response::RespInvalidCommand; + const RoomCommand &sc = cont.room_command(i); + const int num = getPbExtension(sc); + logDebugMessage(QString::fromStdString(sc.ShortDebugString())); + switch ((RoomCommand::RoomCommandType) num) { + case RoomCommand::LEAVE_ROOM: resp = cmdLeaveRoom(sc.GetExtension(Command_LeaveRoom::ext), room, rc); break; + case RoomCommand::ROOM_SAY: resp = cmdRoomSay(sc.GetExtension(Command_RoomSay::ext), room, rc); break; + case RoomCommand::CREATE_GAME: resp = cmdCreateGame(sc.GetExtension(Command_CreateGame::ext), room, rc); break; + case RoomCommand::JOIN_GAME: resp = cmdJoinGame(sc.GetExtension(Command_JoinGame::ext), room, rc); break; + } + if (resp != Response::RespOk) + finalResponseCode = resp; + } + return finalResponseCode; } Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - QMap > gameMap = getGames(); - if (!gameMap.contains(cont.game_id())) - return Response::RespNotInRoom; - const QPair roomIdAndPlayerId = gameMap.value(cont.game_id()); - - QReadLocker roomsLocker(&server->roomsLock); - Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first); - if (!room) - return Response::RespNotInRoom; - - QReadLocker roomGamesLocker(&room->gamesLock); - Server_Game *game = room->getGames().value(cont.game_id()); - if (!game) { - if (room->getExternalGames().contains(cont.game_id())) { - server->sendIsl_GameCommand(cont, - room->getExternalGames().value(cont.game_id()).server_id(), - userInfo->session_id(), - roomIdAndPlayerId.first, - roomIdAndPlayerId.second - ); - return Response::RespNothing; - } - return Response::RespNotInRoom; - } - - QMutexLocker gameLocker(&game->gameMutex); - Server_Player *player = game->getPlayers().value(roomIdAndPlayerId.second); - if (!player) - return Response::RespNotInRoom; - - GameEventStorage ges; - Response::ResponseCode finalResponseCode = Response::RespOk; - for (int i = cont.game_command_size() - 1; i >= 0; --i) { - const GameCommand &sc = cont.game_command(i); - logDebugMessage(QString("game %1 player %2: ").arg(cont.game_id()).arg(roomIdAndPlayerId.second) + QString::fromStdString(sc.ShortDebugString())); - - Response::ResponseCode resp = player->processGameCommand(sc, rc, ges); + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + QMap > gameMap = getGames(); + if (!gameMap.contains(cont.game_id())) + return Response::RespNotInRoom; + const QPair roomIdAndPlayerId = gameMap.value(cont.game_id()); + + QReadLocker roomsLocker(&server->roomsLock); + Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first); + if (!room) + return Response::RespNotInRoom; + + QReadLocker roomGamesLocker(&room->gamesLock); + Server_Game *game = room->getGames().value(cont.game_id()); + if (!game) { + if (room->getExternalGames().contains(cont.game_id())) { + server->sendIsl_GameCommand(cont, + room->getExternalGames().value(cont.game_id()).server_id(), + userInfo->session_id(), + roomIdAndPlayerId.first, + roomIdAndPlayerId.second + ); + return Response::RespNothing; + } + return Response::RespNotInRoom; + } + + QMutexLocker gameLocker(&game->gameMutex); + Server_Player *player = game->getPlayers().value(roomIdAndPlayerId.second); + if (!player) + return Response::RespNotInRoom; + + GameEventStorage ges; + Response::ResponseCode finalResponseCode = Response::RespOk; + for (int i = cont.game_command_size() - 1; i >= 0; --i) { + const GameCommand &sc = cont.game_command(i); + logDebugMessage(QString("game %1 player %2: ").arg(cont.game_id()).arg(roomIdAndPlayerId.second) + QString::fromStdString(sc.ShortDebugString())); + + Response::ResponseCode resp = player->processGameCommand(sc, rc, ges); - if (resp != Response::RespOk) - finalResponseCode = resp; - } - ges.sendToGame(game); - - return finalResponseCode; + if (resp != Response::RespOk) + finalResponseCode = resp; + } + ges.sendToGame(game); + + return finalResponseCode; } Response::ResponseCode Server_ProtocolHandler::processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { - if (!userInfo) - return Response::RespLoginNeeded; - if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) - return Response::RespLoginNeeded; + if (!userInfo) + return Response::RespLoginNeeded; + if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) + return Response::RespLoginNeeded; - Response::ResponseCode finalResponseCode = Response::RespOk; - for (int i = cont.moderator_command_size() - 1; i >= 0; --i) { - Response::ResponseCode resp = Response::RespInvalidCommand; - const ModeratorCommand &sc = cont.moderator_command(i); - const int num = getPbExtension(sc); - logDebugMessage(QString::fromStdString(sc.ShortDebugString())); - - resp = processExtendedModeratorCommand(num, sc, rc); - if (resp != Response::RespOk) - finalResponseCode = resp; - } - return finalResponseCode; + Response::ResponseCode finalResponseCode = Response::RespOk; + for (int i = cont.moderator_command_size() - 1; i >= 0; --i) { + Response::ResponseCode resp = Response::RespInvalidCommand; + const ModeratorCommand &sc = cont.moderator_command(i); + const int num = getPbExtension(sc); + logDebugMessage(QString::fromStdString(sc.ShortDebugString())); + + resp = processExtendedModeratorCommand(num, sc, rc); + if (resp != Response::RespOk) + finalResponseCode = resp; + } + return finalResponseCode; } Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { - if (!userInfo) - return Response::RespLoginNeeded; - if (!(userInfo->user_level() & ServerInfo_User::IsAdmin)) - return Response::RespLoginNeeded; + if (!userInfo) + return Response::RespLoginNeeded; + if (!(userInfo->user_level() & ServerInfo_User::IsAdmin)) + return Response::RespLoginNeeded; - Response::ResponseCode finalResponseCode = Response::RespOk; - for (int i = cont.admin_command_size() - 1; i >= 0; --i) { - Response::ResponseCode resp = Response::RespInvalidCommand; - const AdminCommand &sc = cont.admin_command(i); - const int num = getPbExtension(sc); - logDebugMessage(QString::fromStdString(sc.ShortDebugString())); - - resp = processExtendedAdminCommand(num, sc, rc); - if (resp != Response::RespOk) - finalResponseCode = resp; - } - return finalResponseCode; + Response::ResponseCode finalResponseCode = Response::RespOk; + for (int i = cont.admin_command_size() - 1; i >= 0; --i) { + Response::ResponseCode resp = Response::RespInvalidCommand; + const AdminCommand &sc = cont.admin_command(i); + const int num = getPbExtension(sc); + logDebugMessage(QString::fromStdString(sc.ShortDebugString())); + + resp = processExtendedAdminCommand(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. - if (deleted) - return; - - lastDataReceived = timeRunning; - - ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1); - Response::ResponseCode finalResponseCode; - - if (cont.game_command_size()) - finalResponseCode = processGameCommandContainer(cont, responseContainer); - else if (cont.room_command_size()) - finalResponseCode = processRoomCommandContainer(cont, responseContainer); - else if (cont.session_command_size()) - finalResponseCode = processSessionCommandContainer(cont, responseContainer); - else if (cont.moderator_command_size()) - finalResponseCode = processModeratorCommandContainer(cont, responseContainer); - else if (cont.admin_command_size()) - finalResponseCode = processAdminCommandContainer(cont, responseContainer); - else - finalResponseCode = Response::RespInvalidCommand; - - if ((finalResponseCode != Response::RespNothing)) - sendResponseContainer(responseContainer, finalResponseCode); + // Command processing must be disabled after prepareDestroy() has been called. + if (deleted) + return; + + lastDataReceived = timeRunning; + + ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1); + Response::ResponseCode finalResponseCode; + + if (cont.game_command_size()) + finalResponseCode = processGameCommandContainer(cont, responseContainer); + else if (cont.room_command_size()) + finalResponseCode = processRoomCommandContainer(cont, responseContainer); + else if (cont.session_command_size()) + finalResponseCode = processSessionCommandContainer(cont, responseContainer); + else if (cont.moderator_command_size()) + finalResponseCode = processModeratorCommandContainer(cont, responseContainer); + else if (cont.admin_command_size()) + finalResponseCode = processAdminCommandContainer(cont, responseContainer); + else + finalResponseCode = Response::RespInvalidCommand; + + if ((finalResponseCode != Response::RespNothing)) + sendResponseContainer(responseContainer, finalResponseCode); } void Server_ProtocolHandler::pingClockTimeout() { - int interval = server->getMessageCountingInterval(); - if (interval > 0) { - messageSizeOverTime.prepend(0); - if (messageSizeOverTime.size() > server->getMessageCountingInterval()) - messageSizeOverTime.removeLast(); - messageCountOverTime.prepend(0); - if (messageCountOverTime.size() > server->getMessageCountingInterval()) - messageCountOverTime.removeLast(); - } - - if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime()) - prepareDestroy(); - ++timeRunning; + int interval = server->getMessageCountingInterval(); + if (interval > 0) { + messageSizeOverTime.prepend(0); + if (messageSizeOverTime.size() > server->getMessageCountingInterval()) + messageSizeOverTime.removeLast(); + messageCountOverTime.prepend(0); + if (messageCountOverTime.size() > server->getMessageCountingInterval()) + messageCountOverTime.removeLast(); + } + + if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime()) + prepareDestroy(); + ++timeRunning; } Response::ResponseCode Server_ProtocolHandler::cmdPing(const Command_Ping & /*cmd*/, ResponseContainer & /*rc*/) { - return Response::RespOk; + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd, ResponseContainer &rc) { - QString userName = QString::fromStdString(cmd.user_name()).simplified(); - if (userName.isEmpty() || (userInfo != 0)) - return Response::RespContextError; - QString reasonStr; - int banSecondsLeft = 0; - AuthenticationResult res = server->loginUser(this, userName, QString::fromStdString(cmd.password()), reasonStr, banSecondsLeft); - switch (res) { - case UserIsBanned: { - Response_Login *re = new Response_Login; - re->set_denied_reason_str(reasonStr.toStdString()); - if (banSecondsLeft != 0) - re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toTime_t()); - rc.setResponseExtension(re); - return Response::RespUserIsBanned; - } - case NotLoggedIn: return Response::RespWrongPassword; - case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession; - case UsernameInvalid: return Response::RespUsernameInvalid; - default: authState = res; - } - - userName = QString::fromStdString(userInfo->name()); - Event_ServerMessage event; - event.set_message(server->getLoginMessage().toStdString()); - rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); - - Response_Login *re = new Response_Login; - re->mutable_user_info()->CopyFrom(copyUserInfo(true)); - - if (authState == PasswordRight) { - QMapIterator buddyIterator(databaseInterface->getBuddyList(userName)); - while (buddyIterator.hasNext()) - re->add_buddy_list()->CopyFrom(buddyIterator.next().value()); - - QMapIterator ignoreIterator(databaseInterface->getIgnoreList(userName)); - while (ignoreIterator.hasNext()) - re->add_ignore_list()->CopyFrom(ignoreIterator.next().value()); - } - - joinPersistentGames(rc); - - rc.setResponseExtension(re); - return Response::RespOk; + QString userName = QString::fromStdString(cmd.user_name()).simplified(); + if (userName.isEmpty() || (userInfo != 0)) + return Response::RespContextError; + QString reasonStr; + int banSecondsLeft = 0; + AuthenticationResult res = server->loginUser(this, userName, QString::fromStdString(cmd.password()), reasonStr, banSecondsLeft); + switch (res) { + case UserIsBanned: { + Response_Login *re = new Response_Login; + re->set_denied_reason_str(reasonStr.toStdString()); + if (banSecondsLeft != 0) + re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toTime_t()); + rc.setResponseExtension(re); + return Response::RespUserIsBanned; + } + case NotLoggedIn: return Response::RespWrongPassword; + case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession; + case UsernameInvalid: return Response::RespUsernameInvalid; + default: authState = res; + } + + userName = QString::fromStdString(userInfo->name()); + Event_ServerMessage event; + event.set_message(server->getLoginMessage().toStdString()); + rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); + + Response_Login *re = new Response_Login; + re->mutable_user_info()->CopyFrom(copyUserInfo(true)); + + if (authState == PasswordRight) { + QMapIterator buddyIterator(databaseInterface->getBuddyList(userName)); + while (buddyIterator.hasNext()) + re->add_buddy_list()->CopyFrom(buddyIterator.next().value()); + + QMapIterator ignoreIterator(databaseInterface->getIgnoreList(userName)); + while (ignoreIterator.hasNext()) + re->add_ignore_list()->CopyFrom(ignoreIterator.next().value()); + } + + joinPersistentGames(rc); + + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message &cmd, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - QReadLocker locker(&server->clientsLock); - - QString receiver = QString::fromStdString(cmd.user_name()); - Server_AbstractUserInterface *userInterface = server->findUser(receiver); - if (!userInterface) - return Response::RespNameNotFound; - if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name()))) - return Response::RespInIgnoreList; - - Event_UserMessage event; - event.set_sender_name(userInfo->name()); - event.set_receiver_name(cmd.user_name()); - event.set_message(cmd.message()); - - SessionEvent *se = prepareSessionEvent(event); - userInterface->sendProtocolItem(*se); - rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, se); - - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + QReadLocker locker(&server->clientsLock); + + QString receiver = QString::fromStdString(cmd.user_name()); + Server_AbstractUserInterface *userInterface = server->findUser(receiver); + if (!userInterface) + return Response::RespNameNotFound; + if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name()))) + return Response::RespInIgnoreList; + + Event_UserMessage event; + event.set_sender_name(userInfo->name()); + event.set_receiver_name(cmd.user_name()); + event.set_message(cmd.message()); + + SessionEvent *se = prepareSessionEvent(event); + userInterface->sendProtocolItem(*se); + rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, se); + + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - // We don't need to check whether the user is logged in; persistent games should also work. - // The client needs to deal with an empty result list. - - Response_GetGamesOfUser *re = new Response_GetGamesOfUser; - server->roomsLock.lockForRead(); - QMapIterator roomIterator(server->getRooms()); - while (roomIterator.hasNext()) { - Server_Room *room = roomIterator.next().value(); - room->gamesLock.lockForRead(); - room->getInfo(*re->add_room_list(), false, true); - QListIterator gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name()))); - while (gameIterator.hasNext()) - re->add_game_list()->CopyFrom(gameIterator.next()); - room->gamesLock.unlock(); - } - server->roomsLock.unlock(); - - rc.setResponseExtension(re); - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + // We don't need to check whether the user is logged in; persistent games should also work. + // The client needs to deal with an empty result list. + + Response_GetGamesOfUser *re = new Response_GetGamesOfUser; + server->roomsLock.lockForRead(); + QMapIterator roomIterator(server->getRooms()); + while (roomIterator.hasNext()) { + Server_Room *room = roomIterator.next().value(); + room->gamesLock.lockForRead(); + room->getInfo(*re->add_room_list(), false, true); + QListIterator gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name()))); + while (gameIterator.hasNext()) + re->add_game_list()->CopyFrom(gameIterator.next()); + room->gamesLock.unlock(); + } + server->roomsLock.unlock(); + + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - QString userName = QString::fromStdString(cmd.user_name()); - Response_GetUserInfo *re = new Response_GetUserInfo; - if (userName.isEmpty()) - re->mutable_user_info()->CopyFrom(*userInfo); - else { - - QReadLocker locker(&server->clientsLock); - - ServerInfo_User_Container *infoSource = server->findUser(userName); - if (!infoSource) - return Response::RespNameNotFound; - - re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator)); - } - - rc.setResponseExtension(re); - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + QString userName = QString::fromStdString(cmd.user_name()); + Response_GetUserInfo *re = new Response_GetUserInfo; + if (userName.isEmpty()) + re->mutable_user_info()->CopyFrom(*userInfo); + else { + + QReadLocker locker(&server->clientsLock); + + ServerInfo_User_Container *infoSource = server->findUser(userName); + if (!infoSource) + return Response::RespNameNotFound; + + re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator)); + } + + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdListRooms(const Command_ListRooms & /*cmd*/, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - Event_ListRooms event; - QMapIterator roomIterator(server->getRooms()); - while (roomIterator.hasNext()) - roomIterator.next().value()->getInfo(*event.add_room_list(), false); - rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); - - acceptsRoomListChanges = true; - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + Event_ListRooms event; + QMapIterator roomIterator(server->getRooms()); + while (roomIterator.hasNext()) + roomIterator.next().value()->getInfo(*event.add_room_list(), false); + rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); + + acceptsRoomListChanges = true; + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - if (rooms.contains(cmd.room_id())) - return Response::RespContextError; - - QReadLocker serverLocker(&server->roomsLock); - Server_Room *r = server->getRooms().value(cmd.room_id(), 0); - if (!r) - return Response::RespNameNotFound; - - r->addClient(this); - rooms.insert(r->getId(), r); - - Event_RoomSay joinMessageEvent; - joinMessageEvent.set_message(r->getJoinMessage().toStdString()); - rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent)); - - Response_JoinRoom *re = new Response_JoinRoom; - r->getInfo(*re->mutable_room_info(), true); - - rc.setResponseExtension(re); - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + if (rooms.contains(cmd.room_id())) + return Response::RespContextError; + + QReadLocker serverLocker(&server->roomsLock); + Server_Room *r = server->getRooms().value(cmd.room_id(), 0); + if (!r) + return Response::RespNameNotFound; + + r->addClient(this); + rooms.insert(r->getId(), r); + + Event_RoomSay joinMessageEvent; + joinMessageEvent.set_message(r->getJoinMessage().toStdString()); + rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent)); + + Response_JoinRoom *re = new Response_JoinRoom; + r->getInfo(*re->mutable_room_info(), true); + + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUsers & /*cmd*/, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - Response_ListUsers *re = new Response_ListUsers; - server->clientsLock.lockForRead(); - QMapIterator userIterator = server->getUsers(); - while (userIterator.hasNext()) - re->add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false)); - QMapIterator extIterator = server->getExternalUsers(); - while (extIterator.hasNext()) - re->add_user_list()->CopyFrom(extIterator.next().value()->copyUserInfo(false)); - - acceptsUserListChanges = true; - server->clientsLock.unlock(); - - rc.setResponseExtension(re); - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + Response_ListUsers *re = new Response_ListUsers; + server->clientsLock.lockForRead(); + QMapIterator userIterator = server->getUsers(); + while (userIterator.hasNext()) + re->add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false)); + QMapIterator extIterator = server->getExternalUsers(); + while (extIterator.hasNext()) + re->add_user_list()->CopyFrom(extIterator.next().value()->copyUserInfo(false)); + + acceptsUserListChanges = true; + server->clientsLock.unlock(); + + rc.setResponseExtension(re); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdLeaveRoom(const Command_LeaveRoom & /*cmd*/, Server_Room *room, ResponseContainer & /*rc*/) { - rooms.remove(room->getId()); - room->removeClient(this); - return Response::RespOk; + rooms.remove(room->getId()); + room->removeClient(this); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdRoomSay(const Command_RoomSay &cmd, Server_Room *room, ResponseContainer & /*rc*/) { - QString msg = QString::fromStdString(cmd.message()); - - if (server->getMessageCountingInterval() > 0) { - int totalSize = 0, totalCount = 0; - if (messageSizeOverTime.isEmpty()) - messageSizeOverTime.prepend(0); - messageSizeOverTime[0] += msg.size(); - for (int i = 0; i < messageSizeOverTime.size(); ++i) - totalSize += messageSizeOverTime[i]; - - if (messageCountOverTime.isEmpty()) - messageCountOverTime.prepend(0); - ++messageCountOverTime[0]; - for (int i = 0; i < messageCountOverTime.size(); ++i) - totalCount += messageCountOverTime[i]; - - if ((totalSize > server->getMaxMessageSizePerInterval()) || (totalCount > server->getMaxMessageCountPerInterval())) - return Response::RespChatFlood; - } - msg.replace(QChar('\n'), QChar(' ')); - - room->say(QString::fromStdString(userInfo->name()), msg); - return Response::RespOk; + QString msg = QString::fromStdString(cmd.message()); + + if (server->getMessageCountingInterval() > 0) { + int totalSize = 0, totalCount = 0; + if (messageSizeOverTime.isEmpty()) + messageSizeOverTime.prepend(0); + messageSizeOverTime[0] += msg.size(); + for (int i = 0; i < messageSizeOverTime.size(); ++i) + totalSize += messageSizeOverTime[i]; + + if (messageCountOverTime.isEmpty()) + messageCountOverTime.prepend(0); + ++messageCountOverTime[0]; + for (int i = 0; i < messageCountOverTime.size(); ++i) + totalCount += messageCountOverTime[i]; + + if ((totalSize > server->getMaxMessageSizePerInterval()) || (totalCount > server->getMaxMessageCountPerInterval())) + return Response::RespChatFlood; + } + msg.replace(QChar('\n'), QChar(' ')); + + room->say(QString::fromStdString(userInfo->name()), msg); + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room *room, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - const int gameId = databaseInterface->getNextGameId(); - if (gameId == -1) - return Response::RespInternalError; - - if (server->getMaxGamesPerUser() > 0) - if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser()) - return Response::RespContextError; - - QList gameTypes; - for (int i = cmd.game_type_ids_size() - 1; i >= 0; --i) - gameTypes.append(cmd.game_type_ids(i)); - - QString description = QString::fromStdString(cmd.description()); - if (description.size() > 60) - description = description.left(60); - - Server_Game *game = new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes, cmd.only_buddies(), cmd.only_registered(), cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(), room); - game->addPlayer(this, rc, false, false); - room->addGame(game); - - return Response::RespOk; + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + const int gameId = databaseInterface->getNextGameId(); + if (gameId == -1) + return Response::RespInternalError; + + if (server->getMaxGamesPerUser() > 0) + if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser()) + return Response::RespContextError; + + QList gameTypes; + for (int i = cmd.game_type_ids_size() - 1; i >= 0; --i) + gameTypes.append(cmd.game_type_ids(i)); + + QString description = QString::fromStdString(cmd.description()); + if (description.size() > 60) + description = description.left(60); + + Server_Game *game = new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes, cmd.only_buddies(), cmd.only_registered(), cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(), room); + game->addPlayer(this, rc, false, false); + room->addGame(game); + + return Response::RespOk; } Response::ResponseCode Server_ProtocolHandler::cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc) { - if (authState == NotLoggedIn) - return Response::RespLoginNeeded; - - return room->processJoinGameCommand(cmd, rc, this); + if (authState == NotLoggedIn) + return Response::RespLoginNeeded; + + return room->processJoinGameCommand(cmd, rc, this); } diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 56165c809..0cea37ae3 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -40,64 +40,64 @@ class Command_CreateGame; class Command_JoinGame; class Server_ProtocolHandler : public QObject, public Server_AbstractUserInterface { - Q_OBJECT + Q_OBJECT protected: - QMap rooms; - - bool deleted; - Server_DatabaseInterface *databaseInterface; - AuthenticationResult authState; - bool acceptsUserListChanges; - bool acceptsRoomListChanges; - virtual void logDebugMessage(const QString &message) { } + QMap rooms; + + bool deleted; + Server_DatabaseInterface *databaseInterface; + AuthenticationResult authState; + bool acceptsUserListChanges; + bool acceptsRoomListChanges; + virtual void logDebugMessage(const QString &message) { } private: - QList messageSizeOverTime, messageCountOverTime; - int timeRunning, lastDataReceived; - QTimer *pingClock; + QList messageSizeOverTime, messageCountOverTime; + int timeRunning, lastDataReceived; + QTimer *pingClock; - virtual void transmitProtocolItem(const ServerMessage &item) = 0; - - Response::ResponseCode cmdPing(const Command_Ping &cmd, ResponseContainer &rc); - Response::ResponseCode cmdLogin(const Command_Login &cmd, ResponseContainer &rc); - Response::ResponseCode cmdMessage(const Command_Message &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc); - Response::ResponseCode cmdListRooms(const Command_ListRooms &cmd, ResponseContainer &rc); - Response::ResponseCode cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc); - Response::ResponseCode cmdListUsers(const Command_ListUsers &cmd, ResponseContainer &rc); - Response::ResponseCode cmdLeaveRoom(const Command_LeaveRoom &cmd, Server_Room *room, ResponseContainer &rc); - Response::ResponseCode cmdRoomSay(const Command_RoomSay &cmd, Server_Room *room, ResponseContainer &rc); - Response::ResponseCode cmdCreateGame(const Command_CreateGame &cmd, Server_Room *room, ResponseContainer &rc); - Response::ResponseCode cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc); - - Response::ResponseCode processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } - Response::ResponseCode processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - Response::ResponseCode processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - Response::ResponseCode processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } - Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } + virtual void transmitProtocolItem(const ServerMessage &item) = 0; + + Response::ResponseCode cmdPing(const Command_Ping &cmd, ResponseContainer &rc); + Response::ResponseCode cmdLogin(const Command_Login &cmd, ResponseContainer &rc); + Response::ResponseCode cmdMessage(const Command_Message &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc); + Response::ResponseCode cmdListRooms(const Command_ListRooms &cmd, ResponseContainer &rc); + Response::ResponseCode cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc); + Response::ResponseCode cmdListUsers(const Command_ListUsers &cmd, ResponseContainer &rc); + Response::ResponseCode cmdLeaveRoom(const Command_LeaveRoom &cmd, Server_Room *room, ResponseContainer &rc); + Response::ResponseCode cmdRoomSay(const Command_RoomSay &cmd, Server_Room *room, ResponseContainer &rc); + Response::ResponseCode cmdCreateGame(const Command_CreateGame &cmd, Server_Room *room, ResponseContainer &rc); + Response::ResponseCode cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc); + + Response::ResponseCode processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc); + virtual Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } + Response::ResponseCode processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc); + Response::ResponseCode processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc); + Response::ResponseCode processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc); + virtual Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } + Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc); + virtual Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } private slots: - void pingClockTimeout(); + void pingClockTimeout(); public slots: - void prepareDestroy(); + void prepareDestroy(); public: - Server_ProtocolHandler(Server *_server, Server_DatabaseInterface *_databaseInterface, QObject *parent = 0); - ~Server_ProtocolHandler(); - - bool getAcceptsUserListChanges() const { return acceptsUserListChanges; } - bool getAcceptsRoomListChanges() const { return acceptsRoomListChanges; } - virtual QString getAddress() const = 0; - Server_DatabaseInterface *getDatabaseInterface() const { return databaseInterface; } + Server_ProtocolHandler(Server *_server, Server_DatabaseInterface *_databaseInterface, QObject *parent = 0); + ~Server_ProtocolHandler(); + + bool getAcceptsUserListChanges() const { return acceptsUserListChanges; } + bool getAcceptsRoomListChanges() const { return acceptsRoomListChanges; } + virtual QString getAddress() const = 0; + Server_DatabaseInterface *getDatabaseInterface() const { return databaseInterface; } - int getLastCommandTime() const { return timeRunning - lastDataReceived; } - void processCommandContainer(const CommandContainer &cont); - - void sendProtocolItem(const Response &item); - void sendProtocolItem(const SessionEvent &item); - void sendProtocolItem(const GameEventContainer &item); - void sendProtocolItem(const RoomEvent &item); + int getLastCommandTime() const { return timeRunning - lastDataReceived; } + void processCommandContainer(const CommandContainer &cont); + + void sendProtocolItem(const Response &item); + void sendProtocolItem(const SessionEvent &item); + void sendProtocolItem(const GameEventContainer &item); + void sendProtocolItem(const RoomEvent &item); }; #endif diff --git a/common/server_remoteuserinterface.cpp b/common/server_remoteuserinterface.cpp index cd0e30312..7bbcf9632 100644 --- a/common/server_remoteuserinterface.cpp +++ b/common/server_remoteuserinterface.cpp @@ -4,20 +4,20 @@ void Server_RemoteUserInterface::sendProtocolItem(const Response &item) { - server->sendIsl_Response(item, userInfo->server_id(), userInfo->session_id()); + server->sendIsl_Response(item, userInfo->server_id(), userInfo->session_id()); } void Server_RemoteUserInterface::sendProtocolItem(const SessionEvent &item) { - server->sendIsl_SessionEvent(item, userInfo->server_id(), userInfo->session_id()); + server->sendIsl_SessionEvent(item, userInfo->server_id(), userInfo->session_id()); } void Server_RemoteUserInterface::sendProtocolItem(const GameEventContainer &item) { - server->sendIsl_GameEventContainer(item, userInfo->server_id(), userInfo->session_id()); + server->sendIsl_GameEventContainer(item, userInfo->server_id(), userInfo->session_id()); } void Server_RemoteUserInterface::sendProtocolItem(const RoomEvent &item) { - server->sendIsl_RoomEvent(item, userInfo->server_id(), userInfo->session_id()); + server->sendIsl_RoomEvent(item, userInfo->server_id(), userInfo->session_id()); } diff --git a/common/server_remoteuserinterface.h b/common/server_remoteuserinterface.h index d85b54a3b..311f0516b 100644 --- a/common/server_remoteuserinterface.h +++ b/common/server_remoteuserinterface.h @@ -5,14 +5,14 @@ class Server_RemoteUserInterface : public Server_AbstractUserInterface { public: - Server_RemoteUserInterface(Server *_server, const ServerInfo_User_Container &_userInfoContainer) : Server_AbstractUserInterface(_server, _userInfoContainer) { } - - int getLastCommandTime() const { return 0; } - - void sendProtocolItem(const Response &item); - void sendProtocolItem(const SessionEvent &item); - void sendProtocolItem(const GameEventContainer &item); - void sendProtocolItem(const RoomEvent &item); + Server_RemoteUserInterface(Server *_server, const ServerInfo_User_Container &_userInfoContainer) : Server_AbstractUserInterface(_server, _userInfoContainer) { } + + int getLastCommandTime() const { return 0; } + + void sendProtocolItem(const Response &item); + void sendProtocolItem(const SessionEvent &item); + void sendProtocolItem(const GameEventContainer &item); + void sendProtocolItem(const RoomEvent &item); }; #endif diff --git a/common/server_response_containers.cpp b/common/server_response_containers.cpp index bc36222ef..0c95e5379 100644 --- a/common/server_response_containers.cpp +++ b/common/server_response_containers.cpp @@ -3,64 +3,64 @@ #include "server_game.h" GameEventStorageItem::GameEventStorageItem(const ::google::protobuf::Message &_event, int _playerId, EventRecipients _recipients) - : event(new GameEvent), recipients(_recipients) + : event(new GameEvent), recipients(_recipients) { - event->GetReflection()->MutableMessage(event, _event.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(_event); - event->set_player_id(_playerId); + event->GetReflection()->MutableMessage(event, _event.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(_event); + event->set_player_id(_playerId); } GameEventStorageItem::~GameEventStorageItem() { - delete event; + delete event; } GameEventStorage::GameEventStorage() - : gameEventContext(0) + : gameEventContext(0) { } GameEventStorage::~GameEventStorage() { - delete gameEventContext; - for (int i = 0; i < gameEventList.size(); ++i) - delete gameEventList[i]; + delete gameEventContext; + for (int i = 0; i < gameEventList.size(); ++i) + delete gameEventList[i]; } void GameEventStorage::setGameEventContext(const ::google::protobuf::Message &_gameEventContext) { - delete gameEventContext; - gameEventContext = new GameEventContext; - gameEventContext->GetReflection()->MutableMessage(gameEventContext, _gameEventContext.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(_gameEventContext); + delete gameEventContext; + gameEventContext = new GameEventContext; + gameEventContext->GetReflection()->MutableMessage(gameEventContext, _gameEventContext.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(_gameEventContext); } void GameEventStorage::enqueueGameEvent(const ::google::protobuf::Message &event, int playerId, GameEventStorageItem::EventRecipients recipients, int _privatePlayerId) { - gameEventList.append(new GameEventStorageItem(event, playerId, recipients)); - if (_privatePlayerId != -1) - privatePlayerId = _privatePlayerId; + gameEventList.append(new GameEventStorageItem(event, playerId, recipients)); + if (_privatePlayerId != -1) + privatePlayerId = _privatePlayerId; } void GameEventStorage::sendToGame(Server_Game *game) { - if (gameEventList.isEmpty()) - return; - - GameEventContainer *contPrivate = new GameEventContainer; - GameEventContainer *contOthers = new GameEventContainer; - for (int i = 0; i < gameEventList.size(); ++i) { - const GameEvent &event = gameEventList[i]->getGameEvent(); - const GameEventStorageItem::EventRecipients recipients = gameEventList[i]->getRecipients(); - if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) - contPrivate->add_event_list()->CopyFrom(event); - if (recipients.testFlag(GameEventStorageItem::SendToOthers)) - contOthers->add_event_list()->CopyFrom(event); - } - if (gameEventContext) { - contPrivate->mutable_context()->CopyFrom(*gameEventContext); - contOthers->mutable_context()->CopyFrom(*gameEventContext); - } - game->sendGameEventContainer(contPrivate, GameEventStorageItem::SendToPrivate, privatePlayerId); - game->sendGameEventContainer(contOthers, GameEventStorageItem::SendToOthers, privatePlayerId); + if (gameEventList.isEmpty()) + return; + + GameEventContainer *contPrivate = new GameEventContainer; + GameEventContainer *contOthers = new GameEventContainer; + for (int i = 0; i < gameEventList.size(); ++i) { + const GameEvent &event = gameEventList[i]->getGameEvent(); + const GameEventStorageItem::EventRecipients recipients = gameEventList[i]->getRecipients(); + if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) + contPrivate->add_event_list()->CopyFrom(event); + if (recipients.testFlag(GameEventStorageItem::SendToOthers)) + contOthers->add_event_list()->CopyFrom(event); + } + if (gameEventContext) { + contPrivate->mutable_context()->CopyFrom(*gameEventContext); + contOthers->mutable_context()->CopyFrom(*gameEventContext); + } + game->sendGameEventContainer(contPrivate, GameEventStorageItem::SendToPrivate, privatePlayerId); + game->sendGameEventContainer(contOthers, GameEventStorageItem::SendToOthers, privatePlayerId); } ResponseContainer::ResponseContainer(int _cmdId) @@ -70,9 +70,9 @@ ResponseContainer::ResponseContainer(int _cmdId) ResponseContainer::~ResponseContainer() { - delete responseExtension; - for (int i = 0; i < preResponseQueue.size(); ++i) - delete preResponseQueue[i].second; - for (int i = 0; i < postResponseQueue.size(); ++i) - delete postResponseQueue[i].second; + delete responseExtension; + for (int i = 0; i < preResponseQueue.size(); ++i) + delete preResponseQueue[i].second; + for (int i = 0; i < postResponseQueue.size(); ++i) + delete postResponseQueue[i].second; } diff --git a/common/server_response_containers.h b/common/server_response_containers.h index 9220cdacb..c6c9da74b 100644 --- a/common/server_response_containers.h +++ b/common/server_response_containers.h @@ -9,54 +9,54 @@ class Server_Game; class GameEventStorageItem { public: - enum EventRecipient { SendToPrivate = 0x01, SendToOthers = 0x02}; - Q_DECLARE_FLAGS(EventRecipients, EventRecipient) + enum EventRecipient { SendToPrivate = 0x01, SendToOthers = 0x02}; + Q_DECLARE_FLAGS(EventRecipients, EventRecipient) private: - GameEvent *event; - EventRecipients recipients; + GameEvent *event; + EventRecipients recipients; public: - GameEventStorageItem(const ::google::protobuf::Message &_event, int _playerId, EventRecipients _recipients); - ~GameEventStorageItem(); - - const GameEvent &getGameEvent() const { return *event; } - EventRecipients getRecipients() const { return recipients; } + GameEventStorageItem(const ::google::protobuf::Message &_event, int _playerId, EventRecipients _recipients); + ~GameEventStorageItem(); + + const GameEvent &getGameEvent() const { return *event; } + EventRecipients getRecipients() const { return recipients; } }; Q_DECLARE_OPERATORS_FOR_FLAGS(GameEventStorageItem::EventRecipients) class GameEventStorage { private: - ::google::protobuf::Message *gameEventContext; - QList gameEventList; - int privatePlayerId; + ::google::protobuf::Message *gameEventContext; + QList gameEventList; + int privatePlayerId; public: - GameEventStorage(); - ~GameEventStorage(); - - void setGameEventContext(const ::google::protobuf::Message &_gameEventContext); - ::google::protobuf::Message *getGameEventContext() const { return gameEventContext; } - const QList &getGameEventList() const { return gameEventList; } - int getPrivatePlayerId() const { return privatePlayerId; } - - void enqueueGameEvent(const ::google::protobuf::Message &event, int playerId, GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate | GameEventStorageItem::SendToOthers, int _privatePlayerId = -1); - void sendToGame(Server_Game *game); + GameEventStorage(); + ~GameEventStorage(); + + void setGameEventContext(const ::google::protobuf::Message &_gameEventContext); + ::google::protobuf::Message *getGameEventContext() const { return gameEventContext; } + const QList &getGameEventList() const { return gameEventList; } + int getPrivatePlayerId() const { return privatePlayerId; } + + void enqueueGameEvent(const ::google::protobuf::Message &event, int playerId, GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate | GameEventStorageItem::SendToOthers, int _privatePlayerId = -1); + void sendToGame(Server_Game *game); }; class ResponseContainer { private: - int cmdId; - ::google::protobuf::Message *responseExtension; - QList > preResponseQueue, postResponseQueue; + int cmdId; + ::google::protobuf::Message *responseExtension; + QList > preResponseQueue, postResponseQueue; public: - ResponseContainer(int _cmdId); - ~ResponseContainer(); - - int getCmdId() const { return cmdId; } - void setResponseExtension(::google::protobuf::Message *_responseExtension) { responseExtension = _responseExtension; } - ::google::protobuf::Message *getResponseExtension() const { return responseExtension; } - void enqueuePreResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item) { preResponseQueue.append(qMakePair(type, item)); } - void enqueuePostResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item) { postResponseQueue.append(qMakePair(type, item)); } - const QList > &getPreResponseQueue() const { return preResponseQueue; } - const QList > &getPostResponseQueue() const { return postResponseQueue; } + ResponseContainer(int _cmdId); + ~ResponseContainer(); + + int getCmdId() const { return cmdId; } + void setResponseExtension(::google::protobuf::Message *_responseExtension) { responseExtension = _responseExtension; } + ::google::protobuf::Message *getResponseExtension() const { return responseExtension; } + void enqueuePreResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item) { preResponseQueue.append(qMakePair(type, item)); } + void enqueuePostResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item) { postResponseQueue.append(qMakePair(type, item)); } + const QList > &getPreResponseQueue() const { return preResponseQueue; } + const QList > &getPostResponseQueue() const { return postResponseQueue; } }; #endif diff --git a/common/server_room.cpp b/common/server_room.cpp index 4369e129a..985fb543b 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -13,326 +13,326 @@ #include Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent) - : QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive) + : QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive) { - connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection); + connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection); } Server_Room::~Server_Room() { - qDebug("Server_Room destructor"); - - gamesLock.lockForWrite(); - const QList gameList = games.values(); - for (int i = 0; i < gameList.size(); ++i) - delete gameList[i]; - games.clear(); - gamesLock.unlock(); - - usersLock.lockForWrite(); - users.clear(); - usersLock.unlock(); + qDebug("Server_Room destructor"); + + gamesLock.lockForWrite(); + const QList gameList = games.values(); + for (int i = 0; i < gameList.size(); ++i) + delete gameList[i]; + games.clear(); + gamesLock.unlock(); + + usersLock.lockForWrite(); + users.clear(); + usersLock.unlock(); } Server *Server_Room::getServer() const { - return static_cast(parent()); + return static_cast(parent()); } const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes, bool includeExternalData) const { - result.set_room_id(id); - - result.set_name(name.toStdString()); - result.set_description(description.toStdString()); - result.set_auto_join(autoJoin); - - gamesLock.lockForRead(); - result.set_game_count(games.size() + externalGames.size()); - if (complete) { - QMapIterator gameIterator(games); - while (gameIterator.hasNext()) - gameIterator.next().value()->getInfo(*result.add_game_list()); - if (includeExternalData) { - QMapIterator externalGameIterator(externalGames); - while (externalGameIterator.hasNext()) - result.add_game_list()->CopyFrom(externalGameIterator.next().value()); - } - } - gamesLock.unlock(); - - usersLock.lockForRead(); - result.set_player_count(users.size() + externalUsers.size()); - if (complete) { - QMapIterator userIterator(users); - while (userIterator.hasNext()) - result.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false)); - if (includeExternalData) { - QMapIterator externalUserIterator(externalUsers); - while (externalUserIterator.hasNext()) - result.add_user_list()->CopyFrom(externalUserIterator.next().value().copyUserInfo(false)); - } - } - usersLock.unlock(); - - if (complete || showGameTypes) - for (int i = 0; i < gameTypes.size(); ++i) { - ServerInfo_GameType *gameTypeInfo = result.add_gametype_list(); - gameTypeInfo->set_game_type_id(i); - gameTypeInfo->set_description(gameTypes[i].toStdString()); - } - - return result; + result.set_room_id(id); + + result.set_name(name.toStdString()); + result.set_description(description.toStdString()); + result.set_auto_join(autoJoin); + + gamesLock.lockForRead(); + result.set_game_count(games.size() + externalGames.size()); + if (complete) { + QMapIterator gameIterator(games); + while (gameIterator.hasNext()) + gameIterator.next().value()->getInfo(*result.add_game_list()); + if (includeExternalData) { + QMapIterator externalGameIterator(externalGames); + while (externalGameIterator.hasNext()) + result.add_game_list()->CopyFrom(externalGameIterator.next().value()); + } + } + gamesLock.unlock(); + + usersLock.lockForRead(); + result.set_player_count(users.size() + externalUsers.size()); + if (complete) { + QMapIterator userIterator(users); + while (userIterator.hasNext()) + result.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false)); + if (includeExternalData) { + QMapIterator externalUserIterator(externalUsers); + while (externalUserIterator.hasNext()) + result.add_user_list()->CopyFrom(externalUserIterator.next().value().copyUserInfo(false)); + } + } + usersLock.unlock(); + + if (complete || showGameTypes) + for (int i = 0; i < gameTypes.size(); ++i) { + ServerInfo_GameType *gameTypeInfo = result.add_gametype_list(); + gameTypeInfo->set_game_type_id(i); + gameTypeInfo->set_description(gameTypes[i].toStdString()); + } + + return result; } RoomEvent *Server_Room::prepareRoomEvent(const ::google::protobuf::Message &roomEvent) { - RoomEvent *event = new RoomEvent; - event->set_room_id(id); - event->GetReflection()->MutableMessage(event, roomEvent.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(roomEvent); - return event; + RoomEvent *event = new RoomEvent; + event->set_room_id(id); + event->GetReflection()->MutableMessage(event, roomEvent.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(roomEvent); + return event; } void Server_Room::addClient(Server_ProtocolHandler *client) { - Event_JoinRoom event; - event.mutable_user_info()->CopyFrom(client->copyUserInfo(false)); - sendRoomEvent(prepareRoomEvent(event)); - - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); - - usersLock.lockForWrite(); - users.insert(QString::fromStdString(client->getUserInfo()->name()), client); - roomInfo.set_player_count(users.size() + externalUsers.size()); - usersLock.unlock(); - - // XXX This can be removed during the next client update. - gamesLock.lockForRead(); - roomInfo.set_game_count(games.size() + externalGames.size()); - gamesLock.unlock(); - // ----------- - - emit roomInfoChanged(roomInfo); + Event_JoinRoom event; + event.mutable_user_info()->CopyFrom(client->copyUserInfo(false)); + sendRoomEvent(prepareRoomEvent(event)); + + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); + + usersLock.lockForWrite(); + users.insert(QString::fromStdString(client->getUserInfo()->name()), client); + roomInfo.set_player_count(users.size() + externalUsers.size()); + usersLock.unlock(); + + // XXX This can be removed during the next client update. + gamesLock.lockForRead(); + roomInfo.set_game_count(games.size() + externalGames.size()); + gamesLock.unlock(); + // ----------- + + emit roomInfoChanged(roomInfo); } void Server_Room::removeClient(Server_ProtocolHandler *client) { - usersLock.lockForWrite(); - users.remove(QString::fromStdString(client->getUserInfo()->name())); - - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); - roomInfo.set_player_count(users.size() + externalUsers.size()); - usersLock.unlock(); - - Event_LeaveRoom event; - event.set_name(client->getUserInfo()->name()); - sendRoomEvent(prepareRoomEvent(event)); - - // XXX This can be removed during the next client update. - gamesLock.lockForRead(); - roomInfo.set_game_count(games.size() + externalGames.size()); - gamesLock.unlock(); - // ----------- - - emit roomInfoChanged(roomInfo); + usersLock.lockForWrite(); + users.remove(QString::fromStdString(client->getUserInfo()->name())); + + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); + roomInfo.set_player_count(users.size() + externalUsers.size()); + usersLock.unlock(); + + Event_LeaveRoom event; + event.set_name(client->getUserInfo()->name()); + sendRoomEvent(prepareRoomEvent(event)); + + // XXX This can be removed during the next client update. + gamesLock.lockForRead(); + roomInfo.set_game_count(games.size() + externalGames.size()); + gamesLock.unlock(); + // ----------- + + emit roomInfoChanged(roomInfo); } void Server_Room::addExternalUser(const ServerInfo_User &userInfo) { - // This function is always called from the Server thread with server->roomsMutex locked. - ServerInfo_User_Container userInfoContainer(userInfo); - Event_JoinRoom event; - event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false)); - sendRoomEvent(prepareRoomEvent(event), false); - - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); + // This function is always called from the Server thread with server->roomsMutex locked. + ServerInfo_User_Container userInfoContainer(userInfo); + Event_JoinRoom event; + event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false)); + sendRoomEvent(prepareRoomEvent(event), false); + + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); - usersLock.lockForWrite(); - externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer); - roomInfo.set_player_count(users.size() + externalUsers.size()); - usersLock.unlock(); - - emit roomInfoChanged(roomInfo); + usersLock.lockForWrite(); + externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer); + roomInfo.set_player_count(users.size() + externalUsers.size()); + usersLock.unlock(); + + emit roomInfoChanged(roomInfo); } void Server_Room::removeExternalUser(const QString &name) { - // This function is always called from the Server thread with server->roomsMutex locked. - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); - - usersLock.lockForWrite(); - if (externalUsers.contains(name)) - externalUsers.remove(name); - roomInfo.set_player_count(users.size() + externalUsers.size()); - usersLock.unlock(); - - Event_LeaveRoom event; - event.set_name(name.toStdString()); - sendRoomEvent(prepareRoomEvent(event), false); - - emit roomInfoChanged(roomInfo); + // This function is always called from the Server thread with server->roomsMutex locked. + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); + + usersLock.lockForWrite(); + if (externalUsers.contains(name)) + externalUsers.remove(name); + roomInfo.set_player_count(users.size() + externalUsers.size()); + usersLock.unlock(); + + Event_LeaveRoom event; + event.set_name(name.toStdString()); + sendRoomEvent(prepareRoomEvent(event), false); + + emit roomInfoChanged(roomInfo); } void Server_Room::updateExternalGameList(const ServerInfo_Game &gameInfo) { - // This function is always called from the Server thread with server->roomsMutex locked. - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); - - gamesLock.lockForWrite(); - if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id())) - externalGames.remove(gameInfo.game_id()); - else - externalGames.insert(gameInfo.game_id(), gameInfo); - roomInfo.set_game_count(games.size() + externalGames.size()); - gamesLock.unlock(); - - broadcastGameListUpdate(gameInfo, false); - emit roomInfoChanged(roomInfo); + // This function is always called from the Server thread with server->roomsMutex locked. + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); + + gamesLock.lockForWrite(); + if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id())) + externalGames.remove(gameInfo.game_id()); + else + externalGames.insert(gameInfo.game_id(), gameInfo); + roomInfo.set_game_count(games.size() + externalGames.size()); + gamesLock.unlock(); + + broadcastGameListUpdate(gameInfo, false); + emit roomInfoChanged(roomInfo); } Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGame &cmd, ResponseContainer &rc, Server_AbstractUserInterface *userInterface) { - // This function is called from the Server thread and from the S_PH thread. - // server->roomsMutex is always locked. - - QReadLocker roomGamesLocker(&gamesLock); - Server_Game *g = games.value(cmd.game_id()); - if (!g) { - if (externalGames.contains(cmd.game_id())) { - CommandContainer cont; - cont.set_cmd_id(rc.getCmdId()); - RoomCommand *roomCommand = cont.add_room_command(); - roomCommand->GetReflection()->MutableMessage(roomCommand, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd); - getServer()->sendIsl_RoomCommand(cont, externalGames.value(cmd.game_id()).server_id(), userInterface->getUserInfo()->session_id(), id); - - return Response::RespNothing; - } else - return Response::RespNameNotFound; - } - - QMutexLocker gameLocker(&g->gameMutex); - - Response::ResponseCode result = g->checkJoin(userInterface->getUserInfo(), QString::fromStdString(cmd.password()), cmd.spectator(), cmd.override_restrictions()); - if (result == Response::RespOk) - g->addPlayer(userInterface, rc, cmd.spectator()); - - return result; + // This function is called from the Server thread and from the S_PH thread. + // server->roomsMutex is always locked. + + QReadLocker roomGamesLocker(&gamesLock); + Server_Game *g = games.value(cmd.game_id()); + if (!g) { + if (externalGames.contains(cmd.game_id())) { + CommandContainer cont; + cont.set_cmd_id(rc.getCmdId()); + RoomCommand *roomCommand = cont.add_room_command(); + roomCommand->GetReflection()->MutableMessage(roomCommand, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd); + getServer()->sendIsl_RoomCommand(cont, externalGames.value(cmd.game_id()).server_id(), userInterface->getUserInfo()->session_id(), id); + + return Response::RespNothing; + } else + return Response::RespNameNotFound; + } + + QMutexLocker gameLocker(&g->gameMutex); + + Response::ResponseCode result = g->checkJoin(userInterface->getUserInfo(), QString::fromStdString(cmd.password()), cmd.spectator(), cmd.override_restrictions()); + if (result == Response::RespOk) + g->addPlayer(userInterface, rc, cmd.spectator()); + + return result; } void Server_Room::say(const QString &userName, const QString &s, bool sendToIsl) { - Event_RoomSay event; - event.set_name(userName.toStdString()); - event.set_message(s.toStdString()); - sendRoomEvent(prepareRoomEvent(event), sendToIsl); + Event_RoomSay event; + event.set_name(userName.toStdString()); + event.set_message(s.toStdString()); + sendRoomEvent(prepareRoomEvent(event), sendToIsl); } void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl) { - usersLock.lockForRead(); - { - QMapIterator userIterator(users); - while (userIterator.hasNext()) - userIterator.next().value()->sendProtocolItem(*event); - } - usersLock.unlock(); - - if (sendToIsl) - static_cast(parent())->sendIsl_RoomEvent(*event); - - delete event; + usersLock.lockForRead(); + { + QMapIterator userIterator(users); + while (userIterator.hasNext()) + userIterator.next().value()->sendProtocolItem(*event); + } + usersLock.unlock(); + + if (sendToIsl) + static_cast(parent())->sendIsl_RoomEvent(*event); + + delete event; } void Server_Room::broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl) { - Event_ListGames event; - event.add_game_list()->CopyFrom(gameInfo); - sendRoomEvent(prepareRoomEvent(event), sendToIsl); + Event_ListGames event; + event.add_game_list()->CopyFrom(gameInfo); + sendRoomEvent(prepareRoomEvent(event), sendToIsl); } void Server_Room::addGame(Server_Game *game) { - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); - - gamesLock.lockForWrite(); - connect(game, SIGNAL(gameInfoChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game))); - - game->gameMutex.lock(); - games.insert(game->getGameId(), game); - ServerInfo_Game gameInfo; - game->getInfo(gameInfo); - roomInfo.set_game_count(games.size() + externalGames.size()); - game->gameMutex.unlock(); - gamesLock.unlock(); - - // XXX This can be removed during the next client update. - usersLock.lockForRead(); - roomInfo.set_player_count(users.size() + externalUsers.size()); - usersLock.unlock(); - // ----------- + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); + + gamesLock.lockForWrite(); + connect(game, SIGNAL(gameInfoChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game))); + + game->gameMutex.lock(); + games.insert(game->getGameId(), game); + ServerInfo_Game gameInfo; + game->getInfo(gameInfo); + roomInfo.set_game_count(games.size() + externalGames.size()); + game->gameMutex.unlock(); + gamesLock.unlock(); + + // XXX This can be removed during the next client update. + usersLock.lockForRead(); + roomInfo.set_player_count(users.size() + externalUsers.size()); + usersLock.unlock(); + // ----------- - emit gameListChanged(gameInfo); - emit roomInfoChanged(roomInfo); + emit gameListChanged(gameInfo); + emit roomInfoChanged(roomInfo); } void Server_Room::removeGame(Server_Game *game) { - // No need to lock gamesLock or gameMutex. This method is only - // called from ~Server_Game, which locks both mutexes anyway beforehand. - - disconnect(game, 0, this, 0); - - ServerInfo_Game gameInfo; - game->getInfo(gameInfo); - emit gameListChanged(gameInfo); - - games.remove(game->getGameId()); - - ServerInfo_Room roomInfo; - roomInfo.set_room_id(id); - roomInfo.set_game_count(games.size() + externalGames.size()); - - // XXX This can be removed during the next client update. - usersLock.lockForRead(); - roomInfo.set_player_count(users.size() + externalUsers.size()); - usersLock.unlock(); - // ----------- - - emit roomInfoChanged(roomInfo); + // No need to lock gamesLock or gameMutex. This method is only + // called from ~Server_Game, which locks both mutexes anyway beforehand. + + disconnect(game, 0, this, 0); + + ServerInfo_Game gameInfo; + game->getInfo(gameInfo); + emit gameListChanged(gameInfo); + + games.remove(game->getGameId()); + + ServerInfo_Room roomInfo; + roomInfo.set_room_id(id); + roomInfo.set_game_count(games.size() + externalGames.size()); + + // XXX This can be removed during the next client update. + usersLock.lockForRead(); + roomInfo.set_player_count(users.size() + externalUsers.size()); + usersLock.unlock(); + // ----------- + + emit roomInfoChanged(roomInfo); } int Server_Room::getGamesCreatedByUser(const QString &userName) const { - QReadLocker locker(&gamesLock); - - QMapIterator gamesIterator(games); - int result = 0; - while (gamesIterator.hasNext()) - if (gamesIterator.next().value()->getCreatorInfo()->name() == userName.toStdString()) - ++result; - return result; + QReadLocker locker(&gamesLock); + + QMapIterator gamesIterator(games); + int result = 0; + while (gamesIterator.hasNext()) + if (gamesIterator.next().value()->getCreatorInfo()->name() == userName.toStdString()) + ++result; + return result; } QList Server_Room::getGamesOfUser(const QString &userName) const { - QReadLocker locker(&gamesLock); - - QList result; - QMapIterator gamesIterator(games); - while (gamesIterator.hasNext()) { - Server_Game *game = gamesIterator.next().value(); - if (game->containsUser(userName)) { - ServerInfo_Game gameInfo; - game->getInfo(gameInfo); - result.append(gameInfo); - } - } - return result; + QReadLocker locker(&gamesLock); + + QList result; + QMapIterator gamesIterator(games); + while (gamesIterator.hasNext()) { + Server_Game *game = gamesIterator.next().value(); + if (game->containsUser(userName)) { + ServerInfo_Game gameInfo; + game->getInfo(gameInfo); + result.append(gameInfo); + } + } + return result; } diff --git a/common/server_room.h b/common/server_room.h index 355d89d25..bf91510cb 100644 --- a/common/server_room.h +++ b/common/server_room.h @@ -24,58 +24,58 @@ class ResponseContainer; class Server_AbstractUserInterface; class Server_Room : public QObject { - Q_OBJECT + Q_OBJECT signals: - void roomInfoChanged(const ServerInfo_Room &roomInfo); - void gameListChanged(const ServerInfo_Game &gameInfo); + void roomInfoChanged(const ServerInfo_Room &roomInfo); + void gameListChanged(const ServerInfo_Game &gameInfo); private: - int id; - QString name; - QString description; - bool autoJoin; - QString joinMessage; - QStringList gameTypes; - QMap games; - QMap externalGames; - QMap users; - QMap externalUsers; + int id; + QString name; + QString description; + bool autoJoin; + QString joinMessage; + QStringList gameTypes; + QMap games; + QMap externalGames; + QMap users; + QMap externalUsers; private slots: - void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true); + void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true); public: - mutable QReadWriteLock usersLock; - mutable QReadWriteLock gamesLock; - Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); - ~Server_Room(); - int getId() const { return id; } - QString getName() const { return name; } - QString getDescription() const { return description; } - bool getAutoJoin() const { return autoJoin; } - QString getJoinMessage() const { return joinMessage; } - const QStringList &getGameTypes() const { return gameTypes; } - const QMap &getGames() const { return games; } - const QMap &getExternalGames() const { return externalGames; } - Server *getServer() const; - const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool includeExternalData = true) const; - int getGamesCreatedByUser(const QString &name) const; - QList getGamesOfUser(const QString &name) const; - - void addClient(Server_ProtocolHandler *client); - void removeClient(Server_ProtocolHandler *client); - - void addExternalUser(const ServerInfo_User &userInfo); - void removeExternalUser(const QString &name); - const QMap &getExternalUsers() const { return externalUsers; } - void updateExternalGameList(const ServerInfo_Game &gameInfo); - - Response::ResponseCode processJoinGameCommand(const Command_JoinGame &cmd, ResponseContainer &rc, Server_AbstractUserInterface *userInterface); - - void say(const QString &userName, const QString &s, bool sendToIsl = true); - - void addGame(Server_Game *game); - void removeGame(Server_Game *game); - - void sendRoomEvent(RoomEvent *event, bool sendToIsl = true); - RoomEvent *prepareRoomEvent(const ::google::protobuf::Message &roomEvent); + mutable QReadWriteLock usersLock; + mutable QReadWriteLock gamesLock; + Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); + ~Server_Room(); + int getId() const { return id; } + QString getName() const { return name; } + QString getDescription() const { return description; } + bool getAutoJoin() const { return autoJoin; } + QString getJoinMessage() const { return joinMessage; } + const QStringList &getGameTypes() const { return gameTypes; } + const QMap &getGames() const { return games; } + const QMap &getExternalGames() const { return externalGames; } + Server *getServer() const; + const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool includeExternalData = true) const; + int getGamesCreatedByUser(const QString &name) const; + QList getGamesOfUser(const QString &name) const; + + void addClient(Server_ProtocolHandler *client); + void removeClient(Server_ProtocolHandler *client); + + void addExternalUser(const ServerInfo_User &userInfo); + void removeExternalUser(const QString &name); + const QMap &getExternalUsers() const { return externalUsers; } + void updateExternalGameList(const ServerInfo_Game &gameInfo); + + Response::ResponseCode processJoinGameCommand(const Command_JoinGame &cmd, ResponseContainer &rc, Server_AbstractUserInterface *userInterface); + + void say(const QString &userName, const QString &s, bool sendToIsl = true); + + void addGame(Server_Game *game); + void removeGame(Server_Game *game); + + void sendRoomEvent(RoomEvent *event, bool sendToIsl = true); + RoomEvent *prepareRoomEvent(const ::google::protobuf::Message &roomEvent); }; #endif diff --git a/common/serverinfo_user_container.cpp b/common/serverinfo_user_container.cpp index deead4e04..14435f755 100644 --- a/common/serverinfo_user_container.cpp +++ b/common/serverinfo_user_container.cpp @@ -7,46 +7,46 @@ ServerInfo_User_Container::ServerInfo_User_Container(ServerInfo_User *_userInfo) } ServerInfo_User_Container::ServerInfo_User_Container(const ServerInfo_User &_userInfo) - : userInfo(new ServerInfo_User(_userInfo)) + : userInfo(new ServerInfo_User(_userInfo)) { } ServerInfo_User_Container::ServerInfo_User_Container(const ServerInfo_User_Container &other) { - if (other.userInfo) - userInfo = new ServerInfo_User(*other.userInfo); - else - userInfo = 0; + if (other.userInfo) + userInfo = new ServerInfo_User(*other.userInfo); + else + userInfo = 0; } ServerInfo_User_Container::~ServerInfo_User_Container() { - delete userInfo; + delete userInfo; } void ServerInfo_User_Container::setUserInfo(const ServerInfo_User &_userInfo) { - userInfo = new ServerInfo_User(_userInfo); + userInfo = new ServerInfo_User(_userInfo); } ServerInfo_User &ServerInfo_User_Container::copyUserInfo(ServerInfo_User &result, bool complete, bool internalInfo, bool sessionInfo) const { - if (userInfo) { - result.CopyFrom(*userInfo); - if (!sessionInfo) { - result.clear_session_id(); - result.clear_address(); - } - if (!internalInfo) - result.clear_id(); - if (!complete) - result.clear_avatar_bmp(); - } - return result; + if (userInfo) { + result.CopyFrom(*userInfo); + if (!sessionInfo) { + result.clear_session_id(); + result.clear_address(); + } + if (!internalInfo) + result.clear_id(); + if (!complete) + result.clear_avatar_bmp(); + } + return result; } ServerInfo_User ServerInfo_User_Container::copyUserInfo(bool complete, bool internalInfo, bool sessionInfo) const { - ServerInfo_User result; - return copyUserInfo(result, complete, internalInfo, sessionInfo); + ServerInfo_User result; + return copyUserInfo(result, complete, internalInfo, sessionInfo); } diff --git a/common/serverinfo_user_container.h b/common/serverinfo_user_container.h index 6cf87ab15..d7fb76f8f 100644 --- a/common/serverinfo_user_container.h +++ b/common/serverinfo_user_container.h @@ -5,16 +5,16 @@ class ServerInfo_User; class ServerInfo_User_Container { protected: - ServerInfo_User *userInfo; + ServerInfo_User *userInfo; public: - ServerInfo_User_Container(ServerInfo_User *_userInfo = 0); - ServerInfo_User_Container(const ServerInfo_User &_userInfo); - ServerInfo_User_Container(const ServerInfo_User_Container &other); - virtual ~ServerInfo_User_Container(); - ServerInfo_User *getUserInfo() const { return userInfo; } - void setUserInfo(const ServerInfo_User &_userInfo); - ServerInfo_User ©UserInfo(ServerInfo_User &result, bool complete, bool internalInfo = false, bool sessionInfo = false) const; - ServerInfo_User copyUserInfo(bool complete, bool internalInfo = false, bool sessionInfo = false) const; + ServerInfo_User_Container(ServerInfo_User *_userInfo = 0); + ServerInfo_User_Container(const ServerInfo_User &_userInfo); + ServerInfo_User_Container(const ServerInfo_User_Container &other); + virtual ~ServerInfo_User_Container(); + ServerInfo_User *getUserInfo() const { return userInfo; } + void setUserInfo(const ServerInfo_User &_userInfo); + ServerInfo_User ©UserInfo(ServerInfo_User &result, bool complete, bool internalInfo = false, bool sessionInfo = false) const; + ServerInfo_User copyUserInfo(bool complete, bool internalInfo = false, bool sessionInfo = false) const; }; #endif From 04e80304ef1f4754e760e39b2dcf06d5f6ffc8b1 Mon Sep 17 00:00:00 2001 From: arxanas Date: Tue, 8 Jul 2014 22:56:18 -0400 Subject: [PATCH 24/82] Fix #184: Deck loader reads 4x cardname correctly. --- common/decklist.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/decklist.cpp b/common/decklist.cpp index 8e877ba9a..52ef64156 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -547,14 +547,21 @@ bool DeckList::loadFromStream_Plain(QTextStream &in) line.remove(rx); line = line.simplified(); - int i = line.indexOf(' '); + int cardNameStart = i + 1; + + // If the count ends with an 'x', ignore it. For example, + // "4x Storm Crow" will count 4 correctly. + if (i > 0 && line[i - 1] == 'x') { + i--; + } + bool ok; int number = line.left(i).toInt(&ok); if (!ok) continue; - QString cardName = line.mid(i + 1); + QString cardName = line.mid(cardNameStart); // Common differences between cockatrice's card names // and what's commonly used in decklists rx.setPattern("’"); From 106e9d15383b96076ce89ea14f040c8bff1eac49 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 9 Jul 2014 09:43:01 +0200 Subject: [PATCH 25/82] Updated for new API * Use the new, shortest API m= * for a card, grab prices for all sets * use the lower price for a card from all sets, but avoid prices=0 (e.g.: some M15 cards have no price yet) --- cockatrice/src/priceupdater.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 1cf2b0642..98d3043db 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -126,14 +126,20 @@ void DBPriceUpdater::updatePrices() muidMap.clear(); CardInfo * card; int muid; + SetList sets; for (int i = 0; i < cards.size(); ++i) { card = db->getCard(cards[i], false); - muid=card->getPreferredMuId(); - q += (i ? "&" : "?") + QString("multiverseid=%1").arg(muid); - muidMap.insert(muid, cards[i]); + sets = card->getSets(); + for(int j = 0; j < sets.size(); ++j) + { + muid=card->getMuId(sets[j]->getShortName()); + //qDebug() << "muid " << muid << " card: " << cards[i] << endl; + q += (i ? "&" : "?") + QString("m=%1").arg(muid); + muidMap.insert(muid, cards[i]); + } } QUrl url(q); - + //qDebug() << "request prices from: " << url.toString() << endl; QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); } @@ -208,7 +214,7 @@ void DBPriceUpdater::downloadFinished() * physically exist. Also check the price to see that the cheapest set * ends up as the final price. */ - if (set != "MED" && (!cardsPrice.contains(name) || cardsPrice.value(name) > price)) + if (set != "MED" && price > 0 && (!cardsPrice.contains(name) || cardsPrice.value(name) > price)) cardsPrice.insert(name, price); } } From a6f299b2475748d9f46a9752a10eba433b9a4950 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 9 Jul 2014 17:40:50 +0200 Subject: [PATCH 26/82] fix #203 --- cockatrice/src/carditem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/carditem.cpp b/cockatrice/src/carditem.cpp index a6b582ad6..e7ce64821 100644 --- a/cockatrice/src/carditem.cpp +++ b/cockatrice/src/carditem.cpp @@ -233,7 +233,8 @@ CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPoin void CardItem::deleteDragItem() { - dragItem->deleteLater(); + if(dragItem) + dragItem->deleteLater(); dragItem = NULL; } From 86fa8f304da8ef07a128a8549b22e6aea8fa2aa3 Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Wed, 9 Jul 2014 19:06:31 -0400 Subject: [PATCH 27/82] Change client send-ping timer back to 1sec 9sec was causing too many timeout issues. Timeout from both client and server perspective needs to be revisited --- cockatrice/src/remoteclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index e0d3d8604..4f8deaed6 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -15,7 +15,7 @@ RemoteClient::RemoteClient(QObject *parent) : AbstractClient(parent), timeRunning(0), lastDataReceived(0), messageInProgress(false), handshakeStarted(false), messageLength(0) { timer = new QTimer(this); - timer->setInterval(9000); + timer->setInterval(1000); connect(timer, SIGNAL(timeout()), this, SLOT(ping())); socket = new QTcpSocket(this); From c07b53999ddf5563465bbe0aeb492a06bdd15b97 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 12 Jul 2014 00:11:48 +0200 Subject: [PATCH 28/82] Fix: ensure git is always run from the project directory When trying to obtain the version hash --- cmake/getversion.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/getversion.cmake b/cmake/getversion.cmake index 9db61040a..e434e992a 100644 --- a/cmake/getversion.cmake +++ b/cmake/getversion.cmake @@ -2,6 +2,7 @@ find_package(Git) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} describe --long --always + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID ) From 23b0a6c0dfd6d4a2055f87ae2a4e7efbe1d18bed Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 12 Jul 2014 01:38:00 +0200 Subject: [PATCH 29/82] Windows + CPack: fix packaging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Package the correct cpack’s temporary directory --- cmake/NSIS.definitions.nsh.in | 4 +--- cmake/NSIS.template.in | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/NSIS.definitions.nsh.in b/cmake/NSIS.definitions.nsh.in index 0d98b666f..cc4b014c0 100644 --- a/cmake/NSIS.definitions.nsh.in +++ b/cmake/NSIS.definitions.nsh.in @@ -1,3 +1 @@ -!define NSIS_PROJECT_NAME "@PROJECT_NAME@" -!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@" -!define NSIS_BINARY_PATH "@PROJECT_BINARY_DIR@" \ No newline at end of file +!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@" \ No newline at end of file diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index b2c60d118..c04169c8b 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -2,10 +2,11 @@ !include "MUI2.nsh" !include "FileFunc.nsh" -Name "${NSIS_PROJECT_NAME}" +Name "@CPACK_PACKAGE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" SetCompressor /SOLID lzma InstallDir "$PROGRAMFILES\Cockatrice" +!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" !define MUI_ABORTWARNING !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp" @@ -35,7 +36,8 @@ Section "Application" SecApplication SetShellVarContext all SetOutPath "$INSTDIR" - File /r "${NSIS_BINARY_PATH}\Release\*.*" + @CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@ + @CPACK_NSIS_FULL_INSTALL@ WriteUninstaller "$INSTDIR\uninstall.exe" ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 From 1137179b8828adc6a8e4d5aa8b3293c45b5eaf64 Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Sun, 13 Jul 2014 09:08:32 -0400 Subject: [PATCH 30/82] Add WITH_QT4 note to README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca8796b4f..61b44ed40 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,9 @@ To compile: The following flags can be passed to `cmake`: -- `-DWITH_SERVER=1` build the server -- `-DWITHOUT_CLIENT=1` do not build the client +- `-DWITH_SERVER=1` Build the server +- `-DWITHOUT_CLIENT=1` Do not build the client +- `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5. # Running From 88e6d0d3eb5419b0abe0fcf4046df296d62d4735 Mon Sep 17 00:00:00 2001 From: arxanas Date: Sat, 19 Jul 2014 14:28:46 -0400 Subject: [PATCH 31/82] Tabs to spaces. --- common/decklist.cpp | 870 ++++++++++++++++++++++---------------------- 1 file changed, 435 insertions(+), 435 deletions(-) diff --git a/common/decklist.cpp b/common/decklist.cpp index 90b5cb7e6..8e877ba9a 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -6,155 +6,155 @@ #include "decklist.h" SideboardPlan::SideboardPlan(const QString &_name, const QList &_moveList) - : name(_name), moveList(_moveList) + : name(_name), moveList(_moveList) { } void SideboardPlan::setMoveList(const QList &_moveList) { - moveList = _moveList; + moveList = _moveList; } bool SideboardPlan::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "name") - name = xml->readElementText(); - else if (childName == "move_card_to_zone") { - MoveCard_ToZone m; - while (!xml->atEnd()) { - xml->readNext(); - const QString childName2 = xml->name().toString(); - if (xml->isStartElement()) { - if (childName2 == "card_name") - m.set_card_name(xml->readElementText().toStdString()); - else if (childName2 == "start_zone") - m.set_start_zone(xml->readElementText().toStdString()); - else if (childName2 == "target_zone") - m.set_target_zone(xml->readElementText().toStdString()); - } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { - moveList.append(m); - break; - } - } - } - } else if (xml->isEndElement() && (childName == "sideboard_plan")) - return true; - } - return false; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "name") + name = xml->readElementText(); + else if (childName == "move_card_to_zone") { + MoveCard_ToZone m; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName2 = xml->name().toString(); + if (xml->isStartElement()) { + if (childName2 == "card_name") + m.set_card_name(xml->readElementText().toStdString()); + else if (childName2 == "start_zone") + m.set_start_zone(xml->readElementText().toStdString()); + else if (childName2 == "target_zone") + m.set_target_zone(xml->readElementText().toStdString()); + } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { + moveList.append(m); + break; + } + } + } + } else if (xml->isEndElement() && (childName == "sideboard_plan")) + return true; + } + return false; } void SideboardPlan::write(QXmlStreamWriter *xml) { - xml->writeStartElement("sideboard_plan"); - xml->writeTextElement("name", name); - for (int i = 0; i < moveList.size(); ++i) { - xml->writeStartElement("move_card_to_zone"); - xml->writeTextElement("card_name", QString::fromStdString(moveList[i].card_name())); - xml->writeTextElement("start_zone", QString::fromStdString(moveList[i].start_zone())); - xml->writeTextElement("target_zone", QString::fromStdString(moveList[i].target_zone())); - xml->writeEndElement(); - } - xml->writeEndElement(); + xml->writeStartElement("sideboard_plan"); + xml->writeTextElement("name", name); + for (int i = 0; i < moveList.size(); ++i) { + xml->writeStartElement("move_card_to_zone"); + xml->writeTextElement("card_name", QString::fromStdString(moveList[i].card_name())); + xml->writeTextElement("start_zone", QString::fromStdString(moveList[i].start_zone())); + xml->writeTextElement("target_zone", QString::fromStdString(moveList[i].target_zone())); + xml->writeEndElement(); + } + xml->writeEndElement(); } AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent) - : parent(_parent) + : parent(_parent) { - if (parent) - parent->append(this); + if (parent) + parent->append(this); } int AbstractDecklistNode::depth() const { - if (parent) - return parent->depth() + 1; - else - return 0; + if (parent) + return parent->depth() + 1; + else + return 0; } InnerDecklistNode::InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent) - : AbstractDecklistNode(_parent), name(other->getName()) + : AbstractDecklistNode(_parent), name(other->getName()) { - for (int i = 0; i < other->size(); ++i) { - InnerDecklistNode *inner = dynamic_cast(other->at(i)); - if (inner) - new InnerDecklistNode(inner, this); - else - new DecklistCardNode(dynamic_cast(other->at(i)), this); - } + for (int i = 0; i < other->size(); ++i) { + InnerDecklistNode *inner = dynamic_cast(other->at(i)); + if (inner) + new InnerDecklistNode(inner, this); + else + new DecklistCardNode(dynamic_cast(other->at(i)), this); + } } InnerDecklistNode::~InnerDecklistNode() { - clearTree(); + clearTree(); } QString InnerDecklistNode::visibleNameFromName(const QString &_name) { - if (_name == "main") - return QObject::tr("Maindeck"); - else if (_name == "side") - return QObject::tr("Sideboard"); - else if (_name == "tokens") - return QObject::tr("Tokens"); - else - return _name; + if (_name == "main") + return QObject::tr("Maindeck"); + else if (_name == "side") + return QObject::tr("Sideboard"); + else if (_name == "tokens") + return QObject::tr("Tokens"); + else + return _name; } void InnerDecklistNode::setSortMethod(DeckSortMethod method) { - sortMethod = method; - for (int i = 0; i < size(); i++) - at(i)->setSortMethod(method); + sortMethod = method; + for (int i = 0; i < size(); i++) + at(i)->setSortMethod(method); } QString InnerDecklistNode::getVisibleName() const { - return visibleNameFromName(name); + return visibleNameFromName(name); } void InnerDecklistNode::clearTree() { - for (int i = 0; i < size(); i++) - delete at(i); - clear(); + for (int i = 0; i < size(); i++) + delete at(i); + clear(); } DecklistCardNode::DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent) - : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), price(other->getPrice()) + : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), price(other->getPrice()) { } AbstractDecklistNode *InnerDecklistNode::findChild(const QString &name) { - for (int i = 0; i < size(); i++) - if (at(i)->getName() == name) - return at(i); - return 0; + for (int i = 0; i < size(); i++) + if (at(i)->getName() == name) + return at(i); + return 0; } int InnerDecklistNode::height() const { - return at(0)->height() + 1; + return at(0)->height() + 1; } int InnerDecklistNode::recursiveCount(bool countTotalCards) const { - int result = 0; - for (int i = 0; i < size(); i++) { - InnerDecklistNode *node = dynamic_cast(at(i)); - if (node) - result += node->recursiveCount(countTotalCards); - else if (countTotalCards) - result += dynamic_cast(at(i))->getNumber(); - else - result += 1; - } - return result; + int result = 0; + for (int i = 0; i < size(); i++) { + InnerDecklistNode *node = dynamic_cast(at(i)); + if (node) + result += node->recursiveCount(countTotalCards); + else if (countTotalCards) + result += dynamic_cast(at(i))->getNumber(); + else + result += 1; + } + return result; } float InnerDecklistNode::recursivePrice(bool countTotalCards) const @@ -172,275 +172,275 @@ float InnerDecklistNode::recursivePrice(bool countTotalCards) const bool InnerDecklistNode::compare(AbstractDecklistNode *other) const { - switch (sortMethod) { - case 0: - return compareNumber(other); - case 1: - return compareName(other); - case 2: - return comparePrice(other); - } + switch (sortMethod) { + case 0: + return compareNumber(other); + case 1: + return compareName(other); + case 2: + return comparePrice(other); + } return 0; } bool InnerDecklistNode::compareNumber(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - int n1 = recursiveCount(true); - int n2 = other2->recursiveCount(true); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + int n1 = recursiveCount(true); + int n2 = other2->recursiveCount(true); + return (n1 != n2) ? (n1 > n2) : compareName(other); + } else { + return false; + } } bool InnerDecklistNode::compareName(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + return (getName() > other2->getName()); + } else { + return false; + } } bool InnerDecklistNode::comparePrice(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - int p1 = 100*recursivePrice(true); - int p2 = 100*other2->recursivePrice(true); - return (p1 != p2) ? (p1 > p2) : compareName(other); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + int p1 = 100*recursivePrice(true); + int p2 = 100*other2->recursivePrice(true); + return (p1 != p2) ? (p1 > p2) : compareName(other); + } else { + return false; + } } bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const { - switch (sortMethod) { - case ByNumber: - return compareNumber(other); - case ByName: - return compareName(other); - case ByPrice: - return compareTotalPrice(other); - } + switch (sortMethod) { + case ByNumber: + return compareNumber(other); + case ByName: + return compareName(other); + case ByPrice: + return compareTotalPrice(other); + } return 0; } bool AbstractDecklistCardNode::compareNumber(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - int n1 = getNumber(); - int n2 = other2->getNumber(); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + int n1 = getNumber(); + int n2 = other2->getNumber(); + return (n1 != n2) ? (n1 > n2) : compareName(other); + } else { + return true; + } } bool AbstractDecklistCardNode::compareName(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + return (getName() > other2->getName()); + } else { + return true; + } } bool AbstractDecklistCardNode::compareTotalPrice(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - int p1 = 100*getTotalPrice(); - int p2 = 100*other2->getTotalPrice(); - return (p1 != p2) ? (p1 > p2) : compareName(other); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + int p1 = 100*getTotalPrice(); + int p2 = 100*other2->getTotalPrice(); + return (p1 != p2) ? (p1 > p2) : compareName(other); + } else { + return true; + } } class InnerDecklistNode::compareFunctor { private: - Qt::SortOrder order; + Qt::SortOrder order; public: - compareFunctor(Qt::SortOrder _order) : order(_order) { } - inline bool operator()(QPair a, QPair b) const - { - return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second)); - } + compareFunctor(Qt::SortOrder _order) : order(_order) { } + inline bool operator()(QPair a, QPair b) const + { + return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second)); + } }; bool InnerDecklistNode::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "zone") { - InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); - newZone->readElement(xml); - } else if (childName == "card") { - float price = (xml->attributes().value("price") != NULL) ? xml->attributes().value("price").toString().toFloat() : 0; - DecklistCardNode *newCard = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), price, this); - newCard->readElement(xml); - } - } else if (xml->isEndElement() && (childName == "zone")) - return false; - } - return true; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "zone") { + InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); + newZone->readElement(xml); + } else if (childName == "card") { + float price = (xml->attributes().value("price") != NULL) ? xml->attributes().value("price").toString().toFloat() : 0; + DecklistCardNode *newCard = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), price, this); + newCard->readElement(xml); + } + } else if (xml->isEndElement() && (childName == "zone")) + return false; + } + return true; } void InnerDecklistNode::writeElement(QXmlStreamWriter *xml) { - xml->writeStartElement("zone"); - xml->writeAttribute("name", name); - for (int i = 0; i < size(); i++) - at(i)->writeElement(xml); - xml->writeEndElement(); // zone + xml->writeStartElement("zone"); + xml->writeAttribute("name", name); + for (int i = 0; i < size(); i++) + at(i)->writeElement(xml); + xml->writeEndElement(); // zone } bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isEndElement() && xml->name() == "card") - return false; - } - return true; + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isEndElement() && xml->name() == "card") + return false; + } + return true; } void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml) { - xml->writeEmptyElement("card"); - xml->writeAttribute("number", QString::number(getNumber())); + xml->writeEmptyElement("card"); + xml->writeAttribute("number", QString::number(getNumber())); xml->writeAttribute("price", QString::number(getPrice())); - xml->writeAttribute("name", getName()); + xml->writeAttribute("name", getName()); } QVector > InnerDecklistNode::sort(Qt::SortOrder order) { - QVector > result(size()); - - // Initialize temporary list with contents of current list - QVector > tempList(size()); - for (int i = size() - 1; i >= 0; --i) { - tempList[i].first = i; - tempList[i].second = at(i); - } - - // Sort temporary list - compareFunctor cmp(order); - qSort(tempList.begin(), tempList.end(), cmp); - - // Map old indexes to new indexes and - // copy temporary list to the current one - for (int i = size() - 1; i >= 0; --i) { - result[i].first = tempList[i].first; - result[i].second = i; - replace(i, tempList[i].second); - } + QVector > result(size()); + + // Initialize temporary list with contents of current list + QVector > tempList(size()); + for (int i = size() - 1; i >= 0; --i) { + tempList[i].first = i; + tempList[i].second = at(i); + } + + // Sort temporary list + compareFunctor cmp(order); + qSort(tempList.begin(), tempList.end(), cmp); + + // Map old indexes to new indexes and + // copy temporary list to the current one + for (int i = size() - 1; i >= 0; --i) { + result[i].first = tempList[i].first; + result[i].second = i; + replace(i, tempList[i].second); + } - return result; + return result; } DeckList::DeckList() { - root = new InnerDecklistNode; + root = new InnerDecklistNode; } // TODO: http://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator DeckList::DeckList(const DeckList &other) - : name(other.name), - comments(other.comments), - deckHash(other.deckHash) + : name(other.name), + comments(other.comments), + deckHash(other.deckHash) { - root = new InnerDecklistNode(other.getRoot()); - - QMapIterator spIterator(other.getSideboardPlans()); - while (spIterator.hasNext()) { - spIterator.next(); - sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); - } - updateDeckHash(); + root = new InnerDecklistNode(other.getRoot()); + + QMapIterator spIterator(other.getSideboardPlans()); + while (spIterator.hasNext()) { + spIterator.next(); + sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); + } + updateDeckHash(); } DeckList::DeckList(const QString &nativeString) { - root = new InnerDecklistNode; - loadFromString_Native(nativeString); + root = new InnerDecklistNode; + loadFromString_Native(nativeString); } DeckList::~DeckList() { - delete root; - - QMapIterator i(sideboardPlans); - while (i.hasNext()) - delete i.next().value(); + delete root; + + QMapIterator i(sideboardPlans); + while (i.hasNext()) + delete i.next().value(); } QList DeckList::getCurrentSideboardPlan() { - SideboardPlan *current = sideboardPlans.value(QString(), 0); - if (!current) - return QList(); - else - return current->getMoveList(); + SideboardPlan *current = sideboardPlans.value(QString(), 0); + if (!current) + return QList(); + else + return current->getMoveList(); } void DeckList::setCurrentSideboardPlan(const QList &plan) { - SideboardPlan *current = sideboardPlans.value(QString(), 0); - if (!current) { - current = new SideboardPlan; - sideboardPlans.insert(QString(), current); - } - - current->setMoveList(plan); + SideboardPlan *current = sideboardPlans.value(QString(), 0); + if (!current) { + current = new SideboardPlan; + sideboardPlans.insert(QString(), current); + } + + current->setMoveList(plan); } bool DeckList::readElement(QXmlStreamReader *xml) { - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "deckname") - name = xml->readElementText(); - else if (childName == "comments") - comments = xml->readElementText(); - else if (childName == "zone") { - InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root); - newZone->readElement(xml); - } else if (childName == "sideboard_plan") { - SideboardPlan *newSideboardPlan = new SideboardPlan; - if (newSideboardPlan->readElement(xml)) - sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan); - else - delete newSideboardPlan; - } - } else if (xml->isEndElement() && (childName == "cockatrice_deck")) - return false; - return true; + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "deckname") + name = xml->readElementText(); + else if (childName == "comments") + comments = xml->readElementText(); + else if (childName == "zone") { + InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root); + newZone->readElement(xml); + } else if (childName == "sideboard_plan") { + SideboardPlan *newSideboardPlan = new SideboardPlan; + if (newSideboardPlan->readElement(xml)) + sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan); + else + delete newSideboardPlan; + } + } else if (xml->isEndElement() && (childName == "cockatrice_deck")) + return false; + return true; } void DeckList::write(QXmlStreamWriter *xml) { - xml->writeStartElement("cockatrice_deck"); - xml->writeAttribute("version", "1"); - xml->writeTextElement("deckname", name); - xml->writeTextElement("comments", comments); + xml->writeStartElement("cockatrice_deck"); + xml->writeAttribute("version", "1"); + xml->writeTextElement("deckname", name); + xml->writeTextElement("comments", comments); - for (int i = 0; i < root->size(); i++) - root->at(i)->writeElement(xml); - - QMapIterator i(sideboardPlans); - while (i.hasNext()) - i.next().value()->write(xml); - xml->writeEndElement(); + for (int i = 0; i < root->size(); i++) + root->at(i)->writeElement(xml); + + QMapIterator i(sideboardPlans); + while (i.hasNext()) + i.next().value()->write(xml); + xml->writeEndElement(); } bool DeckList::loadFromXml(QXmlStreamReader *xml) @@ -450,187 +450,187 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml) return false; } - cleanList(); - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isStartElement()) { - if (xml->name() != "cockatrice_deck") - return false; - while (!xml->atEnd()) { - xml->readNext(); - if (!readElement(xml)) - break; - } - } - } - updateDeckHash(); + cleanList(); + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isStartElement()) { + if (xml->name() != "cockatrice_deck") + return false; + while (!xml->atEnd()) { + xml->readNext(); + if (!readElement(xml)) + break; + } + } + } + updateDeckHash(); if (xml->error()) { qDebug() << "Error loading deck from xml: " << xml->errorString(); return false; } - return true; + return true; } bool DeckList::loadFromString_Native(const QString &nativeString) { - QXmlStreamReader xml(nativeString); - return loadFromXml(&xml); + QXmlStreamReader xml(nativeString); + return loadFromXml(&xml); } QString DeckList::writeToString_Native() { - QString result; - QXmlStreamWriter xml(&result); - xml.writeStartDocument(); - write(&xml); - xml.writeEndDocument(); - return result; + QString result; + QXmlStreamWriter xml(&result); + xml.writeStartDocument(); + write(&xml); + xml.writeEndDocument(); + return result; } bool DeckList::loadFromFile_Native(QIODevice *device) { - QXmlStreamReader xml(device); - return loadFromXml(&xml); + QXmlStreamReader xml(device); + return loadFromXml(&xml); } bool DeckList::saveToFile_Native(QIODevice *device) { - QXmlStreamWriter xml(device); - xml.setAutoFormatting(true); - xml.writeStartDocument(); + QXmlStreamWriter xml(device); + xml.setAutoFormatting(true); + xml.writeStartDocument(); - write(&xml); + write(&xml); - xml.writeEndDocument(); - return true; + xml.writeEndDocument(); + return true; } bool DeckList::loadFromStream_Plain(QTextStream &in) { - cleanList(); + cleanList(); - InnerDecklistNode *main = 0, *side = 0; - bool inSideboard = false; + InnerDecklistNode *main = 0, *side = 0; + bool inSideboard = false; - int okRows = 0; - while (!in.atEnd()) { - QString line = in.readLine().simplified(); - if (line.startsWith("//")) - continue; + int okRows = 0; + while (!in.atEnd()) { + QString line = in.readLine().simplified(); + if (line.startsWith("//")) + continue; - InnerDecklistNode *zone; - if (line.startsWith("Sideboard", Qt::CaseInsensitive)) { - inSideboard = true; - continue; - } else if (line.startsWith("SB:", Qt::CaseInsensitive)) { - line = line.mid(3).trimmed(); - if (!side) - side = new InnerDecklistNode("side", root); - zone = side; - } else if (inSideboard) { - if (!side) - side = new InnerDecklistNode("side", root); - zone = side; - } else { - if (!main) - main = new InnerDecklistNode("main", root); - zone = main; - } + InnerDecklistNode *zone; + if (line.startsWith("Sideboard", Qt::CaseInsensitive)) { + inSideboard = true; + continue; + } else if (line.startsWith("SB:", Qt::CaseInsensitive)) { + line = line.mid(3).trimmed(); + if (!side) + side = new InnerDecklistNode("side", root); + zone = side; + } else if (inSideboard) { + if (!side) + side = new InnerDecklistNode("side", root); + zone = side; + } else { + if (!main) + main = new InnerDecklistNode("main", root); + zone = main; + } - // Filter out MWS edition symbols and basic land extras - QRegExp rx("\\[.*\\]"); - line.remove(rx); - rx.setPattern("\\(.*\\)"); - line.remove(rx); - //Filter out post card name editions - rx.setPattern("\\|.*$"); - line.remove(rx); - line = line.simplified(); + // Filter out MWS edition symbols and basic land extras + QRegExp rx("\\[.*\\]"); + line.remove(rx); + rx.setPattern("\\(.*\\)"); + line.remove(rx); + //Filter out post card name editions + rx.setPattern("\\|.*$"); + line.remove(rx); + line = line.simplified(); - int i = line.indexOf(' '); - bool ok; - int number = line.left(i).toInt(&ok); - if (!ok) - continue; + int i = line.indexOf(' '); + bool ok; + int number = line.left(i).toInt(&ok); + if (!ok) + continue; - QString cardName = line.mid(i + 1); + QString cardName = line.mid(i + 1); // Common differences between cockatrice's card names // and what's commonly used in decklists - rx.setPattern("’"); + rx.setPattern("’"); cardName.replace(rx, "'"); - rx.setPattern("Æ"); - cardName.replace(rx, "AE"); - rx.setPattern("^Aether"); - cardName.replace(rx, "AEther"); - rx.setPattern("\\s*[&|/]{1,2}\\s*"); - cardName.replace(rx, " // "); + rx.setPattern("Æ"); + cardName.replace(rx, "AE"); + rx.setPattern("^Aether"); + cardName.replace(rx, "AEther"); + rx.setPattern("\\s*[&|/]{1,2}\\s*"); + cardName.replace(rx, " // "); - ++okRows; - new DecklistCardNode(cardName, number, zone); - } - updateDeckHash(); - return (okRows > 0); + ++okRows; + new DecklistCardNode(cardName, number, zone); + } + updateDeckHash(); + return (okRows > 0); } bool DeckList::loadFromFile_Plain(QIODevice *device) { - QTextStream in(device); - return loadFromStream_Plain(in); + QTextStream in(device); + return loadFromStream_Plain(in); } bool DeckList::saveToStream_Plain(QTextStream &out) { - // Support for this is only possible if the internal structure doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); - } - } - return true; + // Support for this is only possible if the internal structure doesn't get more complicated. + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); + } + } + return true; } bool DeckList::saveToFile_Plain(QIODevice *device) { - QTextStream out(device); - return saveToStream_Plain(out); + QTextStream out(device); + return saveToStream_Plain(out); } QString DeckList::writeToString_Plain() { - QString result; - QTextStream out(&result); - saveToStream_Plain(out); - return result; + QString result; + QTextStream out(&result); + saveToStream_Plain(out); + return result; } void DeckList::cleanList() { - root->clearTree(); - setName(); - setComments(); - deckHash = QString(); - emit deckHashChanged(); + root->clearTree(); + setName(); + setComments(); + deckHash = QString(); + emit deckHashChanged(); } void DeckList::getCardListHelper(InnerDecklistNode *item, QSet &result) const { - for (int i = 0; i < item->size(); ++i) { - DecklistCardNode *node = dynamic_cast(item->at(i)); - if (node) - result.insert(node->getName()); - else - getCardListHelper(dynamic_cast(item->at(i)), result); - } + for (int i = 0; i < item->size(); ++i) { + DecklistCardNode *node = dynamic_cast(item->at(i)); + if (node) + result.insert(node->getName()); + else + getCardListHelper(dynamic_cast(item->at(i)), result); + } } QStringList DeckList::getCardList() const { - QSet result; - getCardListHelper(root, result); - return result.toList(); + QSet result; + getCardListHelper(root, result); + return result.toList(); } int DeckList::getSideboardSize() const @@ -640,7 +640,7 @@ int DeckList::getSideboardSize() const InnerDecklistNode *node = dynamic_cast(root->at(i)); if (node->getName() != "side") continue; - for (int j = 0; j < node->size(); j++) { + for (int j = 0; j < node->size(); j++) { DecklistCardNode *card = dynamic_cast(node->at(j)); size += card->getNumber(); } @@ -650,65 +650,65 @@ int DeckList::getSideboardSize() const DecklistCardNode *DeckList::addCard(const QString &cardName, const QString &zoneName) { - InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); - if (!zoneNode) - zoneNode = new InnerDecklistNode(zoneName, root); + InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); + if (!zoneNode) + zoneNode = new InnerDecklistNode(zoneName, root); - DecklistCardNode *node = new DecklistCardNode(cardName, 1, zoneNode); - updateDeckHash(); - return node; + DecklistCardNode *node = new DecklistCardNode(cardName, 1, zoneNode); + updateDeckHash(); + return node; } bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode) { - if (node == root) - return true; - bool updateHash = false; - if (!rootNode) { - rootNode = root; - updateHash = true; - } - - int index = rootNode->indexOf(node); - if (index != -1) { - delete rootNode->takeAt(index); - if (!rootNode->size()) - deleteNode(rootNode, rootNode->getParent()); - if (updateHash) - updateDeckHash(); - return true; - } - for (int i = 0; i < rootNode->size(); i++) { - InnerDecklistNode *inner = dynamic_cast(rootNode->at(i)); - if (inner) - if (deleteNode(node, inner)) { - if (updateHash) - updateDeckHash(); - return true; - } - } - return false; + if (node == root) + return true; + bool updateHash = false; + if (!rootNode) { + rootNode = root; + updateHash = true; + } + + int index = rootNode->indexOf(node); + if (index != -1) { + delete rootNode->takeAt(index); + if (!rootNode->size()) + deleteNode(rootNode, rootNode->getParent()); + if (updateHash) + updateDeckHash(); + return true; + } + for (int i = 0; i < rootNode->size(); i++) { + InnerDecklistNode *inner = dynamic_cast(rootNode->at(i)); + if (inner) + if (deleteNode(node, inner)) { + if (updateHash) + updateDeckHash(); + return true; + } + } + return false; } void DeckList::updateDeckHash() { - QStringList cardList; - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - for (int k = 0; k < card->getNumber(); ++k) - cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); - } - } - cardList.sort(); - QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); - quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) - + (((quint64) (unsigned char) deckHashArray[1]) << 24) - + (((quint64) (unsigned char) deckHashArray[2] << 16)) - + (((quint64) (unsigned char) deckHashArray[3]) << 8) - + (quint64) (unsigned char) deckHashArray[4]; - deckHash = QString::number(number, 32).rightJustified(8, '0'); - - emit deckHashChanged(); + QStringList cardList; + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + for (int k = 0; k < card->getNumber(); ++k) + cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); + } + } + cardList.sort(); + QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); + quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) + + (((quint64) (unsigned char) deckHashArray[1]) << 24) + + (((quint64) (unsigned char) deckHashArray[2] << 16)) + + (((quint64) (unsigned char) deckHashArray[3]) << 8) + + (quint64) (unsigned char) deckHashArray[4]; + deckHash = QString::number(number, 32).rightJustified(8, '0'); + + emit deckHashChanged(); } From 0eb97c91b38f0096cc32044c4e5973d76155cfb9 Mon Sep 17 00:00:00 2001 From: arxanas Date: Sat, 19 Jul 2014 14:47:19 -0400 Subject: [PATCH 32/82] Fix #183: Deck parser doesn't too eagerly replace ampersands. --- common/decklist.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/common/decklist.cpp b/common/decklist.cpp index 8e877ba9a..f28083370 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -563,9 +563,20 @@ bool DeckList::loadFromStream_Plain(QTextStream &in) cardName.replace(rx, "AE"); rx.setPattern("^Aether"); cardName.replace(rx, "AEther"); - rx.setPattern("\\s*[&|/]{1,2}\\s*"); + rx.setPattern("\\s*[|/]{1,2}\\s*"); cardName.replace(rx, " // "); + // Replace only if the ampersand is preceded by a non-capital letter, + // as would happen with acronyms. So 'Fire & Ice' is replaced but not + // 'R&D' or 'R & D'. + // + // Qt regexes don't support lookbehind so we capture and replace + // instead. + rx.setPattern("([^A-Z])\\s*&\\s*"); + if (rx.indexIn(cardName) != -1) { + cardName.replace(rx, QString("%1 // ").arg(rx.cap(1))); + } + ++okRows; new DecklistCardNode(cardName, number, zone); } From 4df46b13e6ea9ee13b7854eb1f0ab8f9d5fd8f8d Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Sat, 19 Jul 2014 18:27:44 -0400 Subject: [PATCH 33/82] Removed cmake from Travis's OS X dependencies. --- travis-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 0c6c62e65..10af11cd0 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -2,7 +2,7 @@ if [[ $TRAVIS_OS_NAME == "osx" ]] ; then brew update - brew install qt cmake protobuf libgcrypt + brew install qt protobuf libgcrypt else sudo apt-get update -qq sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev From aff32e0f567a7e07664bf3e31c6a06506d2ffeaf Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Sun, 20 Jul 2014 21:09:58 -0400 Subject: [PATCH 34/82] Ignore diacritics as well. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So [card]jotun grunt[/card] works as well as [card]Jötun Grunt[/card]. Also I removed some debugging information I accidentally left in. --- cockatrice/src/carddatabase.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index c55148aa0..5083e6edf 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -436,6 +436,10 @@ int CardInfo::getPreferredMuId() QString CardInfo::simplifyName(const QString &name) { QString simpleName(name); + + // Replace Jötun Grunt with Jotun Grunt. + simpleName = simpleName.normalized(QString::NormalizationForm_KD); + simpleName.remove(QRegExp("[^a-zA-Z0-9 ]")); simpleName = simpleName.toLower(); return simpleName; @@ -553,8 +557,6 @@ CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { QString simpleName = CardInfo::simplifyName(cardName); - qDebug() << "Getting card by name " << simpleName << "\n"; - qDebug() << "Cards available: " << simpleNameCards.size() << "\n"; return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); } From 2a5aa06881326423decdea3326f37afb9e537b2c Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Tue, 22 Jul 2014 20:58:38 -0400 Subject: [PATCH 35/82] =?UTF-8?q?=C3=86=20now=20actually=20works=20with=20?= =?UTF-8?q?the=20case-insensitive=20tags.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cockatrice/src/carddatabase.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 5083e6edf..df4496100 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -437,6 +437,11 @@ int CardInfo::getPreferredMuId() QString CardInfo::simplifyName(const QString &name) { QString simpleName(name); + // So Aetherling would work, but not Ætherling since 'Æ' would get replaced + // with nothing. + simpleName.replace("æ", "ae"); + simpleName.replace("Æ", "AE"); + // Replace Jötun Grunt with Jotun Grunt. simpleName = simpleName.normalized(QString::NormalizationForm_KD); From 68ac8d1a334644b3881fbb29048aeadd1fbcc81a Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Wed, 23 Jul 2014 15:21:46 -0400 Subject: [PATCH 36/82] Fix #225: Multiples of cards are now sent to deckstats. --- cockatrice/src/deckstats_interface.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/deckstats_interface.cpp b/cockatrice/src/deckstats_interface.cpp index c70624bfd..6bdae12f6 100644 --- a/cockatrice/src/deckstats_interface.cpp +++ b/cockatrice/src/deckstats_interface.cpp @@ -88,7 +88,11 @@ struct CopyIfNotAToken { const DecklistCardNode *card ) const { if (!cardDatabase.getCard(card->getName())->getIsToken()) { - destination.addCard(card->getName(), node->getName()); + DecklistCardNode *addedCard = destination.addCard( + card->getName(), + node->getName() + ); + addedCard->setNumber(card->getNumber()); } } }; From bf92e670022d21cacfcea4e11b077303d1c161ec Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Jul 2014 22:11:26 +0200 Subject: [PATCH 37/82] Ensure the whole table gets refreshed when updating prices --- cockatrice/src/decklistmodel.cpp | 10 ++-------- cockatrice/src/decklistmodel.h | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index ec10ff053..c55af0135 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -472,13 +472,7 @@ void DeckListModel::printDeckList(QPrinter *printer) doc.print(printer); } -void DeckListModel::pricesUpdated(InnerDecklistNode *node) +void DeckListModel::pricesUpdated() { - if (!node) - node = root; - - if (node->isEmpty()) - return; - - emit dataChanged(createIndex(0, 2, node->at(0)), createIndex(node->size() - 1, 2, node->last())); + emit layoutChanged(); } diff --git a/cockatrice/src/decklistmodel.h b/cockatrice/src/decklistmodel.h index 2873c8184..f4e93e3bb 100644 --- a/cockatrice/src/decklistmodel.h +++ b/cockatrice/src/decklistmodel.h @@ -51,7 +51,7 @@ public: void cleanList(); DeckLoader *getDeckList() const { return deckList; } void setDeckList(DeckLoader *_deck); - void pricesUpdated(InnerDecklistNode *node = 0); + void pricesUpdated(); private: DeckLoader *deckList; InnerDecklistNode *root; From b40abfa1adcca92adb20e07223dfa153e2e570e2 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Jul 2014 23:00:58 +0200 Subject: [PATCH 38/82] Split price retrieval on multiple request --- cockatrice/src/priceupdater.cpp | 73 +++++++++++++++++++++++++++------ cockatrice/src/priceupdater.h | 4 +- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 98d3043db..1a84880d6 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -121,12 +121,15 @@ DBPriceUpdater::DBPriceUpdater(const DeckList *_deck) */ void DBPriceUpdater::updatePrices() { - QString q = "https://api.deckbrew.com/mtg/cards"; + QString base = "https://api.deckbrew.com/mtg/cards", q = ""; QStringList cards = deck->getCardList(); muidMap.clear(); + urls.clear(); CardInfo * card; int muid; SetList sets; + bool bNotFirst=false; + for (int i = 0; i < cards.size(); ++i) { card = db->getCard(cards[i], false); sets = card->getSets(); @@ -134,11 +137,35 @@ void DBPriceUpdater::updatePrices() { muid=card->getMuId(sets[j]->getShortName()); //qDebug() << "muid " << muid << " card: " << cards[i] << endl; - q += (i ? "&" : "?") + QString("m=%1").arg(muid); + if(bNotFirst) + { + q += QString("&m=%1").arg(muid); + } else { + q += QString("?m=%1").arg(muid); + bNotFirst = true; + } muidMap.insert(muid, cards[i]); } + + if(q.length() > 240) + { + urls.append(base + q); + bNotFirst=false; + q = ""; + } } - QUrl url(q); + if(q.length() > 0) + urls.append(base + q); + + requestNext(); +} + +void DBPriceUpdater::requestNext() +{ + if(urls.empty()) + return; + + QUrl url(urls.takeFirst(), QUrl::TolerantMode); //qDebug() << "request prices from: " << url.toString() << endl; QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); @@ -158,8 +185,13 @@ void DBPriceUpdater::downloadFinished() if (!ok) { QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); reply->deleteLater(); - deleteLater(); - return; + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } if(resultMap.contains("errors")) @@ -173,8 +205,13 @@ void DBPriceUpdater::downloadFinished() #endif ); reply->deleteLater(); - deleteLater(); - return; + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } // Good results are a list @@ -182,8 +219,13 @@ void DBPriceUpdater::downloadFinished() if (!ok) { QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); reply->deleteLater(); - deleteLater(); - return; + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } QMap cardsPrice; @@ -226,11 +268,18 @@ void DBPriceUpdater::downloadFinished() DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); if (!currentCard) continue; - currentCard->setPrice(cardsPrice[currentCard->getName()]); + float price = cardsPrice[currentCard->getName()]; + if(price > 0) + currentCard->setPrice(price); } } reply->deleteLater(); - deleteLater(); - emit finishedUpdate(); + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index 076d9663d..afe582b65 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -1,7 +1,7 @@ #ifndef PRICEUPDATER_H #define PRICEUPDATER_H -#include +#include #include "decklist.h" class QNetworkAccessManager; @@ -46,8 +46,10 @@ class DBPriceUpdater : public AbstractPriceUpdater Q_OBJECT protected: MuidStringMap muidMap; + QList urls; protected: virtual void downloadFinished(); + void requestNext(); public: DBPriceUpdater(const DeckList *deck); virtual void updatePrices(); From 49b3568b53fe4d1060eb9b4b543c37b7a4fcad86 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Jul 2014 23:10:48 +0200 Subject: [PATCH 39/82] small fix: moving the url length check in the inner loop --- cockatrice/src/priceupdater.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 1a84880d6..2a1a012e5 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -145,13 +145,13 @@ void DBPriceUpdater::updatePrices() bNotFirst = true; } muidMap.insert(muid, cards[i]); - } - if(q.length() > 240) - { - urls.append(base + q); - bNotFirst=false; - q = ""; + if(q.length() > 240) + { + urls.append(base + q); + bNotFirst=false; + q = ""; + } } } if(q.length() > 0) From f32a999b4eba5692c2ff6b6f280f10d7d84a94f8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 15:38:36 +0200 Subject: [PATCH 40/82] Remove unused variable --- cockatrice/src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index b7e2c0ea4..df38bbf64 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -136,7 +136,6 @@ int main(int argc, char *argv[]) qsrand(QDateTime::currentDateTime().toTime_t()); - bool startMainProgram = true; #if QT_VERSION < 0x050000 const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); #else From 036980eb44ce0351a31cb2bbe91f42632dc265ec Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 17:37:54 +0200 Subject: [PATCH 41/82] ISO C++ forbids variable length array --- servatrice/src/passwordhasher.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/servatrice/src/passwordhasher.cpp b/servatrice/src/passwordhasher.cpp index 785034b43..0d8cda36e 100644 --- a/servatrice/src/passwordhasher.cpp +++ b/servatrice/src/passwordhasher.cpp @@ -18,12 +18,15 @@ QString PasswordHasher::computeHash(const QString &password, const QString &salt QByteArray passwordBuffer = (salt + password).toUtf8(); int hashLen = gcry_md_get_algo_dlen(algo); - char hash[hashLen], tmp[hashLen]; + char *hash = new char[hashLen], *tmp = new char[hashLen]; gcry_md_hash_buffer(algo, hash, passwordBuffer.data(), passwordBuffer.size()); for (int i = 1; i < rounds; ++i) { memcpy(tmp, hash, hashLen); gcry_md_hash_buffer(algo, hash, tmp, hashLen); } - return salt + QString(QByteArray(hash, hashLen).toBase64()); + QString hashedPass = salt + QString(QByteArray(hash, hashLen).toBase64()); + delete[] tmp; + delete[] hash; + return hashedPass; } From 6c7ba0950a340dd801533d0a557b63da0683fdf7 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:00:20 +0200 Subject: [PATCH 42/82] Fix warning for DeskList copy constructors Using pointers for classes derived from QObject would be the best idea. --- common/decklist.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/decklist.cpp b/common/decklist.cpp index af2f62701..0b2558b5d 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -355,7 +355,8 @@ DeckList::DeckList() // TODO: http://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator DeckList::DeckList(const DeckList &other) - : name(other.name), + : QObject(), + name(other.name), comments(other.comments), deckHash(other.deckHash) { From 3102dd60f92d4665ef004b0f1ab2d482d6d6a65b Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:09:28 +0200 Subject: [PATCH 43/82] Hide unused parameters in common/ to avoid warnings --- common/server.h | 2 +- common/server_database_interface.h | 20 ++++++++++---------- common/server_player.cpp | 2 +- common/server_protocolhandler.h | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/common/server.h b/common/server.h index 6742f6e2b..275891cc3 100644 --- a/common/server.h +++ b/common/server.h @@ -97,7 +97,7 @@ protected slots: void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); void externalResponseReceived(const Response &resp, qint64 sessionId); - virtual void doSendIslMessage(const IslMessage &msg, int serverId) { } + virtual void doSendIslMessage(const IslMessage & /* msg */, int /* serverId */) { } protected: void prepareDestroy(); void setDatabaseInterface(Server_DatabaseInterface *_databaseInterface); diff --git a/common/server_database_interface.h b/common/server_database_interface.h index df75cd63a..798ae9d92 100644 --- a/common/server_database_interface.h +++ b/common/server_database_interface.h @@ -12,18 +12,18 @@ public: : QObject(parent) { } virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0; - virtual bool userExists(const QString &user) { return false; } - virtual QMap getBuddyList(const QString &name) { return QMap(); } - virtual QMap getIgnoreList(const QString &name) { return QMap(); } - virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; } - virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; } + virtual bool userExists(const QString & /* user */) { return false; } + virtual QMap getBuddyList(const QString & /* name */) { return QMap(); } + virtual QMap getIgnoreList(const QString & /* name */) { return QMap(); } + virtual bool isInBuddyList(const QString & /* whoseList */, const QString & /* who */) { return false; } + virtual bool isInIgnoreList(const QString & /* whoseList */, const QString & /* who */) { return false; } virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; - virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList) { } - virtual DeckList *getDeckFromDatabase(int deckId, int userId) { return 0; } + virtual void storeGameInformation(const QString & /* roomName */, const QStringList & /* roomGameTypes */, const ServerInfo_Game & /* gameInfo */, const QSet & /* allPlayersEver */, const QSet & /* allSpectatorsEver */, const QList & /* replayList */) { } + virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; } - virtual qint64 startSession(const QString &userName, const QString &address) { return 0; } + virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; } public slots: - virtual void endSession(qint64 sessionId) { } + virtual void endSession(qint64 /* sessionId */ ) { } public: virtual int getNextGameId() = 0; virtual int getNextReplayId() = 0; @@ -31,7 +31,7 @@ public: virtual void clearSessionTables() { } virtual void lockSessionTables() { } virtual void unlockSessionTables() { } - virtual bool userSessionExists(const QString &userName) { return false; } + virtual bool userSessionExists(const QString & /* userName */) { return false; } }; #endif diff --git a/common/server_player.cpp b/common/server_player.cpp index c25151e8d..b9277a2d4 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -1594,7 +1594,7 @@ Response::ResponseCode Server_Player::cmdRevealCards(const Command_RevealCards & return Response::RespOk; } -Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges) +Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer & /* rc */, GameEventStorage &ges) { Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name())); if (!zone) diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 0cea37ae3..98e0f6cc2 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -49,7 +49,7 @@ protected: AuthenticationResult authState; bool acceptsUserListChanges; bool acceptsRoomListChanges; - virtual void logDebugMessage(const QString &message) { } + virtual void logDebugMessage(const QString & /* message */) { } private: QList messageSizeOverTime, messageCountOverTime; int timeRunning, lastDataReceived; @@ -71,13 +71,13 @@ private: Response::ResponseCode cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc); Response::ResponseCode processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } + virtual Response::ResponseCode processExtendedSessionCommand(int /* cmdType */, const SessionCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; } Response::ResponseCode processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } + virtual Response::ResponseCode processExtendedModeratorCommand(int /* cmdType */, const ModeratorCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; } Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc); - virtual Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } + virtual Response::ResponseCode processExtendedAdminCommand(int /* cmdType */, const AdminCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; } private slots: void pingClockTimeout(); public slots: From 13bd35e020ef2508ab442a3fb5f02e875b76cdac Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:16:08 +0200 Subject: [PATCH 44/82] Servatrice: add prototypes for local functions --- servatrice/src/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index 37b750f4c..c5bf9cde6 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -37,6 +37,23 @@ RNG_Abstract *rng; ServerLogger *logger; QThread *loggerThread; +/* Prototypes */ + +void testRNG(); +void testHash(); +#if QT_VERSION < 0x050000 +void myMessageOutput(QtMsgType type, const char *msg); +void myMessageOutput2(QtMsgType type, const char *msg); +#else +void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg); +void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg); +#endif +#ifdef Q_OS_UNIX +void sigSegvHandler(int sig); +#endif + +/* Implementations */ + void testRNG() { const int n = 500000; From d9577f0d6c0ce8ae2d737a44acf2f316c986130f Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:27:02 +0200 Subject: [PATCH 45/82] Hide unused parameters in cockatrice/ to avoid warnings --- cockatrice/src/dlg_edit_tokens.cpp | 2 +- cockatrice/src/filtertree.h | 6 +++--- cockatrice/src/localserver.cpp | 2 +- cockatrice/src/replay_timeline_widget.cpp | 2 +- cockatrice/src/tab_replays.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cockatrice/src/dlg_edit_tokens.cpp b/cockatrice/src/dlg_edit_tokens.cpp index 28fefa7df..e2de2cd4b 100644 --- a/cockatrice/src/dlg_edit_tokens.cpp +++ b/cockatrice/src/dlg_edit_tokens.cpp @@ -113,7 +113,7 @@ DlgEditTokens::DlgEditTokens(CardDatabaseModel *_cardDatabaseModel, QWidget *par setWindowTitle(tr("Edit tokens")); } -void DlgEditTokens::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous) +void DlgEditTokens::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /* previous */) { const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current); CardInfo *cardInfo = current.row() >= 0 ? cardDatabaseModel->getCard(realIndex.row()) : cardDatabaseModel->getDatabase()->getCard(); diff --git a/cockatrice/src/filtertree.h b/cockatrice/src/filtertree.h index a6c4536d6..3229d93b4 100644 --- a/cockatrice/src/filtertree.h +++ b/cockatrice/src/filtertree.h @@ -18,10 +18,10 @@ public: virtual void enable() { enabled = true; nodeChanged(); } virtual void disable() { enabled = false; nodeChanged(); } virtual FilterTreeNode *parent() const { return NULL; } - virtual FilterTreeNode *nodeAt(int i) const { return NULL; } - virtual void deleteAt(int i) {} + virtual FilterTreeNode *nodeAt(int /* i */) const { return NULL; } + virtual void deleteAt(int /* i */) {} virtual int childCount() const { return 0; } - virtual int childIndex(const FilterTreeNode *node) const { return -1; } + virtual int childIndex(const FilterTreeNode * /* node */) const { return -1; } virtual int index() const { return (parent() != NULL)? parent()->childIndex(this) : -1; } virtual QString text() const { return QString(textCStr()); } virtual bool isLeaf() const { return false; } diff --git a/cockatrice/src/localserver.cpp b/cockatrice/src/localserver.cpp index dc49078aa..a7eda3d1f 100644 --- a/cockatrice/src/localserver.cpp +++ b/cockatrice/src/localserver.cpp @@ -33,7 +33,7 @@ ServerInfo_User LocalServer_DatabaseInterface::getUserData(const QString &name, return result; } -AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) +AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, QString & /* reasonStr */, int & /* secondsLeft */) { return UnknownUser; } diff --git a/cockatrice/src/replay_timeline_widget.cpp b/cockatrice/src/replay_timeline_widget.cpp index 6127489f7..fb3efc1dc 100644 --- a/cockatrice/src/replay_timeline_widget.cpp +++ b/cockatrice/src/replay_timeline_widget.cpp @@ -43,7 +43,7 @@ void ReplayTimelineWidget::setTimeline(const QList &_replayTimeline) update(); } -void ReplayTimelineWidget::paintEvent(QPaintEvent *event) +void ReplayTimelineWidget::paintEvent(QPaintEvent * /* event */) { QPainter painter(this); painter.drawRect(0, 0, width() - 1, height() - 1); diff --git a/cockatrice/src/tab_replays.cpp b/cockatrice/src/tab_replays.cpp index 6b3a62102..00a7f9c0c 100644 --- a/cockatrice/src/tab_replays.cpp +++ b/cockatrice/src/tab_replays.cpp @@ -202,7 +202,7 @@ void TabReplays::actDownload() client->sendCommand(pend); } -void TabReplays::downloadFinished(const Response &r, const CommandContainer &commandContainer, const QVariant &extraData) +void TabReplays::downloadFinished(const Response &r, const CommandContainer & /* commandContainer */, const QVariant &extraData) { if (r.response_code() != Response::RespOk) return; From bd97d7b9336d59a02fcb979145433457421004af Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:40:25 +0200 Subject: [PATCH 46/82] Fix signed/unsigned comparision --- cockatrice/src/tab_game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index b99fff020..715c75f71 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -254,7 +254,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) // Create list: event number -> time [ms] // Distribute simultaneous events evenly across 1 second. - int lastEventTimestamp = -1; + unsigned int lastEventTimestamp = 0; const int eventCount = replay->event_list_size(); for (int i = 0; i < eventCount; ++i) { int j = i + 1; From b10331fe66fc82a75eb39a60df298242949336dd Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:40:56 +0200 Subject: [PATCH 47/82] Hide unused parameters in oracle/ to avoid warnings --- oracle/src/oraclewizard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 96b781459..2a597d456 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -383,7 +383,7 @@ void SaveSetsPage::initializePage() QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); } -void SaveSetsPage::updateTotalProgress(int cardsImported, int setIndex, const QString &setName) +void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) { if (setName.isEmpty()) { messageLog->append("" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + ""); From 0f501762e04c9698845a9831620de955d592f626 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:44:19 +0200 Subject: [PATCH 48/82] Reorder items initialization to match the order in class declaration --- cockatrice/src/tab_supervisor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index dda918a04..166a766ab 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -76,7 +76,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/) } TabSupervisor::TabSupervisor(AbstractClient *_client, QWidget *parent) - : QTabWidget(parent), userInfo(0), client(_client), tabUserLists(0), tabServer(0), tabDeckStorage(0), tabAdmin(0), tabReplays(0) + : QTabWidget(parent), userInfo(0), client(_client), tabServer(0), tabUserLists(0), tabDeckStorage(0), tabReplays(0), tabAdmin(0) { tabChangedIcon = new QIcon(":/resources/icon_tab_changed.svg"); setElideMode(Qt::ElideRight); From 3fda31073f66c79b3a3111b3a067c5e2a5c238aa Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Jul 2014 18:54:45 +0200 Subject: [PATCH 49/82] Declare useless virtual dtor for polymorphic classes --- cockatrice/src/filtertree.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/filtertree.h b/cockatrice/src/filtertree.h index 3229d93b4..d945e5679 100644 --- a/cockatrice/src/filtertree.h +++ b/cockatrice/src/filtertree.h @@ -48,7 +48,7 @@ class FilterTreeBranch : public FilterTreeNode { protected: QList childNodes; public: - ~FilterTreeBranch(); + virtual ~FilterTreeBranch(); FilterTreeNode *nodeAt(int i) const; void deleteAt(int i); int childCount() const { return childNodes.size(); } @@ -102,6 +102,7 @@ public: FilterItem(QString trm, FilterItemList *parent) : p(parent), term(trm) {} + virtual ~FilterItem() {}; CardFilter::Attr attr() const { return p->attr(); } CardFilter::Type type() const { return p->type; } From fe9f7a22b1dd5b60dc1c57649bdf92d261d7d6a0 Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Thu, 24 Jul 2014 19:53:05 -0400 Subject: [PATCH 50/82] Fix issue mentioned in #152: price updater loads all cards now. --- cockatrice/src/priceupdater.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 2a1a012e5..89ff66c08 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -136,6 +136,11 @@ void DBPriceUpdater::updatePrices() for(int j = 0; j < sets.size(); ++j) { muid=card->getMuId(sets[j]->getShortName()); + + if (!muid) { + continue; + } + //qDebug() << "muid " << muid << " card: " << cards[i] << endl; if(bNotFirst) { From 1f0ba0ea084c575d5e8c261b3928f7beb08d6f0d Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 25 Jul 2014 17:32:12 +0200 Subject: [PATCH 51/82] Debug compilation: check flags actually supported by the compiler --- CMakeLists.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 825ed5cb5..d0d405913 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,8 +76,19 @@ IF(MSVC) #set(CMAKE_CXX_FLAGS_DEBUG "/Zi") ELSEIF (CMAKE_COMPILER_IS_GNUCXX) # linux/gcc, bsd/gcc, windows/mingw + include(CheckCXXCompilerFlag) + set(CMAKE_CXX_FLAGS_RELEASE "-s -O2") - set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -pedantic -Werror -Wcast-align -Wmissing-declarations -Winline -Wno-long-long -Wno-error=extra -Wno-error=unused-parameter -Wno-inline -Wno-error=delete-non-virtual-dtor -Wno-error=sign-compare -Wno-error=reorder -Wno-error=missing-declarations") + set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -pedantic -Werror") + + set(ADDITIONAL_DEBUG_FLAGS -Wcast-align -Wmissing-declarations -Winline -Wno-long-long -Wno-error=extra -Wno-error=unused-parameter -Wno-inline -Wno-error=delete-non-virtual-dtor -Wno-error=sign-compare -Wno-error=reorder -Wno-error=missing-declarations) + + FOREACH(FLAG ${ADDITIONAL_DEBUG_FLAGS}) + CHECK_CXX_COMPILER_FLAG("${FLAG}" CXX_HAS_WARNING_${FLAG}) + IF(CXX_HAS_WARNING_${FLAG}) + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAG}") + ENDIF() + ENDFOREACH() ELSE() # other: osx/llvm, bsd/llvm set(CMAKE_CXX_FLAGS_RELEASE "-O2") From aa225f8df7bf4d3cbc776cca19c8b2a7b54fb273 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 26 Jul 2014 23:36:51 +0200 Subject: [PATCH 52/82] Add back support for custom picture urls in cards.xml --- cockatrice/src/carddatabase.cpp | 34 ++++++++++++++++++++++++++++++--- cockatrice/src/carddatabase.h | 7 +++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 51120ee03..e216c10f4 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -153,9 +153,18 @@ QString PictureLoader::getPicUrl(CardInfo *card) { if (!picDownload) return 0; - QString picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); - picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); + // first check if a custom card url exists in cards.xml CardSet *set = card->getPreferredSet(); + QString picUrl = picDownloadHq ? + card->getCustomPicURLHq(set->getShortName()) : + card->getCustomPicURL(set->getShortName()); + + if (!picUrl.isEmpty()) + return picUrl; + + // otherwise, fallback to the default url + picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); + picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(card->getPreferredMuId()))); @@ -275,6 +284,8 @@ CardInfo::CardInfo(CardDatabase *_db, bool _cipt, int _tableRow, const SetList &_sets, + const QStringMap &_customPicURLs, + const QStringMap &_customPicURLsHq, MuidMap _muIds) : db(_db), name(_name), @@ -286,6 +297,8 @@ CardInfo::CardInfo(CardDatabase *_db, text(_text), colors(_colors), loyalty(_loyalty), + customPicURLs(_customPicURLs), + customPicURLsHq(_customPicURLsHq), muIds(_muIds), cipt(_cipt), tableRow(_tableRow), @@ -464,6 +477,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) tmpSet=sets[i]->getShortName(); xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet))); + tmpString = info->getCustomPicURL(tmpSet); + if(!tmpString.isEmpty()) + xml.writeAttribute("picURL", tmpString); + + tmpString = info->getCustomPicURLHq(tmpSet); + if(!tmpString.isEmpty()) + xml.writeAttribute("picURLHq", tmpString); + xml.writeCharacters(tmpSet); xml.writeEndElement(); } @@ -628,6 +649,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) if (xml.name() == "card") { QString name, manacost, type, pt, text; QStringList colors; + QStringMap customPicURLs, customPicURLsHq; MuidMap muids; SetList sets; int tableRow = 0; @@ -654,6 +676,12 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) if (attrs.hasAttribute("muId")) { muids[setName] = attrs.value("muId").toString().toInt(); } + if (attrs.hasAttribute("picURL")) { + customPicURLs[setName] = attrs.value("picURL").toString(); + } + if (attrs.hasAttribute("picURLHq")) { + customPicURLsHq[setName] = attrs.value("picURLHq").toString(); + } } else if (xml.name() == "color") colors << xml.readElementText(); else if (xml.name() == "tablerow") @@ -665,7 +693,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) else if (xml.name() == "token") isToken = xml.readElementText().toInt(); } - addCard(new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids)); + addCard(new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); } } } diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 3d1da9a2b..bb151e7d0 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -110,6 +110,7 @@ private: QString text; QStringList colors; int loyalty; + QStringMap customPicURLs, customPicURLsHq; MuidMap muIds; bool cipt; int tableRow; @@ -128,6 +129,8 @@ public: bool _cipt = false, int _tableRow = 0, const SetList &_sets = SetList(), + const QStringMap &_customPicURLs = QStringMap(), + const QStringMap &_customPicURLsHq = QStringMap(), MuidMap muids = MuidMap()); ~CardInfo(); const QString &getName() const { return name; } @@ -145,12 +148,16 @@ public: void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } const QStringList &getColors() const { return colors; } + QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } + QString getCustomPicURLHq(const QString &set) const { return customPicURLsHq.value(set); } int getMuId(const QString &set) const { return muIds.value(set); } QString getMainCardType() const; QString getCorrectedName() const; int getTableRow() const { return tableRow; } void setTableRow(int _tableRow) { tableRow = _tableRow; } void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } + void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); } + void setCustomPicURLHq(const QString &_set, const QString &_customPicURL) { customPicURLsHq.insert(_set, _customPicURL); } void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); } void addToSet(CardSet *set); QPixmap *loadPixmap(); From fe12f71b93fe377cea5a7055395fe637ae316326 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 27 Jul 2014 00:26:12 +0200 Subject: [PATCH 53/82] Disable some usercontextmenu actions if the user is offline; fix #234 --- cockatrice/src/playerlistwidget.cpp | 2 +- cockatrice/src/user_context_menu.cpp | 7 ++++--- cockatrice/src/user_context_menu.h | 2 +- cockatrice/src/userlist.cpp | 5 +++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cockatrice/src/playerlistwidget.cpp b/cockatrice/src/playerlistwidget.cpp index ee0882039..b535c2c1c 100644 --- a/cockatrice/src/playerlistwidget.cpp +++ b/cockatrice/src/playerlistwidget.cpp @@ -178,5 +178,5 @@ void PlayerListWidget::showContextMenu(const QPoint &pos, const QModelIndex &ind int playerId = index.sibling(index.row(), 4).data(Qt::UserRole + 1).toInt(); UserLevelFlags userLevel(index.sibling(index.row(), 3).data(Qt::UserRole).toInt()); - userContextMenu->showContextMenu(pos, userName, userLevel, playerId); + userContextMenu->showContextMenu(pos, userName, userLevel, true, playerId); } diff --git a/cockatrice/src/user_context_menu.cpp b/cockatrice/src/user_context_menu.cpp index eab48f47d..03ccdd375 100644 --- a/cockatrice/src/user_context_menu.cpp +++ b/cockatrice/src/user_context_menu.cpp @@ -102,7 +102,7 @@ void UserContextMenu::banUser_dialogFinished() client->sendCommand(client->prepareModeratorCommand(cmd)); } -void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, int playerId) +void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, bool online, int playerId) { aUserName->setText(userName); @@ -132,8 +132,9 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName menu->addAction(aBan); } bool anotherUser = userName != QString::fromStdString(tabSupervisor->getUserInfo()->name()); - aChat->setEnabled(anotherUser); - aShowGames->setEnabled(anotherUser); + aDetails->setEnabled(online); + aChat->setEnabled(anotherUser && online); + aShowGames->setEnabled(anotherUser && online); aAddToBuddyList->setEnabled(anotherUser); aRemoveFromBuddyList->setEnabled(anotherUser); aAddToIgnoreList->setEnabled(anotherUser); diff --git a/cockatrice/src/user_context_menu.h b/cockatrice/src/user_context_menu.h index 8e6708b0d..9aeadfd4a 100644 --- a/cockatrice/src/user_context_menu.h +++ b/cockatrice/src/user_context_menu.h @@ -36,7 +36,7 @@ private slots: public: UserContextMenu(const TabSupervisor *_tabSupervisor, QWidget *_parent, TabGame *_game = 0); void retranslateUi(); - void showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, int playerId = -1); + void showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, bool online = true, int playerId = -1); }; #endif diff --git a/cockatrice/src/userlist.cpp b/cockatrice/src/userlist.cpp index 29d13781c..c6174a6e5 100644 --- a/cockatrice/src/userlist.cpp +++ b/cockatrice/src/userlist.cpp @@ -311,8 +311,9 @@ void UserList::userClicked(QTreeWidgetItem *item, int /*column*/) void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) { const ServerInfo_User &userInfo = static_cast(userTree->topLevelItem(index.row()))->getUserInfo(); - - userContextMenu->showContextMenu(pos, QString::fromStdString(userInfo.name()), UserLevelFlags(userInfo.user_level())); + bool online = index.sibling(index.row(), 0).data(Qt::UserRole + 1).toBool(); + + userContextMenu->showContextMenu(pos, QString::fromStdString(userInfo.name()), UserLevelFlags(userInfo.user_level()), online); } void UserList::sortItems() From 689acc1e32ad4dd61729e1b2e677b06bd5bfb751 Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Sat, 26 Jul 2014 21:13:30 -0400 Subject: [PATCH 54/82] Fix #110: Price column is removed when the setting is disabled. --- cockatrice/src/tab_deck_editor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index e402ea118..c01389316 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -646,6 +646,7 @@ void TabDeckEditor::actDecrement() void TabDeckEditor::setPriceTagFeatureEnabled(int enabled) { aUpdatePrices->setVisible(enabled); + deckModel->pricesUpdated(); } void TabDeckEditor::actUpdatePrices() From c3a68fcbd36c3dca4927664f60d914fd107667c1 Mon Sep 17 00:00:00 2001 From: pliu037 Date: Sun, 27 Jul 2014 12:47:05 -0400 Subject: [PATCH 55/82] Cleaned up some lines of code --- cockatrice/src/carddatabase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 98e506b5b..f3a0c72f8 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -129,7 +129,7 @@ void PictureLoader::processLoadQueue() QString setName = ptl.getSetName(); QImage image; - if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg("CUSTOM").arg(correctedName))) + if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg("CUSTOM").arg(correctedName))) { if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) //if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1))) if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { @@ -143,8 +143,8 @@ void PictureLoader::processLoadQueue() else emit imageLoaded(ptl.getCard(), QImage()); } - continue; } + } emit imageLoaded(ptl.getCard(), image); } From 6b9daf3d15bea2fe8e18ff1c5a98b7ecc7ebb325 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 27 Jul 2014 21:29:27 +0200 Subject: [PATCH 56/82] Enable Show this user's games" for offline users --- cockatrice/src/user_context_menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/user_context_menu.cpp b/cockatrice/src/user_context_menu.cpp index 03ccdd375..df3246535 100644 --- a/cockatrice/src/user_context_menu.cpp +++ b/cockatrice/src/user_context_menu.cpp @@ -134,7 +134,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName bool anotherUser = userName != QString::fromStdString(tabSupervisor->getUserInfo()->name()); aDetails->setEnabled(online); aChat->setEnabled(anotherUser && online); - aShowGames->setEnabled(anotherUser && online); + aShowGames->setEnabled(anotherUser); aAddToBuddyList->setEnabled(anotherUser); aRemoveFromBuddyList->setEnabled(anotherUser); aAddToIgnoreList->setEnabled(anotherUser); From 9e12c5b2d44caf3f02beee8bb81bfea07331e92d Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 27 Jul 2014 22:06:35 +0200 Subject: [PATCH 57/82] Use custom picUrl if picUrlHq is not defined --- cockatrice/src/carddatabase.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index e216c10f4..f77b98d54 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -153,12 +153,18 @@ QString PictureLoader::getPicUrl(CardInfo *card) { if (!picDownload) return 0; - // first check if a custom card url exists in cards.xml CardSet *set = card->getPreferredSet(); - QString picUrl = picDownloadHq ? - card->getCustomPicURLHq(set->getShortName()) : - card->getCustomPicURL(set->getShortName()); + QString picUrl = QString(""); + // first check if Hq is enabled and a custom Hq card url exists in cards.xml + if(picDownloadHq) + { + picUrl = card->getCustomPicURLHq(set->getShortName()); + if (!picUrl.isEmpty()) + return picUrl; + } + // then, test for a custom, non-Hq card url in cards.xml + picUrl = card->getCustomPicURL(set->getShortName()); if (!picUrl.isEmpty()) return picUrl; From 951e43b166c1876c2b40eaf3b0084995f4dfdae9 Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Wed, 30 Jul 2014 15:47:57 -0400 Subject: [PATCH 58/82] Update cmake build flags in README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 61b44ed40..b28967739 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,10 @@ To compile: The following flags can be passed to `cmake`: - `-DWITH_SERVER=1` Build the server -- `-DWITHOUT_CLIENT=1` Do not build the client +- `-DWITH_CLIENT=0` Do not build the client +- `-DWITH_ORACLE=0` Do not build Oracle - `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5. +- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings. # Running From c1b7d71dcac59984377eb77df210b0c17381e7bc Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 31 Jul 2014 17:53:39 +0200 Subject: [PATCH 59/82] Added schema for cards.xml --- doc/cards.xsd | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 doc/cards.xsd diff --git a/doc/cards.xsd b/doc/cards.xsd new file mode 100644 index 000000000..a08408bd3 --- /dev/null +++ b/doc/cards.xsd @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b2e1855af7415fd529bbbd4805b3915fed9ebdda Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 31 Jul 2014 18:40:10 +0200 Subject: [PATCH 60/82] Fix: can appear more than once --- doc/cards.xsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cards.xsd b/doc/cards.xsd index a08408bd3..d10032a7e 100644 --- a/doc/cards.xsd +++ b/doc/cards.xsd @@ -34,7 +34,7 @@ - + From 077d9f2745f212889d83533ecbb70bccc0687305 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 31 Jul 2014 17:30:15 -0400 Subject: [PATCH 61/82] Checks if CUSTOM folder was successfully created. If not, prints qDebug message and proceeds as normal except uses default card images instead of custom ones. --- cockatrice/src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index df38bbf64..d53462d03 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -158,7 +158,9 @@ int main(int argc, char *argv[]) QDir().mkpath(dataDir + "/pics"); settingsCache->setPicsPath(dataDir + "/pics"); } - + if (!QDir().mkpath(settingsCache->getPicsPath() + "/CUSTOM")) + qDebug("Could not create " settingsCache->getPicsPath() + "/CUSTOM. Will fall back on default card images."); + #ifdef Q_OS_MAC if(settingsCache->getHandBgPath().isEmpty() && settingsCache->getStackBgPath().isEmpty() && From b3fa99ef36a53e3be101d9127f849fa648350a2b Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 31 Jul 2014 23:04:30 -0400 Subject: [PATCH 62/82] I was too hasty and forgot a + and to toLatin1() a QString in the qDebug arg. --- cockatrice/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index d53462d03..1cda35de3 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -159,7 +159,7 @@ int main(int argc, char *argv[]) settingsCache->setPicsPath(dataDir + "/pics"); } if (!QDir().mkpath(settingsCache->getPicsPath() + "/CUSTOM")) - qDebug("Could not create " settingsCache->getPicsPath() + "/CUSTOM. Will fall back on default card images."); + qDebug("Could not create " + settingsCache->getPicsPath().toLatin1() + "/CUSTOM. Will fall back on default card images."); #ifdef Q_OS_MAC if(settingsCache->getHandBgPath().isEmpty() && From 309494111c68272bc03594f106486dbdd39a5503 Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Sat, 2 Aug 2014 19:32:24 -0400 Subject: [PATCH 63/82] Fix #248: the token dialog generates cards irrespective of their case and punctation. --- cockatrice/src/player.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index d260cbea5..d8d21fd91 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -959,6 +959,10 @@ void Player::actCreateToken() return; lastTokenName = dlg.getName(); + if (CardInfo *correctedCard = db->getCardBySimpleName(lastTokenName, false)) { + lastTokenName = correctedCard->getName(); + } + lastTokenColor = dlg.getColor(); lastTokenPT = dlg.getPT(); lastTokenAnnotation = dlg.getAnnotation(); From a9eaf65a826cf22c9156cc5cb21e49c42fc56d0b Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Sat, 2 Aug 2014 23:44:31 -0400 Subject: [PATCH 64/82] Changed .toLatin1() to .toUtf8(). --- cockatrice/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 1cda35de3..a6608ac9c 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -159,7 +159,7 @@ int main(int argc, char *argv[]) settingsCache->setPicsPath(dataDir + "/pics"); } if (!QDir().mkpath(settingsCache->getPicsPath() + "/CUSTOM")) - qDebug("Could not create " + settingsCache->getPicsPath().toLatin1() + "/CUSTOM. Will fall back on default card images."); + qDebug("Could not create " + settingsCache->getPicsPath().toUtf8() + "/CUSTOM. Will fall back on default card images."); #ifdef Q_OS_MAC if(settingsCache->getHandBgPath().isEmpty() && From 3667ab504cf50c51ab3d6640e8233eba67bec13c Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Mon, 4 Aug 2014 20:09:53 +0200 Subject: [PATCH 65/82] Fix compilation warning introduced in #246 /cockatrice/src/main.cpp:162:16: warning: format string is not a string literal (potentially insecure) [-Wformat-security] qDebug("Could not create " + settingsCache->getPicsPath().toUtf8() + "/CUSTOM. Will fall back on default card images."); --- cockatrice/src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index a6608ac9c..7e0eb3fa4 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "main.h" #include "window_main.h" @@ -159,7 +160,7 @@ int main(int argc, char *argv[]) settingsCache->setPicsPath(dataDir + "/pics"); } if (!QDir().mkpath(settingsCache->getPicsPath() + "/CUSTOM")) - qDebug("Could not create " + settingsCache->getPicsPath().toUtf8() + "/CUSTOM. Will fall back on default card images."); + qDebug() << "Could not create " + settingsCache->getPicsPath().toUtf8() + "/CUSTOM. Will fall back on default card images."; #ifdef Q_OS_MAC if(settingsCache->getHandBgPath().isEmpty() && From 387086cb4c1bd409ac2ee4eac8b1ded891710cd5 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Mon, 4 Aug 2014 15:45:49 -0400 Subject: [PATCH 66/82] Added support for PNG images (both for downloading/saving and loading). --- cockatrice/src/carddatabase.cpp | 46 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index f3bcc14a4..f7296f7d6 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -129,22 +129,24 @@ void PictureLoader::processLoadQueue() QString setName = ptl.getSetName(); QImage image; - if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg("CUSTOM").arg(correctedName))) { - if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) - //if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1))) - if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { - if (picDownload) { - cardsToDownload.append(ptl); - if (!downloadRunning) - startNextPicDownload(); - } else { - if (ptl.nextSet()) - loadQueue.prepend(ptl); - else - emit imageLoaded(ptl.getCard(), QImage()); - } - } - } + //Supports loading JPG and PNG images; default is JPG + if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg("CUSTOM").arg(correctedName))) + if (!image.load(QString("%1/%2/%3.full.png").arg(picsPath).arg("CUSTOM").arg(correctedName))) + if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) + if (!image.load(QString("%1/%2/%3.full.png").arg(picsPath).arg(setName).arg(correctedName))) + if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) + if (!image.load(QString("%1/%2/%3/%4.full.png").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { + if (picDownload) { + cardsToDownload.append(ptl); + if (!downloadRunning) + startNextPicDownload(); + } else { + if (ptl.nextSet()) + loadQueue.prepend(ptl); + else + emit imageLoaded(ptl.getCard(), QImage()); + } + } emit imageLoaded(ptl.getCard(), image); } @@ -226,11 +228,13 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) if (!cardBeingDownloaded.getStripped()) suffix = ".full"; - QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + ".jpg"); - if (!newPic.open(QIODevice::WriteOnly)) - return; - newPic.write(picData); - newPic.close(); + //Supports JPG and PNG images; default is JPG + QString extension = ".jpg"; + if (picData.left(8) == QByteArray::fromHex("89504E470D0A1A0A")) + extension = ".png"; + + if (!testImage.save(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension)) + qDebug() << picsPath.toUtf8() + "/downloadedPics/" + cardBeingDownloaded.getSetName().toUtf8() + "/" + cardBeingDownloaded.getCard()->getCorrectedName().toUtf8() + suffix.toUtf8() + extension.toUtf8() + "was not successfully saved."; emit imageLoaded(cardBeingDownloaded.getCard(), testImage); } else if (cardBeingDownloaded.getHq()) { From 75122c3c9d7cf874ff94ce09ed5c1be6db6f177e Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Mon, 4 Aug 2014 17:01:58 -0400 Subject: [PATCH 67/82] Switched back to using QFile instead of QImage for saving as QImage's save function adds overhead to the file size, leaving me unsure whether the original image is affected. --- cockatrice/src/carddatabase.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index f7296f7d6..583f339f3 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -233,8 +233,11 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) if (picData.left(8) == QByteArray::fromHex("89504E470D0A1A0A")) extension = ".png"; - if (!testImage.save(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension)) - qDebug() << picsPath.toUtf8() + "/downloadedPics/" + cardBeingDownloaded.getSetName().toUtf8() + "/" + cardBeingDownloaded.getCard()->getCorrectedName().toUtf8() + suffix.toUtf8() + extension.toUtf8() + "was not successfully saved."; + QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension); + if (!newPic.open(QIODevice::WriteOnly)) + return; + newPic.write(picData); + newPic.close(); emit imageLoaded(cardBeingDownloaded.getCard(), testImage); } else if (cardBeingDownloaded.getHq()) { From 8587b8c3492e4fa7d3690d22bf62e7d9fea51ccf Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Tue, 5 Aug 2014 03:48:30 -0400 Subject: [PATCH 68/82] Refactored the magic bytes into two QLists: one containing a list of QStrings representing the supported extensions, the other containing a list of QByteArrays representing the magic bytes in hex. Refactored the image loading loop to be two nested for loops: the outer loop iterating through the QList of paths to folders in which to search for images and the inner loop iterating through the QList of supported extensions. --- cockatrice/src/carddatabase.cpp | 68 +++++++++++++++++++++------------ cockatrice/src/carddatabase.h | 2 + 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 583f339f3..a088975ca 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -16,6 +16,10 @@ const int CardDatabase::versionNeeded = 3; +//Specifies image formats and their associated signature bytes (expected first x bytes, in hex) +const QList PictureLoader::imgFormats = QList() << ".png"; +const QList PictureLoader::imgSignatures = QList() << "89504E470D0A1A0A"; + static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSet *set) { xml.writeStartElement("set"); @@ -124,31 +128,42 @@ void PictureLoader::processLoadQueue() } PictureToLoad ptl = loadQueue.takeFirst(); mutex.unlock(); - QString correctedName = ptl.getCard()->getCorrectedName(); - QString picsPath = _picsPath; - QString setName = ptl.getSetName(); + + //The list of paths to the folders in which to search for images + QList picsPaths = QList() << _picsPath + "/CUSTOM/" + ptl.getCard()->getCorrectedName() + ".full" + << _picsPath + "/" + ptl.getSetName() + "/" + ptl.getCard()->getCorrectedName() + ".full" + << _picsPath + "/downloadedPics/" + ptl.getSetName() + "/" + ptl.getCard()->getCorrectedName() + ".full"; QImage image; - //Supports loading JPG and PNG images; default is JPG - if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg("CUSTOM").arg(correctedName))) - if (!image.load(QString("%1/%2/%3.full.png").arg(picsPath).arg("CUSTOM").arg(correctedName))) - if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) - if (!image.load(QString("%1/%2/%3.full.png").arg(picsPath).arg(setName).arg(correctedName))) - if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) - if (!image.load(QString("%1/%2/%3/%4.full.png").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { - if (picDownload) { - cardsToDownload.append(ptl); - if (!downloadRunning) - startNextPicDownload(); - } else { - if (ptl.nextSet()) - loadQueue.prepend(ptl); - else - emit imageLoaded(ptl.getCard(), QImage()); - } - } + QString extension; + bool found; + int i, j; + + //Iterates through the list of paths, iterating through imgFormats in each to find the image in any supported format; default is JPG + for (found = false, i = 0; i < picsPaths.length() && !found; i ++) + for (extension = ".jpg", j = 0; !found; j ++) { + //qDebug() << picsPaths.at(i) + extension; + if (image.load(picsPaths.at(i) + extension)) { + emit imageLoaded(ptl.getCard(), image); + found = true; + } + if (j >= imgFormats.length()) + break; + extension = imgFormats.at(j); + } - emit imageLoaded(ptl.getCard(), image); + if (!found) { + if (picDownload) { + cardsToDownload.append(ptl); + if (!downloadRunning) + startNextPicDownload(); + } else { + if (ptl.nextSet()) + loadQueue.prepend(ptl); + else + emit imageLoaded(ptl.getCard(), QImage()); + } + } } } @@ -228,10 +243,13 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) if (!cardBeingDownloaded.getStripped()) suffix = ".full"; - //Supports JPG and PNG images; default is JPG + //Supports JPG and formats specified in imgFormats; default is JPG QString extension = ".jpg"; - if (picData.left(8) == QByteArray::fromHex("89504E470D0A1A0A")) - extension = ".png"; + for (int i = 0; i < imgFormats.length(); i ++) + if (picData.left(QByteArray::fromHex(imgSignatures.at(i)).length()) == QByteArray::fromHex(imgSignatures.at(i))) { + extension = imgFormats.at(i); + break; + } QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension); if (!newPic.open(QIODevice::WriteOnly)) diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index bb151e7d0..6ffa6b0e7 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -73,6 +73,8 @@ private: bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning; void startNextPicDownload(); QString getPicUrl(CardInfo* card); + static const QList imgFormats; + static const QList imgSignatures; public: PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0); ~PictureLoader(); From 4bb1d28ae710b29a4d64bc9834f010b8ff2ede06 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Tue, 5 Aug 2014 05:40:51 -0400 Subject: [PATCH 69/82] Removed the QLists for determining image format. Instead, using QImageReader, both when downloading/saving and when loading, to determine the correct format (Cockatrice now supports all QImageReader-supported formats). Image loading still uses one for loop to iterate through the QList of paths to folders in which to search for images. --- cockatrice/src/carddatabase.cpp | 60 +++++++++++++-------------------- cockatrice/src/carddatabase.h | 2 -- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index a088975ca..8bedf71e4 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -13,13 +13,10 @@ #include #include #include +#include const int CardDatabase::versionNeeded = 3; -//Specifies image formats and their associated signature bytes (expected first x bytes, in hex) -const QList PictureLoader::imgFormats = QList() << ".png"; -const QList PictureLoader::imgSignatures = QList() << "89504E470D0A1A0A"; - static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSet *set) { xml.writeStartElement("set"); @@ -135,22 +132,17 @@ void PictureLoader::processLoadQueue() << _picsPath + "/downloadedPics/" + ptl.getSetName() + "/" + ptl.getCard()->getCorrectedName() + ".full"; QImage image; - QString extension; - bool found; - int i, j; + bool found = false; - //Iterates through the list of paths, iterating through imgFormats in each to find the image in any supported format; default is JPG - for (found = false, i = 0; i < picsPaths.length() && !found; i ++) - for (extension = ".jpg", j = 0; !found; j ++) { - //qDebug() << picsPaths.at(i) + extension; - if (image.load(picsPaths.at(i) + extension)) { - emit imageLoaded(ptl.getCard(), image); - found = true; - } - if (j >= imgFormats.length()) - break; - extension = imgFormats.at(j); + //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension + for (int i = 0; i < picsPaths.length() && !found; i ++) { + QImageReader imgReader; + imgReader.setFileName(picsPaths.at(i)); + if (imgReader.read(&image)) { + emit imageLoaded(ptl.getCard(), image); + found = true; } + } if (!found) { if (picDownload) { @@ -225,32 +217,26 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) qDebug() << "Download failed:" << reply->errorString(); } - const QByteArray &picData = reply->readAll(); + const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader QImage testImage; - if (testImage.loadFromData(picData)) { - if (!QDir(QString(picsPath + "/downloadedPics/")).exists()) { - QDir dir(picsPath); - if (!dir.exists()) - return; - dir.mkdir("downloadedPics"); - } - if (!QDir(QString(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName())).exists()) { - QDir dir(QString(picsPath + "/downloadedPics")); - dir.mkdir(cardBeingDownloaded.getSetName()); + + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + imgReader.setDevice(reply); + QString extension = "." + imgReader.format(); //the format is determined prior to reading the QImageReader data into a QImage object, as that wipes the QImageReader buffer + if (extension == ".jpeg") + extension = ".jpg"; + + if (imgReader.read(&testImage)) { + if (!QDir().mkpath(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName())) { + qDebug() << picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + " could not be created."; + return; } QString suffix; if (!cardBeingDownloaded.getStripped()) suffix = ".full"; - //Supports JPG and formats specified in imgFormats; default is JPG - QString extension = ".jpg"; - for (int i = 0; i < imgFormats.length(); i ++) - if (picData.left(QByteArray::fromHex(imgSignatures.at(i)).length()) == QByteArray::fromHex(imgSignatures.at(i))) { - extension = imgFormats.at(i); - break; - } - QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension); if (!newPic.open(QIODevice::WriteOnly)) return; diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 6ffa6b0e7..bb151e7d0 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -73,8 +73,6 @@ private: bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning; void startNextPicDownload(); QString getPicUrl(CardInfo* card); - static const QList imgFormats; - static const QList imgSignatures; public: PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0); ~PictureLoader(); From 6502a182e825745edb1db2a32fad45b9660473c0 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 6 Aug 2014 09:31:22 +0200 Subject: [PATCH 70/82] Support loading of files with wrong extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like M15’s .png that cockatrice wrongly saved as .jpg Additionally, move the QImageReader declaration outside of the inner loop --- cockatrice/src/carddatabase.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 8bedf71e4..b3d4bca98 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -132,11 +132,12 @@ void PictureLoader::processLoadQueue() << _picsPath + "/downloadedPics/" + ptl.getSetName() + "/" + ptl.getCard()->getCorrectedName() + ".full"; QImage image; + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); bool found = false; - + //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension for (int i = 0; i < picsPaths.length() && !found; i ++) { - QImageReader imgReader; imgReader.setFileName(picsPaths.at(i)); if (imgReader.read(&image)) { emit imageLoaded(ptl.getCard(), image); From 2e051c3fd7eee34ec97e2050f86af34441c71d8b Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 7 Aug 2014 16:10:57 +0200 Subject: [PATCH 71/82] add success notification to oracle --- oracle/src/oraclewizard.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 2a597d456..b43f1c31a 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -418,9 +418,12 @@ bool SaveSetsPage::validatePage() return false; } if (wizard()->importer->saveToFile(fileName)) + { ok = true; - else + QMessageBox::information(this, tr("Success"), tr("The card database has been saved successfully.")); + } else { QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location.")); + } } while (!ok); return true; From 40e97356788baacbedff9ae26bdc71c06149b019 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 7 Aug 2014 17:27:15 +0200 Subject: [PATCH 72/82] Add icon to oracle --- oracle/CMakeLists.txt | 25 +++- oracle/oracle.desktop | 2 +- oracle/oracle.qrc | 5 + oracle/oracle.rc | 1 + oracle/resources/appicon.icns | Bin 0 -> 42839 bytes oracle/resources/appicon.ico | Bin 0 -> 120784 bytes oracle/resources/oracle.png | Bin 0 -> 2542 bytes oracle/resources/oracle.svg | 228 ++++++++++++++++++++++++++++++++++ oracle/src/main.cpp | 7 +- 9 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 oracle/oracle.qrc create mode 100644 oracle/oracle.rc create mode 100644 oracle/resources/appicon.icns create mode 100644 oracle/resources/appicon.ico create mode 100644 oracle/resources/oracle.png create mode 100644 oracle/resources/oracle.svg diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index c3431571d..c14b0cf6c 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -16,6 +16,19 @@ SET(oracle_SOURCES ../cockatrice/src/qt-json/json.cpp ) +set(oracle_RESOURCES oracle.qrc) + +if(WIN32) + set(oracle_SOURCES ${oracle_SOURCES} oracle.rc) +endif(WIN32) + + +if(APPLE) + set(MACOSX_BUNDLE_ICON_FILE appicon.icns) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) +ENDIF(APPLE) + set(ORACLE_LIBS) # Qt4 stuff @@ -29,6 +42,10 @@ if(Qt4_FOUND) include_directories(${QT_INCLUDES}) LIST(APPEND ORACLE_LIBS ${QT_QTMAIN_LIBRARY}) LIST(APPEND ORACLE_LIBS ${QT_LIBRARIES}) + + # Let cmake chew Qt4's translations and resource files + # Note: header files are MOC-ed automatically by cmake + QT4_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) endif() # qt5 stuff @@ -64,6 +81,10 @@ if(Qt5Widgets_FOUND) list(APPEND ORACLE_LIBS Svg) endif() + # Let cmake chew Qt5's translations and resource files + # Note: header files are MOC-ed automatically by cmake + QT5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) + # guess plugins and libraries directory set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins") get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) @@ -73,7 +94,7 @@ endif() INCLUDE_DIRECTORIES(../cockatrice/src) # Build oracle binary and link it -ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_MOC_SRCS}) +ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS}) if(Qt4_FOUND) if(MSVC) @@ -94,6 +115,8 @@ if(UNIX) else() # Assume linux INSTALL(TARGETS oracle RUNTIME DESTINATION bin/) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) endif() elseif(WIN32) INSTALL(TARGETS oracle RUNTIME DESTINATION ./) diff --git a/oracle/oracle.desktop b/oracle/oracle.desktop index db8d4b617..c4943b859 100644 --- a/oracle/oracle.desktop +++ b/oracle/oracle.desktop @@ -4,5 +4,5 @@ Version=1.0 Type=Application Name=Cockatrice Oracle downloader Exec=oracle -Icon=cockatrice +Icon=oracle Categories=Game;CardGame; diff --git a/oracle/oracle.qrc b/oracle/oracle.qrc new file mode 100644 index 000000000..b58518c41 --- /dev/null +++ b/oracle/oracle.qrc @@ -0,0 +1,5 @@ + + + resources/oracle.svg + + diff --git a/oracle/oracle.rc b/oracle/oracle.rc new file mode 100644 index 000000000..cf949f313 --- /dev/null +++ b/oracle/oracle.rc @@ -0,0 +1 @@ +ID1_ICON1 ICON DISCARDABLE "resources/appicon.ico" diff --git a/oracle/resources/appicon.icns b/oracle/resources/appicon.icns new file mode 100644 index 0000000000000000000000000000000000000000..76c188c93125fef2a89af99dffff19eac5d6be54 GIT binary patch literal 42839 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 Y#5`&L17m+w6uOd@tCsz?>W^&wKl!_or~m)} literal 0 HcmV?d00001 diff --git a/oracle/resources/appicon.ico b/oracle/resources/appicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..112bf0100b033966e3417031156801e58d0d7572 GIT binary patch literal 120784 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%0Dtdvo;195c)*ftyf)^Cok_LCzQu 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 Ef+=RqX#fBK literal 0 HcmV?d00001 diff --git a/oracle/resources/oracle.svg b/oracle/resources/oracle.svg new file mode 100644 index 000000000..29740ffe8 --- /dev/null +++ b/oracle/resources/oracle.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + o + + diff --git a/oracle/src/main.cpp b/oracle/src/main.cpp index d2fb708d2..dcc5afdcc 100644 --- a/oracle/src/main.cpp +++ b/oracle/src/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "oraclewizard.h" #include "settingscache.h" @@ -18,10 +19,14 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("cockatrice"); // this can't be changed, as it influences the default savepath for cards.xml QCoreApplication::setApplicationName("Cockatrice"); - + settingsCache = new SettingsCache; OracleWizard wizard; + + QIcon icon(":/resources/appicon.svg"); + wizard.setWindowIcon(icon); + wizard.show(); return app.exec(); From da9592f0d8ee93a19771e52bd549da847880de92 Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Thu, 7 Aug 2014 11:52:40 -0400 Subject: [PATCH 73/82] Added style guide. --- CONTRIBUTING.md | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e9fe37a0f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,105 @@ +# Style Guide # + +### Compatibility ### + +Cockatrice is written in C++-03, so do not use C++11 constructs such as `auto`. + +Cockatrice support both Qt 4 and Qt 5, so make sure that your code compiles +with both. If there have been breaking changes between Qt 4 and 5, use a +statement such as + + #if QT_VERSION >= 0x500000 + doSomethingWithQt5(); + #else + doSomethingWithQt4(); + #endif + +For consistency, use Qt data structures where possible, such as `QString` over +`std::string` or `QList` over `std::vector`. + +### Header files ### + +Use header files with the extension `.h` and source files with the extension +`.cpp`. + +Use header guards in the form of `FILE_NAME_H`. + +Simple functions, such as getters, may be written inline in the header file, +but other functions should be written in the source file. + +Keep library includes and project includes grouped together. So this is okay: + + // Good: + #include + #include + #include "card.h" + #include "deck.h" + + // Good: + #include "card.h" + #include "deck.h" + #include + #include + + // Bad: + #include + #include "card.h" + #include + #include "deck.h" + +### Naming ### + +Use `UpperCamelCase` for classes, structs, enums, etc. and `lowerCamelCase` for +function and variable names. + +Member variables aren't decorated in any way. Don't prefix or suffix with +underscores, etc. + +For arguments to constructors which have the same names as member variables, +prefix those arguments with underscores: + + MyClass::MyClass(int _myData) + : myData(_myData) + {} + +Pointers and references should be denoted with the `*` or `&` going with the +variable name: + + // Good: + Foo *foo1 = new Foo; + Foo &foo2 = *foo1; + + // Bad: + Bar* bar1 = new Bar; + Bar& bar2 = *bar1; + +Use `0` instead of `NULL` (or `nullptr`) for null pointers. + +### Braces ### + +Use K&R-style braces. Braces for function implementations go on their own +lines, but they go on the same line everywhere else: + + int main() + { + if (someCondition) { + doSomething(); + } else { + while (someOtherCondition) { + doSomethingElse(); + } + } + } + +Braces can be omitted for single-statement if's and the like, as long as it is +still legible. + +### Tabs ### + +Use only spaces. Four spaces per tab. + +### Lines ### + +Do not have trailing whitespace in your lines. + +Lines should be 80 characters or less, as a soft limit. From ffc5b59fec7ccf1100e8c81dc5a2ddf9a5cc1a1a Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Thu, 7 Aug 2014 12:24:49 -0400 Subject: [PATCH 74/82] Fix #258: Dashes to spaces. --- cockatrice/src/carddatabase.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index b3d4bca98..84b387a6a 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -477,6 +477,10 @@ QString CardInfo::simplifyName(const QString &name) { // Replace Jötun Grunt with Jotun Grunt. simpleName = simpleName.normalized(QString::NormalizationForm_KD); + // Replace dashes with spaces so that we can say "garruk the veil cursed" + // instead of the unintuitive "garruk the veilcursed". + simpleName = simpleName.replace("-", " "); + simpleName.remove(QRegExp("[^a-zA-Z0-9 ]")); simpleName = simpleName.toLower(); return simpleName; From bee1ff39f54f6e71883b8318cfb594428bbf906a Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Thu, 7 Aug 2014 15:37:40 -0400 Subject: [PATCH 75/82] Added section on memory management to CONTRIBUTING.md. --- CONTRIBUTING.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9fe37a0f..cb8473890 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -103,3 +103,32 @@ Use only spaces. Four spaces per tab. Do not have trailing whitespace in your lines. Lines should be 80 characters or less, as a soft limit. + +### Memory Management ### + +New code should be written using references over pointers and stack allocation +over heap allocation wherever possible. + + // Good: uses stack allocation and references + void showCard(const Card &card); + int main() + { + Card card; + showCard(card); + } + + // Bad: relies on manual memory management and doesn't give us much + // null-safety. + void showCard(const Card *card); + int main() + { + Card *card = new Card; + showCard(card); + delete card; + } + +(Remember to pass by `const` reference wherever possible, to avoid accidentally +mutating objects.) + +When pointers can't be avoided, try to use a smart pointer of some sort, such +as `QScopedPointer`, or, less preferably, `QSharedPointer`. From 3bce98e3a94f51b3d00718a9d0c8c831b704ea2f Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 7 Aug 2014 22:28:08 +0200 Subject: [PATCH 76/82] Fix crashes trying to load the picture of a card with no --- cockatrice/src/carddatabase.cpp | 46 ++++++++++++++++++++++++--------- cockatrice/src/carddatabase.h | 2 +- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 84b387a6a..778709d14 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -93,6 +93,14 @@ bool PictureToLoad::nextSet() return true; } +QString PictureToLoad::getSetName() const +{ + if (setIndex < sortedSets.size() - 1) + return sortedSets[setIndex]->getCorrectedShortName(); + else + return QString(""); +} + PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent) : QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq), @@ -127,9 +135,15 @@ void PictureLoader::processLoadQueue() mutex.unlock(); //The list of paths to the folders in which to search for images - QList picsPaths = QList() << _picsPath + "/CUSTOM/" + ptl.getCard()->getCorrectedName() + ".full" - << _picsPath + "/" + ptl.getSetName() + "/" + ptl.getCard()->getCorrectedName() + ".full" - << _picsPath + "/downloadedPics/" + ptl.getSetName() + "/" + ptl.getCard()->getCorrectedName() + ".full"; + QList picsPaths = QList() << _picsPath + "/CUSTOM/" + ptl.getCard()->getCorrectedName() + ".full"; + + + QString setName=ptl.getSetName(); + if(!setName.isEmpty()) + { + picsPaths << _picsPath + "/" + setName + "/" + ptl.getCard()->getCorrectedName() + ".full" + << _picsPath + "/downloadedPics/" + setName + "/" + ptl.getCard()->getCorrectedName() + ".full"; + } QImage image; QImageReader imgReader; @@ -166,19 +180,24 @@ QString PictureLoader::getPicUrl(CardInfo *card) CardSet *set = card->getPreferredSet(); QString picUrl = QString(""); - // first check if Hq is enabled and a custom Hq card url exists in cards.xml - if(picDownloadHq) + + // if sets have been defined for the card, they can contain custom picUrls + if(set) { - picUrl = card->getCustomPicURLHq(set->getShortName()); + // first check if Hq is enabled and a custom Hq card url exists in cards.xml + if(picDownloadHq) + { + picUrl = card->getCustomPicURLHq(set->getShortName()); + if (!picUrl.isEmpty()) + return picUrl; + } + + // then, test for a custom, non-Hq card url in cards.xml + picUrl = card->getCustomPicURL(set->getShortName()); if (!picUrl.isEmpty()) return picUrl; } - // then, test for a custom, non-Hq card url in cards.xml - picUrl = card->getCustomPicURL(set->getShortName()); - if (!picUrl.isEmpty()) - return picUrl; - // otherwise, fallback to the default url picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); @@ -456,6 +475,8 @@ void CardInfo::updatePixmapCache() CardSet* CardInfo::getPreferredSet() { + if(sets.isEmpty()) + return 0; SetList sortedSets = sets; sortedSets.sortByKey(); return sortedSets.first(); @@ -463,7 +484,8 @@ CardSet* CardInfo::getPreferredSet() int CardInfo::getPreferredMuId() { - return muIds[getPreferredSet()->getShortName()]; + CardSet *set = getPreferredSet(); + return set ? muIds[set->getShortName()] : 0; } QString CardInfo::simplifyName(const QString &name) { diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index bb151e7d0..91abac59e 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -55,7 +55,7 @@ public: PictureToLoad(CardInfo *_card = 0, bool _stripped = false, bool _hq = true); CardInfo *getCard() const { return card; } bool getStripped() const { return stripped; } - QString getSetName() const { return sortedSets[setIndex]->getCorrectedShortName(); } + QString getSetName() const; bool nextSet(); bool getHq() const { return hq; } void setHq(bool _hq) { hq = _hq; } From 88dfea8ece73c56be2a5307c949d60ceecde7b79 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 7 Aug 2014 22:45:43 +0200 Subject: [PATCH 77/82] Don't save card pictures if we don't have a proper set name for it --- cockatrice/src/carddatabase.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 778709d14..8e45dca05 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -248,21 +248,25 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) extension = ".jpg"; if (imgReader.read(&testImage)) { - if (!QDir().mkpath(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName())) { - qDebug() << picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + " could not be created."; - return; + QString setName = cardBeingDownloaded.getSetName(); + if(!setName.isEmpty()) + { + if (!QDir().mkpath(picsPath + "/downloadedPics/" + setName)) { + qDebug() << picsPath + "/downloadedPics/" + setName + " could not be created."; + return; + } + + QString suffix; + if (!cardBeingDownloaded.getStripped()) + suffix = ".full"; + + QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension); + if (!newPic.open(QIODevice::WriteOnly)) + return; + newPic.write(picData); + newPic.close(); } - QString suffix; - if (!cardBeingDownloaded.getStripped()) - suffix = ".full"; - - QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension); - if (!newPic.open(QIODevice::WriteOnly)) - return; - newPic.write(picData); - newPic.close(); - emit imageLoaded(cardBeingDownloaded.getCard(), testImage); } else if (cardBeingDownloaded.getHq()) { qDebug() << "HQ: received invalid picture. URL:" << reply->request().url(); From bba8184d3715646489c2640a22c300710772c6c8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 7 Aug 2014 22:45:59 +0200 Subject: [PATCH 78/82] Fix off by one --- cockatrice/src/carddatabase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 8e45dca05..9e7b38dc5 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -95,7 +95,7 @@ bool PictureToLoad::nextSet() QString PictureToLoad::getSetName() const { - if (setIndex < sortedSets.size() - 1) + if (setIndex < sortedSets.size()) return sortedSets[setIndex]->getCorrectedShortName(); else return QString(""); From 42f1c6938fcdb24579e881ff4efea9d4da2414c8 Mon Sep 17 00:00:00 2001 From: Daenyth Date: Sun, 29 Jun 2014 23:00:58 -0400 Subject: [PATCH 79/82] Merged: First draft of better pic url error handling Cherry-picked b9cb61abd05f83c67740aafa5e1e6939143547e4 --- cockatrice/src/carddatabase.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 9e7b38dc5..5edd41fba 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -176,7 +176,7 @@ void PictureLoader::processLoadQueue() QString PictureLoader::getPicUrl(CardInfo *card) { - if (!picDownload) return 0; + if (!picDownload) return QString(""); CardSet *set = card->getPreferredSet(); QString picUrl = QString(""); @@ -201,9 +201,22 @@ QString PictureLoader::getPicUrl(CardInfo *card) // otherwise, fallback to the default url picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); - picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); - picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); - picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(card->getPreferredMuId()))); + + if (set) { + picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); + picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); + } + int muid = card->getPreferredMuId(); + if (muid) + picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); + + if (picUrl.contains("!name!") || + picUrl.contains("!setcode!") || + picUrl.contains("!setname!") || + picUrl.contains("!cardid!")) { + qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; + return QString(""); + } return picUrl; } @@ -220,8 +233,11 @@ void PictureLoader::startNextPicDownload() cardBeingDownloaded = cardsToDownload.takeFirst(); - // TODO: Do something useful when picUrl is 0 or empty, etc QString picUrl = getPicUrl(cardBeingDownloaded.getCard()); + if (picUrl.isEmpty()) { + qDebug() << "No url for" << cardBeingDownloaded.getCard()->getName(); + return; + } QUrl url(picUrl); From 765e865aa2cbc7a573ce496240d9bcb93d6a876e Mon Sep 17 00:00:00 2001 From: Daenyth Date: Wed, 2 Jul 2014 19:18:05 -0400 Subject: [PATCH 80/82] Continue downloading card images after failing url on one. I should probably bust this out to a method. --- cockatrice/src/carddatabase.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 5edd41fba..79a225ece 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -236,6 +236,8 @@ void PictureLoader::startNextPicDownload() QString picUrl = getPicUrl(cardBeingDownloaded.getCard()); if (picUrl.isEmpty()) { qDebug() << "No url for" << cardBeingDownloaded.getCard()->getName(); + cardBeingDownloaded = 0; + downloadRunning = false; return; } From 0a5106b8efd561d041ce10ce1efe2b31a9224b61 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 8 Aug 2014 09:57:07 +0200 Subject: [PATCH 81/82] Don't leak a QSettings object at every logged message --- servatrice/src/server_logger.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index 2dbbc5e52..a3e92affa 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -50,9 +50,9 @@ void ServerLogger::logMessage(QString message, void *caller) callerString = QString::number((qulonglong) caller, 16) + " "; //filter out all log entries based on values in configuration file - QSettings *settings = new QSettings("servatrice.ini", QSettings::IniFormat); - bool shouldWeWriteLog = settings->value("server/writelog").toBool(); - QString logFilters = settings->value("server/logfilters").toString(); + QSettings settings("servatrice.ini", QSettings::IniFormat); + bool shouldWeWriteLog = settings.value("server/writelog").toBool(); + QString logFilters = settings.value("server/logfilters").toString(); QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts); bool shouldWeSkipLine = false; From 6e3c7d33da2dbfe4ba5dbb1b0e6fbf1dff713853 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 8 Aug 2014 13:43:50 +0200 Subject: [PATCH 82/82] Make protobuf cleanup its resourced when servatrice ends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should help make valgrind’s log a bit cleaner --- servatrice/src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index c5bf9cde6..d90e06349 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -29,6 +29,7 @@ #include "server_logger.h" #include "rng_sfmt.h" #include "version_string.h" +#include #ifdef Q_OS_UNIX #include #endif @@ -233,5 +234,8 @@ int main(int argc, char *argv[]) loggerThread->wait(); delete loggerThread; + // Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + return retval; }