Add option to share decklists on load. (#6029)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, 12) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, skip, 11) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, 42) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, skip, 41) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, 24.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, skip, 22.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (yes, Arch, skip) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (1, macos-13, Intel, 13, Release, 14.3.1) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (1, macos-14, Apple, 14, Release, 15.4) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (1, macos-15, Apple, 15, Release, 16.2) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (macos-15, Apple, 15, Debug, 16.2) (push) Blocked by required conditions
Build Desktop / Windows ${{matrix.target}} (msvc2019_64, 5.15.*, 7) (push) Blocked by required conditions
Build Desktop / Windows ${{matrix.target}} (msvc2019_64, qtimageformats qtmultimedia qtwebsockets, 6.6.*, 10) (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run

* Add option to share decklists on load.

Took 1 hour 58 minutes

Took 9 minutes


Took 39 minutes

* Lint.

Took 14 minutes


Took 2 minutes

* Stuffs

Took 39 minutes

Took 4 seconds

Took 43 minutes

* Process local player first.

Took 45 minutes

* Consider if the setting is set on the game info first.

Took 4 minutes

* Save an indent level.

Took 43 seconds

* Don't commit logging config.

Took 3 minutes

* Remove a debug print.

Took 10 seconds


Took 7 seconds

* Add another optional guard.

Took 5 minutes

* Hide the tab bar if only one (own deck) is visible.

Took 9 minutes

* Rename setting label for clarity

Took 2 minutes

* Capitalization.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2025-08-15 23:31:05 +02:00 committed by GitHub
parent 881243da6a
commit 09381575a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 268 additions and 33 deletions

View File

@ -199,6 +199,7 @@ set(cockatrice_SOURCES
src/game/cards/exact_card.cpp
src/game/deckview/deck_view.cpp
src/game/deckview/deck_view_container.cpp
src/game/deckview/tabbed_deck_view_container.cpp
src/game/filters/deck_filter_string.cpp
src/game/filters/filter_builder.cpp
src/game/filters/filter_card.cpp

View File

@ -7,6 +7,7 @@
#include "../../game/cards/card_database.h"
#include "../../game/cards/card_database_manager.h"
#include "../../game/deckview/deck_view_container.h"
#include "../../game/deckview/tabbed_deck_view_container.h"
#include "../../game/game_scene.h"
#include "../../game/game_view.h"
#include "../../game/player/player.h"
@ -292,9 +293,9 @@ void TabGame::retranslateUi()
QMapIterator<int, Player *> i(players);
while (i.hasNext())
i.next().value()->retranslateUi();
QMapIterator<int, DeckViewContainer *> j(deckViewContainers);
QMapIterator<int, TabbedDeckViewContainer *> j(deckViewContainers);
while (j.hasNext())
j.next().value()->retranslateUi();
j.next().value()->playerDeckView->retranslateUi();
scene->retranslateUi();
}
@ -564,7 +565,7 @@ void TabGame::actCompleterChanged()
Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
{
bool local = ((clients.size() > 1) || (playerId == localPlayerId));
bool local = clients.size() > 1 || playerId == localPlayerId;
auto *newPlayer = new Player(info, playerId, local, judge, this);
connect(newPlayer, SIGNAL(openDeckEditor(const DeckLoader *)), this, SIGNAL(openDeckEditor(const DeckLoader *)));
QString newPlayerName = "@" + newPlayer->getName();
@ -582,8 +583,8 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
if (clients.size() == 1)
newPlayer->setShortcutsActive();
auto *deckView = new DeckViewContainer(playerId, this);
connect(deckView, &DeckViewContainer::newCardAdded, this, &TabGame::newCardAdded);
auto *deckView = new TabbedDeckViewContainer(playerId, this);
connect(deckView->playerDeckView, &DeckViewContainer::newCardAdded, this, &TabGame::newCardAdded);
deckViewContainers.insert(playerId, deckView);
deckViewContainerLayout->addWidget(deckView);
@ -591,8 +592,8 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
QString deckPath = SettingsCache::instance().debug().getDeckPathForPlayer(newPlayer->getName());
if (!deckPath.isEmpty()) {
QTimer::singleShot(0, this, [deckView, deckPath] {
deckView->loadDeckFromFile(deckPath);
deckView->readyAndUpdate();
deckView->playerDeckView->loadDeckFromFile(deckPath);
deckView->playerDeckView->readyAndUpdate();
});
}
}
@ -777,11 +778,11 @@ void TabGame::startGame(bool _resuming)
{
currentPhase = -1;
QMapIterator<int, DeckViewContainer *> i(deckViewContainers);
QMapIterator<int, TabbedDeckViewContainer *> i(deckViewContainers);
while (i.hasNext()) {
i.next();
i.value()->setReadyStart(false);
i.value()->setVisualDeckStorageExists(false);
i.value()->playerDeckView->setReadyStart(false);
i.value()->playerDeckView->setVisualDeckStorageExists(false);
i.value()->hide();
}
@ -803,7 +804,7 @@ void TabGame::stopGame()
currentPhase = -1;
activePlayer = -1;
QMapIterator<int, DeckViewContainer *> i(deckViewContainers);
QMapIterator<int, TabbedDeckViewContainer *> i(deckViewContainers);
while (i.hasNext()) {
i.next();
i.value()->show();
@ -849,6 +850,44 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event,
const GameEventContext & /*context*/)
{
const int playerListSize = event.player_list_size();
// Always process the local player first so we have an established deckViewcontainer
for (int i = 0; i < playerListSize; ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
const int playerId = prop.player_id();
QString playerName = "@" + QString::fromStdString(prop.user_info().name());
if (sayEdit && !autocompleteUserList.contains(playerName)) {
autocompleteUserList << playerName;
sayEdit->setCompletionList(autocompleteUserList);
}
if (!prop.spectator()) {
Player *player = players.value(playerId, 0);
if (!player) {
if (clients.size() > 1 || playerId == localPlayerId) {
player = addPlayer(playerId, prop.user_info());
playerListWidget->addPlayer(prop);
player->processPlayerInfo(playerInfo);
if (player->getLocal()) {
TabbedDeckViewContainer *deckViewContainer = deckViewContainers.value(playerId);
if (playerInfo.has_deck_list()) {
DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list()));
PictureLoader::cacheCardPixmaps(
CardDatabaseManager::getInstance()->getCards(newDeck.getCardRefList()));
deckViewContainer->playerDeckView->setDeck(newDeck);
player->setDeck(newDeck);
}
deckViewContainer->playerDeckView->setReadyStart(prop.ready_start());
deckViewContainer->playerDeckView->setSideboardLocked(prop.sideboard_locked());
}
}
}
}
}
// then process every non-local player.
for (int i = 0; i < playerListSize; ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
@ -870,17 +909,16 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event,
playerListWidget->addPlayer(prop);
}
player->processPlayerInfo(playerInfo);
if (player->getLocal()) {
DeckViewContainer *deckViewContainer = deckViewContainers.value(playerId);
if (playerInfo.has_deck_list()) {
DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list()));
PictureLoader::cacheCardPixmaps(
CardDatabaseManager::getInstance()->getCards(newDeck.getCardRefList()));
deckViewContainer->setDeck(newDeck);
player->setDeck(newDeck);
}
deckViewContainer->setReadyStart(prop.ready_start());
deckViewContainer->setSideboardLocked(prop.sideboard_locked());
if (player->getLocal() || !gameInfo.share_decklists_on_load()) {
continue;
}
DeckList loader;
loader.loadFromString_Native(QString::fromStdString(playerInfo.deck_list()));
QMapIterator<int, TabbedDeckViewContainer *> i(deckViewContainers);
while (i.hasNext()) {
i.next();
i.value()->addOpponentDeckView(loader, playerId, player->getName());
}
}
}
@ -926,7 +964,7 @@ void TabGame::eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &
case GameEventContext::READY_START: {
bool ready = prop.ready_start();
if (player->getLocal())
deckViewContainers.value(player->getId())->setReadyStart(ready);
deckViewContainers.value(player->getId())->playerDeckView->setReadyStart(ready);
if (ready)
messageLog->logReadyStart(player);
else
@ -957,11 +995,20 @@ void TabGame::eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &
Context_DeckSelect deckSelect = context.GetExtension(Context_DeckSelect::ext);
messageLog->logDeckSelect(player, QString::fromStdString(deckSelect.deck_hash()),
deckSelect.sideboard_size());
if (gameInfo.share_decklists_on_load() && deckSelect.has_deck_list() && eventPlayerId != localPlayerId) {
DeckList loader;
loader.loadFromString_Native(QString::fromStdString(deckSelect.deck_list()));
QMapIterator<int, TabbedDeckViewContainer *> i(deckViewContainers);
while (i.hasNext()) {
i.next();
i.value()->addOpponentDeckView(loader, eventPlayerId, player->getName());
}
}
break;
}
case GameEventContext::SET_SIDEBOARD_LOCK: {
if (player->getLocal())
deckViewContainers.value(player->getId())->setSideboardLocked(prop.sideboard_locked());
deckViewContainers.value(player->getId())->playerDeckView->setSideboardLocked(prop.sideboard_locked());
messageLog->logSetSideboardLock(player, prop.sideboard_locked());
break;
}

