actually finish the work and hook up the LAN stuff

This commit is contained in:
Arisotura 2024-08-07 20:12:53 +02:00
parent 8e4daeace7
commit fc5628bbc2
6 changed files with 107 additions and 86 deletions

View File

@ -151,6 +151,7 @@ void EmuThread::run()
while (emuStatus != emuStatus_Exit) while (emuStatus != emuStatus_Exit)
{ {
MPInterface::Get().Process();
emuInstance->inputProcess(); emuInstance->inputProcess();
if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange();

View File

@ -29,6 +29,7 @@
#include "LANDialog.h" #include "LANDialog.h"
#include "Config.h" #include "Config.h"
#include "main.h" #include "main.h"
#include "LAN.h"
#include "ui_LANStartHostDialog.h" #include "ui_LANStartHostDialog.h"
#include "ui_LANStartClientDialog.h" #include "ui_LANStartClientDialog.h"
@ -37,16 +38,19 @@
using namespace melonDS; using namespace melonDS;
extern EmuThread* emuThread;
LANStartClientDialog* lanClientDlg = nullptr; LANStartClientDialog* lanClientDlg = nullptr;
LANDialog* lanDlg = nullptr; LANDialog* lanDlg = nullptr;
#define lan() ((LAN&)MPInterface::Get())
LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog) LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
MPInterface::Set(MPInterface_LAN);
// TODO: remember the last setting? so this doesn't suck massively // 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) // we could also remember the player name (and auto-init it from the firmware name or whatever)
ui->sbNumPlayers->setRange(2, 16); ui->sbNumPlayers->setRange(2, 16);
@ -62,14 +66,26 @@ void LANStartHostDialog::done(int r)
{ {
if (r == QDialog::Accepted) if (r == QDialog::Accepted)
{ {
if (ui->txtPlayerName->text().trimmed().isEmpty())
{
QMessageBox::warning(this, "melonDS", "Please enter a player name.");
return;
}
std::string player = ui->txtPlayerName->text().toStdString(); std::string player = ui->txtPlayerName->text().toStdString();
int numplayers = ui->sbNumPlayers->value(); int numplayers = ui->sbNumPlayers->value();
// TODO validate input!! if (!lan().StartHost(player.c_str(), numplayers))
{
QMessageBox::warning(this, "melonDS", "Failed to start LAN game.");
return;
}
lanDlg = LANDialog::openDlg(parentWidget()); lanDlg = LANDialog::openDlg(parentWidget());
}
LAN::StartHost(player.c_str(), numplayers); else
{
MPInterface::Set(MPInterface_Local);
} }
QDialog::done(r); QDialog::done(r);
@ -81,6 +97,8 @@ LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), u
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
MPInterface::Set(MPInterface_LAN);
QStandardItemModel* model = new QStandardItemModel(); QStandardItemModel* model = new QStandardItemModel();
ui->tvAvailableGames->setModel(model); ui->tvAvailableGames->setModel(model);
const QStringList listheader = {"Name", "Players", "Status", "Host IP"}; const QStringList listheader = {"Name", "Players", "Status", "Host IP"};
@ -95,14 +113,16 @@ LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), u
QPushButton* btn = ui->buttonBox->addButton("Direct connect...", QDialogButtonBox::ActionRole); QPushButton* btn = ui->buttonBox->addButton("Direct connect...", QDialogButtonBox::ActionRole);
connect(btn, SIGNAL(clicked()), this, SLOT(onDirectConnect())); connect(btn, SIGNAL(clicked()), this, SLOT(onDirectConnect()));
connect(this, &LANStartClientDialog::sgUpdateDiscoveryList, this, &LANStartClientDialog::doUpdateDiscoveryList);
lanClientDlg = this; lanClientDlg = this;
LAN::StartDiscovery(); lan().StartDiscovery();
timerID = startTimer(1000);
} }
LANStartClientDialog::~LANStartClientDialog() LANStartClientDialog::~LANStartClientDialog()
{ {
killTimer(timerID);
lanClientDlg = nullptr; lanClientDlg = nullptr;
delete ui; delete ui;
} }
@ -140,13 +160,13 @@ void LANStartClientDialog::onDirectConnect()
std::string player = ui->txtPlayerName->text().toStdString(); std::string player = ui->txtPlayerName->text().toStdString();
setEnabled(false); setEnabled(false);
LAN::EndDiscovery(); lan().EndDiscovery();
if (!LAN::StartClient(player.c_str(), hostname.c_str())) if (!lan().StartClient(player.c_str(), hostname.c_str()))
{ {
QString msg = QString("Failed to connect to the host %0.").arg(QString::fromStdString(hostname)); QString msg = QString("Failed to connect to the host %0.").arg(QString::fromStdString(hostname));
QMessageBox::warning(this, "melonDS", msg); QMessageBox::warning(this, "melonDS", msg);
setEnabled(true); setEnabled(true);
LAN::StartDiscovery(); lan().StartDiscovery();
return; return;
} }
@ -177,13 +197,13 @@ void LANStartClientDialog::done(int r)
std::string player = ui->txtPlayerName->text().toStdString(); std::string player = ui->txtPlayerName->text().toStdString();
setEnabled(false); setEnabled(false);
LAN::EndDiscovery(); lan().EndDiscovery();
if (!LAN::StartClient(player.c_str(), hostname)) if (!lan().StartClient(player.c_str(), hostname))
{ {
QString msg = QString("Failed to connect to the host %0.").arg(QString(hostname)); QString msg = QString("Failed to connect to the host %0.").arg(QString(hostname));
QMessageBox::warning(this, "melonDS", msg); QMessageBox::warning(this, "melonDS", msg);
setEnabled(true); setEnabled(true);
LAN::StartDiscovery(); lan().StartDiscovery();
return; return;
} }
@ -192,25 +212,25 @@ void LANStartClientDialog::done(int r)
} }
else else
{ {
LAN::EndDiscovery(); lan().EndDiscovery();
MPInterface::Set(MPInterface_Local);
} }
QDialog::done(r); QDialog::done(r);
} }
void LANStartClientDialog::updateDiscoveryList() void LANStartClientDialog::timerEvent(QTimerEvent *event)
{ {
emit sgUpdateDiscoveryList(); doUpdateDiscoveryList();
} }
void LANStartClientDialog::doUpdateDiscoveryList() void LANStartClientDialog::doUpdateDiscoveryList()
{ {
if (LAN::DiscoveryMutex) auto disclist = lan().GetDiscoveryList();
Platform::Mutex_Lock(LAN::DiscoveryMutex);
QStandardItemModel* model = (QStandardItemModel*)ui->tvAvailableGames->model(); QStandardItemModel* model = (QStandardItemModel*)ui->tvAvailableGames->model();
int curcount = model->rowCount(); int curcount = model->rowCount();
int newcount = LAN::DiscoveryList.size(); int newcount = disclist.size();
if (curcount > newcount) if (curcount > newcount)
{ {
model->removeRows(newcount, curcount-newcount); model->removeRows(newcount, curcount-newcount);
@ -229,7 +249,7 @@ void LANStartClientDialog::doUpdateDiscoveryList()
} }
int i = 0; int i = 0;
for (const auto& [key, data] : LAN::DiscoveryList) for (const auto& [key, data] : disclist)
{ {
model->item(i, 0)->setText(data.SessionName); model->item(i, 0)->setText(data.SessionName);
model->item(i, 0)->setData(QVariant(key)); model->item(i, 0)->setData(QVariant(key));
@ -250,9 +270,6 @@ void LANStartClientDialog::doUpdateDiscoveryList()
i++; i++;
} }
if (LAN::DiscoveryMutex)
Platform::Mutex_Unlock(LAN::DiscoveryMutex);
} }
@ -266,42 +283,37 @@ LANDialog::LANDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANDialog)
const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; const QStringList header = {"#", "Player", "Status", "Ping", "IP"};
model->setHorizontalHeaderLabels(header); model->setHorizontalHeaderLabels(header);
connect(this, &LANDialog::sgUpdatePlayerList, this, &LANDialog::doUpdatePlayerList); timerID = startTimer(1000);
} }
LANDialog::~LANDialog() LANDialog::~LANDialog()
{ {
killTimer(timerID);
delete ui; delete ui;
} }
void LANDialog::done(int r) void LANDialog::done(int r)
{ {
// ??? // ???
// TODO handle this situation, and provide the user a way to reopen this dialog
QDialog::done(r); QDialog::done(r);
} }
void LANDialog::updatePlayerList() void LANDialog::timerEvent(QTimerEvent *event)
{ {
playerListMutex.lock(); doUpdatePlayerList();
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() void LANDialog::doUpdatePlayerList()
{ {
playerListMutex.lock(); auto playerlist = lan().GetPlayerList();
auto maxplayers = lan().GetMaxPlayers();
QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model();
int curcount = model->rowCount(); int curcount = model->rowCount();
int newcount = numPlayers; int newcount = playerlist.size();
if (curcount > newcount) if (curcount > newcount)
{ {
model->removeRows(newcount, curcount-newcount); model->removeRows(newcount, curcount-newcount);
@ -320,37 +332,44 @@ void LANDialog::doUpdatePlayerList()
} }
} }
for (int i = 0; i < 16; i++) int i = 0;
for (const auto& player : playerlist)
{ {
LAN::Player* player = &playerList[i]; QString id = QString("%0/%1").arg(player.ID+1).arg(maxplayers);
if (player->Status == 0) break;
QString id = QString("%0/%1").arg(player->ID+1).arg(maxPlayers);
model->item(i, 0)->setText(id); model->item(i, 0)->setText(id);
QString name = player->Name; QString name = player.Name;
model->item(i, 1)->setText(name); model->item(i, 1)->setText(name);
QString status; QString status = "???";
switch (player->Status) switch (player.Status)
{ {
case 1: status = "Connected"; break; case LAN::Player_Client:
case 2: status = "Game host"; break; status = "Connected";
case 3: status = "Connecting"; break; break;
case 4: status = "Connection lost"; break; case LAN::Player_Host:
status = "Game host";
break;
case LAN::Player_Connecting:
status = "Connecting";
break;
case LAN::Player_Disconnected:
status = "Connection lost";
break;
} }
model->item(i, 2)->setText(status); model->item(i, 2)->setText(status);
if (i == myPlayerID) if (player.IsLocalPlayer)
{ {
model->item(i, 3)->setText("-"); model->item(i, 3)->setText("-");
model->item(i, 4)->setText("(local)"); model->item(i, 4)->setText("(local)");
} }
else else
{ {
if (player->Status == 1 || player->Status == 2) if (player.Status == LAN::Player_Client ||
player.Status == LAN::Player_Host)
{ {
QString ping = QString("%0 ms").arg(playerPing[i]); QString ping = QString("%0 ms").arg(player.Ping);
model->item(i, 3)->setText(ping); model->item(i, 3)->setText(ping);
} }
else else
@ -358,19 +377,13 @@ void LANDialog::doUpdatePlayerList()
model->item(i, 3)->setText("-"); 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 u32 ip = player.Address;
// * 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); 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); model->item(i, 4)->setText(ips);
} }
}
playerListMutex.unlock(); i++;
}
} }

