Compare commits

..

No commits in common. "6c8fcf7d197dacc7b49a86cbff7c8dd6282608b2" and "d99798111eb92cb380df5b81ed4d643cb6eb8aea" have entirely different histories.

12 changed files with 63 additions and 186 deletions

View file

@ -246,7 +246,6 @@ set(cockatrice_SOURCES
src/interface/widgets/replay/replay_widget.cpp
src/interface/widgets/server/chat_view/chat_view.cpp
src/interface/widgets/server/game_filter_configs.cpp
src/interface/widgets/server/game_link.cpp
src/interface/widgets/server/game_selector.cpp
src/interface/widgets/server/game_selector_quick_filter_toolbar.cpp
src/interface/widgets/server/games_model.cpp

View file

@ -1,23 +0,0 @@
#include "game_link.h"
#include <QUrl>
#include <QUrlQuery>
QString makeGameJoinLink(const QString &hostname, int port, int roomId, int gameId, const QString &description)
{
QUrl url;
url.setScheme("cockatrice");
url.setHost("joingame");
QUrlQuery query;
query.addQueryItem("hostname", hostname);
query.addQueryItem("port", QString::number(port));
query.addQueryItem("roomid", QString::number(roomId));
query.addQueryItem("gameid", QString::number(gameId));
if (!description.isEmpty()) {
// addQueryItem percent-encodes, so arbitrary descriptions (quotes,
// ampersands, non-ASCII…) survive the trip through chat.
query.addQueryItem("game", description);
}
url.setQuery(query);
return url.toString(QUrl::FullyEncoded);
}

View file

@ -1,41 +0,0 @@
/**
* @file game_link.h
* @ingroup UI
* @brief Builds cockatrice://joingame links that let another user join a server game.
*/
#ifndef GAME_LINK_H
#define GAME_LINK_H
#include <QString>
/**
* Builds a cockatrice://joingame link for the given server game. The receiver's
* client opens it through the intent chain (connect -> join room -> join game).
* @p description, when non-empty, is embedded in the link as the URL-encoded
* "game" query item so the receiving client can name the game in its confirm
* prompt and chat anchor instead of only its numeric id. Links built without it
* stay valid: the parser and chat renderer fall back to the id alone.
*/
QString
makeGameJoinLink(const QString &hostname, int port, int roomId, int gameId, const QString &description = QString());
/**
* One game the inviter is currently in and can invite another user to.
* @p label is meant for display in menus, @p url is the ready-made invite link.
* @p description is the raw game description for building tr()-wrapped invite
* messages (the label already embeds it, but the send sites need the raw value).
* @p onlyBuddies and @p creatorName mirror the server game's room settings so
* callers can gate the invite to the creator's buddies.
*/
struct GameInviteOption
{
int gameId = 0;
QString label;
QString url;
QString description;
bool onlyBuddies = false;
QString creatorName;
};
#endif // GAME_LINK_H

View file

@ -7,7 +7,6 @@
#include "../interface/widgets/tabs/tab_room.h"
#include "../interface/widgets/tabs/tab_supervisor.h"
#include "../interface/widgets/utility/get_text_with_max.h"
#include "game_link.h"
#include "games_model.h"
#include "user/user_list_manager.h"
@ -19,6 +18,8 @@
#include <QMessageBox>
#include <QPushButton>
#include <QTreeView>
#include <QUrl>
#include <QUrlQuery>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/response.pb.h>
#include <libcockatrice/protocol/pb/room_commands.pb.h>
@ -319,12 +320,19 @@ void GameSelector::customContextMenu(const QPoint &point)
dlg.exec();
});
QAction copyLink(tr("Cop&y game link"));
QAction copyLink(tr("Copy Game Link"));
connect(&copyLink, &QAction::triggered, this, [=, this]() {
const ServerInfo_Game &gameInfo = gameListModel->getGame(index.data(Qt::UserRole).toInt());
QGuiApplication::clipboard()->setText(makeGameJoinLink(client->serverName(), client->serverPort(),
gameInfo.room_id(), gameInfo.game_id(),
QString::fromStdString(gameInfo.description())));
QUrl url;
url.setScheme("cockatrice");
url.setHost("joingame");
QUrlQuery query;
query.addQueryItem("hostname", client->serverName());
query.addQueryItem("port", QString::number(client->serverPort()));
query.addQueryItem("roomid", QString::number(gameInfo.room_id()));
query.addQueryItem("gameid", QString::number(gameInfo.game_id()));
url.setQuery(query);
QGuiApplication::clipboard()->setText(url.toString(QUrl::FullyEncoded));
});
QMenu menu;
@ -364,31 +372,13 @@ void GameSelector::joinGame(const bool asSpectator, const bool asJudge)
return;
}
bool spectator = asSpectator || game.player_count() == game.max_players();
bool overrideRestrictions = !tabSupervisor->getAdminLocked();
// Joining a full game without override privileges silently becomes a
// spectator join, so ask first instead of surprising the player.
const bool gameFull = game.player_count() == game.max_players();
if (gameFull && !asSpectator && !asJudge && !overrideRestrictions) {
const QMessageBox::StandardButton answer =
QMessageBox::question(this, tr("Join game"), tr("The game is full. Join as a spectator instead?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (answer != QMessageBox::Yes) {
return;
}
}
bool spectator = asSpectator || gameFull;
QString password;
if (game.with_password() && !(spectator && !game.spectators_need_password()) && !overrideRestrictions) {
bool ok;
// Games without a description have no sensible label — fall back to the
// game id so the prompt still tells the user which game they're entering.
const QString gameLabel = QString::fromStdString(game.description());
const QString prompt = gameLabel.isEmpty() ? tr("Password for game #%1:").arg(game.game_id())
: tr("Password for \"%1\":").arg(gameLabel);
password = getTextWithMax(this, tr("Join game"), prompt, QLineEdit::Password, QString(), &ok);
password = getTextWithMax(this, tr("Join game"), tr("Password:"), QLineEdit::Password, QString(), &ok);
if (!ok) {
return;
}

View file

@ -20,7 +20,6 @@
#include "../interface/card_picture_loader/card_picture_loader.h"
#include "../interface/widgets/cards/card_info_frame_widget.h"
#include "../interface/widgets/dialogs/dlg_create_game.h"
#include "../interface/widgets/server/game_link.h"
#include "../interface/widgets/server/user/user_list_manager.h"
#include "../interface/widgets/utility/completer_utils.h"
#include "../interface/widgets/utility/line_edit_completer.h"
@ -34,8 +33,6 @@
#include "tab_supervisor.h"
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QCompleter>
#include <QDebug>
#include <QDockWidget>
@ -334,9 +331,6 @@ void TabGame::retranslateUi()
if (aGameInfo) {
aGameInfo->setText(tr("Game &information"));
}
if (aCopyGameLink) {
aCopyGameLink->setText(tr("Cop&y game link"));
}
if (aConcede) {
if (game->getPlayerManager()->isMainPlayerConceded()) {
aConcede->setText(tr("Un&concede"));
@ -504,15 +498,6 @@ void TabGame::actGameInfo()
dlg.exec();
}
void TabGame::actCopyGameLink()
{
const QString link =
makeGameJoinLink(tabSupervisor->getClient()->serverName(), tabSupervisor->getClient()->serverPort(),
game->getGameMetaInfo()->proto().room_id(), game->getGameMetaInfo()->gameId(),
QString::fromStdString(game->getGameMetaInfo()->proto().description()));
QApplication::clipboard()->setText(link);
}
void TabGame::actConcede()
{
PlayerLogic *player = game->getPlayerManager()->getActiveLocalPlayer(game->getGameState()->getActivePlayer());
@ -1001,9 +986,6 @@ void TabGame::createMenuItems()
connect(aRotateViewCCW, &QAction::triggered, this, &TabGame::actRotateViewCCW);
aGameInfo = new QAction(this);
connect(aGameInfo, &QAction::triggered, this, &TabGame::actGameInfo);
aCopyGameLink = new QAction(this);
aCopyGameLink->setEnabled(!tabSupervisor->getIsLocalGame() && !tabSupervisor->getClient()->serverName().isEmpty());
connect(aCopyGameLink, &QAction::triggered, this, &TabGame::actCopyGameLink);
aConcede = new QAction(this);
connect(aConcede, &QAction::triggered, this, &TabGame::actConcede);
if (!game->getGameMetaInfo()->started()) {
@ -1042,7 +1024,6 @@ void TabGame::createMenuItems()
gameMenu->addAction(aRotateViewCCW);
gameMenu->addSeparator();
gameMenu->addAction(aGameInfo);
gameMenu->addAction(aCopyGameLink);
gameMenu->addAction(aConcede);
gameMenu->addAction(aFocusChat);
gameMenu->addAction(aLeaveGame);
@ -1065,7 +1046,6 @@ void TabGame::createReplayMenuItems()
aRotateViewCCW = nullptr;
aResetLayout = nullptr;
aGameInfo = nullptr;
aCopyGameLink = nullptr;
aConcede = nullptr;
aFocusChat = nullptr;
aLeaveGame = new QAction(this);

View file

@ -83,8 +83,8 @@ private:
QAction *playersSeparator;
QMenu *gameMenu, *viewMenu;
TearOffMenu *phasesMenu;
QAction *aGameInfo, *aConcede, *aCopyGameLink, *aLeaveGame, *aNextPhase, *aNextPhaseAction, *aNextTurn,
*aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
QAction *aGameInfo, *aConcede, *aLeaveGame, *aNextPhase, *aNextPhaseAction, *aNextTurn, *aReverseTurn,
*aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
QAction *aFocusChat;
QList<QAction *> phaseActions;
QAction *aCardMenu;
@ -148,7 +148,6 @@ private slots:
void actGameInfo();
void actConcede();
void actCopyGameLink();
void actRemoveLocalArrows();
void actRotateViewCW();
void actRotateViewCCW();

View file

@ -10,7 +10,6 @@ set(HEADERS
game/server_card.h
game/server_cardzone.h
game/server_counter.h
game/game_config.h
game/server_game.h
game/server_player.h
game/server_spectator.h

View file

@ -1,26 +0,0 @@
#ifndef GAME_CONFIG_H
#define GAME_CONFIG_H
#include <QList>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
struct GameConfig
{
ServerInfo_User creatorInfo;
int gameId = -1;
QString description;
QString password;
int maxPlayers = 2;
QList<int> gameTypes;
bool onlyBuddies = false;
bool onlyRegistered = false;
bool spectatorsAllowed = false;
bool spectatorsNeedPassword = false;
bool spectatorsCanTalk = true;
bool spectatorsSeeEverything = true;
int startingLifeTotal = 20;
bool shareDecklistsOnLoad = true;
};
#endif

View file

@ -53,19 +53,34 @@
#include <libcockatrice/protocol/pb/game_replay.pb.h>
#include <libcockatrice/utility/zone_names.h>
Server_Game::Server_Game(const GameConfig &config, Server_Room *_room)
: QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(config.creatorInfo)),
gameStarted(false), gameClosed(false), gameId(config.gameId), description(config.description.simplified()),
password(config.password), maxPlayers(config.maxPlayers), gameTypes(config.gameTypes), activePlayer(-1),
activePhase(-1), onlyBuddies(config.onlyBuddies), onlyRegistered(config.onlyRegistered),
spectatorsAllowed(config.spectatorsAllowed), spectatorsNeedPassword(config.spectatorsNeedPassword),
spectatorsCanTalk(config.spectatorsCanTalk), spectatorsSeeEverything(config.spectatorsSeeEverything),
startingLifeTotal(config.startingLifeTotal), shareDecklistsOnLoad(config.shareDecklistsOnLoad),
inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false),
turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr), gameMutex()
Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
int _gameId,
const QString &_description,
const QString &_password,
int _maxPlayers,
const QList<int> &_gameTypes,
bool _onlyBuddies,
bool _onlyRegistered,
bool _spectatorsAllowed,
bool _spectatorsNeedPassword,
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),
shareDecklistsOnLoad(_shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0),
firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr),
gameMutex()
{
currentReplay = new GameReplay;
currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId());
description = _description.simplified();
connect(this, &Server_Game::sigStartGameIfReady, this, &Server_Game::doStartGameIfReady, Qt::QueuedConnection);

View file

@ -21,7 +21,6 @@
#define SERVERGAME_H
#include "../server_response_containers.h"
#include "game_config.h"
#include <QDateTime>
#include <QMap>
@ -93,7 +92,21 @@ private slots:
public:
mutable QRecursiveMutex gameMutex;
Server_Game(const GameConfig &config, Server_Room *parent);
Server_Game(const ServerInfo_User &_creatorInfo,
int _gameId,
const QString &_description,
const QString &_password,
int _maxPlayers,
const QList<int> &_gameTypes,
bool _onlyBuddies,
bool _onlyRegistered,
bool _spectatorsAllowed,
bool _spectatorsNeedPassword,
bool _spectatorsCanTalk,
bool _spectatorsSeeEverything,
int _startingLifeTotal,
bool _shareDecklistsOnLoad,
Server_Room *parent);
~Server_Game() override;
Server_Room *getRoom() const
{

View file

@ -1,6 +1,5 @@
#include "server_protocolhandler.h"
#include "game/game_config.h"
#include "game/server_game.h"
#include "game/server_player.h"
#include "server_database_interface.h"
@ -921,22 +920,10 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
// When server doesn't permit registered users to exist, do not honor only-reg setting
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
GameConfig config{.creatorInfo = copyUserInfo(false),
.gameId = gameId,
.description = description,
.password = QString::fromStdString(cmd.password()),
.maxPlayers = static_cast<int>(cmd.max_players()),
.gameTypes = gameTypes,
.onlyBuddies = cmd.only_buddies(),
.onlyRegistered = onlyRegisteredUsers,
.spectatorsAllowed = cmd.spectators_allowed(),
.spectatorsNeedPassword = cmd.spectators_need_password(),
.spectatorsCanTalk = cmd.spectators_can_talk(),
.spectatorsSeeEverything = cmd.spectators_see_everything(),
.startingLifeTotal = startingLifeTotal,
.shareDecklistsOnLoad = shareDecklistsOnLoad};
auto *game = new Server_Game(config, room);
auto *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, shareDecklistsOnLoad, room);
game->addPlayer(this, rc, asSpectator, asJudge, false);
room->addGame(game);

View file

@ -1,4 +1,3 @@
#include "game/game_config.h"
#include "game/server_abstract_player.h"
#include "game/server_card.h"
#include "game/server_cardzone.h"
@ -23,21 +22,7 @@ TEST(ReverseCardMoveTest, MoveCardFromBottomTest)
// instantiate a fake server instance
FakeServer server;
Server_Room room(0, 0, "", "", "", "", false, "", {}, &server);
GameConfig config{.creatorInfo = user,
.gameId = 1,
.description = QString(),
.password = QString(),
.maxPlayers = 2,
.gameTypes = QList<int>(),
.onlyBuddies = false,
.onlyRegistered = false,
.spectatorsAllowed = false,
.spectatorsNeedPassword = false,
.spectatorsCanTalk = false,
.spectatorsSeeEverything = false,
.startingLifeTotal = 20,
.shareDecklistsOnLoad = false};
Server_Game game(config, &room);
Server_Game game(user, 1, "", "", 2, QList<int>(), false, false, false, false, false, false, 20, false, &room);
Server_AbstractPlayer player(&game, 1, user, false, nullptr);
Server_CardZone deckZone(&player, ZoneNames::DECK, true, ServerInfo_Zone::PublicZone);
Server_CardZone exileZone(&player, ZoneNames::EXILE, true, ServerInfo_Zone::PublicZone);