Skip to content

Commit

Permalink
Renewing design to support retransmission and multiple messages per f…
Browse files Browse the repository at this point in the history
…rame
  • Loading branch information
afska committed Nov 14, 2020
1 parent af29b8c commit 1811eb3
Show file tree
Hide file tree
Showing 46 changed files with 7,888 additions and 217 deletions.
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
build/
# Directories
build
.vscode

# Files
*.elf
*.gba
*.sav
.vscode/
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
# gba-link-connection

A GBA Link Cable library to add multiplayer support to homebrew games.
A GameBoy Advance Link Cable library to add multiplayer support to homebrew games.

![screenshot](https://user-images.githubusercontent.com/1631752/97110671-45c17800-16b9-11eb-8fd9-11e4c0248506.png)
The library uses message queues to send/receive data and transmits when it's possible. As it uses CPU interrupts, the connection is alive even if a console drops a frame or gets stucked in a long iteration loop. After such event, all nodes end up receiving all the pending messages, so a lockstep communication protocol can be used.

![screenshot](https://user-images.githubusercontent.com/1631752/99154109-1d131980-268c-11eb-86b1-7a728f639e5e.png)

## Usage

All the complexity is abstracted in a single header file that exposes an easy-to-use interface.

- Include [LinkConnection.h](src/lib/LinkConnection.h) in your game code.
- Check out an example implementation in [main.cpp](src/main.cpp).
* A build is available in *Releases*.
* It can be tested on real GBAs or with the *NO$GBA* emulator.
- Include [LinkConnection.h](lib/LinkConnection.h) in your game code, and read its comment with instructions.
- Check out the [examples](examples) folder
* Builds are available in *Releases*.
* They can be tested on real GBAs or with emulators (*NO$GBA*, *mGBA*, or *VBA-M*).

## Constructor options

`new LinkConnection(...)` accepts these **optional** parameters:

Name | Type | Default | Description
--- | --- | --- | ---
`startNow` | **bool** | `true` | Automatically starts serial communication. Otherwise, you'll need to call `linkConnection->activate()`.
`baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate.
`timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection.
`bufferSize` | **u32** | `60` | Number of messages that the queues will be able to store.

## Makefile actions

Expand Down
2 changes: 1 addition & 1 deletion Makefile → examples/basic/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ LIBS := -ltonc
BUILD := build
SRCDIRS := src
DATADIRS := data
INCDIRS := src
INCDIRS := src lib
LIBDIRS := $(TONCLIB)

# --- switches ---
Expand Down
1 change: 1 addition & 0 deletions examples/basic/lib/LinkConnection.h
64 changes: 64 additions & 0 deletions examples/basic/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <tonc.h>

#include <string>

// (0) Include the header
#include "../lib/LinkConnection.h"

void log(std::string text);

// (1) Create a LinkConnection instance
LinkConnection* linkConnection = new LinkConnection();

void init() {
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0;
tte_init_se_default(0, BG_CBB(0) | BG_SBB(31));

irq_init(NULL);

// (2) Add the interrupt service routines
irq_add(II_VBLANK, LINK_ISR_VBLANK);
irq_add(II_SERIAL, LINK_ISR_SERIAL);
}

int main() {
init();

u16 data[LINK_MAX_PLAYERS];

while (1) {
// (3) Send/read messages messages
u16 keys = ~REG_KEYS & KEY_ANY;
u16 message = (keys << 1) | 1;
linkConnection->send(message);
auto linkState = linkConnection->linkState.get();

std::string output = "";
if (linkState->isConnected()) {
output += "Players: " + std::to_string(linkState->playerCount) + "\n";

for (u32 i = 0; i < linkState->playerCount; i++) {
while (linkState->hasMessage(i))
data[i] = linkState->readMessage(i) >> 1;

output += "Player " + std::to_string(i) + ": " +
std::to_string(data[i]) + "\n";
}

output += "_sent: " + std::to_string(message) + "\n";
output += "_self pID: " + std::to_string(linkState->currentPlayerId);
} else
output += std::string("Waiting...");
log(output);

VBlankIntrWait();
}

return 0;
}

void log(std::string text) {
tte_erase_screen();
tte_write("#{P:0,0}");
tte_write(text.c_str());
}
Loading

0 comments on commit 1811eb3

Please sign in to comment.