mirror of
https://github.com/afska/gba-link-connection.git
synced 2026-04-25 08:07:59 -05:00
Starting a redesign to reduce header size
This commit is contained in:
parent
9449481152
commit
9de9fc4f33
|
|
@ -154,7 +154,7 @@ Name | Type | Default | Description
|
|||
`forwarding` | **bool** | `true` | If `true`, the server forwards all messages to the clients. Otherwise, clients only see messages sent from the server (ignoring other peers).
|
||||
`retransmission` | **bool** | `true` | If `true`, the library handles retransmission for you, so there should be no packet loss.
|
||||
`maxPlayers` | **u8** *(2~5)* | `5` | Maximum number of allowed players. The adapter will accept connections after reaching the limit, but the library will ignore them. If your game only supports -for example- two players, set this to `2` as it will make transfers faster.
|
||||
`timeout` | **u32** | `5` | Number of *frames* without receiving *any* data to reset the connection.
|
||||
`timeout` | **u32** | `8` | Number of *frames* without receiving *any* data to reset the connection.
|
||||
`remoteTimeout` | **u32** | `10` | Number of *successful transfers* without a message from a client to mark the player as disconnected.
|
||||
`interval` | **u16** | `50` | Number of *1024cycles* (61.04μs) ticks between transfers *(50 = 3.052ms)*. It's the interval of Timer #`sendTimerId`.
|
||||
`sendTimerId` | **u8** *(0~3)* | `3` | GBA Timer to use for sending.
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ start:
|
|||
|
||||
// (1) Create a LinkWireless instance
|
||||
linkWireless = new LinkWireless(forwarding, retransmission, maxPlayers);
|
||||
// linkWireless->debug = [](std::string str) { log(str); };
|
||||
|
||||
if (firstTime) {
|
||||
// (2) Add the required interrupt service routines
|
||||
|
|
@ -249,6 +250,7 @@ void messageLoop() {
|
|||
u32 lastLostPacketPlayerId = 0;
|
||||
u32 lastLostPacketExpected = 0;
|
||||
u32 lastLostPacketReceived = 0;
|
||||
u32 lastLostPacketReceivedPacketId = 0;
|
||||
|
||||
while (true) {
|
||||
CHECK_ERRORS("Error :(")
|
||||
|
|
@ -303,6 +305,7 @@ void messageLoop() {
|
|||
lastLostPacketPlayerId = message.playerId;
|
||||
lastLostPacketExpected = expected;
|
||||
lastLostPacketReceived = message.data[0];
|
||||
lastLostPacketReceivedPacketId = message._packetId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -341,8 +344,7 @@ void messageLoop() {
|
|||
|
||||
// Debug output
|
||||
output += "\n_buffer: " + std::to_string(linkWireless->_getPendingCount());
|
||||
if (retransmission && !packetLossCheck && linkWireless->playerCount() > 2 &&
|
||||
linkWireless->playerCount() < 4) {
|
||||
if (retransmission && !packetLossCheck && linkWireless->playerCount() < 4) {
|
||||
output +=
|
||||
"\n_lastPkgId: " + std::to_string(linkWireless->_lastPacketId());
|
||||
output += "\n_nextPndngPkgId: " +
|
||||
|
|
@ -361,7 +363,8 @@ void messageLoop() {
|
|||
}
|
||||
if (packetLossCheck && lostPackets > 0) {
|
||||
output += "\n\n_lostPackets: " + std::to_string(lostPackets) + "\n";
|
||||
output += "_last: (" + std::to_string(lastLostPacketPlayerId) + ") " +
|
||||
output += "_last: (" + std::to_string(lastLostPacketPlayerId) + "->" +
|
||||
std::to_string(lastLostPacketReceivedPacketId) + ") " +
|
||||
std::to_string(lastLostPacketReceived) + " [vs " +
|
||||
std::to_string(lastLostPacketExpected) + "]";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,6 @@
|
|||
// That can cause packet loss. You might want to use libugba's instead.
|
||||
// (see examples)
|
||||
// --------------------------------------------------------------------------
|
||||
// `send(...)` restrictions:
|
||||
// - don't send 0xFFFF, it's reserved for errors!
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
#include <tonc_core.h>
|
||||
#include <string>
|
||||
|
|
@ -58,17 +55,20 @@
|
|||
#include "LinkGPIO.h"
|
||||
#include "LinkSPI.h"
|
||||
|
||||
// #include <functional>
|
||||
|
||||
// Buffer size
|
||||
#define LINK_WIRELESS_QUEUE_SIZE 30
|
||||
|
||||
#define LINK_WIRELESS_MAX_PLAYERS 5
|
||||
#define LINK_WIRELESS_MIN_PLAYERS 2
|
||||
#define LINK_WIRELESS_DEFAULT_TIMEOUT 5
|
||||
#define LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT 10
|
||||
#define LINK_WIRELESS_DEFAULT_TIMEOUT 0xfffffffe // 8 // TODO: FIX
|
||||
#define LINK_WIRELESS_DEFAULT_REMOTE_TIMEOUT 0xfffffffe // 10
|
||||
#define LINK_WIRELESS_DEFAULT_INTERVAL 50
|
||||
#define LINK_WIRELESS_DEFAULT_SEND_TIMER_ID 3
|
||||
#define LINK_WIRELESS_BASE_FREQUENCY TM_FREQ_1024
|
||||
#define LINK_WIRELESS_MSG_CONFIRMATION 0
|
||||
#define LINK_WIRELESS_PACKET_ID_BITS 6
|
||||
#define LINK_WIRELESS_MAX_PACKET_IDS (1 << LINK_WIRELESS_PACKET_ID_BITS)
|
||||
#define LINK_WIRELESS_PING_WAIT 50
|
||||
#define LINK_WIRELESS_TRANSFER_WAIT 15
|
||||
#define LINK_WIRELESS_BROADCAST_SEARCH_WAIT_FRAMES 60
|
||||
|
|
@ -123,6 +123,8 @@ const u16 LINK_WIRELESS_TIMER_IRQ_IDS[] = {IRQ_TIMER0, IRQ_TIMER1, IRQ_TIMER2,
|
|||
|
||||
class LinkWireless {
|
||||
public:
|
||||
// std::function<void(std::string str)> debug;
|
||||
|
||||
enum State {
|
||||
NEEDS_RESET,
|
||||
AUTHENTICATED,
|
||||
|
|
@ -145,11 +147,10 @@ class LinkWireless {
|
|||
WEIRD_PLAYER_ID = 7,
|
||||
SEND_DATA_FAILED = 8,
|
||||
RECEIVE_DATA_FAILED = 9,
|
||||
BAD_CONFIRMATION = 10,
|
||||
ACKNOWLEDGE_FAILED = 10,
|
||||
BAD_MESSAGE = 11,
|
||||
ACKNOWLEDGE_FAILED = 12,
|
||||
TIMEOUT = 13,
|
||||
REMOTE_TIMEOUT = 14
|
||||
TIMEOUT = 12,
|
||||
REMOTE_TIMEOUT = 13
|
||||
};
|
||||
|
||||
struct Message {
|
||||
|
|
@ -441,7 +442,7 @@ class LinkWireless {
|
|||
bool _canSend() { return !sessionState.outgoingMessages.isFull(); }
|
||||
u32 _getPendingCount() { return sessionState.outgoingMessages.size(); }
|
||||
u32 _lastPacketId() { return sessionState.lastPacketId; }
|
||||
u32 _lastConfirmationFromClient1() {
|
||||
int _lastConfirmationFromClient1() {
|
||||
return sessionState.lastConfirmationFromClients[1];
|
||||
}
|
||||
u32 _lastPacketIdFromClient1() {
|
||||
|
|
@ -608,18 +609,22 @@ class LinkWireless {
|
|||
u8 playerCount = 1;
|
||||
u8 currentPlayerId = 0;
|
||||
|
||||
bool didReceiveFirstPacketIdFromServer = false;
|
||||
u32 lastPacketId = 0;
|
||||
u32 lastPacketIdFromServer = 0;
|
||||
u32 lastConfirmationFromServer = 0;
|
||||
u32 lastPacketIdFromClients[LINK_WIRELESS_MAX_PLAYERS];
|
||||
u32 lastConfirmationFromClients[LINK_WIRELESS_MAX_PLAYERS];
|
||||
int lastConfirmationFromClients[LINK_WIRELESS_MAX_PLAYERS];
|
||||
};
|
||||
|
||||
struct MessageHeader {
|
||||
unsigned int packetId : 22;
|
||||
unsigned int size : 5;
|
||||
unsigned int packetId : LINK_WIRELESS_PACKET_ID_BITS;
|
||||
unsigned int isConfirmation : 1;
|
||||
unsigned int playerId : 3;
|
||||
unsigned int clientCount : 2;
|
||||
unsigned int dataChecksum : 4;
|
||||
|
||||
unsigned int size : 5;
|
||||
};
|
||||
|
||||
union MessageHeaderSerializer {
|
||||
|
|
@ -796,12 +801,12 @@ class LinkWireless {
|
|||
}
|
||||
|
||||
void sendPendingData() { // (irq only)
|
||||
u32 lastPacketId = setDataFromOutgoingMessages();
|
||||
int lastPacketId = setDataFromOutgoingMessages();
|
||||
sendCommandAsync(LINK_WIRELESS_COMMAND_SEND_DATA, true);
|
||||
clearOutgoingMessagesIfNeeded(lastPacketId);
|
||||
}
|
||||
|
||||
u32 setDataFromOutgoingMessages() { // (irq only)
|
||||
int setDataFromOutgoingMessages() { // (irq only)
|
||||
u32 maxTransferLength = getDeviceTransferLength();
|
||||
|
||||
addData(0, true);
|
||||
|
|
@ -811,13 +816,14 @@ class LinkWireless {
|
|||
else
|
||||
addPingMessageIfNeeded();
|
||||
|
||||
u32 lastPacketId = 0;
|
||||
int lastPacketId = -1;
|
||||
|
||||
sessionState.outgoingMessages.forEach(
|
||||
[this, maxTransferLength, &lastPacketId](Message message) {
|
||||
u8 size = message.dataSize;
|
||||
u32 header =
|
||||
buildMessageHeader(message.playerId, size, message._packetId);
|
||||
buildMessageHeader(message.playerId, size, message._packetId,
|
||||
size > 0 ? buildChecksum(message.data[0]) : 0);
|
||||
|
||||
if (dataSize /* -1 (wireless header) + 1 (msg header) */ + size >
|
||||
maxTransferLength)
|
||||
|
|
@ -847,10 +853,12 @@ class LinkWireless {
|
|||
serializer.asInt = result.responses[i];
|
||||
|
||||
MessageHeader header = serializer.asStruct;
|
||||
u8 remotePlayerCount = LINK_WIRELESS_MIN_PLAYERS + header.clientCount;
|
||||
u8 remotePlayerId = header.playerId;
|
||||
u8 size = header.size;
|
||||
u32 packetId = header.packetId;
|
||||
bool isConfirmation = header.isConfirmation;
|
||||
u8 remotePlayerId = header.playerId;
|
||||
u8 remotePlayerCount = LINK_WIRELESS_MIN_PLAYERS + header.clientCount;
|
||||
u32 checksum = header.dataChecksum;
|
||||
u8 size = header.size;
|
||||
|
||||
if (i + size >= result.responsesSize) {
|
||||
reset();
|
||||
|
|
@ -861,30 +869,10 @@ class LinkWireless {
|
|||
sessionState.timeouts[0] = 0;
|
||||
sessionState.timeouts[remotePlayerId] = 0;
|
||||
|
||||
if (state == SERVING) {
|
||||
if (config.retransmission &&
|
||||
packetId != LINK_WIRELESS_MSG_CONFIRMATION &&
|
||||
sessionState.lastPacketIdFromClients[remotePlayerId] > 0 &&
|
||||
packetId !=
|
||||
sessionState.lastPacketIdFromClients[remotePlayerId] + 1)
|
||||
goto skip;
|
||||
bool isFromSamePlayer = remotePlayerId == sessionState.currentPlayerId;
|
||||
bool hasBadChecksum = checksum != buildChecksum(result.responses[i + 1]);
|
||||
|
||||
if (packetId != LINK_WIRELESS_MSG_CONFIRMATION)
|
||||
sessionState.lastPacketIdFromClients[remotePlayerId] = packetId;
|
||||
} else {
|
||||
if (config.retransmission &&
|
||||
packetId != LINK_WIRELESS_MSG_CONFIRMATION &&
|
||||
sessionState.lastPacketIdFromServer > 0 &&
|
||||
packetId != sessionState.lastPacketIdFromServer + 1)
|
||||
goto skip;
|
||||
|
||||
sessionState.playerCount = remotePlayerCount;
|
||||
|
||||
if (packetId != LINK_WIRELESS_MSG_CONFIRMATION)
|
||||
sessionState.lastPacketIdFromServer = packetId;
|
||||
}
|
||||
|
||||
if (remotePlayerId == sessionState.currentPlayerId) {
|
||||
if (isFromSamePlayer || hasBadChecksum) {
|
||||
skip:
|
||||
i += size;
|
||||
continue;
|
||||
|
|
@ -898,15 +886,15 @@ class LinkWireless {
|
|||
message.dataSize = size;
|
||||
message.playerId = remotePlayerId;
|
||||
|
||||
if (config.retransmission &&
|
||||
packetId == LINK_WIRELESS_MSG_CONFIRMATION) {
|
||||
if (!handleConfirmation(message)) {
|
||||
reset();
|
||||
lastError = BAD_CONFIRMATION;
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
if (!acceptMessage(message, isConfirmation, remotePlayerCount))
|
||||
goto skip;
|
||||
|
||||
if (config.retransmission && isConfirmation) {
|
||||
if (!handleConfirmation(message))
|
||||
goto skip;
|
||||
} else {
|
||||
sessionState.tmpMessagesToReceive.push(message);
|
||||
}
|
||||
|
||||
i += size;
|
||||
}
|
||||
|
|
@ -915,8 +903,43 @@ class LinkWireless {
|
|||
return true;
|
||||
}
|
||||
|
||||
void clearOutgoingMessagesIfNeeded(u32 lastPacketId) { // (irq only)
|
||||
if (!config.retransmission)
|
||||
bool acceptMessage(Message& message,
|
||||
bool isConfirmation,
|
||||
u32 remotePlayerCount) { // (irq only)
|
||||
if (state == SERVING) {
|
||||
u32 expectedPacketId =
|
||||
(sessionState.lastPacketIdFromClients[message.playerId] + 1) %
|
||||
LINK_WIRELESS_MAX_PACKET_IDS;
|
||||
|
||||
if (config.retransmission && !isConfirmation &&
|
||||
message._packetId != expectedPacketId)
|
||||
return false;
|
||||
|
||||
if (!isConfirmation)
|
||||
sessionState.lastPacketIdFromClients[message.playerId] =
|
||||
message._packetId;
|
||||
} else {
|
||||
u32 expectedPacketId = (sessionState.lastPacketIdFromServer + 1) %
|
||||
LINK_WIRELESS_MAX_PACKET_IDS;
|
||||
|
||||
if (config.retransmission && !isConfirmation &&
|
||||
sessionState.didReceiveFirstPacketIdFromServer &&
|
||||
message._packetId != expectedPacketId)
|
||||
return false;
|
||||
|
||||
sessionState.playerCount = remotePlayerCount;
|
||||
|
||||
if (!isConfirmation) {
|
||||
sessionState.didReceiveFirstPacketIdFromServer = true;
|
||||
sessionState.lastPacketIdFromServer = message._packetId;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clearOutgoingMessagesIfNeeded(int lastPacketId) { // (irq only)
|
||||
if (!config.retransmission && lastPacketId > -1)
|
||||
removeConfirmedMessages(lastPacketId);
|
||||
}
|
||||
|
||||
|
|
@ -924,7 +947,7 @@ class LinkWireless {
|
|||
if (sessionState.outgoingMessages.isEmpty() && !sessionState.pingSent) {
|
||||
Message emptyMessage;
|
||||
emptyMessage.playerId = sessionState.currentPlayerId;
|
||||
emptyMessage._packetId = ++sessionState.lastPacketId;
|
||||
emptyMessage._packetId = newPacketId();
|
||||
sessionState.outgoingMessages.push(emptyMessage);
|
||||
sessionState.pingSent = true;
|
||||
}
|
||||
|
|
@ -932,11 +955,13 @@ class LinkWireless {
|
|||
|
||||
void addConfirmations() { // (irq only)
|
||||
if (state == SERVING) {
|
||||
addData(buildConfirmationHeader(0));
|
||||
addData(
|
||||
buildConfirmationHeader(0, sessionState.lastPacketIdFromClients[1]));
|
||||
for (u32 i = 0; i < (u32)(config.maxPlayers - 1); i++)
|
||||
addData(sessionState.lastPacketIdFromClients[1 + i]);
|
||||
} else {
|
||||
addData(buildConfirmationHeader(sessionState.currentPlayerId));
|
||||
addData(buildConfirmationHeader(sessionState.currentPlayerId,
|
||||
sessionState.lastPacketIdFromServer));
|
||||
addData(sessionState.lastPacketIdFromServer);
|
||||
}
|
||||
}
|
||||
|
|
@ -952,53 +977,79 @@ class LinkWireless {
|
|||
confirmation.dataSize != (u32)(config.maxPlayers - 1))
|
||||
return false;
|
||||
|
||||
sessionState.lastConfirmationFromServer =
|
||||
u32 confirmationData =
|
||||
confirmation.data[sessionState.currentPlayerId - 1];
|
||||
removeConfirmedMessages(sessionState.lastConfirmationFromServer);
|
||||
u32 lastConfirmation = sessionState.lastConfirmationFromServer;
|
||||
sessionState.lastConfirmationFromServer = confirmationData;
|
||||
|
||||
if (sessionState.lastConfirmationFromServer != lastConfirmation)
|
||||
removeConfirmedMessages(confirmationData);
|
||||
} else {
|
||||
if (state != SERVING || confirmation.dataSize != 1)
|
||||
return false;
|
||||
|
||||
u32 confirmationData = confirmation.data[0];
|
||||
int lastConfirmation =
|
||||
sessionState.lastConfirmationFromClients[confirmation.playerId];
|
||||
sessionState.lastConfirmationFromClients[confirmation.playerId] =
|
||||
confirmationData;
|
||||
if (lastConfirmation == -1 ||
|
||||
sessionState.lastConfirmationFromClients[confirmation.playerId] !=
|
||||
lastConfirmation)
|
||||
removeConfirmedMessages(confirmationData); // TODO: FIX!
|
||||
|
||||
u32 min = 0xffffffff;
|
||||
for (u32 i = 0; i < (u32)(config.maxPlayers - 1); i++) {
|
||||
u32 confirmationData = sessionState.lastConfirmationFromClients[1 + i];
|
||||
if (confirmationData > 0 && confirmationData < min)
|
||||
min = confirmationData;
|
||||
}
|
||||
if (min < 0xffffffff)
|
||||
removeConfirmedMessages(min);
|
||||
// TODO: USE > DIFF
|
||||
// u32 min = 0xffffffff;
|
||||
// for (u32 i = 0; i < (u32)(config.maxPlayers - 1); i++) {
|
||||
// int confirmationData = sessionState.lastConfirmationFromClients[1 +
|
||||
// i]; if (confirmationData > -1 && (u32)confirmationData < min)
|
||||
// min = confirmationData;
|
||||
// }
|
||||
// if (min < 0xffffffff)
|
||||
// removeConfirmedMessages(min);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void removeConfirmedMessages(u32 confirmation) { // (irq only)
|
||||
while (!sessionState.outgoingMessages.isEmpty() &&
|
||||
sessionState.outgoingMessages.peek()._packetId <= confirmation)
|
||||
while (!sessionState.outgoingMessages.isEmpty()) {
|
||||
u32 packetId = sessionState.outgoingMessages.peek()._packetId;
|
||||
sessionState.outgoingMessages.pop();
|
||||
if (packetId == confirmation)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u32 buildConfirmationHeader(u8 playerId) { // (irq only)
|
||||
u32 buildConfirmationHeader(u8 playerId, u32 confirmation) { // (irq only)
|
||||
return buildMessageHeader(playerId,
|
||||
playerId == 0 ? config.maxPlayers - 1 : 1, 0);
|
||||
playerId == 0 ? config.maxPlayers - 1 : 1, 0,
|
||||
buildChecksum(confirmation), true);
|
||||
}
|
||||
|
||||
u32 buildMessageHeader(u8 playerId, u8 size, u32 packetId) { // (irq only)
|
||||
u32 buildMessageHeader(u8 playerId,
|
||||
u8 size,
|
||||
u32 packetId,
|
||||
u8 dataChecksum,
|
||||
bool isConfirmation = false) { // (irq only)
|
||||
MessageHeader header;
|
||||
header.clientCount = sessionState.playerCount - LINK_WIRELESS_MIN_PLAYERS;
|
||||
header.playerId = playerId;
|
||||
header.size = size;
|
||||
header.packetId = packetId;
|
||||
header.isConfirmation = isConfirmation;
|
||||
header.playerId = playerId;
|
||||
header.clientCount = sessionState.playerCount - LINK_WIRELESS_MIN_PLAYERS;
|
||||
header.dataChecksum = dataChecksum;
|
||||
header.size = size;
|
||||
|
||||
MessageHeaderSerializer serializer;
|
||||
serializer.asStruct = header;
|
||||
return serializer.asInt;
|
||||
}
|
||||
|
||||
u32 buildChecksum(u16 data) { // (irq only)
|
||||
// (hamming weight)
|
||||
return __builtin_popcount(data) % 16;
|
||||
}
|
||||
|
||||
void trackRemoteTimeouts() { // (irq only)
|
||||
for (u32 i = 0; i < sessionState.playerCount; i++)
|
||||
if (i != sessionState.currentPlayerId)
|
||||
|
|
@ -1063,13 +1114,14 @@ class LinkWireless {
|
|||
this->sessionState.sendReceiveLatch = false;
|
||||
this->sessionState.pingSent = false;
|
||||
this->sessionState.shouldWaitForServer = false;
|
||||
this->sessionState.didReceiveFirstPacketIdFromServer = false;
|
||||
this->sessionState.lastPacketId = 0;
|
||||
this->sessionState.lastPacketIdFromServer = 0;
|
||||
this->sessionState.lastConfirmationFromServer = 0;
|
||||
for (u32 i = 0; i < LINK_WIRELESS_MAX_PLAYERS; i++) {
|
||||
this->sessionState.timeouts[i] = 0;
|
||||
this->sessionState.lastPacketIdFromClients[i] = 0;
|
||||
this->sessionState.lastConfirmationFromClients[i] = 0;
|
||||
this->sessionState.lastConfirmationFromClients[i] = -1;
|
||||
}
|
||||
this->asyncCommand.isActive = false;
|
||||
this->dataSize = 0;
|
||||
|
|
@ -1135,7 +1187,7 @@ class LinkWireless {
|
|||
auto message = sessionState.tmpMessagesToSend.pop();
|
||||
|
||||
if (isSessionActive()) {
|
||||
message._packetId = ++sessionState.lastPacketId;
|
||||
message._packetId = newPacketId();
|
||||
sessionState.outgoingMessages.push(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -1158,6 +1210,11 @@ class LinkWireless {
|
|||
}
|
||||
}
|
||||
|
||||
u32 newPacketId() { // (irq only)
|
||||
return sessionState.lastPacketId =
|
||||
(sessionState.lastPacketId + 1) % LINK_WIRELESS_MAX_PACKET_IDS;
|
||||
}
|
||||
|
||||
void pingAdapter() {
|
||||
linkGPIO->setMode(LinkGPIO::Pin::SO, LinkGPIO::Direction::OUTPUT);
|
||||
linkGPIO->setMode(LinkGPIO::Pin::SD, LinkGPIO::Direction::OUTPUT);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user