Backport LAN (#2131)

backport the old LAN feature to the modern melonDS codebase.
This commit is contained in:
Arisotura
2024-08-10 23:20:50 +02:00
committed by GitHub
parent ec71b15505
commit 8d31875902
34 changed files with 4163 additions and 87 deletions

View File

@ -6,6 +6,9 @@ add_library(net-utils STATIC
Net_Slirp.cpp
PacketDispatcher.cpp
LocalMP.cpp
LAN.cpp
Netplay.cpp
MPInterface.cpp
)
target_include_directories(net-utils PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
@ -21,3 +24,12 @@ else()
add_subdirectory(libslirp EXCLUDE_FROM_ALL)
target_link_libraries(net-utils PUBLIC slirp)
endif()
if (USE_VCPKG)
find_package(unofficial-enet CONFIG REQUIRED)
target_link_libraries(net-utils PRIVATE unofficial::enet::enet)
else()
pkg_check_modules(ENet REQUIRED IMPORTED_TARGET libenet)
fix_interface_includes(PkgConfig::ENet)
target_link_libraries(net-utils PUBLIC PkgConfig::ENet)
endif()

1091
src/net/LAN.cpp Normal file

File diff suppressed because it is too large Load Diff

156
src/net/LAN.h Normal file
View File

@ -0,0 +1,156 @@
/*
Copyright 2016-2024 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 <string>
#include <vector>
#include <map>
#include <queue>
#include <enet/enet.h>
#ifndef socket_t
#ifdef __WIN32__
#include <winsock2.h>
#define socket_t SOCKET
#else
#define socket_t int
#endif
#endif
#include "types.h"
#include "Platform.h"
#include "MPInterface.h"
namespace melonDS
{
class LAN : public MPInterface
{
public:
LAN() noexcept;
LAN(const LAN&) = delete;
LAN& operator=(const LAN&) = delete;
LAN(LAN&& other) = delete;
LAN& operator=(LAN&& other) = delete;
~LAN() noexcept;
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];
PlayerStatus Status;
u32 Address;
bool IsLocalPlayer;
u32 Ping;
};
struct DiscoveryData
{
u32 Magic;
u32 Version;
u32 Tick;
char SessionName[64];
u8 NumPlayers;
u8 MaxPlayers;
u8 Status; // 0=idle 1=playing
};
bool StartDiscovery();
void EndDiscovery();
bool StartHost(const char* player, int numplayers);
bool StartClient(const char* player, const char* host);
void EndSession();
std::map<u32, DiscoveryData> GetDiscoveryList();
std::vector<Player> GetPlayerList();
int GetNumPlayers() { return NumPlayers; }
int GetMaxPlayers() { return MaxPlayers; }
void Process() override;
void Begin(int inst) override;
void End(int inst) override;
int SendPacket(int inst, u8* data, int len, u64 timestamp) override;
int RecvPacket(int inst, u8* data, u64* timestamp) override;
int SendCmd(int inst, u8* data, int len, u64 timestamp) override;
int SendReply(int inst, u8* data, int len, u64 timestamp, u16 aid) override;
int SendAck(int inst, u8* data, int len, u64 timestamp) override;
int RecvHostPacket(int inst, u8* data, u64* timestamp) override;
u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask) override;
private:
bool Inited;
bool Active;
bool IsHost;
ENetHost* Host;
ENetPeer* RemotePeers[16];
socket_t DiscoverySocket;
u32 DiscoveryLastTick;
std::map<u32, DiscoveryData> DiscoveryList;
Platform::Mutex* DiscoveryMutex;
Player Players[16];
int NumPlayers;
int MaxPlayers;
Platform::Mutex* PlayersMutex;
Player MyPlayer;
u32 HostAddress;
u16 ConnectedBitmask;
int MPRecvTimeout;
int LastHostID;
ENetPeer* LastHostPeer;
std::queue<ENetPacket*> RXQueue;
u32 FrameCount;
void ProcessDiscovery();
void HostUpdatePlayerList();
void ClientUpdatePlayerList();
void ProcessHostEvent(ENetEvent& event);
void ProcessClientEvent(ENetEvent& event);
void ProcessEvent(ENetEvent& event);
void ProcessLAN(int type);
int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp);
int RecvPacketGeneric(u8* packet, bool block, u64* timestamp);
};
}
#endif // LAN_H

View File

@ -19,8 +19,6 @@
#include <cstring>
#include "LocalMP.h"
#include "Platform.h"
#include "types.h"
using namespace melonDS;
using namespace melonDS::Platform;

View File

@ -21,6 +21,7 @@
#include "types.h"
#include "Platform.h"
#include "MPInterface.h"
namespace melonDS
{
@ -33,20 +34,11 @@ struct MPStatusData
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;
};
constexpr u32 kPacketQueueSize = 0x10000;
constexpr u32 kReplyQueueSize = 0x10000;
constexpr u32 kMaxFrameSize = 0x948;
class LocalMP
class LocalMP : public MPInterface
{
public:
LocalMP() noexcept;
@ -56,8 +48,7 @@ public:
LocalMP& operator=(LocalMP&& other) = delete;
~LocalMP() noexcept;
[[nodiscard]] int GetRecvTimeout() const noexcept { return RecvTimeout; }
void SetRecvTimeout(int timeout) noexcept { RecvTimeout = timeout; }
void Process() {}
void Begin(int inst);
void End(int inst);
@ -69,11 +60,13 @@ public:
int SendAck(int inst, u8* data, int len, u64 timestamp);
int RecvHostPacket(int inst, u8* data, u64* timestamp);
u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask);
private:
void FIFORead(int inst, int fifo, void* buf, int len) noexcept;
void FIFOWrite(int inst, int fifo, void* buf, int len) noexcept;
int SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp) noexcept;
int RecvPacketGeneric(int inst, u8* packet, bool block, u64* timestamp) noexcept;
Platform::Mutex* MPQueueLock;
MPStatusData MPStatus {};
u8 MPPacketQueue[kPacketQueueSize] {};
@ -81,8 +74,6 @@ private:
u32 PacketReadOffset[16] {};
u32 ReplyReadOffset[16] {};
int RecvTimeout = 25;
int LastHostID = -1;
Platform::Semaphore* SemPool[32] {};
};

68
src/net/MPInterface.cpp Normal file
View File

@ -0,0 +1,68 @@
/*
Copyright 2016-2024 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 "MPInterface.h"
#include "LocalMP.h"
#include "LAN.h"
namespace melonDS
{
class DummyMP : public MPInterface
{
public:
void Process() override {}
void Begin(int inst) override {}
void End(int inst) override {}
int SendPacket(int inst, u8* data, int len, u64 timestamp) override { return 0; }
int RecvPacket(int inst, u8* data, u64* timestamp) override { return 0; }
int SendCmd(int inst, u8* data, int len, u64 timestamp) override { return 0; }
int SendReply(int inst, u8* data, int len, u64 timestamp, u16 aid) override { return 0; }
int SendAck(int inst, u8* data, int len, u64 timestamp) override { return 0; }
int RecvHostPacket(int inst, u8* data, u64* timestamp) override { return 0; }
u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask) override { return 0; }
};
std::unique_ptr<MPInterface> MPInterface::Current(std::make_unique<DummyMP>());
MPInterfaceType MPInterface::CurrentType = MPInterface_Dummy;
void MPInterface::Set(MPInterfaceType type)
{
switch (type)
{
case MPInterface_Local:
Current = std::make_unique<LocalMP>();
break;
case MPInterface_LAN:
Current = std::make_unique<LAN>();
break;
default:
Current = std::make_unique<DummyMP>();
break;
}
CurrentType = type;
}
}

82
src/net/MPInterface.h Normal file
View File

@ -0,0 +1,82 @@
/*
Copyright 2016-2024 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 MPINTERFACE_H
#define MPINTERFACE_H
#include <memory>
#include "types.h"
namespace melonDS
{
// TODO: provision for excluding unwanted interfaces at compile time
enum MPInterfaceType
{
MPInterface_Dummy = -1,
MPInterface_Local,
MPInterface_LAN,
MPInterface_Netplay,
};
struct MPPacketHeader
{
u32 Magic;
u32 SenderID;
u32 Type; // 0=regular 1=CMD 2=reply 3=ack
u32 Length;
u64 Timestamp;
};
class MPInterface
{
public:
virtual ~MPInterface() = default;
static MPInterface& Get() { return *Current; }
static MPInterfaceType GetType() { return CurrentType; }
static void Set(MPInterfaceType type);
[[nodiscard]] int GetRecvTimeout() const noexcept { return RecvTimeout; }
void SetRecvTimeout(int timeout) noexcept { RecvTimeout = timeout; }
// function called every video frame
virtual void Process() = 0;
virtual void Begin(int inst) = 0;
virtual void End(int inst) = 0;
virtual int SendPacket(int inst, u8* data, int len, u64 timestamp) = 0;
virtual int RecvPacket(int inst, u8* data, u64* timestamp) = 0;
virtual int SendCmd(int inst, u8* data, int len, u64 timestamp) = 0;
virtual int SendReply(int inst, u8* data, int len, u64 timestamp, u16 aid) = 0;
virtual int SendAck(int inst, u8* data, int len, u64 timestamp) = 0;
virtual int RecvHostPacket(int inst, u8* data, u64* timestamp) = 0;
virtual u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask) = 0;
protected:
int RecvTimeout = 25;
private:
static MPInterfaceType CurrentType;
static std::unique_ptr<MPInterface> Current;
};
}
#endif // MPINTERFACE_H

1085
src/net/Netplay.cpp Normal file

File diff suppressed because it is too large Load Diff

57
src/net/Netplay.h Normal file
View File

@ -0,0 +1,57 @@
/*
Copyright 2016-2024 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
{
struct Player
{
int ID;
char Name[32];
int Status; // 0=no player 1=normal 2=host 3=connecting
melonDS::u32 Address;
};
extern bool Active;
bool Init();
void DeInit();
void StartHost(const char* player, int port);
void StartClient(const char* player, const char* host, int port);
void StartMirror(const Player* player);
melonDS::u32 PlayerAddress(int id);
void StartGame();
void StartLocal();
void StartGame();
void ProcessFrame();
void ProcessInput();
}
#endif // NETPLAY_H