View File

@ -24,7 +24,6 @@
#include <QItemSelection> #include <QItemSelection>
#include "types.h" #include "types.h"
#include "LAN.h"
namespace Ui namespace Ui
{ {
@ -70,10 +69,8 @@ public:
return dlg; return dlg;
} }
void updateDiscoveryList(); protected:
void timerEvent(QTimerEvent* event) override;
signals:
void sgUpdateDiscoveryList();
private slots: private slots:
void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev); void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev);
@ -85,6 +82,7 @@ private slots:
private: private:
Ui::LANStartClientDialog* ui; Ui::LANStartClientDialog* ui;
int timerID;
}; };
class LANDialog : public QDialog class LANDialog : public QDialog
@ -102,10 +100,8 @@ public:
return dlg; return dlg;
} }
void updatePlayerList(); protected:
void timerEvent(QTimerEvent* event) override;
signals:
void sgUpdatePlayerList();
private slots: private slots:
void done(int r); void done(int r);
@ -114,14 +110,7 @@ private slots:
private: private:
Ui::LANDialog* ui; Ui::LANDialog* ui;
int timerID;
LAN::Player playerList[16];
melonDS::u32 playerPing[16];
int numPlayers;
int maxPlayers;
int myPlayerID;
melonDS::u32 hostAddress;
QMutex playerListMutex;
}; };
#endif // LANDIALOG_H #endif // LANDIALOG_H

