mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-23 18:06:26 -07:00
Compare commits
5 commits
d99798111e
...
6c8fcf7d19
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c8fcf7d19 | ||
|
|
94943f7ff3 | ||
|
|
078e67c56f | ||
|
|
d36865518e | ||
|
|
7c134efd29 |
12 changed files with 186 additions and 63 deletions
|
|
@ -246,6 +246,7 @@ 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
|
||||
|
|
|
|||
23
cockatrice/src/interface/widgets/server/game_link.cpp
Normal file
23
cockatrice/src/interface/widgets/server/game_link.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#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);
|
||||
}
|
||||
41
cockatrice/src/interface/widgets/server/game_link.h
Normal file
41
cockatrice/src/interface/widgets/server/game_link.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @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
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#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"
|
||||
|
||||
|
|
@ -18,8 +19,6 @@
|
|||
#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>
|
||||
|
|
@ -320,19 +319,12 @@ void GameSelector::customContextMenu(const QPoint &point)
|
|||
dlg.exec();
|
||||
});
|
||||
|
||||
QAction copyLink(tr("Copy Game Link"));
|
||||
QAction copyLink(tr("Cop&y game link"));
|
||||
connect(©Link, &QAction::triggered, this, [=, this]() {
|
||||
const ServerInfo_Game &gameInfo = gameListModel->getGame(index.data(Qt::UserRole).toInt());
|
||||
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));
|
||||
QGuiApplication::clipboard()->setText(makeGameJoinLink(client->serverName(), client->serverPort(),
|
||||
gameInfo.room_id(), gameInfo.game_id(),
|
||||
QString::fromStdString(gameInfo.description())));
|
||||
});
|
||||
|
||||
QMenu menu;
|
||||
|
|
@ -372,13 +364,31 @@ 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;
|
||||
password = getTextWithMax(this, tr("Join game"), tr("Password:"), QLineEdit::Password, QString(), &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);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#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"
|
||||
|
|
@ -33,6 +34,8 @@
|
|||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QCompleter>
|
||||
#include <QDebug>
|
||||
#include <QDockWidget>
|
||||
|
|
@ -331,6 +334,9 @@ 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"));
|
||||
|
|
@ -498,6 +504,15 @@ 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());
|
||||
|
|
@ -986,6 +1001,9 @@ 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()) {
|
||||
|
|
@ -1024,6 +1042,7 @@ void TabGame::createMenuItems()
|
|||
gameMenu->addAction(aRotateViewCCW);
|
||||
gameMenu->addSeparator();
|
||||
gameMenu->addAction(aGameInfo);
|
||||
gameMenu->addAction(aCopyGameLink);
|
||||
gameMenu->addAction(aConcede);
|
||||
gameMenu->addAction(aFocusChat);
|
||||
gameMenu->addAction(aLeaveGame);
|
||||
|
|
@ -1046,6 +1065,7 @@ void TabGame::createReplayMenuItems()
|
|||
aRotateViewCCW = nullptr;
|
||||
aResetLayout = nullptr;
|
||||
aGameInfo = nullptr;
|
||||
aCopyGameLink = nullptr;
|
||||
aConcede = nullptr;
|
||||
aFocusChat = nullptr;
|
||||
aLeaveGame = new QAction(this);
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ private:
|
|||
QAction *playersSeparator;
|
||||
QMenu *gameMenu, *viewMenu;
|
||||
TearOffMenu *phasesMenu;
|
||||
QAction *aGameInfo, *aConcede, *aLeaveGame, *aNextPhase, *aNextPhaseAction, *aNextTurn, *aReverseTurn,
|
||||
*aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
|
||||
QAction *aGameInfo, *aConcede, *aCopyGameLink, *aLeaveGame, *aNextPhase, *aNextPhaseAction, *aNextTurn,
|
||||
*aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
|
||||
QAction *aFocusChat;
|
||||
QList<QAction *> phaseActions;
|
||||
QAction *aCardMenu;
|
||||
|
|
@ -148,6 +148,7 @@ private slots:
|
|||
|
||||
void actGameInfo();
|
||||
void actConcede();
|
||||
void actCopyGameLink();
|
||||
void actRemoveLocalArrows();
|
||||
void actRotateViewCW();
|
||||
void actRotateViewCCW();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
#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
|
||||
|
|
@ -53,34 +53,19 @@
|
|||
#include <libcockatrice/protocol/pb/game_replay.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
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()
|
||||
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()
|
||||
{
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#define SERVERGAME_H
|
||||
|
||||
#include "../server_response_containers.h"
|
||||
#include "game_config.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
|
|
@ -92,21 +93,7 @@ private slots:
|
|||
|
||||
public:
|
||||
mutable QRecursiveMutex gameMutex;
|
||||
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(const GameConfig &config, Server_Room *parent);
|
||||
~Server_Game() override;
|
||||
Server_Room *getRoom() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "server_protocolhandler.h"
|
||||
|
||||
#include "game/game_config.h"
|
||||
#include "game/server_game.h"
|
||||
#include "game/server_player.h"
|
||||
#include "server_database_interface.h"
|
||||
|
|
@ -920,10 +921,22 @@ 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());
|
||||
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);
|
||||
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);
|
||||
|
||||
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
||||
room->addGame(game);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "game/game_config.h"
|
||||
#include "game/server_abstract_player.h"
|
||||
#include "game/server_card.h"
|
||||
#include "game/server_cardzone.h"
|
||||
|
|
@ -22,7 +23,21 @@ TEST(ReverseCardMoveTest, MoveCardFromBottomTest)
|
|||
// instantiate a fake server instance
|
||||
FakeServer server;
|
||||
Server_Room room(0, 0, "", "", "", "", false, "", {}, &server);
|
||||
Server_Game game(user, 1, "", "", 2, QList<int>(), false, false, false, false, false, false, 20, false, &room);
|
||||
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_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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue