diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 4666c1d1..ef59901d 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -64,7 +64,8 @@ MainWindow* topWindow = nullptr; const string kWifiSettingsPath = "wfcsettings.bin"; -EmuInstance::EmuInstance(int inst) : instanceID(inst), +EmuInstance::EmuInstance(int inst) : deleting(false), + instanceID(inst), globalCfg(Config::GetGlobalTable()), localCfg(Config::GetLocalTable(inst)) { @@ -115,7 +116,8 @@ EmuInstance::EmuInstance(int inst) : instanceID(inst), EmuInstance::~EmuInstance() { - // TODO window cleanup and shit? + deleting = true; + deleteAllWindows(); LocalMP::End(instanceID); @@ -167,6 +169,44 @@ void EmuInstance::createWindow() emuThread->attachWindow(win); } +void EmuInstance::deleteWindow(int id, bool close) +{ + if (id >= kMaxWindows) return; + + MainWindow* win = windowList[id]; + if (!win) return; + + if (win->hasOpenGL() && win == mainWindow) + { + // we intentionally don't unpause here + emuThread->emuPause(); + emuThread->deinitContext(); + } + + emuThread->detachWindow(win); + + if (close) + win->close(); + + windowList[id] = nullptr; + numWindows--; + + if (topWindow == win) topWindow = nullptr; + if (mainWindow == win) mainWindow = nullptr; + + if ((!mainWindow) && !deleting) + { + // if we closed this instance's main window, delete the instance + deleteEmuInstance(instanceID); + } +} + +void EmuInstance::deleteAllWindows() +{ + for (int i = kMaxWindows-1; i >= 0; i--) + deleteWindow(i, true); +} + void EmuInstance::osdAddMessage(unsigned int color, const char* fmt, ...) { diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 44bd75e1..72adccab 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -91,6 +91,8 @@ public: std::string instanceFileSuffix(); void createWindow(); + void deleteWindow(int id, bool close); + void deleteAllWindows(); void osdAddMessage(unsigned int color, const char* fmt, ...); @@ -215,6 +217,8 @@ private: bool hotkeyPressed(int id) { return hotkeyPress & (1< #include -#include -#include -#include -#include - #include "LAN.h" -#include "Config.h" -#include "main.h" - -#include "ui_LANStartHostDialog.h" -#include "ui_LANStartClientDialog.h" -#include "ui_LANDialog.h" using namespace melonDS; @@ -72,6 +61,21 @@ const u32 kPacketMagic = 0x4946494E; // NIFI const u32 kProtocolVersion = 1; +enum +{ + Chan_Cmd = 0, // channel 0 -- control commands + Chan_MP, // channel 1 -- MP data exchange +}; + +enum +{ + Cmd_ClientInit = 1, // 01 -- host->client -- init new client and assign ID + Cmd_PlayerInfo, // 02 -- client->host -- send client player info to host + Cmd_PlayerList, // 03 -- host->client -- broadcast updated player list + Cmd_PlayerConnect, // 04 -- both -- signal connected state (ready to receive MP frames) + Cmd_PlayerDisconnect, // 05 -- both -- signal disconnected state (not receiving MP frames) +}; + struct MPPacketHeader { u32 Magic; @@ -250,7 +254,7 @@ bool StartHost(const char* playername, int numplayers) memset(player, 0, sizeof(Player)); player->ID = 0; strncpy(player->Name, playername, 31); - player->Status = 2; + player->Status = Player_Host; player->Address = 0x0100007F; NumPlayers = 1; MaxPlayers = numplayers; @@ -293,7 +297,7 @@ bool StartClient(const char* playername, const char* host) memset(player, 0, sizeof(Player)); player->ID = 0; strncpy(player->Name, playername, 31); - player->Status = 3; + player->Status = Player_Connecting; ENetEvent event; int conn = 0; @@ -314,8 +318,8 @@ bool StartClient(const char* playername, const char* host) else if (conn == 1 && event.type == ENET_EVENT_TYPE_RECEIVE) { u8* data = event.packet->data; - if (event.channelID != 0) continue; - if (data[0] != 0x01) continue; + if (event.channelID != Chan_Cmd) continue; + if (data[0] != Cmd_ClientInit) continue; if (event.packet->dataLength != 11) continue; u32 magic = data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24); @@ -328,7 +332,7 @@ bool StartClient(const char* playername, const char* host) // send player information MyPlayer.ID = data[9]; u8 cmd[9+sizeof(Player)]; - cmd[0] = 0x02; + cmd[0] = Cmd_PlayerInfo; cmd[1] = (u8)kLANMagic; cmd[2] = (u8)(kLANMagic >> 8); cmd[3] = (u8)(kLANMagic >> 16); @@ -339,7 +343,7 @@ bool StartClient(const char* playername, const char* host) cmd[8] = (u8)(kProtocolVersion >> 24); memcpy(&cmd[9], &MyPlayer, sizeof(Player)); ENetPacket* pkt = enet_packet_create(cmd, 9+sizeof(Player), ENET_PACKET_FLAG_RELIABLE); - enet_peer_send(event.peer, 0, pkt); + enet_peer_send(event.peer, Chan_Cmd, pkt); conn = 2; break; @@ -475,11 +479,11 @@ void ProcessDiscovery() void HostUpdatePlayerList() { u8 cmd[2+sizeof(Players)]; - cmd[0] = 0x03; + cmd[0] = Cmd_PlayerList; 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); + enet_host_broadcast(Host, Chan_Cmd, pkt); //if (lanDlg) // lanDlg->updatePlayerList(); @@ -510,13 +514,13 @@ void ProcessHostEvent(ENetEvent& event) for (id = 0; id < 16; id++) { if (id >= NumPlayers) break; - if (Players[id].Status == 0) break; + if (Players[id].Status == Player_None) break; } if (id < 16) { u8 cmd[11]; - cmd[0] = 0x01; + cmd[0] = Cmd_ClientInit; cmd[1] = (u8)kLANMagic; cmd[2] = (u8)(kLANMagic >> 8); cmd[3] = (u8)(kLANMagic >> 16); @@ -528,10 +532,10 @@ void ProcessHostEvent(ENetEvent& event) cmd[9] = (u8)id; cmd[10] = MaxPlayers; ENetPacket* pkt = enet_packet_create(cmd, 11, ENET_PACKET_FLAG_RELIABLE); - enet_peer_send(event.peer, 0, pkt); + enet_peer_send(event.peer, Chan_Cmd, pkt); Players[id].ID = id; - Players[id].Status = 3; + Players[id].Status = Player_Connecting; Players[id].Address = event.peer->address.host; event.peer->data = &Players[id]; NumPlayers++; @@ -557,7 +561,7 @@ void ProcessHostEvent(ENetEvent& event) RemotePeers[id] = nullptr; player->ID = 0; - player->Status = 0; + player->Status = Player_None; NumPlayers--; // broadcast updated player list @@ -572,7 +576,7 @@ void ProcessHostEvent(ENetEvent& event) u8* data = (u8*)event.packet->data; switch (data[0]) { - case 0x02: // client sending player info + case Cmd_PlayerInfo: // client sending player info { if (event.packet->dataLength != (9+sizeof(Player))) break; @@ -595,7 +599,7 @@ void ProcessHostEvent(ENetEvent& event) break; } - player.Status = 1; + player.Status = Player_Client; player.Address = event.peer->address.host; memcpy(hostside, &player, sizeof(Player)); @@ -604,7 +608,7 @@ void ProcessHostEvent(ENetEvent& event) } break; - case 0x04: // player connected + case Cmd_PlayerConnect: // player connected { if (event.packet->dataLength != 1) break; Player* player = (Player*)event.peer->data; @@ -615,7 +619,7 @@ void ProcessHostEvent(ENetEvent& event) } break; - case 0x05: // player disconnected + case Cmd_PlayerDisconnect: // player disconnected { if (event.packet->dataLength != 1) break; Player* player = (Player*)event.peer->data; @@ -646,7 +650,7 @@ void ProcessClientEvent(ENetEvent& event) { Player* player = &Players[i]; if (i == MyPlayer.ID) continue; - if (player->Status != 1) continue; + if (player->Status != Player_Client) continue; if (player->Address == event.peer->address.host) { @@ -676,7 +680,7 @@ void ProcessClientEvent(ENetEvent& event) int id = player->ID; RemotePeers[id] = nullptr; - player->Status = 4; + player->Status = Player_Disconnected; ClientUpdatePlayerList(); } @@ -689,7 +693,7 @@ void ProcessClientEvent(ENetEvent& event) u8* data = (u8*)event.packet->data; switch (data[0]) { - case 0x03: // host sending player list + case Cmd_PlayerList: // host sending player list { if (event.packet->dataLength != (2+sizeof(Players))) break; if (data[1] > 16) break; @@ -709,7 +713,7 @@ void ProcessClientEvent(ENetEvent& event) { Player* player = &Players[i]; if (i == MyPlayer.ID) continue; - if (player->Status != 1) continue; + if (player->Status != Player_Client) continue; if (!RemotePeers[i]) { @@ -727,7 +731,7 @@ void ProcessClientEvent(ENetEvent& event) } break; - case 0x04: // player connected + case Cmd_PlayerConnect: // player connected { if (event.packet->dataLength != 1) break; Player* player = (Player*)event.peer->data; @@ -738,7 +742,7 @@ void ProcessClientEvent(ENetEvent& event) } break; - case 0x05: // player disconnected + case Cmd_PlayerDisconnect: // player disconnected { if (event.packet->dataLength != 1) break; Player* player = (Player*)event.peer->data; @@ -813,7 +817,7 @@ void Process(int type) ENetEvent event; while (enet_host_service(Host, &event, timeout) > 0) { - if (event.type == ENET_EVENT_TYPE_RECEIVE && event.channelID == 1) + if (event.type == ENET_EVENT_TYPE_RECEIVE && event.channelID == Chan_MP) { MPPacketHeader* header = (MPPacketHeader*)&event.packet->data[0]; //printf("- enet_host_service: (%d) got MP frame, len=%d type=%08X fc=%04X\n", type, event.packet->dataLength, header->Type, *(u16*)&event.packet->data[sizeof(MPPacketHeader)+12]); @@ -871,7 +875,7 @@ void ProcessFrame() for (int i = 0; i < 16; i++) { - if (Players[i].Status == 0) continue; + if (Players[i].Status == Player_None) continue; if (i == MyPlayer.ID) continue; if (!RemotePeers[i]) continue; @@ -897,9 +901,9 @@ void MPBegin() LastHostID = -1; LastHostPeer = nullptr; - u8 cmd = 0x04; + u8 cmd = Cmd_PlayerConnect; ENetPacket* pkt = enet_packet_create(&cmd, 1, ENET_PACKET_FLAG_RELIABLE); - enet_host_broadcast(Host, 0, pkt); + enet_host_broadcast(Host, Chan_Cmd, pkt); } void MPEnd() @@ -908,9 +912,9 @@ void MPEnd() ConnectedBitmask &= ~(1 << MyPlayer.ID); - u8 cmd = 0x05; + u8 cmd = Cmd_PlayerDisconnect; ENetPacket* pkt = enet_packet_create(&cmd, 1, ENET_PACKET_FLAG_RELIABLE); - enet_host_broadcast(Host, 0, pkt); + enet_host_broadcast(Host, Chan_Cmd, pkt); } @@ -935,9 +939,9 @@ int SendMPPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) memcpy(&enetpacket->data[sizeof(MPPacketHeader)], packet, len); if (((type & 0xFFFF) == 2) && LastHostPeer) - enet_peer_send(LastHostPeer, 1, enetpacket); + enet_peer_send(LastHostPeer, Chan_MP, enetpacket); else - enet_host_broadcast(Host, 1, enetpacket); + enet_host_broadcast(Host, Chan_MP, enetpacket); enet_host_flush(Host); return len; diff --git a/src/frontend/qt_sdl/LAN.h b/src/frontend/qt_sdl/LAN.h index d66318cc..347980ac 100644 --- a/src/frontend/qt_sdl/LAN.h +++ b/src/frontend/qt_sdl/LAN.h @@ -27,39 +27,49 @@ namespace LAN { +using namespace melonDS; + +enum PlayerStatus +{ + Player_None = 0, // no player in this entry + Player_Client, // game client + Player_Host, // game host + Player_Connecting, // player still connecting + Player_Disconnected, // player disconnected +}; struct Player { int ID; char Name[32]; - int Status; // 0=no player 1=normal 2=host 3=connecting 4=disconnected - melonDS::u32 Address; + PlayerStatus Status; + u32 Address; }; struct DiscoveryData { - melonDS::u32 Magic; - melonDS::u32 Version; - melonDS::u32 Tick; + u32 Magic; + u32 Version; + u32 Tick; char SessionName[64]; - melonDS::u8 NumPlayers; - melonDS::u8 MaxPlayers; - melonDS::u8 Status; // 0=idle 1=playing + u8 NumPlayers; + u8 MaxPlayers; + u8 Status; // 0=idle 1=playing }; extern bool Active; -extern std::map DiscoveryList; +extern std::map DiscoveryList; extern QMutex DiscoveryMutex; // TODO: turn into Platform::Mutex or rework this to be nicer extern Player Players[16]; -extern melonDS::u32 PlayerPing[16]; +extern u32 PlayerPing[16]; extern int NumPlayers; extern int MaxPlayers; extern Player MyPlayer; -extern melonDS::u32 HostAddress; +extern u32 HostAddress; bool Init(); void DeInit(); @@ -75,13 +85,13 @@ void SetMPRecvTimeout(int timeout); void MPBegin(); void MPEnd(); -int SendMPPacket(melonDS::u8* data, int len, melonDS::u64 timestamp); -int RecvMPPacket(melonDS::u8* data, melonDS::u64* timestamp); -int SendMPCmd(melonDS::u8* data, int len, melonDS::u64 timestamp); -int SendMPReply(melonDS::u8* data, int len, melonDS::u64 timestamp, melonDS::u16 aid); -int SendMPAck(melonDS::u8* data, int len, melonDS::u64 timestamp); -int RecvMPHostPacket(melonDS::u8* data, melonDS::u64* timestamp); -melonDS::u16 RecvMPReplies(melonDS::u8* data, melonDS::u64 timestamp, melonDS::u16 aidmask); +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); } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 7ff33340..b43ef0ad 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -73,6 +73,7 @@ #include "version.h" #include "Savestate.h" #include "LocalMP.h" +#include "LANDialog.h" //#include "main_shaders.h" @@ -763,24 +764,9 @@ void MainWindow::closeEvent(QCloseEvent* event) QByteArray geom = saveGeometry(); QByteArray enc = geom.toBase64(QByteArray::Base64Encoding); windowCfg.SetString("Geometry", enc.toStdString()); - Config::Save(); - if (hasOGL && (windowID == 0)) - { - // we intentionally don't unpause here - emuThread->emuPause(); - emuThread->deinitContext(); - } - - emuThread->detachWindow(this); - - if (windowID == 0) - { - int inst = emuInstance->instanceID; - deleteEmuInstance(inst); - } - + emuInstance->deleteWindow(windowID, false); QMainWindow::closeEvent(event); } @@ -1694,12 +1680,14 @@ void MainWindow::onMPNewInstance() void MainWindow::onLANStartHost() { - //LANStartHostDialog::openDlg(this); + if (!lanWarning(true)) return; + LANStartHostDialog::openDlg(this); } void MainWindow::onLANStartClient() { - //LANStartClientDialog::openDlg(this); + if (!lanWarning(false)) return; + LANStartClientDialog::openDlg(this); } void MainWindow::onNPStartHost() @@ -1720,6 +1708,24 @@ void MainWindow::onNPTest() //Netplay::StartGame(); } +bool MainWindow::lanWarning(bool host) +{ + if (numEmuInstances() < 2) + return true; + + QString verb = host ? "host" : "join"; + QString msg = "Multiple emulator instances are currently open.\n" + "If you "+verb+" a LAN game now, all secondary instances will be closed.\n\n" + "Do you wish to continue?"; + + auto res = QMessageBox::warning(this, "melonDS", msg, QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + if (res == QMessageBox::No) + return false; + + deleteAllEmuInstances(1); + return true; +} + void MainWindow::onOpenEmuSettings() { emuThread->emuPause(); diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index b2c76e15..5286a23f 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -237,6 +237,8 @@ private: void createScreenPanel(); + bool lanWarning(bool host); + bool showOSD; bool hasOGL; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 69285e97..64aa664d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -124,12 +124,25 @@ void deleteEmuInstance(int id) emuInstances[id] = nullptr; } -void deleteAllEmuInstances() +void deleteAllEmuInstances(int first) { - for (int i = 0; i < kMaxEmuInstances; i++) + for (int i = first; i < kMaxEmuInstances; i++) deleteEmuInstance(i); } +int numEmuInstances() +{ + int ret = 0; + + for (int i = 0; i < kMaxEmuInstances; i++) + { + if (emuInstances[i]) + ret++; + } + + return ret; +} + void pathInit() { diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 5f2d7bb5..e49a5cc3 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -50,6 +50,7 @@ extern QString emuDirectory; bool createEmuInstance(); void deleteEmuInstance(int id); -void deleteAllEmuInstances(); +void deleteAllEmuInstances(int first = 0); +int numEmuInstances(); #endif // MAIN_H