DSWifi supports local multiplayer with a limit of up to 15 client consoles connected to a single host console (the maximum supported by the hardware). The maximum number of players is decided by the developer of the application. This document describes how host and client consoles need to use DSWifi to develop applications that use this kind of multiplayer.
1. Information about transfer modes
There are two ways to transfer information between host and clients: multiplayer transfers and direct communications between the host and a client. Both systems have advantages and disadvantages:
- Multiplayer transfers:
- Communications are optimized because the DS WiFi hardware does a lot of the work automatically. After the host sends a message and clients receive it, they start sending a reply packet automatically without any involvement of the CPU. If multiple clients are connected to a host, they send the messages in sequence without any kind of intermediate handshakes that would waste time. This is achieved by using a Contention Free Period (CFP).
- You need to define a packet size for host packets and another size for client packets. While in theory it is possible to change this dynamically, DSWifi expects the developer to define an initial size, and it is only possible to send smaller packets later.
- This mode is generally recommended for games that involve more than one client and one host.
- Direct transfers:
- Communications are done by sending regular data packets, and the application needs to organize the response of every packet. This means the communications are generally less optimal than with multiplayer transfers.
- There are no restrictions about the size of the packets (apart from regular WiFi limitations). It is possible to send any size of packet at any point.
- This mode is a good idea if you have a 2-player game in which you don't mind losing a bit of latency in exchange for flexibility in the packets you send.
2. Library initialization
You need to initialize the library skipping the autoconnection step:
bool Wifi_InitDefault(bool useFirmwareSettings)
Initializes WiFi library.
#define INIT_ONLY
Init library only, don't try to connect to AP. Used by Wifi_InitDefault().
Definition dswifi9.h:54
This is required even if your application needs to switch between Internet and local multiplayer mode, you can't use USE_WFC
. Also, hardware timer 3 will be used by the WiFi library after this call.
After using multiplayer mode you can call this to switch to Internet mode:
void Wifi_InternetMode(void)
Sets the WiFI hardware in Internet mode.
If you are done using wireless mode you can disable it to save power by calling:
void Wifi_DisableWifi(void)
Instructs the ARM7 to disengage wireless and stop receiving or transmitting.
3. Host consoles
A host console becomes an access point for other consoles. It is required to define the size of host packets and client packets for multiplayer transfers (even if you don't plan to use them). For example:
typedef struct {
u32 command;
u32 arg;
} packet_host_to_client;
typedef struct {
u32 response;
} packet_client_to_host;
sizeof(packet_host_to_client),
sizeof(packet_client_to_host));
swiWaitForVBlank();
bool Wifi_LibraryModeReady(void)
Checks whether a library mode change has finished or not.
int Wifi_MultiplayerHostMode(int max_clients, size_t host_packet_size, size_t client_packet_size)
Sets the WiFI hardware in mulitplayer host mode.
Now we can start advertising this access point. For that, you need to setup the beacon packet information. You need to assign a name to the AP (or pass NULL
if you don't care about the name) and a game ID that will be used to identify APs that have been created by this application. Note that the AP name doesn't affect anything, and it can safely be left empty. The AP can still be identified by the game ID:
void Wifi_SetChannel(int channel)
If the WiFi system is not connected or connecting to an access point, instruct the chipset to change ...
int Wifi_BeaconStart(const char *ssid, u32 game_id)
Sends a beacon frame to the ARM7 to be used in multiplayer host mode.
void Wifi_MultiplayerAllowNewClients(bool allow)
Allows or disallows new clients to connect to this console acting as host.
At this point client consoles can detect that the AP exists and they can attempt to connect to it. This process is done by the ARM7 without any intervention by the ARM9.
At any point you can call check the number of clients currently connected to this host, as well as their MAC address and association ID. In general, you only care about their association IDs (a number that goes from 1 to 15). DSWifi uses this AID in several multiplayer-related functions.
printf("Num clients: %d (mask 0x%02X)\n", num_clients, players_mask);
printf("\n");
for (int i = 0; i < num_clients; i++)
{
printf("AID %d (State %d) %04X:%04X:%04X\n",
client[i].association_id,
client[i].state,
client[i].macaddr[0], client[i].macaddr[1], client[i].macaddr[2]);
}
int Wifi_MultiplayerGetClients(int max_clients, Wifi_ConnectedClient *client_data)
Get information of clients connected to a multiplayer host.
int Wifi_MultiplayerGetNumClients(void)
Get the number of clients connected to this DS acting as a multiplayer host.
u16 Wifi_MultiplayerGetClientMask(void)
Returns a mask where each bit represents an connected client.
Structure that represents a DS connected to a host DS.
Definition dswifi_common.h:243
Once you have enough clients you can prevent new connections by calling this:
At any point, if you determine that a client is idle, or cheating, or anything, you can call the following function to kick that client out of the host:
void Wifi_MultiplayerKickClientByAID(int association_id)
Kick the client with the provided AID from the host.
You also need to setup a handler for received packets from clients. This is a sample handler that can handle both multiplayer packets and regular data packets:
{
printf("Client %d: ", aid);
{
for (int i = 0; i < len; i += 2)
{
u16 data = 0;
printf("%04X ", data);
}
printf("\n");
}
{
if (len < 50)
{
char string[50];
string[len] = 0;
printf("%s", string);
}
printf("\n");
}
}
Wifi_MPPacketType
Possible types of packets received by multiplayer handlers.
Definition dswifi9.h:496
@ WIFI_MPTYPE_DATA
Regular data packet.
Definition dswifi9.h:500
@ WIFI_MPTYPE_REPLY
Multiplayer REPLY packet.
Definition dswifi9.h:499
void Wifi_RxRawReadPacket(u32 base, u32 size_bytes, void *dst)
Allows user code to read a packet from within the WifiPacketHandler function.
Call this to setup the handler:
void Wifi_MultiplayerFromClientSetPacketHandler(WifiFromClientPacketHandler func)
Set a handler on a host console for packets received from clients.
Finally, to send data to the clients you can do it like this:
packet_host_to_client host_packet = { ... };
int association_id = 1;
const char *str = "Hey, this is some text";
int Wifi_MultiplayerHostCmdTxFrame(const void *data, u16 datalen)
Sends a multiplayer host frame.
int Wifi_MultiplayerHostToClientDataTxFrame(int aid, const void *data, u16 datalen)
Sends a data frame to the client with the specified association ID.
When you want to leave host mode, call this:
void Wifi_IdleMode(void)
Sets the WiFI hardware in "active" mode.
After calling this, all clients will be disconnected from the host, and the access point created by this console will stop being advertised.
4. Client consoles
Enter client mode:
typedef struct {
u32 response;
} packet_client_to_host;
swiWaitForVBlank();
int Wifi_MultiplayerClientMode(size_t client_packet_size)
Sets the WiFI hardware in mulitplayer client mode.
The first step is to enter scan mode to look for access points. By default, only access points with Nintendo-specific metadata will be saved, other access points are ignored in multiplayer mode:
void Wifi_ScanMode(void)
Makes the ARM7 go into scan mode and list Internet APs.
Scan mode will iterate through all channels and add the access points to an internal list kept by DSWifi. At the moment, access points of host consoles using DSWifi don't use any encryption. You can check the list of detected access points like this, for example, but note that you need to do this in an interactive menu so that the user can wait for access points to show up:
printf("Number of AP: %d\n", count);
printf("\n");
for (int i = 0; i < count; i++)
{
printf(
"[%.24s] %s\n", ap.
ssid);
printf(
"Channel %2d | RSSI %u\n", ap.
channel, ap.
rssi);
printf("Players %d/%d | %s\n",
printf("Game ID: %08X\n", (unsigned int)ap.nintendo.game_id);
printf("\n");
}
int Wifi_GetNumAP(void)
Returns the current number of APs that are known and tracked internally.
int Wifi_GetAPData(int apnum, Wifi_AccessPoint *apdata)
Grabs data from internal structures for user code (always succeeds).
Structure that defines how to connect to an access point.
Definition dswifi_common.h:197
u8 channel
Valid channels are 1-13, setting the channel to 0 will indicate the system should search....
Definition dswifi_common.h:226
u16 rssi
Running average of the recent RSSI values for this AP, will be set to 0 after not receiving beacons f...
Definition dswifi_common.h:217
Wifi_NintendoVendorInfo nintendo
Information send by Nintendo DS hosts in beacon frames, used if WFLAG_APDATA_NINTENDO_TAG is set in "...
Definition dswifi_common.h:229
char ssid[33]
AP's SSID. Zero terminated is not necessary. If ssid[0] is zero, the SSID will be ignored in trying t...
Definition dswifi_common.h:200
u8 players_max
Maximum number of players allowed by the host (including the host)
Definition dswifi_common.h:172
u8 players_current
Current number of players connected to the host (including the host)
Definition dswifi_common.h:174
u8 allows_connections
Set to 1 if the host accepts new connections, 0 if not.
Definition dswifi_common.h:176
As you can see, there is a lot of information in a Wifi_AccessPoint
struct. It contains the maximum number of clients that the host allows, the number of clients connected currently, and whether this host allows new connections or not. This is just a hint that can be ignored by clients (which can try to connect to APs that are full and not allowing new connections, but the connection will fail).
There are two more fields in the Wifi_AccessPoint
structure that haven't been used in the example. The user name of the host is stored in ap.nintendo.name
, and its length is stored in ap.nintendo.name_len
. The name is stored in UTF-16LE format by default and the default value is the name defined by the user in the firmware of the DS. However, you can use Wifi_MultiplayerHostName()
to set the values of both fields to anything that you prefer. DSWifi doesn't use the fields for anything, it just provides them to the user of the library, so you can use any format you want (you can use an ASCII string if you don't want to use UTF-16LE, for example).
When you have found an AP you want to connect to, run:
int Wifi_ConnectOpenAP(Wifi_AccessPoint *apdata)
Connect to an AP without encryption (and NDS multiplayer hosts).
Then, wait for the connection to be completed (or to fail!):
while (1)
{
swiWaitForVBlank();
{
}
{
}
}
int Wifi_AssocStatus(void)
Returns information about the status of connection to an AP.
@ ASSOCSTATUS_CANNOTCONNECT
Error in connecting... (COMPLETE if Wifi_ConnectAP was called to start)
Definition dswifi9.h:222
@ ASSOCSTATUS_ASSOCIATED
Connected! (COMPLETE if Wifi_ConnectAP was called to start)
Definition dswifi9.h:220
Remember to call Wifi_AssocStatus()
regularly to check if the connection with the host has finished.
You also need to setup a handler for packets received from the host:
{
printf("Host (%d): ", len);
{
for (int i = 0; i < len; i += 2)
{
u16 data = 0;
printf("%04X ", data);
}
printf("\n");
}
{
if (len < 50)
{
char string[50];
string[len] = 0;
printf("%s", string);
}
printf("\n");
}
}
Call this to setup the handler:
void Wifi_MultiplayerFromHostSetPacketHandler(WifiFromHostPacketHandler func)
Set a handler on a client console for packets received from the host.
Finally, to send data to the host you can do it like this:
packet_client_to_host = { ... };
Wifi_MultiplayerHostReplyTxFrame(&host_packet, sizeof(host_packet));
int association_id = 1;
const char *str = "Hey, this is some text";
int Wifi_MultiplayerClientToHostDataTxFrame(const void *data, u16 datalen)
Sends a data frame to the host.
If you want to leave this AP, but remain in client mode:
int Wifi_DisconnectAP(void)
Disassociate from the Access Point.
When you want to leave client mode, call this:
After calling this, the client will send a packet to the host to notify that it's leaving, and DSWifi will become idle.