base for forwarding input to clients

This commit is contained in:
Arisotura 2023-03-23 22:09:51 +01:00
parent 1d135bc0a5
commit 79494fad5e
5 changed files with 189 additions and 9 deletions

View File

@ -19,6 +19,9 @@
#ifndef INPUT_H
#define INPUT_H
#include <SDL2/SDL.h>
#include <QKeyEvent>
#include "types.h"
namespace Input

View File

@ -19,10 +19,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <enet/enet.h>
#include "NDS.h"
#include "main.h"
#include "Netplay.h"
#include "Input.h"
extern EmuThread* emuThread;
namespace Netplay
@ -34,6 +41,14 @@ bool IsHost;
ENetHost* Host;
ENetPeer* Peer;
struct InputFrame
{
u32 FrameNum;
u32 KeyMask;
};
std::queue<InputFrame> InputQueue;
bool Init()
{
@ -119,26 +134,158 @@ void StartClient()
}
void StartGame()
{
if (!IsHost)
{
printf("?????\n");
return;
}
// tell remote peers to start game
u8 cmd[1] = {0x01};
ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(Host, 0, pkt);
// start game locally
NDS::Start();
emuThread->emuRun();
}
void ProcessFrame()
{
bool block = false;
if (emuThread->emuIsRunning())
{
if (IsHost)
{
// TODO: prevent the clients from running too far behind
}
else
{
// block if we ran out of input frames
// TODO: in this situation, make sure we do receive an input frame
// or if we don't after X time, handle it gracefully
if (InputQueue.empty())
block = true;
}
}
block=false;
ENetEvent event;
while (enet_host_service(Host, &event, 0) > 0)
while (enet_host_service(Host, &event, block ? 5000 : 0) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_CONNECT:
printf("client connected %08X %d\n", event.peer->address.host, event.peer->address.port);
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;
break;
case ENET_EVENT_TYPE_RECEIVE:
printf("received shit\n");
{
if (event.packet->dataLength < 1)
{
printf("?????\n");
break;
}
u8* data = (u8*)event.packet->data;
switch (data[0])
{
case 0x01: // start game
NDS::Start();
emuThread->emuRun();
break;
case 0x02: // input frame
{
if (event.packet->dataLength != (sizeof(InputFrame)+1))
break;
InputFrame frame;
memcpy(&frame, &data[1], sizeof(InputFrame));
InputQueue.push(frame);
break;
}
}
}
break;
}
}
}
void ProcessInput()
{
// netplay input processing
//
// N = current frame #
// L = amount of lag frames
//
// host side:
// we take the current input (normally meant for frame N)
// and delay it to frame N+L
//
// client side:
// we receive input from the host
// 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 (IsHost)
{
u32 lag = 4; // TODO: make configurable!!
InputFrame frame;
frame.FrameNum = NDS::NumFrames + lag;
frame.KeyMask = Input::InputMask;
// TODO: touchscreen input and other shit!
InputQueue.push(frame);
u8 cmd[1+sizeof(InputFrame)];
cmd[0] = 0x02;
memcpy(&cmd[1], &frame, sizeof(InputFrame));
ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(Host, 0, pkt);
}
if (InputQueue.empty())
{
printf("Netplay: BAD! INPUT QUEUE EMPTY\n");
return;
}
InputFrame& frame = InputQueue.front();
if (frame.FrameNum < NDS::NumFrames)
{
printf("Netplay: BAD! LAGGING BEHIND\n");
while (frame.FrameNum < NDS::NumFrames)
{
if (InputQueue.size() < 2) break;
InputQueue.pop();
frame = InputQueue.front();
}
}
if (frame.FrameNum > NDS::NumFrames)
{
// frame in the future, ignore
return;
}
// apply this input frame
printf("[%08d] INPUT=%08X (%08d) (backlog=%d)\n", NDS::NumFrames, frame.KeyMask, frame.FrameNum, InputQueue.size());
NDS::SetKeyMask(frame.KeyMask);
InputQueue.pop();
}
}

View File

@ -32,7 +32,10 @@ void DeInit();
void StartHost();
void StartClient();
void StartGame();
void ProcessFrame();
void ProcessInput();
}

View File

@ -472,7 +472,14 @@ void EmuThread::run()
}
// process input and hotkeys
NDS::SetKeyMask(Input::InputMask);
if (Netplay::Active)
{
Netplay::ProcessInput();
}
else
{
NDS::SetKeyMask(Input::InputMask);
}
if (Input::HotkeyPressed(HK_Lid))
{
@ -1576,6 +1583,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actMPStartClient = submenu->addAction("NETPLAY CLIENT");
connect(actMPStartClient, &QAction::triggered, this, &MainWindow::onMPStartClient);
actMPTest = submenu->addAction("NETPLAY GO");
connect(actMPTest, &QAction::triggered, this, &MainWindow::onMPTest);
}
}
{
@ -2337,8 +2347,11 @@ void MainWindow::onOpenFile()
recentFileList.prepend(filename);
updateRecentFilesMenu();
NDS::Start();
emuThread->emuRun();
if (!Netplay::Active)
{
NDS::Start();
emuThread->emuRun();
}
updateCartInserted(false);
}
@ -2434,8 +2447,11 @@ void MainWindow::onClickRecentFile()
recentFileList.prepend(filename);
updateRecentFilesMenu();
NDS::Start();
emuThread->emuRun();
if (!Netplay::Active)
{
NDS::Start();
emuThread->emuRun();
}
updateCartInserted(false);
}
@ -2458,8 +2474,11 @@ void MainWindow::onBootFirmware()
return;
}
NDS::Start();
emuThread->emuRun();
if (!Netplay::Active)
{
NDS::Start();
emuThread->emuRun();
}
}
void MainWindow::onInsertCart()
@ -2844,6 +2863,12 @@ void MainWindow::onMPStartClient()
Netplay::StartClient();
}
void MainWindow::onMPTest()
{
// HAX
Netplay::StartGame();
}
void MainWindow::onOpenEmuSettings()
{
emuThread->emuPause();

View File

@ -313,6 +313,7 @@ private slots:
void onMPNewInstance();
void onMPStartHost();
void onMPStartClient();
void onMPTest();
void onOpenEmuSettings();
void onEmuSettingsDialogFinished(int res);
@ -412,6 +413,7 @@ public:
QAction* actMPNewInstance;
QAction* actMPStartHost;
QAction* actMPStartClient;
QAction* actMPTest;
QAction* actEmuSettings;
#ifdef __APPLE__