diff --git a/src/frontend/qt_sdl/NetplayDialog.cpp b/src/frontend/qt_sdl/NetplayDialog.cpp index e9ed6022..78f448d4 100644 --- a/src/frontend/qt_sdl/NetplayDialog.cpp +++ b/src/frontend/qt_sdl/NetplayDialog.cpp @@ -24,18 +24,11 @@ #include #include -#include -#include "NDS.h" -#include "NDSCart.h" -#include "main.h" -//#include "IPC.h" #include "NetplayDialog.h" -//#include "Input.h" -//#include "ROMManager.h" #include "Config.h" -#include "Savestate.h" -#include "Platform.h" +#include "main.h" +#include "Netplay.h" #include "ui_NetplayStartHostDialog.h" #include "ui_NetplayStartClientDialog.h" @@ -44,8 +37,9 @@ using namespace melonDS; -extern EmuThread* emuThread; -NetplayDialog* netplayDlg; +NetplayDialog* netplayDlg = nullptr; + +#define netplay() ((Netplay&)MPInterface::Get()) NetplayStartHostDialog::NetplayStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NetplayStartHostDialog) @@ -53,6 +47,8 @@ NetplayStartHostDialog::NetplayStartHostDialog(QWidget* parent) : QDialog(parent ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); + setMPInterface(MPInterface_Netplay); + ui->txtPort->setText("8064"); } @@ -72,7 +68,11 @@ void NetplayStartHostDialog::done(int r) netplayDlg = NetplayDialog::openDlg(parentWidget()); - Netplay::StartHost(player.c_str(), port); + netplay().StartHost(player.c_str(), port); + } + else + { + setMPInterface(MPInterface_Local); } QDialog::done(r); @@ -84,6 +84,8 @@ NetplayStartClientDialog::NetplayStartClientDialog(QWidget* parent) : QDialog(pa ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); + setMPInterface(MPInterface_Netplay); + ui->txtPort->setText("8064"); } @@ -104,7 +106,11 @@ void NetplayStartClientDialog::done(int r) netplayDlg = NetplayDialog::openDlg(parentWidget()); - Netplay::StartClient(player.c_str(), host.c_str(), port); + netplay().StartClient(player.c_str(), host.c_str(), port); + } + else + { + setMPInterface(MPInterface_Local); } QDialog::done(r); @@ -119,50 +125,55 @@ NetplayDialog::NetplayDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Netp QStandardItemModel* model = new QStandardItemModel(); ui->tvPlayerList->setModel(model); - connect(this, &NetplayDialog::sgUpdatePlayerList, this, &NetplayDialog::doUpdatePlayerList); + timerID = startTimer(1000); } NetplayDialog::~NetplayDialog() { + killTimer(timerID); + delete ui; } void NetplayDialog::done(int r) { - // ??? + // TODO QDialog::done(r); } -void NetplayDialog::updatePlayerList(Netplay::Player* players, int num) +void NetplayDialog::timerEvent(QTimerEvent *event) { - emit sgUpdatePlayerList(players, num); + doUpdatePlayerList(); } -void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) +void NetplayDialog::doUpdatePlayerList() { + auto playerlist = netplay().GetPlayerList(); + int numplayers = playerlist.size(); + auto maxplayers = netplay().GetMaxPlayers(); + QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); model->clear(); - model->setRowCount(num); + model->setRowCount(numplayers); // TODO: remove IP column in final product const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; model->setHorizontalHeaderLabels(header); - for (int i = 0; i < num; i++) + int i = 0; + for (const auto& player : playerlist) { - Netplay::Player* player = &players[i]; - - QString id = QString("%0").arg(player->ID+1); + QString id = QString("%0").arg(player.ID+1); model->setItem(i, 0, new QStandardItem(id)); - QString name = player->Name; + QString name = player.Name; model->setItem(i, 1, new QStandardItem(name)); QString status; - switch (player->Status) + switch (player.Status) { case 1: status = ""; break; case 2: status = "Host"; break; @@ -174,8 +185,10 @@ void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) model->setItem(i, 3, new QStandardItem("x")); char ip[32]; - u32 addr = player->Address; + 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)); + + i++; } } diff --git a/src/frontend/qt_sdl/NetplayDialog.h b/src/frontend/qt_sdl/NetplayDialog.h index 1fa0dcf2..cd1cdc98 100644 --- a/src/frontend/qt_sdl/NetplayDialog.h +++ b/src/frontend/qt_sdl/NetplayDialog.h @@ -94,18 +94,17 @@ public: return dlg; } - void updatePlayerList(Netplay::Player* players, int num); - -signals: - void sgUpdatePlayerList(Netplay::Player* players, int num); +protected: + void timerEvent(QTimerEvent* event) override; private slots: void done(int r); - void doUpdatePlayerList(Netplay::Player* players, int num); + void doUpdatePlayerList(); private: Ui::NetplayDialog* ui; + int timerID; }; #endif // NETPLAYDIALOG_H diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index b59bb770..4fe86449 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -75,6 +75,7 @@ #include "Savestate.h" #include "MPInterface.h" #include "LANDialog.h" +#include "NetplayDialog.h" //#include "main_shaders.h" @@ -1705,14 +1706,14 @@ void MainWindow::onLANStartClient() void MainWindow::onNPStartHost() { - //Netplay::StartHost(); - //NetplayStartHostDialog::openDlg(this); + if (!netplayWarning(true)) return; + NetplayStartHostDialog::openDlg(this); } void MainWindow::onNPStartClient() { - //Netplay::StartClient(); - //NetplayStartClientDialog::openDlg(this); + if (!netplayWarning(false)) return; + NetplayStartClientDialog::openDlg(this); } void MainWindow::onNPTest() @@ -1752,6 +1753,15 @@ bool MainWindow::lanWarning(bool host) return true; } +bool MainWindow::netplayWarning(bool host) +{ + // TODO + // * if a game is running it needs to be closed + // * if multiple instances are open they need to be closed + + return true; +} + void MainWindow::onOpenEmuSettings() { emuThread->emuPause(); diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 30d97b17..92879afe 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -242,6 +242,7 @@ private: void createScreenPanel(); bool lanWarning(bool host); + bool netplayWarning(bool host); bool showOSD; diff --git a/src/net/LocalMP.cpp b/src/net/LocalMP.cpp index a789964e..058deedb 100644 --- a/src/net/LocalMP.cpp +++ b/src/net/LocalMP.cpp @@ -59,6 +59,8 @@ LocalMP::~LocalMP() noexcept } Mutex_Free(MPQueueLock); + + Log(LogLevel::Info, "MP comm deinited\n"); } void LocalMP::Begin(int inst) diff --git a/src/net/MPInterface.cpp b/src/net/MPInterface.cpp index 39d1915d..7b8605ae 100644 --- a/src/net/MPInterface.cpp +++ b/src/net/MPInterface.cpp @@ -19,6 +19,7 @@ #include "MPInterface.h" #include "LocalMP.h" #include "LAN.h" +#include "Netplay.h" namespace melonDS { @@ -47,6 +48,8 @@ MPInterfaceType MPInterface::CurrentType = MPInterface_Dummy; void MPInterface::Set(MPInterfaceType type) { + Current = nullptr; + switch (type) { case MPInterface_Local: @@ -57,6 +60,10 @@ void MPInterface::Set(MPInterfaceType type) Current = std::make_unique(); break; + case MPInterface_Netplay: + Current = std::make_unique(); + break; + default: Current = std::make_unique(); break; diff --git a/src/net/Netplay.cpp b/src/net/Netplay.cpp index 1dc04282..5ab5fb71 100644 --- a/src/net/Netplay.cpp +++ b/src/net/Netplay.cpp @@ -33,95 +33,83 @@ #include "Savestate.h" #include "Platform.h" -using namespace melonDS; -namespace Netplay +namespace melonDS { -bool Active; -bool IsHost; -bool IsMirror; - -ENetHost* Host; -ENetHost* MirrorHost; - -Player Players[16]; -int NumPlayers; - -Player MyPlayer; -u32 HostAddress; -bool Lag; - -int NumMirrorClients; - -struct InputFrame -{ - u32 FrameNum; - u32 KeyMask; - u32 Touching; - u32 TouchX, TouchY; -}; - -std::queue InputQueue; - -enum -{ - Blob_CartROM = 0, - Blob_CartSRAM, - Blob_InitState, - - Blob_MAX -}; - -const u32 kChunkSize = 0x10000; -u8 ChunkBuffer[0x10 + kChunkSize]; -u8* Blobs[Blob_MAX]; -u32 BlobLens[Blob_MAX]; -int CurBlobType; -u32 CurBlobLen; +const u32 kLocalhost = 0x0100007F; -bool Init() +Netplay::Netplay() noexcept : LocalMP(), Inited(false) { Active = false; IsHost = false; - IsMirror = false; Host = nullptr; - MirrorHost = nullptr; Lag = false; + PlayersMutex = Platform::Mutex_Create(); + + memset(RemotePeers, 0, sizeof(RemotePeers)); memset(Players, 0, sizeof(Players)); NumPlayers = 0; + MaxPlayers = 0; - NumMirrorClients = 0; - - for (int i = 0; i < Blob_MAX; i++) + // TODO make this somewhat nicer + if (enet_initialize() != 0) { - Blobs[i] = nullptr; - BlobLens[i] = 0; - } - CurBlobType = -1; - CurBlobLen = 0; - - /*if (enet_initialize() != 0) - { - printf("enet shat itself :(\n"); - return false; + Platform::Log(Platform::LogLevel::Error, "Netplay: failed to initialize enet\n"); + return; } - printf("enet init OK\n");*/ - return true; + Platform::Log(Platform::LogLevel::Info, "Netplay: enet initialized\n"); + Inited = true; } -void DeInit() +Netplay::~Netplay() { - // TODO: cleanup resources properly!! + EndSession(); - //enet_deinitialize(); + Inited = false; + enet_deinitialize(); + + Platform::Mutex_Free(PlayersMutex); + + Platform::Log(Platform::LogLevel::Info, "Netplay: enet deinitialized\n"); } -void StartHost(const char* playername, int port) +std::vector Netplay::GetPlayerList() +{ + Platform::Mutex_Lock(PlayersMutex); + + std::vector ret; + for (int i = 0; i < 16; i++) + { + if (Players[i].Status == Player_None) continue; + + // make a copy of the player entry, fix up the address field + Player newp = Players[i]; + if (newp.ID == MyPlayer.ID) + { + newp.IsLocalPlayer = true; + newp.Address = kLocalhost; + } + else + { + newp.IsLocalPlayer = false; + if (newp.Status == Player_Host) + newp.Address = HostAddress; + } + + ret.push_back(newp); + } + + Platform::Mutex_Unlock(PlayersMutex); + return ret; +} + + +bool Netplay::StartHost(const char* playername, int port) { ENetAddress addr; addr.host = ENET_HOST_ANY; @@ -131,7 +119,7 @@ void StartHost(const char* playername, int port) if (!Host) { printf("host shat itself :(\n"); - return; + return false; } Player* player = &Players[0]; @@ -145,7 +133,7 @@ void StartHost(const char* playername, int port) HostAddress = 0x0100007F; - NumMirrorClients = 0; + /*NumMirrorClients = 0; ENetAddress mirroraddr; mirroraddr.host = ENET_HOST_ANY; @@ -155,23 +143,24 @@ printf("host mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr.p if (!MirrorHost) { printf("mirror host shat itself :(\n"); - return; - } + return false; + }*/ Active = true; IsHost = true; - IsMirror = false; + //IsMirror = false; //netplayDlg->updatePlayerList(Players, NumPlayers); + return true; } -void StartClient(const char* playername, const char* host, int port) +bool Netplay::StartClient(const char* playername, const char* host, int port) { Host = enet_host_create(nullptr, 1, 1, 0, 0); if (!Host) { printf("client shat itself :(\n"); - return; + return false; } printf("client created, connecting (%s, %s:%d)\n", playername, host, port); @@ -183,7 +172,7 @@ void StartClient(const char* playername, const char* host, int port) if (!peer) { printf("connect shat itself :(\n"); - return; + return false; } ENetEvent event; @@ -201,7 +190,7 @@ void StartClient(const char* playername, const char* host, int port) { printf("connection failed\n"); enet_peer_reset(peer); - return; + return false; } Player* player = &MyPlayer; @@ -214,10 +203,17 @@ void StartClient(const char* playername, const char* host, int port) Active = true; IsHost = false; - IsMirror = false; + //IsMirror = false; + return true; } -void StartMirror(const Player* player) +void Netplay::EndSession() +{ + // todo +} + +#if 0 +void Netplay::StartMirror(const Player* player) { for (int i = 0; i < Blob_MAX; i++) { @@ -275,7 +271,7 @@ void StartMirror(const Player* player) } -u32 PlayerAddress(int id) +u32 Netplay::PlayerAddress(int id) { if (id < 0 || id > 16) return 0; @@ -285,9 +281,9 @@ u32 PlayerAddress(int id) } -bool SpawnMirrorInstance(Player player) +bool Netplay::SpawnMirrorInstance(Player player) { -#if 0 + u16 curmask = IPC::GetInstanceBitmask(); QProcess newinst; @@ -336,11 +332,11 @@ bool SpawnMirrorInstance(Player player) if (player.Address == 0x0100007F) player.Address = HostAddress; // calls Netplay::StartMirror() //IPC::SendCommand(1<data; if (buf[0] == 0x01) @@ -463,7 +459,7 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); if (pkt->dataLength != 2) return; bool res = false; -#if 0 + // reset NDS::SetConsoleType(buf[1]); NDS::EjectCart(); @@ -488,10 +484,10 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); //LoadCheats(); } -#endif + // load initial state // TODO: terrible hack!! - #if 0 + FILE* f = Platform::OpenFile("netplay2.mln", "wb"); fwrite(Blobs[Blob_InitState], BlobLens[Blob_InitState], 1, f); fclose(f); @@ -513,7 +509,6 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); printf("[MC] state loaded, PC=%08X/%08X\n", NDS::GetPC(0), NDS::GetPC(1)); ENetPacket* resp = enet_packet_create(buf, 1, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(peer, 1, resp); - #endif } else if (buf[0] == 0x05) { @@ -522,11 +517,12 @@ printf("[MC] state loaded, PC=%08X/%08X\n", NDS::GetPC(0), NDS::GetPC(1)); } } -void SyncMirrorClients() + +void Netplay::SyncMirrorClients() { printf("[MIRROR HOST] syncing clients\n"); -#if 0 + SendBlobToMirrorClients(Blob_CartSRAM, NDSCart::GetSaveMemoryLength(), NDSCart::GetSaveMemory()); // send initial state @@ -586,10 +582,11 @@ void SyncMirrorClients() //enet_host_flush(MirrorHost); StartLocal(); -#endif -} -void StartGame() +} +#endif + +void Netplay::StartGame() { if (!IsHost) { @@ -600,7 +597,7 @@ void StartGame() // spawn mirror instances as needed for (int i = 1; i < NumPlayers; i++) { - SpawnMirrorInstance(Players[i]); + //SpawnMirrorInstance(Players[i]); } //SyncMirrorClients(); @@ -623,7 +620,7 @@ void StartGame() //StartLocal(); } -void StartLocal() +void Netplay::StartLocal() { for (int i = 0; i < 4; i++) { @@ -641,7 +638,7 @@ void StartLocal() } -void ProcessHost() +void Netplay::ProcessHost() { if (!Host) return; @@ -730,7 +727,7 @@ void ProcessHost() } } -void ProcessClient() +void Netplay::ProcessClient() { if (!Host) return; @@ -764,7 +761,7 @@ void ProcessClient() NumMirrorClients = 0; // create mirror host - ENetAddress mirroraddr; + /*ENetAddress mirroraddr; mirroraddr.host = ENET_HOST_ANY; mirroraddr.port = 8064+1 + data[1]; // FIXME!!!! printf("client mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr.port); @@ -773,7 +770,7 @@ printf("client mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr { printf("mirror host shat itself :(\n"); break; - } + }*/ // send player information MyPlayer.ID = data[1]; @@ -804,11 +801,11 @@ printf("client mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr case 0x04: // start game { // spawn mirror instances as needed - for (int i = 0; i < NumPlayers; i++) + /*for (int i = 0; i < NumPlayers; i++) { if (i != MyPlayer.ID) SpawnMirrorInstance(Players[i]); - } + }*/ //SyncMirrorClients(); printf("bourf\n"); @@ -826,10 +823,11 @@ printf("birf\n"); } } -void ProcessMirrorHost() +#if 0 +void Netplay::ProcessMirrorHost() { if (!MirrorHost) return; -#if 0 + bool block = false; ENetEvent event; while (enet_host_service(MirrorHost, &event, block ? 5000 : 0) > 0) @@ -915,13 +913,12 @@ void ProcessMirrorHost() break; } } -#endif } -void ProcessMirrorClient() +void Netplay::ProcessMirrorClient() { if (!MirrorHost) return; -#if 0 + bool block = false; if (emuThread->emuIsRunning())// && NDS::NumFrames > 4) { @@ -982,16 +979,16 @@ printf("mirror client lag notify: %d\n", lag); if (block) break; } -#endif } +#endif -void ProcessFrame() +void Netplay::ProcessFrame() { - if (IsMirror) + /*if (IsMirror) { ProcessMirrorClient(); } - else + else*/ { if (IsHost) { @@ -1002,11 +999,12 @@ void ProcessFrame() ProcessClient(); } - ProcessMirrorHost(); + //ProcessMirrorHost(); } } -void ProcessInput() +#if 0 +void Netplay::ProcessInput() { // netplay input processing // @@ -1022,7 +1020,7 @@ void ProcessInput() // apply each input to the frame it's assigned to // before running a frame, we need to wait to have received input for it // TODO: alert host if we are running too far behind -#if 0 + if (!IsMirror) { u32 lag = 4; // TODO: make configurable!! @@ -1078,7 +1076,13 @@ void ProcessInput() else NDS::ReleaseScreen(); InputQueue.pop(); +} #endif + +void Netplay::Process() +{ + ProcessFrame(); + LocalMP::Process(); } } diff --git a/src/net/Netplay.h b/src/net/Netplay.h index 1eff54b0..a9e48e3d 100644 --- a/src/net/Netplay.h +++ b/src/net/Netplay.h @@ -19,39 +19,93 @@ #ifndef NETPLAY_H #define NETPLAY_H +#include + +#include + #include "types.h" +#include "Platform.h" +#include "LocalMP.h" -namespace Netplay +namespace melonDS { -struct Player +// since netplay relies on local MP comm locally, +// we extend the LocalMP class to reuse its functionality +class Netplay : public LocalMP { - int ID; - char Name[32]; - int Status; // 0=no player 1=normal 2=host 3=connecting - melonDS::u32 Address; +public: + Netplay() noexcept; + Netplay(const Netplay&) = delete; + Netplay& operator=(const Netplay&) = delete; + Netplay(Netplay&& other) = delete; + Netplay& operator=(Netplay&& other) = delete; + ~Netplay() noexcept; + + enum PlayerStatus + { + Player_None = 0, // no player in this entry + Player_Client, // game client + Player_Host, // game host + Player_Connecting, // player still connecting + Player_Disconnected, // player disconnected + }; + + struct Player + { + int ID; + char Name[32]; + int Status; // 0=no player 1=normal 2=host 3=connecting + u32 Address; + bool IsLocalPlayer; + }; + + bool StartHost(const char* player, int port); + bool StartClient(const char* player, const char* host, int port); + void EndSession(); + + std::vector GetPlayerList(); + int GetNumPlayers() { return NumPlayers; } + int GetMaxPlayers() { return MaxPlayers; } + + void Process() override; + +private: + bool Inited; + bool Active; + bool IsHost; + + ENetHost* Host; + ENetPeer* RemotePeers[16]; + + Player Players[16]; + int NumPlayers; + int MaxPlayers; + Platform::Mutex* PlayersMutex; + + Player MyPlayer; + u32 HostAddress; + bool Lag; + + int NumMirrorClients; + + struct InputFrame + { + u32 FrameNum; + u32 KeyMask; + u32 Touching; + u32 TouchX, TouchY; + }; + + std::queue InputQueue; + + void StartGame(); + void StartLocal(); + void ProcessHost(); + void ProcessClient(); + void ProcessFrame(); }; - -extern bool Active; - -bool Init(); -void DeInit(); - -void StartHost(const char* player, int port); -void StartClient(const char* player, const char* host, int port); -void StartMirror(const Player* player); - -melonDS::u32 PlayerAddress(int id); - -void StartGame(); -void StartLocal(); - -void StartGame(); - -void ProcessFrame(); -void ProcessInput(); - } #endif // NETPLAY_H