View File

@ -13,6 +13,7 @@
#include <QLoggingCategory>
#include <QMap>
class TabbedDeckViewContainer;
inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game");
class UserListProxy;
@ -105,7 +106,7 @@ private:
PhasesToolbar *phasesToolbar;
GameScene *scene;
GameView *gameView;
QMap<int, DeckViewContainer *> deckViewContainers;
QMap<int, TabbedDeckViewContainer *> deckViewContainers;
QVBoxLayout *deckViewContainerLayout;
QWidget *gamePlayAreaWidget, *deckViewContainerWidget;
QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock;

View File

@ -102,9 +102,15 @@ void DlgCreateGame::sharedCtor()
startingLifeTotalEdit->setValue(20);
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
shareDecklistsOnLoadLabel = new QLabel(tr("Open decklists in lobby"));
shareDecklistsOnLoadCheckBox = new QCheckBox();
shareDecklistsOnLoadLabel->setBuddy(shareDecklistsOnLoadCheckBox);
QGridLayout *gameSetupOptionsLayout = new QGridLayout;
gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0);
gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1);
gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadLabel, 1, 0);
gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadCheckBox, 1, 1);
gameSetupOptionsGroupBox = new QGroupBox(tr("Game setup options"));
gameSetupOptionsGroupBox->setLayout(gameSetupOptionsLayout);
@ -149,6 +155,7 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap<int, QString> &_gameType
spectatorsSeeEverythingCheckBox->setChecked(SettingsCache::instance().getSpectatorsCanSeeEverything());
createGameAsSpectatorCheckBox->setChecked(SettingsCache::instance().getCreateGameAsSpectator());
startingLifeTotalEdit->setValue(SettingsCache::instance().getDefaultStartingLifeTotal());
shareDecklistsOnLoadCheckBox->setChecked(SettingsCache::instance().getShareDecklistsOnLoad());
if (!rememberGameSettings->isChecked()) {
actReset();
@ -181,6 +188,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMap<int, QS
spectatorsSeeEverythingCheckBox->setEnabled(false);
createGameAsSpectatorCheckBox->setEnabled(false);
startingLifeTotalEdit->setEnabled(false);
shareDecklistsOnLoadCheckBox->setEnabled(false);
descriptionEdit->setText(QString::fromStdString(gameInfo.description()));
maxPlayersEdit->setValue(gameInfo.max_players());
@ -225,6 +233,7 @@ void DlgCreateGame::actReset()
createGameAsSpectatorCheckBox->setChecked(false);
startingLifeTotalEdit->setValue(20);
shareDecklistsOnLoadCheckBox->setChecked(false);
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
while (gameTypeCheckBoxIterator.hasNext()) {
@ -253,6 +262,7 @@ void DlgCreateGame::actOK()
cmd.set_join_as_judge(QApplication::keyboardModifiers() & Qt::ShiftModifier);
cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked());
cmd.set_starting_life_total(startingLifeTotalEdit->value());
cmd.set_share_decklists_on_load(shareDecklistsOnLoadCheckBox->isChecked());
QString _gameTypes = QString();
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
@ -276,6 +286,7 @@ void DlgCreateGame::actOK()
SettingsCache::instance().setSpectatorsCanSeeEverything(spectatorsSeeEverythingCheckBox->isChecked());
SettingsCache::instance().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked());
SettingsCache::instance().setDefaultStartingLifeTotal(startingLifeTotalEdit->value());
SettingsCache::instance().setShareDecklistsOnLoad(shareDecklistsOnLoadCheckBox->isChecked());
SettingsCache::instance().setGameTypes(_gameTypes);
}
PendingCommand *pend = room->prepareRoomCommand(cmd);

