Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Citra-qt: Add multiplayer ui #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added dist/icons/connected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dist/icons/disconnected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion dist/icons/icons.qrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<RCC>
<qresource prefix="icons">
<file>citra.png</file>
<file>checked.png</file>
<file>connected.png</file>
<file>disconnected.png</file>
<file>failed.png</file>
<file>citra.png</file>
<file>lock.png</file>
</qresource>
</RCC>
7 changes: 7 additions & 0 deletions dist/icons/license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The icons in this folder have the following licenses:
checked.png Free for non-commercial use
connect.png CC BY-ND 3.0 (aquired from https://icons8.com)
disconnect.png CC BY-ND 3.0 (aquired from https://icons8.com)
failed.png Free for non-commercial use
lock.png CC BY-ND 3.0 (aquired from https://icons8.com)

Binary file added dist/icons/lock.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions src/citra_qt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,27 @@ add_executable(citra-qt
hotkeys.h
main.cpp
main.h
multiplayer/chat_room.h
multiplayer/chat_room.cpp
multiplayer/client_room.h
multiplayer/client_room.cpp
multiplayer/direct_connect.h
multiplayer/direct_connect.cpp
multiplayer/host_room.h
multiplayer/host_room.cpp
multiplayer/lobby.h
multiplayer/lobby_p.h
multiplayer/lobby.cpp
multiplayer/message.h
multiplayer/message.cpp
multiplayer/validation.h
ui_settings.cpp
ui_settings.h
updater/updater.cpp
updater/updater.h
updater/updater_p.h
util/clickable_label.h
util/clickable_label.cpp
util/spinbox.cpp
util/spinbox.h
util/util.cpp
Expand All @@ -77,6 +93,11 @@ set(UIS
configuration/configure_system.ui
configuration/configure_web.ui
debugger/registers.ui
multiplayer/direct_connect.ui
multiplayer/lobby.ui
multiplayer/chat_room.ui
multiplayer/client_room.ui
multiplayer/host_room.ui
aboutdialog.ui
hotkeys.ui
main.ui
Expand Down
2 changes: 0 additions & 2 deletions src/citra_qt/bootmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,10 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
setWindowTitle(QString::fromStdString(window_title));

InputCommon::Init();
Network::Init();
}

GRenderWindow::~GRenderWindow() {
InputCommon::Shutdown();
Network::Shutdown();
}

void GRenderWindow::moveContext() {
Expand Down
34 changes: 34 additions & 0 deletions src/citra_qt/configuration/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "citra_qt/ui_settings.h"
#include "common/file_util.h"
#include "input_common/main.h"
#include "network/network.h"

Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
Expand Down Expand Up @@ -150,6 +151,12 @@ void Config::ReadValues() {
qt_config->value("verify_endpoint_url", "https://services.citra-emu.org/api/profile")
.toString()
.toStdString();
Settings::values.announce_multiplayer_room_endpoint_url =
qt_config
->value("announce_multiplayer_room_endpoint_url",
"https://services.citra-emu.org/api/multiplayer/rooms")
.toString()
.toStdString();
Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString();
Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString();
qt_config->endGroup();
Expand Down Expand Up @@ -212,6 +219,18 @@ void Config::ReadValues() {
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();

qt_config->beginGroup("Multiplayer");
UISettings::values.nickname = qt_config->value("nickname", "").toString();
UISettings::values.ip = qt_config->value("ip", "").toString();
UISettings::values.port = qt_config->value("port", Network::DefaultRoomPort).toString();
UISettings::values.room_nickname = qt_config->value("room_nickname", "").toString();
UISettings::values.room_name = qt_config->value("room_name", "").toString();
UISettings::values.room_port = qt_config->value("room_port", 24872).toString();
UISettings::values.host_type = qt_config->value("host_type", 0).toString();
UISettings::values.max_player = qt_config->value("max_player", 8).toUInt();
UISettings::values.game_id = qt_config->value("game_id", 0).toULongLong();
qt_config->endGroup();

qt_config->endGroup();
}

Expand Down Expand Up @@ -306,6 +325,9 @@ void Config::SaveValues() {
QString::fromStdString(Settings::values.telemetry_endpoint_url));
qt_config->setValue("verify_endpoint_url",
QString::fromStdString(Settings::values.verify_endpoint_url));
qt_config->setValue(
"announce_multiplayer_room_endpoint_url",
QString::fromStdString(Settings::values.announce_multiplayer_room_endpoint_url));
qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username));
qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token));
qt_config->endGroup();
Expand Down Expand Up @@ -351,6 +373,18 @@ void Config::SaveValues() {
qt_config->setValue("firstStart", UISettings::values.first_start);
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);

qt_config->beginGroup("Multiplayer");
qt_config->setValue("nickname", UISettings::values.nickname);
qt_config->setValue("ip", UISettings::values.ip);
qt_config->setValue("port", UISettings::values.port);
qt_config->setValue("room_nickname", UISettings::values.room_nickname);
qt_config->setValue("room_name", UISettings::values.room_name);
qt_config->setValue("room_port", UISettings::values.room_port);
qt_config->setValue("host_type", UISettings::values.host_type);
qt_config->setValue("max_player", UISettings::values.max_player);
qt_config->setValue("game_id", UISettings::values.game_id);
qt_config->endGroup();

qt_config->endGroup();
}

Expand Down
4 changes: 4 additions & 0 deletions src/citra_qt/game_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
}

