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