View File

@ -36,12 +36,13 @@ private:
QMap<int, QRadioButton *> gameTypeCheckBoxes;
QGroupBox *generalGroupBox, *spectatorsGroupBox, *gameSetupOptionsGroupBox;
QLabel *descriptionLabel, *passwordLabel, *maxPlayersLabel, *startingLifeTotalLabel;
QLabel *descriptionLabel, *passwordLabel, *maxPlayersLabel, *startingLifeTotalLabel, *shareDecklistsOnLoadLabel;
QLineEdit *descriptionEdit, *passwordEdit;
QSpinBox *maxPlayersEdit, *startingLifeTotalEdit;
QCheckBox *onlyBuddiesCheckBox, *onlyRegisteredCheckBox;
QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox,
*spectatorsSeeEverythingCheckBox, *createGameAsSpectatorCheckBox;
QCheckBox *shareDecklistsOnLoadCheckBox;
QDialogButtonBox *buttonBox;
QPushButton *clearButton;
QCheckBox *rememberGameSettings;

View File

@ -40,6 +40,9 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &_allGameTypes,
hideNotBuddyCreatedGames = new QCheckBox(tr("Hide games not created by buddy"));
hideNotBuddyCreatedGames->setChecked(gamesProxyModel->getHideNotBuddyCreatedGames());
hideOpenDecklistGames = new QCheckBox(tr("Hide games with forced open decklists"));
hideOpenDecklistGames->setChecked(gamesProxyModel->getHideOpenDecklistGames());
maxGameAgeComboBox = new QComboBox();
maxGameAgeComboBox->setEditable(false);
maxGameAgeComboBox->addItems(gameAgeMap.values());
@ -115,6 +118,7 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &_allGameTypes,
restrictionsLayout->addWidget(hideBuddiesOnlyGames, 3, 0);
restrictionsLayout->addWidget(hideIgnoredUserGames, 4, 0);
restrictionsLayout->addWidget(hideNotBuddyCreatedGames, 5, 0);
restrictionsLayout->addWidget(hideOpenDecklistGames, 6, 0);
auto *restrictionsGroupBox = new QGroupBox(tr("Restrictions"));
restrictionsGroupBox->setLayout(restrictionsLayout);
@ -218,6 +222,11 @@ bool DlgFilterGames::getHideNotBuddyCreatedGames() const
return hideNotBuddyCreatedGames->isChecked();
}
bool DlgFilterGames::getHideOpenDecklistGames() const
{
return hideOpenDecklistGames->isChecked();
}
QString DlgFilterGames::getGameNameFilter() const
{
return gameNameFilterEdit->text();

View File

@ -27,6 +27,7 @@ private:
QCheckBox *hidePasswordProtectedGames;
QCheckBox *hideIgnoredUserGames;
QCheckBox *hideNotBuddyCreatedGames;
QCheckBox *hideOpenDecklistGames;
QLineEdit *gameNameFilterEdit;
QLineEdit *creatorNameFilterEdit;
QMap<int, QCheckBox *> gameTypeFilterCheckBoxes;
@ -57,6 +58,8 @@ public:
void setShowPasswordProtectedGames(bool _passwordProtectedGamesHidden);
bool getHideBuddiesOnlyGames() const;
void setHideBuddiesOnlyGames(bool _hideBuddiesOnlyGames);
bool getHideOpenDecklistGames() const;
void setHideOpenDecklistGames(bool _hideOpenDecklistGames);
bool getHideIgnoredUserGames() const;
void setHideIgnoredUserGames(bool _hideIgnoredUserGames);
bool getHideNotBuddyCreatedGames() const;

View File

@ -0,0 +1,64 @@
#include "tabbed_deck_view_container.h"
#include "../../client/tabs/tab_game.h"
#include "deck_view.h"
TabbedDeckViewContainer::TabbedDeckViewContainer(int _playerId, TabGame *parent)
: QTabWidget(nullptr), playerId(_playerId), parentGame(parent)
{
setTabsClosable(true);
connect(this, &QTabWidget::tabCloseRequested, this, &TabbedDeckViewContainer::closeTab);
playerDeckView = new DeckViewContainer(playerId, parentGame);
int playerTabIndex = addTab(playerDeckView, "Your Deck");
tabBar()->setTabButton(playerTabIndex, QTabBar::RightSide, nullptr);
updateTabBarVisibility();
}
void TabbedDeckViewContainer::addOpponentDeckView(const DeckList &opponentDeck, int opponentId, QString opponentName)
{
if (opponentDeckViews.contains(opponentId)) {
opponentDeckViews[opponentId]->setDeck(opponentDeck);
} else {
auto *opponentDeckView = new DeckView(this);
opponentDeckView->setDeck(opponentDeck);
addTab(opponentDeckView, QString("%1's Deck").arg(opponentName));
opponentDeckViews.insert(opponentId, opponentDeckView);
}
updateTabBarVisibility();
}
void TabbedDeckViewContainer::closeTab(int index)
{
QWidget *widgetToClose = widget(index);
// Prevent removing the player tab
if (widgetToClose == playerDeckView) {
return;
}
// Remove it from map if it's an opponent
auto it = opponentDeckViews.begin();
while (it != opponentDeckViews.end()) {
if (it.value() == widgetToClose) {
it = opponentDeckViews.erase(it);
} else {
++it;
}
}
removeTab(index);
widgetToClose->deleteLater();
updateTabBarVisibility();
}
void TabbedDeckViewContainer::updateTabBarVisibility()
{
if (tabBar()->count() <= 1) {
tabBar()->hide();
} else {
tabBar()->show();
}
}

View File

@ -0,0 +1,23 @@
#ifndef TABBED_DECK_VIEW_CONTAINER_H
#define TABBED_DECK_VIEW_CONTAINER_H
#include "deck_view_container.h"
#include <QTabWidget>
class TabbedDeckViewContainer : public QTabWidget
{
Q_OBJECT
public:
explicit TabbedDeckViewContainer(int _playerId, TabGame *parent);
void closeTab(int index);
void updateTabBarVisibility();
void addOpponentDeckView(const DeckList &opponentDeck, int opponentId, QString opponentName);
int playerId;
TabGame *parentGame;
DeckViewContainer *playerDeckView;
QMap<int, DeckView *> opponentDeckViews;
};
#endif // TABBED_DECK_VIEW_CONTAINER_H

View File

@ -161,6 +161,7 @@ void GameSelector::actSetFilter()
gameListProxyModel->setHidePasswordProtectedGames(dlg.getHidePasswordProtectedGames());
gameListProxyModel->setHideIgnoredUserGames(dlg.getHideIgnoredUserGames());
gameListProxyModel->setHideNotBuddyCreatedGames(dlg.getHideNotBuddyCreatedGames());
gameListProxyModel->setHideOpenDecklistGames(dlg.getHideOpenDecklistGames());
gameListProxyModel->setGameNameFilter(dlg.getGameNameFilter());
gameListProxyModel->setCreatorNameFilter(dlg.getCreatorNameFilter());
gameListProxyModel->setGameTypeFilter(dlg.getGameTypeFilter());

View File

@ -153,6 +153,8 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const
result.append(tr("buddies only"));
if (gameentry.only_registered())
result.append(tr("reg. users only"));
if (gameentry.share_decklists_on_load())
result.append(tr("open decklists"));
return result.join(", ");
}
case Qt::DecorationRole: {
@ -320,6 +322,12 @@ void GamesProxyModel::setHideNotBuddyCreatedGames(bool value)
invalidateFilter();
}
void GamesProxyModel::setHideOpenDecklistGames(bool _hideOpenDecklistGames)
{
hideOpenDecklistGames = _hideOpenDecklistGames;
invalidateFilter();
}
void GamesProxyModel::setGameNameFilter(const QString &_gameNameFilter)
{
gameNameFilter = _gameNameFilter;
@ -398,6 +406,7 @@ void GamesProxyModel::resetFilterParameters()
hideBuddiesOnlyGames = false;
hideIgnoredUserGames = false;
hideNotBuddyCreatedGames = false;
hideOpenDecklistGames = false;
gameNameFilter = QString();
creatorNameFilter = QString();
gameTypeFilter.clear();
@ -415,7 +424,7 @@ void GamesProxyModel::resetFilterParameters()
bool GamesProxyModel::areFilterParametersSetToDefaults() const
{
return !hideFullGames && !hideGamesThatStarted && !hidePasswordProtectedGames && !hideBuddiesOnlyGames &&
!hideIgnoredUserGames && !hideNotBuddyCreatedGames && gameNameFilter.isEmpty() &&
!hideOpenDecklistGames && !hideIgnoredUserGames && !hideNotBuddyCreatedGames && gameNameFilter.isEmpty() &&
creatorNameFilter.isEmpty() && gameTypeFilter.isEmpty() && maxPlayersFilterMin == DEFAULT_MAX_PLAYERS_MIN &&
maxPlayersFilterMax == DEFAULT_MAX_PLAYERS_MAX && maxGameAge == DEFAULT_MAX_GAME_AGE &&
!showOnlyIfSpectatorsCanWatch && !showSpectatorPasswordProtected && !showOnlyIfSpectatorsCanChat &&
@ -431,6 +440,7 @@ void GamesProxyModel::loadFilterParameters(const QMap<int, QString> &allGameType
hideIgnoredUserGames = gameFilters.isHideIgnoredUserGames();
hideBuddiesOnlyGames = gameFilters.isHideBuddiesOnlyGames();
hideNotBuddyCreatedGames = gameFilters.isHideNotBuddyCreatedGames();
hideOpenDecklistGames = gameFilters.isHideOpenDecklistGames();
gameNameFilter = gameFilters.getGameNameFilter();
creatorNameFilter = gameFilters.getCreatorNameFilter();
maxPlayersFilterMin = gameFilters.getMinPlayers();
@ -461,6 +471,7 @@ void GamesProxyModel::saveFilterParameters(const QMap<int, QString> &allGameType
gameFilters.setHidePasswordProtectedGames(hidePasswordProtectedGames);
gameFilters.setHideIgnoredUserGames(hideIgnoredUserGames);
gameFilters.setHideNotBuddyCreatedGames(hideNotBuddyCreatedGames);
gameFilters.setHideOpenDecklistGames(hideOpenDecklistGames);
gameFilters.setGameNameFilter(gameNameFilter);
gameFilters.setCreatorNameFilter(creatorNameFilter);
@ -504,6 +515,9 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow) const
if (hideBuddiesOnlyGames && game.only_buddies()) {
return false;
}
if (hideOpenDecklistGames && game.share_decklists_on_load()) {
return false;
}
if (hideIgnoredUserGames && userListProxy->isUserIgnored(QString::fromStdString(game.creator_info().name()))) {
return false;
}

View File

@ -81,6 +81,7 @@ private:
bool hideGamesThatStarted;
bool hidePasswordProtectedGames;
bool hideNotBuddyCreatedGames;
bool hideOpenDecklistGames;
QString gameNameFilter, creatorNameFilter;
QSet<int> gameTypeFilter;
quint32 maxPlayersFilterMin, maxPlayersFilterMax;
@ -121,6 +122,11 @@ public:
return hideNotBuddyCreatedGames;
}
void setHideNotBuddyCreatedGames(bool value);
bool getHideOpenDecklistGames() const
{
return hideOpenDecklistGames;
}
void setHideOpenDecklistGames(bool _hideOpenDecklistGames);
QString getGameNameFilter() const
{
return gameNameFilter;

View File

@ -355,6 +355,7 @@ SettingsCache::SettingsCache()
spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool();
createGameAsSpectator = settings->value("game/creategameasspectator", false).toBool();
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
shareDecklistsOnLoad = settings->value("game/sharedecklistsonload", false).toBool();
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
@ -1360,7 +1361,13 @@ void SettingsCache::setDefaultStartingLifeTotal(const int _defaultStartingLifeTo
{
defaultStartingLifeTotal = _defaultStartingLifeTotal;
settings->setValue("game/defaultstartinglifetotal", defaultStartingLifeTotal);
};
}
void SettingsCache::setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad)
{
shareDecklistsOnLoad = _shareDecklistsOnLoad;
settings->setValue("game/sharedecklistsonload", shareDecklistsOnLoad);
}
void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value)
{
@ -1373,6 +1380,7 @@ void SettingsCache::setStartupCardUpdateCheckPromptForUpdate(bool value)
startupCardUpdateCheckPromptForUpdate = value;
settings->setValue("personal/startupCardUpdateCheckPromptForUpdate", startupCardUpdateCheckPromptForUpdate);
}
void SettingsCache::setStartupCardUpdateCheckAlwaysUpdate(bool value)
{
startupCardUpdateCheckAlwaysUpdate = value;

View File

@ -302,6 +302,7 @@ private:
bool spectatorsCanSeeEverything;
bool createGameAsSpectator;
int defaultStartingLifeTotal;
bool shareDecklistsOnLoad;
int keepalive;
int timeout;
void translateLegacySettings();
@ -805,6 +806,10 @@ public:
{
return defaultStartingLifeTotal;
}
bool getShareDecklistsOnLoad() const
{
return shareDecklistsOnLoad;
}
bool getCreateGameAsSpectator() const
{
return createGameAsSpectator;
@ -1032,6 +1037,7 @@ public slots:
void setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEverything);
void setCreateGameAsSpectator(const bool _createGameAsSpectator);
void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal);
void setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad);
void setRememberGameSettings(const bool _rememberGameSettings);
void setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value);
void setStartupCardUpdateCheckPromptForUpdate(bool value);

View File

@ -83,6 +83,17 @@ bool GameFiltersSettings::isHideNotBuddyCreatedGames()
return !(previous == QVariant()) && previous.toBool();
}
void GameFiltersSettings::setHideOpenDecklistGames(bool hide)
{
setValue(hide, "hide_open_decklist_games", "filter_games");
}
bool GameFiltersSettings::isHideOpenDecklistGames()
{
QVariant previous = getValue("hide_open_decklist_games", "filter_games");
return !(previous == QVariant()) && previous.toBool();
}
void GameFiltersSettings::setGameNameFilter(QString gameName)
{
setValue(gameName, "game_name_filter", "filter_games");

View File

@ -15,6 +15,8 @@ public:
bool isHidePasswordProtectedGames();
bool isHideIgnoredUserGames();
bool isHideNotBuddyCreatedGames();
void setHideOpenDecklistGames(bool hide);
bool isHideOpenDecklistGames();
QString getGameNameFilter();
QString getCreatorNameFilter();
int getMinPlayers();

View File

@ -7,4 +7,5 @@ message Context_DeckSelect {
}
optional string deck_hash = 1;
optional int32 sideboard_size = 2 [default = -1];
optional string deck_list = 3;
}

View File

@ -39,6 +39,7 @@ message Command_CreateGame {
optional bool join_as_judge = 11;
optional bool join_as_spectator = 12;
optional uint32 starting_life_total = 13;
optional bool share_decklists_on_load = 14;
}
message Command_JoinGame {

View File

@ -16,6 +16,7 @@ message ServerInfo_Game {
optional bool spectators_need_password = 12;
optional bool spectators_can_chat = 13;
optional bool spectators_omniscient = 14;
optional bool share_decklists_on_load = 15;
optional uint32 player_count = 30;
optional uint32 spectators_count = 31;
optional bool started = 50;

View File

@ -21,6 +21,7 @@
#include "decklist.h"
#include "pb/context_connection_state_changed.pb.h"
#include "pb/context_deck_select.pb.h"
#include "pb/context_ping_changed.pb.h"
#include "pb/event_delete_arrow.pb.h"
#include "pb/event_game_closed.pb.h"
@ -62,15 +63,16 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
bool _spectatorsCanTalk,
bool _spectatorsSeeEverything,
int _startingLifeTotal,
bool _shareDecklistsOnLoad,
Server_Room *_room)
: QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(_creatorInfo)),
gameStarted(false), gameClosed(false), gameId(_gameId), password(_password), maxPlayers(_maxPlayers),
gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies),
onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed),
spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk),
spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal), inactivityCounter(0),
startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false),
startTime(QDateTime::currentDateTime()), pingClock(nullptr),
spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal),
shareDecklistsOnLoad(_shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0),
firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr),
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
gameMutex()
#else
@ -800,6 +802,7 @@ void Server_Game::getInfo(ServerInfo_Game &result) const
result.set_spectators_need_password(getSpectatorsNeedPassword());
result.set_spectators_can_chat(spectatorsCanTalk);
result.set_spectators_omniscient(spectatorsSeeEverything);
result.set_share_decklists_on_load(shareDecklistsOnLoad);
result.set_spectators_count(getSpectatorCount());
result.set_start_time(startTime.toSecsSinceEpoch());
}

