[Game] Extract makeGameJoinLink helper for cockatrice://joingame links (#7133)

The inline URL building in GameSelector's copy-link action moves into a
shared helper so every invite/copy site produces the same link format.
The helper embeds the game description as an extra "game" query item
(percent-encoded); links without it stay valid — the receiving parser
ignores unknown query items.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-16 22:14:49 +02:00 committed by GitHub
parent 7c134efd29
commit d36865518e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 69 additions and 12 deletions

View file

@ -246,6 +246,7 @@ set(cockatrice_SOURCES
src/interface/widgets/replay/replay_widget.cpp src/interface/widgets/replay/replay_widget.cpp
src/interface/widgets/server/chat_view/chat_view.cpp src/interface/widgets/server/chat_view/chat_view.cpp
src/interface/widgets/server/game_filter_configs.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.cpp
src/interface/widgets/server/game_selector_quick_filter_toolbar.cpp src/interface/widgets/server/game_selector_quick_filter_toolbar.cpp
src/interface/widgets/server/games_model.cpp src/interface/widgets/server/games_model.cpp

View 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);
}

View 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

View file

@ -7,6 +7,7 @@
#include "../interface/widgets/tabs/tab_room.h" #include "../interface/widgets/tabs/tab_room.h"
#include "../interface/widgets/tabs/tab_supervisor.h" #include "../interface/widgets/tabs/tab_supervisor.h"
#include "../interface/widgets/utility/get_text_with_max.h" #include "../interface/widgets/utility/get_text_with_max.h"
#include "game_link.h"
#include "games_model.h" #include "games_model.h"
#include "user/user_list_manager.h" #include "user/user_list_manager.h"
@ -18,8 +19,6 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QTreeView> #include <QTreeView>
#include <QUrl>
#include <QUrlQuery>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/response.pb.h> #include <libcockatrice/protocol/pb/response.pb.h>
#include <libcockatrice/protocol/pb/room_commands.pb.h> #include <libcockatrice/protocol/pb/room_commands.pb.h>
@ -323,16 +322,9 @@ void GameSelector::customContextMenu(const QPoint &point)
QAction copyLink(tr("Copy Game Link")); QAction copyLink(tr("Copy Game Link"));
connect(&copyLink, &QAction::triggered, this, [=, this]() { connect(&copyLink, &QAction::triggered, this, [=, this]() {
const ServerInfo_Game &gameInfo = gameListModel->getGame(index.data(Qt::UserRole).toInt()); const ServerInfo_Game &gameInfo = gameListModel->getGame(index.data(Qt::UserRole).toInt());
QUrl url; QGuiApplication::clipboard()->setText(makeGameJoinLink(client->serverName(), client->serverPort(),
url.setScheme("cockatrice"); gameInfo.room_id(), gameInfo.game_id(),
url.setHost("joingame"); QString::fromStdString(gameInfo.description())));
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; QMenu menu;