flesh out LAN discovery UI

This commit is contained in:
Arisotura 2023-09-09 19:40:39 +02:00
parent 5153b17dac
commit db7cee7f2c
3 changed files with 190 additions and 60 deletions

View File

@ -20,7 +20,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <queue> #include <queue>
#include <map>
#include <vector> #include <vector>
#ifdef __WIN32__ #ifdef __WIN32__
@ -50,6 +49,8 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QPushButton>
#include <QInputDialog>
#include "LAN.h" #include "LAN.h"
#include "Config.h" #include "Config.h"
@ -61,7 +62,8 @@
extern EmuThread* emuThread; extern EmuThread* emuThread;
LANDialog* lanDlg; LANStartClientDialog* lanClientDlg = nullptr;
LANDialog* lanDlg = nullptr;
LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog) LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog)
@ -102,36 +104,111 @@ LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), u
{ {
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
QStandardItemModel* model = new QStandardItemModel();
ui->tvAvailableGames->setModel(model);
const QStringList listheader = {"Name", "Players", "Status"};
model->setHorizontalHeaderLabels(listheader);
ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Connect");
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() LANStartClientDialog::~LANStartClientDialog()
{ {
lanClientDlg = nullptr;
delete ui; delete ui;
} }
void LANStartClientDialog::onDirectConnect()
{
QString host = QInputDialog::getText(this, "Direct connect", "Host address:");
if (host.isEmpty()) return;
printf("dicks: %s\n", host.toStdString().c_str());
//QDialog::done(QDialog::Accepted);
}
void LANStartClientDialog::done(int r) void LANStartClientDialog::done(int r)
{ {
if (r == QDialog::Accepted) if (r == QDialog::Accepted)
{ {
std::string player = ui->txtPlayerName->text().toStdString(); std::string player = ui->txtPlayerName->text().toStdString();
std::string host = ui->txtIPAddress->text().toStdString(); //std::string host = ui->txtIPAddress->text().toStdString();
// TODO validate input!! // TODO validate input!!
lanDlg = LANDialog::openDlg(parentWidget()); lanDlg = LANDialog::openDlg(parentWidget());
LAN::StartClient(player.c_str(), host.c_str()); //LAN::StartClient(player.c_str(), host.c_str());
} }
else else
{ {
// TEST!! // TEST!!
printf("borp\n"); printf("borp\n");
LAN::StartDiscovery(); //LAN::StartDiscovery();
} }
QDialog::done(r); 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<QStandardItem*> row;
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);
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);
i++;
}
LAN::DiscoveryMutex.unlock();
}
LANDialog::LANDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANDialog) LANDialog::LANDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANDialog)
{ {
@ -211,17 +288,6 @@ const u32 kPacketMagic = 0x4946494E; // NIFI
const u32 kProtocolVersion = 1; const u32 kProtocolVersion = 1;
struct DiscoveryData
{
u32 Magic;
u32 Version;
u32 Tick;
char SessionName[64];
u8 NumPlayers;
u8 MaxPlayers;
u8 Status; // 0=idle 1=playing
};
struct MPPacketHeader struct MPPacketHeader
{ {
u32 Magic; u32 Magic;
@ -237,6 +303,7 @@ const int kLANPort = 7064;
socket_t DiscoverySocket; socket_t DiscoverySocket;
u32 DiscoveryLastTick; u32 DiscoveryLastTick;
std::map<u32, DiscoveryData> DiscoveryList; std::map<u32, DiscoveryData> DiscoveryList;
QMutex DiscoveryMutex;
bool Active; bool Active;
bool IsHost; bool IsHost;
@ -315,7 +382,7 @@ void DeInit()
} }
void StartDiscovery() bool StartDiscovery()
{ {
int res; int res;
@ -323,7 +390,7 @@ void StartDiscovery()
if (DiscoverySocket < 0) if (DiscoverySocket < 0)
{ {
DiscoverySocket = INVALID_SOCKET; DiscoverySocket = INVALID_SOCKET;
return; return false;
} }
sockaddr_in_t saddr; sockaddr_in_t saddr;
@ -336,7 +403,7 @@ void StartDiscovery()
{ {
closesocket(DiscoverySocket); closesocket(DiscoverySocket);
DiscoverySocket = INVALID_SOCKET; DiscoverySocket = INVALID_SOCKET;
return; return false;
} }
int opt_true = 1; int opt_true = 1;
@ -345,16 +412,29 @@ void StartDiscovery()
{ {
closesocket(DiscoverySocket); closesocket(DiscoverySocket);
DiscoverySocket = INVALID_SOCKET; DiscoverySocket = INVALID_SOCKET;
return; return false;
} }
printf("startdisco\n");
DiscoveryLastTick = SDL_GetTicks(); DiscoveryLastTick = SDL_GetTicks();
DiscoveryList.clear(); DiscoveryList.clear();
Active = true; Active = true;
return true;
} }
void StartHost(const char* playername, int numplayers) void EndDiscovery()
{
if (DiscoverySocket != INVALID_SOCKET)
{
closesocket(DiscoverySocket);
DiscoverySocket = INVALID_SOCKET;
}
if (!IsHost)
Active = false;
}
bool StartHost(const char* playername, int numplayers)
{ {
ENetAddress addr; ENetAddress addr;
addr.host = ENET_HOST_ANY; addr.host = ENET_HOST_ANY;
@ -363,9 +443,7 @@ void StartHost(const char* playername, int numplayers)
Host = enet_host_create(&addr, 16, 2, 0, 0); Host = enet_host_create(&addr, 16, 2, 0, 0);
if (!Host) if (!Host)
{ {
// TODO handle this gracefully return false;
printf("host shat itself :(\n");
return;
} }
Player* player = &Players[0]; Player* player = &Players[0];
@ -388,28 +466,26 @@ void StartHost(const char* playername, int numplayers)
lanDlg->updatePlayerList(Players, NumPlayers); lanDlg->updatePlayerList(Players, NumPlayers);
StartDiscovery(); StartDiscovery();
return true;
} }
void StartClient(const char* playername, const char* host) bool StartClient(const char* playername, const char* host)
{ {
Host = enet_host_create(nullptr, 16, 2, 0, 0); Host = enet_host_create(nullptr, 16, 2, 0, 0);
if (!Host) if (!Host)
{ {
// TODO handle this gracefully return false;
printf("client shat itself :(\n");
return;
} }
printf("client created, connecting (%s, %s:%d)\n", playername, host, kLANPort);
ENetAddress addr; ENetAddress addr;
enet_address_set_host(&addr, host); enet_address_set_host(&addr, host);
addr.port = kLANPort; addr.port = kLANPort;
ENetPeer* peer = enet_host_connect(Host, &addr, 2, 0); ENetPeer* peer = enet_host_connect(Host, &addr, 2, 0);
if (!peer) if (!peer)
{ {
printf("connect shat itself :(\n"); enet_host_destroy(Host);
return; Host = nullptr;
return false;
} }
ENetEvent event; ENetEvent event;
@ -418,16 +494,16 @@ void StartClient(const char* playername, const char* host)
{ {
if (event.type == ENET_EVENT_TYPE_CONNECT) if (event.type == ENET_EVENT_TYPE_CONNECT)
{ {
printf("connected!\n");
conn = true; conn = true;
} }
} }
if (!conn) if (!conn)
{ {
printf("connection failed\n");
enet_peer_reset(peer); enet_peer_reset(peer);
return; enet_host_destroy(Host);
Host = nullptr;
return false;
} }
Player* player = &MyPlayer; Player* player = &MyPlayer;
@ -442,6 +518,7 @@ void StartClient(const char* playername, const char* host)
Active = true; Active = true;
IsHost = false; IsHost = false;
return true;
} }
@ -480,6 +557,8 @@ void ProcessDiscovery()
} }
else else
{ {
DiscoveryMutex.lock();
// listen for LAN sessions // listen for LAN sessions
fd_set fd; fd_set fd;
@ -503,6 +582,13 @@ void ProcessDiscovery()
if (beacon.NumPlayers > beacon.MaxPlayers) continue; if (beacon.NumPlayers > beacon.MaxPlayers) continue;
u32 key = ntohl(raddr.sin_addr.s_addr); u32 key = ntohl(raddr.sin_addr.s_addr);
if (DiscoveryList.find(key) != DiscoveryList.end())
{
if (beacon.Tick <= DiscoveryList[key].Tick)
continue;
}
beacon.Magic = tick; beacon.Magic = tick;
DiscoveryList[key] = beacon; DiscoveryList[key] = beacon;
} }
@ -524,11 +610,12 @@ void ProcessDiscovery()
DiscoveryList.erase(key); DiscoveryList.erase(key);
} }
for (const auto& [key, data] : DiscoveryList) DiscoveryMutex.unlock();
{
printf("DISCOVERY: %d.%d.%d.%d\n", key>>24, (key>>16)&0xFF, (key>>8)&0xFF, key&0xFF); // update the list in the connect dialog if needed
printf("- game: %s, %d/%d players\n", data.SessionName, data.NumPlayers, data.MaxPlayers);
} if (lanClientDlg)
lanClientDlg->updateDiscoveryList();
} }
} }