View File

@ -67,6 +67,7 @@ private:
bool spectatorsCanTalk;
bool spectatorsSeeEverything;
int startingLifeTotal;
bool shareDecklistsOnLoad;
int inactivityCounter;
int startTimeOfThisGame, secondsElapsed;
bool firstGameStarted;
@ -106,7 +107,8 @@ public:
bool _spectatorsNeedPassword,
bool _spectatorsCanTalk,
bool _spectatorsSeeEverything,
int startingLifeTotal,
int _startingLifeTotal,
bool _shareDecklistsOnLoad,
Server_Room *parent);
~Server_Game() override;
Server_Room *getRoom() const
@ -168,6 +170,10 @@ public:
{
return startingLifeTotal;
}
bool getShareDecklistsOnLoad() const
{
return shareDecklistsOnLoad;
}
Response::ResponseCode
checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions, bool asJudge);
bool containsUser(const QString &userName) const;

View File

@ -836,6 +836,9 @@ Server_Player::cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &r
Context_DeckSelect context;
context.set_deck_hash(deck->getDeckHash().toStdString());
context.set_sideboard_size(deck->getSideboardSize());
if (game->getShareDecklistsOnLoad()) {
context.set_deck_list(deck->writeToString_Native().toStdString());
}
ges.setGameEventContext(context);
auto *re = new Response_DeckDownload;

View File

@ -96,6 +96,10 @@ public:
Server_AbstractUserInterface *_handler);
~Server_Player() override;
void prepareDestroy();
const DeckList *getDeckList() const
{
return deck;
}
Server_AbstractUserInterface *getUserInterface() const
{
return userInterface;

View File

@ -818,6 +818,8 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
QString description = nameFromStdString(cmd.description());
int startingLifeTotal = cmd.has_starting_life_total() ? cmd.starting_life_total() : 20;
bool shareDecklistsOnLoad = cmd.has_share_decklists_on_load() ? cmd.share_decklists_on_load() : false;
const int gameId = databaseInterface->getNextGameId();
if (gameId == -1) {
return Response::RespInternalError;
@ -828,7 +830,7 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
Server_Game *game = new Server_Game(
copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes,
cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(),
cmd.spectators_can_talk(), cmd.spectators_see_everything(), startingLifeTotal, room);
cmd.spectators_can_talk(), cmd.spectators_see_everything(), startingLifeTotal, shareDecklistsOnLoad, room);
game->addPlayer(this, rc, asSpectator, asJudge, false);
room->addGame(game);

View File

@ -402,6 +402,9 @@ void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectato
void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal */)
{
}
void SettingsCache::setShareDecklistsOnLoad(const bool /* _shareDecklistsOnLoad */)
{
}
void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */)
{
}

View File

@ -406,6 +406,9 @@ void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectato
void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal */)
{
}
void SettingsCache::setShareDecklistsOnLoad(const bool /* _shareDecklistsOnLoad */)
{
}
void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */)
{
}