Adding Doxygen-style docs for IDE autocompletion in LinkWireless

This commit is contained in:
Rodrigo Alfonso
2024-08-08 07:49:03 -03:00
parent 184419f7de
commit 514c02ccf0
3 changed files with 228 additions and 11 deletions

View File

@@ -195,12 +195,12 @@ Name | Return type | Description
`getServersAsyncStart()` | **bool** | Starts looking for broadcasting servers and changes the state to `SEARCHING`. After this, call `getServersAsyncEnd(...)` 1 second later.
`getServersAsyncEnd(servers)` | **bool** | Fills the `servers` array with all the currently broadcasting servers. Changes the state to `AUTHENTICATED` again.
`connect(serverId)` | **bool** | Starts a connection with `serverId` and changes the state to `CONNECTING`.
`keepConnecting()` | **bool** | When connecting, this needs to be called until the state is `CONNECTED`. It assigns a player ID. Keep in mind that `isConnected()` and `playerCount()` won't be updated until the first message from server arrives.
`keepConnecting()` | **bool** | When connecting, this needs to be called until the state is `CONNECTED`. It assigns a player ID. Keep in mind that `isConnected()` and `playerCount()` won't be updated until the first message from the server arrives.
`send(data)` | **bool** | Enqueues `data` to be sent to other nodes.
`receive(messages)` | **bool** | Fills the `messages` array with incoming messages, forwarding if needed.
`getState()` | **LinkWireless::State** | Returns the current state (one of `LinkWireless::State::NEEDS_RESET`, `LinkWireless::State::AUTHENTICATED`, `LinkWireless::State::SEARCHING`, `LinkWireless::State::SERVING`, `LinkWireless::State::CONNECTING`, or `LinkWireless::State::CONNECTED`).
`isConnected()` | **bool** | Returns true if the player count is higher than 1.
`isSessionActive()` | **bool** | Returns true if the state is `SERVING` or `CONNECTED`.
`isConnected()` | **bool** | Returns `true` if the player count is higher than `1`.
`isSessionActive()` | **bool** | Returns `true` if the state is `SERVING` or `CONNECTED`.
`playerCount()` | **u8** *(1~5)* | Returns the number of connected players.
`currentPlayerId()` | **u8** *(0~4)* | Returns the current player ID.
`getLastError([clear])` | **LinkWireless::Error** | If one of the other methods returns `false`, you can inspect this to know the cause. After this call, the last error is cleared if `clear` is `true` (default behavior).

View File

@@ -789,12 +789,12 @@ class LinkRawWireless {
[[nodiscard]] State getState() { return state; }
/**
* @brief Returns true if the player count is higher than 1.
* @brief Returns `true` if the player count is higher than `1`.
*/
[[nodiscard]] bool isConnected() { return sessionState.playerCount > 1; }
/**
* @brief Returns true if the state is `SERVING` or `CONNECTED`.
* @brief Returns `true` if the state is `SERVING` or `CONNECTED`.
*/
[[nodiscard]] bool isSessionActive() {
return state == SERVING || state == CONNECTED;

View File

@@ -63,19 +63,43 @@
// #include <string>
// #include <functional>
// Buffer size
/**
* @brief Buffer size (how many incoming and outgoing messages the queues can
* store at max). The default value is `30`, which seems fine for most games.
* \warning This affects how much memory is allocated. With the default value,
* it's `960` bytes. There's a double-buffered incoming queue and a
* double-buffered outgoing queue (to avoid data races).
* \warning You can calculate the usage with `LINK_WIRELESS_QUEUE_SIZE * 32`.
*/
#define LINK_WIRELESS_QUEUE_SIZE 30
// Max server transfer length
/**
* @brief Max server transfer length per timer tick. Must be in the range
* `[6;20]`. The default value is `20`, but you might want to set it a bit lower
* to reduce CPU usage.
*/
#define LINK_WIRELESS_MAX_SERVER_TRANSFER_LENGTH 20
// Max client transfer length
/**
* @brief Max client transfer length per timer tick. Must be in the range
* `[2;4]`. The default value is `4`. Changing this is not recommended, it's
* already too low.
*/
#define LINK_WIRELESS_MAX_CLIENT_TRANSFER_LENGTH 4
// Put Interrupt Service Routines (ISR) in IWRAM (uncomment to enable)
/**
* @brief Put Interrupt Service Routines (ISR) in IWRAM (uncomment to enable).
* This can significantly improve performance due to its faster access, but it's
* disabled by default to conserve IWRAM space, which is limited.
*/
// #define LINK_WIRELESS_PUT_ISR_IN_IWRAM
// Use send/receive latch (uncomment to enable)
/**
* @brief Use send/receive latch (uncomment to enable).
* This makes it alternate between sends and receives on each timer tick
* (instead of doing both things). Enabling it will introduce some latency but
* also reduce overall CPU usage.
*/
// #define LINK_WIRELESS_USE_SEND_RECEIVE_LATCH
static volatile char LINK_WIRELESS_VERSION[] = "LinkWireless/v7.0.0";
@@ -108,6 +132,9 @@ static volatile char LINK_WIRELESS_VERSION[] = "LinkWireless/v7.0.0";
if (!reset()) \
return false;
/**
* @brief A high level driver for the GBA Wireless Adapter.
*/
class LinkWireless {
private:
using u32 = unsigned int;
@@ -213,6 +240,27 @@ class LinkWireless {
bool isFull() { return currentPlayerCount == 0; }
};
/**
* @brief Constructs a new LinkWireless object.
* @param forwarding If `true`, the server forwards all messages to the
* clients. Otherwise, clients only see messages sent from the server
* (ignoring other peers).
* @param retransmission If `true`, the library handles retransmission for
* you, so there should be no packet loss.
* @param maxPlayers Maximum number of allowed players. If your game only
* supports -for example- two players, set this to `2` as it will make
* transfers faster.
* @param timeout Number of *frames* without receiving *any* data to reset the
* connection.
* @param remoteTimeout Number of *successful transfers* without a message
* from a client to mark the player as disconnected.
* @param interval Number of *1024-cycle ticks* (61.04μs) between transfers
* *(50 = 3.052ms)*. It's the interval of Timer #`sendTimerId`. Lower values
* will transfer faster but also consume more CPU.
* @param sendTimerId GBA Timer to use for sending.
* @param asyncACKTimerId GBA Timer to use for ACKs. If you have free timers,
* use one here to reduce CPU usage.
*/
explicit LinkWireless(
bool forwarding = true,
bool retransmission = true,
@@ -232,8 +280,16 @@ class LinkWireless {
this->config.asyncACKTimerId = asyncACKTimerId;
}
/**
* @brief Returns whether the library is active or not.
*/
[[nodiscard]] bool isActive() { return isEnabled; }
/**
* @brief Activates the library. When an adapter is connected, it changes the
* state to `AUTHENTICATED`. It can also be used to disconnect or reset the
* adapter.
*/
bool activate() {
lastError = NONE;
isEnabled = false;
@@ -246,6 +302,11 @@ class LinkWireless {
return success;
}
/**
* @brief Puts the adapter into a low consumption mode and then deactivates
* the library. It returns a boolean indicating whether the transition to low
* consumption mode was successful.
*/
bool deactivate() {
activate();
bool success = sendCommand(COMMAND_BYE).success;
@@ -258,6 +319,17 @@ class LinkWireless {
return success;
}
/**
* @brief Starts broadcasting a server and changes the state to `SERVING`. You
* can optionally provide data that games will be able to read. If the adapter
* is already serving, this method only updates the broadcast data.
* @param gameName Game name. Maximum `14` characters + NULL terminator.
* @param userName User name. Maximum `8` characters + NULL terminator.
* @param gameId `(0 ~ 0x7FFF)` Game ID.
* \warning Updating broadcast data while serving can fail if the adapter is
* busy. In that case, this will return `false` and `getLastError()` will be
* `BUSY_TRY_AGAIN`.
*/
bool serve(const char* gameName = "",
const char* userName = "",
u16 gameId = MAX_GAME_ID) {
@@ -319,10 +391,25 @@ class LinkWireless {
return true;
}
/**
* @brief Fills the `servers` array with all the currently broadcasting
* servers.
* @param servers The array to be filled with data.
* \warning This action takes 1 second to complete.
* \warning For an async version, see `getServersAsyncStart()`.
*/
bool getServers(Server servers[]) {
return getServers(servers, []() {});
}
/**
* @brief Fills the `servers` array with all the currently broadcasting
* servers.
* @param servers The array to be filled with data.
* @param onWait A function which will be invoked each time VBlank starts.
* \warning This action takes 1 second to complete.
* \warning For an async version, see `getServersAsyncStart()`.
*/
template <typename F>
bool getServers(Server servers[], F onWait) {
if (!getServersAsyncStart())
@@ -336,6 +423,10 @@ class LinkWireless {
return true;
}
/**
* @brief Starts looking for broadcasting servers and changes the state to
* `SEARCHING`. After this, call `getServersAsyncEnd(...)` 1 second later.
*/
bool getServersAsyncStart() {
LINK_WIRELESS_RESET_IF_NEEDED
if (state != AUTHENTICATED) {
@@ -356,6 +447,11 @@ class LinkWireless {
return true;
}
/**
* @brief Fills the `servers` array with all the currently broadcasting
* servers. Changes the state to `AUTHENTICATED` again.
* @param servers The array to be filled with data.
*/
bool getServersAsyncEnd(Server servers[]) {
LINK_WIRELESS_RESET_IF_NEEDED
if (state != SEARCHING) {
@@ -412,6 +508,11 @@ class LinkWireless {
return true;
}
/**
* @brief Starts a connection with `serverId` and changes the state to
* `CONNECTING`.
* @param serverId Device ID of the server.
*/
bool connect(u16 serverId) {
LINK_WIRELESS_RESET_IF_NEEDED
if (state != AUTHENTICATED) {
@@ -433,6 +534,12 @@ class LinkWireless {
return true;
}
/**
* @brief When connecting, this needs to be called until the state is
* `CONNECTED`. It assigns a player ID. Keep in mind that `isConnected()` and
* `playerCount()` won't be updated until the first message from the server
* arrives.
*/
bool keepConnecting() {
LINK_WIRELESS_RESET_IF_NEEDED
if (state != CONNECTING) {
@@ -470,6 +577,10 @@ class LinkWireless {
return true;
}
/**
* @brief Enqueues `data` to be sent to other nodes.
* @param data The value to be sent.
*/
bool send(u16 data, int _author = -1) {
LINK_WIRELESS_RESET_IF_NEEDED
if (!isSessionActive()) {
@@ -505,6 +616,11 @@ class LinkWireless {
return true;
}
/**
* @brief Fills the `messages` array with incoming messages, forwarding if
* needed.
* @param messages The array to be filled with data.
*/
bool receive(Message messages[]) {
if (!isEnabled || state == NEEDS_RESET || !isSessionActive())
return false;
@@ -528,14 +644,43 @@ class LinkWireless {
return true;
}
/**
* @brief Returns the current state (one of
* `LinkWireless::State::NEEDS_RESET`, `LinkWireless::State::AUTHENTICATED`,
* `LinkWireless::State::SEARCHING`, `LinkWireless::State::SERVING`,
* `LinkWireless::State::CONNECTING`, or `LinkWireless::State::CONNECTED`).
*/
[[nodiscard]] State getState() { return state; }
/**
* @brief Returns `true` if the player count is higher than `1`.
*/
[[nodiscard]] bool isConnected() { return sessionState.playerCount > 1; }
/**
* @brief Returns `true` if the state is `SERVING` or `CONNECTED`.
*/
[[nodiscard]] bool isSessionActive() {
return state == SERVING || state == CONNECTED;
}
/**
* @brief Returns the number of connected players.
*/
[[nodiscard]] u8 playerCount() { return sessionState.playerCount; }
/**
* @brief Returns the current player ID.
*/
[[nodiscard]] u8 currentPlayerId() { return sessionState.currentPlayerId; }
[[nodiscard]] Error getLastError(bool clear = true) {
/**
* @brief If one of the other methods returns `false`, you can inspect this to
* know the cause. After this call, the last error is cleared if `clear` is
* `true` (default behavior).
* @param clear Whether it should clear the error or not.
*/
Error getLastError(bool clear = true) {
Error error = lastError;
if (clear)
lastError = NONE;
@@ -547,32 +692,80 @@ class LinkWireless {
delete linkGPIO;
}
/**
* @brief Returns whether it's running an async command or not.
* \warning This is internal API!
*/
[[nodiscard]] bool _hasActiveAsyncCommand() { return asyncCommand.isActive; }
/**
* @brief Returns whether there's room for new outgoing messages or not.
* \warning This is internal API!
*/
[[nodiscard]] bool _canSend() {
return !sessionState.outgoingMessages.isFull();
}
/**
* @brief Returns the number of pending outgoing messages.
* \warning This is internal API!
*/
[[nodiscard]] u32 _getPendingCount() {
return sessionState.outgoingMessages.size();
}
/**
* @brief Returns the last packet ID.
* \warning This is internal API!
*/
[[nodiscard]] u32 _lastPacketId() { return sessionState.lastPacketId; }
/**
* @brief Returns the last confirmation received from player ID 1.
* \warning This is internal API!
*/
[[nodiscard]] u32 _lastConfirmationFromClient1() {
return sessionState.lastConfirmationFromClients[1];
}
/**
* @brief Returns the last packet ID received from player ID 1.
* \warning This is internal API!
*/
[[nodiscard]] u32 _lastPacketIdFromClient1() {
return sessionState.lastPacketIdFromClients[1];
}
/**
* @brief Returns the last confirmation received from the server.
* \warning This is internal API!
*/
[[nodiscard]] u32 _lastConfirmationFromServer() {
return sessionState.lastConfirmationFromServer;
}
/**
* @brief Returns the last packet ID received from the server.
* \warning This is internal API!
*/
[[nodiscard]] u32 _lastPacketIdFromServer() {
return sessionState.lastPacketIdFromServer;
}
/**
* @brief Returns the next pending packet ID.
* \warning This is internal API!
*/
[[nodiscard]] u32 _nextPendingPacketId() {
return sessionState.outgoingMessages.isEmpty()
? 0
: sessionState.outgoingMessages.peek().packetId;
}
/**
* @brief This method is called by the VBLANK interrupt handler.
* \warning This is internal API!
*/
void _onVBlank() {
if (!isEnabled)
return;
@@ -625,6 +818,10 @@ class LinkWireless {
void _onACKTimer() { __onACKTimer(); }
#endif
/**
* @brief This method is called by the SERIAL interrupt handler.
* \warning This is internal API!
*/
LINK_WIRELESS_ALWAYS_INLINE void __onSerial() {
if (!isEnabled)
return;
@@ -679,6 +876,10 @@ class LinkWireless {
#endif
}
/**
* @brief This method is called by the TIMER interrupt handler.
* \warning This is internal API!
*/
LINK_WIRELESS_ALWAYS_INLINE void __onTimer() {
if (!isEnabled)
return;
@@ -699,6 +900,10 @@ class LinkWireless {
#endif
}
/**
* @brief This method is called by the other TIMER interrupt handler.
* \warning This is internal API!
*/
LINK_WIRELESS_ALWAYS_INLINE void __onACKTimer() {
if (!isEnabled || !asyncCommand.isActive ||
asyncCommand.ackStep == AsyncCommand::ACKStep::READY)
@@ -1732,18 +1937,30 @@ class LinkWireless {
extern LinkWireless* linkWireless;
/**
* @brief VBLANK interrupt handler.
*/
inline void LINK_WIRELESS_ISR_VBLANK() {
linkWireless->_onVBlank();
}
/**
* @brief SERIAL interrupt handler.
*/
inline void LINK_WIRELESS_ISR_SERIAL() {
linkWireless->_onSerial();
}
/**
* @brief TIMER interrupt handler used for sending.
*/
inline void LINK_WIRELESS_ISR_TIMER() {
linkWireless->_onTimer();
}
/**
* @brief TIMER interrupt handler used for ACKs.
*/
inline void LINK_WIRELESS_ISR_ACK_TIMER() {
linkWireless->_onACKTimer();
}