From df624bfa184128b712fda35eeeb8e2ff58656e0f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 24 Mar 2023 17:13:50 +0100 Subject: [PATCH] basic host/client connect infrastructure, player list --- src/frontend/qt_sdl/Netplay.cpp | 345 ++++++++++++++++++++++++++++++-- src/frontend/qt_sdl/Netplay.h | 102 +++++++++- src/frontend/qt_sdl/main.cpp | 6 +- 3 files changed, 433 insertions(+), 20 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index d42827bf..c6fb19ca 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -23,13 +23,146 @@ #include +#include + #include "NDS.h" #include "main.h" #include "Netplay.h" #include "Input.h" +#include "ui_NetplayStartHostDialog.h" +#include "ui_NetplayStartClientDialog.h" +#include "ui_NetplayDialog.h" + 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, &sgUpdatePlayerList, this, &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); + + const QStringList header = {"#", "Player", "Status", "Ping"}; + 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")); + } +} namespace Netplay @@ -39,7 +172,11 @@ bool Active; bool IsHost; ENetHost* Host; -ENetPeer* Peer; + +Player Players[16]; +int NumPlayers; + +Player TempPlayer; // holds client player info temporarily struct InputFrame { @@ -50,12 +187,18 @@ struct InputFrame std::queue InputQueue; +// + + bool Init() { Active = false; IsHost = false; Host = nullptr; + memset(Players, 0, sizeof(Players)); + NumPlayers = 0; + if (enet_initialize() != 0) { printf("enet shat itself :(\n"); @@ -72,41 +215,48 @@ void DeInit() } -void StartHost() +void StartHost(const char* playername, int port) { ENetAddress addr; addr.host = ENET_HOST_ANY; - addr.port = 8064; + addr.port = port; - // TODO determine proper number of clients/channels - Host = enet_host_create(&addr, 16, 16, 0, 0); + Host = enet_host_create(&addr, 16, 1, 0, 0); if (!Host) { printf("host shat itself :(\n"); return; } + Player* player = &Players[0]; + memset(player, 0, sizeof(Player)); + player->ID = 0; + strncpy(player->Name, playername, 31); + player->Status = 2; + NumPlayers = 1; + Active = true; IsHost = true; + + netplayDlg->updatePlayerList(Players, NumPlayers); } -void StartClient() +void StartClient(const char* playername, const char* host, int port) { - // TODO determine proper number of clients/channels - Host = enet_host_create(nullptr, 16, 16, 0, 0); + Host = enet_host_create(nullptr, 16, 1, 0, 0); if (!Host) { printf("client shat itself :(\n"); return; } - printf("client created, connecting\n"); + printf("client created, connecting (%s, %s:%d)\n", playername, host, port); ENetAddress addr; - enet_address_set_host(&addr, "127.0.0.1"); // TODO!!!! - addr.port = 8064; - Peer = enet_host_connect(Host, &addr, 2, 0); - if (!Peer) + enet_address_set_host(&addr, host); + addr.port = port; + ENetPeer* peer = enet_host_connect(Host, &addr, 1, 0); + if (!peer) { printf("connect shat itself :(\n"); return; @@ -126,9 +276,16 @@ void StartClient() if (!conn) { printf("connection failed\n"); - enet_peer_reset(Peer); + enet_peer_reset(peer); + return; } + Player* player = &TempPlayer; + memset(player, 0, sizeof(Player)); + player->ID = 0; + strncpy(player->Name, playername, 31); + player->Status = 3; + Active = true; IsHost = false; } @@ -153,8 +310,164 @@ void StartGame() } +void ProcessHost() +{ + ENetEvent event; + while (enet_host_service(Host, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + // client connected; assign player number + + int id; + for (id = 0; id < 16; id++) + { + if (id >= NumPlayers) break; + if (Players[id].Status == 0) break; + } + + if (id < 16) + { + u8 cmd[2]; + cmd[0] = 0x01; + cmd[1] = (u8)id; + ENetPacket* pkt = enet_packet_create(cmd, 2, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, pkt); + + Players[id].ID = id; + Players[id].Status = 3; + event.peer->data = &Players[id]; + NumPlayers++; + } + } + break; + + case ENET_EVENT_TYPE_DISCONNECT: + { + // TODO + printf("disco\n"); + } + break; + + case ENET_EVENT_TYPE_RECEIVE: + { + if (event.packet->dataLength < 1) break; + + u8* data = (u8*)event.packet->data; + switch (data[0]) + { + case 0x02: // client sending player info + { + if (event.packet->dataLength != (1+sizeof(Player))) break; + + Player player; + memcpy(&player, &data[1], sizeof(Player)); + player.Name[31] = '\0'; + + Player* hostside = (Player*)event.peer->data; + if (player.ID != hostside->ID) + { + printf("what??? %d =/= %d\n", player.ID, hostside->ID); + // TODO: disconnect + break; + } + + player.Status = 1; + memcpy(hostside, &player, sizeof(Player)); + + // broadcast updated player list + u8 cmd[2+sizeof(Players)]; + cmd[0] = 0x03; + cmd[1] = (u8)NumPlayers; + memcpy(&cmd[2], Players, sizeof(Players)); + ENetPacket* pkt = enet_packet_create(cmd, 2+sizeof(Players), ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(Host, 0, pkt); + + netplayDlg->updatePlayerList(Players, NumPlayers); + } + break; + } + } + break; + } + } +} + +void ProcessClient() +{ + ENetEvent event; + while (enet_host_service(Host, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + printf("schmo\n"); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + { + // TODO + printf("shma\n"); + } + break; + + case ENET_EVENT_TYPE_RECEIVE: + { + if (event.packet->dataLength < 1) break; + + u8* data = (u8*)event.packet->data; + switch (data[0]) + { + case 0x01: // host sending player ID + { + if (event.packet->dataLength != 2) break; + + // send player information + TempPlayer.ID = data[1];printf("host assigned ID: %d\n", data[1]); + u8 cmd[1+sizeof(Player)]; + cmd[0] = 0x02; + memcpy(&cmd[1], &TempPlayer, sizeof(Player)); + ENetPacket* pkt = enet_packet_create(cmd, 1+sizeof(Player), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, pkt); + } + break; + + case 0x03: // host sending player list + { + if (event.packet->dataLength != (2+sizeof(Players))) break; + if (data[1] > 16) break; + + NumPlayers = data[1]; + memcpy(Players, &data[2], sizeof(Players)); + for (int i = 0; i < 16; i++) + { + Players[i].Name[31] = '\0'; + } + + netplayDlg->updatePlayerList(Players, NumPlayers); + } + break; + } + } + break; + } + } +} + void ProcessFrame() { + if (IsHost) + { + ProcessHost(); + } + else + { + ProcessClient(); + } + + return; bool block = false; if (emuThread->emuIsRunning()) { @@ -181,12 +494,12 @@ void ProcessFrame() { case ENET_EVENT_TYPE_CONNECT: printf("client connected %08X %d\n", event.peer->address.host, event.peer->address.port); - Peer = event.peer; + //Peer = event.peer; break; case ENET_EVENT_TYPE_DISCONNECT: printf("client disconnected %08X %d\n", event.peer->address.host, event.peer->address.port); - Peer = nullptr; + //Peer = nullptr; break; case ENET_EVENT_TYPE_RECEIVE: diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index cd4665d3..80bfcee5 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -19,8 +19,106 @@ #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 +{ + +struct Player +{ + int ID; + char Name[32]; + int Status; // 0=no player 1=normal 2=host 3=connecting +}; + +} + +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 { @@ -29,8 +127,8 @@ extern bool Active; bool Init(); void DeInit(); -void StartHost(); -void StartClient(); +void StartHost(const char* player, int port); +void StartClient(const char* player, const char* host, int port); void StartGame(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index caa3f0a4..8a0d0013 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -2855,12 +2855,14 @@ void MainWindow::onMPNewInstance() void MainWindow::onMPStartHost() { - Netplay::StartHost(); + //Netplay::StartHost(); + NetplayStartHostDialog::openDlg(this); } void MainWindow::onMPStartClient() { - Netplay::StartClient(); + //Netplay::StartClient(); + NetplayStartClientDialog::openDlg(this); } void MainWindow::onMPTest()