mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-25 15:19:53 -06:00
base for forwarding input to clients
This commit is contained in:
@ -19,6 +19,9 @@
|
|||||||
#ifndef INPUT_H
|
#ifndef INPUT_H
|
||||||
#define INPUT_H
|
#define INPUT_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace Input
|
namespace Input
|
||||||
|
@ -19,10 +19,17 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include <enet/enet.h>
|
#include <enet/enet.h>
|
||||||
|
|
||||||
|
#include "NDS.h"
|
||||||
|
#include "main.h"
|
||||||
#include "Netplay.h"
|
#include "Netplay.h"
|
||||||
|
#include "Input.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern EmuThread* emuThread;
|
||||||
|
|
||||||
|
|
||||||
namespace Netplay
|
namespace Netplay
|
||||||
@ -34,6 +41,14 @@ bool IsHost;
|
|||||||
ENetHost* Host;
|
ENetHost* Host;
|
||||||
ENetPeer* Peer;
|
ENetPeer* Peer;
|
||||||
|
|
||||||
|
struct InputFrame
|
||||||
|
{
|
||||||
|
u32 FrameNum;
|
||||||
|
u32 KeyMask;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::queue<InputFrame> InputQueue;
|
||||||
|
|
||||||
|
|
||||||
bool Init()
|
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()
|
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;
|
ENetEvent event;
|
||||||
while (enet_host_service(Host, &event, 0) > 0)
|
while (enet_host_service(Host, &event, block ? 5000 : 0) > 0)
|
||||||
{
|
{
|
||||||
switch (event.type)
|
switch (event.type)
|
||||||
{
|
{
|
||||||
case ENET_EVENT_TYPE_CONNECT:
|
case ENET_EVENT_TYPE_CONNECT:
|
||||||
printf("client connected %08X %d\n", event.peer->address.host, event.peer->address.port);
|
printf("client connected %08X %d\n", event.peer->address.host, event.peer->address.port);
|
||||||
|
Peer = event.peer;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ENET_EVENT_TYPE_DISCONNECT:
|
case ENET_EVENT_TYPE_DISCONNECT:
|
||||||
printf("client disconnected %08X %d\n", event.peer->address.host, event.peer->address.port);
|
printf("client disconnected %08X %d\n", event.peer->address.host, event.peer->address.port);
|
||||||
|
Peer = nullptr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ENET_EVENT_TYPE_RECEIVE:
|
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;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,10 @@ void DeInit();
|
|||||||
void StartHost();
|
void StartHost();
|
||||||
void StartClient();
|
void StartClient();
|
||||||
|
|
||||||
|
void StartGame();
|
||||||
|
|
||||||
void ProcessFrame();
|
void ProcessFrame();
|
||||||
|
void ProcessInput();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,7 +472,14 @@ void EmuThread::run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process input and hotkeys
|
// process input and hotkeys
|
||||||
NDS::SetKeyMask(Input::InputMask);
|
if (Netplay::Active)
|
||||||
|
{
|
||||||
|
Netplay::ProcessInput();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NDS::SetKeyMask(Input::InputMask);
|
||||||
|
}
|
||||||
|
|
||||||
if (Input::HotkeyPressed(HK_Lid))
|
if (Input::HotkeyPressed(HK_Lid))
|
||||||
{
|
{
|
||||||
@ -1576,6 +1583,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
|
|||||||
|
|
||||||
actMPStartClient = submenu->addAction("NETPLAY CLIENT");
|
actMPStartClient = submenu->addAction("NETPLAY CLIENT");
|
||||||
connect(actMPStartClient, &QAction::triggered, this, &MainWindow::onMPStartClient);
|
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);
|
recentFileList.prepend(filename);
|
||||||
updateRecentFilesMenu();
|
updateRecentFilesMenu();
|
||||||
|
|
||||||
NDS::Start();
|
if (!Netplay::Active)
|
||||||
emuThread->emuRun();
|
{
|
||||||
|
NDS::Start();
|
||||||
|
emuThread->emuRun();
|
||||||
|
}
|
||||||
|
|
||||||
updateCartInserted(false);
|
updateCartInserted(false);
|
||||||
}
|
}
|
||||||
@ -2434,8 +2447,11 @@ void MainWindow::onClickRecentFile()
|
|||||||
recentFileList.prepend(filename);
|
recentFileList.prepend(filename);
|
||||||
updateRecentFilesMenu();
|
updateRecentFilesMenu();
|
||||||
|
|
||||||
NDS::Start();
|
if (!Netplay::Active)
|
||||||
emuThread->emuRun();
|
{
|
||||||
|
NDS::Start();
|
||||||
|
emuThread->emuRun();
|
||||||
|
}
|
||||||
|
|
||||||
updateCartInserted(false);
|
updateCartInserted(false);
|
||||||
}
|
}
|
||||||
@ -2458,8 +2474,11 @@ void MainWindow::onBootFirmware()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::Start();
|
if (!Netplay::Active)
|
||||||
emuThread->emuRun();
|
{
|
||||||
|
NDS::Start();
|
||||||
|
emuThread->emuRun();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onInsertCart()
|
void MainWindow::onInsertCart()
|
||||||
@ -2844,6 +2863,12 @@ void MainWindow::onMPStartClient()
|
|||||||
Netplay::StartClient();
|
Netplay::StartClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onMPTest()
|
||||||
|
{
|
||||||
|
// HAX
|
||||||
|
Netplay::StartGame();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onOpenEmuSettings()
|
void MainWindow::onOpenEmuSettings()
|
||||||
{
|
{
|
||||||
emuThread->emuPause();
|
emuThread->emuPause();
|
||||||
|
@ -313,6 +313,7 @@ private slots:
|
|||||||
void onMPNewInstance();
|
void onMPNewInstance();
|
||||||
void onMPStartHost();
|
void onMPStartHost();
|
||||||
void onMPStartClient();
|
void onMPStartClient();
|
||||||
|
void onMPTest();
|
||||||
|
|
||||||
void onOpenEmuSettings();
|
void onOpenEmuSettings();
|
||||||
void onEmuSettingsDialogFinished(int res);
|
void onEmuSettingsDialogFinished(int res);
|
||||||
@ -412,6 +413,7 @@ public:
|
|||||||
QAction* actMPNewInstance;
|
QAction* actMPNewInstance;
|
||||||
QAction* actMPStartHost;
|
QAction* actMPStartHost;
|
||||||
QAction* actMPStartClient;
|
QAction* actMPStartClient;
|
||||||
|
QAction* actMPTest;
|
||||||
|
|
||||||
QAction* actEmuSettings;
|
QAction* actEmuSettings;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
Reference in New Issue
Block a user