View File

@ -20,7 +20,9 @@
#define LAN_H #define LAN_H
#include <string> #include <string>
#include <map>
#include <QDialog> #include <QDialog>
#include <QMutex>
#include "types.h" #include "types.h"
@ -33,6 +35,7 @@ class LANDialog;
namespace LAN namespace LAN
{ {
struct Player struct Player
{ {
int ID; int ID;
@ -40,6 +43,18 @@ struct Player
int Status; // 0=no player 1=normal 2=host 3=connecting int Status; // 0=no player 1=normal 2=host 3=connecting
u32 Address; u32 Address;
}; };
struct DiscoveryData
{
u32 Magic;
u32 Version;
u32 Tick;
char SessionName[64];
u8 NumPlayers;
u8 MaxPlayers;
u8 Status; // 0=idle 1=playing
};
} }
class LANStartHostDialog : public QDialog class LANStartHostDialog : public QDialog
@ -79,9 +94,17 @@ public:
return dlg; return dlg;
} }
void updateDiscoveryList();
signals:
void sgUpdateDiscoveryList();
private slots: private slots:
void onDirectConnect();
void done(int r); void done(int r);
void doUpdateDiscoveryList();
private: private:
Ui::LANStartClientDialog* ui; Ui::LANStartClientDialog* ui;
}; };
@ -120,12 +143,16 @@ namespace LAN
extern bool Active; extern bool Active;
extern std::map<u32, DiscoveryData> DiscoveryList;
extern QMutex DiscoveryMutex;
bool Init(); bool Init();
void DeInit(); void DeInit();
void StartDiscovery(); bool StartDiscovery();
void StartHost(const char* player, int numplayers); void EndDiscovery();
void StartClient(const char* player, const char* host); bool StartHost(const char* player, int numplayers);
bool StartClient(const char* player, const char* host);
void ProcessFrame(); void ProcessFrame();

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>547</width>
<height>229</height> <height>409</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -20,33 +20,49 @@
<string>Join LAN game - melonDS</string> <string>Join LAN game - melonDS</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item> <item>
<layout class="QFormLayout" name="formLayout"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item row="0" column="0"> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Player name:</string> <string>Player name:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QLineEdit" name="txtPlayerName"/> <widget class="QLineEdit" name="txtPlayerName">
</item> <property name="sizePolicy">
<item row="1" column="0"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<widget class="QLabel" name="label_3"> <horstretch>0</horstretch>
<property name="text"> <verstretch>0</verstretch>
<string>Host address:</string> </sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item>
<widget class="QLineEdit" name="txtIPAddress"/> <spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QTreeView" name="tvAvailableGames"/>
</item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">