View File

@ -96,7 +96,6 @@ LAN::LAN() noexcept : Inited(false)
memset(RemotePeers, 0, sizeof(RemotePeers)); memset(RemotePeers, 0, sizeof(RemotePeers));
memset(Players, 0, sizeof(Players)); memset(Players, 0, sizeof(Players));
memset(PlayerPing, 0, sizeof(PlayerPing));
NumPlayers = 0; NumPlayers = 0;
MaxPlayers = 0; MaxPlayers = 0;
@ -174,9 +173,16 @@ std::vector<LAN::Player> LAN::GetPlayerList()
// make a copy of the player entry, fix up the address field // make a copy of the player entry, fix up the address field
Player newp = Players[i]; Player newp = Players[i];
if (newp.ID == MyPlayer.ID) if (newp.ID == MyPlayer.ID)
{
newp.IsLocalPlayer = true;
newp.Address = kLocalhost; newp.Address = kLocalhost;
else if (newp.Status == Player_Host) }
newp.Address = HostAddress; else
{
newp.IsLocalPlayer = false;
if (newp.Status == Player_Host)
newp.Address = HostAddress;
}
ret.push_back(newp); ret.push_back(newp);
} }
@ -894,17 +900,18 @@ void LAN::Process()
{ {
FrameCount = 0; FrameCount = 0;
Platform::Mutex_Lock(PlayersMutex);
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
if (Players[i].Status == Player_None) continue; if (Players[i].Status == Player_None) continue;
if (i == MyPlayer.ID) continue; if (i == MyPlayer.ID) continue;
if (!RemotePeers[i]) continue; if (!RemotePeers[i]) continue;
PlayerPing[i] = RemotePeers[i]->roundTripTime; Players[i].Ping = RemotePeers[i]->roundTripTime;
} }
//if (lanDlg) Platform::Mutex_Unlock(PlayersMutex);
// lanDlg->updatePlayerList();
} }
} }

