start encapsulating LAN

This commit is contained in:
Arisotura 2024-08-07 16:39:30 +02:00
parent 7b40d3f6ca
commit 8e4daeace7
4 changed files with 234 additions and 172 deletions

View File

@ -45,14 +45,13 @@
#define INVALID_SOCKET (socket_t)-1
#endif
#include <enet/enet.h>
// REMOVEME REMOVEME REMOVEME
#include <SDL2/SDL.h>
#include "LAN.h"
using namespace melonDS;
namespace LAN
namespace melonDS
{
const u32 kDiscoveryMagic = 0x444E414C; // LAND
@ -61,6 +60,8 @@ const u32 kPacketMagic = 0x4946494E; // NIFI
const u32 kProtocolVersion = 1;
const u32 kLocalhost = 0x0100007F;
enum
{
Chan_Cmd = 0, // channel 0 -- control commands
@ -76,51 +77,14 @@ enum
Cmd_PlayerDisconnect, // 05 -- both -- signal disconnected state (not receiving MP frames)
};
struct MPPacketHeader
{
u32 Magic;
u32 SenderID;
u32 Type; // 0=regular 1=CMD 2=reply 3=ack
u32 Length;
u64 Timestamp;
};
const int kDiscoveryPort = 7063;
const int kLANPort = 7064;
socket_t DiscoverySocket;
u32 DiscoveryLastTick;
std::map<u32, DiscoveryData> DiscoveryList;
Platform::Mutex* DiscoveryMutex = nullptr;
bool Active;
bool IsHost;
ENetHost* Host;
ENetPeer* RemotePeers[16];
Player Players[16];
u32 PlayerPing[16];
int NumPlayers;
int MaxPlayers;
u16 ConnectedBitmask;
Player MyPlayer;
u32 HostAddress;
bool Lag;
int MPRecvTimeout;
int LastHostID;
ENetPeer* LastHostPeer;
std::queue<ENetPacket*> RXQueue;
u32 FrameCount;
bool Init()
LAN::LAN() noexcept : Inited(false)
{
DiscoveryMutex = Platform::Mutex_Create();
PlayersMutex = Platform::Mutex_Create();
DiscoverySocket = INVALID_SOCKET;
DiscoveryLastTick = 0;
@ -128,7 +92,7 @@ bool Init()
Active = false;
IsHost = false;
Host = nullptr;
Lag = false;
//Lag = false;
memset(RemotePeers, 0, sizeof(RemotePeers));
memset(Players, 0, sizeof(Players));
@ -144,19 +108,18 @@ bool Init()
FrameCount = 0;
// TODO we init enet here but also in Netplay
// that is redundant
// TODO make this somewhat nicer
if (enet_initialize() != 0)
{
printf("enet shat itself :(\n");
return false;
return;
}
printf("enet init OK\n");
return true;
Inited = true;
}
void DeInit()
LAN::~LAN() noexcept
{
if (DiscoverySocket)
{
@ -187,12 +150,46 @@ void DeInit()
enet_deinitialize();
Platform::Mutex_Free(DiscoveryMutex);
DiscoveryMutex = nullptr;
Platform::Mutex_Free(PlayersMutex);
}
bool StartDiscovery()
std::map<u32, LAN::DiscoveryData> LAN::GetDiscoveryList()
{
Platform::Mutex_Lock(DiscoveryMutex);
auto ret = DiscoveryList;
Platform::Mutex_Unlock(DiscoveryMutex);
return ret;
}
std::vector<LAN::Player> LAN::GetPlayerList()
{
Platform::Mutex_Lock(PlayersMutex);
std::vector<Player> ret;
for (int i = 0; i < 16; i++)
{
if (Players[i].Status == Player_None) continue;
// make a copy of the player entry, fix up the address field
Player newp = Players[i];
if (newp.ID == MyPlayer.ID)
newp.Address = kLocalhost;
else if (newp.Status == Player_Host)
newp.Address = HostAddress;
ret.push_back(newp);
}
Platform::Mutex_Unlock(PlayersMutex);
return ret;
}
bool LAN::StartDiscovery()
{
if (!Inited) return false;
int res;
DiscoverySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
@ -231,8 +228,10 @@ bool StartDiscovery()
return true;
}
void EndDiscovery()
void LAN::EndDiscovery()
{
if (!Inited) return;
if (DiscoverySocket != INVALID_SOCKET)
{
closesocket(DiscoverySocket);
@ -243,8 +242,11 @@ void EndDiscovery()
Active = false;
}
bool StartHost(const char* playername, int numplayers)
bool LAN::StartHost(const char* playername, int numplayers)
{
if (!Inited) return false;
if (numplayers > 16) return false;
ENetAddress addr;
addr.host = ENET_HOST_ANY;
addr.port = kLANPort;
@ -255,17 +257,21 @@ bool StartHost(const char* playername, int numplayers)
return false;
}
Platform::Mutex_Lock(PlayersMutex);
Player* player = &Players[0];
memset(player, 0, sizeof(Player));
player->ID = 0;
strncpy(player->Name, playername, 31);
player->Status = Player_Host;
player->Address = 0x0100007F;
player->Address = kLocalhost;
NumPlayers = 1;
MaxPlayers = numplayers;
memcpy(&MyPlayer, player, sizeof(Player));
HostAddress = 0x0100007F;
Platform::Mutex_Unlock(PlayersMutex);
HostAddress = kLocalhost;
LastHostID = -1;
LastHostPeer = nullptr;
@ -279,8 +285,10 @@ bool StartHost(const char* playername, int numplayers)
return true;
}
bool StartClient(const char* playername, const char* host)
bool LAN::StartClient(const char* playername, const char* host)
{
if (!Inited) return false;
Host = enet_host_create(nullptr, 16, 2, 0, 0);
if (!Host)
{
@ -298,12 +306,16 @@ bool StartClient(const char* playername, const char* host)
return false;
}
Platform::Mutex_Lock(PlayersMutex);
Player* player = &MyPlayer;
memset(player, 0, sizeof(Player));
player->ID = 0;
strncpy(player->Name, playername, 31);
player->Status = Player_Connecting;
Platform::Mutex_Unlock(PlayersMutex);
ENetEvent event;
int conn = 0;
u32 starttick = SDL_GetTicks();
@ -331,6 +343,7 @@ bool StartClient(const char* playername, const char* host)
u32 version = data[5] | (data[6] << 8) | (data[7] << 16) | (data[8] << 24);
if (magic != kLANMagic) continue;
if (version != kProtocolVersion) continue;
if (data[10] > 16) continue;
MaxPlayers = data[10];
@ -383,7 +396,7 @@ bool StartClient(const char* playername, const char* host)
}
void ProcessDiscovery()
void LAN::ProcessDiscovery()
{
if (DiscoverySocket == INVALID_SOCKET)
return;
@ -473,15 +486,10 @@ void ProcessDiscovery()
}
Platform::Mutex_Unlock(DiscoveryMutex);
// update the list in the connect dialog if needed
//if (lanClientDlg)
// lanClientDlg->updateDiscoveryList();
}
}
void HostUpdatePlayerList()
void LAN::HostUpdatePlayerList()
{
u8 cmd[2+sizeof(Players)];
cmd[0] = Cmd_PlayerList;
@ -489,18 +497,13 @@ void HostUpdatePlayerList()
memcpy(&cmd[2], Players, sizeof(Players));
ENetPacket* pkt = enet_packet_create(cmd, 2+sizeof(Players), ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(Host, Chan_Cmd, pkt);
//if (lanDlg)
// lanDlg->updatePlayerList();
}
void ClientUpdatePlayerList()
void LAN::ClientUpdatePlayerList()
{
//if (lanDlg)
// lanDlg->updatePlayerList();
}
void ProcessHostEvent(ENetEvent& event)
void LAN::ProcessHostEvent(ENetEvent& event)
{
switch (event.type)
{
@ -539,12 +542,16 @@ void ProcessHostEvent(ENetEvent& event)
ENetPacket* pkt = enet_packet_create(cmd, 11, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(event.peer, Chan_Cmd, pkt);
Platform::Mutex_Lock(PlayersMutex);
Players[id].ID = id;
Players[id].Status = Player_Connecting;
Players[id].Address = event.peer->address.host;
event.peer->data = &Players[id];
NumPlayers++;
Platform::Mutex_Unlock(PlayersMutex);
RemotePeers[id] = event.peer;
}
else
@ -604,10 +611,14 @@ void ProcessHostEvent(ENetEvent& event)
break;
}
Platform::Mutex_Lock(PlayersMutex);
player.Status = Player_Client;
player.Address = event.peer->address.host;
memcpy(hostside, &player, sizeof(Player));
Platform::Mutex_Unlock(PlayersMutex);
// broadcast updated player list
HostUpdatePlayerList();
}
@ -642,7 +653,7 @@ void ProcessHostEvent(ENetEvent& event)
}
}
void ProcessClientEvent(ENetEvent& event)
void LAN::ProcessClientEvent(ENetEvent& event)
{
switch (event.type)
{
@ -685,7 +696,9 @@ void ProcessClientEvent(ENetEvent& event)
int id = player->ID;
RemotePeers[id] = nullptr;
Platform::Mutex_Lock(PlayersMutex);
player->Status = Player_Disconnected;
Platform::Mutex_Unlock(PlayersMutex);
ClientUpdatePlayerList();
}
@ -703,6 +716,8 @@ void ProcessClientEvent(ENetEvent& event)
if (event.packet->dataLength != (2+sizeof(Players))) break;
if (data[1] > 16) break;
Platform::Mutex_Lock(PlayersMutex);
NumPlayers = data[1];
memcpy(Players, &data[2], sizeof(Players));
for (int i = 0; i < 16; i++)
@ -710,8 +725,7 @@ void ProcessClientEvent(ENetEvent& event)
Players[i].Name[31] = '\0';
}
//if (lanDlg)
// lanDlg->updatePlayerList();
Platform::Mutex_Unlock(PlayersMutex);
// establish connections to any new clients
for (int i = 0; i < 16; i++)
@ -765,7 +779,7 @@ void ProcessClientEvent(ENetEvent& event)
}
}
void ProcessEvent(ENetEvent& event)
void LAN::ProcessEvent(ENetEvent& event)
{
if (IsHost)
ProcessHostEvent(event);
@ -776,7 +790,7 @@ void ProcessEvent(ENetEvent& event)
// 0 = per-frame processing of events and eventual misc. frame
// 1 = checking if a misc. frame has arrived
// 2 = waiting for a MP frame
void Process(int type)
void LAN::ProcessLAN(int type)
{
if (!Host) return;
//printf("Process(%d): %d %d\n", type, RXQueue.empty(), RXQueue.size());
@ -868,10 +882,12 @@ void Process(int type)
}
}
void ProcessFrame()
void LAN::Process()
{
if (!Active) return;
ProcessDiscovery();
Process(0);
ProcessLAN(0);
FrameCount++;
if (FrameCount >= 60)
@ -893,12 +909,7 @@ void ProcessFrame()
}
void SetMPRecvTimeout(int timeout)
{
MPRecvTimeout = timeout;
}
void MPBegin()
void LAN::Begin(int inst)
{
if (!Host) return;
@ -911,7 +922,7 @@ void MPBegin()
enet_host_broadcast(Host, Chan_Cmd, pkt);
}
void MPEnd()
void LAN::End(int inst)
{
if (!Host) return;
@ -923,7 +934,7 @@ void MPEnd()
}
int SendMPPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
int LAN::SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
{
if (!Host) return 0;
@ -952,11 +963,11 @@ int SendMPPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
return len;
}
int RecvMPPacketGeneric(u8* packet, bool block, u64* timestamp)
int LAN::RecvPacketGeneric(u8* packet, bool block, u64* timestamp)
{
if (!Host) return 0;
Process(block ? 2 : 1);
ProcessLAN(block ? 2 : 1);
if (RXQueue.empty()) return 0;
ENetPacket* enetpacket = RXQueue.front();
@ -983,33 +994,33 @@ int RecvMPPacketGeneric(u8* packet, bool block, u64* timestamp)
}
int SendMPPacket(u8* packet, int len, u64 timestamp)
int LAN::SendPacket(int inst, u8* packet, int len, u64 timestamp)
{
return SendMPPacketGeneric(0, packet, len, timestamp);
return SendPacketGeneric(0, packet, len, timestamp);
}
int RecvMPPacket(u8* packet, u64* timestamp)
int LAN::RecvPacket(int inst, u8* packet, u64* timestamp)
{
return RecvMPPacketGeneric(packet, false, timestamp);
return RecvPacketGeneric(packet, false, timestamp);
}
int SendMPCmd(u8* packet, int len, u64 timestamp)
int LAN::SendCmd(int inst, u8* packet, int len, u64 timestamp)
{
return SendMPPacketGeneric(1, packet, len, timestamp);
return SendPacketGeneric(1, packet, len, timestamp);
}
int SendMPReply(u8* packet, int len, u64 timestamp, u16 aid)
int LAN::SendReply(int inst, u8* packet, int len, u64 timestamp, u16 aid)
{
return SendMPPacketGeneric(2 | (aid<<16), packet, len, timestamp);
return SendPacketGeneric(2 | (aid<<16), packet, len, timestamp);
}
int SendMPAck(u8* packet, int len, u64 timestamp)
int LAN::SendAck(int inst, u8* packet, int len, u64 timestamp)
{
return SendMPPacketGeneric(3, packet, len, timestamp);
return SendPacketGeneric(3, packet, len, timestamp);
}
int RecvMPHostPacket(u8* packet, u64* timestamp)
int LAN::RecvHostPacket(int inst, u8* packet, u64* timestamp)
{
if (LastHostID != -1)
{
@ -1019,10 +1030,10 @@ int RecvMPHostPacket(u8* packet, u64* timestamp)
return -1;
}
return RecvMPPacketGeneric(packet, true, timestamp);
return RecvPacketGeneric(packet, true, timestamp);
}
u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask)
u16 LAN::RecvReplies(int inst, u8* packets, u64 timestamp, u16 aidmask)
{
if (!Host) return 0;
@ -1034,7 +1045,7 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask)
for (;;)
{
Process(2);
ProcessLAN(2);
if (RXQueue.empty())
{
// no more replies available

View File

@ -22,77 +22,128 @@
#include <string>
#include <map>
#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 LAN
namespace melonDS
{
using namespace melonDS;
enum PlayerStatus
class LAN : public MPInterface
{
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
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;
};
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);
std::map<u32, DiscoveryData> GetDiscoveryList();
std::vector<Player> GetPlayerList();
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];
u32 PlayerPing[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);
};
struct Player
{
int ID;
char Name[32];
PlayerStatus Status;
u32 Address;
};
struct DiscoveryData
{
u32 Magic;
u32 Version;
u32 Tick;
char SessionName[64];
u8 NumPlayers;
u8 MaxPlayers;
u8 Status; // 0=idle 1=playing
};
extern bool Active;
extern std::map<u32, DiscoveryData> DiscoveryList;
extern Platform::Mutex* DiscoveryMutex; // TODO: turn into Platform::Mutex or rework this to be nicer
extern Player Players[16];
extern u32 PlayerPing[16];
extern int NumPlayers;
extern int MaxPlayers;
extern Player MyPlayer;
extern u32 HostAddress;
bool Init();
void DeInit();
bool StartDiscovery();
void EndDiscovery();
bool StartHost(const char* player, int numplayers);
bool StartClient(const char* player, const char* host);
void ProcessFrame();
void SetMPRecvTimeout(int timeout);
void MPBegin();
void MPEnd();
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

View File

@ -34,15 +34,6 @@ 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;

View File

@ -34,6 +34,15 @@ enum MPInterfaceType
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: