sm64coopdx/src/pc/network/packets/packet_ordered.c
2023-11-08 19:58:32 -05:00

255 lines
7.3 KiB
C

#include <stdio.h>
#include "../network.h"
#include "pc/utils/misc.h"
//#define DISABLE_MODULE_LOG 1
#include "pc/debuglog.h"
#define PACKET_ORDERED_TIMEOUT 30
struct OrderedPacketList {
struct Packet p;
struct OrderedPacketList* next;
};
struct OrderedPacketTable {
u8 fromGlobalId;
u16 groupId;
u16 processSeqId;
f32 lastReceived;
struct OrderedPacketList* packets;
struct OrderedPacketTable* next;
};
static struct OrderedPacketTable* orderedPacketTable[MAX_PLAYERS] = { 0 };
u8 gAllowOrderedPacketClear = 1;
static void packet_ordered_check_for_processing(struct OrderedPacketTable* opt) {
if (!opt) { return; }
struct OrderedPacketList* opl = opt->packets;
struct OrderedPacketList* oplLast = opl;
struct Packet* p = NULL;
while (opl != NULL) {
if (opt->processSeqId == opl->p.orderedSeqId) {
// we found the packet we're supposed to process!
p = &opl->p;
break;
}
oplLast = opl;
opl = opl->next;
}
// make sure we found the packet
if (p == NULL) { return; }
// process it
packet_process(p);
LOG_INFO("processed ordered packet (%d, %d, %d)", p->orderedFromGlobalId, p->orderedGroupId, p->orderedSeqId);
// remove from linked list
if (oplLast == opl) {
// we processed the head of the list
opt->packets = opl->next;
} else if (oplLast) {
// we processed from after the head
oplLast->next = opl->next;
}
// deallocate
free(opl);
// find the next one we have to process.
opt->processSeqId++;
packet_ordered_check_for_processing(opt);
}
static void packet_ordered_add_to_table(struct OrderedPacketTable* opt, struct Packet* p) {
// sanity check
SOFT_ASSERT(opt != NULL);
SOFT_ASSERT(opt->fromGlobalId == p->orderedFromGlobalId);
SOFT_ASSERT(opt->groupId == p->orderedGroupId);
if (p->orderedSeqId < opt->processSeqId) {
// this packet has already been processed!
LOG_INFO("this packet has already been processed!");
return;
}
if (p->orderedSeqId == opt->processSeqId) {
// this is the packet that the table is waiting on, process it!
packet_process(p);
// find the next one we have to process.
opt->processSeqId++;
packet_ordered_check_for_processing(opt);
return;
}
struct OrderedPacketList* opl = opt->packets;
struct OrderedPacketList* oplLast = opl;
// make sure this packet isn't currently in the list
while (opl != NULL) {
if (opl->p.orderedSeqId == p->orderedSeqId) {
// this packet is already in the list!
LOG_INFO("this packet is already in the list!");
return;
}
// iterate
oplLast = opl;
opl = opl->next;
}
// allocate the packet list
opl = calloc(1, sizeof(struct OrderedPacketList));
if (oplLast == NULL) {
opt->packets = opl;
} else {
oplLast->next = opl;
}
// copy the packet over to the list
memcpy(&opl->p, p, sizeof(struct Packet));
opl->next = NULL;
LOG_INFO("added to list for (%d, %d, %d)", opt->fromGlobalId, opt->groupId, p->orderedSeqId);
opt->lastReceived = clock_elapsed();
packet_ordered_check_for_processing(opt);
}
void packet_ordered_add(struct Packet* p) {
u8 globalId = p->orderedFromGlobalId;
if (globalId > MAX_PLAYERS) { return; }
struct OrderedPacketTable* opt = orderedPacketTable[globalId];
// try to find a ordered packet table for the packet's group
struct OrderedPacketTable* optLast = opt;
while (opt != NULL) {
if (opt->groupId == p->orderedGroupId) {
// found a matching group
packet_ordered_add_to_table(opt, p);
return;
}
// iterate
optLast = opt;
opt = opt->next;
}
// could not find a matching group, allocate a ordered packet table
opt = malloc(sizeof(struct OrderedPacketTable));
// put the opt in the right place
if (optLast == NULL) {
orderedPacketTable[globalId] = opt;
} else {
optLast->next = opt;
}
// set opt params
opt->fromGlobalId = p->orderedFromGlobalId;
opt->groupId = p->orderedGroupId;
opt->processSeqId = 1;
opt->packets = NULL;
opt->next = NULL;
opt->lastReceived = clock_elapsed();
LOG_INFO("created table for (%d, %d)", opt->fromGlobalId, opt->groupId);
// add the packet to the table
packet_ordered_add_to_table(opt, p);
}
void packet_ordered_clear_table(u8 globalIndex, u16 groupId) {
LOG_INFO("clearing out ordered packet table for %d (%d)", globalIndex, groupId);
if (globalIndex > MAX_PLAYERS) { return; }
struct OrderedPacketTable* opt = orderedPacketTable[globalIndex];
struct OrderedPacketTable* optLast = opt;
while (opt != NULL) {
if (opt->groupId == groupId) {
// clear opl of table
struct OrderedPacketList* opl = opt->packets;
while (opl != NULL) {
struct OrderedPacketList* oplNext = opl->next;
free(opl);
opl = oplNext;
LOG_INFO("cleared out opl");
}
// remove from linked list
if (optLast == opt) {
orderedPacketTable[globalIndex] = opt->next;
} else {
optLast->next = opt->next;
}
// deallocate table
free(opt);
LOG_INFO("cleared out opt");
return;
}
// goto the next table
optLast = opt;
opt = opt->next;
}
}
void packet_ordered_clear(u8 globalIndex) {
if (globalIndex > MAX_PLAYERS) { return; }
if (!gAllowOrderedPacketClear) {
LOG_INFO("disallowed ordered packets to be cleared");
return;
}
LOG_INFO("clearing out all ordered packet tables for %d", globalIndex);
struct OrderedPacketTable* opt = orderedPacketTable[globalIndex];
while (opt != NULL) {
// clear opl of table
struct OrderedPacketList* opl = opt->packets;
while (opl != NULL) {
struct OrderedPacketList* oplNext = opl->next;
free(opl);
opl = oplNext;
LOG_INFO("cleared out opl");
}
// goto next table and free the current one
struct OrderedPacketTable* optNext = opt->next;
free(opt);
opt = optNext;
LOG_INFO("cleared out opt");
}
orderedPacketTable[globalIndex] = NULL;
}
void packet_ordered_clear_all(void) {
gAllowOrderedPacketClear = 1;
for (int i = 0; i < MAX_PLAYERS; i++) {
packet_ordered_clear(i);
}
}
void packet_ordered_update(void) {
f32 currentClock = clock_elapsed();
// check all ordered tables for a time out
for (s32 i = 0; i < MAX_PLAYERS; i++) {
struct OrderedPacketTable* opt = orderedPacketTable[i];
while (opt != NULL) {
struct OrderedPacketTable* optNext = opt->next;
float elapsed = (currentClock - opt->lastReceived);
if (elapsed > PACKET_ORDERED_TIMEOUT) {
// too much time has elapsed since we last received a packet for this group, forget the table!
packet_ordered_clear_table(i, opt->groupId);
}
opt = optNext;
}
}
}