melonDS/src/net/PacketDispatcher.cpp

161 lines
3.8 KiB
C++

/*
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 "PacketDispatcher.h"
using namespace melonDS;
struct PacketHeader
{
u32 magic;
u32 senderID;
u32 headerLength;
u32 dataLength;
};
const u32 kPacketMagic = 0x4B504C4D;
PacketDispatcher::PacketDispatcher() : mutex(Platform::Mutex_Create())
{
instanceMask = 0;
}
PacketDispatcher::~PacketDispatcher()
{
Platform::Mutex_Free(mutex);
}
void PacketDispatcher::registerInstance(int inst)
{
Mutex_Lock(mutex);
instanceMask |= (1 << inst);
packetQueues[inst] = std::make_unique<PacketQueue>();
Mutex_Unlock(mutex);
}
void PacketDispatcher::unregisterInstance(int inst)
{
Mutex_Lock(mutex);
instanceMask &= ~(1 << inst);
packetQueues[inst] = nullptr;
Mutex_Unlock(mutex);
}
void PacketDispatcher::clear()
{
Mutex_Lock(mutex);
for (int i = 0; i < 16; i++)
{
if (!(instanceMask & (1 << i)))
continue;
packetQueues[i]->Clear();
}
Mutex_Unlock(mutex);
}
void PacketDispatcher::sendPacket(const void* header, int headerlen, const void* data, int datalen, int sender, u16 recv_mask)
{
if (!header) headerlen = 0;
if (!data) datalen = 0;
if ((!headerlen) && (!datalen)) return;
if ((sizeof(PacketHeader) + headerlen + datalen) >= 0x8000) return;
if (sender < 0 || sender > 16) return;
recv_mask &= instanceMask;
if (sender < 16) recv_mask &= ~(1 << sender);
if (!recv_mask) return;
PacketHeader phdr;
phdr.magic = kPacketMagic;
phdr.senderID = sender;
phdr.headerLength = headerlen;
phdr.dataLength = datalen;
int totallen = sizeof(phdr) + headerlen + datalen;
Mutex_Lock(mutex);
for (int i = 0; i < 16; i++)
{
if (!(recv_mask & (1 << i)))
continue;
PacketQueue* queue = packetQueues[i].get();
// if we run out of space: discard old packets
while (!queue->CanFit(totallen))
{
PacketHeader tmp;
queue->Read(&tmp, sizeof(tmp));
queue->Skip(tmp.headerLength + tmp.dataLength);
}
queue->Write(&phdr, sizeof(phdr));
if (headerlen) queue->Write(header, headerlen);
if (datalen) queue->Write(data, datalen);
}
Mutex_Unlock(mutex);
}
bool PacketDispatcher::recvPacket(void *header, int *headerlen, void *data, int *datalen, int receiver)
{
if ((!header) && (!data)) return false;
if (receiver < 0 || receiver > 15) return false;
Mutex_Lock(mutex);
PacketQueue* queue = packetQueues[receiver].get();
PacketHeader phdr;
if (!queue->Read(&phdr, sizeof(phdr)))
{
Mutex_Unlock(mutex);
return false;
}
if (phdr.magic != kPacketMagic)
{
Mutex_Unlock(mutex);
return false;
}
if (phdr.headerLength)
{
if (headerlen) *headerlen = phdr.headerLength;
if (header) queue->Read(header, phdr.headerLength);
else queue->Skip(phdr.headerLength);
}
if (phdr.dataLength)
{
if (datalen) *datalen = phdr.dataLength;
if (data) queue->Read(data, phdr.dataLength);
else queue->Skip(phdr.dataLength);
}
Mutex_Unlock(mutex);
return true;
}