Skip to content
Rodrigo Alfonso edited this page Feb 21, 2025 · 9 revisions

πŸ”’ Protocols

Documentation about the protocols is available here!

❓ FAQ

βš™οΈ General

Do I need to include ALL the files in my project?

No, but I'd recommend you to copy the whole thing first and then delete the stuff you don't need. Some libraries depend on others, so check out the #includes below the big comment at the top of the file. All of them require _link_common.hpp.

dependencies

How do I use multiple libraries in the same project?

Most libraries provide activate() and deactivate() methods. You must only enable one at a time!

If you are using library A and want to switch to library B, do the following:

  • Call deactivate() on A.
  • Disable all the interrupt handlers you've added.
  • Add all the required interrupt handlers of B, if needed.
  • Call activate() on B.

When these methods aren't available, look for a reset() method. If there isn't one, the library just resets itself after usage.

I'm using libtonc and these libraries disconnect like crazy!

The interrupt handler provided by libtonc has a bug where it sometimes misses interrupts. I'd recommend using the one in libugba, like gba-link-connection examples do.

How do I use these libraries with the Butano engine?

Check out the LinkUniversal_real example code, which uses Butano 18.8.0.

Note that the engine already has an integrated multiplayer handler based on an old version of πŸ‘Ύ LinkCable.

Why do some libraries use random numbers?

To prevent livelocks in their protocols, mostly. You can set your own RNG seed in Link::randomSeed.

🌎 LinkUniversal

How does this work?

This library alternates between Link Cable and Wireless Adapter modes to achieve a working connection as fast as possible. When using the Wireless Adapter, it automatically handles the room creation with a simple autopairing logic, so users don't have to 'host' or 'join' rooms.

...Autopairing is nice, but we have zero control over who reaches who!

Well, you can implement a room list and host/join logic in your game using πŸ“» LinkWireless. Then, if you prefer to continue using 🌎 LinkUniversal, you can switch to it:

linkWireless->deactivate(false); // (false = don't turn off the adapter)
// disable LinkWireless ISRs here
// enable LinkUniversal ISRs here
linkUniversal->setProtocol(LinkUniversal::Protocol::WIRELESS_RESTORE_EXISTING);
linkUniversal->activate();
do { linkUniversal->sync() } while (!linkUniversal->isConnected());

How can I do platform-specific stuff?

The library provides the getLinkCable() and getLinkWireless() methods to retrieve the internal instances. Use responsibly! You can break everything!

Why is sending 0 or 0xFFFF not allowed?

Because πŸ‘Ύ LinkCable doesn't allow it, and if I allowed it here, it wouldn't be so Universal, right? That said, you can always use getLinkWireless()->send(...) and send any 16-bit word in wireless mode.

Why do I have packet loss?

You shouldn't, so please check:

  • that your interrupt handler doesn't miss interrupts.
  • that LINK_CABLE_ISR_SERIAL is called on time, when using a Link Cable, since it's time-sensitive. That means, be careful with DMA usage (which stops the CPU), and write short interrupt handlers. Alternatively, you can activate nested interrupts by setting REG_IME=1 at the start of your handlers, but beware of race conditions!
  • that the retransmission option is enabled, when using a Wireless Adapter.
  • that you empty your message queue (aka canRead() returns false) before every new sync() call.
  • that LINK_CABLE_QUEUE_SIZE and LINK_WIRELESS_QUEUE_SIZE are large enough.
  • that the return value of didQueueOverflow() is always false. If true, it means the internal queue lost messages at some point.

How can I improve performance?

  • πŸ‘Ύ LinkCable shouldn't eat too much CPU (like ~5%) in any normal usages.
  • πŸ“» LinkWireless, in its default configuration, takes ~5% when placing ISR code in IWRAM and ~10% when in ROM, though this depends on a lot of factors like send interval, number of players, retransmission/forwarding options, etc.

You can use the LinkUniversal_stress.gba and LinkWireless_prof_*.gba roms to check the total cycles consumed by ISRs per frame.

These compile-time constants could affect performance significantly:

  • LINK_WIRELESS_PUT_ISR_IN_IWRAM_*
  • LINK_WIRELESS_MAX_SERVER_TRANSFER_LENGTH

πŸ“» LinkWireless

How can I "close" a room so other people can't join?

If the room reaches its maximum number of allowed players, this is done automatically. If you want to implement a screen where players join the room waiting for others until everyone is ready, and then close the server, you can do it by calling closeServer().

If a player leaves a game, how can it re-enter?

By design, if a player disconnects, the room is closed. The hardware allows it though, since it has a slot system where -for example- players 2 and 4 can be connected but 3 is offline, but you'd have to implement it yourself using πŸ”§πŸ“» LinkRawWireless (and lose most of the features that πŸ“» LinkWireless handles for you like retransmission, or forwarding).

...But then... how do I handle reconnections?

🌎 LinkUniversal's autopairing can be very handy here, but keep in mind that player IDs are not preserved, so you need to build a system where each player has a sticky ID, independent from the assigned hardware ID.

πŸ’» LinkCableMultiboot / πŸ“‘ LinkWirelessMultiboot

What's the difference between the sync and async versions?

The sync version is simpler (with one method call you can send the whole ROM and get the result), but blocks the system until completion. It also uses the MultiBoot SWI, which prevents music from being played easily since it requires DMA to be disabled. The async version is what games probably want: it's slower, but allows animations and music to be played.

Where can I get example code for the async versions?

Check out the LinkUniversal_real example code.

How do I maintain the wireless room alive when sending a multiboot program?

Both sync and async versions of the library have a keepConnectionAlive configuration option that keeps the Wireless Adapter alive after the transfer is completed. Then, when the others boot the received ROM, they can call linkWireless->restoreExistingConnection() to initialize πŸ“» LinkWireless from the existing connection. The same is possible in 🌎 LinkUniversal using the LinkUniversal::Protocol::WIRELESS_RESTORE_EXISTING protocol.

Remember that the other consoles need some time to boot the game, so it's recommended to use high timeouts at first. Once everyone sends messages indicating they are alive, you can set a tighter timeout with linkWireless->config.timeout and call linkWireless->resetTimeout().