Cockatrice/cockatrice/src/interface/widgets/tabs/tab_server.cpp
BruebachL 59d90db3c7
Define Cockatrice as an editor/handler for .cod files and cockatrice:// protocol on all platforms (#6775)
* [Application] Add single instance guard and mime types.

Took 2 hours 39 minutes

Took 18 minutes

Took 5 minutes

Took 12 seconds


Took 11 seconds

* Rework

Took 30 minutes


Took 50 seconds

* Only enforce single instance if launched with arguments.

Took 5 minutes

* Prototype intents

Took 53 minutes

Took 6 seconds

* Connect/disconnect and join game/room intents.

Took 3 hours 14 minutes

Took 2 seconds

Took 15 seconds

* Fix include.

Took 1 minute


Took 23 seconds

Took 2 seconds

* Mac handling.

Took 10 minutes

Took 12 seconds

Took 3 minutes

* Lint.

Took 3 minutes

* Rebase.

Took 3 minutes

Took 17 seconds

* Implement UrlSchemeEventFilter

Took 10 minutes

Took 7 seconds

* Qt Moc

Took 3 minutes

* Modern PList.

Took 21 minutes

Took 1 minute

* Debug output.

Took 6 minutes

Took 19 minutes

* Watch file:// prefix.

Took 15 minutes

Took 7 seconds

* Better handler.

Took 6 minutes

* Don't store reference in member

Took 5 minutes

* Move impl to cpp, fix lifetime issues.

Took 11 minutes

Took 2 minutes

* Better single-instance handoff, url intent harded

copy game link context-menu
Polish for installers

Took 35 minutes

Took 8 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-08-08 19:02:19 +02:00

244 lines
8.9 KiB
C++

