mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-23 10:05:10 -07:00
Compare commits
6 commits
cefda69104
...
21871ffbae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21871ffbae | ||
|
|
76a386d6ed | ||
|
|
68e4fa054d | ||
|
|
6f86c45ea8 | ||
|
|
dade7ae78a | ||
|
|
8f52223322 |
24 changed files with 288 additions and 59 deletions
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: "Checkout repository"
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: "Initialize CodeQL"
|
||||
uses: github/codeql-action/init@v4
|
||||
|
|
|
|||
2
.github/workflows/docker-release.yml
vendored
2
.github/workflows/docker-release.yml
vendored
|
|
@ -127,7 +127,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: "Download digests"
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digest-*
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
#include "dlg_convert_deck_to_cod_format.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../deck_loader/deck_loader.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/visual_deck_storage_settings.h>
|
||||
|
||||
DialogConvertDeckToCodFormat::DialogConvertDeckToCodFormat(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
|
|
@ -38,3 +46,71 @@ bool DialogConvertDeckToCodFormat::dontAskAgain() const
|
|||
{
|
||||
return dontAskAgainCheckbox->isChecked();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
||||
{
|
||||
QFileInfo fileInfo(filePath);
|
||||
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
|
||||
|
||||
if (QFile::exists(newFileName)) {
|
||||
QMessageBox::StandardButton reply =
|
||||
QMessageBox::question(parent, QObject::tr("Overwrite Existing File?"),
|
||||
QObject::tr("A .cod version of this deck already exists. Overwrite it?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
return reply == QMessageBox::Yes;
|
||||
}
|
||||
return true; // Safe to proceed
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DialogConvertDeckToCodFormat::promptIfRequired(QWidget *parent,
|
||||
const QString &filePath,
|
||||
const std::function<bool()> &convert)
|
||||
{
|
||||
if (DeckFileFormat::getFormatFromName(filePath) == DeckFileFormat::Cockatrice) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieve saved preference if the prompt is disabled
|
||||
if (!SettingsCache::instance().visualDeckStorage().getVisualDeckStoragePromptForConversion()) {
|
||||
if (!SettingsCache::instance().visualDeckStorage().getVisualDeckStorageAlwaysConvert()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!confirmOverwriteIfExists(parent, filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return convert();
|
||||
}
|
||||
|
||||
// Show the dialog to the user
|
||||
DialogConvertDeckToCodFormat conversionDialog(parent);
|
||||
if (conversionDialog.exec() != QDialog::Accepted) {
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStoragePromptForConversion(
|
||||
!conversionDialog.dontAskAgain());
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStorageAlwaysConvert(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to convert file
|
||||
if (!confirmOverwriteIfExists(parent, filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!convert()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStoragePromptForConversion(false);
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStorageAlwaysConvert(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@
|
|||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <functional>
|
||||
|
||||
class QWidget;
|
||||
|
||||
class DialogConvertDeckToCodFormat : public QDialog
|
||||
{
|
||||
|
|
@ -24,6 +27,21 @@ public:
|
|||
|
||||
[[nodiscard]] bool dontAskAgain() const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the deck file at \a filePath can store tags.
|
||||
*
|
||||
* If the file is not a .cod deck, prompts the user for conversion to the
|
||||
* Cockatrice format, honoring the saved "always convert / don't ask again"
|
||||
* preference. On acceptance \a convert is called to perform the conversion.
|
||||
*
|
||||
* @param parent The widget to parent the prompt to.
|
||||
* @param filePath The path of the deck file to check.
|
||||
* @param convert Called to convert the deck once the user agrees.
|
||||
* @return true if tags can be stored (no conversion needed, or the conversion
|
||||
* was performed), false if the user declined to convert.
|
||||
*/
|
||||
static bool promptIfRequired(QWidget *parent, const QString &filePath, const std::function<bool()> &convert);
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
QLabel *label;
|
||||
|
|
|
|||
|
|
@ -525,6 +525,13 @@ void UserInfoPopup::rebuildActionButtons(const ServerInfo_User &userInfo, bool o
|
|||
connect(games, &QPushButton::clicked, this, [this, name] { emit showGamesRequested(name); });
|
||||
add(games);
|
||||
|
||||
// ── Invite (only while the inviter has a joinable game for this user) ────
|
||||
if (!isSelf && online && gameInviteAvailable && gameInviteAvailable(name)) {
|
||||
auto *invite = makeBtn(tr("Invite"), tr("Invite to your game"), actionArea, theme);
|
||||
connect(invite, &QPushButton::clicked, this, [this, name] { emit inviteRequested(name); });
|
||||
add(invite);
|
||||
}
|
||||
|
||||
// ── Buddy / ignore (registered users only) ────────────────────────────────
|
||||
if (!isSelf && isReg) {
|
||||
if (isBuddy) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <QMap>
|
||||
#include <QPixmap>
|
||||
#include <QStandardItemModel>
|
||||
#include <functional>
|
||||
#include <libcockatrice/network/server/remote/user_level.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_game.pb.h>
|
||||
|
|
@ -149,6 +150,17 @@ public:
|
|||
/** Re-pulls the avatar/card art for the currently shown user (e.g. after it loads). */
|
||||
void refreshHeader();
|
||||
|
||||
/**
|
||||
* Sets a predicate evaluated on every action-button rebuild. It receives
|
||||
* the name of the user the popup currently shows; when it returns true an
|
||||
* "Invite" button is shown. The popup itself never resolves the invite
|
||||
* link, it just forwards the request.
|
||||
*/
|
||||
void setGameInviteAvailable(std::function<bool(const QString &userName)> available)
|
||||
{
|
||||
gameInviteAvailable = std::move(available);
|
||||
}
|
||||
|
||||
signals:
|
||||
void mouseEnteredPopup();
|
||||
void mouseLeftPopup();
|
||||
|
|
@ -159,6 +171,7 @@ signals:
|
|||
|
||||
// ── Action signals — connect to UserContextMenu::exec*() ──────────────────
|
||||
void chatRequested(const QString &userName);
|
||||
void inviteRequested(const QString &userName);
|
||||
void detailsRequested(const QString &userName);
|
||||
void showGamesRequested(const QString &userName);
|
||||
void addBuddyRequested(const QString &userName);
|
||||
|
|
@ -200,6 +213,7 @@ private:
|
|||
QString currentUser;
|
||||
ServerInfo_User currentUserInfo;
|
||||
bool currentOnline = false;
|
||||
std::function<bool(const QString &userName)> gameInviteAvailable;
|
||||
|
||||
UserInfoHeaderWidget *header;
|
||||
QWidget *actionArea; ///< rebuilt per user
|
||||
|
|
|
|||
|
|
@ -345,6 +345,11 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
|
|||
&cardArtProvider->cache(), &cardArtParamsMap,
|
||||
window()); // parented to main window so it floats above siblings
|
||||
|
||||
// The invite availability is scoped to the room this list belongs to,
|
||||
// and gated on the room's buddy-only setting for the hovered user.
|
||||
userInfoPopup->setGameInviteAvailable(
|
||||
[this](const QString &userName) { return userContextMenu->hasGameInviteLink(userName); });
|
||||
|
||||
userInfoPopup->hide();
|
||||
userInfoPopup->setWindowOpacity(0.0);
|
||||
userInfoPopup->installEventFilter(this);
|
||||
|
|
@ -662,6 +667,8 @@ void UserListWidget::connectPopupSignals()
|
|||
|
||||
// Wire all action signals to UserContextMenu::exec*()
|
||||
connect(userInfoPopup, &UserInfoPopup::chatRequested, userContextMenu, &UserContextMenu::execChat);
|
||||
connect(userInfoPopup, &UserInfoPopup::inviteRequested, this,
|
||||
[this](const QString &userName) { userContextMenu->execInvite(userName); });
|
||||
connect(userInfoPopup, &UserInfoPopup::detailsRequested, userContextMenu, &UserContextMenu::execDetails);
|
||||
connect(userInfoPopup, &UserInfoPopup::showGamesRequested, userContextMenu, &UserContextMenu::execShowGames);
|
||||
connect(userInfoPopup, &UserInfoPopup::addBuddyRequested, userContextMenu, &UserContextMenu::execAddToBuddy);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <QTextEdit>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <functional>
|
||||
#include <libcockatrice/network/server/remote/user_level.h>
|
||||
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
|
||||
|
||||
class QTreeWidget;
|
||||
|
|
|
|||
|
|
@ -1091,7 +1091,8 @@ QList<GameInviteOption> TabSupervisor::getGameInviteLinksForRoom(int roomId) con
|
|||
// The inviter may be in several games of the same room (hosting one and
|
||||
// spectating another, for example). Return every game so the caller can
|
||||
// let the user choose which one to invite to.
|
||||
for (TabGame *tab : gameTabs) {
|
||||
for (auto it = gameTabs.cbegin(); it != gameTabs.cend(); ++it) {
|
||||
TabGame *tab = it.value();
|
||||
GameMetaInfo *metaInfo = tab->getGame()->getGameMetaInfo();
|
||||
if (metaInfo->proto().room_id() != roomId) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
#include "../visual_deck_storage_widget.h"
|
||||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
|
|
@ -499,21 +497,6 @@ void DeckPreviewWidget::actDeleteFile()
|
|||
// The folder widget removes this preview once the row is gone.
|
||||
}
|
||||
|
||||
static bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
||||
{
|
||||
QFileInfo fileInfo(filePath);
|
||||
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
|
||||
|
||||
if (QFile::exists(newFileName)) {
|
||||
QMessageBox::StandardButton reply =
|
||||
QMessageBox::question(parent, QObject::tr("Overwrite Existing File?"),
|
||||
QObject::tr("A .cod version of this deck already exists. Overwrite it?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
return reply == QMessageBox::Yes;
|
||||
}
|
||||
return true; // Safe to proceed
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the deck's file format supports tags.
|
||||
* If not, then prompt the user for file conversion.
|
||||
|
|
@ -521,45 +504,8 @@ static bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
|||
*/
|
||||
bool DeckPreviewWidget::promptFileConversionIfRequired()
|
||||
{
|
||||
if (DeckFileFormat::getFormatFromName(filePath) == DeckFileFormat::Cockatrice) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieve saved preference if the prompt is disabled
|
||||
if (!SettingsCache::instance().visualDeckStorage().getVisualDeckStoragePromptForConversion()) {
|
||||
if (!SettingsCache::instance().visualDeckStorage().getVisualDeckStorageAlwaysConvert()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!confirmOverwriteIfExists(this, filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DialogConvertDeckToCodFormat::promptIfRequired(this, filePath, [this] {
|
||||
model->convertToCockatriceFormat(row());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show the dialog to the user
|
||||
DialogConvertDeckToCodFormat conversionDialog(this);
|
||||
if (conversionDialog.exec() != QDialog::Accepted) {
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStoragePromptForConversion(
|
||||
!conversionDialog.dontAskAgain());
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStorageAlwaysConvert(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to convert file
|
||||
if (!confirmOverwriteIfExists(this, filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
model->convertToCockatriceFormat(row());
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStoragePromptForConversion(false);
|
||||
SettingsCache::instance().visualDeckStorage().setVisualDeckStorageAlwaysConvert(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_tournament.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
|
||||
#include <libcockatrice/protocol/pb/context_connection_state_changed.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_game_say.pb.h>
|
||||
|
|
@ -536,6 +537,15 @@ Server_AbstractParticipant::processGameCommand(const GameCommand &command, Respo
|
|||
case GameCommand::SET_PLAYMAT:
|
||||
return cmdSetPlaymat(command.GetExtension(Command_SetPlaymat::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::REPORT_MATCH_RESULT:
|
||||
return cmdReportMatchResult(command.GetExtension(Command_ReportMatchResult::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::ADVANCE_TOURNAMENT:
|
||||
return cmdAdvanceTournament(command.GetExtension(Command_AdvanceTournament::ext), rc, ges);
|
||||
break;
|
||||
case GameCommand::TOURNAMENT_SETTINGS_SELECT:
|
||||
return cmdTournamentSettingsSelect(command.GetExtension(Command_TournamentSettingsSelect::ext), rc, ges);
|
||||
break;
|
||||
default:
|
||||
return Response::RespInvalidCommand;
|
||||
}
|
||||
|
|
@ -584,3 +594,25 @@ void Server_AbstractParticipant::getInfo(ServerInfo_Player *info,
|
|||
{
|
||||
getProperties(*info->mutable_properties(), withUserInfo);
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdReportMatchResult(const Command_ReportMatchResult & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
Response::ResponseCode Server_AbstractParticipant::cmdAdvanceTournament(const Command_AdvanceTournament & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
Response::ResponseCode
|
||||
Server_AbstractParticipant::cmdTournamentSettingsSelect(const Command_TournamentSettingsSelect & /*cmd*/,
|
||||
ResponseContainer & /*rc*/,
|
||||
GameEventStorage & /*ges*/)
|
||||
{
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ class Command_DeckSelect;
|
|||
class Command_SetSideboardLock;
|
||||
class Command_ChangeZoneProperties;
|
||||
class Command_SetPlaymat;
|
||||
class Command_ReportMatchResult;
|
||||
class Command_AdvanceTournament;
|
||||
class Command_TournamentSettingsSelect;
|
||||
|
||||
class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container
|
||||
{
|
||||
|
|
@ -175,6 +178,13 @@ public:
|
|||
cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdReportMatchResult(const Command_ReportMatchResult &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode
|
||||
cmdAdvanceTournament(const Command_AdvanceTournament &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
||||
virtual Response::ResponseCode cmdTournamentSettingsSelect(const Command_TournamentSettingsSelect &cmd,
|
||||
ResponseContainer &rc,
|
||||
GameEventStorage &ges);
|
||||
|
||||
Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges);
|
||||
void sendGameEvent(const GameEventContainer &event);
|
||||
|
|
|
|||
|
|
@ -1663,3 +1663,9 @@ void Server_AbstractPlayer::getPlayerProperties(ServerInfo_PlayerProperties &res
|
|||
playmatParams->set_zoom(playmat.params.zoom);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_AbstractPlayer::setDeck(DeckList *_deck)
|
||||
{
|
||||
delete deck;
|
||||
deck = _deck;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public:
|
|||
{
|
||||
return deck;
|
||||
}
|
||||
void setDeck(DeckList *_deck);
|
||||
bool getReadyStart() const
|
||||
{
|
||||
return readyStart;
|
||||
|
|
|
|||
|
|
@ -363,6 +363,18 @@ void Server_Room::broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool
|
|||
sendRoomEvent(prepareRoomEvent(event), sendToIsl);
|
||||
}
|
||||
|
||||
Server_AbstractUserInterface *Server_Room::getUserInterfaceByName(const QString &name) const
|
||||
{
|
||||
usersLock.lockForRead();
|
||||
auto it = users.constFind(name);
|
||||
Server_AbstractUserInterface *result = nullptr;
|
||||
if (it != users.constEnd()) {
|
||||
result = it.value();
|
||||
}
|
||||
usersLock.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Server_Room::addGame(Server_Game *game)
|
||||
{
|
||||
ServerInfo_Room roomInfo;
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ public:
|
|||
void addGame(Server_Game *game);
|
||||
void removeGame(Server_Game *game);
|
||||
|
||||
Server_AbstractUserInterface *getUserInterfaceByName(const QString &name) const;
|
||||
|
||||
void sendRoomEvent(RoomEvent *event, bool sendToIsl = true);
|
||||
RoomEvent *prepareRoomEvent(const ::google::protobuf::Message &roomEvent);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ set(PROTO_FILES
|
|||
command_set_sideboard_lock.proto
|
||||
command_set_sideboard_plan.proto
|
||||
command_shuffle.proto
|
||||
command_tournament.proto
|
||||
command_undo_draw.proto
|
||||
commands.proto
|
||||
context_concede.proto
|
||||
|
|
@ -118,6 +119,7 @@ set(PROTO_FILES
|
|||
event_set_card_counter.proto
|
||||
event_set_counter.proto
|
||||
event_shuffle.proto
|
||||
event_tournament_state.proto
|
||||
event_user_joined.proto
|
||||
event_user_left.proto
|
||||
event_user_message.proto
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
syntax = "proto2";
|
||||
import "game_commands.proto";
|
||||
import "event_tournament_state.proto";
|
||||
|
||||
message Command_ReportMatchResult {
|
||||
extend GameCommand {
|
||||
optional Command_ReportMatchResult ext = 1037;
|
||||
}
|
||||
|
||||
optional sint32 game_id = 1 [default = -1];
|
||||
optional sint32 winner_id = 2 [default = -1];
|
||||
}
|
||||
|
||||
message Command_AdvanceTournament {
|
||||
extend GameCommand {
|
||||
optional Command_AdvanceTournament ext = 1038;
|
||||
}
|
||||
}
|
||||
|
||||
message Command_TournamentSettingsSelect {
|
||||
extend GameCommand {
|
||||
optional Command_TournamentSettingsSelect ext = 1039;
|
||||
}
|
||||
|
||||
optional TournamentSettings settings = 1;
|
||||
}
|
||||
|
|
@ -24,4 +24,10 @@ message Event_GameStateChanged {
|
|||
|
||||
// the amount of seconds since the game started
|
||||
optional uint32 seconds_elapsed = 5;
|
||||
|
||||
// whether this game is a tournament game
|
||||
optional bool is_tournament = 9;
|
||||
|
||||
// for tournament sub-games: the ID of the parent tournament game (-1 if not a sub-game)
|
||||
optional sint32 parent_game_id = 10 [default = -1];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
syntax = "proto2";
|
||||
import "game_event.proto";
|
||||
|
||||
message TournamentPlayer {
|
||||
optional sint32 player_id = 1;
|
||||
optional string player_name = 2;
|
||||
optional uint32 wins = 3;
|
||||
optional uint32 losses = 4;
|
||||
optional uint32 draws = 5;
|
||||
optional bool deck_submitted = 6;
|
||||
}
|
||||
|
||||
message TournamentPairing {
|
||||
optional sint32 player1_id = 1;
|
||||
optional sint32 player2_id = 2 [default = -1];
|
||||
optional sint32 game_id = 3;
|
||||
optional sint32 winner_id = 4 [default = -1];
|
||||
optional sint32 player1_match_wins = 5 [default = 0];
|
||||
optional sint32 player2_match_wins = 6 [default = 0];
|
||||
}
|
||||
|
||||
message TournamentSettings {
|
||||
optional uint32 games_per_match = 1 [default = 1];
|
||||
}
|
||||
|
||||
message Event_TournamentState {
|
||||
extend GameEvent {
|
||||
optional Event_TournamentState ext = 2027;
|
||||
}
|
||||
|
||||
enum TournamentPhase {
|
||||
PHASE_DECK_BUILDING = 0;
|
||||
PHASE_PLAYING = 1;
|
||||
PHASE_FINISHED = 2;
|
||||
}
|
||||
|
||||
optional TournamentPhase phase = 1;
|
||||
optional uint32 current_round = 2;
|
||||
optional uint32 total_rounds = 3;
|
||||
repeated TournamentPlayer players = 4;
|
||||
repeated TournamentPairing pairings = 5;
|
||||
optional TournamentSettings settings = 6;
|
||||
}
|
||||
|
|
@ -180,6 +180,15 @@ message GameCommand {
|
|||
/// Server: Server_Player::cmdSetPlaymat
|
||||
/// Client: reflected via player properties changed event
|
||||
SET_PLAYMAT = 1035;
|
||||
|
||||
/// Report the result of a tournament match sub-game.
|
||||
REPORT_MATCH_RESULT = 1037;
|
||||
|
||||
/// Advance the tournament to the next round.
|
||||
ADVANCE_TOURNAMENT = 1038;
|
||||
|
||||
/// Select tournament settings.
|
||||
TOURNAMENT_SETTINGS_SELECT = 1039;
|
||||
}
|
||||
|
||||
extensions 100 to max;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ message GameEvent {
|
|||
CHANGE_ZONE_PROPERTIES = 2020;
|
||||
REVERSE_TURN = 2021;
|
||||
GAME_LOG_NOTICE = 2022;
|
||||
TOURNAMENT_STATE = 2027;
|
||||
}
|
||||
optional sint32 player_id = 1 [default = -1];
|
||||
extensions 100 to max;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,12 @@ message Command_CreateGame {
|
|||
|
||||
// share decklists with all players when selected
|
||||
optional bool share_decklists_on_load = 14;
|
||||
|
||||
// number of games per match in tournament mode (e.g. 3 for best of 3)
|
||||
optional uint32 games_per_match = 15 [default = 1];
|
||||
|
||||
// whether this is a tournament game
|
||||
optional bool is_tournament = 16;
|
||||
}
|
||||
|
||||
message Command_JoinGame {
|
||||
|
|
|
|||
|
|
@ -65,4 +65,7 @@ message ServerInfo_Game {
|
|||
|
||||
// the current host of the game, which may differ from the creator after a host transfer
|
||||
optional ServerInfo_User host_info = 53;
|
||||
|
||||
// whether this game is a tournament game
|
||||
optional bool is_tournament = 54;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue