get things in a startable state

This commit is contained in:
Arisotura 2024-08-11 12:46:44 +02:00
parent a35865620f
commit a985478979
8 changed files with 262 additions and 172 deletions

View File

@ -24,18 +24,11 @@
#include <enet/enet.h>
#include <QStandardItemModel>
#include <QProcess>
#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++;
}
}

View File

@ -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

View File

@ -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();

View File

@ -242,6 +242,7 @@ private:
void createScreenPanel();
bool lanWarning(bool host);
bool netplayWarning(bool host);
bool showOSD;

View File

@ -59,6 +59,8 @@ LocalMP::~LocalMP() noexcept
}
Mutex_Free(MPQueueLock);
Log(LogLevel::Info, "MP comm deinited\n");
}
void LocalMP::Begin(int inst)

View File

@ -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<LAN>();
break;
case MPInterface_Netplay:
Current = std::make_unique<Netplay>();
break;
default:
Current = std::make_unique<DummyMP>();
break;

View File

@ -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<InputFrame> 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::Player> Netplay::GetPlayerList()
{
Platform::Mutex_Lock(PlayersMutex);
std::vector<Player> 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<<newid, IPC::Cmd_SetupNetplayMirror, sizeof(Player), &player);
#endif
return true;
}
bool SendBlobToMirrorClients(int type, u32 len, u8* data)
bool Netplay::SendBlobToMirrorClients(int type, u32 len, u8* data)
{
u8* buf = ChunkBuffer;
@ -381,7 +377,7 @@ bool SendBlobToMirrorClients(int type, u32 len, u8* data)
return true;
}
void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt)
void Netplay::RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt)
{
u8* buf = pkt->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();
}
}

View File

@ -19,39 +19,93 @@
#ifndef NETPLAY_H
#define NETPLAY_H
#include <queue>
#include <enet/enet.h>
#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<Player> 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<InputFrame> 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