From d9cbf8297a1159279da148ebe3a57755aa7227ea Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 12 Nov 2022 22:20:52 +0100 Subject: [PATCH 01/81] temporary hack to make RTC deterministic --- src/RTC.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/RTC.cpp b/src/RTC.cpp index 94d7ae7c..fb99122f 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -133,7 +133,7 @@ void ByteIn(u8 val) case 0x20: { - time_t timestamp = time(NULL); + /*time_t timestamp = time(NULL); struct tm timedata; localtime_r(×tamp, &timedata); @@ -143,19 +143,29 @@ void ByteIn(u8 val) Output[3] = BCD(timedata.tm_wday); Output[4] = BCD(timedata.tm_hour); Output[5] = BCD(timedata.tm_min); - Output[6] = BCD(timedata.tm_sec); + Output[6] = BCD(timedata.tm_sec);*/ + Output[0] = BCD(22); + Output[1] = BCD(11); + Output[2] = BCD(3); + Output[3] = BCD(3); + Output[4] = BCD(6); + Output[5] = BCD(0); + Output[6] = BCD(0); } break; case 0x60: { - time_t timestamp = time(NULL); + /*time_t timestamp = time(NULL); struct tm timedata; localtime_r(×tamp, &timedata); Output[0] = BCD(timedata.tm_hour); Output[1] = BCD(timedata.tm_min); - Output[2] = BCD(timedata.tm_sec); + Output[2] = BCD(timedata.tm_sec);*/ + Output[0] = BCD(6); + Output[1] = BCD(0); + Output[2] = BCD(0); } break; From db6fea4a258274369d0f16ba7fa5d64acd265a9b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 10 Jan 2023 20:02:25 +0100 Subject: [PATCH 02/81] lay base for IPC work --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/IPC.cpp | 88 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/IPC.h | 34 ++++++++++++ src/frontend/qt_sdl/Platform.cpp | 67 ++--------------------- 4 files changed, 128 insertions(+), 62 deletions(-) create mode 100644 src/frontend/qt_sdl/IPC.cpp create mode 100644 src/frontend/qt_sdl/IPC.h diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 24261030..c3e86dcb 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES_QT_SDL RAMInfoDialog.cpp TitleManagerDialog.cpp Input.cpp + IPC.cpp LAN_PCap.cpp LAN_Socket.cpp LocalMP.cpp diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp new file mode 100644 index 00000000..c8edf511 --- /dev/null +++ b/src/frontend/qt_sdl/IPC.cpp @@ -0,0 +1,88 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include + +#include "IPC.h" + + +namespace IPC +{ + +QSharedMemory* Buffer = nullptr; +int InstanceID; + + +void Init() +{ + InstanceID = 0; + + Buffer = new QSharedMemory("melonIPC"); + + if (!Buffer->attach()) + { + printf("IPC sharedmem doesn't exist. creating\n"); + if (!Buffer->create(1024)) + { + printf("IPC sharedmem create failed :(\n"); + delete Buffer; + Buffer = nullptr; + return; + } + + Buffer->lock(); + memset(Buffer->data(), 0, Buffer->size()); + Buffer->unlock(); + } + + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + u16 mask = *(u16*)&data[0]; + for (int i = 0; i < 16; i++) + { + if (!(mask & (1<unlock(); + + printf("IPC: instance ID %d\n", InstanceID); +} + +void DeInit() +{ + if (Buffer) + { + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + *(u16*)&data[0] &= ~(1<unlock(); + + Buffer->detach(); + delete Buffer; + } + Buffer = nullptr; +} + +} diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h new file mode 100644 index 00000000..bd71fd70 --- /dev/null +++ b/src/frontend/qt_sdl/IPC.h @@ -0,0 +1,34 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef IPC_H +#define IPC_H + +#include "types.h" + +namespace IPC +{ + +extern int InstanceID; + +void Init(); +void DeInit(); + +} + +#endif // IPC_H diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 5263377e..c68a817a 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -38,6 +38,7 @@ #include "LAN_Socket.h" #include "LAN_PCap.h" #include "LocalMP.h" +#include "IPC.h" #include "OSD.h" #ifdef __WIN32__ @@ -55,64 +56,6 @@ void emuStop(); namespace Platform { -QSharedMemory* IPCBuffer = nullptr; -int IPCInstanceID; - -void IPCInit() -{ - IPCInstanceID = 0; - - IPCBuffer = new QSharedMemory("melonIPC"); - - if (!IPCBuffer->attach()) - { - Log(LogLevel::Info, "IPC sharedmem doesn't exist. creating\n"); - if (!IPCBuffer->create(1024)) - { - Log(LogLevel::Error, "IPC sharedmem create failed :(\n"); - delete IPCBuffer; - IPCBuffer = nullptr; - return; - } - - IPCBuffer->lock(); - memset(IPCBuffer->data(), 0, IPCBuffer->size()); - IPCBuffer->unlock(); - } - - IPCBuffer->lock(); - u8* data = (u8*)IPCBuffer->data(); - u16 mask = *(u16*)&data[0]; - for (int i = 0; i < 16; i++) - { - if (!(mask & (1<unlock(); - - Log(LogLevel::Info, "IPC: instance ID %d\n", IPCInstanceID); -} - -void IPCDeInit() -{ - if (IPCBuffer) - { - IPCBuffer->lock(); - u8* data = (u8*)IPCBuffer->data(); - *(u16*)&data[0] &= ~(1<unlock(); - - IPCBuffer->detach(); - delete IPCBuffer; - } - IPCBuffer = nullptr; -} - - void Init(int argc, char** argv) { #if defined(__WIN32__) || defined(PORTABLE) @@ -147,12 +90,12 @@ void Init(int argc, char** argv) EmuDirectory = confdir.toStdString(); #endif - IPCInit(); + IPC::Init(); } void DeInit() { - IPCDeInit(); + IPC::DeInit(); } void SignalStop(StopReason reason) @@ -178,12 +121,12 @@ void SignalStop(StopReason reason) int InstanceID() { - return IPCInstanceID; + return IPC::InstanceID; } std::string InstanceFileSuffix() { - int inst = IPCInstanceID; + int inst = IPC::InstanceID; if (inst == 0) return ""; char suffix[16] = {0}; From 5aa378d316760d84841fb4c09e6d8251db666c7b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 15 Feb 2023 16:56:18 +0100 Subject: [PATCH 03/81] here have more shitty code --- src/frontend/qt_sdl/IPC.cpp | 89 +++++++++++++++++++++++++++++++-- src/frontend/qt_sdl/LocalMP.cpp | 9 ---- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index c8edf511..02db7194 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -30,6 +30,29 @@ namespace IPC QSharedMemory* Buffer = nullptr; int InstanceID; +struct BufferHeader +{ + u16 NumInstances; + u16 InstanceBitmask; // bitmask of all instances present + u32 CommandWriteOffset; +}; + +struct CommandHeader +{ + u32 Magic; + u16 SenderID; + u16 Recipients; + u16 Command; + u16 Length; +}; + +u32 CommandReadOffset; + +const u32 kBufferSize = 0x4000; +const u32 kMaxCommandSize = 0x800; +const u32 kCommandStart = sizeof(BufferHeader); +const u32 kCommandEnd = kBufferSize; + void Init() { @@ -40,7 +63,7 @@ void Init() if (!Buffer->attach()) { printf("IPC sharedmem doesn't exist. creating\n"); - if (!Buffer->create(1024)) + if (!Buffer->create(kBufferSize)) { printf("IPC sharedmem create failed :(\n"); delete Buffer; @@ -55,13 +78,15 @@ void Init() Buffer->lock(); u8* data = (u8*)Buffer->data(); - u16 mask = *(u16*)&data[0]; + BufferHeader* header = (BufferHeader*)&data[0]; + u16 mask = header->InstanceBitmask; for (int i = 0; i < 16; i++) { if (!(mask & (1<InstanceBitmask |= (1<NumInstances++; break; } } @@ -76,7 +101,9 @@ void DeInit() { Buffer->lock(); u8* data = (u8*)Buffer->data(); - *(u16*)&data[0] &= ~(1<InstanceBitmask &= ~(1<NumInstances--; Buffer->unlock(); Buffer->detach(); @@ -85,4 +112,58 @@ void DeInit() Buffer = nullptr; } + +void FIFORead(void* buf, int len) +{ + u8* data = (u8*)Buffer->data(); + + u32 offset, start, end; + + offset = CommandReadOffset; + start = kCommandStart; + end = kCommandEnd; + + if ((offset + len) >= end) + { + u32 part1 = end - offset; + memcpy(buf, &data[offset], part1); + memcpy(&((u8*)buf)[part1], &data[start], len - part1); + offset = start + len - part1; + } + else + { + memcpy(buf, &data[offset], len); + offset += len; + } + + CommandReadOffset = offset; +} + +void FIFOWrite(void* buf, int len) +{ + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + + u32 offset, start, end; + + offset = header->CommandWriteOffset; + start = kCommandStart; + end = kCommandEnd; + + if ((offset + len) >= end) + { + u32 part1 = end - offset; + memcpy(&data[offset], buf, part1); + memcpy(&data[start], &((u8*)buf)[part1], len - part1); + offset = start + len - part1; + } + else + { + memcpy(&data[offset], buf, len); + offset += len; + } + + header->CommandWriteOffset = offset; +} + } diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index 31801b7f..291cb11a 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -67,15 +67,6 @@ struct MPPacketHeader u64 Timestamp; }; -struct MPSync -{ - u32 Magic; - u32 SenderID; - u16 ClientMask; - u16 Type; - u64 Timestamp; -}; - QSharedMemory* MPQueue; int InstanceID; u32 PacketReadOffset; From ea951d092e29910348becf236a6e0c5c4d581624 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 15 Feb 2023 18:24:24 +0100 Subject: [PATCH 04/81] attempt at syncing pause command between instances. works somewhat --- src/frontend/qt_sdl/IPC.cpp | 98 +++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/IPC.h | 9 ++++ src/frontend/qt_sdl/Input.cpp | 11 +++- src/frontend/qt_sdl/Input.h | 2 + src/frontend/qt_sdl/main.cpp | 6 +++ 5 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 02db7194..05c7f82e 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -22,6 +22,14 @@ #include #include "IPC.h" +#include "Config.h" +//#include "Input.h" + + +namespace Input +{ +void ExtHotkeyPress(int id); +} namespace IPC @@ -79,6 +87,15 @@ void Init() Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; + + if (header->NumInstances == 0) + { + // initialize the FIFO + header->CommandWriteOffset = kCommandStart; + } + + CommandReadOffset = header->CommandWriteOffset; + u16 mask = header->InstanceBitmask; for (int i = 0; i < 16; i++) { @@ -166,4 +183,85 @@ void FIFOWrite(void* buf, int len) header->CommandWriteOffset = offset; } +void Process() +{ + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + + // check if we got new commands + while (CommandReadOffset != header->CommandWriteOffset) + { + CommandHeader cmdheader; + u8 cmddata[kMaxCommandSize]; + + FIFORead(&cmdheader, sizeof(cmdheader)); + + if ((cmdheader.Magic != 0x4D434C4D) || (cmdheader.Length > kMaxCommandSize)) + { + printf("IPC: !!! COMMAND BUFFER IS FUCKED. RESETTING\n"); + CommandReadOffset = header->CommandWriteOffset; + Buffer->unlock(); + return; + } + + if (cmdheader.Length) + FIFORead(cmddata, cmdheader.Length); + + if (!(cmdheader.Recipients & (1<unlock(); +} + +bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) +{ + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + + recipients &= header->InstanceBitmask; + recipients &= ~(1<unlock(); + return false; + } + + if (len && cmddata==nullptr) + { + printf("IPC: ????? sending command with NULL buffer\n"); + Buffer->unlock(); + return false; + } + if (len > kMaxCommandSize) + { + printf("IPC: command too long\n"); + Buffer->unlock(); + return false; + } + + CommandHeader cmdheader; + cmdheader.Magic = 0x4D434C4D; + cmdheader.SenderID = InstanceID; + cmdheader.Recipients = recipients; + cmdheader.Command = command; + cmdheader.Length = len; + FIFOWrite(&cmdheader, sizeof(cmdheader)); + if (len) + FIFOWrite(cmddata, len); + + Buffer->unlock(); + return true; +} + } diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index bd71fd70..80546ec1 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -24,11 +24,20 @@ namespace IPC { +enum +{ + Cmd_Pause = 1, +}; + extern int InstanceID; void Init(); void DeInit(); +void Process(); + +bool SendCommand(u16 recipients, u16 command, u16 len, void* data); + } #endif // IPC_H diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index c1ef87c9..e0701087 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -30,7 +30,7 @@ int JoystickID; SDL_Joystick* Joystick = nullptr; u32 KeyInputMask, JoyInputMask; -u32 KeyHotkeyMask, JoyHotkeyMask; +u32 KeyHotkeyMask, JoyHotkeyMask, ExtHotkeyMask; u32 HotkeyMask, LastHotkeyMask; u32 HotkeyPress, HotkeyRelease; @@ -45,6 +45,7 @@ void Init() KeyHotkeyMask = 0; JoyHotkeyMask = 0; + ExtHotkeyMask = 0; HotkeyMask = 0; LastHotkeyMask = 0; } @@ -184,6 +185,11 @@ bool JoystickButtonDown(int val) return false; } +void ExtHotkeyPress(int id) +{ + ExtHotkeyMask |= (1< Date: Sat, 11 Mar 2023 13:46:29 +0100 Subject: [PATCH 05/81] make cross-instance pause work without being a shitshow --- src/frontend/qt_sdl/IPC.cpp | 50 +++++++++++++++++++++++++----------- src/frontend/qt_sdl/IPC.h | 3 +++ src/frontend/qt_sdl/main.cpp | 28 +++++++++++++++++++- src/frontend/qt_sdl/main.h | 3 +++ 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 05c7f82e..0ceb7b2c 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -61,6 +61,8 @@ const u32 kMaxCommandSize = 0x800; const u32 kCommandStart = sizeof(BufferHeader); const u32 kCommandEnd = kBufferSize; +bool CmdRecvFlags[Cmd_MAX]; + void Init() { @@ -109,6 +111,8 @@ void Init() } Buffer->unlock(); + memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); + printf("IPC: instance ID %d\n", InstanceID); } @@ -185,6 +189,8 @@ void FIFOWrite(void* buf, int len) void Process() { + memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); + Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; @@ -211,13 +217,18 @@ void Process() if (!(cmdheader.Recipients & (1<= Cmd_MAX) + continue; + // handle this command - switch (cmdheader.Command) + /*switch (cmdheader.Command) { case Cmd_Pause: Input::ExtHotkeyPress(HK_Pause); break; - } + }*/ + CmdRecvFlags[cmdheader.Command] = true; + // TODO: store the command data, for future commands that will need it } Buffer->unlock(); @@ -225,6 +236,22 @@ void Process() bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) { + if (command >= Cmd_MAX) + { + printf("IPC: invalid command %d\n", command); + return false; + } + if (len && cmddata==nullptr) + { + printf("IPC: ????? sending command with NULL buffer\n"); + return false; + } + if (len > kMaxCommandSize) + { + printf("IPC: command too long\n"); + return false; + } + Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; @@ -237,19 +264,6 @@ bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) return false; } - if (len && cmddata==nullptr) - { - printf("IPC: ????? sending command with NULL buffer\n"); - Buffer->unlock(); - return false; - } - if (len > kMaxCommandSize) - { - printf("IPC: command too long\n"); - Buffer->unlock(); - return false; - } - CommandHeader cmdheader; cmdheader.Magic = 0x4D434C4D; cmdheader.SenderID = InstanceID; @@ -264,4 +278,10 @@ bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) return true; } +bool CommandReceived(u16 command) +{ + if (command >= Cmd_MAX) return false; + return CmdRecvFlags[command]; +} + } diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index 80546ec1..5016836d 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -27,6 +27,8 @@ namespace IPC enum { Cmd_Pause = 1, + + Cmd_MAX }; extern int InstanceID; @@ -37,6 +39,7 @@ void DeInit(); void Process(); bool SendCommand(u16 recipients, u16 command, u16 len, void* data); +bool CommandReceived(u16 command); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 8436068d..10d459c6 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -198,6 +198,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); + connect(this, SIGNAL(windowIPCPause()), mainWindow, SLOT(onIPCPause())); + static_cast(mainWindow->panel)->transferLayout(this); } @@ -359,6 +361,8 @@ void EmuThread::run() { IPC::Process(); + if (IPC::CommandReceived(IPC::Cmd_Pause)) emit windowIPCPause(); + Input::Process(); if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); @@ -2707,10 +2711,32 @@ void MainWindow::onPause(bool checked) pausedManually = false; } - if (Platform::InstanceID()==0) // HAX IPC::SendCommand(0xFFFF, IPC::Cmd_Pause, 0, nullptr); } +void MainWindow::onIPCPause() +{ + // for IPC, using the normal way to trigger a pause (actPause->trigger()) + // isn't viable, because it would lead to broadcasting more IPC 'pause' messages + // so we have to replicate it this way + + actPause->toggle(); // changes visual state, without triggering onPause() + bool checked = actPause->isChecked(); + + if (checked) + { + emuThread->emuPause(); + OSD::AddMessage(0, "Paused"); + pausedManually = true; + } + else + { + emuThread->emuUnpause(); + OSD::AddMessage(0, "Resumed"); + pausedManually = false; + } +} + void MainWindow::onReset() { if (!RunningSomething) return; diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 073a4da0..f1adaeec 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -78,6 +78,8 @@ signals: void windowEmuReset(); void windowEmuFrameStep(); + void windowIPCPause(); + void windowLimitFPSChange(); void screenLayoutChange(); @@ -296,6 +298,7 @@ private slots: void onQuit(); void onPause(bool checked); + void onIPCPause(); void onReset(); void onStop(); void onFrameStep(); From 6fed0f8d7565dc4a7aa3b8de28f5d886103f03b8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 21:16:37 +0100 Subject: [PATCH 06/81] integrate local MP comm within the IPC module --- src/frontend/qt_sdl/IPC.cpp | 547 +++++++++++++++++++++++++++++-- src/frontend/qt_sdl/IPC.h | 17 +- src/frontend/qt_sdl/Platform.cpp | 37 ++- src/frontend/qt_sdl/main.cpp | 12 +- 4 files changed, 571 insertions(+), 42 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 0ceb7b2c..d216bc5e 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -19,6 +19,18 @@ #include #include #include + +#ifdef __WIN32__ + #include +#else + #include + #include + #include + #ifdef __APPLE__ + #include "sem_timedwait.h" + #endif +#endif + #include #include "IPC.h" @@ -40,9 +52,14 @@ int InstanceID; struct BufferHeader { - u16 NumInstances; + u16 NumInstances; // total number of instances present u16 InstanceBitmask; // bitmask of all instances present + u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive MP packets u32 CommandWriteOffset; + u32 MPPacketWriteOffset; + u32 MPReplyWriteOffset; + u16 MPHostInstanceID; // instance ID from which the last CMD frame was sent + u16 MPReplyBitmask; // bitmask of which clients replied in time }; struct CommandHeader @@ -54,17 +71,179 @@ struct CommandHeader u16 Length; }; -u32 CommandReadOffset; +struct MPPacketHeader +{ + u32 Magic; + u32 SenderID; + u32 Type; // 0=regular 1=CMD 2=reply 3=ack + u32 Length; + u64 Timestamp; +}; -const u32 kBufferSize = 0x4000; +u32 CommandReadOffset; +u32 MPPacketReadOffset; +u32 MPReplyReadOffset; + +const u32 kBufferSize = 0x30000; const u32 kMaxCommandSize = 0x800; +const u32 kMaxFrameSize = 0x800; const u32 kCommandStart = sizeof(BufferHeader); -const u32 kCommandEnd = kBufferSize; +const u32 kCommandEnd = (kBufferSize / 3); +const u32 kMPPacketStart = kCommandEnd; +const u32 kMPPacketEnd = (2 * (kBufferSize / 3)); +const u32 kMPReplyStart = kMPPacketEnd; +const u32 kMPReplyEnd = kBufferSize; bool CmdRecvFlags[Cmd_MAX]; +int MPRecvTimeout; +int MPLastHostID; -void Init() + +// we need to come up with our own abstraction layer for named semaphores +// because QSystemSemaphore doesn't support waiting with a timeout +// and, as such, is unsuitable to our needs + +#ifdef __WIN32__ + +bool SemInited[32]; +HANDLE SemPool[32]; + +void SemPoolInit() +{ + for (int i = 0; i < 32; i++) + { + SemPool[i] = INVALID_HANDLE_VALUE; + SemInited[i] = false; + } +} + +void SemDeinit(int num); + +void SemPoolDeinit() +{ + for (int i = 0; i < 32; i++) + SemDeinit(i); +} + +bool SemInit(int num) +{ + if (SemInited[num]) + return true; + + char semname[64]; + sprintf(semname, "Local\\melonNIFI_Sem%02d", num); + + HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname); + SemPool[num] = sem; + SemInited[num] = true; + return sem != INVALID_HANDLE_VALUE; +} + +void SemDeinit(int num) +{ + if (SemPool[num] != INVALID_HANDLE_VALUE) + { + CloseHandle(SemPool[num]); + SemPool[num] = INVALID_HANDLE_VALUE; + } + + SemInited[num] = false; +} + +bool SemPost(int num) +{ + SemInit(num); + return ReleaseSemaphore(SemPool[num], 1, nullptr) != 0; +} + +bool SemWait(int num, int timeout) +{ + return WaitForSingleObject(SemPool[num], timeout) == WAIT_OBJECT_0; +} + +void SemReset(int num) +{ + while (WaitForSingleObject(SemPool[num], 0) == WAIT_OBJECT_0); +} + +#else + +bool SemInited[32]; +sem_t* SemPool[32]; + +void SemPoolInit() +{ + for (int i = 0; i < 32; i++) + { + SemPool[i] = SEM_FAILED; + SemInited[i] = false; + } +} + +void SemDeinit(int num); + +void SemPoolDeinit() +{ + for (int i = 0; i < 32; i++) + SemDeinit(i); +} + +bool SemInit(int num) +{ + if (SemInited[num]) + return true; + + char semname[64]; + sprintf(semname, "/melonNIFI_Sem%02d", num); + + sem_t* sem = sem_open(semname, O_CREAT, 0644, 0); + SemPool[num] = sem; + SemInited[num] = true; + return sem != SEM_FAILED; +} + +void SemDeinit(int num) +{ + if (SemPool[num] != SEM_FAILED) + { + sem_close(SemPool[num]); + SemPool[num] = SEM_FAILED; + } + + SemInited[num] = false; +} + +bool SemPost(int num) +{ + SemInit(num); + return sem_post(SemPool[num]) == 0; +} + +bool SemWait(int num, int timeout) +{ + if (!timeout) + return sem_trywait(SemPool[num]) == 0; + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += timeout * 1000000; + long sec = ts.tv_nsec / 1000000000; + ts.tv_nsec -= sec * 1000000000; + ts.tv_sec += sec; + + return sem_timedwait(SemPool[num], &ts) == 0; +} + +void SemReset(int num) +{ + while (sem_trywait(SemPool[num]) == 0); +} + +#endif + + +bool Init() { InstanceID = 0; @@ -78,7 +257,7 @@ void Init() printf("IPC sharedmem create failed :(\n"); delete Buffer; Buffer = nullptr; - return; + return false; } Buffer->lock(); @@ -92,11 +271,15 @@ void Init() if (header->NumInstances == 0) { - // initialize the FIFO + // initialize the FIFOs header->CommandWriteOffset = kCommandStart; + header->MPPacketWriteOffset = kMPPacketStart; + header->MPReplyWriteOffset = kMPReplyStart; } CommandReadOffset = header->CommandWriteOffset; + MPPacketReadOffset = header->MPPacketWriteOffset; + MPReplyReadOffset = header->MPReplyWriteOffset; u16 mask = header->InstanceBitmask; for (int i = 0; i < 16; i++) @@ -113,7 +296,22 @@ void Init() memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); - printf("IPC: instance ID %d\n", InstanceID); + MPLastHostID = -1; + MPRecvTimeout = 25; + + printf("IPC: init OK, instance ID %d\n", InstanceID); + return true; +} + +bool InitSema() +{ + // prepare semaphores + // semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame + // semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply + + SemPoolInit(); + if (!SemInit(InstanceID)) return false; + if (!SemInit(16+InstanceID)) return false; } void DeInit() @@ -123,6 +321,7 @@ void DeInit() Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; + header->ConnectedBitmask &= ~(1 << InstanceID); header->InstanceBitmask &= ~(1<NumInstances--; Buffer->unlock(); @@ -133,16 +332,63 @@ void DeInit() Buffer = nullptr; } +void DeInitSema() +{ + SemPoolDeinit(); +} -void FIFORead(void* buf, int len) + +void SetMPRecvTimeout(int timeout) +{ + MPRecvTimeout = timeout; +} + +void MPBegin() +{ + Buffer->lock(); + BufferHeader* header = (BufferHeader*)Buffer->data(); + MPPacketReadOffset = header->MPPacketWriteOffset; + MPReplyReadOffset = header->MPReplyWriteOffset; + SemReset(InstanceID); + SemReset(16+InstanceID); + header->ConnectedBitmask |= (1 << InstanceID); + Buffer->unlock(); +} + +void MPEnd() +{ + Buffer->lock(); + BufferHeader* header = (BufferHeader*)Buffer->data(); + //SemReset(InstanceID); + //SemReset(16+InstanceID); + header->ConnectedBitmask &= ~(1 << InstanceID); + Buffer->unlock(); +} + + +template void FIFORead(void* buf, int len) { u8* data = (u8*)Buffer->data(); u32 offset, start, end; - - offset = CommandReadOffset; - start = kCommandStart; - end = kCommandEnd; + if (fifo == 0) + { + offset = CommandReadOffset; + start = kCommandStart; + end = kCommandEnd; + } + else if (fifo == 1) + { + offset = MPPacketReadOffset; + start = kMPPacketStart; + end = kMPPacketEnd; + } + else if (fifo == 2) + { + offset = MPReplyReadOffset; + start = kMPReplyStart; + end = kMPReplyEnd; + } if ((offset + len) >= end) { @@ -157,19 +403,35 @@ void FIFORead(void* buf, int len) offset += len; } - CommandReadOffset = offset; + if (fifo == 0) CommandReadOffset = offset; + else if (fifo == 1) MPPacketReadOffset = offset; + else if (fifo == 2) MPReplyReadOffset = offset; } -void FIFOWrite(void* buf, int len) +template void FIFOWrite(void* buf, int len) { u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; u32 offset, start, end; - - offset = header->CommandWriteOffset; - start = kCommandStart; - end = kCommandEnd; + if (fifo == 0) + { + offset = header->CommandWriteOffset; + start = kCommandStart; + end = kCommandEnd; + } + else if (fifo == 1) + { + offset = header->MPPacketWriteOffset; + start = kMPPacketStart; + end = kMPPacketEnd; + } + else if (fifo == 2) + { + offset = header->MPReplyWriteOffset; + start = kMPReplyStart; + end = kMPReplyEnd; + } if ((offset + len) >= end) { @@ -184,10 +446,13 @@ void FIFOWrite(void* buf, int len) offset += len; } - header->CommandWriteOffset = offset; + if (fifo == 0) header->CommandWriteOffset = offset; + else if (fifo == 1) header->MPPacketWriteOffset = offset; + else if (fifo == 2) header->MPReplyWriteOffset = offset; } -void Process() + +void ProcessCommands() { memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); @@ -201,7 +466,7 @@ void Process() CommandHeader cmdheader; u8 cmddata[kMaxCommandSize]; - FIFORead(&cmdheader, sizeof(cmdheader)); + FIFORead<0>(&cmdheader, sizeof(cmdheader)); if ((cmdheader.Magic != 0x4D434C4D) || (cmdheader.Length > kMaxCommandSize)) { @@ -212,7 +477,7 @@ void Process() } if (cmdheader.Length) - FIFORead(cmddata, cmdheader.Length); + FIFORead<0>(cmddata, cmdheader.Length); if (!(cmdheader.Recipients & (1<unlock(); @@ -270,9 +536,9 @@ bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) cmdheader.Recipients = recipients; cmdheader.Command = command; cmdheader.Length = len; - FIFOWrite(&cmdheader, sizeof(cmdheader)); + FIFOWrite<0>(&cmdheader, sizeof(cmdheader)); if (len) - FIFOWrite(cmddata, len); + FIFOWrite<0>(cmddata, len); Buffer->unlock(); return true; @@ -284,4 +550,235 @@ bool CommandReceived(u16 command) return CmdRecvFlags[command]; } + +int SendMPPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) +{ + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + + u16 mask = header->ConnectedBitmask; + + // TODO: check if the FIFO is full! + + MPPacketHeader pktheader; + pktheader.Magic = 0x4946494E; + pktheader.SenderID = InstanceID; + pktheader.Type = type; + pktheader.Length = len; + pktheader.Timestamp = timestamp; + + type &= 0xFFFF; + + if (type != 2) + { + FIFOWrite<1>(&pktheader, sizeof(pktheader)); + if (len) FIFOWrite<1>(packet, len); + } + + if (type == 1) + { + // NOTE: this is not guarded against, say, multiple multiplay games happening on the same machine + // we would need to pass the packet's SenderID through the wifi module for that + header->MPHostInstanceID = InstanceID; + header->MPReplyBitmask = 0; + MPReplyReadOffset = header->MPReplyWriteOffset; + SemReset(16 + InstanceID); + } + else if (type == 2) + { + FIFOWrite<2>(&pktheader, sizeof(pktheader)); + if (len) FIFOWrite<2>(packet, len); + + header->MPReplyBitmask |= (1 << InstanceID); + } + + Buffer->unlock(); + + if (type == 2) + { + SemPost(16 + header->MPHostInstanceID); + } + else + { + for (int i = 0; i < 16; i++) + { + if (mask & (1<lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + + MPPacketHeader pktheader; + FIFORead<1>(&pktheader, sizeof(pktheader)); + + if (pktheader.Magic != 0x4946494E) + { + printf("PACKET FIFO OVERFLOW\n"); + MPPacketReadOffset = header->MPPacketWriteOffset; + SemReset(InstanceID); + Buffer->unlock(); + return 0; + } + + if (pktheader.SenderID == InstanceID) + { + // skip this packet + MPPacketReadOffset += pktheader.Length; + if (MPPacketReadOffset >= kMPPacketEnd) + MPPacketReadOffset += kMPPacketStart - kMPPacketEnd; + + Buffer->unlock(); + continue; + } + + if (pktheader.Length) + { + FIFORead<1>(packet, pktheader.Length); + + if (pktheader.Type == 1) + MPLastHostID = pktheader.SenderID; + } + + if (timestamp) *timestamp = pktheader.Timestamp; + Buffer->unlock(); + return pktheader.Length; + } +} + +int SendMPPacket(u8* packet, int len, u64 timestamp) +{ + return SendMPPacketGeneric(0, packet, len, timestamp); +} + +int RecvMPPacket(u8* packet, u64* timestamp) +{ + return RecvMPPacketGeneric(packet, false, timestamp); +} + + +int SendMPCmd(u8* packet, int len, u64 timestamp) +{ + return SendMPPacketGeneric(1, packet, len, timestamp); +} + +int SendMPReply(u8* packet, int len, u64 timestamp, u16 aid) +{ + return SendMPPacketGeneric(2 | (aid<<16), packet, len, timestamp); +} + +int SendMPAck(u8* packet, int len, u64 timestamp) +{ + return SendMPPacketGeneric(3, packet, len, timestamp); +} + +int RecvMPHostPacket(u8* packet, u64* timestamp) +{ + if (MPLastHostID != -1) + { + // check if the host is still connected + + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + u16 curinstmask = header->ConnectedBitmask; + Buffer->unlock(); + + if (!(curinstmask & (1 << MPLastHostID))) + return -1; + } + + return RecvMPPacketGeneric(packet, true, timestamp); +} + +u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) +{ + u16 ret = 0; + u16 myinstmask = (1 << InstanceID); + u16 curinstmask; + + { + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + curinstmask = header->ConnectedBitmask; + Buffer->unlock(); + } + + // if all clients have left: return early + if ((myinstmask & curinstmask) == curinstmask) + return 0; + + for (;;) + { + if (!SemWait(16+InstanceID, MPRecvTimeout)) + { + // no more replies available + return ret; + } + + Buffer->lock(); + u8* data = (u8*)Buffer->data(); + BufferHeader* header = (BufferHeader*)&data[0]; + + MPPacketHeader pktheader; + FIFORead<2>(&pktheader, sizeof(pktheader)); + + if (pktheader.Magic != 0x4946494E) + { + printf("REPLY FIFO OVERFLOW\n"); + MPReplyReadOffset = header->MPReplyWriteOffset; + SemReset(16+InstanceID); + Buffer->unlock(); + return 0; + } + + if ((pktheader.SenderID == InstanceID) || // packet we sent out (shouldn't happen, but hey) + (pktheader.Timestamp < (timestamp - 32))) // stale packet + { + // skip this packet + MPReplyReadOffset += pktheader.Length; + if (MPReplyReadOffset >= kMPReplyEnd) + MPReplyReadOffset += kMPReplyStart - kMPReplyEnd; + + Buffer->unlock(); + continue; + } + + if (pktheader.Length) + { + u32 aid = (pktheader.Type >> 16); + FIFORead<2>(&packets[(aid-1)*1024], pktheader.Length); + ret |= (1 << aid); + } + + myinstmask |= (1 << pktheader.SenderID); + if (((myinstmask & curinstmask) == curinstmask) || + ((ret & aidmask) == aidmask)) + { + // all the clients have sent their reply + + Buffer->unlock(); + return ret; + } + + Buffer->unlock(); + } +} + } diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index 5016836d..18a1ed09 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -33,14 +33,27 @@ enum extern int InstanceID; -void Init(); +bool Init(); +bool InitSema(); void DeInit(); +void DeInitSema(); -void Process(); +void SetMPRecvTimeout(int timeout); +void MPBegin(); +void MPEnd(); +void ProcessCommands(); bool SendCommand(u16 recipients, u16 command, u16 len, void* data); bool CommandReceived(u16 command); +int SendMPPacket(u8* data, int len, u64 timestamp); +int RecvMPPacket(u8* data, u64* timestamp); +int SendMPCmd(u8* data, int len, u64 timestamp); +int SendMPReply(u8* data, int len, u64 timestamp, u16 aid); +int SendMPAck(u8* data, int len, u64 timestamp); +int RecvMPHostPacket(u8* data, u64* timestamp); +u16 RecvMPReplies(u8* data, u64 timestamp, u16 aidmask); + } #endif // IPC_H diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index c68a817a..d6993482 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -90,12 +90,13 @@ void Init(int argc, char** argv) EmuDirectory = confdir.toStdString(); #endif - IPC::Init(); + //IPC::Init(); + //IPC::SetMPRecvTimeout(Config::MPRecvTimeout); } void DeInit() { - IPC::DeInit(); + //IPC::DeInit(); } void SignalStop(StopReason reason) @@ -531,57 +532,67 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen bool MP_Init() { - return LocalMP::Init(); + //return LocalMP::Init(); + return true; } void MP_DeInit() { - return LocalMP::DeInit(); + //return LocalMP::DeInit(); } void MP_Begin() { - return LocalMP::Begin(); + //return LocalMP::Begin(); + return IPC::MPBegin(); } void MP_End() { - return LocalMP::End(); + //return LocalMP::End(); + return IPC::MPEnd(); } int MP_SendPacket(u8* data, int len, u64 timestamp) { - return LocalMP::SendPacket(data, len, timestamp); + //return LocalMP::SendPacket(data, len, timestamp); + return IPC::SendMPPacket(data, len, timestamp); } int MP_RecvPacket(u8* data, u64* timestamp) { - return LocalMP::RecvPacket(data, timestamp); + //return LocalMP::RecvPacket(data, timestamp); + return IPC::RecvMPPacket(data, timestamp); } int MP_SendCmd(u8* data, int len, u64 timestamp) { - return LocalMP::SendCmd(data, len, timestamp); + //return LocalMP::SendCmd(data, len, timestamp); + return IPC::SendMPCmd(data, len, timestamp); } int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid) { - return LocalMP::SendReply(data, len, timestamp, aid); + //return LocalMP::SendReply(data, len, timestamp, aid); + return IPC::SendMPReply(data, len, timestamp, aid); } int MP_SendAck(u8* data, int len, u64 timestamp) { - return LocalMP::SendAck(data, len, timestamp); + //return LocalMP::SendAck(data, len, timestamp); + return IPC::SendMPAck(data, len, timestamp); } int MP_RecvHostPacket(u8* data, u64* timestamp) { - return LocalMP::RecvHostPacket(data, timestamp); + //return LocalMP::RecvHostPacket(data, timestamp); + return IPC::RecvMPHostPacket(data, timestamp); } u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask) { - return LocalMP::RecvReplies(data, timestamp, aidmask); + //return LocalMP::RecvReplies(data, timestamp, aidmask); + return IPC::RecvMPReplies(data, timestamp, aidmask); } bool LAN_Init() diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 10d459c6..1295def0 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -317,6 +317,9 @@ void EmuThread::run() { u32 mainScreenPos[3]; + IPC::InitSema(); + IPC::SetMPRecvTimeout(Config::MPRecvTimeout); + NDS::Init(); mainScreenPos[0] = 0; @@ -359,7 +362,7 @@ void EmuThread::run() while (EmuRunning != emuStatus_Exit) { - IPC::Process(); + IPC::ProcessCommands(); if (IPC::CommandReceived(IPC::Cmd_Pause)) emit windowIPCPause(); @@ -658,6 +661,7 @@ void EmuThread::run() GPU::DeInitRenderer(); NDS::DeInit(); + IPC::DeInitSema(); //Platform::LAN_DeInit(); } @@ -2965,7 +2969,8 @@ void MainWindow::onOpenMPSettings() void MainWindow::onMPSettingsFinished(int res) { AudioInOut::AudioMute(mainWindow); - LocalMP::SetRecvTimeout(Config::MPRecvTimeout); + //LocalMP::SetRecvTimeout(Config::MPRecvTimeout); + IPC::SetMPRecvTimeout(Config::MPRecvTimeout); emuThread->emuUnpause(); } @@ -3272,6 +3277,8 @@ int main(int argc, char** argv) MelonApplication melon(argc, argv); + IPC::Init(); + CLI::CommandLineOptions* options = CLI::ManageArgs(melon); // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl @@ -3386,6 +3393,7 @@ int main(int argc, char** argv) SDL_Quit(); Platform::DeInit(); + IPC::DeInit(); return ret; } From dfeebb28c466703cc9445705cba9a39ef2ec08cd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 21:24:52 +0100 Subject: [PATCH 07/81] Arisotura pls clean up after yourself --- src/Platform.h | 2 - src/Wifi.cpp | 6 - src/frontend/qt_sdl/CMakeLists.txt | 1 - src/frontend/qt_sdl/LocalMP.cpp | 628 ----------------------------- src/frontend/qt_sdl/LocalMP.h | 45 --- src/frontend/qt_sdl/Platform.cpp | 21 - src/frontend/qt_sdl/main.cpp | 2 - 7 files changed, 705 deletions(-) delete mode 100644 src/frontend/qt_sdl/LocalMP.cpp delete mode 100644 src/frontend/qt_sdl/LocalMP.h diff --git a/src/Platform.h b/src/Platform.h index 91da4c1e..d816ab1f 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -336,8 +336,6 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen // local multiplayer comm interface // packet type: DS-style TX header (12 bytes) + original 802.11 frame -bool MP_Init(); -void MP_DeInit(); void MP_Begin(); void MP_End(); int MP_SendPacket(u8* data, int len, u64 timestamp); diff --git a/src/Wifi.cpp b/src/Wifi.cpp index fa05a651..e597bee6 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -150,12 +150,8 @@ u64 RXTimestamp; bool Init() { - //MPInited = false; //LANInited = false; - Platform::MP_Init(); - MPInited = true; - Platform::LAN_Init(); LANInited = true; @@ -166,8 +162,6 @@ bool Init() void DeInit() { - if (MPInited) - Platform::MP_DeInit(); if (LANInited) Platform::LAN_DeInit(); diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index c3e86dcb..9fd99e47 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -28,7 +28,6 @@ set(SOURCES_QT_SDL IPC.cpp LAN_PCap.cpp LAN_Socket.cpp - LocalMP.cpp OSD.cpp OSD_shaders.h font.h diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp deleted file mode 100644 index 291cb11a..00000000 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ /dev/null @@ -1,628 +0,0 @@ -/* - Copyright 2016-2022 melonDS team - - This file is part of melonDS. - - melonDS is free software: you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - melonDS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#include -#include -#include - -#ifdef __WIN32__ - #include -#else - #include - #include - #include - #ifdef __APPLE__ - #include "sem_timedwait.h" - #endif -#endif - -#include -#include - -#include "Config.h" -#include "LocalMP.h" -#include "Platform.h" - -using Platform::Log; -using Platform::LogLevel; - -namespace LocalMP -{ - -u32 MPUniqueID; -u8 PacketBuffer[2048]; - -struct MPQueueHeader -{ - u16 NumInstances; - u16 InstanceBitmask; // bitmask of all instances present - u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive packets - u32 PacketWriteOffset; - u32 ReplyWriteOffset; - u16 MPHostInstanceID; // instance ID from which the last CMD frame was sent - u16 MPReplyBitmask; // bitmask of which clients replied in time -}; - -struct MPPacketHeader -{ - u32 Magic; - u32 SenderID; - u32 Type; // 0=regular 1=CMD 2=reply 3=ack - u32 Length; - u64 Timestamp; -}; - -QSharedMemory* MPQueue; -int InstanceID; -u32 PacketReadOffset; -u32 ReplyReadOffset; - -const u32 kQueueSize = 0x20000; -const u32 kMaxFrameSize = 0x800; -const u32 kPacketStart = sizeof(MPQueueHeader); -const u32 kReplyStart = kQueueSize / 2; -const u32 kPacketEnd = kReplyStart; -const u32 kReplyEnd = kQueueSize; - -int RecvTimeout; - -int LastHostID; - - -// we need to come up with our own abstraction layer for named semaphores -// because QSystemSemaphore doesn't support waiting with a timeout -// and, as such, is unsuitable to our needs - -#ifdef __WIN32__ - -bool SemInited[32]; -HANDLE SemPool[32]; - -void SemPoolInit() -{ - for (int i = 0; i < 32; i++) - { - SemPool[i] = INVALID_HANDLE_VALUE; - SemInited[i] = false; - } -} - -void SemDeinit(int num); - -void SemPoolDeinit() -{ - for (int i = 0; i < 32; i++) - SemDeinit(i); -} - -bool SemInit(int num) -{ - if (SemInited[num]) - return true; - - char semname[64]; - sprintf(semname, "Local\\melonNIFI_Sem%02d", num); - - HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname); - SemPool[num] = sem; - SemInited[num] = true; - return sem != INVALID_HANDLE_VALUE; -} - -void SemDeinit(int num) -{ - if (SemPool[num] != INVALID_HANDLE_VALUE) - { - CloseHandle(SemPool[num]); - SemPool[num] = INVALID_HANDLE_VALUE; - } - - SemInited[num] = false; -} - -bool SemPost(int num) -{ - SemInit(num); - return ReleaseSemaphore(SemPool[num], 1, nullptr) != 0; -} - -bool SemWait(int num, int timeout) -{ - return WaitForSingleObject(SemPool[num], timeout) == WAIT_OBJECT_0; -} - -void SemReset(int num) -{ - while (WaitForSingleObject(SemPool[num], 0) == WAIT_OBJECT_0); -} - -#else - -bool SemInited[32]; -sem_t* SemPool[32]; - -void SemPoolInit() -{ - for (int i = 0; i < 32; i++) - { - SemPool[i] = SEM_FAILED; - SemInited[i] = false; - } -} - -void SemDeinit(int num); - -void SemPoolDeinit() -{ - for (int i = 0; i < 32; i++) - SemDeinit(i); -} - -bool SemInit(int num) -{ - if (SemInited[num]) - return true; - - char semname[64]; - sprintf(semname, "/melonNIFI_Sem%02d", num); - - sem_t* sem = sem_open(semname, O_CREAT, 0644, 0); - SemPool[num] = sem; - SemInited[num] = true; - return sem != SEM_FAILED; -} - -void SemDeinit(int num) -{ - if (SemPool[num] != SEM_FAILED) - { - sem_close(SemPool[num]); - SemPool[num] = SEM_FAILED; - } - - SemInited[num] = false; -} - -bool SemPost(int num) -{ - SemInit(num); - return sem_post(SemPool[num]) == 0; -} - -bool SemWait(int num, int timeout) -{ - if (!timeout) - return sem_trywait(SemPool[num]) == 0; - - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_nsec += timeout * 1000000; - long sec = ts.tv_nsec / 1000000000; - ts.tv_nsec -= sec * 1000000000; - ts.tv_sec += sec; - - return sem_timedwait(SemPool[num], &ts) == 0; -} - -void SemReset(int num) -{ - while (sem_trywait(SemPool[num]) == 0); -} - -#endif - - -bool Init() -{ - MPQueue = new QSharedMemory("melonNIFI"); - - if (!MPQueue->attach()) - { - Log(LogLevel::Info, "MP sharedmem doesn't exist. creating\n"); - if (!MPQueue->create(kQueueSize)) - { - Log(LogLevel::Error, "MP sharedmem create failed :(\n"); - return false; - } - - MPQueue->lock(); - memset(MPQueue->data(), 0, MPQueue->size()); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - header->PacketWriteOffset = kPacketStart; - header->ReplyWriteOffset = kReplyStart; - MPQueue->unlock(); - } - - MPQueue->lock(); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - - u16 mask = header->InstanceBitmask; - for (int i = 0; i < 16; i++) - { - if (!(mask & (1<InstanceBitmask |= (1<ConnectedBitmask |= (1 << i); - break; - } - } - header->NumInstances++; - - PacketReadOffset = header->PacketWriteOffset; - ReplyReadOffset = header->ReplyWriteOffset; - - MPQueue->unlock(); - - // prepare semaphores - // semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame - // semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply - - SemPoolInit(); - SemInit(InstanceID); - SemInit(16+InstanceID); - - LastHostID = -1; - - Log(LogLevel::Info, "MP comm init OK, instance ID %d\n", InstanceID); - - RecvTimeout = 25; - - return true; -} - -void DeInit() -{ - MPQueue->lock(); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - header->ConnectedBitmask &= ~(1 << InstanceID); - header->InstanceBitmask &= ~(1 << InstanceID); - header->NumInstances--; - MPQueue->unlock(); - - SemPoolDeinit(); - - MPQueue->detach(); - delete MPQueue; -} - -void SetRecvTimeout(int timeout) -{ - RecvTimeout = timeout; -} - -void Begin() -{ - MPQueue->lock(); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - PacketReadOffset = header->PacketWriteOffset; - ReplyReadOffset = header->ReplyWriteOffset; - SemReset(InstanceID); - SemReset(16+InstanceID); - header->ConnectedBitmask |= (1 << InstanceID); - MPQueue->unlock(); -} - -void End() -{ - MPQueue->lock(); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - //SemReset(InstanceID); - //SemReset(16+InstanceID); - header->ConnectedBitmask &= ~(1 << InstanceID); - MPQueue->unlock(); -} - -void FIFORead(int fifo, void* buf, int len) -{ - u8* data = (u8*)MPQueue->data(); - - u32 offset, start, end; - if (fifo == 0) - { - offset = PacketReadOffset; - start = kPacketStart; - end = kPacketEnd; - } - else - { - offset = ReplyReadOffset; - start = kReplyStart; - end = kReplyEnd; - } - - if ((offset + len) >= end) - { - u32 part1 = end - offset; - memcpy(buf, &data[offset], part1); - memcpy(&((u8*)buf)[part1], &data[start], len - part1); - offset = start + len - part1; - } - else - { - memcpy(buf, &data[offset], len); - offset += len; - } - - if (fifo == 0) PacketReadOffset = offset; - else ReplyReadOffset = offset; -} - -void FIFOWrite(int fifo, void* buf, int len) -{ - u8* data = (u8*)MPQueue->data(); - MPQueueHeader* header = (MPQueueHeader*)&data[0]; - - u32 offset, start, end; - if (fifo == 0) - { - offset = header->PacketWriteOffset; - start = kPacketStart; - end = kPacketEnd; - } - else - { - offset = header->ReplyWriteOffset; - start = kReplyStart; - end = kReplyEnd; - } - - if ((offset + len) >= end) - { - u32 part1 = end - offset; - memcpy(&data[offset], buf, part1); - memcpy(&data[start], &((u8*)buf)[part1], len - part1); - offset = start + len - part1; - } - else - { - memcpy(&data[offset], buf, len); - offset += len; - } - - if (fifo == 0) header->PacketWriteOffset = offset; - else header->ReplyWriteOffset = offset; -} - -int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) -{ - MPQueue->lock(); - u8* data = (u8*)MPQueue->data(); - MPQueueHeader* header = (MPQueueHeader*)&data[0]; - - u16 mask = header->ConnectedBitmask; - - // TODO: check if the FIFO is full! - - MPPacketHeader pktheader; - pktheader.Magic = 0x4946494E; - pktheader.SenderID = InstanceID; - pktheader.Type = type; - pktheader.Length = len; - pktheader.Timestamp = timestamp; - - type &= 0xFFFF; - int nfifo = (type == 2) ? 1 : 0; - FIFOWrite(nfifo, &pktheader, sizeof(pktheader)); - if (len) - FIFOWrite(nfifo, packet, len); - - if (type == 1) - { - // NOTE: this is not guarded against, say, multiple multiplay games happening on the same machine - // we would need to pass the packet's SenderID through the wifi module for that - header->MPHostInstanceID = InstanceID; - header->MPReplyBitmask = 0; - ReplyReadOffset = header->ReplyWriteOffset; - SemReset(16 + InstanceID); - } - else if (type == 2) - { - header->MPReplyBitmask |= (1 << InstanceID); - } - - MPQueue->unlock(); - - if (type == 2) - { - SemPost(16 + header->MPHostInstanceID); - } - else - { - for (int i = 0; i < 16; i++) - { - if (mask & (1<lock(); - u8* data = (u8*)MPQueue->data(); - MPQueueHeader* header = (MPQueueHeader*)&data[0]; - - MPPacketHeader pktheader; - FIFORead(0, &pktheader, sizeof(pktheader)); - - if (pktheader.Magic != 0x4946494E) - { - Log(LogLevel::Warn, "PACKET FIFO OVERFLOW\n"); - PacketReadOffset = header->PacketWriteOffset; - SemReset(InstanceID); - MPQueue->unlock(); - return 0; - } - - if (pktheader.SenderID == InstanceID) - { - // skip this packet - PacketReadOffset += pktheader.Length; - if (PacketReadOffset >= kPacketEnd) - PacketReadOffset += kPacketStart - kPacketEnd; - - MPQueue->unlock(); - continue; - } - - if (pktheader.Length) - { - FIFORead(0, packet, pktheader.Length); - - if (pktheader.Type == 1) - LastHostID = pktheader.SenderID; - } - - if (timestamp) *timestamp = pktheader.Timestamp; - MPQueue->unlock(); - return pktheader.Length; - } -} - -int SendPacket(u8* packet, int len, u64 timestamp) -{ - return SendPacketGeneric(0, packet, len, timestamp); -} - -int RecvPacket(u8* packet, u64* timestamp) -{ - return RecvPacketGeneric(packet, false, timestamp); -} - - -int SendCmd(u8* packet, int len, u64 timestamp) -{ - return SendPacketGeneric(1, packet, len, timestamp); -} - -int SendReply(u8* packet, int len, u64 timestamp, u16 aid) -{ - return SendPacketGeneric(2 | (aid<<16), packet, len, timestamp); -} - -int SendAck(u8* packet, int len, u64 timestamp) -{ - return SendPacketGeneric(3, packet, len, timestamp); -} - -int RecvHostPacket(u8* packet, u64* timestamp) -{ - if (LastHostID != -1) - { - // check if the host is still connected - - MPQueue->lock(); - u8* data = (u8*)MPQueue->data(); - MPQueueHeader* header = (MPQueueHeader*)&data[0]; - u16 curinstmask = header->ConnectedBitmask; - MPQueue->unlock(); - - if (!(curinstmask & (1 << LastHostID))) - return -1; - } - - return RecvPacketGeneric(packet, true, timestamp); -} - -u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask) -{ - u16 ret = 0; - u16 myinstmask = (1 << InstanceID); - u16 curinstmask; - - { - MPQueue->lock(); - u8* data = (u8*)MPQueue->data(); - MPQueueHeader* header = (MPQueueHeader*)&data[0]; - curinstmask = header->ConnectedBitmask; - MPQueue->unlock(); - } - - // if all clients have left: return early - if ((myinstmask & curinstmask) == curinstmask) - return 0; - - for (;;) - { - if (!SemWait(16+InstanceID, RecvTimeout)) - { - // no more replies available - return ret; - } - - MPQueue->lock(); - u8* data = (u8*)MPQueue->data(); - MPQueueHeader* header = (MPQueueHeader*)&data[0]; - - MPPacketHeader pktheader; - FIFORead(1, &pktheader, sizeof(pktheader)); - - if (pktheader.Magic != 0x4946494E) - { - Log(LogLevel::Warn, "REPLY FIFO OVERFLOW\n"); - ReplyReadOffset = header->ReplyWriteOffset; - SemReset(16+InstanceID); - MPQueue->unlock(); - return 0; - } - - if ((pktheader.SenderID == InstanceID) || // packet we sent out (shouldn't happen, but hey) - (pktheader.Timestamp < (timestamp - 32))) // stale packet - { - // skip this packet - ReplyReadOffset += pktheader.Length; - if (ReplyReadOffset >= kReplyEnd) - ReplyReadOffset += kReplyStart - kReplyEnd; - - MPQueue->unlock(); - continue; - } - - if (pktheader.Length) - { - u32 aid = (pktheader.Type >> 16); - FIFORead(1, &packets[(aid-1)*1024], pktheader.Length); - ret |= (1 << aid); - } - - myinstmask |= (1 << pktheader.SenderID); - if (((myinstmask & curinstmask) == curinstmask) || - ((ret & aidmask) == aidmask)) - { - // all the clients have sent their reply - - MPQueue->unlock(); - return ret; - } - - MPQueue->unlock(); - } -} - -} - diff --git a/src/frontend/qt_sdl/LocalMP.h b/src/frontend/qt_sdl/LocalMP.h deleted file mode 100644 index 51dfcb93..00000000 --- a/src/frontend/qt_sdl/LocalMP.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright 2016-2022 melonDS team - - This file is part of melonDS. - - melonDS is free software: you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - melonDS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#ifndef LOCALMP_H -#define LOCALMP_H - -#include "types.h" - -namespace LocalMP -{ - -bool Init(); -void DeInit(); - -void SetRecvTimeout(int timeout); - -void Begin(); -void End(); - -int SendPacket(u8* data, int len, u64 timestamp); -int RecvPacket(u8* data, u64* timestamp); -int SendCmd(u8* data, int len, u64 timestamp); -int SendReply(u8* data, int len, u64 timestamp, u16 aid); -int SendAck(u8* data, int len, u64 timestamp); -int RecvHostPacket(u8* data, u64* timestamp); -u16 RecvReplies(u8* data, u64 timestamp, u16 aidmask); - -} - -#endif // LOCALMP_H diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index d6993482..04df5438 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -37,7 +37,6 @@ #include "CameraManager.h" #include "LAN_Socket.h" #include "LAN_PCap.h" -#include "LocalMP.h" #include "IPC.h" #include "OSD.h" @@ -530,68 +529,48 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen -bool MP_Init() -{ - //return LocalMP::Init(); - return true; -} - -void MP_DeInit() -{ - //return LocalMP::DeInit(); -} - void MP_Begin() { - //return LocalMP::Begin(); return IPC::MPBegin(); } void MP_End() { - //return LocalMP::End(); return IPC::MPEnd(); } int MP_SendPacket(u8* data, int len, u64 timestamp) { - //return LocalMP::SendPacket(data, len, timestamp); return IPC::SendMPPacket(data, len, timestamp); } int MP_RecvPacket(u8* data, u64* timestamp) { - //return LocalMP::RecvPacket(data, timestamp); return IPC::RecvMPPacket(data, timestamp); } int MP_SendCmd(u8* data, int len, u64 timestamp) { - //return LocalMP::SendCmd(data, len, timestamp); return IPC::SendMPCmd(data, len, timestamp); } int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid) { - //return LocalMP::SendReply(data, len, timestamp, aid); return IPC::SendMPReply(data, len, timestamp, aid); } int MP_SendAck(u8* data, int len, u64 timestamp) { - //return LocalMP::SendAck(data, len, timestamp); return IPC::SendMPAck(data, len, timestamp); } int MP_RecvHostPacket(u8* data, u64* timestamp) { - //return LocalMP::RecvHostPacket(data, timestamp); return IPC::RecvMPHostPacket(data, timestamp); } u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask) { - //return LocalMP::RecvReplies(data, timestamp, aidmask); return IPC::RecvMPReplies(data, timestamp, aidmask); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 1295def0..82961132 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -88,7 +88,6 @@ #include "Wifi.h" #include "Platform.h" #include "IPC.h" -#include "LocalMP.h" #include "Config.h" #include "DSi_I2C.h" @@ -2969,7 +2968,6 @@ void MainWindow::onOpenMPSettings() void MainWindow::onMPSettingsFinished(int res) { AudioInOut::AudioMute(mainWindow); - //LocalMP::SetRecvTimeout(Config::MPRecvTimeout); IPC::SetMPRecvTimeout(Config::MPRecvTimeout); emuThread->emuUnpause(); From 6b385cab4e10ae09cb6378dd591550dbc6762aa7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 21:42:55 +0100 Subject: [PATCH 08/81] avoid spamming host-RX checks every 8us if the host isn't responding --- src/Wifi.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Wifi.cpp b/src/Wifi.cpp index e597bee6..1ca90940 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -112,6 +112,7 @@ bool IsMP; bool IsMPClient; u64 NextSync; // for clients: timestamp for next sync point u64 RXTimestamp; +u64 LastHostRXCheck; // multiplayer host TX sequence: // 1. preamble @@ -264,6 +265,7 @@ void Reset() IsMPClient = false; NextSync = 0; RXTimestamp = 0; + LastHostRXCheck = 0; WifiAP::Reset(); } @@ -340,6 +342,7 @@ void DoSavestate(Savestate* file) file->Bool32(&IsMPClient); file->Var64(&NextSync); file->Var64(&RXTimestamp); + file->Var64(&LastHostRXCheck); } @@ -1658,8 +1661,12 @@ void USTimer(u32 param) if (USTimestamp >= NextSync) { - // TODO: not do this every tick if it fails to receive a frame! - CheckRX(2); + u64 delay = USTimestamp - LastHostRXCheck; + if (delay >= 512) + { + CheckRX(2); + LastHostRXCheck = USTimestamp; + } } } From e14c06401e0ccd2041dba0b956f83bde5ec60fec Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 23:07:29 +0100 Subject: [PATCH 09/81] make cross-instance pause work without breaking local wifi connections --- src/frontend/qt_sdl/IPC.cpp | 83 ++++++++++++++++++++++-------------- src/frontend/qt_sdl/IPC.h | 4 +- src/frontend/qt_sdl/main.cpp | 21 ++++----- src/frontend/qt_sdl/main.h | 6 ++- 4 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index d216bc5e..fdc4683b 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -35,13 +35,10 @@ #include "IPC.h" #include "Config.h" -//#include "Input.h" +#include "main.h" -namespace Input -{ -void ExtHotkeyPress(int id); -} +extern EmuThread* emuThread; namespace IPC @@ -55,6 +52,7 @@ struct BufferHeader u16 NumInstances; // total number of instances present u16 InstanceBitmask; // bitmask of all instances present u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive MP packets + u16 ActiveBitmask; // bitmask of which instances are currently active (ie. not halted externally) u32 CommandWriteOffset; u32 MPPacketWriteOffset; u32 MPReplyWriteOffset; @@ -94,8 +92,6 @@ const u32 kMPPacketEnd = (2 * (kBufferSize / 3)); const u32 kMPReplyStart = kMPPacketEnd; const u32 kMPReplyEnd = kBufferSize; -bool CmdRecvFlags[Cmd_MAX]; - int MPRecvTimeout; int MPLastHostID; @@ -294,8 +290,6 @@ bool Init() } Buffer->unlock(); - memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); - MPLastHostID = -1; MPRecvTimeout = 25; @@ -312,6 +306,8 @@ bool InitSema() SemPoolInit(); if (!SemInit(InstanceID)) return false; if (!SemInit(16+InstanceID)) return false; + + return true; } void DeInit() @@ -322,7 +318,8 @@ void DeInit() u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; header->ConnectedBitmask &= ~(1 << InstanceID); - header->InstanceBitmask &= ~(1<ActiveBitmask &= ~(1 << InstanceID); + header->InstanceBitmask &= ~(1 << InstanceID); header->NumInstances--; Buffer->unlock(); @@ -365,6 +362,15 @@ void MPEnd() Buffer->unlock(); } +void SetActive(bool active) +{ + Buffer->lock(); + BufferHeader* header = (BufferHeader*)Buffer->data(); + if (active) header->ActiveBitmask |= (1 << InstanceID); + else header->ActiveBitmask &= ~(1 << InstanceID); + Buffer->unlock(); +} + template void FIFORead(void* buf, int len) { @@ -454,8 +460,6 @@ template void FIFOWrite(void* buf, int len) void ProcessCommands() { - memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); - Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; @@ -468,7 +472,9 @@ void ProcessCommands() FIFORead<0>(&cmdheader, sizeof(cmdheader)); - if ((cmdheader.Magic != 0x4D434C4D) || (cmdheader.Length > kMaxCommandSize)) + if ((cmdheader.Magic != 0x4D434C4D) || + (cmdheader.Length > kMaxCommandSize) || + (cmdheader.Command >= Cmd_MAX)) { printf("IPC: !!! COMMAND BUFFER IS FUCKED. RESETTING\n"); CommandReadOffset = header->CommandWriteOffset; @@ -476,25 +482,27 @@ void ProcessCommands() return; } + if (!(cmdheader.Recipients & (1<= kCommandEnd) + CommandReadOffset += kCommandStart - kCommandEnd; + + continue; + } + + // handle this command + if (cmdheader.Length) FIFORead<0>(cmddata, cmdheader.Length); - if (!(cmdheader.Recipients & (1<= Cmd_MAX) - continue; - - // handle this command - /*switch (cmdheader.Command) + switch (cmdheader.Command) { case Cmd_Pause: - Input::ExtHotkeyPress(HK_Pause); + emuThread->IPCPause(cmddata[0] != 0); break; - }*/ - CmdRecvFlags[cmdheader.Command] = true; - // TODO: store the command data, for future commands that will need it - // TODO: also what if, say, we get multiple pause commands before CommandReceived() is called? + } } Buffer->unlock(); @@ -544,10 +552,10 @@ bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) return true; } -bool CommandReceived(u16 command) +bool SendCommandU8(u16 recipients, u16 command, u8 arg) { - if (command >= Cmd_MAX) return false; - return CmdRecvFlags[command]; + u8 data = arg; + return SendCommand(recipients, command, 1, &data); } @@ -689,6 +697,8 @@ int SendMPAck(u8* packet, int len, u64 timestamp) int RecvMPHostPacket(u8* packet, u64* timestamp) { + bool block = true; + if (MPLastHostID != -1) { // check if the host is still connected @@ -697,26 +707,32 @@ int RecvMPHostPacket(u8* packet, u64* timestamp) u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; u16 curinstmask = header->ConnectedBitmask; + u16 actinstmask = header->ActiveBitmask; Buffer->unlock(); if (!(curinstmask & (1 << MPLastHostID))) return -1; + + if (!(actinstmask & (1 << MPLastHostID))) + block = false; } - return RecvMPPacketGeneric(packet, true, timestamp); + return RecvMPPacketGeneric(packet, block, timestamp); } u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) { u16 ret = 0; u16 myinstmask = (1 << InstanceID); - u16 curinstmask; + u16 curinstmask, actinstmask; + int timeout = MPRecvTimeout; { Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; curinstmask = header->ConnectedBitmask; + actinstmask = header->ActiveBitmask; Buffer->unlock(); } @@ -724,9 +740,12 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) if ((myinstmask & curinstmask) == curinstmask) return 0; + if ((myinstmask & actinstmask) == actinstmask) + timeout = 0; + for (;;) { - if (!SemWait(16+InstanceID, MPRecvTimeout)) + if (!SemWait(16+InstanceID, timeout)) { // no more replies available return ret; diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index 18a1ed09..eca43837 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -42,9 +42,11 @@ void SetMPRecvTimeout(int timeout); void MPBegin(); void MPEnd(); +void SetActive(bool active); + void ProcessCommands(); bool SendCommand(u16 recipients, u16 command, u16 len, void* data); -bool CommandReceived(u16 command); +bool SendCommandU8(u16 recipients, u16 command, u8 arg); int SendMPPacket(u8* data, int len, u64 timestamp); int RecvMPPacket(u8* data, u64* timestamp); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 82961132..9182adaf 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -197,7 +197,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); - connect(this, SIGNAL(windowIPCPause()), mainWindow, SLOT(onIPCPause())); + connect(this, SIGNAL(windowIPCPause(bool)), mainWindow, SLOT(onIPCPause(bool))); static_cast(mainWindow->panel)->transferLayout(this); } @@ -363,8 +363,6 @@ void EmuThread::run() { IPC::ProcessCommands(); - if (IPC::CommandReceived(IPC::Cmd_Pause)) emit windowIPCPause(); - Input::Process(); if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); @@ -678,6 +676,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); AudioInOut::Enable(); + IPC::SetActive(true); } void EmuThread::initContext() @@ -702,6 +701,7 @@ void EmuThread::emuPause() while (EmuStatus != emuStatus_Paused); AudioInOut::Disable(); + IPC::SetActive(false); } void EmuThread::emuUnpause() @@ -714,6 +714,7 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; AudioInOut::Enable(); + IPC::SetActive(true); } void EmuThread::emuStop() @@ -722,12 +723,13 @@ void EmuThread::emuStop() EmuPauseStack = EmuPauseStackRunning; AudioInOut::Disable(); + IPC::SetActive(false); } void EmuThread::emuFrameStep() { - if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause(); - EmuRunning = emuStatus_FrameStep; + if (EmuRunning != 3) IPC::SetActive(false); + EmuRunning = 3; } bool EmuThread::emuIsRunning() @@ -2714,19 +2716,18 @@ void MainWindow::onPause(bool checked) pausedManually = false; } - IPC::SendCommand(0xFFFF, IPC::Cmd_Pause, 0, nullptr); + IPC::SendCommandU8(0xFFFF, IPC::Cmd_Pause, (u8)checked); } -void MainWindow::onIPCPause() +void MainWindow::onIPCPause(bool pause) { // for IPC, using the normal way to trigger a pause (actPause->trigger()) // isn't viable, because it would lead to broadcasting more IPC 'pause' messages // so we have to replicate it this way - actPause->toggle(); // changes visual state, without triggering onPause() - bool checked = actPause->isChecked(); + actPause->setChecked(pause); // changes visual state, without triggering onPause() - if (checked) + if (pause) { emuThread->emuPause(); OSD::AddMessage(0, "Paused"); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index f1adaeec..107b5714 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -68,6 +68,8 @@ public: void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); + void IPCPause(bool pause) { emit windowIPCPause(pause); } + signals: void windowUpdate(); void windowTitleChange(QString title); @@ -78,7 +80,7 @@ signals: void windowEmuReset(); void windowEmuFrameStep(); - void windowIPCPause(); + void windowIPCPause(bool pause); void windowLimitFPSChange(); @@ -298,7 +300,7 @@ private slots: void onQuit(); void onPause(bool checked); - void onIPCPause(); + void onIPCPause(bool pause); void onReset(); void onStop(); void onFrameStep(); From ed32edf683d321796a6aa49e6ab609da35999bf8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 22 Mar 2023 20:33:10 +0100 Subject: [PATCH 10/81] lay base for netplay --- cmake/FindENet.cmake | 48 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 4 +++ src/frontend/qt_sdl/Netplay.cpp | 48 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/Netplay.h | 32 ++++++++++++++++++++ src/frontend/qt_sdl/main.cpp | 3 ++ 5 files changed, 135 insertions(+) create mode 100644 cmake/FindENet.cmake create mode 100644 src/frontend/qt_sdl/Netplay.cpp create mode 100644 src/frontend/qt_sdl/Netplay.h diff --git a/cmake/FindENet.cmake b/cmake/FindENet.cmake new file mode 100644 index 00000000..f9044c30 --- /dev/null +++ b/cmake/FindENet.cmake @@ -0,0 +1,48 @@ +# - Try to find enet +# Once done this will define +# +# ENET_FOUND - system has enet +# ENET_INCLUDE_DIRS - the enet include directory +# ENET_LIBRARIES - the libraries needed to use enet +# +# $ENETDIR is an environment variable used for finding enet. +# +# Borrowed from The Mana World +# http://themanaworld.org/ +# +# Several changes and additions by Fabian 'x3n' Landau +# Lots of simplifications by Adrian Friedli +# > www.orxonox.net < + +FIND_PATH(ENET_INCLUDE_DIRS enet/enet.h + PATHS + $ENV{ENETDIR} + /usr/local + /usr + PATH_SUFFIXES include +) + +FIND_LIBRARY(ENET_LIBRARY + NAMES enet + PATHS + $ENV{ENETDIR} + /usr/local + /usr + PATH_SUFFIXES lib +) + +# handle the QUIETLY and REQUIRED arguments and set ENET_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ENet DEFAULT_MSG ENET_LIBRARY ENET_INCLUDE_DIRS) + +IF (ENET_FOUND) + IF(WIN32) + SET(WINDOWS_ENET_DEPENDENCIES "ws2_32;winmm") + SET(ENET_LIBRARIES ${ENET_LIBRARY} ${WINDOWS_ENET_DEPENDENCIES}) + ELSE(WIN32) + SET(ENET_LIBRARIES ${ENET_LIBRARY}) + ENDIF(WIN32) +ENDIF (ENET_FOUND) + +MARK_AS_ADVANCED(ENET_LIBRARY ENET_LIBRARIES ENET_INCLUDE_DIRS) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 9fd99e47..cc9244d5 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCES_QT_SDL IPC.cpp LAN_PCap.cpp LAN_Socket.cpp + Netplay.cpp OSD.cpp OSD_shaders.h font.h @@ -91,6 +92,8 @@ add_compile_definitions(ARCHIVE_SUPPORT_ENABLED) add_executable(melonDS ${SOURCES_QT_SDL}) +find_package(ENet REQUIRED) + if (WIN32) target_link_libraries(melonDS PUBLIC opengl32) @@ -158,6 +161,7 @@ endif() target_link_libraries(melonDS PRIVATE core) target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive PkgConfig::Zstd) target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS}) +target_link_libraries(melonDS PRIVATE ${ENET_LIBRARIES}) if (UNIX) option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp new file mode 100644 index 00000000..e524e857 --- /dev/null +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -0,0 +1,48 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include + +#include + +#include "Netplay.h" + + +namespace Netplay +{ + +bool Init() +{ + if (enet_initialize() != 0) + { + printf("enet shat itself :(\n"); + return false; + } + + printf("enet init OK\n"); + return true; +} + +void DeInit() +{ + enet_deinitialize(); +} + +} diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h new file mode 100644 index 00000000..c7ce6ef3 --- /dev/null +++ b/src/frontend/qt_sdl/Netplay.h @@ -0,0 +1,32 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETPLAY_H +#define NETPLAY_H + +#include "types.h" + +namespace Netplay +{ + +bool Init(); +void DeInit(); + +} + +#endif // NETPLAY_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 9182adaf..f5635ff2 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -88,6 +88,7 @@ #include "Wifi.h" #include "Platform.h" #include "IPC.h" +#include "Netplay.h" #include "Config.h" #include "DSi_I2C.h" @@ -318,6 +319,7 @@ void EmuThread::run() IPC::InitSema(); IPC::SetMPRecvTimeout(Config::MPRecvTimeout); + Netplay::Init(); NDS::Init(); @@ -658,6 +660,7 @@ void EmuThread::run() GPU::DeInitRenderer(); NDS::DeInit(); + Netplay::DeInit(); IPC::DeInitSema(); //Platform::LAN_DeInit(); } From 1d135bc0a54ef3510352ec8a81cecc898503b8fc Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 23 Mar 2023 14:29:16 +0100 Subject: [PATCH 11/81] get enet going, I guess still very experimental --- src/frontend/qt_sdl/Netplay.cpp | 96 +++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/Netplay.h | 7 +++ src/frontend/qt_sdl/main.cpp | 20 +++++++ src/frontend/qt_sdl/main.h | 4 ++ 4 files changed, 127 insertions(+) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index e524e857..0096e505 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -28,8 +28,19 @@ namespace Netplay { +bool Active; +bool IsHost; + +ENetHost* Host; +ENetPeer* Peer; + + bool Init() { + Active = false; + IsHost = false; + Host = nullptr; + if (enet_initialize() != 0) { printf("enet shat itself :(\n"); @@ -45,4 +56,89 @@ void DeInit() enet_deinitialize(); } + +void StartHost() +{ + ENetAddress addr; + addr.host = ENET_HOST_ANY; + addr.port = 8064; + + // TODO determine proper number of clients/channels + Host = enet_host_create(&addr, 16, 16, 0, 0); + if (!Host) + { + printf("host shat itself :(\n"); + return; + } + + Active = true; + IsHost = true; +} + +void StartClient() +{ + // TODO determine proper number of clients/channels + Host = enet_host_create(nullptr, 16, 16, 0, 0); + if (!Host) + { + printf("client shat itself :(\n"); + return; + } + + printf("client created, connecting\n"); + + 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) + { + printf("connect shat itself :(\n"); + return; + } + + ENetEvent event; + bool conn = false; + if (enet_host_service(Host, &event, 5000) > 0) + { + if (event.type == ENET_EVENT_TYPE_CONNECT) + { + printf("connected!\n"); + conn = true; + } + } + + if (!conn) + { + printf("connection failed\n"); + enet_peer_reset(Peer); + } + + Active = true; + IsHost = false; +} + + +void ProcessFrame() +{ + ENetEvent event; + while (enet_host_service(Host, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + printf("client connected %08X %d\n", event.peer->address.host, event.peer->address.port); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf("client disconnected %08X %d\n", event.peer->address.host, event.peer->address.port); + break; + + case ENET_EVENT_TYPE_RECEIVE: + printf("received shit\n"); + break; + } + } +} + } diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index c7ce6ef3..7cef744a 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -24,9 +24,16 @@ namespace Netplay { +extern bool Active; + bool Init(); void DeInit(); +void StartHost(); +void StartClient(); + +void ProcessFrame(); + } #endif // NETPLAY_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f5635ff2..42db6788 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -363,6 +363,8 @@ void EmuThread::run() while (EmuRunning != emuStatus_Exit) { + if (Netplay::Active) Netplay::ProcessFrame(); + IPC::ProcessCommands(); Input::Process(); @@ -1566,6 +1568,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actMPNewInstance = submenu->addAction("Launch new instance"); connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); + + submenu->addSeparator(); + + actMPStartHost = submenu->addAction("NETPLAY HOST"); + connect(actMPStartHost, &QAction::triggered, this, &MainWindow::onMPStartHost); + + actMPStartClient = submenu->addAction("NETPLAY CLIENT"); + connect(actMPStartClient, &QAction::triggered, this, &MainWindow::onMPStartClient); } } { @@ -2824,6 +2834,16 @@ void MainWindow::onMPNewInstance() newinst.startDetached(); } +void MainWindow::onMPStartHost() +{ + Netplay::StartHost(); +} + +void MainWindow::onMPStartClient() +{ + Netplay::StartClient(); +} + void MainWindow::onOpenEmuSettings() { emuThread->emuPause(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 107b5714..338af664 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -311,6 +311,8 @@ private slots: void onRAMInfo(); void onOpenTitleManager(); void onMPNewInstance(); + void onMPStartHost(); + void onMPStartClient(); void onOpenEmuSettings(); void onEmuSettingsDialogFinished(int res); @@ -408,6 +410,8 @@ public: QAction* actRAMInfo; QAction* actTitleManager; QAction* actMPNewInstance; + QAction* actMPStartHost; + QAction* actMPStartClient; QAction* actEmuSettings; #ifdef __APPLE__ From 79494fad5e0df055752177f8ddcb5bd36dbe08b0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 23 Mar 2023 22:09:51 +0100 Subject: [PATCH 12/81] base for forwarding input to clients --- src/frontend/qt_sdl/Input.h | 3 + src/frontend/qt_sdl/Netplay.cpp | 151 +++++++++++++++++++++++++++++++- src/frontend/qt_sdl/Netplay.h | 3 + src/frontend/qt_sdl/main.cpp | 39 +++++++-- src/frontend/qt_sdl/main.h | 2 + 5 files changed, 189 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h index 95c300e5..9a242318 100644 --- a/src/frontend/qt_sdl/Input.h +++ b/src/frontend/qt_sdl/Input.h @@ -19,6 +19,9 @@ #ifndef INPUT_H #define INPUT_H +#include +#include + #include "types.h" namespace Input diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 0096e505..d42827bf 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -19,10 +19,17 @@ #include #include #include +#include #include +#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 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(); +} + } diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index 7cef744a..cd4665d3 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -32,7 +32,10 @@ void DeInit(); void StartHost(); void StartClient(); +void StartGame(); + void ProcessFrame(); +void ProcessInput(); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 42db6788..caa3f0a4 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -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(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 338af664..2d6ddae7 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -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__ From 1b8197671ca88a11064a6cddaeecc24167ac7eb8 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 24 Mar 2023 17:13:50 +0100 Subject: [PATCH 13/81] 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() From e9eb926b830fdbf27023fa7c8c39758cd725874e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 24 Mar 2023 17:17:12 +0100 Subject: [PATCH 14/81] maybe try not being a derp, Arisotura --- src/frontend/qt_sdl/NetplayDialog.ui | 31 +++++ .../qt_sdl/NetplayStartClientDialog.ui | 107 ++++++++++++++++++ src/frontend/qt_sdl/NetplayStartHostDialog.ui | 97 ++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 src/frontend/qt_sdl/NetplayDialog.ui create mode 100644 src/frontend/qt_sdl/NetplayStartClientDialog.ui create mode 100644 src/frontend/qt_sdl/NetplayStartHostDialog.ui diff --git a/src/frontend/qt_sdl/NetplayDialog.ui b/src/frontend/qt_sdl/NetplayDialog.ui new file mode 100644 index 00000000..86b51324 --- /dev/null +++ b/src/frontend/qt_sdl/NetplayDialog.ui @@ -0,0 +1,31 @@ + + + NetplayDialog + + + + 0 + 0 + 522 + 391 + + + + NETPLAY SHITO + + + + + + STATUS PLACEHOLDER + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/NetplayStartClientDialog.ui b/src/frontend/qt_sdl/NetplayStartClientDialog.ui new file mode 100644 index 00000000..df5b4ea7 --- /dev/null +++ b/src/frontend/qt_sdl/NetplayStartClientDialog.ui @@ -0,0 +1,107 @@ + + + NetplayStartClientDialog + + + + 0 + 0 + 400 + 229 + + + + + 0 + 0 + + + + NETPLAY CLIENT + + + + QLayout::SetFixedSize + + + + + + + Player name: + + + + + + + Host port: + + + + + + + + + + + + + Host address: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NetplayStartClientDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NetplayStartClientDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/NetplayStartHostDialog.ui b/src/frontend/qt_sdl/NetplayStartHostDialog.ui new file mode 100644 index 00000000..f704e743 --- /dev/null +++ b/src/frontend/qt_sdl/NetplayStartHostDialog.ui @@ -0,0 +1,97 @@ + + + NetplayStartHostDialog + + + + 0 + 0 + 400 + 229 + + + + + 0 + 0 + + + + NETPLAY HOST + + + + QLayout::SetFixedSize + + + + + + + Player name: + + + + + + + Port: + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NetplayStartHostDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NetplayStartHostDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From dbf80435a9d4b4fe4cf761434812dacd98380ae9 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 24 Mar 2023 17:20:48 +0100 Subject: [PATCH 15/81] dfgsdgfg --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index c6fb19ca..d5cc7d07 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -110,7 +110,7 @@ NetplayDialog::NetplayDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Netp QStandardItemModel* model = new QStandardItemModel(); ui->tvPlayerList->setModel(model); - connect(this, &sgUpdatePlayerList, this, &doUpdatePlayerList); + connect(this, &NetplayDialog::sgUpdatePlayerList, this, &NetplayDialog::doUpdatePlayerList); } NetplayDialog::~NetplayDialog() From 6d3534bc3d6ac5e764bc6fefdbb3952a0ab4ff17 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 24 Mar 2023 21:16:32 +0100 Subject: [PATCH 16/81] more WIP --- src/frontend/qt_sdl/IPC.cpp | 9 +++ src/frontend/qt_sdl/IPC.h | 4 ++ src/frontend/qt_sdl/Netplay.cpp | 109 +++++++++++++++++++++++++++++--- src/frontend/qt_sdl/Netplay.h | 4 ++ 4 files changed, 118 insertions(+), 8 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index fdc4683b..609d42d7 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -371,6 +371,15 @@ void SetActive(bool active) Buffer->unlock(); } +u16 GetInstanceBitmask() +{ + Buffer->lock(); + BufferHeader* header = (BufferHeader*)Buffer->data(); + u16 ret = header->InstanceBitmask; + Buffer->unlock(); + return ret; +} + template void FIFORead(void* buf, int len) { diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index eca43837..cf1a2390 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -28,6 +28,8 @@ enum { Cmd_Pause = 1, + Cmd_SetupNetplayMirror, + Cmd_MAX }; @@ -44,6 +46,8 @@ void MPEnd(); void SetActive(bool active); +u16 GetInstanceBitmask(); + void ProcessCommands(); bool SendCommand(u16 recipients, u16 command, u16 len, void* data); bool SendCommandU8(u16 recipients, u16 command, u8 arg); diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index d5cc7d07..8328a121 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -24,9 +24,11 @@ #include #include +#include #include "NDS.h" #include "main.h" +#include "IPC.h" #include "Netplay.h" #include "Input.h" @@ -137,7 +139,9 @@ void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) model->clear(); model->setRowCount(num); - const QStringList header = {"#", "Player", "Status", "Ping"}; + // TODO: remove IP column in final product + + const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; model->setHorizontalHeaderLabels(header); for (int i = 0; i < num; i++) @@ -161,6 +165,11 @@ void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num) // TODO: ping model->setItem(i, 3, new QStandardItem("x")); + + char ip[32]; + 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)); } } @@ -170,13 +179,16 @@ namespace Netplay bool Active; bool IsHost; +bool IsMirror; ENetHost* Host; +ENetHost* MirrorHost; Player Players[16]; int NumPlayers; -Player TempPlayer; // holds client player info temporarily +Player MyPlayer; +u32 HostAddress; struct InputFrame { @@ -187,14 +199,13 @@ struct InputFrame std::queue InputQueue; -// - - bool Init() { Active = false; IsHost = false; + IsMirror = false; Host = nullptr; + MirrorHost = nullptr; memset(Players, 0, sizeof(Players)); NumPlayers = 0; @@ -211,6 +222,8 @@ bool Init() void DeInit() { + // TODO: cleanup resources properly!! + enet_deinitialize(); } @@ -233,10 +246,26 @@ void StartHost(const char* playername, int port) player->ID = 0; strncpy(player->Name, playername, 31); player->Status = 2; + player->Address = 0x0100007F; NumPlayers = 1; + memcpy(&MyPlayer, player, sizeof(Player)); + + HostAddress = 0x0100007F; + + ENetAddress mirroraddr; + mirroraddr.host = ENET_HOST_ANY; + mirroraddr.port = port + 1; + + MirrorHost = enet_host_create(&mirroraddr, 16, 1, 0, 0); + if (!MirrorHost) + { + printf("mirror host shat itself :(\n"); + return; + } Active = true; IsHost = true; + IsMirror = false; netplayDlg->updatePlayerList(Players, NumPlayers); } @@ -280,17 +309,79 @@ void StartClient(const char* playername, const char* host, int port) return; } - Player* player = &TempPlayer; + Player* player = &MyPlayer; memset(player, 0, sizeof(Player)); player->ID = 0; strncpy(player->Name, playername, 31); player->Status = 3; + HostAddress = addr.host; + Active = true; IsHost = false; + IsMirror = false; } +u32 PlayerAddress(int id) +{ + if (id < 0 || id > 16) return 0; + + u32 ret = Players[id].Address; + if (ret == 0x0100007F) ret = HostAddress; + return ret; +} + + +bool SpawnMirrorInstance(Player* player) +{ + u16 curmask = IPC::GetInstanceBitmask(); + + QProcess newinst; + newinst.setProgram(QApplication::applicationFilePath()); + newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1)); + +#ifdef __WIN32__ + newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args) + { + args->flags |= CREATE_NEW_CONSOLE; + }); +#endif + + if (!newinst.startDetached()) + return false; + + // try to determine the ID of the new instance + + int newid = -1; + for (int tries = 0; tries < 10; tries++) + { + QThread::usleep(100 * 1000); + + u16 newmask = IPC::GetInstanceBitmask(); + if (newmask == curmask) continue; + + newmask &= ~curmask; + for (int id = 0; id < 32; id++) + { + if (newmask & (1 << id)) + { + newid = id; + break; + } + } + } + + if (newid == -1) return false; + + // setup that instance + printf("netplay: spawned mirror instance for player %d with ID %d, configuring\n", player->ID, newid); + + IPC::SendCommand(1<address.host; event.peer->data = &Players[id]; NumPlayers++; } @@ -375,6 +467,7 @@ void ProcessHost() } player.Status = 1; + player.Address = event.peer->address.host; memcpy(hostside, &player, sizeof(Player)); // broadcast updated player list @@ -425,10 +518,10 @@ void ProcessClient() if (event.packet->dataLength != 2) break; // send player information - TempPlayer.ID = data[1];printf("host assigned ID: %d\n", data[1]); + MyPlayer.ID = data[1]; u8 cmd[1+sizeof(Player)]; cmd[0] = 0x02; - memcpy(&cmd[1], &TempPlayer, sizeof(Player)); + memcpy(&cmd[1], &MyPlayer, sizeof(Player)); ENetPacket* pkt = enet_packet_create(cmd, 1+sizeof(Player), ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, pkt); } diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index 80bfcee5..d2590785 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -42,6 +42,7 @@ struct Player int ID; char Name[32]; int Status; // 0=no player 1=normal 2=host 3=connecting + u32 Address; }; } @@ -129,6 +130,9 @@ void DeInit(); void StartHost(const char* player, int port); void StartClient(const char* player, const char* host, int port); +void StartMirror(const Player* player); + +u32 PlayerAddress(int id); void StartGame(); From d9537d87cd04ff5d56d817ed3a069a4b9804bc75 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 00:10:40 +0100 Subject: [PATCH 17/81] attempt at adding mirror instances and shito --- src/frontend/qt_sdl/IPC.cpp | 35 ++++++++++++++ src/frontend/qt_sdl/IPC.h | 5 ++ src/frontend/qt_sdl/Netplay.cpp | 77 ++++++++++++++++++++++++++++-- src/frontend/qt_sdl/ROMManager.cpp | 3 ++ src/frontend/qt_sdl/ROMManager.h | 2 + 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 609d42d7..108dd76b 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -36,6 +36,9 @@ #include "IPC.h" #include "Config.h" #include "main.h" +#include "ROMManager.h" +#include "Netplay.h" +#include "NDS.h" extern EmuThread* emuThread; @@ -511,6 +514,29 @@ void ProcessCommands() case Cmd_Pause: emuThread->IPCPause(cmddata[0] != 0); break; + + case Cmd_LoadROM: + { + u16 len = *(u16*)&cmddata[0]; + cmddata[2+len] = '\0'; + + char* cstr = (char*)&cmddata[2]; + std::string str = cstr; + ROMManager::LoadROM(QString::fromStdString(str).split('|'), true); + } + break; + + case Cmd_SetupNetplayMirror: + { + Netplay::Player* player = (Netplay::Player*)&cmddata[0]; + Netplay::StartMirror(player); + } + break; + + case Cmd_Start: + NDS::Start(); + emuThread->emuRun(); + break; } } @@ -567,6 +593,15 @@ bool SendCommandU8(u16 recipients, u16 command, u8 arg) return SendCommand(recipients, command, 1, &data); } +bool SendCommandStr(u16 recipients, u16 command, std::string str) +{ + u8 data[kMaxCommandSize] = {0}; + + strncpy((char*)&data[2], str.c_str(), kMaxCommandSize-3); + *(u16*)&data[0] = strlen((const char*)&data[2]) + 1; + return SendCommand(recipients, command, 2+(*(u16*)&data[0]), data); +} + int SendMPPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) { diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index cf1a2390..7a1ceab8 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -19,6 +19,8 @@ #ifndef IPC_H #define IPC_H +#include + #include "types.h" namespace IPC @@ -28,7 +30,9 @@ enum { Cmd_Pause = 1, + Cmd_LoadROM, Cmd_SetupNetplayMirror, + Cmd_Start, Cmd_MAX }; @@ -51,6 +55,7 @@ u16 GetInstanceBitmask(); void ProcessCommands(); bool SendCommand(u16 recipients, u16 command, u16 len, void* data); bool SendCommandU8(u16 recipients, u16 command, u8 arg); +bool SendCommandStr(u16 recipients, u16 command, std::string str); int SendMPPacket(u8* data, int len, u64 timestamp); int RecvMPPacket(u8* data, u64* timestamp); diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 8328a121..d078da69 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -31,6 +31,7 @@ #include "IPC.h" #include "Netplay.h" #include "Input.h" +#include "ROMManager.h" #include "ui_NetplayStartHostDialog.h" #include "ui_NetplayStartClientDialog.h" @@ -272,7 +273,7 @@ void StartHost(const char* playername, int port) void StartClient(const char* playername, const char* host, int port) { - Host = enet_host_create(nullptr, 16, 1, 0, 0); + Host = enet_host_create(nullptr, 1, 1, 0, 0); if (!Host) { printf("client shat itself :(\n"); @@ -322,6 +323,54 @@ void StartClient(const char* playername, const char* host, int port) IsMirror = false; } +void StartMirror(const Player* player) +{ + MirrorHost = enet_host_create(nullptr, 1, 1, 0, 0); + if (!MirrorHost) + { + printf("mirror shat itself :(\n"); + return; + } + + printf("mirror created, connecting\n"); + + ENetAddress addr; + addr.host = player->Address; + addr.port = 8064+1 + player->ID; // FIXME!!!!!!!!!! + ENetPeer* peer = enet_host_connect(MirrorHost, &addr, 1, 0); + if (!peer) + { + printf("connect shat itself :(\n"); + return; + } + + ENetEvent event; + bool conn = false; + if (enet_host_service(MirrorHost, &event, 5000) > 0) + { + if (event.type == ENET_EVENT_TYPE_CONNECT) + { + printf("connected!\n"); + conn = true; + } + } + + if (!conn) + { + printf("connection failed\n"); + enet_peer_reset(peer); + return; + } + + memcpy(&MyPlayer, player, sizeof(Player)); + + HostAddress = addr.host; + + Active = true; + IsHost = false; + IsMirror = true; +} + u32 PlayerAddress(int id) { @@ -333,7 +382,7 @@ u32 PlayerAddress(int id) } -bool SpawnMirrorInstance(Player* player) +bool SpawnMirrorInstance(Player player) { u16 curmask = IPC::GetInstanceBitmask(); @@ -375,9 +424,13 @@ bool SpawnMirrorInstance(Player* player) if (newid == -1) return false; // setup that instance - printf("netplay: spawned mirror instance for player %d with ID %d, configuring\n", player->ID, newid); + printf("netplay: spawned mirror instance for player %d with ID %d, configuring\n", player.ID, newid); - IPC::SendCommand(1<emuRun(); @@ -542,6 +604,11 @@ void ProcessClient() netplayDlg->updatePlayerList(Players, NumPlayers); } break; + + case 0x04: // start game + NDS::Start(); + emuThread->emuRun(); + break; } } break; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index a2a5fca6..8de2157e 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -41,6 +41,8 @@ using namespace Platform; namespace ROMManager { +QStringList FullROMPath; + int CartType = -1; std::string BaseROMDir = ""; std::string BaseROMName = ""; @@ -729,6 +731,7 @@ bool LoadROM(QStringList filepath, bool reset) if (NDSSave) delete NDSSave; NDSSave = nullptr; + FullROMPath = filepath; BaseROMDir = basepath; BaseROMName = romname; BaseAssetName = romname.substr(0, romname.rfind('.')); diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 8e199dba..99b57ba6 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -29,6 +29,8 @@ namespace ROMManager { +extern QStringList FullROMPath; + extern SaveManager* NDSSave; extern SaveManager* GBASave; From 630ab01ba320997844cca62f6588c96e68f02049 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 00:22:36 +0100 Subject: [PATCH 18/81] maybe also start mirror instances on the client side --- src/frontend/qt_sdl/Netplay.cpp | 64 ++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index d078da69..e1c0119e 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -606,8 +606,21 @@ void ProcessClient() break; case 0x04: // start game - NDS::Start(); - emuThread->emuRun(); + { + // spawn mirror instances as needed + for (int i = 0; i < NumPlayers; i++) + { + if (i != MyPlayer.ID) + SpawnMirrorInstance(Players[i]); + } + + // tell other mirror instances to start the game + IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); + + // start game locally + NDS::Start(); + emuThread->emuRun(); + } break; } } @@ -616,9 +629,45 @@ void ProcessClient() } } +void ProcessMirror() +{ + ENetEvent event; + while (enet_host_service(MirrorHost, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + printf("schmu\n"); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + { + // TODO + printf("shmz\n"); + } + break; + + case ENET_EVENT_TYPE_RECEIVE: + { + if (event.packet->dataLength != sizeof(InputFrame)) break; + + u8* data = (u8*)event.packet->data; + InputFrame frame; + memcpy(&frame, data, sizeof(InputFrame)); + InputQueue.push(frame); + } + break; + } + } +} + void ProcessFrame() { - if (IsHost) + if (IsMirror) + { + ProcessMirror(); + } + else if (IsHost) { ProcessHost(); } @@ -712,7 +761,7 @@ void ProcessInput() // 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) + if (!IsMirror) { u32 lag = 4; // TODO: make configurable!! @@ -723,11 +772,10 @@ void ProcessInput() InputQueue.push(frame); - u8 cmd[1+sizeof(InputFrame)]; - cmd[0] = 0x02; - memcpy(&cmd[1], &frame, sizeof(InputFrame)); + u8 cmd[sizeof(InputFrame)]; + memcpy(cmd, &frame, sizeof(InputFrame)); ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE); - enet_host_broadcast(Host, 0, pkt); + enet_host_broadcast(MirrorHost, 0, pkt); } if (InputQueue.empty()) From 567401200ae6db238b5860c26f69889dcbf7b12f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 00:51:56 +0100 Subject: [PATCH 19/81] zarg --- src/frontend/qt_sdl/Netplay.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index e1c0119e..8e7b3d07 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -256,7 +256,7 @@ void StartHost(const char* playername, int port) ENetAddress mirroraddr; mirroraddr.host = ENET_HOST_ANY; mirroraddr.port = port + 1; - +printf("host mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr.port); MirrorHost = enet_host_create(&mirroraddr, 16, 1, 0, 0); if (!MirrorHost) { @@ -337,6 +337,7 @@ void StartMirror(const Player* player) ENetAddress addr; addr.host = player->Address; addr.port = 8064+1 + player->ID; // FIXME!!!!!!!!!! + printf("mirror client connecting to %08X:%d\n", addr.host, addr.port); ENetPeer* peer = enet_host_connect(MirrorHost, &addr, 1, 0); if (!peer) { @@ -579,6 +580,18 @@ void ProcessClient() { if (event.packet->dataLength != 2) break; + // create mirror host + 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); + MirrorHost = enet_host_create(&mirroraddr, 16, 1, 0, 0); + if (!MirrorHost) + { + printf("mirror host shat itself :(\n"); + break; + } + // send player information MyPlayer.ID = data[1]; u8 cmd[1+sizeof(Player)]; @@ -613,13 +626,13 @@ void ProcessClient() if (i != MyPlayer.ID) SpawnMirrorInstance(Players[i]); } - +printf("bourf\n"); // tell other mirror instances to start the game IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); - +printf("birf\n"); // start game locally - NDS::Start(); - emuThread->emuRun(); + NDS::Start();printf("barf\n"); + emuThread->emuRun();printf("burf\n"); } break; } From 44385ce2330726d3dfbd3e3cf70eab73c31be802 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 00:57:58 +0100 Subject: [PATCH 20/81] stoopid --- src/frontend/qt_sdl/Netplay.cpp | 44 +++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 8e7b3d07..19444940 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -642,7 +642,32 @@ printf("birf\n"); } } -void ProcessMirror() +void ProcessMirrorHost() +{ + ENetEvent event; + while (enet_host_service(MirrorHost, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + printf("schmz\n"); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + { + // TODO + printf("shmtt\n"); + } + break; + + case ENET_EVENT_TYPE_RECEIVE: + // TODO?? + break; + } + } +} + +void ProcessMirrorClient() { ENetEvent event; while (enet_host_service(MirrorHost, &event, 0) > 0) @@ -678,15 +703,20 @@ void ProcessFrame() { if (IsMirror) { - ProcessMirror(); - } - else if (IsHost) - { - ProcessHost(); + ProcessMirrorClient(); } else { - ProcessClient(); + if (IsHost) + { + ProcessHost(); + } + else + { + ProcessClient(); + } + + ProcessMirrorHost(); } return; From 31de28deeb131440f133ccf0cd267c300dae3f44 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 01:00:24 +0100 Subject: [PATCH 21/81] preemptively avoid a crash --- src/frontend/qt_sdl/Netplay.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 19444940..2aa524cf 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -466,6 +466,8 @@ void StartGame() void ProcessHost() { + if (!Host) return; + ENetEvent event; while (enet_host_service(Host, &event, 0) > 0) { @@ -553,6 +555,8 @@ void ProcessHost() void ProcessClient() { + if (!Host) return; + ENetEvent event; while (enet_host_service(Host, &event, 0) > 0) { @@ -644,6 +648,8 @@ printf("birf\n"); void ProcessMirrorHost() { + if (!MirrorHost) return; + ENetEvent event; while (enet_host_service(MirrorHost, &event, 0) > 0) { @@ -669,6 +675,8 @@ void ProcessMirrorHost() void ProcessMirrorClient() { + if (!MirrorHost) return; + ENetEvent event; while (enet_host_service(MirrorHost, &event, 0) > 0) { From 6d7ebb768679be3d0baad3d408b2c6394233cad7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 10:03:33 +0100 Subject: [PATCH 22/81] attempt at preventing desyncs, let's see --- src/frontend/qt_sdl/Netplay.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 2aa524cf..7fd3b60c 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -677,8 +677,15 @@ void ProcessMirrorClient() { if (!MirrorHost) return; + bool block = false; + if (emuThread->emuIsRunning() && NDS::NumFrames > 4) + { + if (InputQueue.empty()) + block = true; + } + ENetEvent event; - while (enet_host_service(MirrorHost, &event, 0) > 0) + while (enet_host_service(MirrorHost, &event, block ? 5000 : 0) > 0) { switch (event.type) { @@ -839,6 +846,7 @@ void ProcessInput() if (frame.FrameNum < NDS::NumFrames) { + // TODO: this situation is a desync printf("Netplay: BAD! LAGGING BEHIND\n"); while (frame.FrameNum < NDS::NumFrames) { From 0f4a81cf90449d7d3f986541bcebe536f6922a1b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 10:36:30 +0100 Subject: [PATCH 23/81] fix bug --- src/frontend/qt_sdl/IPC.cpp | 4 ++++ src/frontend/qt_sdl/Netplay.cpp | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 108dd76b..e47b9522 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -509,6 +509,8 @@ void ProcessCommands() if (cmdheader.Length) FIFORead<0>(cmddata, cmdheader.Length); + Buffer->unlock(); + switch (cmdheader.Command) { case Cmd_Pause: @@ -538,6 +540,8 @@ void ProcessCommands() emuThread->emuRun(); break; } + + Buffer->lock(); } Buffer->unlock(); diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 7fd3b60c..11728c82 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -838,7 +838,8 @@ void ProcessInput() if (InputQueue.empty()) { - printf("Netplay: BAD! INPUT QUEUE EMPTY\n"); + if (NDS::NumFrames < 4) + printf("Netplay: BAD! INPUT QUEUE EMPTY\n"); return; } @@ -863,7 +864,7 @@ void ProcessInput() } // apply this input frame - printf("[%08d] INPUT=%08X (%08d) (backlog=%d)\n", NDS::NumFrames, frame.KeyMask, frame.FrameNum, InputQueue.size()); + if (frame.KeyMask != 0xFFF) printf("[%08d] INPUT=%08X (%08d) (backlog=%d)\n", NDS::NumFrames, frame.KeyMask, frame.FrameNum, InputQueue.size()); NDS::SetKeyMask(frame.KeyMask); InputQueue.pop(); } From d6c3bc906d57adffe7c607b5209834a297c257b4 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 10:51:17 +0100 Subject: [PATCH 24/81] maybe it would work better this way, Arisotura --- src/frontend/qt_sdl/Netplay.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 11728c82..1bbcd51c 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -711,6 +711,8 @@ void ProcessMirrorClient() } break; } + + if (block) break; } } From a81f2b39b6035d0925f1f6d570b4b0540aceede5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 21:53:12 +0100 Subject: [PATCH 25/81] attempt at keeping mirror clients from lagging behind too much --- src/frontend/qt_sdl/Netplay.cpp | 114 ++++++++++++-------------------- 1 file changed, 44 insertions(+), 70 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 1bbcd51c..e03919ae 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -190,6 +190,7 @@ int NumPlayers; Player MyPlayer; u32 HostAddress; +bool Lag; struct InputFrame { @@ -207,6 +208,7 @@ bool Init() IsMirror = false; Host = nullptr; MirrorHost = nullptr; + Lag = false; memset(Players, 0, sizeof(Players)); NumPlayers = 0; @@ -650,13 +652,15 @@ void ProcessMirrorHost() { if (!MirrorHost) return; + bool block = false; ENetEvent event; - while (enet_host_service(MirrorHost, &event, 0) > 0) + while (enet_host_service(MirrorHost, &event, block ? 5000 : 0) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: printf("schmz\n"); + event.peer->data = (void*)0; break; case ENET_EVENT_TYPE_DISCONNECT: @@ -667,7 +671,32 @@ void ProcessMirrorHost() break; case ENET_EVENT_TYPE_RECEIVE: - // TODO?? + { + if (event.packet->dataLength != 1) break; + u8* data = (u8*)event.packet->data; + + if (data[0]) + { + event.peer->data = (void*)1; + block = true; + } + else + { + event.peer->data = (void*)0; + block = false; + + for (int i = 0; i < MirrorHost->peerCount; i++) + { + ENetPeer* peer = &(MirrorHost->peers[i]); + if (peer->state != ENET_PEER_STATE_CONNECTED) continue; + if (peer->data != (void*)0) + { + block = true; + break; + } + } + } + } break; } } @@ -708,6 +737,18 @@ void ProcessMirrorClient() InputFrame frame; memcpy(&frame, data, sizeof(InputFrame)); InputQueue.push(frame); + + bool lag = (InputQueue.size() > 4*2); + if (lag != Lag) + { + // let the mirror host know they are running too fast for us + + u8 data = lag ? 1 : 0; + ENetPacket* pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, pkt); + + Lag = lag; + } } break; } @@ -735,73 +776,6 @@ void ProcessFrame() ProcessMirrorHost(); } - - return; - 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, 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: - { - 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() @@ -840,7 +814,7 @@ void ProcessInput() if (InputQueue.empty()) { - if (NDS::NumFrames < 4) + if (NDS::NumFrames > 4) printf("Netplay: BAD! INPUT QUEUE EMPTY\n"); return; } From 2d247f6c770621cec321ac8f7a0d24c506307cc4 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 25 Mar 2023 22:09:59 +0100 Subject: [PATCH 26/81] mhm --- src/frontend/qt_sdl/Netplay.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index e03919ae..354ba953 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -720,6 +720,7 @@ void ProcessMirrorClient() { case ENET_EVENT_TYPE_CONNECT: printf("schmu\n"); + Lag = false; break; case ENET_EVENT_TYPE_DISCONNECT: @@ -742,7 +743,7 @@ void ProcessMirrorClient() if (lag != Lag) { // let the mirror host know they are running too fast for us - +printf("mirror client lag notify: %d\n", lag); u8 data = lag ? 1 : 0; ENetPacket* pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, pkt); From 3c6e020f9ec009c4d9919e579e2b87059e5d8fc2 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 26 Mar 2023 18:50:18 +0200 Subject: [PATCH 27/81] attempt 2 at keeping mirror clients from lagging too far behind --- src/frontend/qt_sdl/Netplay.cpp | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 354ba953..ed5f8b95 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -672,8 +672,8 @@ void ProcessMirrorHost() case ENET_EVENT_TYPE_RECEIVE: { - if (event.packet->dataLength != 1) break; - u8* data = (u8*)event.packet->data; + if (event.packet->dataLength != 4) break; + /*u8* data = (u8*)event.packet->data; if (data[0]) { @@ -685,6 +685,29 @@ void ProcessMirrorHost() event.peer->data = (void*)0; block = false; + for (int i = 0; i < MirrorHost->peerCount; i++) + { + ENetPeer* peer = &(MirrorHost->peers[i]); + if (peer->state != ENET_PEER_STATE_CONNECTED) continue; + if (peer->data != (void*)0) + { + block = true; + break; + } + } + }*/ + u32 clientframes = *(u32*)event.packet->data; + + if (clientframes > (NDS::NumFrames + 4)) + { + event.peer->data = (void*)1; + block = true; + } + else + { + event.peer->data = (void*)0; + block = false; + for (int i = 0; i < MirrorHost->peerCount; i++) { ENetPeer* peer = &(MirrorHost->peers[i]); @@ -739,7 +762,7 @@ void ProcessMirrorClient() memcpy(&frame, data, sizeof(InputFrame)); InputQueue.push(frame); - bool lag = (InputQueue.size() > 4*2); + /*bool lag = (InputQueue.size() > 4*2); if (lag != Lag) { // let the mirror host know they are running too fast for us @@ -749,6 +772,10 @@ printf("mirror client lag notify: %d\n", lag); enet_peer_send(event.peer, 0, pkt); Lag = lag; + }*/ + { + ENetPacket* pkt = enet_packet_create(&NDS::NumFrames, 4, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, pkt); } } break; From 37a454044bab0b8716ad1344ac9fa68a31fd925d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 26 Mar 2023 19:03:36 +0200 Subject: [PATCH 28/81] attempt --- src/frontend/qt_sdl/Netplay.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index ed5f8b95..aa3a649c 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -697,7 +697,7 @@ void ProcessMirrorHost() } }*/ u32 clientframes = *(u32*)event.packet->data; - +printf("[SYNC] HOST=%d CLIENT=%d\n", NDS::NumFrames, clientframes); if (clientframes > (NDS::NumFrames + 4)) { event.peer->data = (void*)1; @@ -776,6 +776,7 @@ printf("mirror client lag notify: %d\n", lag); { ENetPacket* pkt = enet_packet_create(&NDS::NumFrames, 4, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, pkt); + enet_host_flush(MirrorHost); } } break; @@ -838,6 +839,7 @@ void ProcessInput() memcpy(cmd, &frame, sizeof(InputFrame)); ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 0, pkt); + enet_host_flush(MirrorHost); } if (InputQueue.empty()) From 25d346e6899a10d8adf69086ecd8c0cb2373fdc2 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 26 Mar 2023 19:09:41 +0200 Subject: [PATCH 29/81] maybe betterer this way, Arisotura? --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index aa3a649c..ad32bab0 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -698,7 +698,7 @@ void ProcessMirrorHost() }*/ u32 clientframes = *(u32*)event.packet->data; printf("[SYNC] HOST=%d CLIENT=%d\n", NDS::NumFrames, clientframes); - if (clientframes > (NDS::NumFrames + 4)) + if (clientframes < (NDS::NumFrames - 4)) { event.peer->data = (void*)1; block = true; From 4229ecdb18fd68dcc06c7558047f129370acccdc Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 26 Mar 2023 19:29:21 +0200 Subject: [PATCH 30/81] zarg? --- src/frontend/qt_sdl/Netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index ad32bab0..02301615 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -697,8 +697,8 @@ void ProcessMirrorHost() } }*/ u32 clientframes = *(u32*)event.packet->data; -printf("[SYNC] HOST=%d CLIENT=%d\n", NDS::NumFrames, clientframes); - if (clientframes < (NDS::NumFrames - 4)) +//printf("[SYNC] HOST=%d CLIENT=%d\n", NDS::NumFrames, clientframes); + if (clientframes < (NDS::NumFrames - 16)) { event.peer->data = (void*)1; block = true; From 29e2c62b66ffc5918b044e089495a3b354263004 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 26 Mar 2023 22:40:09 +0200 Subject: [PATCH 31/81] quick attempt. --- src/frontend/qt_sdl/IPC.cpp | 3 +-- src/frontend/qt_sdl/Netplay.cpp | 20 ++++++++++++++++---- src/frontend/qt_sdl/Netplay.h | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index e47b9522..14609275 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -536,8 +536,7 @@ void ProcessCommands() break; case Cmd_Start: - NDS::Start(); - emuThread->emuRun(); + Netplay::StartLocal(); break; } diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 02301615..f8b43282 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -461,6 +461,19 @@ void StartGame() IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); // start game locally + StartLocal(); +} + +void StartLocal() +{ + for (int i = 0; i < 4; i++) + { + InputFrame frame; + frame.FrameNum = i; + frame.KeyMask = 0xFFF; + InputQueue.push(frame); + } + NDS::Start(); emuThread->emuRun(); } @@ -637,8 +650,7 @@ printf("bourf\n"); IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); printf("birf\n"); // start game locally - NDS::Start();printf("barf\n"); - emuThread->emuRun();printf("burf\n"); + StartLocal(); } break; } @@ -730,7 +742,7 @@ void ProcessMirrorClient() if (!MirrorHost) return; bool block = false; - if (emuThread->emuIsRunning() && NDS::NumFrames > 4) + if (emuThread->emuIsRunning())// && NDS::NumFrames > 4) { if (InputQueue.empty()) block = true; @@ -844,7 +856,7 @@ void ProcessInput() if (InputQueue.empty()) { - if (NDS::NumFrames > 4) + //if (NDS::NumFrames > 4) printf("Netplay: BAD! INPUT QUEUE EMPTY\n"); return; } diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index d2590785..13edde7e 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -135,6 +135,7 @@ void StartMirror(const Player* player); u32 PlayerAddress(int id); void StartGame(); +void StartLocal(); void ProcessFrame(); void ProcessInput(); From 2d131dd7558c807753bd3942191a66275e7f04da Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 26 Mar 2023 22:57:04 +0200 Subject: [PATCH 32/81] fix unsigned comparison bug (heh) --- src/frontend/qt_sdl/Netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index f8b43282..38a759a9 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -708,9 +708,9 @@ void ProcessMirrorHost() } } }*/ - u32 clientframes = *(u32*)event.packet->data; + s32 clientframes = *(s32*)event.packet->data; //printf("[SYNC] HOST=%d CLIENT=%d\n", NDS::NumFrames, clientframes); - if (clientframes < (NDS::NumFrames - 16)) + if (clientframes < (((s32)NDS::NumFrames) - 16)) { event.peer->data = (void*)1; block = true; From b59de12ce48de85054e739aaf18486886ffd9bff Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Mar 2023 21:01:05 +0200 Subject: [PATCH 33/81] add touchscreen input --- src/frontend/qt_sdl/Input.cpp | 19 +++++++++++++++++++ src/frontend/qt_sdl/Input.h | 6 ++++++ src/frontend/qt_sdl/Netplay.cpp | 13 ++++++++++++- src/frontend/qt_sdl/main.cpp | 16 +++++++++------- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index e0701087..c7c1d853 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -36,6 +36,9 @@ u32 HotkeyPress, HotkeyRelease; u32 InputMask; +bool Touching; +int TouchX, TouchY; + void Init() { @@ -48,6 +51,10 @@ void Init() ExtHotkeyMask = 0; HotkeyMask = 0; LastHotkeyMask = 0; + + Touching = false; + TouchX = 0; + TouchY = 0; } @@ -190,6 +197,18 @@ void ExtHotkeyPress(int id) ExtHotkeyMask |= (1< InputQueue; @@ -471,6 +473,9 @@ void StartLocal() InputFrame frame; frame.FrameNum = i; frame.KeyMask = 0xFFF; + frame.Touching = 0; + frame.TouchX = 0; + frame.TouchY = 0; InputQueue.push(frame); } @@ -843,7 +848,10 @@ void ProcessInput() InputFrame frame; frame.FrameNum = NDS::NumFrames + lag; frame.KeyMask = Input::InputMask; - // TODO: touchscreen input and other shit! + frame.Touching = Input::Touching ? 1:0; + frame.TouchX = Input::TouchX; + frame.TouchY = Input::TouchY; + // TODO: other shit! (some hotkeys for example?) InputQueue.push(frame); @@ -884,6 +892,9 @@ void ProcessInput() // apply this input frame if (frame.KeyMask != 0xFFF) printf("[%08d] INPUT=%08X (%08d) (backlog=%d)\n", NDS::NumFrames, frame.KeyMask, frame.FrameNum, InputQueue.size()); NDS::SetKeyMask(frame.KeyMask); + if (frame.Touching) NDS::TouchScreen(frame.TouchX, frame.TouchY); + else NDS::ReleaseScreen(); + InputQueue.pop(); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 8a0d0013..32911cb8 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -479,6 +479,8 @@ void EmuThread::run() else { NDS::SetKeyMask(Input::InputMask); + if (Input::Touching) NDS::TouchScreen(Input::TouchX, Input::TouchY); + else NDS::ReleaseScreen(); } if (Input::HotkeyPressed(HK_Lid)) @@ -923,7 +925,7 @@ void ScreenHandler::screenOnMousePress(QMouseEvent* event) if (Frontend::GetTouchCoords(x, y, false)) { touching = true; - NDS::TouchScreen(x, y); + Input::TouchScreen(x, y); } } @@ -935,7 +937,7 @@ void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) if (touching) { touching = false; - NDS::ReleaseScreen(); + Input::ReleaseScreen(); } } @@ -952,7 +954,7 @@ void ScreenHandler::screenOnMouseMove(QMouseEvent* event) int y = event->pos().y(); if (Frontend::GetTouchCoords(x, y, true)) - NDS::TouchScreen(x, y); + Input::TouchScreen(x, y); } void ScreenHandler::screenHandleTablet(QTabletEvent* event) @@ -970,14 +972,14 @@ void ScreenHandler::screenHandleTablet(QTabletEvent* event) if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) { touching = true; - NDS::TouchScreen(x, y); + Input::TouchScreen(x, y); } } break; case QEvent::TabletRelease: if (touching) { - NDS::ReleaseScreen(); + Input::ReleaseScreen(); touching = false; } break; @@ -1003,14 +1005,14 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event) if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) { touching = true; - NDS::TouchScreen(x, y); + Input::TouchScreen(x, y); } } break; case QEvent::TouchEnd: if (touching) { - NDS::ReleaseScreen(); + Input::ReleaseScreen(); touching = false; } break; From 227b50cc3e489579794d4a41bd788e8fc810ac47 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 10 Jan 2023 20:02:25 +0100 Subject: [PATCH 34/81] lay base for IPC work From e65b9f4d55587ac281988ce3521c82e3af6ec05e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 13:46:29 +0100 Subject: [PATCH 35/81] make cross-instance pause work without being a shitshow --- src/frontend/qt_sdl/IPC.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 14609275..1836a6d0 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -472,6 +472,8 @@ template void FIFOWrite(void* buf, int len) void ProcessCommands() { + memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); + Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; @@ -504,6 +506,9 @@ void ProcessCommands() continue; } + if (cmdheader.Command >= Cmd_MAX) + continue; + // handle this command if (cmdheader.Length) From b0f85391e68856e1db40bffae39cdb0d2b71bf88 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 21:16:37 +0100 Subject: [PATCH 36/81] integrate local MP comm within the IPC module --- src/frontend/qt_sdl/IPC.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 1836a6d0..1372eae9 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -98,6 +98,8 @@ const u32 kMPReplyEnd = kBufferSize; int MPRecvTimeout; int MPLastHostID; +int MPRecvTimeout; +int MPLastHostID; // we need to come up with our own abstraction layer for named semaphores // because QSystemSemaphore doesn't support waiting with a timeout @@ -546,6 +548,7 @@ void ProcessCommands() } Buffer->lock(); + // TODO: also what if, say, we get multiple pause commands before CommandReceived() is called? } Buffer->unlock(); From cf3aedb5095deac734701da5df52898772ead8ee Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 23:07:29 +0100 Subject: [PATCH 37/81] make cross-instance pause work without breaking local wifi connections --- src/frontend/qt_sdl/IPC.cpp | 10 ++++++---- src/frontend/qt_sdl/main.cpp | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 1372eae9..1b0abf06 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -474,8 +474,6 @@ template void FIFOWrite(void* buf, int len) void ProcessCommands() { - memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); - Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; @@ -505,11 +503,15 @@ void ProcessCommands() if (CommandReadOffset >= kCommandEnd) CommandReadOffset += kCommandStart - kCommandEnd; - continue; + { + // skip this command + CommandReadOffset += cmdheader.Length; + if (CommandReadOffset >= kCommandEnd) + CommandReadOffset += kCommandStart - kCommandEnd; } - if (cmdheader.Command >= Cmd_MAX) continue; + } // handle this command diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 32911cb8..1e290ff0 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -691,6 +691,7 @@ void EmuThread::emuRun() emit windowEmuStart(); AudioInOut::Enable(); IPC::SetActive(true); + IPC::SetActive(true); } void EmuThread::initContext() @@ -716,6 +717,7 @@ void EmuThread::emuPause() AudioInOut::Disable(); IPC::SetActive(false); + IPC::SetActive(false); } void EmuThread::emuUnpause() @@ -729,6 +731,7 @@ void EmuThread::emuUnpause() AudioInOut::Enable(); IPC::SetActive(true); + IPC::SetActive(true); } void EmuThread::emuStop() @@ -738,6 +741,7 @@ void EmuThread::emuStop() AudioInOut::Disable(); IPC::SetActive(false); + IPC::SetActive(false); } void EmuThread::emuFrameStep() From b9a1a54304f630ad40a4354ff53c0204c0d80767 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 23 Mar 2023 22:09:51 +0100 Subject: [PATCH 38/81] base for forwarding input to clients --- src/frontend/qt_sdl/Netplay.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Netplay.h b/src/frontend/qt_sdl/Netplay.h index 13edde7e..c18eb635 100644 --- a/src/frontend/qt_sdl/Netplay.h +++ b/src/frontend/qt_sdl/Netplay.h @@ -137,6 +137,8 @@ u32 PlayerAddress(int id); void StartGame(); void StartLocal(); +void StartGame(); + void ProcessFrame(); void ProcessInput(); From 586b2d49849f0d7617412ea82dd6df65770d3629 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Mar 2023 23:21:38 +0200 Subject: [PATCH 39/81] blarg --- src/frontend/qt_sdl/Platform.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 04df5438..6c11a592 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -431,6 +431,8 @@ void Log(LogLevel level, const char* fmt, ...) { if (fmt == nullptr) return; + if (level <= LogLevel::Debug) + return; va_list args; va_start(args, fmt); From db09e0da9fb01db8dc71623a93a080abb6e64610 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:11:55 +0200 Subject: [PATCH 40/81] begin work on state sync --- src/frontend/qt_sdl/Netplay.cpp | 219 ++++++++++++++++++++++++++++++- src/frontend/qt_sdl/ROMManager.h | 1 + 2 files changed, 213 insertions(+), 7 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 12059664..56d54545 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -27,11 +27,13 @@ #include #include "NDS.h" +#include "NDSCart.h" #include "main.h" #include "IPC.h" #include "Netplay.h" #include "Input.h" #include "ROMManager.h" +#include "Config.h" #include "ui_NetplayStartHostDialog.h" #include "ui_NetplayStartClientDialog.h" @@ -202,6 +204,21 @@ struct InputFrame std::queue InputQueue; +enum +{ + Blob_CartROM = 0, + Blob_CartSRAM, + + Blob_MAX +}; + +const u32 kChunkSize = 0x10000; +u8 ChunkBuffer[0x10 + kChunkSize]; +u8* Blobs[Blob_MAX]; +u32 BlobLens[Blob_MAX]; +int CurBlobType; +u32 CurBlobLen; + bool Init() { @@ -215,6 +232,14 @@ bool Init() memset(Players, 0, sizeof(Players)); NumPlayers = 0; + for (int i = 0; i < Blob_MAX; i++) + { + Blobs[i] = nullptr; + BlobLens[i] = 0; + } + CurBlobType = -1; + CurBlobLen = 0; + if (enet_initialize() != 0) { printf("enet shat itself :(\n"); @@ -329,7 +354,15 @@ void StartClient(const char* playername, const char* host, int port) void StartMirror(const Player* player) { - MirrorHost = enet_host_create(nullptr, 1, 1, 0, 0); + for (int i = 0; i < Blob_MAX; i++) + { + Blobs[i] = nullptr; + BlobLens[i] = 0; + } + CurBlobType = -1; + CurBlobLen = 0; + + MirrorHost = enet_host_create(nullptr, 1, 2, 0, 0); if (!MirrorHost) { printf("mirror shat itself :(\n"); @@ -416,7 +449,7 @@ bool SpawnMirrorInstance(Player player) if (newmask == curmask) continue; newmask &= ~curmask; - for (int id = 0; id < 32; id++) + for (int id = 0; id < 16; id++) { if (newmask & (1 << id)) { @@ -431,8 +464,8 @@ bool SpawnMirrorInstance(Player player) // setup that instance printf("netplay: spawned mirror instance for player %d with ID %d, configuring\n", player.ID, newid); - std::string rompath = ROMManager::FullROMPath.join('|').toStdString(); - IPC::SendCommandStr(1< 0) + { + buf[0] = 0x02; + *(u32*)&buf[12] = 0; + + for (u32 pos = 0; pos < len; pos += kChunkSize) + { + u32 chunklen = kChunkSize; + if ((pos + chunklen) > len) + chunklen = len - pos; + + *(u32*)&buf[8] = pos; + memcpy(&buf[16], &data[pos], chunklen); + + ENetPacket* pkt = enet_packet_create(buf, 16+chunklen, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(MirrorHost, 1, pkt); + } + } + + buf[0] = 0x03; + + pkt = enet_packet_create(buf, 8, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(MirrorHost, 1, pkt); + + return true; +} + +void RecvBlobFromMirrorHost(ENetPacket* pkt) +{ + u8* buf = pkt->data; + if (buf[0] == 0x01) + { + if (CurBlobType != -1) return; + if (pkt->dataLength != 8) return; + + int type = buf[1]; + if (type > Blob_MAX) return; + + u32 len = *(u32*)&buf[4]; + if (len > 0x40000000) return; + + if (Blobs[type] != nullptr) return; + if (BlobLens[type] != 0) return; + + if (len) Blobs[type] = new u8[len]; + BlobLens[type] = len; + + CurBlobType = type; + CurBlobLen = len; + } + else if (buf[0] == 0x02) + { + if (CurBlobType < 0 || CurBlobType > Blob_MAX) return; + if (pkt->dataLength > (16+kChunkSize)) return; + + int type = buf[1]; + if (type != CurBlobType) return; + + u32 len = *(u32*)&buf[4]; + if (len != CurBlobLen) return; + + u32 pos = *(u32*)&buf[8]; + if (pos >= len) return; + if ((pos + (pkt->dataLength-16)) > len) return; + + u8* dst = Blobs[type]; + if (!dst) return; + if (BlobLens[type] != len) return; + + memcpy(&dst[pos], &buf[16], pkt->dataLength-16); + } + else if (buf[0] == 0x03) + { + if (CurBlobType < 0 || CurBlobType > Blob_MAX) return; + if (pkt->dataLength != 8) return; + + int type = buf[1]; + if (type != CurBlobType) return; + + u32 len = *(u32*)&buf[4]; + if (len != CurBlobLen) return; + + CurBlobType = -1; + CurBlobLen = 0; + } + else if (buf[0] == 0x04) + { + if (pkt->dataLength != 2) return; + + bool res = false; + + // reset + NDS::SetConsoleType(buf[1]); + NDS::EjectCart(); + NDS::Reset(); + //SetBatteryLevels(); + + if (Blobs[Blob_CartROM]) + { + res = NDS::LoadCart(Blobs[Blob_CartROM], BlobLens[Blob_CartROM], + Blobs[Blob_CartSRAM], BlobLens[Blob_CartSRAM]); + if (!res) + { + printf("!!!! FAIL!!\n"); + return; + } + } + + for (int i = 0; i < Blob_MAX; i++) + { + if (Blobs[i]) delete[] Blobs[i]; + Blobs[i] = nullptr; + BlobLens[i] = 0; + } + + if (res) + { + ROMManager::CartType = 0; + //ROMManager::NDSSave = new SaveManager(savname); + + //LoadCheats(); + } + + // TODO: load state!!!! + + StartLocal(); + } +} + +void SyncMirrorClients() +{ + SendBlobToMirrorClients(Blob_CartROM, NDSCart::CartROMSize, NDSCart::CartROM); + SendBlobToMirrorClients(Blob_CartSRAM, NDSCart::GetSaveMemoryLength(), NDSCart::GetSaveMemory()); + + // TODO: send initial state!! + + u8 data[2]; + data[0] = 0x04; + data[1] = (u8)Config::ConsoleType; + ENetPacket* pkt = enet_packet_create(&data, 2, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(MirrorHost, 1, pkt); +} + void StartGame() { if (!IsHost) @@ -454,13 +643,21 @@ void StartGame() SpawnMirrorInstance(Players[i]); } + SyncMirrorClients(); + // tell remote peers to start game u8 cmd[1] = {0x04}; ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(Host, 0, pkt); // tell other mirror instances to start the game - IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); + //IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); + + // TO START MIRROR CLIENT SHITO + // + // 1. NDS::Reset() + // 2. load ROM + // 3. load state // start game locally StartLocal(); @@ -609,7 +806,7 @@ void ProcessClient() 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); - MirrorHost = enet_host_create(&mirroraddr, 16, 1, 0, 0); + MirrorHost = enet_host_create(&mirroraddr, 16, 2, 0, 0); if (!MirrorHost) { printf("mirror host shat itself :(\n"); @@ -650,9 +847,11 @@ printf("client mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr if (i != MyPlayer.ID) SpawnMirrorInstance(Players[i]); } + + SyncMirrorClients(); printf("bourf\n"); // tell other mirror instances to start the game - IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); + //IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); printf("birf\n"); // start game locally StartLocal(); @@ -688,6 +887,7 @@ void ProcessMirrorHost() break; case ENET_EVENT_TYPE_RECEIVE: + if (event.channelID == 0) { if (event.packet->dataLength != 4) break; /*u8* data = (u8*)event.packet->data; @@ -771,6 +971,7 @@ void ProcessMirrorClient() break; case ENET_EVENT_TYPE_RECEIVE: + if (event.channelID == 0) { if (event.packet->dataLength != sizeof(InputFrame)) break; @@ -796,6 +997,10 @@ printf("mirror client lag notify: %d\n", lag); enet_host_flush(MirrorHost); } } + else if (event.channelID == 1) + { + RecvBlobFromMirrorHost(event.packet); + } break; } diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 99b57ba6..d4b06ad4 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -30,6 +30,7 @@ namespace ROMManager { extern QStringList FullROMPath; +extern int CartType; extern SaveManager* NDSSave; extern SaveManager* GBASave; From 109b6b85969303fc684b6bae1062ad0f8350b06d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:22:21 +0200 Subject: [PATCH 41/81] not working, huh --- src/frontend/qt_sdl/Netplay.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 56d54545..0e1fb1f6 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -515,7 +515,7 @@ bool SendBlobToMirrorClients(int type, u32 len, u8* data) void RecvBlobFromMirrorHost(ENetPacket* pkt) { - u8* buf = pkt->data; + u8* buf = pkt->data;printf("[MIRROR CLIENT] blob %02X\n", buf[0]); if (buf[0] == 0x01) { if (CurBlobType != -1) return; @@ -610,7 +610,7 @@ void RecvBlobFromMirrorHost(ENetPacket* pkt) } // TODO: load state!!!! - +printf("[MIRROR CLIENT] start\n"); StartLocal(); } } @@ -627,6 +627,8 @@ void SyncMirrorClients() data[1] = (u8)Config::ConsoleType; ENetPacket* pkt = enet_packet_create(&data, 2, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); + + printf("[MIRROR HOST] clients synced\n"); } void StartGame() From 3e49fd1cfb170c956920f4a79b26e90d83c3c3ff Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:30:08 +0200 Subject: [PATCH 42/81] fuck --- src/frontend/qt_sdl/Netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 0e1fb1f6..bfecf3ca 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -286,7 +286,7 @@ void StartHost(const char* playername, int port) mirroraddr.host = ENET_HOST_ANY; mirroraddr.port = port + 1; printf("host mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr.port); - MirrorHost = enet_host_create(&mirroraddr, 16, 1, 0, 0); + MirrorHost = enet_host_create(&mirroraddr, 16, 2, 0, 0); if (!MirrorHost) { printf("mirror host shat itself :(\n"); @@ -515,7 +515,7 @@ bool SendBlobToMirrorClients(int type, u32 len, u8* data) void RecvBlobFromMirrorHost(ENetPacket* pkt) { - u8* buf = pkt->data;printf("[MIRROR CLIENT] blob %02X\n", buf[0]); + u8* buf = pkt->data; if (buf[0] == 0x01) { if (CurBlobType != -1) return; From 907ec318b39b3c1e059d3fb2b56a2a5f7d4afd20 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:35:34 +0200 Subject: [PATCH 43/81] d --- src/frontend/qt_sdl/Netplay.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index bfecf3ca..bd1e5c77 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -617,6 +617,8 @@ printf("[MIRROR CLIENT] start\n"); void SyncMirrorClients() { + printf("[MIRROR HOST] syncing clients\n"); + SendBlobToMirrorClients(Blob_CartROM, NDSCart::CartROMSize, NDSCart::CartROM); SendBlobToMirrorClients(Blob_CartSRAM, NDSCart::GetSaveMemoryLength(), NDSCart::GetSaveMemory()); @@ -877,14 +879,14 @@ void ProcessMirrorHost() switch (event.type) { case ENET_EVENT_TYPE_CONNECT: - printf("schmz\n"); + printf("[MIRROR HOST] mirror client connected\n"); event.peer->data = (void*)0; break; case ENET_EVENT_TYPE_DISCONNECT: { // TODO - printf("shmtt\n"); + printf("[MIRROR HOST] mirror client disconnected\n"); } break; From 4332370a90908a602483637c9a77bd5fb4350e85 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:43:57 +0200 Subject: [PATCH 44/81] maybe it'll work better this way (go eat, Arisotura) --- src/frontend/qt_sdl/Netplay.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index bd1e5c77..c155a478 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -194,6 +194,8 @@ Player MyPlayer; u32 HostAddress; bool Lag; +int NumMirrorClients; + struct InputFrame { u32 FrameNum; @@ -232,6 +234,8 @@ bool Init() memset(Players, 0, sizeof(Players)); NumPlayers = 0; + NumMirrorClients = 0; + for (int i = 0; i < Blob_MAX; i++) { Blobs[i] = nullptr; @@ -282,6 +286,8 @@ void StartHost(const char* playername, int port) HostAddress = 0x0100007F; + NumMirrorClients = 0; + ENetAddress mirroraddr; mirroraddr.host = ENET_HOST_ANY; mirroraddr.port = port + 1; @@ -647,7 +653,7 @@ void StartGame() SpawnMirrorInstance(Players[i]); } - SyncMirrorClients(); + //SyncMirrorClients(); // tell remote peers to start game u8 cmd[1] = {0x04}; @@ -664,7 +670,7 @@ void StartGame() // 3. load state // start game locally - StartLocal(); + //StartLocal(); } void StartLocal() @@ -805,6 +811,8 @@ void ProcessClient() { if (event.packet->dataLength != 2) break; + NumMirrorClients = 0; + // create mirror host ENetAddress mirroraddr; mirroraddr.host = ENET_HOST_ANY; @@ -852,13 +860,13 @@ printf("client mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr SpawnMirrorInstance(Players[i]); } - SyncMirrorClients(); + //SyncMirrorClients(); printf("bourf\n"); // tell other mirror instances to start the game //IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr); printf("birf\n"); // start game locally - StartLocal(); + //StartLocal(); } break; } @@ -880,13 +888,26 @@ void ProcessMirrorHost() { case ENET_EVENT_TYPE_CONNECT: printf("[MIRROR HOST] mirror client connected\n"); + NumMirrorClients++; event.peer->data = (void*)0; + + if (NumMirrorClients > NumPlayers) + { + printf("??????\n"); + } + else if (NumMirrorClients == NumPlayers) + { + // all mirror clients are connected, we're ready to go + SyncMirrorClients(); + StartLocal(); + } break; case ENET_EVENT_TYPE_DISCONNECT: { // TODO printf("[MIRROR HOST] mirror client disconnected\n"); + NumMirrorClients--; } break; From d82cbee7280fd6eefed8d5eddafd640d3869d5ae Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:47:30 +0200 Subject: [PATCH 45/81] yeah, Arisotura, you really need to eat --- src/frontend/qt_sdl/Netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index c155a478..d3462ae6 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -891,11 +891,11 @@ void ProcessMirrorHost() NumMirrorClients++; event.peer->data = (void*)0; - if (NumMirrorClients > NumPlayers) + if (NumMirrorClients >= NumPlayers) { printf("??????\n"); } - else if (NumMirrorClients == NumPlayers) + else if (NumMirrorClients == (NumPlayers-1)) { // all mirror clients are connected, we're ready to go SyncMirrorClients(); From 65228af642ba556a906efe3e9b0677886c46352e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 21:52:43 +0200 Subject: [PATCH 46/81] sdfgdfgsdfg --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index d3462ae6..642029b7 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -995,7 +995,7 @@ void ProcessMirrorClient() } break; - case ENET_EVENT_TYPE_RECEIVE: + case ENET_EVENT_TYPE_RECEIVE:printf("RX %d %d\n", event.channelID, event.packet->dataLength); if (event.channelID == 0) { if (event.packet->dataLength != sizeof(InputFrame)) break; From 409695c3cfad4db04e908e019b2b38b9c9fddc22 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:00:01 +0200 Subject: [PATCH 47/81] Arisotura you're fucking stupid --- src/frontend/qt_sdl/Netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 642029b7..32ceeda9 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -381,7 +381,7 @@ void StartMirror(const Player* player) addr.host = player->Address; addr.port = 8064+1 + player->ID; // FIXME!!!!!!!!!! printf("mirror client connecting to %08X:%d\n", addr.host, addr.port); - ENetPeer* peer = enet_host_connect(MirrorHost, &addr, 1, 0); + ENetPeer* peer = enet_host_connect(MirrorHost, &addr, 2, 0); if (!peer) { printf("connect shat itself :(\n"); @@ -995,7 +995,7 @@ void ProcessMirrorClient() } break; - case ENET_EVENT_TYPE_RECEIVE:printf("RX %d %d\n", event.channelID, event.packet->dataLength); + case ENET_EVENT_TYPE_RECEIVE: if (event.channelID == 0) { if (event.packet->dataLength != sizeof(InputFrame)) break; From 9438a87e6abbb3f67c7db6250dbc83f43b1f2999 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:05:07 +0200 Subject: [PATCH 48/81] HDJHSOKDSD --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 32ceeda9..7231528a 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -995,7 +995,7 @@ void ProcessMirrorClient() } break; - case ENET_EVENT_TYPE_RECEIVE: + case ENET_EVENT_TYPE_RECEIVE:printf("RX %d %d\n", event.channelID, event.packet->dataLength); if (event.channelID == 0) { if (event.packet->dataLength != sizeof(InputFrame)) break; From cd875526e14a65c7169bc326e954c3e48565dd57 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:15:26 +0200 Subject: [PATCH 49/81] maybe this will work better now --- src/frontend/qt_sdl/Netplay.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 7231528a..f0b0236c 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -541,6 +541,19 @@ void RecvBlobFromMirrorHost(ENetPacket* pkt) CurBlobType = type; CurBlobLen = len; + + ENetEvent evt; + while (enet_host_service(MirrorHost, &evt, 5000) > 0) + { + if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) + { + RecvBlobFromMirrorHost(evt.packet); + if (evt.packet->dataLength >= 1 && evt.packet->data[0] == 0x03) + break; + } + else + break; + } } else if (buf[0] == 0x02) { @@ -995,7 +1008,7 @@ void ProcessMirrorClient() } break; - case ENET_EVENT_TYPE_RECEIVE:printf("RX %d %d\n", event.channelID, event.packet->dataLength); + case ENET_EVENT_TYPE_RECEIVE://printf("RX %d %d\n", event.channelID, event.packet->dataLength); if (event.channelID == 0) { if (event.packet->dataLength != sizeof(InputFrame)) break; From c565fbf82955372fdbd40a83651920a0f37077e3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:28:33 +0200 Subject: [PATCH 50/81] more proper sync --- src/frontend/qt_sdl/Netplay.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index f0b0236c..381d1689 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -519,7 +519,7 @@ bool SendBlobToMirrorClients(int type, u32 len, u8* data) return true; } -void RecvBlobFromMirrorHost(ENetPacket* pkt) +void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt) { u8* buf = pkt->data; if (buf[0] == 0x01) @@ -629,7 +629,13 @@ void RecvBlobFromMirrorHost(ENetPacket* pkt) } // TODO: load state!!!! -printf("[MIRROR CLIENT] start\n"); + + ENetPacket* resp = enet_packet_create(buf, 1, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(peer, 1, resp); + } + else if (buf[0] == 0x05) + { + printf("[MIRROR CLIENT] start\n"); StartLocal(); } } @@ -649,6 +655,26 @@ void SyncMirrorClients() ENetPacket* pkt = enet_packet_create(&data, 2, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); + // wait for all clients to have caught up + int ngood = 0; + ENetEvent evt; + while (enet_host_service(MirrorHost, &evt, 5000) > 0) + { + if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) + { + if (evt.packet->dataLength == 1 && evt.packet->data[0] == 0x04) + ngood++; + } + else + break; + + if (ngood >= (NumPlayers-1)) + break; + } + + if (ngood != (NumPlayers-1)) + printf("!!! BAD!! %d %d\n", ngood, NumPlayers); + printf("[MIRROR HOST] clients synced\n"); } @@ -1037,7 +1063,7 @@ printf("mirror client lag notify: %d\n", lag); } else if (event.channelID == 1) { - RecvBlobFromMirrorHost(event.packet); + RecvBlobFromMirrorHost(event.peer, event.packet); } break; } From 36a62c9624d4ee838f602c0f6ae0013e8d324479 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:28:58 +0200 Subject: [PATCH 51/81] blarg! --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 381d1689..886a3ce8 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -547,7 +547,7 @@ void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt) { if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) { - RecvBlobFromMirrorHost(evt.packet); + RecvBlobFromMirrorHost(evt.peer, evt.packet); if (evt.packet->dataLength >= 1 && evt.packet->data[0] == 0x03) break; } From c7106b870c493d87d09f4c94c936459d987389ea Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:48:37 +0200 Subject: [PATCH 52/81] b0rp --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 886a3ce8..5fe1d824 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -659,7 +659,7 @@ void SyncMirrorClients() int ngood = 0; ENetEvent evt; while (enet_host_service(MirrorHost, &evt, 5000) > 0) - { + {printf("EVENT %d CH %d\n", evt.type, evt.channelID); if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) { if (evt.packet->dataLength == 1 && evt.packet->data[0] == 0x04) From 4c3bd07f0f7fcc2efed77affba9eedce462d3a34 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:54:29 +0200 Subject: [PATCH 53/81] sdfa. --- src/frontend/qt_sdl/Netplay.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 5fe1d824..dde344fa 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -535,7 +535,7 @@ void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt) if (Blobs[type] != nullptr) return; if (BlobLens[type] != 0) return; - +printf("[MC] start blob type=%d len=%d\n", type, len); if (len) Blobs[type] = new u8[len]; BlobLens[type] = len; @@ -586,7 +586,7 @@ void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt) u32 len = *(u32*)&buf[4]; if (len != CurBlobLen) return; - +printf("[MC] finish blob type=%d len=%d\n", type, len); CurBlobType = -1; CurBlobLen = 0; } @@ -629,7 +629,7 @@ void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt) } // TODO: load state!!!! - +printf("[MC] good\n"); ENetPacket* resp = enet_packet_create(buf, 1, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(peer, 1, resp); } From 8a68dd36ce7d90b31a35d87a1e8cc0a2f9fcf959 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 22:59:45 +0200 Subject: [PATCH 54/81] how 'bout now? --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index dde344fa..357ded08 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -658,7 +658,7 @@ void SyncMirrorClients() // wait for all clients to have caught up int ngood = 0; ENetEvent evt; - while (enet_host_service(MirrorHost, &evt, 5000) > 0) + while (enet_host_service(MirrorHost, &evt, 60000) > 0) {printf("EVENT %d CH %d\n", evt.type, evt.channelID); if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) { From 6ed77df24b76926f8731faf9b62489e86fc0a2e5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 23:07:17 +0200 Subject: [PATCH 55/81] maybe actually starting the mirror clients would yield better results, Arisotura --- src/frontend/qt_sdl/Netplay.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 357ded08..1abb31f8 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -676,6 +676,14 @@ void SyncMirrorClients() printf("!!! BAD!! %d %d\n", ngood, NumPlayers); printf("[MIRROR HOST] clients synced\n"); + + // start + + data[0] = 0x05; + pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(MirrorHost, 1, pkt); + + StartLocal(); } void StartGame() @@ -938,7 +946,7 @@ void ProcessMirrorHost() { // all mirror clients are connected, we're ready to go SyncMirrorClients(); - StartLocal(); + //StartLocal(); } break; From de48816f78875b778b5867da421f3e2d28eb5623 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 23:22:18 +0200 Subject: [PATCH 56/81] super ugly attempt at state sharing --- src/frontend/qt_sdl/Netplay.cpp | 41 +++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 1abb31f8..f2b830fe 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -34,6 +34,7 @@ #include "Input.h" #include "ROMManager.h" #include "Config.h" +#include "Savestate.h" #include "ui_NetplayStartHostDialog.h" #include "ui_NetplayStartClientDialog.h" @@ -210,6 +211,7 @@ enum { Blob_CartROM = 0, Blob_CartSRAM, + Blob_InitState, Blob_MAX }; @@ -613,13 +615,6 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); } } - for (int i = 0; i < Blob_MAX; i++) - { - if (Blobs[i]) delete[] Blobs[i]; - Blobs[i] = nullptr; - BlobLens[i] = 0; - } - if (res) { ROMManager::CartType = 0; @@ -628,7 +623,22 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); //LoadCheats(); } - // TODO: load state!!!! + // load initial state + // TODO: terrible hack!! + FILE* f = fopen("netplay2.mln", "wb"); + fwrite(Blobs[Blob_InitState], BlobLens[Blob_InitState], 1, f); + fclose(f); + Savestate* state = new Savestate("netplay2.mln", false); + NDS::DoSavestate(state); + delete state; + + for (int i = 0; i < Blob_MAX; i++) + { + if (Blobs[i]) delete[] Blobs[i]; + Blobs[i] = nullptr; + BlobLens[i] = 0; + } + printf("[MC] good\n"); ENetPacket* resp = enet_packet_create(buf, 1, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(peer, 1, resp); @@ -647,7 +657,20 @@ void SyncMirrorClients() SendBlobToMirrorClients(Blob_CartROM, NDSCart::CartROMSize, NDSCart::CartROM); SendBlobToMirrorClients(Blob_CartSRAM, NDSCart::GetSaveMemoryLength(), NDSCart::GetSaveMemory()); - // TODO: send initial state!! + // send initial state + // TODO: this is a terrible hack! + Savestate* state = new Savestate("netplay.mln", true); + NDS::DoSavestate(state); + delete state; + FILE* f = fopen("netplay.mln", "rb"); + fseek(f, 0, SEEK_END); + u32 flen = ftell(f); + fseek(f, 0, SEEK_SET); + u8* statebuf = new u8[flen]; + fread(statebuf, flen, 1, f); + fclose(f); + SendBlobToMirrorClients(Blob_InitState, flen, statebuf); + delete[] statebuf; u8 data[2]; data[0] = 0x04; From 02f42df8af3e453d6400910f7ce0467589eb5974 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 23:30:54 +0200 Subject: [PATCH 57/81] blorp --- src/frontend/qt_sdl/Netplay.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index f2b830fe..52992231 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -659,17 +659,22 @@ void SyncMirrorClients() // send initial state // TODO: this is a terrible hack! + printf("[MH] state start\n"); Savestate* state = new Savestate("netplay.mln", true); NDS::DoSavestate(state); delete state; + printf("[MH] state taken\n"); FILE* f = fopen("netplay.mln", "rb"); + printf("[MH] state=%d\n", f?1:0); fseek(f, 0, SEEK_END); u32 flen = ftell(f); fseek(f, 0, SEEK_SET); u8* statebuf = new u8[flen]; fread(statebuf, flen, 1, f); fclose(f); + printf("[MH] state read, len=%d\n", flen); SendBlobToMirrorClients(Blob_InitState, flen, statebuf); + printf("[MH] state sent\n"); delete[] statebuf; u8 data[2]; From cfc4398af1e7dfeb4c8e01e1ff172795128df357 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 23:34:20 +0200 Subject: [PATCH 58/81] pfft --- src/frontend/qt_sdl/Netplay.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 52992231..dafafeba 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -35,6 +35,7 @@ #include "ROMManager.h" #include "Config.h" #include "Savestate.h" +#include "Platform.h" #include "ui_NetplayStartHostDialog.h" #include "ui_NetplayStartClientDialog.h" @@ -625,7 +626,7 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); // load initial state // TODO: terrible hack!! - FILE* f = fopen("netplay2.mln", "wb"); + FILE* f = Platform::OpenFile("netplay2.mln", "wb"); fwrite(Blobs[Blob_InitState], BlobLens[Blob_InitState], 1, f); fclose(f); Savestate* state = new Savestate("netplay2.mln", false); @@ -664,7 +665,7 @@ void SyncMirrorClients() NDS::DoSavestate(state); delete state; printf("[MH] state taken\n"); - FILE* f = fopen("netplay.mln", "rb"); + FILE* f = Platform::OpenFile("netplay.mln", "rb"); printf("[MH] state=%d\n", f?1:0); fseek(f, 0, SEEK_END); u32 flen = ftell(f); From 4dfd635e85e88dd1d1facca0806918c1837c2364 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 23:37:56 +0200 Subject: [PATCH 59/81] AAAAAAAAAAAAAAAAA --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index dafafeba..856f51de 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -665,7 +665,7 @@ void SyncMirrorClients() NDS::DoSavestate(state); delete state; printf("[MH] state taken\n"); - FILE* f = Platform::OpenFile("netplay.mln", "rb"); + FILE* f = Platform::OpenLocalFile("netplay.mln", "rb"); printf("[MH] state=%d\n", f?1:0); fseek(f, 0, SEEK_END); u32 flen = ftell(f); From 399a892c4b44a0503ebdbb3cdd5fe41b817d5551 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 5 Apr 2023 23:45:05 +0200 Subject: [PATCH 60/81] welp --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 856f51de..e9da64fe 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -687,7 +687,7 @@ void SyncMirrorClients() // wait for all clients to have caught up int ngood = 0; ENetEvent evt; - while (enet_host_service(MirrorHost, &evt, 60000) > 0) + while (enet_host_service(MirrorHost, &evt, 120000) > 0) {printf("EVENT %d CH %d\n", evt.type, evt.channelID); if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) { From ba5ecc46c330262b9b10cf7348459502f6311a85 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 18:55:16 +0200 Subject: [PATCH 61/81] please remember to flush --- src/frontend/qt_sdl/Netplay.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index e9da64fe..c5ef15d6 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -511,6 +511,7 @@ bool SendBlobToMirrorClients(int type, u32 len, u8* data) ENetPacket* pkt = enet_packet_create(buf, 16+chunklen, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); + enet_host_flush(MirrorHost); } } @@ -683,6 +684,7 @@ void SyncMirrorClients() data[1] = (u8)Config::ConsoleType; ENetPacket* pkt = enet_packet_create(&data, 2, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); + enet_host_flush(MirrorHost); // wait for all clients to have caught up int ngood = 0; @@ -711,6 +713,7 @@ void SyncMirrorClients() data[0] = 0x05; pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); + enet_host_flush(MirrorHost); StartLocal(); } From cf901207e5c3e63ccb3560c8295a9c1b615d7fbd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 19:09:22 +0200 Subject: [PATCH 62/81] bleh --- src/frontend/qt_sdl/Netplay.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index c5ef15d6..b1d1ac61 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -553,10 +553,16 @@ printf("[MC] start blob type=%d len=%d\n", type, len); { RecvBlobFromMirrorHost(evt.peer, evt.packet); if (evt.packet->dataLength >= 1 && evt.packet->data[0] == 0x03) + { + printf("[MC] blob done while in fast recv loop\n"); break; + } } else + { + printf("[MC] fast recv loop aborted because evt %d ch %d\n", evt.type, evt.channelID); break; + } } } else if (buf[0] == 0x02) @@ -641,7 +647,7 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); BlobLens[i] = 0; } -printf("[MC] good\n"); +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); } @@ -665,7 +671,7 @@ void SyncMirrorClients() Savestate* state = new Savestate("netplay.mln", true); NDS::DoSavestate(state); delete state; - printf("[MH] state taken\n"); + printf("[MH] state taken: PC=%08X/%08X\n", NDS::GetPC(0), NDS::GetPC(1)); FILE* f = Platform::OpenLocalFile("netplay.mln", "rb"); printf("[MH] state=%d\n", f?1:0); fseek(f, 0, SEEK_END); From ae531d343c1080a08f56da01ce6c14056b532ec3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 19:20:33 +0200 Subject: [PATCH 63/81] gurgl --- src/frontend/qt_sdl/Netplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index b1d1ac61..4765f59b 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -583,7 +583,7 @@ printf("[MC] start blob type=%d len=%d\n", type, len); u8* dst = Blobs[type]; if (!dst) return; if (BlobLens[type] != len) return; - +printf("[MC] recv blob data, type=%d pos=%08X len=%08X data=%08X\n", type, pos, len, pkt->dataLength-16); memcpy(&dst[pos], &buf[16], pkt->dataLength-16); } else if (buf[0] == 0x03) @@ -695,7 +695,7 @@ void SyncMirrorClients() // wait for all clients to have caught up int ngood = 0; ENetEvent evt; - while (enet_host_service(MirrorHost, &evt, 120000) > 0) + while (enet_host_service(MirrorHost, &evt, 300000) > 0) {printf("EVENT %d CH %d\n", evt.type, evt.channelID); if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1) { From d02c677af5599b8605ccd58b9a0dd59af629bf36 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 19:44:00 +0200 Subject: [PATCH 64/81] add BIOS and firmware to savestates --- src/NDS.cpp | 4 ++-- src/SPI.cpp | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 2f1759fa..a2329bc6 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -858,8 +858,8 @@ bool DoSavestate(Savestate* file) file->VarArray(SharedWRAM, SharedWRAMSize); file->VarArray(ARM7WRAM, ARM7WRAMSize); - //file->VarArray(ARM9BIOS, 0x1000); - //file->VarArray(ARM7BIOS, 0x4000); + file->VarArray(ARM9BIOS, 0x1000); + file->VarArray(ARM7BIOS, 0x4000); file->VarArray(ExMemCnt, 2*sizeof(u16)); file->VarArray(ROMSeed0, 2*8); diff --git a/src/SPI.cpp b/src/SPI.cpp index bbfacbf2..09703ecd 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -458,6 +458,28 @@ void DoSavestate(Savestate* file) // CHECKME/TODO: trust the firmware to stay the same????? // embedding the whole firmware in the savestate would be derpo tho?? + file->Var32(&FirmwareLength); + if (file->Saving) + { + file->VarArray(Firmware, FirmwareLength); + } + else + { + if (Firmware) delete[] Firmware; + Firmware = new u8[FirmwareLength]; + file->VarArray(Firmware, FirmwareLength); + + FirmwareMask = FirmwareLength - 1; + + u32 userdata = 0x7FE00 & FirmwareMask; + if (*(u16*)&Firmware[userdata+0x170] == ((*(u16*)&Firmware[userdata+0x70] + 1) & 0x7F)) + { + if (VerifyCRC16(0xFFFF, userdata+0x100, 0x70, userdata+0x172)) + userdata += 0x100; + } + + UserSettings = userdata; + } file->Var32(&Hold); file->Var8(&CurCmd); From ee7123651b7b54572db59d9d8ff766e641744065 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 21:20:06 +0200 Subject: [PATCH 65/81] fix bug in savestate loading --- src/NDS.cpp | 4 +++- src/frontend/qt_sdl/Netplay.cpp | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index a2329bc6..9d8772a4 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -935,7 +935,9 @@ bool DoSavestate(Savestate* file) { // 'dept of redundancy dept' // but we do need to update the mappings - MapSharedWRAM(WRAMCnt); + u8 wramcnt = WRAMCnt; + WRAMCnt ^= 0xFF; + MapSharedWRAM(wramcnt); InitTimings(); SetGBASlotTimings(); diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 4765f59b..dbf4b2ab 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -615,7 +615,7 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); if (Blobs[Blob_CartROM]) { res = NDS::LoadCart(Blobs[Blob_CartROM], BlobLens[Blob_CartROM], - Blobs[Blob_CartSRAM], BlobLens[Blob_CartSRAM]); + Blobs[Blob_CartSRAM], BlobLens[Blob_CartSRAM]); if (!res) { printf("!!!! FAIL!!\n"); @@ -647,6 +647,10 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); BlobLens[i] = 0; } + /*Savestate* zorp = new Savestate("netplay3.mln", true); + NDS::DoSavestate(zorp); + delete zorp;*/ + 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); From 065415613b2b3fe609a0e2a9b15782126f1b076c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 22:01:13 +0200 Subject: [PATCH 66/81] attempt --- src/frontend/qt_sdl/Netplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index dbf4b2ab..494dc746 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -217,7 +217,7 @@ enum Blob_MAX }; -const u32 kChunkSize = 0x10000; +const u32 kChunkSize = 0x1000000; u8 ChunkBuffer[0x10 + kChunkSize]; u8* Blobs[Blob_MAX]; u32 BlobLens[Blob_MAX]; From 60d88badb2efbe81c0cfafdfd2c0c718beb9b746 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 6 Apr 2023 23:24:45 +0200 Subject: [PATCH 67/81] nah --- src/frontend/qt_sdl/Netplay.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 494dc746..4cfd1c29 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -217,7 +217,7 @@ enum Blob_MAX }; -const u32 kChunkSize = 0x1000000; +const u32 kChunkSize = 0x10000; u8 ChunkBuffer[0x10 + kChunkSize]; u8* Blobs[Blob_MAX]; u32 BlobLens[Blob_MAX]; @@ -511,7 +511,7 @@ bool SendBlobToMirrorClients(int type, u32 len, u8* data) ENetPacket* pkt = enet_packet_create(buf, 16+chunklen, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); - enet_host_flush(MirrorHost); + //enet_host_flush(MirrorHost); } } @@ -694,7 +694,7 @@ void SyncMirrorClients() data[1] = (u8)Config::ConsoleType; ENetPacket* pkt = enet_packet_create(&data, 2, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); - enet_host_flush(MirrorHost); + //enet_host_flush(MirrorHost); // wait for all clients to have caught up int ngood = 0; @@ -723,7 +723,7 @@ void SyncMirrorClients() data[0] = 0x05; pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 1, pkt); - enet_host_flush(MirrorHost); + //enet_host_flush(MirrorHost); StartLocal(); } @@ -1108,7 +1108,7 @@ printf("mirror client lag notify: %d\n", lag); { ENetPacket* pkt = enet_packet_create(&NDS::NumFrames, 4, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, pkt); - enet_host_flush(MirrorHost); + //enet_host_flush(MirrorHost); } } else if (event.channelID == 1) @@ -1178,7 +1178,7 @@ void ProcessInput() memcpy(cmd, &frame, sizeof(InputFrame)); ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(MirrorHost, 0, pkt); - enet_host_flush(MirrorHost); + //enet_host_flush(MirrorHost); } if (InputQueue.empty()) From 86bf219450218139e9151d096e9881b4baa9cca6 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 2 Sep 2023 21:06:04 +0200 Subject: [PATCH 68/81] lay base for LAN --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/IPC.cpp | 3 - src/frontend/qt_sdl/LAN.cpp | 116 +++++++++++++++++++++++++++++ src/frontend/qt_sdl/LAN.h | 50 +++++++++++++ src/frontend/qt_sdl/Netplay.cpp | 8 +- 5 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 src/frontend/qt_sdl/LAN.cpp create mode 100644 src/frontend/qt_sdl/LAN.h diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index cc9244d5..dbc3c98c 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES_QT_SDL TitleManagerDialog.cpp Input.cpp IPC.cpp + LAN.cpp LAN_PCap.cpp LAN_Socket.cpp Netplay.cpp diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index 1b0abf06..85766417 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -98,9 +98,6 @@ const u32 kMPReplyEnd = kBufferSize; int MPRecvTimeout; int MPLastHostID; -int MPRecvTimeout; -int MPLastHostID; - // we need to come up with our own abstraction layer for named semaphores // because QSystemSemaphore doesn't support waiting with a timeout // and, as such, is unsuitable to our needs diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp new file mode 100644 index 00000000..49646952 --- /dev/null +++ b/src/frontend/qt_sdl/LAN.cpp @@ -0,0 +1,116 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include + +// + +#include "LAN.h" +#include "Config.h" +//#include "main.h" +// + + +//extern EmuThread* emuThread; + + +namespace LAN +{ + +// + + +bool Init() +{ + // + + return true; +} + +void DeInit() +{ + // +} + + +void SetMPRecvTimeout(int timeout) +{ + //MPRecvTimeout = timeout; +} + +void MPBegin() +{ + // +} + +void MPEnd() +{ + // +} + +void SetActive(bool active) +{ + // +} + +u16 GetInstanceBitmask() +{ + // + return 0; +} + + +int SendMPPacket(u8* packet, int len, u64 timestamp) +{ + return 0; +} + +int RecvMPPacket(u8* packet, u64* timestamp) +{ + return 0; +} + + +int SendMPCmd(u8* packet, int len, u64 timestamp) +{ + return 0; +} + +int SendMPReply(u8* packet, int len, u64 timestamp, u16 aid) +{ + return 0; +} + +int SendMPAck(u8* packet, int len, u64 timestamp) +{ + return 0; +} + +int RecvMPHostPacket(u8* packet, u64* timestamp) +{ + return 0; +} + +u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) +{ + return 0; +} + +} diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h new file mode 100644 index 00000000..bc19e662 --- /dev/null +++ b/src/frontend/qt_sdl/LAN.h @@ -0,0 +1,50 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef LAN_H +#define LAN_H + +#include + +#include "types.h" + +namespace LAN +{ + +bool Init(); +void DeInit(); + +void SetMPRecvTimeout(int timeout); +void MPBegin(); +void MPEnd(); + +void SetActive(bool active); + +u16 GetInstanceBitmask(); + +int SendMPPacket(u8* data, int len, u64 timestamp); +int RecvMPPacket(u8* data, u64* timestamp); +int SendMPCmd(u8* data, int len, u64 timestamp); +int SendMPReply(u8* data, int len, u64 timestamp, u16 aid); +int SendMPAck(u8* data, int len, u64 timestamp); +int RecvMPHostPacket(u8* data, u64* timestamp); +u16 RecvMPReplies(u8* data, u64 timestamp, u16 aidmask); + +} + +#endif // LAN_H diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index 4cfd1c29..b6c1511f 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -633,6 +633,7 @@ printf("[MC] finish blob type=%d len=%d\n", type, len); // 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); @@ -654,6 +655,7 @@ 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) { @@ -666,12 +668,12 @@ void SyncMirrorClients() { printf("[MIRROR HOST] syncing clients\n"); - SendBlobToMirrorClients(Blob_CartROM, NDSCart::CartROMSize, NDSCart::CartROM); + //SendBlobToMirrorClients(Blob_CartROM, NDSCart::CartROMSize, NDSCart::CartROM); SendBlobToMirrorClients(Blob_CartSRAM, NDSCart::GetSaveMemoryLength(), NDSCart::GetSaveMemory()); // send initial state // TODO: this is a terrible hack! - printf("[MH] state start\n"); + /*printf("[MH] state start\n"); Savestate* state = new Savestate("netplay.mln", true); NDS::DoSavestate(state); delete state; @@ -687,7 +689,7 @@ void SyncMirrorClients() printf("[MH] state read, len=%d\n", flen); SendBlobToMirrorClients(Blob_InitState, flen, statebuf); printf("[MH] state sent\n"); - delete[] statebuf; + delete[] statebuf;*/ u8 data[2]; data[0] = 0x04; From ca4d745e71b2bfb9ef89e90fbaafe4e818c99840 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 09:59:02 +0200 Subject: [PATCH 69/81] more base work for LAN --- src/frontend/qt_sdl/LAN.cpp | 147 +++++++++++++++++++- src/frontend/qt_sdl/LAN.h | 92 ++++++++++++ src/frontend/qt_sdl/LANDialog.ui | 31 +++++ src/frontend/qt_sdl/LANStartClientDialog.ui | 97 +++++++++++++ src/frontend/qt_sdl/LANStartHostDialog.ui | 97 +++++++++++++ src/frontend/qt_sdl/main.cpp | 19 +++ src/frontend/qt_sdl/main.h | 4 + 7 files changed, 483 insertions(+), 4 deletions(-) create mode 100644 src/frontend/qt_sdl/LANDialog.ui create mode 100644 src/frontend/qt_sdl/LANStartClientDialog.ui create mode 100644 src/frontend/qt_sdl/LANStartHostDialog.ui diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 49646952..d5e8ef9a 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -20,15 +20,154 @@ #include #include -// +#include + +#include #include "LAN.h" #include "Config.h" -//#include "main.h" -// +#include "main.h" + +#include "ui_LANStartHostDialog.h" +#include "ui_LANStartClientDialog.h" +#include "ui_LANDialog.h" -//extern EmuThread* emuThread; +extern EmuThread* emuThread; +LANDialog* lanDlg; + + +LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartHostDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + //ui->txtPort->setText("8064"); +} + +LANStartHostDialog::~LANStartHostDialog() +{ + delete ui; +} + +void LANStartHostDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + std::string player = ui->txtPlayerName->text().toStdString(); + //int port = ui->txtPort->text().toInt(); + + // TODO validate input!! + + lanDlg = LANDialog::openDlg(parentWidget()); + + //Netplay::StartHost(player.c_str(), port); + } + + QDialog::done(r); +} + + +LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANStartClientDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + //ui->txtPort->setText("8064"); +} + +LANStartClientDialog::~LANStartClientDialog() +{ + delete ui; +} + +void LANStartClientDialog::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!! + + lanDlg = LANDialog::openDlg(parentWidget()); + + //Netplay::StartClient(player.c_str(), host.c_str(), port); + } + + QDialog::done(r); +} + + +LANDialog::LANDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LANDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + QStandardItemModel* model = new QStandardItemModel(); + ui->tvPlayerList->setModel(model); + + connect(this, &LANDialog::sgUpdatePlayerList, this, &LANDialog::doUpdatePlayerList); +} + +LANDialog::~LANDialog() +{ + delete ui; +} + +void LANDialog::done(int r) +{ + // ??? + + QDialog::done(r); +} + +void LANDialog::updatePlayerList(LAN::Player* players, int num) +{ + emit sgUpdatePlayerList(players, num); +} + +void LANDialog::doUpdatePlayerList(LAN::Player* players, int num) +{ + QStandardItemModel* model = (QStandardItemModel*)ui->tvPlayerList->model(); + + model->clear(); + model->setRowCount(num); + + // TODO: remove IP column in final product + + const QStringList header = {"#", "Player", "Status", "Ping", "IP"}; + model->setHorizontalHeaderLabels(header); + + for (int i = 0; i < num; i++) + { + LAN::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")); + + char ip[32]; + 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)); + } +} namespace LAN diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index bc19e662..41f4df1b 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -20,9 +20,101 @@ #define LAN_H #include +#include #include "types.h" +namespace Ui +{ +class LANStartHostDialog; +class LANStartClientDialog; +class LANDialog; +} + +namespace LAN +{ +struct Player +{ + int ID; + char Name[32]; + int Status; // 0=no player 1=normal 2=host 3=connecting + u32 Address; +}; +} + +class LANStartHostDialog : public QDialog +{ + Q_OBJECT + +public: + explicit LANStartHostDialog(QWidget* parent); + ~LANStartHostDialog(); + + static LANStartHostDialog* openDlg(QWidget* parent) + { + LANStartHostDialog* dlg = new LANStartHostDialog(parent); + dlg->open(); + return dlg; + } + +private slots: + void done(int r); + +private: + Ui::LANStartHostDialog* ui; +}; + +class LANStartClientDialog : public QDialog +{ + Q_OBJECT + +public: + explicit LANStartClientDialog(QWidget* parent); + ~LANStartClientDialog(); + + static LANStartClientDialog* openDlg(QWidget* parent) + { + LANStartClientDialog* dlg = new LANStartClientDialog(parent); + dlg->open(); + return dlg; + } + +private slots: + void done(int r); + +private: + Ui::LANStartClientDialog* ui; +}; + +class LANDialog : public QDialog +{ + Q_OBJECT + +public: + explicit LANDialog(QWidget* parent); + ~LANDialog(); + + static LANDialog* openDlg(QWidget* parent) + { + LANDialog* dlg = new LANDialog(parent); + dlg->show(); + return dlg; + } + + void updatePlayerList(LAN::Player* players, int num); + +signals: + void sgUpdatePlayerList(LAN::Player* players, int num); + +private slots: + void done(int r); + + void doUpdatePlayerList(LAN::Player* players, int num); + +private: + Ui::LANDialog* ui; +}; + namespace LAN { diff --git a/src/frontend/qt_sdl/LANDialog.ui b/src/frontend/qt_sdl/LANDialog.ui new file mode 100644 index 00000000..e99000e4 --- /dev/null +++ b/src/frontend/qt_sdl/LANDialog.ui @@ -0,0 +1,31 @@ + + + LANDialog + + + + 0 + 0 + 522 + 391 + + + + LAN SHITO + + + + + + STATUS PLACEHOLDER + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/LANStartClientDialog.ui b/src/frontend/qt_sdl/LANStartClientDialog.ui new file mode 100644 index 00000000..8721bfe7 --- /dev/null +++ b/src/frontend/qt_sdl/LANStartClientDialog.ui @@ -0,0 +1,97 @@ + + + LANStartClientDialog + + + + 0 + 0 + 400 + 229 + + + + + 0 + 0 + + + + Join LAN game - melonDS + + + + QLayout::SetFixedSize + + + + + + + Player name: + + + + + + + + + + Host address: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + LANStartClientDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + LANStartClientDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/LANStartHostDialog.ui b/src/frontend/qt_sdl/LANStartHostDialog.ui new file mode 100644 index 00000000..0d6cd50c --- /dev/null +++ b/src/frontend/qt_sdl/LANStartHostDialog.ui @@ -0,0 +1,97 @@ + + + LANStartHostDialog + + + + 0 + 0 + 389 + 228 + + + + + 0 + 0 + + + + Host LAN game - melonDS + + + + QLayout::SetFixedSize + + + + + + + Player name: + + + + + + + + + + Number of players: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + LANStartHostDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + LANStartHostDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 1e290ff0..138e5ab2 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -88,6 +88,7 @@ #include "Wifi.h" #include "Platform.h" #include "IPC.h" +#include "LAN.h" #include "Netplay.h" #include "Config.h" #include "DSi_I2C.h" @@ -1584,6 +1585,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) submenu->addSeparator(); + actLANStartHost = submenu->addAction("Host LAN game"); + connect(actLANStartHost, &QAction::triggered, this, &MainWindow::onLANStartHost); + + actLANStartClient = submenu->addAction("Join LAN game"); + connect(actLANStartClient, &QAction::triggered, this, &MainWindow::onLANStartClient); + + submenu->addSeparator(); + actMPStartHost = submenu->addAction("NETPLAY HOST"); connect(actMPStartHost, &QAction::triggered, this, &MainWindow::onMPStartHost); @@ -2859,6 +2868,16 @@ void MainWindow::onMPNewInstance() newinst.startDetached(); } +void MainWindow::onLANStartHost() +{ + LANStartHostDialog::openDlg(this); +} + +void MainWindow::onLANStartClient() +{ + LANStartClientDialog::openDlg(this); +} + void MainWindow::onMPStartHost() { //Netplay::StartHost(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 2d6ddae7..93bcf6e0 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -311,6 +311,8 @@ private slots: void onRAMInfo(); void onOpenTitleManager(); void onMPNewInstance(); + void onLANStartHost(); + void onLANStartClient(); void onMPStartHost(); void onMPStartClient(); void onMPTest(); @@ -411,6 +413,8 @@ public: QAction* actRAMInfo; QAction* actTitleManager; QAction* actMPNewInstance; + QAction* actLANStartHost; + QAction* actLANStartClient; QAction* actMPStartHost; QAction* actMPStartClient; QAction* actMPTest; From 58691a08b2b021c6975113418e3556855780f7c7 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 12:59:24 +0200 Subject: [PATCH 70/81] get some connection infrastructure going... --- src/frontend/qt_sdl/LAN.cpp | 367 +++++++++++++++++++++++++++++++- src/frontend/qt_sdl/LAN.h | 7 + src/frontend/qt_sdl/Netplay.cpp | 6 +- src/frontend/qt_sdl/main.cpp | 7 +- 4 files changed, 373 insertions(+), 14 deletions(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index d5e8ef9a..e4c6cb92 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -42,7 +42,10 @@ LANStartHostDialog::LANStartHostDialog(QWidget* parent) : QDialog(parent), ui(ne ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - //ui->txtPort->setText("8064"); + // TODO: remember the last setting? so this doesn't suck massively + // we could also remember the player name (and auto-init it from the firmware name or whatever) + ui->sbNumPlayers->setRange(2, 16); + ui->sbNumPlayers->setValue(16); } LANStartHostDialog::~LANStartHostDialog() @@ -55,13 +58,13 @@ void LANStartHostDialog::done(int r) if (r == QDialog::Accepted) { std::string player = ui->txtPlayerName->text().toStdString(); - //int port = ui->txtPort->text().toInt(); + int numplayers = ui->sbNumPlayers->value(); // TODO validate input!! lanDlg = LANDialog::openDlg(parentWidget()); - //Netplay::StartHost(player.c_str(), port); + LAN::StartHost(player.c_str(), numplayers); } QDialog::done(r); @@ -72,8 +75,6 @@ LANStartClientDialog::LANStartClientDialog(QWidget* parent) : QDialog(parent), u { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - - //ui->txtPort->setText("8064"); } LANStartClientDialog::~LANStartClientDialog() @@ -87,13 +88,12 @@ void LANStartClientDialog::done(int r) { std::string player = ui->txtPlayerName->text().toStdString(); std::string host = ui->txtIPAddress->text().toStdString(); - //int port = ui->txtPort->text().toInt(); // TODO validate input!! lanDlg = LANDialog::openDlg(parentWidget()); - //Netplay::StartClient(player.c_str(), host.c_str(), port); + LAN::StartClient(player.c_str(), host.c_str()); } QDialog::done(r); @@ -173,19 +173,366 @@ void LANDialog::doUpdatePlayerList(LAN::Player* players, int num) namespace LAN { -// +const int kLANPort = 7064; + +bool Active; +bool IsHost; + +ENetHost* Host; +ENetPeer* RemotePeers[16]; + +Player Players[16]; +int NumPlayers; +int MaxPlayers; + +Player MyPlayer; +u32 HostAddress; +bool Lag; bool Init() { - // + Active = false; + IsHost = false; + Host = nullptr; + Lag = false; + memset(RemotePeers, 0, sizeof(RemotePeers)); + memset(Players, 0, sizeof(Players)); + NumPlayers = 0; + MaxPlayers = 0; + + // TODO we init enet here but also in Netplay + // that is redundant + if (enet_initialize() != 0) + { + printf("enet shat itself :(\n"); + return false; + } + + printf("enet init OK\n"); return true; } void DeInit() { - // + // TODO: cleanup resources properly!! + + enet_deinitialize(); +} + + +void StartHost(const char* playername, int numplayers) +{ + ENetAddress addr; + addr.host = ENET_HOST_ANY; + addr.port = kLANPort; + + Host = enet_host_create(&addr, 16, 1, 0, 0); + if (!Host) + { + // TODO handle this gracefully + 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; + player->Address = 0x0100007F; + NumPlayers = 1; + MaxPlayers = numplayers; + memcpy(&MyPlayer, player, sizeof(Player)); + + HostAddress = 0x0100007F; + + Active = true; + IsHost = true; + + lanDlg->updatePlayerList(Players, NumPlayers); +} + +void StartClient(const char* playername, const char* host) +{ + Host = enet_host_create(nullptr, 16, 1, 0, 0); + if (!Host) + { + // TODO handle this gracefully + printf("client shat itself :(\n"); + return; + } + + printf("client created, connecting (%s, %s:%d)\n", playername, host, kLANPort); + + ENetAddress addr; + enet_address_set_host(&addr, host); + addr.port = kLANPort; + ENetPeer* peer = enet_host_connect(Host, &addr, 1, 0); + if (!peer) + { + printf("connect shat itself :(\n"); + return; + } + + ENetEvent event; + bool conn = false; + if (enet_host_service(Host, &event, 5000) > 0) + { + if (event.type == ENET_EVENT_TYPE_CONNECT) + { + printf("connected!\n"); + conn = true; + } + } + + if (!conn) + { + printf("connection failed\n"); + enet_peer_reset(peer); + return; + } + + Player* player = &MyPlayer; + memset(player, 0, sizeof(Player)); + player->ID = 0; + strncpy(player->Name, playername, 31); + player->Status = 3; + + HostAddress = addr.host; + + Active = true; + IsHost = false; +} + + +void ProcessHost() +{ + if (!Host) return; + + ENetEvent event; + while (enet_host_service(Host, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + if ((NumPlayers >= MaxPlayers) || (NumPlayers >= 16)) + { + // game is full, reject connection + enet_peer_disconnect(event.peer, 0); + break; + } + + // 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[3]; + cmd[0] = 0x01; + cmd[1] = (u8)id; + cmd[2] = MaxPlayers; + ENetPacket* pkt = enet_packet_create(cmd, 3, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, pkt); + + Players[id].ID = id; + Players[id].Status = 3; + Players[id].Address = event.peer->address.host; + event.peer->data = &Players[id]; + NumPlayers++; + + RemotePeers[id] = event.peer; + } + else + { + // ??? + enet_peer_disconnect(event.peer, 0); + } + } + 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; + player.Address = event.peer->address.host; + 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); + + lanDlg->updatePlayerList(Players, NumPlayers); + } + break; + } + } + break; + } + } +} + +void ProcessClient() +{ + if (!Host) return; + + ENetEvent event; + while (enet_host_service(Host, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + // another client is establishing a direct connection to us + + int playerid = -1; + for (int i = 0; i < 16; i++) + { + Player* player = &Players[i]; + if (player->ID == MyPlayer.ID) continue; + if (player->Status != 1) continue; + + if (player->Address == event.peer->address.host) + { + playerid = i; + break; + } + } + + if (playerid < 0) + { + enet_peer_disconnect(event.peer, 0); + break; + } + + RemotePeers[playerid] = event.peer; + } + 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 != 3) break; + + MaxPlayers = data[2]; + + // send player information + MyPlayer.ID = data[1]; + u8 cmd[1+sizeof(Player)]; + cmd[0] = 0x02; + memcpy(&cmd[1], &MyPlayer, 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'; + } + + lanDlg->updatePlayerList(Players, NumPlayers); + + // establish connections to any new clients + for (int i = 0; i < 16; i++) + { + Player* player = &Players[i]; + if (player->ID == MyPlayer.ID) continue; + if (player->Status != 1) continue; + + if (!RemotePeers[i]) + { + ENetAddress peeraddr; + peeraddr.host = player->Address; + peeraddr.port = kLANPort; + ENetPeer* peer = enet_host_connect(Host, &peeraddr, 1, 0); + if (!peer) + { + // TODO deal with this + continue; + } + } + } + } + break; + + case 0x04: // start game + { + // + } + break; + } + } + break; + } + } +} + +void Process() +{ + if (IsHost) + ProcessHost(); + else + ProcessClient(); } diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index 41f4df1b..5aab3764 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -118,9 +118,16 @@ private: namespace LAN { +extern bool Active; + bool Init(); void DeInit(); +void StartHost(const char* player, int numplayers); +void StartClient(const char* player, const char* host); + +void Process(); + void SetMPRecvTimeout(int timeout); void MPBegin(); void MPEnd(); diff --git a/src/frontend/qt_sdl/Netplay.cpp b/src/frontend/qt_sdl/Netplay.cpp index b6c1511f..74fd30b6 100644 --- a/src/frontend/qt_sdl/Netplay.cpp +++ b/src/frontend/qt_sdl/Netplay.cpp @@ -247,13 +247,13 @@ bool Init() CurBlobType = -1; CurBlobLen = 0; - if (enet_initialize() != 0) + /*if (enet_initialize() != 0) { printf("enet shat itself :(\n"); return false; } - printf("enet init OK\n"); + printf("enet init OK\n");*/ return true; } @@ -261,7 +261,7 @@ void DeInit() { // TODO: cleanup resources properly!! - enet_deinitialize(); + //enet_deinitialize(); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 138e5ab2..21f0296d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -320,6 +320,7 @@ void EmuThread::run() IPC::InitSema(); IPC::SetMPRecvTimeout(Config::MPRecvTimeout); + LAN::Init(); Netplay::Init(); NDS::Init(); @@ -364,7 +365,10 @@ void EmuThread::run() while (EmuRunning != emuStatus_Exit) { - if (Netplay::Active) Netplay::ProcessFrame(); + if (LAN::Active) + LAN::Process(); + else if (Netplay::Active) + Netplay::ProcessFrame(); IPC::ProcessCommands(); @@ -673,6 +677,7 @@ void EmuThread::run() GPU::DeInitRenderer(); NDS::DeInit(); Netplay::DeInit(); + LAN::DeInit(); IPC::DeInitSema(); //Platform::LAN_DeInit(); } From e16b7917fed8b822ce02985fe7d72939b21cb1a3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 19:35:47 +0200 Subject: [PATCH 71/81] get some LAN comm started --- src/frontend/qt_sdl/LAN.cpp | 507 ++++++++++++++++++++++-------------- src/frontend/qt_sdl/LAN.h | 6 +- 2 files changed, 317 insertions(+), 196 deletions(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index e4c6cb92..3be2ccde 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include +#include #include @@ -173,6 +175,15 @@ void LANDialog::doUpdatePlayerList(LAN::Player* players, int num) namespace LAN { +struct MPPacketHeader +{ + u32 Magic; + u32 SenderID; + u32 Type; // 0=regular 1=CMD 2=reply 3=ack + u32 Length; + u64 Timestamp; +}; + const int kLANPort = 7064; bool Active; @@ -185,10 +196,15 @@ Player Players[16]; int NumPlayers; int MaxPlayers; +u16 ConnectedBitmask; + Player MyPlayer; u32 HostAddress; bool Lag; +int MPRecvTimeout; +std::queue RXQueue; + bool Init() { @@ -202,6 +218,10 @@ bool Init() NumPlayers = 0; MaxPlayers = 0; + ConnectedBitmask = 0; + + MPRecvTimeout = 25; + // TODO we init enet here but also in Netplay // that is redundant if (enet_initialize() != 0) @@ -228,7 +248,7 @@ void StartHost(const char* playername, int numplayers) addr.host = ENET_HOST_ANY; addr.port = kLANPort; - Host = enet_host_create(&addr, 16, 1, 0, 0); + Host = enet_host_create(&addr, 16, 2, 0, 0); if (!Host) { // TODO handle this gracefully @@ -256,7 +276,7 @@ void StartHost(const char* playername, int numplayers) void StartClient(const char* playername, const char* host) { - Host = enet_host_create(nullptr, 16, 1, 0, 0); + Host = enet_host_create(nullptr, 16, 2, 0, 0); if (!Host) { // TODO handle this gracefully @@ -269,7 +289,7 @@ void StartClient(const char* playername, const char* host) ENetAddress addr; enet_address_set_host(&addr, host); addr.port = kLANPort; - ENetPeer* peer = enet_host_connect(Host, &addr, 1, 0); + ENetPeer* peer = enet_host_connect(Host, &addr, 2, 0); if (!peer) { printf("connect shat itself :(\n"); @@ -307,286 +327,391 @@ void StartClient(const char* playername, const char* host) } -void ProcessHost() +void ProcessHostEvent(ENetEvent& event) { - if (!Host) return; - - ENetEvent event; - while (enet_host_service(Host, &event, 0) > 0) + switch (event.type) { - switch (event.type) + case ENET_EVENT_TYPE_CONNECT: { - case ENET_EVENT_TYPE_CONNECT: + if ((NumPlayers >= MaxPlayers) || (NumPlayers >= 16)) { - if ((NumPlayers >= MaxPlayers) || (NumPlayers >= 16)) - { - // game is full, reject connection - enet_peer_disconnect(event.peer, 0); - break; - } - - // 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[3]; - cmd[0] = 0x01; - cmd[1] = (u8)id; - cmd[2] = MaxPlayers; - ENetPacket* pkt = enet_packet_create(cmd, 3, ENET_PACKET_FLAG_RELIABLE); - enet_peer_send(event.peer, 0, pkt); - - Players[id].ID = id; - Players[id].Status = 3; - Players[id].Address = event.peer->address.host; - event.peer->data = &Players[id]; - NumPlayers++; - - RemotePeers[id] = event.peer; - } - else - { - // ??? - enet_peer_disconnect(event.peer, 0); - } + // game is full, reject connection + enet_peer_disconnect(event.peer, 0); + break; } - break; - case ENET_EVENT_TYPE_DISCONNECT: + // client connected; assign player number + + int id; + for (id = 0; id < 16; id++) { - // TODO - printf("disco\n"); + if (id >= NumPlayers) break; + if (Players[id].Status == 0) break; } - break; - case ENET_EVENT_TYPE_RECEIVE: + if (id < 16) { - if (event.packet->dataLength < 1) break; + u8 cmd[3]; + cmd[0] = 0x01; + cmd[1] = (u8)id; + cmd[2] = MaxPlayers; + ENetPacket* pkt = enet_packet_create(cmd, 3, ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(event.peer, 0, pkt); - u8* data = (u8*)event.packet->data; - switch (data[0]) - { - case 0x02: // client sending player info - { - if (event.packet->dataLength != (1+sizeof(Player))) break; + Players[id].ID = id; + Players[id].Status = 3; + Players[id].Address = event.peer->address.host; + event.peer->data = &Players[id]; + NumPlayers++; - 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; - player.Address = event.peer->address.host; - 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); - - lanDlg->updatePlayerList(Players, NumPlayers); - } - break; - } + RemotePeers[id] = event.peer; + } + else + { + // ??? + enet_peer_disconnect(event.peer, 0); } - break; } + 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; + player.Address = event.peer->address.host; + 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); + + lanDlg->updatePlayerList(Players, NumPlayers); + } + break; + + case 0x04: // player connected + { + if (event.packet->dataLength != 2) break; + if (data[1] > 15) break; + + ConnectedBitmask |= (1<dataLength != 2) break; + if (data[1] > 15) break; + + ConnectedBitmask &= ~(1< 0) + switch (event.type) { - switch (event.type) + case ENET_EVENT_TYPE_CONNECT: { - case ENET_EVENT_TYPE_CONNECT: + // another client is establishing a direct connection to us + + int playerid = -1; + for (int i = 0; i < 16; i++) { - // another client is establishing a direct connection to us + Player* player = &Players[i]; + if (player->ID == MyPlayer.ID) continue; + if (player->Status != 1) continue; - int playerid = -1; - for (int i = 0; i < 16; i++) + if (player->Address == event.peer->address.host) { - Player* player = &Players[i]; - if (player->ID == MyPlayer.ID) continue; - if (player->Status != 1) continue; - - if (player->Address == event.peer->address.host) - { - playerid = i; - break; - } - } - - if (playerid < 0) - { - enet_peer_disconnect(event.peer, 0); + playerid = i; break; } - - RemotePeers[playerid] = event.peer; } - break; - case ENET_EVENT_TYPE_DISCONNECT: + if (playerid < 0) { - // TODO - printf("shma\n"); + enet_peer_disconnect(event.peer, 0); + break; } - break; - case ENET_EVENT_TYPE_RECEIVE: + RemotePeers[playerid] = event.peer; + } + 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]) { - if (event.packet->dataLength < 1) break; - - u8* data = (u8*)event.packet->data; - switch (data[0]) + case 0x01: // host sending player ID { - case 0x01: // host sending player ID + if (event.packet->dataLength != 3) break; + + MaxPlayers = data[2]; + + // send player information + MyPlayer.ID = data[1]; + u8 cmd[1+sizeof(Player)]; + cmd[0] = 0x02; + memcpy(&cmd[1], &MyPlayer, 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++) { - if (event.packet->dataLength != 3) break; - - MaxPlayers = data[2]; - - // send player information - MyPlayer.ID = data[1]; - u8 cmd[1+sizeof(Player)]; - cmd[0] = 0x02; - memcpy(&cmd[1], &MyPlayer, sizeof(Player)); - ENetPacket* pkt = enet_packet_create(cmd, 1+sizeof(Player), ENET_PACKET_FLAG_RELIABLE); - enet_peer_send(event.peer, 0, pkt); + Players[i].Name[31] = '\0'; } - break; - case 0x03: // host sending player list + lanDlg->updatePlayerList(Players, NumPlayers); + + // establish connections to any new clients + for (int i = 0; i < 16; i++) { - if (event.packet->dataLength != (2+sizeof(Players))) break; - if (data[1] > 16) break; + Player* player = &Players[i]; + if (player->ID == MyPlayer.ID) continue; + if (player->Status != 1) continue; - NumPlayers = data[1]; - memcpy(Players, &data[2], sizeof(Players)); - for (int i = 0; i < 16; i++) + if (!RemotePeers[i]) { - Players[i].Name[31] = '\0'; - } - - lanDlg->updatePlayerList(Players, NumPlayers); - - // establish connections to any new clients - for (int i = 0; i < 16; i++) - { - Player* player = &Players[i]; - if (player->ID == MyPlayer.ID) continue; - if (player->Status != 1) continue; - - if (!RemotePeers[i]) + ENetAddress peeraddr; + peeraddr.host = player->Address; + peeraddr.port = kLANPort; + ENetPeer* peer = enet_host_connect(Host, &peeraddr, 2, 0); + if (!peer) { - ENetAddress peeraddr; - peeraddr.host = player->Address; - peeraddr.port = kLANPort; - ENetPeer* peer = enet_host_connect(Host, &peeraddr, 1, 0); - if (!peer) - { - // TODO deal with this - continue; - } + // TODO deal with this + continue; } } } - break; - - case 0x04: // start game - { - // - } - break; } + break; + + case 0x04: // player connected + { + if (event.packet->dataLength != 2) break; + if (data[1] > 15) break; + + ConnectedBitmask |= (1<dataLength != 2) break; + if (data[1] > 15) break; + + ConnectedBitmask &= ~(1< 0) + { + if (event.type == ENET_EVENT_TYPE_RECEIVE && event.channelID == 1) + { + RXQueue.push(event.packet); + if (block) return; + } + else + { + ProcessEvent(event); + if (block) + { + u32 time = SDL_GetTicks(); + timeout -= (time - time_last); + if (timeout <= 0) return; + } + } + } } void SetMPRecvTimeout(int timeout) { - //MPRecvTimeout = timeout; + MPRecvTimeout = timeout; } void MPBegin() { - // + ConnectedBitmask |= (1<data[0], &pktheader, sizeof(MPPacketHeader)); + if (len) + memcpy(&enetpacket->data[sizeof(MPPacketHeader)], packet, len); + + enet_host_broadcast(Host, 1, enetpacket); + enet_host_flush(Host); + + return len; } -u16 GetInstanceBitmask() +int RecvMPPacketGeneric(u8* packet, bool block, u64* timestamp) { - // - return 0; + if (!Host) return 0; + + Process(block); + if (RXQueue.empty()) return 0; + + ENetPacket* enetpacket = RXQueue.front(); + RXQueue.pop(); + MPPacketHeader* header = (MPPacketHeader*)&enetpacket->data[0]; + bool good = true; + if (enetpacket->dataLength < sizeof(MPPacketHeader)) + good = false; + else if (header->Magic != 0x4946494E) + good = false; + else if (header->SenderID == MyPlayer.ID) + good = false; + + if (!good) + { + enet_packet_destroy(enetpacket); + return 0; + } + + u32 len = header->Length; + if (len) + memcpy(packet, &enetpacket->data[sizeof(MPPacketHeader)], len); + + if (timestamp) *timestamp = header->Timestamp; + return len; } int SendMPPacket(u8* packet, int len, u64 timestamp) { - return 0; + return SendMPPacketGeneric(0, packet, len, timestamp); } int RecvMPPacket(u8* packet, u64* timestamp) { - return 0; + return RecvMPPacketGeneric(packet, false, timestamp); } int SendMPCmd(u8* packet, int len, u64 timestamp) { - return 0; + return SendMPPacketGeneric(1, packet, len, timestamp); } int SendMPReply(u8* packet, int len, u64 timestamp, u16 aid) { - return 0; + return SendMPPacketGeneric(2 | (aid<<16), packet, len, timestamp); } int SendMPAck(u8* packet, int len, u64 timestamp) { - return 0; + return SendMPPacketGeneric(3, packet, len, timestamp); } int RecvMPHostPacket(u8* packet, u64* timestamp) diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index 5aab3764..1fe0cac2 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -126,16 +126,12 @@ void DeInit(); void StartHost(const char* player, int numplayers); void StartClient(const char* player, const char* host); -void Process(); +void Process(bool block = false); void SetMPRecvTimeout(int timeout); void MPBegin(); void MPEnd(); -void SetActive(bool active); - -u16 GetInstanceBitmask(); - int SendMPPacket(u8* data, int len, u64 timestamp); int RecvMPPacket(u8* data, u64* timestamp); int SendMPCmd(u8* data, int len, u64 timestamp); From 9e9bd8db54a87dae7ff8f025bb45421597cfa974 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 19:42:06 +0200 Subject: [PATCH 72/81] maybe it would work better this way, don't ya think? --- src/frontend/qt_sdl/Platform.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 6c11a592..14f4b013 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -38,6 +38,8 @@ #include "LAN_Socket.h" #include "LAN_PCap.h" #include "IPC.h" +#include "IPC.h" +#include "LAN.h" #include "OSD.h" #ifdef __WIN32__ @@ -533,47 +535,56 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen void MP_Begin() { - return IPC::MPBegin(); + //return IPC::MPBegin(); + return LAN::MPBegin(); } void MP_End() { - return IPC::MPEnd(); + //return IPC::MPEnd(); + return LAN::MPEnd(); } int MP_SendPacket(u8* data, int len, u64 timestamp) { - return IPC::SendMPPacket(data, len, timestamp); + //return IPC::SendMPPacket(data, len, timestamp); + return LAN::SendMPPacket(data, len, timestamp); } int MP_RecvPacket(u8* data, u64* timestamp) { - return IPC::RecvMPPacket(data, timestamp); + //return IPC::RecvMPPacket(data, timestamp); + return LAN::RecvMPPacket(data, timestamp); } int MP_SendCmd(u8* data, int len, u64 timestamp) { - return IPC::SendMPCmd(data, len, timestamp); + //return IPC::SendMPCmd(data, len, timestamp); + return LAN::SendMPCmd(data, len, timestamp); } int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid) { - return IPC::SendMPReply(data, len, timestamp, aid); + //return IPC::SendMPReply(data, len, timestamp, aid); + return LAN::SendMPReply(data, len, timestamp, aid); } int MP_SendAck(u8* data, int len, u64 timestamp) { - return IPC::SendMPAck(data, len, timestamp); + //return IPC::SendMPAck(data, len, timestamp); + return LAN::SendMPAck(data, len, timestamp); } int MP_RecvHostPacket(u8* data, u64* timestamp) { - return IPC::RecvMPHostPacket(data, timestamp); + //return IPC::RecvMPHostPacket(data, timestamp); + return LAN::RecvMPHostPacket(data, timestamp); } u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask) { - return IPC::RecvMPReplies(data, timestamp, aidmask); + //return IPC::RecvMPReplies(data, timestamp, aidmask); + return LAN::RecvMPReplies(data, timestamp, aidmask); } bool LAN_Init() From 467786548800b58b6f68cee85af731f942471fa3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 20:11:33 +0200 Subject: [PATCH 73/81] attempt at receiving MP replies and such, let's see --- src/frontend/qt_sdl/LAN.cpp | 85 +++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 3be2ccde..86dcad48 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -203,6 +203,7 @@ u32 HostAddress; bool Lag; int MPRecvTimeout; +int LastHostID; std::queue RXQueue; @@ -221,6 +222,7 @@ bool Init() ConnectedBitmask = 0; MPRecvTimeout = 25; + LastHostID = -1; // TODO we init enet here but also in Netplay // that is redundant @@ -267,6 +269,7 @@ void StartHost(const char* playername, int numplayers) memcpy(&MyPlayer, player, sizeof(Player)); HostAddress = 0x0100007F; + LastHostID = -1; Active = true; IsHost = true; @@ -321,6 +324,7 @@ void StartClient(const char* playername, const char* host) player->Status = 3; HostAddress = addr.host; + LastHostID = -1; Active = true; IsHost = false; @@ -614,8 +618,9 @@ void SetMPRecvTimeout(int timeout) void MPBegin() { ConnectedBitmask |= (1<Length; if (len) + { + if (len > 2048) len = 2048; + memcpy(packet, &enetpacket->data[sizeof(MPPacketHeader)], len); + if (header->Type == 1) + LastHostID = header->SenderID; + } + if (timestamp) *timestamp = header->Timestamp; + enet_packet_destroy(enetpacket); return len; } @@ -716,12 +729,76 @@ int SendMPAck(u8* packet, int len, u64 timestamp) int RecvMPHostPacket(u8* packet, u64* timestamp) { - return 0; + if (LastHostID != -1) + { + // check if the host is still connected + + if (!(ConnectedBitmask & (1<data[0]; + bool good = true; + if (enetpacket->dataLength < sizeof(MPPacketHeader)) + good = false; + else if (header->Magic != 0x4946494E) + good = false; + else if (header->SenderID == MyPlayer.ID) + good = false; + else if ((header->Type & 0xFFFF) != 2) + good = false; + else if (header->Timestamp < (timestamp - 32)) + good = false; + + if (good) + { + u32 len = header->Length; + if (len) + { + if (len > 1024) len = 1024; + + u32 aid = header->Type >> 16; + memcpy(&packets[(aid-1)*1024], &enetpacket->data[sizeof(MPPacketHeader)], len); + + ret |= (1<SenderID); + if (((myinstmask & ConnectedBitmask) == ConnectedBitmask) || + ((ret & aidmask) == aidmask)) + { + // all the clients have sent their reply + enet_packet_destroy(enetpacket); + return ret; + } + } + + enet_packet_destroy(enetpacket); + } } } From 085cc413012330cd1298a7fca61e6b2e19b6fb0f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 20:50:21 +0200 Subject: [PATCH 74/81] debug... --- src/frontend/qt_sdl/LAN.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 86dcad48..0a370acf 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -204,6 +204,7 @@ bool Lag; int MPRecvTimeout; int LastHostID; +ENetPeer* LastHostPeer; std::queue RXQueue; @@ -223,6 +224,7 @@ bool Init() MPRecvTimeout = 25; LastHostID = -1; + LastHostPeer = nullptr; // TODO we init enet here but also in Netplay // that is redundant @@ -270,6 +272,7 @@ void StartHost(const char* playername, int numplayers) HostAddress = 0x0100007F; LastHostID = -1; + LastHostPeer = nullptr; Active = true; IsHost = true; @@ -325,6 +328,7 @@ void StartClient(const char* playername, const char* host) HostAddress = addr.host; LastHostID = -1; + LastHostPeer = nullptr; Active = true; IsHost = false; @@ -593,6 +597,7 @@ void Process(bool block) { if (event.type == ENET_EVENT_TYPE_RECEIVE && event.channelID == 1) { + event.packet->userData = event.peer; RXQueue.push(event.packet); if (block) return; } @@ -619,6 +624,7 @@ void MPBegin() { ConnectedBitmask |= (1<data[sizeof(MPPacketHeader)], packet, len); - enet_host_broadcast(Host, 1, enetpacket); + if (((type & 0xFFFF) == 2) && LastHostPeer) + enet_peer_send(LastHostPeer, 1, enetpacket); + else + enet_host_broadcast(Host, 1, enetpacket); enet_host_flush(Host); return len; @@ -692,7 +701,10 @@ int RecvMPPacketGeneric(u8* packet, bool block, u64* timestamp) memcpy(packet, &enetpacket->data[sizeof(MPPacketHeader)], len); if (header->Type == 1) + { LastHostID = header->SenderID; + LastHostPeer = (ENetPeer*)enetpacket->userData; + } } if (timestamp) *timestamp = header->Timestamp; @@ -756,6 +768,7 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) if (RXQueue.empty()) { // no more replies available + printf("RecvMPReplies timeout, ret=%04X myinstmask=%04X conn=%04X aidmask=%04X\n", ret, myinstmask, ConnectedBitmask, aidmask); return ret; } @@ -796,6 +809,7 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) return ret; } } + else printf("RecvMPReplies received frame but bad\n"); enet_packet_destroy(enetpacket); } From 6be38028b1183a03a55ce29e33ed5c3eb5557d64 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 3 Sep 2023 20:53:37 +0200 Subject: [PATCH 75/81] aaaa --- src/frontend/qt_sdl/LAN.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 0a370acf..b27957b7 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -809,7 +809,7 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) return ret; } } - else printf("RecvMPReplies received frame but bad\n"); + else printf("RecvMPReplies received frame but bad (type=%08X ts=%016llX/%016llX)\n", header->Type, header->Timestamp, timestamp); enet_packet_destroy(enetpacket); } From 692f2a5c836055c3703ded51d5bf7ddea27aa860 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 8 Sep 2023 11:39:19 +0200 Subject: [PATCH 76/81] attempt? --- src/frontend/qt_sdl/LAN.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index b27957b7..278f21aa 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -646,7 +646,8 @@ int SendMPPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) if (!Host) return 0; // TODO make the reliable part optional? - u32 flags = ENET_PACKET_FLAG_RELIABLE; + //u32 flags = ENET_PACKET_FLAG_RELIABLE; + u32 flags = ENET_PACKET_FLAG_UNSEQUENCED; ENetPacket* enetpacket = enet_packet_create(nullptr, sizeof(MPPacketHeader)+len, flags); From 3236f7ddff5b69a34c646fc341b1ba7289eebd1e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 9 Sep 2023 11:57:54 +0200 Subject: [PATCH 77/81] attempt at LAN discovery feature --- src/frontend/qt_sdl/LAN.cpp | 176 +++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/LAN.h | 2 +- src/frontend/qt_sdl/main.cpp | 2 +- 3 files changed, 178 insertions(+), 2 deletions(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 278f21aa..87a6e114 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -20,6 +20,27 @@ #include #include #include +#include +#include + +#ifdef __WIN32__ + #include + #include + + #define socket_t SOCKET + #define sockaddr_t SOCKADDR + #define sockaddr_in_t SOCKADDR_IN +#else + #include + #include + #include + #include + + #define socket_t int + #define sockaddr_t struct sockaddr + #define sockaddr_in_t struct sockaddr_in + #define closesocket close +#endif #include #include @@ -175,6 +196,22 @@ void LANDialog::doUpdatePlayerList(LAN::Player* players, int num) namespace LAN { +const u32 kDiscoveryMagic = 0x444E414C; // LAND +const u32 kPacketMagic = 0x4946494E; // NIFI + +const u32 kProtocolVersion = 1; + +struct DiscoveryData +{ + u32 Magic; + u32 Version; + u32 Tick; + char SessionName[64]; + u8 NumPlayers; + u8 MaxPlayers; + u8 Status; // 0=idle 1=playing +}; + struct MPPacketHeader { u32 Magic; @@ -184,8 +221,13 @@ struct MPPacketHeader u64 Timestamp; }; +const int kDiscoveryPort = 7063; const int kLANPort = 7064; +socket_t DiscoverySocket; +u32 DiscoveryLastTick; +std::map DiscoveryList; + bool Active; bool IsHost; @@ -210,6 +252,9 @@ std::queue RXQueue; bool Init() { + DiscoverySocket = INVALID_SOCKET; + DiscoveryLastTick = 0; + Active = false; IsHost = false; Host = nullptr; @@ -246,6 +291,44 @@ void DeInit() } +void StartDiscovery() +{ + int res; + + DiscoverySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (DiscoverySocket < 0) + { + DiscoverySocket = INVALID_SOCKET; + return; + } + + sockaddr_in_t saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + saddr.sin_port = htons(kDiscoveryPort); + res = bind(DiscoverySocket, (const sockaddr_t*)&saddr, sizeof(saddr)); + if (res < 0) + { + closesocket(DiscoverySocket); + DiscoverySocket = INVALID_SOCKET; + return; + } + + int opt_true = 1; + res = setsockopt(DiscoverySocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int)); + if (res < 0) + { + closesocket(DiscoverySocket); + DiscoverySocket = INVALID_SOCKET; + return; + } + + DiscoveryLastTick = SDL_GetTicks(); + + DiscoveryList.clear(); +} + void StartHost(const char* playername, int numplayers) { ENetAddress addr; @@ -335,6 +418,93 @@ void StartClient(const char* playername, const char* host) } +void ProcessDiscovery() +{ + if (DiscoverySocket == INVALID_SOCKET) + return; + + u32 tick = SDL_GetTicks(); + if ((tick - DiscoveryLastTick) < 1000) + return; + + DiscoveryLastTick = tick; + + if (IsHost) + { + // advertise this LAN session over the network + + DiscoveryData beacon; + memset(&beacon, 0, sizeof(beacon)); + beacon.Magic = kDiscoveryMagic; + beacon.Version = kProtocolVersion; + beacon.Tick = tick; + snprintf(beacon.SessionName, 64, "%s's game", MyPlayer.Name); + beacon.NumPlayers = NumPlayers; + beacon.MaxPlayers = MaxPlayers; + beacon.Status = 0; // TODO + + sockaddr_in_t saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + saddr.sin_port = htons(kDiscoveryPort); + + sendto(DiscoverySocket, (const char*)&beacon, sizeof(beacon), 0, (const sockaddr_t*)&saddr, sizeof(saddr)); + } + else + { + // listen for LAN sessions + + fd_set fd; + struct timeval tv; + for (;;) + { + FD_ZERO(&fd); FD_SET(DiscoverySocket, &fd); + tv.tv_sec = 0; tv.tv_usec = 0; + if (!select(DiscoverySocket+1, &fd, nullptr, nullptr, &tv)) + break; + + DiscoveryData beacon; + sockaddr_in_t raddr; + int ralen = sizeof(raddr); + + int rlen = recvfrom(DiscoverySocket, (char*)&beacon, sizeof(beacon), 0, (sockaddr_t*)&raddr, &ralen); + if (rlen < sizeof(beacon)) continue; + if (beacon.Magic != kDiscoveryMagic) continue; + if (beacon.Version != kProtocolVersion) continue; + if (beacon.MaxPlayers > 16) continue; + if (beacon.NumPlayers > beacon.MaxPlayers) continue; + + u32 key = ntohl(raddr.sin_addr.s_addr); + beacon.Magic = tick; + DiscoveryList[key] = beacon; + } + + // cleanup: remove hosts that haven't given a sign of life in the last 5 seconds + + std::vector deletelist; + + for (const auto& [key, data] : DiscoveryList) + { + u32 age = tick - data.Magic; + if (age < 5000) continue; + + deletelist.push_back(key); + } + + for (const auto& key : deletelist) + { + DiscoveryList.erase(key); + } + + for (const auto& [key, data] : DiscoveryList) + { + printf("DISCOVERY: %d.%d.%d.%d\n", key>>24, (key>>16)&0xFF, (key>>8)&0xFF, key&0xFF); + printf("- game: %s, %d/%d players\n", data.SessionName, data.NumPlayers, data.MaxPlayers); + } + } +} + void ProcessHostEvent(ENetEvent& event) { switch (event.type) @@ -614,6 +784,12 @@ void Process(bool block) } } +void ProcessFrame() +{ + ProcessDiscovery(); + Process(false); +} + void SetMPRecvTimeout(int timeout) { diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index 1fe0cac2..6cf27afc 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -126,7 +126,7 @@ void DeInit(); void StartHost(const char* player, int numplayers); void StartClient(const char* player, const char* host); -void Process(bool block = false); +void ProcessFrame(); void SetMPRecvTimeout(int timeout); void MPBegin(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 21f0296d..9158751a 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -366,7 +366,7 @@ void EmuThread::run() while (EmuRunning != emuStatus_Exit) { if (LAN::Active) - LAN::Process(); + LAN::ProcessFrame(); else if (Netplay::Active) Netplay::ProcessFrame(); From 8e7606cd60ec2653fa9599208fa24ff8a43e9375 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 9 Sep 2023 12:00:14 +0200 Subject: [PATCH 78/81] blarg --- src/frontend/qt_sdl/LAN.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 87a6e114..348d20dd 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -42,6 +42,10 @@ #define closesocket close #endif +#ifndef INVALID_SOCKET + #define INVALID_SOCKET (socket_t)-1 +#endif + #include #include @@ -466,7 +470,7 @@ void ProcessDiscovery() DiscoveryData beacon; sockaddr_in_t raddr; - int ralen = sizeof(raddr); + socklen_t ralen = sizeof(raddr); int rlen = recvfrom(DiscoverySocket, (char*)&beacon, sizeof(beacon), 0, (sockaddr_t*)&raddr, &ralen); if (rlen < sizeof(beacon)) continue; From c874fe69e89d165b04eee7b2ea97a95b42506865 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 9 Sep 2023 12:04:26 +0200 Subject: [PATCH 79/81] maybe actually hook it up somewhere? --- src/frontend/qt_sdl/LAN.cpp | 8 +++++++- src/frontend/qt_sdl/LAN.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index 348d20dd..badf8f35 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -122,6 +122,11 @@ void LANStartClientDialog::done(int r) LAN::StartClient(player.c_str(), host.c_str()); } + else + { + // TEST!! + LAN::StartDiscovery(); + } QDialog::done(r); } @@ -329,7 +334,6 @@ void StartDiscovery() } DiscoveryLastTick = SDL_GetTicks(); - DiscoveryList.clear(); } @@ -365,6 +369,8 @@ void StartHost(const char* playername, int numplayers) IsHost = true; lanDlg->updatePlayerList(Players, NumPlayers); + + StartDiscovery(); } void StartClient(const char* playername, const char* host) diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index 6cf27afc..7398f7d6 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -123,6 +123,7 @@ extern bool Active; bool Init(); void DeInit(); +void StartDiscovery(); void StartHost(const char* player, int numplayers); void StartClient(const char* player, const char* host); From f2916528b1394ab6634d3e54b268072e79884650 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 9 Sep 2023 12:09:19 +0200 Subject: [PATCH 80/81] more like this --- src/frontend/qt_sdl/LAN.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index badf8f35..f3973702 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -125,6 +125,7 @@ void LANStartClientDialog::done(int r) else { // TEST!! + printf("borp\n"); LAN::StartDiscovery(); } @@ -332,9 +333,11 @@ void StartDiscovery() DiscoverySocket = INVALID_SOCKET; return; } - +printf("startdisco\n"); DiscoveryLastTick = SDL_GetTicks(); DiscoveryList.clear(); + + Active = true; } void StartHost(const char* playername, int numplayers) From 1f57a5dec2f7e61ca6d7eaace4ede581b2b91d98 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 9 Sep 2023 14:13:27 +0200 Subject: [PATCH 81/81] clean up resources --- src/frontend/qt_sdl/LAN.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LAN.cpp b/src/frontend/qt_sdl/LAN.cpp index f3973702..525f8059 100644 --- a/src/frontend/qt_sdl/LAN.cpp +++ b/src/frontend/qt_sdl/LAN.cpp @@ -295,7 +295,21 @@ bool Init() void DeInit() { - // TODO: cleanup resources properly!! + if (DiscoverySocket) + { + closesocket(DiscoverySocket); + DiscoverySocket = INVALID_SOCKET; + } + + while (!RXQueue.empty()) + { + ENetPacket* packet = RXQueue.front(); + RXQueue.pop(); + enet_packet_destroy(packet); + } + + enet_host_destroy(Host); + Host = nullptr; enet_deinitialize(); }