From 5d84f3831c3dcc1f6464ff01c93382088e261fad Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 6 Aug 2024 18:19:01 +0200 Subject: [PATCH] start decoupling LAN/netplay dialogs from the actual modules --- src/frontend/qt_sdl/CMakeLists.txt | 2 + src/frontend/qt_sdl/LAN.cpp | 338 ---------------------- src/frontend/qt_sdl/LAN.h | 105 ------- src/frontend/qt_sdl/LANDialog.cpp | 401 ++++++++++++++++++++++++++ src/frontend/qt_sdl/LANDialog.h | 127 ++++++++ src/frontend/qt_sdl/Netplay.cpp | 138 --------- src/frontend/qt_sdl/Netplay.h | 90 ------ src/frontend/qt_sdl/NetplayDialog.cpp | 181 ++++++++++++ src/frontend/qt_sdl/NetplayDialog.h | 111 +++++++ 9 files changed, 822 insertions(+), 671 deletions(-) create mode 100644 src/frontend/qt_sdl/LANDialog.cpp create mode 100644 src/frontend/qt_sdl/LANDialog.h create mode 100644 src/frontend/qt_sdl/NetplayDialog.cpp create mode 100644 src/frontend/qt_sdl/NetplayDialog.h diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 972539fe..32ffff9e 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -54,6 +54,8 @@ set(SOURCES_QT_SDL LAN.cpp Netplay.cpp + LANDialog.cpp + NetplayDialog.cpp ) if (APPLE) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index e2c85d75..9a86b2eb 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -63,344 +63,6 @@ using namespace melonDS; - -extern EmuThread* emuThread; -LANStartClientDialog* lanClientDlg = nullptr; -LANDialog* lanDlg = nullptr; - - -LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - // TODO: remember the last setting? so this doesn't suck massively - // we could also remember the player name (and auto-init it from the firmware name or whatever) - ui->sbNumPlayers->setRange(2, 16); - ui->sbNumPlayers->setValue(16); -} - -LANStartHostDialog::~LANStartHostDialog() -{ - delete ui; -} - -void LANStartHostDialog::done(int r) -{ - if (r == QDialog::Accepted) - { - std::string player = ui->txtPlayerName->text().toStdString(); - int numplayers = ui->sbNumPlayers->value(); - - // TODO validate input!! - - lanDlg = LANDialog::openDlg(parentWidget()); - - LAN::StartHost(player.c_str(), numplayers); - } - - QDialog::done(r); -} - - -LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartClientDialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - QStandardItemModel* model = new QStandardItemModel(); - ui->tvAvailableGames->setModel(model); - const QStringList listheader = {"Name", "Players", "Status", "Host IP"}; - model->setHorizontalHeaderLabels(listheader); - - connect(ui->tvAvailableGames->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(onGameSelectionChanged(const QItemSelection&, const QItemSelection&))); - - ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Connect"); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - QPushButton* btn = ui->buttonBox->addButton("Direct connect...", QDialogButtonBox::ActionRole); - connect(btn, SIGNAL(clicked()), this, SLOT(onDirectConnect())); - - connect(this, &LANStartClientDialog::sgUpdateDiscoveryList, this, &LANStartClientDialog::doUpdateDiscoveryList); - - lanClientDlg = this; - LAN::StartDiscovery(); -} - -LANStartClientDialog::~LANStartClientDialog() -{ - lanClientDlg = nullptr; - delete ui; -} - -void LANStartClientDialog::onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev) -{ - QModelIndexList indlist = cur.indexes(); - if (indlist.count() == 0) - { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - } - else - { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - } -} - -void LANStartClientDialog::on_tvAvailableGames_doubleClicked(QModelIndex index) -{ - done(QDialog::Accepted); -} - -void LANStartClientDialog::onDirectConnect() -{ - if (ui->txtPlayerName->text().trimmed().isEmpty()) - { - QMessageBox::warning(this, "melonDS", "Please enter a player name before connecting."); - return; - } - - QString host = QInputDialog::getText(this, "Direct connect", "Host address:"); - if (host.isEmpty()) return; - - std::string hostname = host.toStdString(); - std::string player = ui->txtPlayerName->text().toStdString(); - - setEnabled(false); - LAN::EndDiscovery(); - if (!LAN::StartClient(player.c_str(), hostname.c_str())) - { - QString msg = QString("Failed to connect to the host %0.").arg(QString::fromStdString(hostname)); - QMessageBox::warning(this, "melonDS", msg); - setEnabled(true); - LAN::StartDiscovery(); - return; - } - - setEnabled(true); - lanDlg = LANDialog::openDlg(parentWidget()); - QDialog::done(QDialog::Accepted); -} - -void LANStartClientDialog::done(int r) -{ - if (r == QDialog::Accepted) - { - if (ui->txtPlayerName->text().trimmed().isEmpty()) - { - QMessageBox::warning(this, "melonDS", "Please enter a player name before connecting."); - return; - } - - QModelIndexList indlist = ui->tvAvailableGames->selectionModel()->selectedRows(); - if (indlist.count() == 0) return; - - QStandardItemModel* model = (QStandardItemModel*)ui->tvAvailableGames->model(); - QStandardItem* item = model->item(indlist[0].row()); - u32 addr = item->data().toUInt(); - char hostname[16]; - snprintf(hostname, 16, "%d.%d.%d.%d", (addr>>24), ((addr>>16)&0xFF), ((addr>>8)&0xFF), (addr&0xFF)); - - std::string player = ui->txtPlayerName->text().toStdString(); - - setEnabled(false); - LAN::EndDiscovery(); - if (!LAN::StartClient(player.c_str(), hostname)) - { - QString msg = QString("Failed to connect to the host %0.").arg(QString(hostname)); - QMessageBox::warning(this, "melonDS", msg); - setEnabled(true); - LAN::StartDiscovery(); - return; - } - - setEnabled(true); - lanDlg = LANDialog::openDlg(parentWidget()); - } - else - { - LAN::EndDiscovery(); - } - - QDialog::done(r); -} - -void LANStartClientDialog::updateDiscoveryList() -{ - emit sgUpdateDiscoveryList(); -} - -void LANStartClientDialog::doUpdateDiscoveryList() -{ - LAN::DiscoveryMutex.lock(); - - QStandardItemModel* model = (QStandardItemModel*)ui->tvAvailableGames->model(); - int curcount = model->rowCount(); - int newcount = LAN::DiscoveryList.size(); - if (curcount > newcount) - { - model->removeRows(newcount, curcount-newcount); - } - else if (curcount < newcount) - { - for (int i = curcount; i < newcount; i++) - { - QList row; - row.append(new QStandardItem()); - row.append(new QStandardItem()); - row.append(new QStandardItem()); - row.append(new QStandardItem()); - model->appendRow(row); - } - } - - int i = 0; - for (const auto& [key, data] : LAN::DiscoveryList) - { - model->item(i, 0)->setText(data.SessionName); - model->item(i, 0)->setData(QVariant(key)); - - QString plcount = QString("%0/%1").arg(data.NumPlayers).arg(data.MaxPlayers); - model->item(i, 1)->setText(plcount); - - QString status; - switch (data.Status) - { - case 0: status = "Idle"; break; - case 1: status = "Playing"; break; - } - model->item(i, 2)->setText(status); - - QString ip = QString("%0.%1.%2.%3").arg(key>>24).arg((key>>16)&0xFF).arg((key>>8)&0xFF).arg(key&0xFF); - model->item(i, 3)->setText(ip); - - i++; - } - - LAN::DiscoveryMutex.unlock(); -} - - -LANDialog::LANDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANDialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - QStandardItemModel* model = new QStandardItemModel(); - ui->tvPlayerList->setModel(model); - const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; - model->setHorizontalHeaderLabels(header); - - connect(this, &LANDialog::sgUpdatePlayerList, this, &LANDialog::doUpdatePlayerList); -} - -LANDialog::~LANDialog() -{ - delete ui; -} - -void LANDialog::done(int r) -{ - // ??? - - QDialog::done(r); -} - -void LANDialog::updatePlayerList() -{ - playerListMutex.lock(); - memcpy(playerList, LAN::Players, sizeof(playerList)); - memcpy(playerPing, LAN::PlayerPing, sizeof(playerPing)); - numPlayers = LAN::NumPlayers; - maxPlayers = LAN::MaxPlayers; - myPlayerID = LAN::MyPlayer.ID; - hostAddress = LAN::HostAddress; - playerListMutex.unlock(); - - emit sgUpdatePlayerList(); -} - -void LANDialog::doUpdatePlayerList() -{ - playerListMutex.lock(); - - QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); - int curcount = model->rowCount(); - int newcount = numPlayers; - if (curcount > newcount) - { - model->removeRows(newcount, curcount-newcount); - } - else if (curcount < newcount) - { - for (int i = curcount; i < newcount; i++) - { - QList row; - row.append(new QStandardItem()); - row.append(new QStandardItem()); - row.append(new QStandardItem()); - row.append(new QStandardItem()); - row.append(new QStandardItem()); - model->appendRow(row); - } - } - - for (int i = 0; i < 16; i++) - { - LAN::Player* player = &playerList[i]; - if (player->Status == 0) break; - - QString id = QString("%0/%1").arg(player->ID+1).arg(maxPlayers); - model->item(i, 0)->setText(id); - - QString name = player->Name; - model->item(i, 1)->setText(name); - - QString status; - switch (player->Status) - { - case 1: status = "Connected"; break; - case 2: status = "Game host"; break; - case 3: status = "Connecting"; break; - case 4: status = "Connection lost"; break; - } - model->item(i, 2)->setText(status); - - if (i == myPlayerID) - { - model->item(i, 3)->setText("-"); - model->item(i, 4)->setText("(local)"); - } - else - { - if (player->Status == 1 || player->Status == 2) - { - QString ping = QString("%0 ms").arg(playerPing[i]); - model->item(i, 3)->setText(ping); - } - else - { - model->item(i, 3)->setText("-"); - } - - // note on the player IP display - // * we make an exception for the host -- the player list is issued by the host, so the host IP would be 127.0.0.1 - // * for the same reason, the host can't know its own IP, so for the current player we force it to 127.0.0.1 - u32 ip; - if (player->Status == 2) - ip = hostAddress; - else - ip = player->Address; - - QString ips = QString("%0.%1.%2.%3").arg(ip&0xFF).arg((ip>>8)&0xFF).arg((ip>>16)&0xFF).arg(ip>>24); - model->item(i, 4)->setText(ips); - } - } - - playerListMutex.unlock(); -} - - namespace LAN { diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index bc2e2779..c78faa20 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -21,19 +21,9 @@ #include #include -#include -#include -#include #include "types.h" -namespace Ui -{ -class LANStartHostDialog; -class LANStartClientDialog; -class LANDialog; -} - namespace LAN { @@ -56,101 +46,6 @@ struct DiscoveryData melonDS::u8 Status; // 0=idle 1=playing }; -} - -class LANStartHostDialog : public QDialog -{ - Q_OBJECT - -public: - explicit LANStartHostDialog(QWidget* parent); - ~LANStartHostDialog(); - - static LANStartHostDialog* openDlg(QWidget* parent) - { - LANStartHostDialog* dlg = new LANStartHostDialog(parent); - dlg->open(); - return dlg; - } - -private slots: - void done(int r); - -private: - Ui::LANStartHostDialog* ui; -}; - -class LANStartClientDialog : public QDialog -{ - Q_OBJECT - -public: - explicit LANStartClientDialog(QWidget* parent); - ~LANStartClientDialog(); - - static LANStartClientDialog* openDlg(QWidget* parent) - { - LANStartClientDialog* dlg = new LANStartClientDialog(parent); - dlg->open(); - return dlg; - } - - void updateDiscoveryList(); - -signals: - void sgUpdateDiscoveryList(); - -private slots: - void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev); - void on_tvAvailableGames_doubleClicked(QModelIndex index); - void onDirectConnect(); - void done(int r); - - void doUpdateDiscoveryList(); - -private: - Ui::LANStartClientDialog* ui; -}; - -class LANDialog : public QDialog -{ - Q_OBJECT - -public: - explicit LANDialog(QWidget* parent); - ~LANDialog(); - - static LANDialog* openDlg(QWidget* parent) - { - LANDialog* dlg = new LANDialog(parent); - dlg->show(); - return dlg; - } - - void updatePlayerList(); - -signals: - void sgUpdatePlayerList(); - -private slots: - void done(int r); - - void doUpdatePlayerList(); - -private: - Ui::LANDialog* ui; - - LAN::Player playerList[16]; - melonDS::u32 playerPing[16]; - int numPlayers; - int maxPlayers; - int myPlayerID; - melonDS::u32 hostAddress; - QMutex playerListMutex; -}; - -namespace LAN -{ extern bool Active; diff --git a/src/frontend/qt_sdl/LANDialog.cpp b/src/frontend/qt_sdl/LANDialog.cpp new file mode 100644 index 00000000..e3c4bf32 --- /dev/null +++ b/src/frontend/qt_sdl/LANDialog.cpp @@ -0,0 +1,401 @@ +/* + Copyright 2016-2024 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include +#include + +#ifdef __WIN32__ +#include + #include + + #define socket_t SOCKET + #define sockaddr_t SOCKADDR + #define sockaddr_in_t SOCKADDR_IN +#else +#include +#include +#include +#include + +#define socket_t int +#define sockaddr_t struct sockaddr +#define sockaddr_in_t struct sockaddr_in +#define closesocket close +#endif + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (socket_t)-1 +#endif + +#include +#include + +#include +#include +#include +#include + +#include "LANDialog.h" +#include "Config.h" +#include "main.h" + +#include "ui_LANStartHostDialog.h" +#include "ui_LANStartClientDialog.h" +#include "ui_LANDialog.h" + +using namespace melonDS; + + +extern EmuThread* emuThread; +LANStartClientDialog* lanClientDlg = nullptr; +LANDialog* lanDlg = nullptr; + + +LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + // TODO: remember the last setting? so this doesn't suck massively + // we could also remember the player name (and auto-init it from the firmware name or whatever) + ui->sbNumPlayers->setRange(2, 16); + ui->sbNumPlayers->setValue(16); +} + +LANStartHostDialog::~LANStartHostDialog() +{ + delete ui; +} + +void LANStartHostDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + std::string player = ui->txtPlayerName->text().toStdString(); + int numplayers = ui->sbNumPlayers->value(); + + // TODO validate input!! + + lanDlg = LANDialog::openDlg(parentWidget()); + + LAN::StartHost(player.c_str(), numplayers); + } + + QDialog::done(r); +} + + +LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartClientDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + QStandardItemModel* model = new QStandardItemModel(); + ui->tvAvailableGames->setModel(model); + const QStringList listheader = {"Name", "Players", "Status", "Host IP"}; + model->setHorizontalHeaderLabels(listheader); + + connect(ui->tvAvailableGames->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(onGameSelectionChanged(const QItemSelection&, const QItemSelection&))); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Connect"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + QPushButton* btn = ui->buttonBox->addButton("Direct connect...", QDialogButtonBox::ActionRole); + connect(btn, SIGNAL(clicked()), this, SLOT(onDirectConnect())); + + connect(this, &LANStartClientDialog::sgUpdateDiscoveryList, this, &LANStartClientDialog::doUpdateDiscoveryList); + + lanClientDlg = this; + LAN::StartDiscovery(); +} + +LANStartClientDialog::~LANStartClientDialog() +{ + lanClientDlg = nullptr; + delete ui; +} + +void LANStartClientDialog::onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev) +{ + QModelIndexList indlist = cur.indexes(); + if (indlist.count() == 0) + { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } + else + { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + } +} + +void LANStartClientDialog::on_tvAvailableGames_doubleClicked(QModelIndex index) +{ + done(QDialog::Accepted); +} + +void LANStartClientDialog::onDirectConnect() +{ + if (ui->txtPlayerName->text().trimmed().isEmpty()) + { + QMessageBox::warning(this, "melonDS", "Please enter a player name before connecting."); + return; + } + + QString host = QInputDialog::getText(this, "Direct connect", "Host address:"); + if (host.isEmpty()) return; + + std::string hostname = host.toStdString(); + std::string player = ui->txtPlayerName->text().toStdString(); + + setEnabled(false); + LAN::EndDiscovery(); + if (!LAN::StartClient(player.c_str(), hostname.c_str())) + { + QString msg = QString("Failed to connect to the host %0.").arg(QString::fromStdString(hostname)); + QMessageBox::warning(this, "melonDS", msg); + setEnabled(true); + LAN::StartDiscovery(); + return; + } + + setEnabled(true); + lanDlg = LANDialog::openDlg(parentWidget()); + QDialog::done(QDialog::Accepted); +} + +void LANStartClientDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + if (ui->txtPlayerName->text().trimmed().isEmpty()) + { + QMessageBox::warning(this, "melonDS", "Please enter a player name before connecting."); + return; + } + + QModelIndexList indlist = ui->tvAvailableGames->selectionModel()->selectedRows(); + if (indlist.count() == 0) return; + + QStandardItemModel* model = (QStandardItemModel*)ui->tvAvailableGames->model(); + QStandardItem* item = model->item(indlist[0].row()); + u32 addr = item->data().toUInt(); + char hostname[16]; + snprintf(hostname, 16, "%d.%d.%d.%d", (addr>>24), ((addr>>16)&0xFF), ((addr>>8)&0xFF), (addr&0xFF)); + + std::string player = ui->txtPlayerName->text().toStdString(); + + setEnabled(false); + LAN::EndDiscovery(); + if (!LAN::StartClient(player.c_str(), hostname)) + { + QString msg = QString("Failed to connect to the host %0.").arg(QString(hostname)); + QMessageBox::warning(this, "melonDS", msg); + setEnabled(true); + LAN::StartDiscovery(); + return; + } + + setEnabled(true); + lanDlg = LANDialog::openDlg(parentWidget()); + } + else + { + LAN::EndDiscovery(); + } + + QDialog::done(r); +} + +void LANStartClientDialog::updateDiscoveryList() +{ + emit sgUpdateDiscoveryList(); +} + +void LANStartClientDialog::doUpdateDiscoveryList() +{ + LAN::DiscoveryMutex.lock(); + + QStandardItemModel* model = (QStandardItemModel*)ui->tvAvailableGames->model(); + int curcount = model->rowCount(); + int newcount = LAN::DiscoveryList.size(); + if (curcount > newcount) + { + model->removeRows(newcount, curcount-newcount); + } + else if (curcount < newcount) + { + for (int i = curcount; i < newcount; i++) + { + QList row; + row.append(new QStandardItem()); + row.append(new QStandardItem()); + row.append(new QStandardItem()); + row.append(new QStandardItem()); + model->appendRow(row); + } + } + + int i = 0; + for (const auto& [key, data] : LAN::DiscoveryList) + { + model->item(i, 0)->setText(data.SessionName); + model->item(i, 0)->setData(QVariant(key)); + + QString plcount = QString("%0/%1").arg(data.NumPlayers).arg(data.MaxPlayers); + model->item(i, 1)->setText(plcount); + + QString status; + switch (data.Status) + { + case 0: status = "Idle"; break; + case 1: status = "Playing"; break; + } + model->item(i, 2)->setText(status); + + QString ip = QString("%0.%1.%2.%3").arg(key>>24).arg((key>>16)&0xFF).arg((key>>8)&0xFF).arg(key&0xFF); + model->item(i, 3)->setText(ip); + + i++; + } + + LAN::DiscoveryMutex.unlock(); +} + + +LANDialog::LANDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + QStandardItemModel* model = new QStandardItemModel(); + ui->tvPlayerList->setModel(model); + const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; + model->setHorizontalHeaderLabels(header); + + connect(this, &LANDialog::sgUpdatePlayerList, this, &LANDialog::doUpdatePlayerList); +} + +LANDialog::~LANDialog() +{ + delete ui; +} + +void LANDialog::done(int r) +{ + // ??? + + QDialog::done(r); +} + +void LANDialog::updatePlayerList() +{ + playerListMutex.lock(); + memcpy(playerList, LAN::Players, sizeof(playerList)); + memcpy(playerPing, LAN::PlayerPing, sizeof(playerPing)); + numPlayers = LAN::NumPlayers; + maxPlayers = LAN::MaxPlayers; + myPlayerID = LAN::MyPlayer.ID; + hostAddress = LAN::HostAddress; + playerListMutex.unlock(); + + emit sgUpdatePlayerList(); +} + +void LANDialog::doUpdatePlayerList() +{ + playerListMutex.lock(); + + QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); + int curcount = model->rowCount(); + int newcount = numPlayers; + if (curcount > newcount) + { + model->removeRows(newcount, curcount-newcount); + } + else if (curcount < newcount) + { + for (int i = curcount; i < newcount; i++) + { + QList row; + row.append(new QStandardItem()); + row.append(new QStandardItem()); + row.append(new QStandardItem()); + row.append(new QStandardItem()); + row.append(new QStandardItem()); + model->appendRow(row); + } + } + + for (int i = 0; i < 16; i++) + { + LAN::Player* player = &playerList[i]; + if (player->Status == 0) break; + + QString id = QString("%0/%1").arg(player->ID+1).arg(maxPlayers); + model->item(i, 0)->setText(id); + + QString name = player->Name; + model->item(i, 1)->setText(name); + + QString status; + switch (player->Status) + { + case 1: status = "Connected"; break; + case 2: status = "Game host"; break; + case 3: status = "Connecting"; break; + case 4: status = "Connection lost"; break; + } + model->item(i, 2)->setText(status); + + if (i == myPlayerID) + { + model->item(i, 3)->setText("-"); + model->item(i, 4)->setText("(local)"); + } + else + { + if (player->Status == 1 || player->Status == 2) + { + QString ping = QString("%0 ms").arg(playerPing[i]); + model->item(i, 3)->setText(ping); + } + else + { + model->item(i, 3)->setText("-"); + } + + // note on the player IP display + // * we make an exception for the host -- the player list is issued by the host, so the host IP would be 127.0.0.1 + // * for the same reason, the host can't know its own IP, so for the current player we force it to 127.0.0.1 + u32 ip; + if (player->Status == 2) + ip = hostAddress; + else + ip = player->Address; + + QString ips = QString("%0.%1.%2.%3").arg(ip&0xFF).arg((ip>>8)&0xFF).arg((ip>>16)&0xFF).arg(ip>>24); + model->item(i, 4)->setText(ips); + } + } + + playerListMutex.unlock(); +} diff --git a/src/frontend/qt_sdl/LANDialog.h b/src/frontend/qt_sdl/LANDialog.h new file mode 100644 index 00000000..36bb2f39 --- /dev/null +++ b/src/frontend/qt_sdl/LANDialog.h @@ -0,0 +1,127 @@ +/* + Copyright 2016-2024 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef LANDIALOG_H +#define LANDIALOG_H + +#include +#include +#include + +#include "types.h" +#include "LAN.h" + +namespace Ui +{ + class LANStartHostDialog; + class LANStartClientDialog; + class LANDialog; +} + +class LANStartHostDialog : public QDialog +{ +Q_OBJECT + +public: + explicit LANStartHostDialog(QWidget* parent); + ~LANStartHostDialog(); + + static LANStartHostDialog* openDlg(QWidget* parent) + { + LANStartHostDialog* dlg = new LANStartHostDialog(parent); + dlg->open(); + return dlg; + } + +private slots: + void done(int r); + +private: + Ui::LANStartHostDialog* ui; +}; + +class LANStartClientDialog : public QDialog +{ +Q_OBJECT + +public: + explicit LANStartClientDialog(QWidget* parent); + ~LANStartClientDialog(); + + static LANStartClientDialog* openDlg(QWidget* parent) + { + LANStartClientDialog* dlg = new LANStartClientDialog(parent); + dlg->open(); + return dlg; + } + + void updateDiscoveryList(); + +signals: + void sgUpdateDiscoveryList(); + +private slots: + void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev); + void on_tvAvailableGames_doubleClicked(QModelIndex index); + void onDirectConnect(); + void done(int r); + + void doUpdateDiscoveryList(); + +private: + Ui::LANStartClientDialog* ui; +}; + +class LANDialog : public QDialog +{ +Q_OBJECT + +public: + explicit LANDialog(QWidget* parent); + ~LANDialog(); + + static LANDialog* openDlg(QWidget* parent) + { + LANDialog* dlg = new LANDialog(parent); + dlg->show(); + return dlg; + } + + void updatePlayerList(); + +signals: + void sgUpdatePlayerList(); + +private slots: + void done(int r); + + void doUpdatePlayerList(); + +private: + Ui::LANDialog* ui; + + LAN::Player playerList[16]; + melonDS::u32 playerPing[16]; + int numPlayers; + int maxPlayers; + int myPlayerID; + melonDS::u32 hostAddress; + QMutex playerListMutex; +}; + +#endif // LANDIALOG_H diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 488051ea..c2a148fb 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -43,144 +43,6 @@ using namespace melonDS; - -extern EmuThread* emuThread; -NetplayDialog* netplayDlg; - - -NetplayStartHostDialog::NetplayStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayStartHostDialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - ui->txtPort->setText("8064"); -} - -NetplayStartHostDialog::~NetplayStartHostDialog() -{ - delete ui; -} - -void NetplayStartHostDialog::done(int r) -{ - if (r == QDialog::Accepted) - { - std::string player = ui->txtPlayerName->text().toStdString(); - int port = ui->txtPort->text().toInt(); - - // TODO validate input!! - - netplayDlg = NetplayDialog::openDlg(parentWidget()); - - Netplay::StartHost(player.c_str(), port); - } - - QDialog::done(r); -} - - -NetplayStartClientDialog::NetplayStartClientDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayStartClientDialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - ui->txtPort->setText("8064"); -} - -NetplayStartClientDialog::~NetplayStartClientDialog() -{ - delete ui; -} - -void NetplayStartClientDialog::done(int r) -{ - if (r == QDialog::Accepted) - { - std::string player = ui->txtPlayerName->text().toStdString(); - std::string host = ui->txtIPAddress->text().toStdString(); - int port = ui->txtPort->text().toInt(); - - // TODO validate input!! - - netplayDlg = NetplayDialog::openDlg(parentWidget()); - - Netplay::StartClient(player.c_str(), host.c_str(), port); - } - - QDialog::done(r); -} - - -NetplayDialog::NetplayDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayDialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - QStandardItemModel* model = new QStandardItemModel(); - ui->tvPlayerList->setModel(model); - - connect(this, &NetplayDialog::sgUpdatePlayerList, this, &NetplayDialog::doUpdatePlayerList); -} - -NetplayDialog::~NetplayDialog() -{ - delete ui; -} - -void NetplayDialog::done(int r) -{ - // ??? - - QDialog::done(r); -} - -void NetplayDialog::updatePlayerList(Netplay::Player* players, int num) -{ - emit sgUpdatePlayerList(players, num); -} - -void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) -{ - QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); - - model->clear(); - model->setRowCount(num); - - // TODO: remove IP column in final product - - const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; - model->setHorizontalHeaderLabels(header); - - for (int i = 0; i < num; i++) - { - Netplay::Player* player = &players[i]; - - QString id = QString("%0").arg(player->ID+1); - model->setItem(i, 0, new QStandardItem(id)); - - QString name = player->Name; - model->setItem(i, 1, new QStandardItem(name)); - - QString status; - switch (player->Status) - { - case 1: status = ""; break; - case 2: status = "Host"; break; - default: status = "ded"; break; - } - model->setItem(i, 2, new QStandardItem(status)); - - // TODO: ping - model->setItem(i, 3, new QStandardItem("x")); - - char ip[32]; - u32 addr = player->Address; - sprintf(ip, "%d.%d.%d.%d", addr&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF, addr>>24); - model->setItem(i, 4, new QStandardItem(ip)); - } -} - - namespace Netplay { diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index f6ca4fe4..b869fefc 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -19,21 +19,8 @@ #ifndef NETPLAY_H #define NETPLAY_H -#include - #include "types.h" -namespace Ui -{ -class NetplayStartHostDialog; -class NetplayStartClientDialog; -class NetplayDialog; -} - -class NetplayStartHostDialog; -class NetplayStartClientDialog; -class NetplayDialog; - namespace Netplay { @@ -45,83 +32,6 @@ struct Player melonDS::u32 Address; }; -} - -class NetplayStartHostDialog : public QDialog -{ - Q_OBJECT - -public: - explicit NetplayStartHostDialog(QWidget* parent); - ~NetplayStartHostDialog(); - - static NetplayStartHostDialog* openDlg(QWidget* parent) - { - NetplayStartHostDialog* dlg = new NetplayStartHostDialog(parent); - dlg->open(); - return dlg; - } - -private slots: - void done(int r); - -private: - Ui::NetplayStartHostDialog* ui; -}; - -class NetplayStartClientDialog : public QDialog -{ - Q_OBJECT - -public: - explicit NetplayStartClientDialog(QWidget* parent); - ~NetplayStartClientDialog(); - - static NetplayStartClientDialog* openDlg(QWidget* parent) - { - NetplayStartClientDialog* dlg = new NetplayStartClientDialog(parent); - dlg->open(); - return dlg; - } - -private slots: - void done(int r); - -private: - Ui::NetplayStartClientDialog* ui; -}; - -class NetplayDialog : public QDialog -{ - Q_OBJECT - -public: - explicit NetplayDialog(QWidget* parent); - ~NetplayDialog(); - - static NetplayDialog* openDlg(QWidget* parent) - { - NetplayDialog* dlg = new NetplayDialog(parent); - dlg->show(); - return dlg; - } - - void updatePlayerList(Netplay::Player* players, int num); - -signals: - void sgUpdatePlayerList(Netplay::Player* players, int num); - -private slots: - void done(int r); - - void doUpdatePlayerList(Netplay::Player* players, int num); - -private: - Ui::NetplayDialog* ui; -}; - -namespace Netplay -{ extern bool Active; diff --git a/src/frontend/qt_sdl/NetplayDialog.cpp b/src/frontend/qt_sdl/NetplayDialog.cpp new file mode 100644 index 00000000..f28823ab --- /dev/null +++ b/src/frontend/qt_sdl/NetplayDialog.cpp @@ -0,0 +1,181 @@ +/* + Copyright 2016-2024 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include + +#include + +#include +#include + +#include "NDS.h" +#include "NDSCart.h" +#include "main.h" +#include "IPC.h" +#include "Netplay.h" +#include "Input.h" +#include "ROMManager.h" +#include "Config.h" +#include "Savestate.h" +#include "Platform.h" + +#include "ui_NetplayStartHostDialog.h" +#include "ui_NetplayStartClientDialog.h" +#include "ui_NetplayDialog.h" + +using namespace melonDS; + + +extern EmuThread* emuThread; +NetplayDialog* netplayDlg; + + +NetplayStartHostDialog::NetplayStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayStartHostDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + ui->txtPort->setText("8064"); +} + +NetplayStartHostDialog::~NetplayStartHostDialog() +{ + delete ui; +} + +void NetplayStartHostDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + std::string player = ui->txtPlayerName->text().toStdString(); + int port = ui->txtPort->text().toInt(); + + // TODO validate input!! + + netplayDlg = NetplayDialog::openDlg(parentWidget()); + + Netplay::StartHost(player.c_str(), port); + } + + QDialog::done(r); +} + + +NetplayStartClientDialog::NetplayStartClientDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayStartClientDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + ui->txtPort->setText("8064"); +} + +NetplayStartClientDialog::~NetplayStartClientDialog() +{ + delete ui; +} + +void NetplayStartClientDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + std::string player = ui->txtPlayerName->text().toStdString(); + std::string host = ui->txtIPAddress->text().toStdString(); + int port = ui->txtPort->text().toInt(); + + // TODO validate input!! + + netplayDlg = NetplayDialog::openDlg(parentWidget()); + + Netplay::StartClient(player.c_str(), host.c_str(), port); + } + + QDialog::done(r); +} + + +NetplayDialog::NetplayDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + QStandardItemModel* model = new QStandardItemModel(); + ui->tvPlayerList->setModel(model); + + connect(this, &NetplayDialog::sgUpdatePlayerList, this, &NetplayDialog::doUpdatePlayerList); +} + +NetplayDialog::~NetplayDialog() +{ + delete ui; +} + +void NetplayDialog::done(int r) +{ + // ??? + + QDialog::done(r); +} + +void NetplayDialog::updatePlayerList(Netplay::Player* players, int num) +{ + emit sgUpdatePlayerList(players, num); +} + +void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) +{ + QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); + + model->clear(); + model->setRowCount(num); + + // TODO: remove IP column in final product + + const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; + model->setHorizontalHeaderLabels(header); + + for (int i = 0; i < num; i++) + { + Netplay::Player* player = &players[i]; + + QString id = QString("%0").arg(player->ID+1); + model->setItem(i, 0, new QStandardItem(id)); + + QString name = player->Name; + model->setItem(i, 1, new QStandardItem(name)); + + QString status; + switch (player->Status) + { + case 1: status = ""; break; + case 2: status = "Host"; break; + default: status = "ded"; break; + } + model->setItem(i, 2, new QStandardItem(status)); + + // TODO: ping + model->setItem(i, 3, new QStandardItem("x")); + + char ip[32]; + u32 addr = player->Address; + sprintf(ip, "%d.%d.%d.%d", addr&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF, addr>>24); + model->setItem(i, 4, new QStandardItem(ip)); + } +} diff --git a/src/frontend/qt_sdl/NetplayDialog.h b/src/frontend/qt_sdl/NetplayDialog.h new file mode 100644 index 00000000..1fa0dcf2 --- /dev/null +++ b/src/frontend/qt_sdl/NetplayDialog.h @@ -0,0 +1,111 @@ +/* + Copyright 2016-2024 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETPLAYDIALOG_H +#define NETPLAYDIALOG_H + +#include + +#include "types.h" +#include "Netplay.h" + +namespace Ui +{ + class NetplayStartHostDialog; + class NetplayStartClientDialog; + class NetplayDialog; +} + +class NetplayStartHostDialog; +class NetplayStartClientDialog; +class NetplayDialog; + +class NetplayStartHostDialog : public QDialog +{ +Q_OBJECT + +public: + explicit NetplayStartHostDialog(QWidget* parent); + ~NetplayStartHostDialog(); + + static NetplayStartHostDialog* openDlg(QWidget* parent) + { + NetplayStartHostDialog* dlg = new NetplayStartHostDialog(parent); + dlg->open(); + return dlg; + } + +private slots: + void done(int r); + +private: + Ui::NetplayStartHostDialog* ui; +}; + +class NetplayStartClientDialog : public QDialog +{ +Q_OBJECT + +public: + explicit NetplayStartClientDialog(QWidget* parent); + ~NetplayStartClientDialog(); + + static NetplayStartClientDialog* openDlg(QWidget* parent) + { + NetplayStartClientDialog* dlg = new NetplayStartClientDialog(parent); + dlg->open(); + return dlg; + } + +private slots: + void done(int r); + +private: + Ui::NetplayStartClientDialog* ui; +}; + +class NetplayDialog : public QDialog +{ +Q_OBJECT + +public: + explicit NetplayDialog(QWidget* parent); + ~NetplayDialog(); + + static NetplayDialog* openDlg(QWidget* parent) + { + NetplayDialog* dlg = new NetplayDialog(parent); + dlg->show(); + return dlg; + } + + void updatePlayerList(Netplay::Player* players, int num); + +signals: + void sgUpdatePlayerList(Netplay::Player* players, int num); + +private slots: + void done(int r); + + void doUpdatePlayerList(Netplay::Player* players, int num); + +private: + Ui::NetplayDialog* ui; +}; + +#endif // NETPLAYDIALOG_H