#include "tab_server.h"
#include "../interface/widgets/server/user/user_list_widget.h"
#include "tab_supervisor.h"
#include <QHBoxLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/event_list_rooms.pb.h>
#include <libcockatrice/protocol/pb/event_server_message.pb.h>
#include <libcockatrice/protocol/pb/response_join_room.pb.h>
#include <libcockatrice/protocol/pb/session_commands.pb.h>
#include <libcockatrice/protocol/pending_command.h>
RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) : QGroupBox(parent), client(_client)
{
roomList = new QTreeWidget;
roomList->setRootIsDecorated(false);
roomList->setColumnCount(5);
roomList->header()->setStretchLastSection(false);
roomList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
roomList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
roomList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
roomList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
joinButton = new QPushButton;
connect(joinButton, &QPushButton::clicked, this, &RoomSelector::joinClicked);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(joinButton);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(roomList);
vbox->addLayout(buttonLayout);
retranslateUi();
setLayout(vbox);
connect(client, &AbstractClient::listRoomsEventReceived, this, &RoomSelector::processListRoomsEvent);
connect(roomList, &QTreeWidget::activated, this, &RoomSelector::joinClicked);
client->sendCommand(client->prepareSessionCommand(Command_ListRooms()));
}
void RoomSelector::retranslateUi()
{
setTitle(tr("Rooms"));
joinButton->setText(tr("Joi&n"));
QTreeWidgetItem *header = roomList->headerItem();
header->setText(0, tr("Room"));
header->setText(1, tr("Description"));
header->setText(2, tr("Permissions"));
header->setText(3, tr("Players"));
header->setText(4, tr("Games"));
header->setTextAlignment(2, Qt::AlignRight);
header->setTextAlignment(3, Qt::AlignRight);
header->setTextAlignment(4, Qt::AlignRight);
}
void RoomSelector::processListRoomsEvent(const Event_ListRooms &event)
{
const int roomListSize = event.room_list_size();
for (int i = 0; i < roomListSize; ++i) {
const ServerInfo_Room &room = event.room_list(i);
for (int j = 0; j < roomList->topLevelItemCount(); ++j) {
QTreeWidgetItem *twi = roomList->topLevelItem(j);
if (twi->data(0, Qt::UserRole).toInt() == room.room_id()) {
if (room.has_name()) {
twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name()));
}
if (room.has_description()) {
twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description()));
}
if (room.has_permissionlevel()) {
twi->setData(2, Qt::DisplayRole, getRoomPermissionDisplay(room));
}
if (room.has_player_count()) {
twi->setData(3, Qt::DisplayRole, room.player_count());
}
if (room.has_game_count()) {
twi->setData(4, Qt::DisplayRole, room.game_count());
}
return;
}
}
QTreeWidgetItem *twi = new QTreeWidgetItem;
twi->setData(0, Qt::UserRole, room.room_id());
if (room.has_name()) {
twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name()));
}
if (room.has_description()) {
twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description()));
}
if (room.has_permissionlevel()) {
twi->setData(2, Qt::DisplayRole, getRoomPermissionDisplay(room));
}
twi->setData(3, Qt::DisplayRole, room.player_count());
twi->setData(4, Qt::DisplayRole, room.game_count());
twi->setTextAlignment(2, Qt::AlignRight);
twi->setTextAlignment(3, Qt::AlignRight);
twi->setTextAlignment(4, Qt::AlignRight);
roomList->addTopLevelItem(twi);
if (room.has_auto_join()) {
if (room.auto_join()) {
emit joinRoomRequest(room.room_id(), false);
}
}
}
}
QString RoomSelector::getRoomPermissionDisplay(const ServerInfo_Room &room)
{
/*
* A server room can have a permission level and a privilege level. How ever we want to display only the necessary
* information on the server tab needed to inform users of required permissions to enter a room. If the room has a
* privilege level the server tab will display the privilege level in the "permissions" column in the row however if
* the room contains a permissions level for the room the permissions level defined for the room will be displayed.
*/
QString roomPermissionDisplay = QString::fromStdString(room.privilegelevel()).toLower();
if (QString::fromStdString(room.permissionlevel()).toLower() != "none") {
roomPermissionDisplay = QString::fromStdString(room.permissionlevel()).toLower();
}
if (roomPermissionDisplay == "") { // catch all for misconfigured .ini room definitions
roomPermissionDisplay = "none";
}
return roomPermissionDisplay;
}
void RoomSelector::joinClicked()
{
QTreeWidgetItem *twi = roomList->currentItem();
if (!twi) {
return;
}
int id = twi->data(0, Qt::UserRole).toInt();
emit joinRoomRequest(id, true);
}
TabServer::TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client)
{
roomSelector = new RoomSelector(client);
serverInfoBox = new QTextBrowser;
serverInfoBox->setOpenExternalLinks(true);
connect(roomSelector, &RoomSelector::joinRoomRequest, this, &TabServer::joinRoom);
connect(client, &AbstractClient::serverMessageEventReceived, this, &TabServer::processServerMessageEvent);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(roomSelector);
vbox->addWidget(serverInfoBox);
retranslateUi();
QWidget *mainWidget = new QWidget(this);
mainWidget->setLayout(vbox);
setCentralWidget(mainWidget);
}
void TabServer::retranslateUi()
{
roomSelector->retranslateUi();
}
void TabServer::processServerMessageEvent(const Event_ServerMessage &event)
{
serverInfoBox->setHtml(QString::fromStdString(event.message()));
if (shouldEmitUpdate) {
// prevent the initial server message from taking attention from ping icon
emit userEvent();
} else {
shouldEmitUpdate = true;
}
}
void TabServer::joinRoom(int id, bool setCurrent)
{
TabRoom *room = tabSupervisor->getRoomTabs().value(id);
if (!room) {
Command_JoinRoom cmd;
cmd.set_room_id(id);
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(setCurrent);
connect(pend, &PendingCommand::finished, this,
[this, id](const Response &r, const CommandContainer &c, const QVariant &v) {
joinRoomFinished(r, c, v, id);
});
client->sendCommand(pend);
return;
}
if (setCurrent) {
tabSupervisor->setCurrentWidget((QWidget *)room);
}
}
void TabServer::joinRoomFinished(const Response &r,
const CommandContainer & /*commandContainer*/,
const QVariant &extraData,
int roomId)
{
switch (r.response_code()) {
case Response::RespOk:
break;
case Response::RespNameNotFound:
QMessageBox::critical(this, tr("Error"),
tr("Failed to join the server room: it doesn't exist on the server."));
emit roomJoinFailed(roomId);
return;
case Response::RespContextError:
QMessageBox::critical(
this, tr("Error"),
tr("The server thinks you are in the server room but your client is unable to display it. "
"Try restarting your client."));
emit roomJoinFailed(roomId);
return;
case Response::RespUserLevelTooLow:
QMessageBox::critical(this, tr("Error"),
tr("You do not have the required permission to join this server room."));
emit roomJoinFailed(roomId);
return;
default:
QMessageBox::critical(
this, tr("Error"),
tr("Failed to join the server room due to an unknown error: %1.").arg(r.response_code()));
emit roomJoinFailed(roomId);
return;
}
const Response_JoinRoom &resp = r.GetExtension(Response_JoinRoom::ext);
emit roomJoined(resp.room_info(), extraData.toBool());
}