QStandardItemModel* GameList::GetModel() const {
return item_model;
}

void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
if (!FileUtil::Exists(dir_path.toStdString()) ||
!FileUtil::IsDirectory(dir_path.toStdString())) {
Expand Down
2 changes: 2 additions & 0 deletions src/citra_qt/game_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class GameList : public QWidget {
void SaveInterfaceLayout();
void LoadInterfaceLayout();

QStandardItemModel* GetModel() const;

static const QStringList supported_file_extensions;

signals:
Expand Down
140 changes: 140 additions & 0 deletions src/citra_qt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
#include "citra_qt/multiplayer/client_room.h"
#include "citra_qt/multiplayer/direct_connect.h"
#include "citra_qt/multiplayer/host_room.h"
#include "citra_qt/multiplayer/lobby.h"
#include "citra_qt/multiplayer/message.h"
#include "citra_qt/ui_settings.h"
#include "citra_qt/updater/updater.h"
#include "citra_qt/util/clickable_label.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
Expand Down Expand Up @@ -114,6 +120,18 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
ConnectMenuEvents();
ConnectWidgetEvents();

Network::Init();

if (auto member = Network::GetRoomMember().lock()) {
// register the network structs to use in slots and signals
qRegisterMetaType<Network::RoomMember::State>();
state_callback_handle = member->BindOnStateChanged(
[this](const Network::RoomMember::State& state) { emit NetworkStateChanged(state); });
connect(this, &GMainWindow::NetworkStateChanged, this, &GMainWindow::OnNetworkStateChanged);
}

qRegisterMetaType<Common::WebResult>();

setWindowTitle(QString("Citra %1| %2-%3")
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
show();
Expand Down Expand Up @@ -141,6 +159,13 @@ GMainWindow::~GMainWindow() {
delete render_window;

Pica::g_debug_context.reset();

if (state_callback_handle) {
if (auto member = Network::GetRoomMember().lock()) {
member->Unbind(state_callback_handle);
}
}
Network::Shutdown();
}

void GMainWindow::InitializeWidgets() {
Expand Down Expand Up @@ -178,12 +203,21 @@ void GMainWindow::InitializeWidgets() {
tr("Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For "
"full-speed emulation this should be at most 16.67 ms."));

announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>();
announce_multiplayer_session->BindErrorCallback(
[this](const Common::WebResult& result) { emit AnnounceFailed(result); });
connect(this, &GMainWindow::AnnounceFailed, this, &GMainWindow::OnAnnounceFailed);
network_status = new ClickableLabel();
network_status->setToolTip(tr("Current connection status."));

for (auto& label : {emu_speed_label, game_fps_label, emu_frametime_label}) {
label->setVisible(false);
label->setFrameStyle(QFrame::NoFrame);
label->setContentsMargins(4, 0, 4, 0);
statusBar()->addPermanentWidget(label, 0);
}
statusBar()->addPermanentWidget(network_status, 0);
network_status->setPixmap(QPixmap(":/icons/disconnected.png"));
statusBar()->setVisible(true);
setStyleSheet("QStatusBar::item{border: none;}");
}
Expand Down Expand Up @@ -345,6 +379,7 @@ void GMainWindow::ConnectWidgetEvents() {
connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);

connect(this, &GMainWindow::UpdateProgress, this, &GMainWindow::OnUpdateProgress);
connect(network_status, &ClickableLabel::clicked, this, &GMainWindow::OnOpenNetworkRoom);
}

void GMainWindow::ConnectMenuEvents() {
Expand All @@ -369,6 +404,15 @@ void GMainWindow::ConnectMenuEvents() {
ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F"));
connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);

// Multiplayer
connect(ui.action_View_Lobby, &QAction::triggered, this, &GMainWindow::OnViewLobby);
connect(ui.action_Start_Room, &QAction::triggered, this, &GMainWindow::OnCreateRoom);
connect(ui.action_Stop_Room, &QAction::triggered, this, &GMainWindow::OnCloseRoom);
connect(ui.action_Connect_To_Room, &QAction::triggered, this,
&GMainWindow::OnDirectConnectToRoom);
connect(ui.action_Chat, &QAction::triggered, this, &GMainWindow::OnOpenNetworkRoom);

ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);

Expand Down Expand Up @@ -784,6 +828,27 @@ void GMainWindow::OnMenuRecentFile() {
}
}

void GMainWindow::OnNetworkStateChanged(const Network::RoomMember::State& state) {
if (state == Network::RoomMember::State::Joined) {
network_status->setPixmap(QPixmap(":/icons/connected.png"));
ui.action_Chat->setEnabled(true);
return;
}
network_status->setPixmap(QPixmap(":/icons/disconnected.png"));
ui.action_Chat->setDisabled(true);

ChangeRoomState();
}

void GMainWindow::OnAnnounceFailed(const Common::WebResult& result) {
announce_multiplayer_session->Stop();
QMessageBox::warning(
this, tr("Error"),
tr("Announcing the room failed. \nThe room will not get listed publicy. \nError: ") +
QString::fromStdString(result.result_string),
QMessageBox::Ok);
}

void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true);
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
Expand Down Expand Up @@ -900,6 +965,59 @@ void GMainWindow::OnCreateGraphicsSurfaceViewer() {
graphicsSurfaceViewerWidget->show();
}