View File

@ -20,7 +20,9 @@
#define LAN_H #define LAN_H
#include <string> #include <string>
#include <vector>
#include <map> #include <map>
#include <queue>
#include <enet/enet.h> #include <enet/enet.h>
@ -65,6 +67,9 @@ public:
char Name[32]; char Name[32];
PlayerStatus Status; PlayerStatus Status;
u32 Address; u32 Address;
bool IsLocalPlayer;
u32 Ping;
}; };
struct DiscoveryData struct DiscoveryData
@ -85,6 +90,8 @@ public:
std::map<u32, DiscoveryData> GetDiscoveryList(); std::map<u32, DiscoveryData> GetDiscoveryList();
std::vector<Player> GetPlayerList(); std::vector<Player> GetPlayerList();
int GetNumPlayers() { return NumPlayers; }
int GetMaxPlayers() { return MaxPlayers; }
void Process() override; void Process() override;
@ -113,7 +120,6 @@ private:
Platform::Mutex* DiscoveryMutex; Platform::Mutex* DiscoveryMutex;
Player Players[16]; Player Players[16];
u32 PlayerPing[16];
int NumPlayers; int NumPlayers;
int MaxPlayers; int MaxPlayers;
Platform::Mutex* PlayersMutex; Platform::Mutex* PlayersMutex;

View File

@ -18,6 +18,7 @@
#include "MPInterface.h" #include "MPInterface.h"
#include "LocalMP.h" #include "LocalMP.h"
#include "LAN.h"
namespace melonDS namespace melonDS
{ {
@ -51,6 +52,10 @@ void MPInterface::Set(MPInterfaceType type)
Current = std::make_unique<LocalMP>(); Current = std::make_unique<LocalMP>();
break; break;
case MPInterface_LAN:
Current = std::make_unique<LAN>();
break;
default: default:
Current = std::make_unique<DummyMP>(); Current = std::make_unique<DummyMP>();
break; break;