static void BringWidgetToFront(QWidget* widget) {
widget->show();
widget->activateWindow();
widget->raise();
}

void GMainWindow::OnViewLobby() {
if (lobby == nullptr) {
lobby = new Lobby(this, game_list->GetModel(), announce_multiplayer_session);
connect(lobby, &QWidget::close, this, [&] { lobby = nullptr; });
}
BringWidgetToFront(lobby);
}

void GMainWindow::OnCreateRoom() {
if (host_room == nullptr) {
host_room = new HostRoomWindow(this, game_list->GetModel(), announce_multiplayer_session);
connect(host_room, &QWidget::close, this, [&] { host_room = nullptr; });
}
BringWidgetToFront(host_room);
}

void GMainWindow::OnCloseRoom() {
if (auto room = Network::GetRoom().lock()) {
if (room->GetState() == Network::Room::State::Open) {
if (NetworkMessage::WarnCloseRoom()) {
room->Destroy();
announce_multiplayer_session->Stop();
}
}
}
}

void GMainWindow::OnOpenNetworkRoom() {
if (auto member = Network::GetRoomMember().lock()) {
if (member->IsConnected()) {
if (client_room == nullptr) {
client_room = new ClientRoomWindow(this);
connect(client_room, &QWidget::close, this, [&] { client_room = nullptr; });
}
BringWidgetToFront(client_room);
}
}
}

void GMainWindow::OnDirectConnectToRoom() {
if (direct_connect == nullptr) {
direct_connect = new DirectConnectWindow(this);
connect(direct_connect, &QWidget::close, this, [&] { direct_connect = nullptr; });
}
BringWidgetToFront(direct_connect);
}

void GMainWindow::UpdateStatusBar() {
if (emu_thread == nullptr) {
status_bar_update_timer.stop();
Expand Down Expand Up @@ -1024,6 +1142,16 @@ void GMainWindow::closeEvent(QCloseEvent* event) {

render_window->close();

// Close Multiplayer windows
if (host_room)
host_room->close();
if (direct_connect)
direct_connect->close();
if (client_room)
client_room->close();
if (lobby)
lobby->close();

QWidget::closeEvent(event);
}

Expand Down Expand Up @@ -1084,6 +1212,18 @@ void GMainWindow::UpdateUITheme() {
}
}

void GMainWindow::ChangeRoomState() {
if (auto room = Network::GetRoom().lock()) {
if (room->GetState() == Network::Room::State::Open) {
ui.action_Start_Room->setDisabled(true);
ui.action_Stop_Room->setEnabled(true);
return;
}
ui.action_Start_Room->setEnabled(true);
ui.action_Stop_Room->setDisabled(true);
}
}

#ifdef main
#undef main
#endif
Expand Down
Loading