3. Foreground Communication

The following libraries have been provided for applications to intentionally use the wireless communication module for wireless communication.

  • Local Communication (UDS Communication)
  • Nintendo 3DS Download Play
  • Automatic Connection
  • Reliable Local (RDT) Communication

This chapter describes programming procedures and other information necessary to develop applications using each of these libraries.

3.1. Nintendo 3DS Local Play (UDS) Communication

The 3DS platform uses Nintendo 3DS Local Play (UDS) communication as the Nintendo-specific local communication method intended for use in games.

Nintendo 3DS Local Play (UDS) communication has the following characteristics.

  • Allows up to 16 systems to be simultaneously connected to a single network (although proper operation with 13 or more connected systems is not guaranteed, at present).
  • Supports packet broadcasting.
  • Not synchronized with picture frames.
  • Does not guarantee that communication data will arrive.
  • Encrypts communication channels.
  • Preserves connections even if data is not sent or received regularly.

 

Warning:

Nintendo 3DS Local Play (UDS) communication is not available between development CTR systems (debugger and development system) and retail CTR systems.

Nintendo 3DS Local Play (UDS) communication uses the UDS library provided by the CTR-SDK.

The following terms are used with the UDS library.

Node

A terminal (device) that sends and receives data on a UDS communication network.

Hosts and clients (described later) are both types of nodes.

Node ID

An ID that is assigned when a node connects to a UDS communication network.

For more information, see 3.1.8. Node ID Assignment.

Host

A node that establishes a new UDS communication network and can change network attributes as necessary.

Corresponds to the "MP Parent" in MP communications for the Nintendo DS system or Nintendo DSi system.

Client

A node that sends and receives data after connecting to an existing network established by a host.

Corresponds to the "MP Client" in MP communications for the Nintendo DS system or Nintendo DSi system.

Spectator

A system, but not a node, that only receives data after connecting to an existing network established by a host.

Unlike a client node, a spectator system does not count toward the maximum number of simultaneous network connections. The library does not place any limitations on the number of simultaneous spectator connections. The application also cannot limit the number of connections.

Unicast communication

Unicast communication is a method to send packets from a node to one other node, and is typically between a host and a client. Client-to-client unicast communication is possible, but two transmissions are performed within the library because the communication must pass through the host. The extra transmission increases the latency of data sent between clients, but it also ensures that the recipient node is never out of communication range.

Broadcast communication

Broadcast communication is a method to send packets from a node to all other nodes. It enables packets to be sent to multiple nodes via a single transmission. Communication via this method is processed as a single transmission, regardless of how many nodes there are in the network. Although this reduces the latency of data sent from clients, note that transmissions will not reach clients that are out of communication range.

3.1.1. Internal UDS Communication States

Figure 3-1 shows internal state transitions for the UDS library.

Figure 3-1. State Transition Diagram

Disconnected Creating Network Host Destroying Network Create Network Success Destroy Network Connecting Network Client Disconnecting Network Connect Network Success (Client) Disconnect Network Spectator Success (Spectator) Disconnect Network Initializing Finalizing Initialize Success Finalize None Intermediate state State UDS State Transition Diagram Local host was disconnected Local host was disconnected Failed Failed Transitions due to function calls Automatic transitions Failed Scan

There are two types of internal states: states, which show the current status, and intermediate states, through which the current state transitions to another. Intermediate states exist for internal library processing. Although application developers generally do not need to pay attention to them, if the current state is an intermediate state, wait for it to transition to a normal state.

The UDS library restricts functions that can be called from some states. An error is returned immediately if you call a function from a state in which it is unusable.

Table 3-1. States

State Name

Description

None

Has not yet initialized the UDS library.

Disconnected

Has initialized the UDS library but does not belong to a network.

Host

Created a new network and is running as a host node.

Client

Connected to an existing network as a client node.

Spectator

Connected to an existing network as a spectator node.

Table 3-2. Intermediate States

Intermediate State Name

Description

Initializing

Initializing the UDS library. When it is successful, this state transitions to Disconnected.

Finalizing

Shutting down the UDS library. After it is finished, this state transitions to None.

Creating Network

Creating a network based on the settings. After it is finished, this state transitions to Master.

Connecting Network

Connecting to an existing network as a client or spectator node.

When it is successful, this state transitions to Client or Spectator.

Destroying Network

Disconnects all client and spectator nodes from the network and then destroys the network.

After it is finished, this state transitions to Disconnected.

Disconnecting Network

Leaving the current network connection. After it is finished, this state transitions to Disconnected.

A node also enters this state when it is disconnected from the network because of an external cause.

These states are defined by nn::uds::State. The following table shows how the names of the states and definitions correspond.

Table 3-3. State Names and Their Corresponding Definitions

State Name

Definition Name

None

STATE_NONE

Disconnected

STATE_DISCONNECTED

Host

STATE_MASTER

Client

STATE_CLIENT

Spectator

STATE_SPECTATOR

Initializing

STATE_PROCESSING_INITIALIZE

(The application never gets this state.)

Finalizing

STATE_PROCESSING_FINALIZE

(The application never gets this state.)

Creating network

STATE_CREATING_NETWORK

Connecting to network

STATE_CONNECTING_NETWORK

Destroying the network

STATE_DESTROYING_NETWORK

Disconnecting from the network

STATE_DISCONNECTING_NETWORK

3.1.2. Initializing

Call nn::uds::Initialize() to initialize the UDS library.

Code 3-1. Initializing the UDS Library
nn::Result nn::uds::Initialize(
        nn::os::Event* pStatusUpdateEvent,
        void* receiveBuffer, const size_t bufferSize);
nn::Result nn::uds::Initialize(
        nn::os::Event* pStatusUpdateEvent,
void* receiveBuffer, const size_t bufferSize, 
        nn::cfg::UserName* pUserName); 

Set pStatusUpdateEvent equal to a pointer to an instance of the nn::os::Event class. This instance is initialized as an auto-reset event.

The Event instance is initialized within nn::uds::Initialize() and is used to notify the application at the following times.

  • When the calling node creates or connects to a network.
  • When the calling node disconnects or is disconnected from the network.
  • When another node connects to the network.
  • When another node disconnects or is disconnected from the network.

Set receiveBuffer and bufferSize to the buffer for receiving data used within the UDS library and its size, respectively. The application must allocate this memory from non-device memory with the starting address nn::uds::BUFFER_ALIGNMENT (4096 bytes) aligned, and the size must be a multiple of nn::uds::BUFFER_UNITSIZE (4096). Do not allow applications to access this memory until nn::uds::Finalize() has completed finalizing the memory specified for the receive data buffer.

Specify pUserName to use a user name that is different from the name in System Settings (for example, when naming a Mii). Handle user names according to the CTR Guidelines: UGC. Specify NULL to use the user name in System Settings. This is the same as initializing the library with the version of the function that does not have a pUserName parameter.

If nn::uds::resultAlreadyOccupiedWirelessDevice is returned in the return value, UDS communication cannot be started because the wireless communication module is being used for other communications. nn::uds::ResultWirelessOff is returned if the system is in wireless-disabled mode.

3.1.3. Connecting

A UDS communication network is created after the UDS library has been initialized. The following sections describe the procedures for creating a network as a host node and connecting to a network as a client node or spectator system.

3.1.3.1. Generating Local Communication IDs

You must first specify a 32-bit local communication ID to identify networks before calling functions to create or search a UDS communication network. Use the following function to generate a local communication ID by converting from a 20-bit unique ID value.

Code 3-2. Generating Local Communication IDs
bit32 nn::uds::CreateLocalCommunicationId(bit32 uniqueId, 
                                          bool isDemo = false); 

For uniqueId, specify the unique ID assigned by Nintendo Licensing Department to each title. When communicating between different titles, specify the unique ID for either one of them. An ID value of 0 is reserved by the library and cannot be used.

If you want to disable UDS communication between retail versions and download application demo versions that have the same unique ID, set isDemo to true on the demo version only. Always set this parameter to false in the retail version, regardless of whether you want to communicate with demo versions.

Note:

For test programs and titles that use UDS communication and have not been allocated a unique ID by Nintendo, specify a prototype software code in the range 0xFF000 to 0xFF3FF for uniqueId. In retail software, you must always specify the unique ID assigned by Nintendo.

3.1.3.2. Creating a New Network

Build a UDS communication network by calling nn::uds::CreateNetwork() on the host.

Code 3-3. Creating a Network
nn::Result nn::uds::CreateNetwork(
        u8 subId,
        u8 maxEntry,
        bit32 localId,
        const char passphrase[], size_t passphraseLength,
        u8 channel);
nn::Result nn::uds::CreateNetwork(
        u8 subId,
        u8 maxEntry,
        bit32 localId,
        const char passphrase[], size_t passphraseLength,
        u8 channel,
        const void* pData, size_t dataSize); 

The subId parameter is the communication mode ID. The application can set this freely. This is expected to be used to distinguish between communication modes such as "network battle" and "data exchange." Do not specify 0xFF for subId. The client uses the value 0xFF to search the network for all subId's.

For maxEntry, specify the maximum number of nodes that can connect to the network. You can specify up to 16 nodes including the host and clients, but operation has not been adequately tested with 13 or more simultaneously connected systems. Consequently, proper operation with 13 or more connected systems is currently not guaranteed. Note that the host itself is included in the node count, but spectator systems are not.

For localId, specify the local communication ID generated from the unique ID.

Set passphrase to a string denoting the type of encryption key used to encrypt the wireless layer. Communication is established when a combination of localId and passphrase matches between the host and the client or spectator. Avoid using passphrases that are easily determined, such as extremely short strings, strings that are easy to guess, and strings that you have specified to third parties (including strings used in other titles).

Set passphraseLength to the length of the passphrase. You can specify a value in the range from 8 to 255 (inclusive).

For channel, specify the channel to use for UDS communication: 0, 1, 6, or 11. If 0 is specified, the channel is selected automatically. This channel specification is used only for debugging.

Specify the data and its byte size in pData and dataSize, when setting optional data in a beacon at the same time as the build. When 0 is specified for dataSize, an error will not occur when pData is NULL. For more information about setting data in a beacon, see 3.1.3.5. Setting Optional Data in a Beacon.

Warning:

The channel is always selected automatically when executed on production version hardware.

To build a network that uses a specified channel, you must first set the system to debugging mode. (Using the Config tool, go to Debug Settings and set Debug Mode to enable.)

With UDS communication, a TemporaryID is generated randomly to identify each time the network is rebuilt. However, when the time between rebuilding is short, and there were no connections from clients and no communication with other devices, the TemporaryID is not updated.

3.1.3.3. Finding Nearby Networks

Clients and spectators must search for a nearby network built with the same local communication ID by calling nn::uds::Scan().

Code 3-4. Searching for an Existing Network
nn::Result nn::uds::Scan(
        void * pBuffer, size_t bufferSize,
        u8 subId,
        bit32 localId); 

For pBuffer, specify the address of the buffer in which to store network information found by the search. Each network requires (approximately) a 1-KB buffer. Network information is not saved beyond the buffer size.

subId is the communication mode ID to search. The network to search is the one formed with the same value as specified in subId. Specify a value of 0xFF to search all networks.

For localId, specify the local communication ID generated from the unique ID.

Warning:

Development CTR systems (debugger and development system) and retail CTR systems cannot discover each other through scanning.

Next, the buffer obtained by running nn::uds::Scan() is passed to a nn::uds::ScanResultReader constructor to generate an instance of that class for analyzing the search results.

You can call the GetFirstDescription() member function on the generated class instance to get the first search result. You can use the GetNextDescription() function to get the next search result. Search results are stored in the order of strongest received signal strength indicator (RSSI value).

The search results are given as an instance of the nn::uds::NetworkDescriptionReader class. You can use this instance's member functions to analyze network information. The GetNetworkDescription(), GetNodeInformationList(), and GetRadioStrength() functions get network information, a list of nodes, and the signal strength (as one of four levels), respectively. You can also use CompareWith() to compare network information. One of three results is returned: "Exactly the same," "Same network but different data," or "Different network."

3.1.3.4. Connecting to a Network

A client or spectator can get a variety of information by analyzing search results. Of this, the network information obtained by GetNetworkDescription() can be used to call nn:uds::ConnectNetwork() and connect to an existing network.

Code 3-5. Connecting to a Network
nn::Result nn::uds::ConnectNetwork(
        const NetworkDescription & networkDescription,
        ConnectType type,
        const char passphrase[], size_t passphraseLength); 

For networkDescription, specify the network information obtained by a network search.

For type, specify the type of the connecting node. You can select one of the following two node types.

Table 3-4. Network Connection Nodes

Setting Value

Description

CONNECT_AS_CLIENT

Connect to the network as a Client node, which can send and receive data and is given a node ID when it connects to the network.

CONNECT_AS_SPECTATOR

Connect to the network as a Spectator node, which can only receive data and is not given a node ID when it connects to the network.

Check whether a system can connect to a network as a spectator by calling the nn::uds::NetworkDescription network information class's CanConnectAsSpectator() member function and looking for a return value of true.

Set passphrase to a string denoting the type of encryption key used to encrypt the wireless layer. Communication is established if the combination of the passphrase and the local communication ID of the network built by the host match.

Set passphraseLength to the length of the passphrase. You can specify a value in the range from 8 to 255 (inclusive).

Note:

The function returns a ResultAlreadyNetworkIsFull error when connected to a network that is already full, and a ResultNotFoundNetwork error when the connected network no longer exists.

3.1.3.5. Setting Optional Data in a Beacon

The host that established the network can set up to NET_DESC_APPDATA_SIZE_MAX bytes of optional data in the network beacon frame.

Code 3-6. Setting Optional Data in a Beacon
nn::Result nn::uds::SetApplicationDataToBeacon(
                        const void* pData, size_t dataSize); 

pData and dataSize specify the starting address of the optional data and the size.

The optional data set in the beacon may include such information as the session state (for example, waiting for opponents or in a match) or a comment. This feature was designed to enable applications to manage their own connections, such as informing clients and spectators who found the network of the network status. If you reset this optional data, however, it is not possible to notify clients and spectators that are already connected of these changes.

It is important to note that optional data is not encrypted. Beacons are easily intercepted by PCs and other devices. Although it is possible to detect if a beacon has been tampered with, it is not possible to detect if devices are using an unmodified beacon that has been intercepted. To prevent the use of specific data in a beacon, you must employ countermeasures to ensure that the data cannot be interpreted if it is intercepted and redistributed by a spectator.

3.1.3.6. Getting Optional Data from a Beacon

Client and spectator systems may get the optional data set in the beacon frame by the host by calling member functions for the network information obtained after a network search, or by calling functions for getting information while connected to a network.

Code 3-7. Getting Optional Data From a Beacon
size_t nn::uds::NetworkDescription::GetApplicationData(
                        bit8* buffer, const size_t bufferSize) const;
nn::Result nn::uds::GetApplicationDataFromBeacon(
                        void* pBuffer, size_t* pDataSize, size_t bufferSize); 

buffer, bufferSize, pBuffer, and bufferSize specify the starting address of the buffer storing the optional data and the buffer size. Be sure to specify the maximum optional data size for the optional data (NET_DESC_APPDATA_SIZE_MAX) and allocate a buffer of that size.

You can check the size required for the obtained optional data from the nn::uds::NetworkDescription::GetApplicationData() function return value, and from the pDataSize parameter to the nn::uds::GetApplicationDataFromBeacon() function.

3.1.4. Communicating

At this point, preparations to send and receive data via UDS communication should be complete. The following sections describe how to send and receive data.

3.1.4.1. Creating an Endpoint

Before you send or receive data, you must create a network endpoint. An endpoint is a gateway for sending and receiving data and is created with nn::uds::CreateEndpoint().

Note:

You can currently create up to 16 endpoints at the same time.

We recommend creating separate endpoints for sending and receiving. The same endpoint can be used for both sending and receiving, but be careful to avoid using an endpoint currently in the midst of sending or receiving for a separate send or receive transaction.

To create an endpoint for receiving data, call nn::uds::Attach() and associate the endpoint with the port number and node ID. When you do so, the library creates a reception buffer in the memory block allocated via nn::uds::Initialize().

Code 3-8. Associating an Endpoint With a Port Number and Node ID
nn::Result nn::uds::Attach(
    EndpointDescriptor * pEndpointDesc,
    u16 srcNodeId,
    u8 port,
    size_t receiveBufferSize = ATTACH_BUFFER_SIZE_DEFAULT); 

In pEndpointDesc, specify the endpoint you created with nn::uds::CreateEndpoint().

Set srcNodeId to the node ID of the sender. Only data sent from the specified node is stored in the reception buffer. Specify BROADCAST_NODE_ID to store data sent from all nodes in the reception buffer.

Set port to the number of the port on which to receive data. Only data sent to the specified port number is stored in the reception buffer. Port number 0 is reserved for the system and cannot be used.

Set receiveBufferSize to the size of the allocated reception buffer. The minimum value allowed for this parameter is ATTACH_BUFFER_SIZE_MIN. Note that the total size of all reception buffers must not exceed the size of the memory block allocated by nn::uds::Initialize() because the reception buffers are allocated from this memory block. Memory is allocated in units of 32 bytes, so you can make the most efficient use of memory by allocating sizes that are multiples of 32. The default allocates an area the size of ATTACH_BUFFER_SIZE_DEFAULT (eight times UDS_PACKET_PAYLOAD_MAX_SIZE) as the reception buffer.

You can also get the node ID of the sender when receiving data. This allows you to identify the sender even if you receive data sent to the same port by multiple nodes using a single endpoint. The reception buffer is a ring buffer, however, which means that it is possible to lose the earliest packets received if your buffer is not large enough.

When you no longer need to conduct communication through the endpoint, such as when disconnecting from the network, free the endpoint by using nn::uds::DestroyEndpoint(). When you destroy a reception endpoint, the library frees the reception buffer created by the call to nn::uds::Attach().

3.1.4.2. Sending

Data is sent to a node by nn::uds::SendTo() through the created endpoint. Note that the function blocks until sending is complete.

Code 3-9. Sending Data
nn::Result nn::uds::SendTo(
        const EndpointDescriptor & endpointDesc,
        const void * data, size_t dataSize,
        u16 destNodeId, u8 port,
        bit8 option = 0x00); 

For endpointDesc, specify the endpoint that you created.

For data, specify a pointer to the data to send. For dataSize, specify the size of that data. The maximum amount of data that can be sent by a single function call is defined by UDS_PACKET_PAYLOAD_MAX_SIZE. Setting dataSize to a value greater than the maximum size causes a ResultTooLarge error to be returned. The function returns a ResultMisalignedAddress error if the starting address of the data to send is not 4-byte aligned.

For destNodeId, specify the node ID where data is to be sent. Specify a value of BROADCAST_NODE_ID (0xFFFF) to broadcast the data. A spectator can only receive data that is broadcast via this method. It is not possible to send data to a specific spectator. Spectators also cannot send data.

For port, specify the port number. Port number 0 is reserved for the system and cannot be used.

Specify the bitwise OR of the following flags for option. The specification for this argument can be omitted by setting 0x00. If the option setting is not used, unicast communications from client to client pass through the host. In addition, with the exception of packets passed through the host, packet buffering is enabled by the send buffer.

  • NO_WAIT
    If this flag is specified, data is sent immediately without copying it to the UDS send buffer, but execution is blocked until sending is complete.
    If this flag is not specified, the library keeps the transmit buffer small to prevent transmission delay as much as possible, allocating the amount that can be sent at a time (about the same as UDS_PACKET_PAYLOAD_MAX_SIZE). It then sends the data in the buffer when the buffer becomes full, or when a set time (the maximum transmission delay time) has elapsed since the earliest data in the buffer was stored. Because of this, when this flag is not set and data is sent repeatedly, there are cases when data is just accumulated in the buffer, and others when the buffer becomes full and the transmission process is triggered. Blocking time is longer for the latter than for the former.
    The default maximum transmission delay time is set to 10 ms, but it can be set within the range from 5 to 100 ms using nn::uds::SetMaxSendDelay().
Note:

The decision about whether to specify NO_WAIT depends on the real-time responsiveness required by a particular communication.

Specify NO_WAIT if you want to minimize the time it takes data to reach the receiver and shorten the time required to exchange data as much as possible.

If Internet latencies are not a problem for your application, you can handle more packets by not specifying NO_WAIT. This improves communication bandwidth when sending a large volume of small packets, because packets are gathered together and then sent. Although throughput does not improve, the number of packets that can be sent per second increases.

  • FORCE_DIRECT_BC
    Direct broadcast communication is used to send data for any receiver. Unicast communication between clients does not pass through the host. Although this decreases transmission latency, senders must take into account nodes outside their communication range.  
Transmission Sequence of UDS Communication

The library accumulates packets processed via SendTo() in a radio frame (unit of communication under wireless standards). If the NO_WAIT option is not specified, it sends the packets after accumulating as much data as possible in the send buffer. If the NO_WAIT option is specified, it sends the packets immediately.

When a node receives a radio frame, the library breaks it up into packets and assigns each to a reception buffer. At this point, nodes other than the host discard all packets not addressed to them. UDS communication (which is a star network) is designed so that the host accumulates packets addressed to other nodes in its send buffer and resends them, to enable communication between clients.

Guaranteed Order of Sent Packets

When data is sent to the same receiver using the same option settings (FORCE_DIRECT_BC), the order of the packets is guaranteed.

If you do not specify an option, or specify options differently, the order of packets sent via unicast and broadcast communication may change due to differences in the paths they traverse. Nevertheless, because changing the order of packets sent via unicast and broadcast communication is not likely to cause problems in and of itself, you can essentially assume that the order of packets is guaranteed.

Send Delay

The library is designed so that small packets are gathered together and then sent to increase the efficiency of the physical layer of wireless communication. When data is sent using nn::uds::SendTo(), there is a chance of waiting to send data for a set send delay time. To avoid this send delay, include NO_WAIT in the option parameter specified to nn::uds::SendTo(), when you want to prioritize latency over line efficiency.

Use nn::uds::SetMaxSendDelay() to specify the maximum send delay time.

Code 3-10. Maximum Send Delay Time Setting
nn::Result nn::uds::SetMaxSendDelay(nn::fnd::TimeSpan maxDelay); 

Specify a value in the range from 5 to 100 ms for the maximum send delay time of maxDelay. The default is 10 ms.

This function can only be called when the system is not connected to the network.

3.1.4.3. Receiving

The library receives data sent via the endpoint you associated with the port number and node ID by the call to Attach(). Call nn::uds::Receive() if you do not need to get the node ID of the sender, or nn::uds::ReceiveFrom() if you do need to get the node ID of the sender. This section describes the nn::uds::ReceiveFrom() function.

Code 3-11. Receiving Data
nn::Result nn::uds::ReceiveFrom(
        const EndpointDescriptor & endpointDesc,
        void * pBuffer,
        size_t * pReceivedSize,
        u16 * pSrcNodeId,
        size_t bufferSize,
        bit8 option = 0x00); 

For pEndpointDesc, specify an endpoint created by nn::uds::CreateEndpoint() and attached by nn::uds::Attach().

For pBuffer, specify a pointer to the address used to store received data. The buffer starting address and size must both be 4-byte aligned. The function returns a ResultMisalignedAddress error or a ResultMisalignedSize error if the starting address or size of the receive buffer is not 4-byte aligned.

For pReceivedSize, specify a pointer to the address used to store the size of the received data. The maximum amount of data that can be received by a single function call is defined by UDS_PACKET_PAYLOAD_MAX_SIZE.

For pSrcNodeId, specify a pointer to the address used to store the sender's node ID.

For bufferSize, specify the size of the buffer used to store the received data.

Specify NO_WAIT for option if the function is to end immediately, even if no data has been received. Omit option for the function to continue until either data is received or an error occurs.

The Possibility of Receiving Corrupted Packets

The hardware performs a check for corrupted data when UDS communications are used, but because the emphasis is on speed and adaptability, no checks are conducted at the software level for the completeness of individual packets.

If corruption or tampering will be a problem for your data (for example, when exchanging items), have your application conduct the check at the level of individual files or whatever level is appropriate. There is no need to check at the level of individual packets if tampering will not be a problem, or if your application requires real-time responsiveness.

3.1.4.4. Causes of Packet Loss

This section describes the causes of packet loss in UDS communication.

Degradation of the Environment

Degradation of the communication environment increases packet loss. Examples of such degradation include loss of signal strength due to the distance between the systems, interference due to radio transmissions by third parties, and noise and other external factors.

Actual measurements of packet loss (the library does not guarantee these values) range from about 1% (under good conditions) up to about 10% (when conditions have degraded to the limit of ability to maintain a connection). Nintendo engineers have noted a tendency for packet loss to rise very suddenly and steeply when the system or device cannot keep up with the processing load.

Connection Speed

As a rule of thumb, the maximum communication volume for UDS is about 500 to 600 packets per second over the entire network. You may experience packet loss if this volume is greatly exceeded or if neighboring networks put stress on your communication bandwidth.

In this case, the rate of packet loss depends on the volume of packets being sent. You can lower this rate by reducing the number of systems that are connected.

Nintendo has confirmed an increase in packet loss in the com_demo1 demo when a simple communication program communicating on a single network exceeds around 800 packets per second.

Reception Buffer Overflows

If your application receives packets at a faster pace than it calls the receive function, the reception buffer could overflow and the earliest packets could be dropped. If this happens, the rate of lost packets gradually increases because of the increase in the communication load. Although you can handle temporary increases in load by creating a larger reception buffer, this technique does not remedy chronic buffer overflows.

Warning:

The difference in processing speed on SNAKE and CTR causes communication problems. Implement your communication code in a manner that is not overly dependent on the speed of the processor.

For example, use a timer to delay processing after sending packets to avoid sending too many packets or have the sender check the receive status of the receiver as necessary.

3.1.4.5. Communicating More Efficiently

UDS communication is based on the IEEE 802.11b/g standard. Under this standard, the entire network on a channel shares radio bandwidth. The standard also has backoff periods of non-communication to avoid interference with the transmission of radio frames. You can improve efficiency by reducing the number of radio frames that are sent, even if the size of the data you send is the same, by such means as accumulating packets until the maximum size that can be sent in a radio frame is reached.

A UDS network has a star-shaped network topology, in which all clients are connected to a host. In normal unicast communication, transmissions from the host to a client or from a client to a host are performed in one pass, but transmissions from one client to another are performed in two passes because they are routed through the host. If you are sending the same data to multiple nodes, you can reduce the number of packets sent over the network as a whole by using broadcast communication. Broadcast communication sends data to all nodes connected to the network in a single pass. No matter how many nodes there are, it is possible to send them data with a single transmission. However, although it is guaranteed that all nodes will be in the communication range of a broadcast communication from the host, you must account for the possibility that some nodes will be out of range of a broadcast communication from a client.

Implementing a Mesh Network With UDS Communication

It is relatively easy to implement communication processing in a mesh network because all nodes in such a network perform the same processing. In a mesh network, however, the number of packets sent per unit of time over the entire network increases exponentially with the increase in communicating systems, because all nodes in a mesh network send packets to all other nodes.

With UDS communication, you can reduce the ratio of packets sent to nodes by using broadcast communication, but when broadcasting from a client you must account for the possibility that packets may not reach all nodes. Clients that detect they have not received a packet must then request that the packet be resent. Because the packet is resent using unicast communication, you must account for the latency of the round trip plus the traversal through the host.

With broadcast communication, the same packets ordinarily reach all nodes. Although this is fine if you want to send the same data to all nodes, this method of communication does not reduce the number of packets if you want to send different data to each node. If you specify FORCE_DIRECT_BC in the send options, unicast communication sends data in a single pass in the same manner as broadcast communication, and received nodes discard packets that are not addressed to them. You can reduce the number of packets sent over the entire network per unit of time by using this feature in conjunction with the feature to accumulate packets and send them in batches. If you use this method, however, you must account for the possibility that some nodes will be out of communication range.

3.1.5. Disconnecting

This section describes the procedure by which a host node disconnects client and spectator systems, and a client or spectator system disconnects itself.

3.1.5.1. Disconnecting Client and Spectator Systems as a Host Node

A host can disconnect client and spectator systems from a network using the following methods.

  • Disconnect a specific client from the network.
  • Disconnect all clients from the network.
  • Disconnect all spectator systems from the network.

Call nn::uds::EjectClient() to disconnect a client from a network.

Code 3-12. Disconnecting a Client From a Network
nn::Result nn::uds::EjectClient(u16 nodeId); 

To disconnect a specific client from a network, set nodeId equal to its node ID.

To disconnect all clients from a network at the same time, set nodeId equal to BROADCAST_NODE_ID (0xFFFF).

Call nn::uds::EjectSpectator() to disconnect spectator systems from a network.

Code 3-13. Disconnecting Spectator Systems From a Network
nn::Result nn::uds::EjectSpectator(void);
nn::Result nn::uds::AllowToSpectate(void); 

Call nn::uds::EjectSpectator() to disconnect all spectator systems from a network and deny any further connections from spectator systems. This assumes that the network was initially created to allow spectator connections. Call nn::uds::AllowToSpectate() to again allow spectator connections.

3.1.5.2. Destroying a Network as a Host Node

A host can disconnect all client and spectator systems and then free a network. Call nn::uds::DestroyNetwork() to free the network.

Code 3-14. Destroying a Network
nn::Result nn::uds::DestroyNetwork(void); 
Note:

If the host destroys the network, the client receives a disconnect reason of DISCARDED_FROM_NETWORK (in the disconnectReason member of the nn::uds::ConnectionStatus structure). In some cases, however, a reason of CONNECTION_LOST might be received depending on the communication environment.

3.1.5.3. Disconnecting as a Client or Spectator System

Call nn::uds::DisconnectNetwork() to disconnect a client or spectator system from its current network.

Code 3-15. Disconnecting From a Network
nn::Result nn::uds::DisconnectNetwork(void); 

3.1.5.4. Controlling Connections From Clients by the Host

The host allows and disallows client connections to the network.

Code 3-16. Controlling Connections From Clients
nn::Result nn::uds::DisallowToConnect(bool isDisallowToReconnect = false);
nn::Result nn::uds::AllowToConnect(); 

The nn::uds::DisallowToConnect() function maintains current connections and prevents connections from further clients. However, if false is specified in isDisallowToReconnect, reconnection is allowed if the client is disconnected after this function is called. Client connections are allowed under initial settings when the network is configured.

The nn:uds::AllowToConnect() function allows connections from clients. Denial of spectator connections set using nn::uds::EjectSpectator() remains disallowed.

3.1.5.5. Disconnections Due to Transitioning to Sleep Mode or Switching to Wireless-Disabled Mode

If the system transitions to sleep mode without terminating UDS communication or the system is changed to wireless-disabled mode using wireless communication, the internal state of UDS communication changes to nn::uds::STATE_ERROR. If any function other than nn::uds::Finalize() is called in this state, it returns nn::uds:: ResultInvalidState if the system is in sleep mode, and nn::uds::ResultWirelessOff if the system is in wireless-disabled mode. If the internal state recovers from the error, UDS communication must be ended using nn::uds::Finalize(), and then restarted. If the system was disconnected by being changed to wireless-disabled mode, communication must be reinitialized after it changes back to wireless-enabled mode.

Note:

When transitioning to sleep mode during UDS communication, we recommend that applications terminate UDS communication first and then enter sleep mode.

3.1.6. Finalizing

Call nn::uds::Finalize() to shut down UDS communication.

Code 3-17. Shutting Down the UDS Library
nn::Result nn::uds::Finalize(void); 

The library frees the memory block region allocated by nn::uds::Initialize() in addition to the nn::os::Event instance. Applications that use UDS communication must finalize UDS communication before shutting down. For more information, see Cautions When Shutting Down in the 3DS Programming Manual: System.

3.1.7. Network State Synchronization

The uds library automatically synchronizes the following information on nodes connected to a network.

  • The current connection count
  • Each node's node ID
  • Each node's local Friend Code (scrambled for privacy protection)
  • Each node's user name

The application does not need to explicitly send notifications when node information is updated.

This information is synchronized across all host and client nodes, so that clients can detect new connections and disconnections and also get information about each node.

3.1.8. Node ID Assignment

Nodes connected to a UDS communication network are managed by 16-bit numbers called node IDs. Host and client systems are assigned node IDs but spectator systems are not.

Unlike an ID in MP communications, a node ID cannot be assigned to another node after it has been used in a network. When a client reconnects to the same network, it is assigned the same node ID as when it last connected.

Because a node is assigned the same node ID when it reconnects, the host internally keeps information for 64 disconnected nodes. If more than this number of nodes are disconnected, information is deleted for the earliest client node to be disconnected. A node is assigned a different ID when it reconnects to a network from which its disconnected node information was deleted. The following node IDs are reserved and are never assigned to clients.

Table 3-5. Reserved Node IDs

Reserved Node IDs

Description

0

A nonexistent node. Used for internal library processing.

1

This is a fixed value assigned to the host.

0xFFFF (BROADCAST_NODE_ID)

This node ID is specified when broadcasting.

3.1.9. Getting Information

You can get a system’s own information, such as connection state and signal strength.

3.1.9.1. Getting the Connection State

Call the nn::uds::GetConnectionStatus() function to get the system’s current connection state.

Code 3-18. Getting the Connection State
nn::Result nn::uds::GetConnectionStatus(nn::uds::ConnectionStatus* pStatus); 

Pass a pointer to an nn::uds::ConnectionStatus structure in the pStatus parameter to hold the obtained connection state. The following sample code defines the structure.

Code 3-19. nn::uds::ConnectionStatus Structure Definition
struct nn::uds::ConnectionStatus
{
    State               nowState;
    DisconnectReason    disconnectReason;
    u16                 myNodeId;
    bit16               updateNodeBitmap;
    u16                 nodeIdList[NODE_MAX];
    u8                  nowEntry;
    u8                  maxEntry;
    bit16               slotBitmap;
}; 

The nowState member stores the current state. For more information about states, see 3.1.1. Internal UDS Communication States.

The disconnectReason member stores the reason for disconnection. The following table shows the correlation between member values and reasons.

Table 3-6. Disconnection Reasons

Value

Disconnection Reason

BEFORE_COMMUNICATION

No communication is established.

NETWORK_IS_AVAILABLE

Not disconnected. Connection is maintained.

REQUEST_FROM_MYSELF

Disconnected due to the local host’s own operation.

REQUEST_FROM_SYSTEM

Disconnected due to a request from the Nintendo 3DS system, such as for transition to sleep mode or wireless-disabled mode.

DISCARDED_FROM_NETWORK

Disconnected due to an operation on the host system.

CONNECTION_LOST

Connection could not be maintained due to deteriorating signal conditions.

UNKNOWN_DISCONNECT_REASON

Disconnection reason unknown.

The myNodeId member stores its own Node ID. In the case of Spectator, it stores the value 0xFFFF.

The updateNodeBitmap member stores a bitmap indicating those nodes that have changed state since the previous function call. Bits of value 1 correspond to those node IDs for nodes that have had state changes. Bit 1 in the bitmap corresponds to the first element in nodeIdList, with the least-significant bits to most-significant bits corresponding to the first through last elements.

The nowEntry member stores the number of nodes currently connected to the network, and the maxEntry member stores the maximum number of nodes that can simultaneously connect. Note that maxEntry cannot be changed while communication is in progress.

The slotBitmap member stores a bitmap indicating the slots storing node information. This is not a delta, and reflects the current data state.

3.1.9.2. Getting the Signal Strength

Call the nn::uds::GetLinkLevel() function to get the system’s current signal strength ("link level").

Code 3-20. Getting the Signal Strength
nn::Result nn::uds::GetLinkLevel(nn::uds::LinkLevel* pLinkLevel);
nn::uds::LinkLevel nn::uds::GetLinkLevel(); 

Pass a pointer to the variable for holding the signal strength value in the pLinkLevel parameter. The following table shows how link level values correspond to signal strength.

Table 3-7. Signal Strength

Value

Description

LINK_LEVEL_0

Signal strength is very low, or communication has not been established.

LINK_LEVEL_1

Signal strength is low.

LINK_LEVEL_2

Signal strength is medium.

LINK_LEVEL_3

Signal strength is good.

Applications do not need to constantly display the signal strength. Because this is a blocking function, we do not recommend calling it frequently (such as every game frame).

To display the signal strength as an indicator before connecting to a network as a client or spectator node, call GetRadioStrength on the instance of the nn::uds::NetworkDescriptionReader class obtained as the result of nn::uds::Scan().

3.1.9.3. Getting the Node Information

Call the nn::uds::GetNodeInformation() function to get user information for the specified node.

Code 3-21. Getting the Node Information
nn::Result nn::uds::GetNodeInformation(nn::uds::NodeInformation* pNodeInfo, 
                                       u16 nodeId); 

pNodeInfo specifies a pointer to the nn::uds::NodeInformation structure storing the node information.

The nodeId argument specifies the ID of the node for which to get information.

3.1.9.4. Getting the Channel

Call the nn::uds::GetChannel() function to get the current channel.

Code 3-22. Getting the Channel Information
nn::Result nn::uds::GetChannel(u8* pChannel); 

pChannel specifies a pointer to the variable that stores the channel value.

3.2. Nintendo 3DS Download Play

Nintendo 3DS Download Play (or simply "Download Play") is a feature used by hosts to distribute programs to clients through wireless communication, as in Nintendo DS Download Play. Nintendo 3DS Download Play has the following characteristics.

  • Clients of up to 32 MB (33,554,432 Bytes) can be distributed.
  • Only applications with client programs that run in standard mode can be distributed.
  • If necessary, a system update is performed by wireless communication before distribution of client programs.
  • A client program can get information that identifies the host during local communication.
  • A client program is saved to a dedicated memory region, and is overwritten if another client program is downloaded.
  • Client programs are not displayed on the HOME Menu (and cannot be started from the HOME Menu).

 

Warning:

Nintendo 3DS Download Play cannot be performed between a development CTR system (debugger or development system) and a retail CTR system.

Note:

The size of the client program is its size after being imported. To check this value, open the DevMenu's SDMC or HIO tab, and check the RequiredSize value when the client program is selected.

Use the DLP library provided by the CTR-SDK to support Download Play in your application.

The program for clients is provided by the system application, so for distribution, the application must prepare both the client program and the server code used to control how the program is distributed. This library also provides a class that supports features for clients acting as fake Download Play children. Because the library handles communication internally, the application simply needs to call the necessary functions to check the state.

The following figure shows the process flow on the server carrying out for Nintendo 3DS Download Play, based on the state transitions for the parent (server) and child (client).

Figure 3-2. Process Flow Carried Out for Nintendo 3DS Download Play (Server)

CLIENT_SERVER_STATE_INVALID STATE_INVALID CLIENT_STATE_DISCONNECTED_NETWORK CLIENT_STATE_SCANNING CLIENT_STATE_WAITING_CONNECT CLIENT_STATE_WAITING_ACCEPT CLIENT_STATE_DOWNLOAD_COMPLETE CLIENT_STATE_REBOOTING SERVER_STATE_OPENED_SESSIONS SERVER_STATE_DISTRIBUTING SERVER_STATE_COMPLETE_DISTRIBUTION SERVER_STATE_REBOOTING_CLIENTS SERVER_STATE_INITIALIZED Initialize() OpenSessions() AcceptClient() StartDistribute() Finalize() RebootAllClients() Initialize Reboot Start scanning Connect to server Server Side Client Side Functions called Client processing Client state Client state (invisible to server) Process flow DisconnectClient() CLIENT_STATE_WAITING_INVITE Server state If client requires system update, the system update (including download, reboot, and reconnection) is performed in this state.  Deny connection (client is disconnected) Allow connection CLIENT_STATE_DOWNLOADING CLIENT_STATE_JOINED_SESSION

 

Warning:

When Nintendo 3DS Download Play is performed with the downloadable application, the client on the lower system version of the development hardware may receive a fatal error after download is completed. The server version and the client’s system version must be the same. For the production release of a card-based software application, the system update for the client takes place via wireless communications. For a downloadable application, communication is interrupted immediately after the system update is started.

3.2.1. Initializing

The nn::dlp::Server class has all the functions required for server code. You do not need to initialize the DLP library itself. Call the nn::dlp::Server class’s Initialize function to initialize the server.

Code 3-23. Initializing
static nn::Result nn::dlp::Server::Initialize(
                bool* pNotice, nn::Handle eventHandle, u8 maxClientNum,
                u8 childIndex, void* pBuffer, size_t bufferSize,
                size_t blockBufferSize = MIN_NETWORK_BLOCK_BUFFER_SIZE * 2,
                size_t blockBufferNum = MIN_NETWORK_BLOCK_BUFFER_NUM);
static size_t nn::dlp::Server::GetBufferSize(u8 maxClientNum,
                size_t blockBufferSize = MIN_NETWORK_BLOCK_BUFFER_SIZE * 2,
                size_t blockBufferNum = MIN_NETWORK_BLOCK_BUFFER_NUM); 

pNotice indicates whether the Download Play warning message must be displayed. When true is returned, display the message provided in the guidelines. The message indicates that when a downloadable application becomes a server, connections from clients with earlier system versions will be disconnected without a warning. Note that, for future updates, if the system supports system updates via a downloadable application, message display will no longer be required and true will never be returned in pNotice. To enable switching between displaying and not displaying a message according to the state of system updates, always display a message according to the result in pNotice. The pNotice parameter is never set to true for card-based applications. However, if a message is displayed according to pNotice in a card-based application, no source code modifications will be needed if it is later released as a downloadable application.

For eventHandle, pass the handle in the nn::os::Event class, which waits for notifications from Download Play. The application must initialize the events that pass this handle.

For maxClientNum, specify the maximum number of clients that can connect to the server. This value is between 1 and MAX_CLIENT_NUM.

For childIndex, specify the index of the client program to be distributed (ChildIndex in the RSF file). Icons and titles displayed on the client are obtained from header information in the client program specified by this parameter.

For the pBuffer and bufferSize parameters, specify the work buffer and its size. Pass the block buffer size and number of buffers in blockBufferSize and blockBufferNum. The block buffer is a client program read-out buffer. The application must allocate the work buffer from a location that is not in device memory, starting at an address that is nn::dlp::Server::BUFFER_ALIGNMENT (4096-byte) aligned. The buffer size must be a multiple of nn::dlp::Server::BUFFER_UNITSIZE (4096 bytes) that is greater than or equal to the size obtained from the GetBufferSize() function passing maxClientNum, blockBufferSize, and blockBufferNum as arguments. If an out-of-range value is specified for the argument, GetBufferSize() returns an indefinite value.

The ServerWithName class adds the ability to specify the user name to the Initialize() function of the Server class. If this class is used to specify a user name, always check for profanity according to the UGC guidelines. If the user name contains profanity according to the check, set isNgUserName to true in the structure passed to the Initialize() function, and pass the user name to the function as is, without processing to remove or overwrite the profanity. The ServerWithName class is the same as the Server class, except for the Initialize() function.

The following table shows the return values that can be returned by the Initialize() function and their causes.

Table 3-8. Return Values Returned by the Initialize() Function

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultAlreadyOccupiedWirelessDevice

The wireless communication module is secured by another wireless process. Terminate infrastructure communication or other communications.

ResultOutOfRange

Out-of-range value specified in argument.

ResultInvalidPointer

Invalid pointer specified in argument.

ResultInvalidHandle

Invalid handle specified in argument.

ResultInternalError

Unrecoverable error in library.

ResultFailedToAccessMedia

Either the client program having the specified index does not exist, or the server card has been removed.

ResultChildTooLarge

The size of the client program exceeds the maximum size that can be distributed.

ResultInvalidRegion

The regions of the host and client programs differ.

ResultWirelessOff

Wireless communication unavailable (sleep mode or wireless-disabled mode).

3.2.2. Checking the State

There are two ways to check the server state. You can either call the GetState() function to get it periodically, or you can call the GetEventDesc() function to get event details when the event specified during initialization enters the signaled state.

Code 3-24. Checking the State
static nn::Result nn::dlp::Server::GetEventDesc(
                                nn::dlp::EventDesc* pEventDesc);
static nn::Result nn::dlp::Server::GetState(nn::dlp::ServerState* pState); 

The GetEventDesc() function can get details for up to 32 events for storage in a queue, discarding the earliest items when the queue becomes full. Processing returns immediately with a return value of nn::dlp::ResultNoData in the absence of an event.

However, this does not mean that a server cannot function without monitoring events’ signaled states. It is probably easier to implement server code that calls GetState() periodically to check the state and then process it appropriately.

Note:

For this server implementation we strongly recommend polling for the state using GetState(). This makes error handing easier when errors arise, which invariably happens if it becomes impossible to sustain the communications state, for example, if the system enters sleep mode or switches to wireless-disabled mode while downloading or being invited to participate in a session.

The following table shows the server states that can be obtained by the GetState() function.

Table 3-9. Server States

Definition

Description

SERVER_STATE_INVALID

Not initialized yet.

SERVER_STATE_INITIALIZED

Server initialization has completed.

SERVER_STATE_OPENED_SESSIONS

The server is accepting requests to join a session. Clients can join only in this state.

SERVER_STATE_DISTRIBUTING

The server is distributing.

SERVER_STATE_COMPLETE_DISTRIBUTION

The server has finished distributing.

SERVER_STATE_REBOOTING_CLIENTS

The server is rebooting the clients after distributing. Confirm that all clients have been disconnected before you finalize Nintendo 3DS Download Play.

SERVER_STATE_ERROR

An error has occurred, requiring reinitialization. The server transitions to this state if the system has entered sleep mode or switched to wireless-disabled mode some time between the Initialize() function and the Finalize() function, if the client has disconnected during distribution for any reason other than to reboot after distribution, or if the card has been removed in the middle of the process.

The following table shows the return values that might be returned by the GetEventDesc() or GetState() function, and their causes.

Table 3-10. Return Values Returned by the GetEventDesc() and GetState() Functions

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultNoData

No data to be obtained.

ResultInvalidPointer

Invalid pointer specified in argument.

3.2.3. Starting and Stopping Sessions

In Nintendo 3DS Download Play, a session starts after distribution requests can be received from clients and ends when the distribution has completed and the clients restart.

You can use the OpenSessions() or CloseSessions() function to start or stop sessions.

Code 3-25. Starting and Stopping Sessions
static nn::Result nn::dlp::Server::OpenSessions(
                                bool isManualAccept = false, u8 channel = 0);
static nn::Result nn::dlp::Server::CloseSessions(); 

Start a session with true for the isManualAccept parameter to have the application handle accepting or rejecting clients that attempt to join the session. Omit this argument or pass false to automatically accept connections.

Specify any one of 0, 1, 6, or 11 in channel for the communication channel to be used. If 0 is specified, the channel is selected automatically. Usually, no channel needs to be specified.

Warning:

The channel is always selected automatically when executed on production version hardware.

If a client disconnects in the middle of a session or if, for another reason, the server state transitions stop, call the CloseSessions() function to stop the session. Because clients that have rebooted following distribution will not necessarily reconnect to the server, you must implement your server code to allow the user to stop a session at any time.

The following table shows the return values that might be returned by OpenSessions() or CloseSessions(), and their causes.

Table 3-11. Return Values of the OpenSessions() and CloseSessions() Functions

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

The function called an invalid state.

ResultOutOfRange

Out-of-range value specified in argument.

ResultWirelessOff

Wireless communication unavailable (sleep mode or wireless-disabled mode).

3.2.4. Checking the Connection State

After you have started a session and as long as no errors have occurred, use the GetConnectingClients() function to check both the number of connected clients and their node IDs. Use these IDs to get the client information and state with the GetClientInfo() and GetClientState() functions.

Code 3-26. Checking the Connection State
static nn::Result nn::dlp::Server::GetConnectingClients(
                        u16* pNum, u16 clients[], u16 size);
static nn::Result nn::dlp::Server::GetClientInfo(
                        nn::dlp::NodeInfo* pClientInfo, u16 nodeId);
static nn::Result nn::dlp::Server::GetClientState(
                        nn::dlp::ClientState* pClientState, u16 nodeId);
static nn::Result nn::dlp::Server::GetClientState(
                        nn::dlp::ClientState* pClientState, size_t* pTotalNum,
                        size_t* pDownloadedNum, u16 nodeId); 

The GetConnectingClients() function stores the number of clients connected to a session in the pNum parameter and a list of their node IDs in pClients. Note that pNum is a pointer to a u16 variable and clients is a u16 array. Set size equal to the size of the array specified by pClients. The array size must be greater than or equal to the value specified for maxClientNum during initialization.

Use the nodeId parameter to the GetClientInfo() or GetClientState() function to specify the node ID of the client for which to get information. If you specify a nonexistent client or server node ID, the function returns nn::dlp::ResultNoData.

The nn::dlp::NodeInfo structure specified by the pClientInfo parameter of the GetClientInfo() function stores the same node information mentioned in 3.1.9.3. Getting the Node Information. You can use this information to display user names and for other purposes.

The nn::dlp::ClientState-type pointer specified by the pClientState parameter to the GetClientState() function stores the client state. pTotalNum holds the total number of packets of the client program, and pDownloadNum holds the number of downloaded packets. Be careful with division for showing progress, because the value of pTotalNum can be 0.

The client state is stored in pClientState.

Table 3-12. Client State (Only When It Can Be Checked by the Server)

Definition

Description

CLIENT_STATE_WAITING_INVITE

Accepted by the server to participate in a session and waiting to be invited by the host to participate in a session.

CLIENT_STATE_WAITING_ACCEPT

The client is connected to the network and waiting for the server to accept participation in the session.

CLIENT_STATE_JOINED_SESSION

Joined in the session.

CLIENT_STATE_DOWNLOADING

Distribution has started and downloading is in progress. Note that a distribution may have just started and no data may have been received from the server yet.

CLIENT_STATE_DOWNLOAD_COMPLETE

Downloading has completed.

The following table shows the return values that might be returned by the GetConnectingClients(), GetClientInfo(), or GetClientState() functions, and their causes.

Table 3-13. Return Values Returned by the GetConnectingClients(), GetClientInfo(), and GetClientState() Functions

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

The function called an invalid state.

ResultInvalidPointer

Invalid pointer specified in argument.

ResultNoData

Not connected to a client with the specified node ID.

3.2.5. Accepting and Rejecting Connections

After the server has started a session, it accepts client requests to join the session until it starts distribution. An application can accept or reject client connections if the session was started with true passed as the isManualAccept argument.

Code 3-27. Accepting and Rejecting Connections
static nn::Result nn::dlp::Server::AcceptClient(u16 nodeId);
static nn::Result nn::dlp::Server::DisconnectClient(u16 nodeId); 

You can accept connections with AcceptClient() and reject connections with DisconnectClient(). However, these functions only work when the client state is CLIENT_STATE_WAITING_ACCEPT.

The following table shows the return values that might be returned by AcceptClient() or DisconnectClient(), and their causes.

Table 3-14. Return Values Returned by the AcceptClient() and DisconnectClient() Functions

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

The function called an invalid state.

ResultNoData

Not connected to a client with the specified node ID.

ResultWirelessOff

Wireless communication unavailable (sleep mode or wireless-disabled mode).

3.2.6. Starting Distribution

After you have established the clients to distribute to, you can call the StartDistribute() function to stop accepting requests to join the session and start distribution to clients.

Code 3-28. Starting Distribution
static nn::Result nn::dlp::Server::StartDistribute(); 

With this function, the system starts distributing and transitions to the SERVER_STATE_PREPARING_FOR_TITLE_DISTRIBUTION state.

Simply check the client connection states, display progress, and otherwise wait for the server state to transition to SERVER_STATE_COMPLETE_DISTRIBUTION, signaling that the distribution is complete.

Warning:

Clients whose state does not change from CLIENT_STATE_JOINED_SESSION even after distribution has started are clients that require a system update. These clients remain in this state while they receive the system update (a process which includes download, reboot, and reconnection), and until the client state transitions to CLIENT_STATE_DOWNLOADING, the server simply waits for the client to reconnect. Not all clients reconnect, however, because communication between the server and client may have become impossible (due to, for example, canceling a download, switching to wireless-disabled mode, degradation of communication quality, and so on). For this reason, make sure that you implement a solution so that user operations can stop a session at any time (using the CloseSessions() function).

The following table shows the return values that might be returned by the StartDistribute() function, and their causes.

Table 3-15. Return Values Returned by the StartDistribute() Function

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

The function was called in an inappropriate state, or no client exists to be distributed.

ResultWirelessOff

Wireless communication unavailable (sleep mode or wireless-disabled mode).

ResultFailedToAccessMedia

Failed to access media. The server card may have been removed.

Note:

The specifications state that the client does not transition to the disconnected screen even if the connection is lost during distribution (for example, if the server card is pulled out). The application also does not need to deal with this situation.

3.2.7. Rebooting Clients

After distribution has finished and the server state has transitioned to SERVER_STATE_COMPLETE_DISTRIBUTION, call the RebootAllClients() function to reboot all clients that have joined the session.

Code 3-29. Rebooting Clients
static nn::Result nn::dlp::Server::RebootAllClients(
                        const char passpharase[] = NULL); 

For passphrase, specify the passphrase used when the network was created, because it is needed to conduct local communications after clients reboot. You can specify a string as long as MAX_CHILD_UDS_PASSPHRASE_LENGTH, counting the NULL character. Specify a different value for this passphrase for each download session, and do not specify strings that are easy to guess.

When this function is called after distribution completes, all clients are restarted and the server state transitions to SERVER_STATE_REBOOTING_CLIENTS. Finalize the server after you have checked the clients’ connection states and confirmed that they have all disconnected.

The following table shows the return values that might be returned by the RebootAllClients() function, and their causes .

Table 3-16. Return Values Returned by RebootAllClients()

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

The function called an invalid state.

3.2.8. Finalizing

After confirming that every client has rebooted and disconnected from the session, you can call the Finalize() function to finalize the server. The Finalize() function must be called when transiting to sleep status due to closing the system.

Code 3-30. Finalization
static nn::Result nn::dlp::Server::Finalize(); 

Nintendo 3DS Download Play ends when finalization is complete.

To perform local communication with a distributed client program, create a network using the UDS library after finalization and wait for clients to connect. However, note that clients do not necessarily reconnect.

The following table shows the return values that might be returned by Finalize(), and their causes.

Table 3-17. Return Values Returned by Finalize()

Return Value

Cause

IsSuccess() returns true

The process was successful.

3.2.9. Client Programs

After downloading a client program through Download Play, a client reboots itself as instructed by the server. To make it easier for the client program to perform local communication with the host that used to be the server, you can get information for reconnecting to this host from the nn::dlp::GetRebootInfo() function.

Code 3-31. Getting Reconnection Information
nn::Result nn::dlp::GetRebootInfo(nn::dlp::RebootInfo* pRebootInfo); 

You can call this function without initializing the DLP library. Pass pRebootInfo a pointer to a nn::dlp::RebootInfo structure that stores reconnection information.

Code 3-32. Definition of the nn::dlp::RebootInfo Structure
typedef struct
{
    u8      bssid[6];
    char    passphrase[MAX_CHILD_UDS_PASSPHRASE_LENGTH];
    NN_PADDING1;
} nn::dlp::RebootInfo; 

The BSSID and passphrase of the network to be created by the parent are stored in bssid and passphrase.

Based on this information, the application can automatically determine which network to connect to.

3.2.9.1. Embedding a Client Program

The unique IDs for the child program and the application on the server as specified in the RSF file must match. In addition, the client program must have a Category of DlpChild. Although you need a CTR icon for your client program, you do not need a CTR title banner for it.

If you are using OMake to build the application, specify the client program’s CIA file in the CHILD_APPS[] build variable in the OMakefile for the application on the server.

If you are not using OMake, use the ctr_makeciaarchive command-line tool to create a CFA file, and include this in the CCI file by using the -content option of ctr_makerom.

3.2.9.2. Forcing a Download of the Client Program

Clients generally download only client programs that are either not present on the client or that are newer than previously imported versions, but you can use the Config tool to force a client to ignore the client program version and download it anyway.

3.2.9.3. Debugging a Client Program

Complete the following steps to debug a client program after Download Play has started the client program.

  • Attach the client program by calling the debugger’s ATTACHA command.
  • Execution pauses, so use the LS command to load the debugging information from the AXF file.
  • Resume execution.

3.2.9.4. Identifying Client Programs

Call the nn::dlp::IsChild() function to determine whether the currently running application is a client program.

Code 3-33. Identifying Client Programs
bool nn::dlp::IsChild(); 

Do not call this function when using the nn::dlp::Server or nn::dlp::FakeClient objects (the interval from first calling Initialize() until Finalize() completes).

3.2.10. Fake Clients

Fake clients (nn::dlp::FakeClient class) are provided so that applications can join Download Play sessions as if they were clients without actually downloading the client program or restarting.

The use of fake clients enables applications to participate in the same session as the Nintendo 3DS systems that are participating in Download Play using smooth wireless communications in a network that includes both applications and client programs. The obvious advantage is that this allows the network to include users with and without the software. But it also enables session management and other tasks to be handled by the DLP library.

Warning:

When revising a title that uses a fake client with an update or remaster version, special consideration is required if a fake client that differs from the remaster version will connect to the DLP server.

See the chapter about fake client features in the Patch Manual.

The following figure shows the process flow carried out on the fake client for Nintendo 3DS Download Play, based on the state transitions for the parent (server) and child (client).

Figure 3-3. Process Flow of Download Play (Fake Client)

CLIENT_SERVER_STATE_INVALID STATE_INVALID CLIENT_STATE_DISCONNECTED_NETWORK CLIENT_STATE_SCANNING CLIENT_STATE_WAITING_CONNECT CLIENT_STATE_WAITING_ACCEPT CLIENT_STATE_JOINED_SESSION CLIENT_STATE_REBOOTING SERVER_STATE_OPENED_SESSIONS SERVER_STATE_DISTRIBUTING SERVER_STATE_COMPLETE_DISTRIBUTION SERVER_STATE_REBOOTING_CLIENTS SERVER_STATE_INITIALIZED Initialize Start session Allow connection End Reboot clients Initialize() Finalize() StartScan() StartFakeSession() Server Side Fake Client Side Server processing Functions called Client state Client state (invisible to server) Process flow CLIENT_STATE_WAITING_INVITE CLIENT_STATE_DOWNLOAD_COMPLETE CLIENT_STATE_DOWNLOADING Server state Start distribution Server state (invisible to client)

3.2.10.1. Initializing

The nn::dlp::FakeClient class has all the functions required for fake client code. You do not need to initialize the DLP library itself. Just call the class’s Initialize() function to initialize the fake client.

Code 3-34. Initializing the Fake Client
static nn::Result nn::dlp::FakeClient::Initialize(
u8 scanNum, nn::Handle eventHandle, 
void* pBuffer, const size_t bufferSize);
static size_t nn::dlp::FakeClient::GetBufferSize(u8 scanNum); 

For scanNum, specify the maximum number of titles that can be obtained in one scanning. Set this in the range from 1 to MAX_SCAN_NUM.

For eventHandle, pass the handle in the nn::os::Event class, which waits for notifications from Download Play. The application must initialize the events that pass this handle.

For the pBuffer and bufferSize parameters, pass the work buffer and its size. The application must allocate the work buffer from a location that is not in device memory, starting at an address that is nn::dlp::FakeClient::BUFFER_ALIGNMENT (4096-byte) aligned. The buffer size must be a multiple of nn::dlp::FakeClient::BUFFER_UNITSIZE (4096 bytes) that is greater than or equal to the size obtained from the GetBufferSize() function passing scanNum as an argument. If an out-of-range value is specified for the argument, GetBufferSize() returns an indefinite value.

The FakeClientWithName class adds the ability to specify the user name to the Initialize() function of the FakeClient class. If this class is used to specify a user name, always check for profanity according to the UGC guidelines. If the user name contains profanity according to the check, set isNgUserName to true in the structure passed to the Initialize() function, and pass the user name to the function as is, without processing to remove or overwrite the profanity. The FakeClientWithName class is the same as the FakeClient class, except for the Initialize() function.

The following table shows the return values that can be returned by the Initialize() function, and their causes.

Table 3-18. Return Values Returned by the Initialize() Function

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultAlreadyOccupiedWirelessDevice

The wireless communication module is secured by another wireless process. Terminate infrastructure communication or other communications.

ResultOutOfRange

Out-of-range value specified in argument.

ResultInvalidPointer

Invalid pointer specified in argument.

ResultInvalidHandle

Invalid handle specified in argument.

ResultInternalError

Unrecoverable error in library.

ResultWirelessOff

Wireless communication unavailable (sleep mode or wireless-disabled mode).

3.2.10.2. Checking the State

There are two ways to check the state of the fake client. You can use the GetEventDesc() function to get event details when the event specified during initialization enters the signaled state, or you can use the GetMyStatus() function to get the state periodically.

Code 3-35. Checking the Fake Client’s State
static nn::Result nn::dlp::FakeClient::GetEventDesc(
                        nn::dlp::EventDesc* pEventDesc);
static nn::Result nn::dlp::FakeClient::GetMyStatus(
                        nn::dlp::ClientStatus* pStatus); 

The GetEventDesc() function can get details for up to 32 events for storage in a queue, discarding the earliest items when the queue becomes full. If there are no events, processing returns immediately with a return value of nn::dlp::ResultNoData.

Monitoring for the signaled states of events is not necessary for fake clients to work. It is probably easier to implement code that calls GetMyStatus() periodically (for example, every game frame) to check the state and then process it appropriately.

Note:

For this fake client implementation we strongly recommend polling for the state using GetMyStatus(). This makes error handing easier when errors arise. This invariably happens if it becomes impossible to sustain the communications state; for example, if the system enters sleep mode or switches to wireless-disabled mode while scanning for servers or downloading.

The nn::dlp::ClientStatus structure obtainable with GetMyStatus() is defined as follows.

Code 3-36. Definition of nn::dlp::ClientStatus Structure
typedef struct
{
    u16             nodeId;
    SessionType     sessionType;
    ClientState     state;
    size_t          totalNum;
    size_t          downloadedNum;
} nn::dlp::ClientStatus; 

The state of the fake client is stored in the state member. Table 3-19 provides the definitions for the stored values.

Table 3-19. Fake Client States (Only Those States the Fake Client Can Check)

Definition

Description

CLIENT_STATE_INVALID

Not initialized yet.

CLIENT_STATE_DISCONNECTED_NETWORK

Initialization completed, or disconnected from the server.

CLIENT_STATE_SCANNING

Scanning for servers.

CLIENT_STATE_WAITING_CONNECT

Waiting to connect to the network.

CLIENT_STATE_WAITING_INVITE

Waiting to be invited to join the session.

CLIENT_STATE_JOINED_SESSION

Joined in the session.

CLIENT_STATE_DOWNLOADING

Distribution has started and downloading is in progress. Fake clients do not receive data from the server.

CLIENT_STATE_DOWNLOAD_COMPLETE

Downloading has completed.

CLIENT_STATE_RECONNECTING_NETWORK

Fake clients do not transition to this state.

CLIENT_STATE_REBOOTING

Reboot request received from the server.

CLIENT_STATE_ERROR

An error has occurred that requires reinitialization.

The server transitions to this state if the system entered sleep mode or switched to wireless-disabled mode sometime between the Initialize() function and the Finalize() function, it is disconnected from the server during distribution, or if another client was disconnected from the server during distribution.

The values stored in members other than state do not need to be implemented for fake clients.

3.2.10.3. Scanning for Servers

To get a list of servers (titles), the fake client scans for servers that have started Download Play sessions.

Code 3-37. Scanning for Servers
static nn::Result nn::dlp::FakeClient::StartScan(
                u32 uniqueId, u8 childIndex, const u8* pMac = NULL);
static nn::Result nn::dlp::FakeClient::StopScan(); 

Call the StartScan() function to start scanning for servers. You can narrow down the scan target titles and servers by specifying values for uniqueId, childIndex, and pMac.

Warning:

If you select the server for another application, the operations of that server may be adversely affected. For fake clients you must specify values for uniqueId and childIndex and scan only for those servers distributing specific titles.

Scanning for servers continues until you call the StopScan() function. The scan result for as many servers as specified by the scanNum parameter of the Initialize() function is retained until the next call to StartScan().

The following table shows the return values that can be returned by the StartScan() function, and their causes.

Table 3-20. Return Values Returned by the StartScan() Function

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

Function called in invalid state (in some state other than CLIENT_STATE_DISCONNECTED_NETWORK).

3.2.10.4. Getting the Scan Results

To get the results of the scanning, call the GetTitleInfo() function and get the results in the form of a list of title information (nn::dlp::TitleInfo). Based on this title information, you can use the GetServerInfo() function to get the server information (nn::dlp::ServerInfo). These functions return nn::dlp::ResultInvalidState if they are called when the state of the fake client is either CLIENT_STATE_INVALID or CLIENT_STATE_ERROR.

Code 3-38. Getting the Scan Results
static nn::Result nn::dlp::FakeClient::GetTitleInfo(
        nn::dlp::TitleInfo* pTitleInfo, bool isReset = false);
static nn::Result nn::dlp::FakeClient::GetTitleInfo(
        nn::dlp::TitleInfo* pTitleInfo, 
        const u8* pMac, u32 uniqueId, u8 childIndex);
static nn::Result nn::dlp::FakeClient::GetServerInfo(
        nn::dlp::ServerInfo* pServerInfo, const u8* pMac);
static nn::Result nn::dlp::FakeClient::DeleteScanInfo(const u8* pMac); 

Among the overloaded GetTitleInfo() functions, those that have the isReset parameter are used to get the list of title information. Normally you set isReset to false to get title information that has not yet been obtained by the GetTitleInfo() function. When IsSuccess() on the instance of the return value returns true, new title information has been obtained. If there is no not-yet-obtained title information or if no more title information can be saved, nn::dlp::ResultNoData is returned. By specifying true for isReset you can get the whole list of title information again from the start, including title information that has already been obtained. Scanning for servers continues from its start with StartScan() to its end with StopScan(), and you can store title information on as many titles as specified by the scanNum parameter of the Initialize() function. The list of title information is cleared the next time StartScan() is called.

The overloaded function with the pMac, uniqueId, and childIndex parameters is used when you know the server’s MAC address, unique ID, and client program index.

The nn::dlp::TitleInfo structure is defined as follows.

Code 3-39. Definition of nn::dlp::TitleInfo
typedef struct
{
    u32                         uniqueId;
    u8                          childIndex;
    NN_PADDING3;
    u8                          mac[6];
    u16                         programVersion;
    bit8                        ratingInfo[RATING_INFO_SIZE];
    wchar_t                     shortTitleName[SHORT_TITLE_NAME_LENGTH];
    wchar_t                     longTitleName[LONG_TITLE_NAME_LENGTH];
    nn::dlp::IconInfo           icon;
    u32                         importSize;
    nn::cfg::CfgRegionCode      region;
    bool                        ulcd;
    NN_PADDING2;
} nn::dlp::TitleInfo; 

Use the three members uniqueId, childIndex, and mac to specifically identify title information.

Use the GetServerInfo() function to get server information. For pMac, specify the MAC address of the server from which you want to get server information. If there is no device with that specified MAC address or if the device is not operating as a Download Play server, the function returns nn::dlp::ResultNoData.

The server information includes information about link levels and a list of connected clients. The structure is defined as follows.

Code 3-40. Definition of nn::dlp::ServerInfo
typedef struct
{
    u8                      mac[6];
    u8                      channel;
    nn::uds::LinkLevel      linkLevel;
    u8                      maxNodeNum;
    u8                      nodeNum;
    bit16                   dlpVersion;
    NN_PADDING4;
    NodeInfo                nodeInfo[MAX_NODE_NUM];
    nn::os::Tick            lastUpdateTick;
} nn::dlp::ServerInfo; 

If the state is CLIENT_STATE_SCANNING (meaning that scanning is currently taking place), the values for linkLevel (the link level), nodeNum (the number of connected nodes, including the server), and nodeInfo (details about the nodes) all reflect the current state. In the other states there are no changes, so in these cases use the functions that are introduced in 3.2.10.6. Getting Extraneous Information.

Download Play servers are invariably assigned 1 as their node ID, just like the hosts in Local Play communications. So that several servers can be listed even when scanning has been narrowed down, consider measures such as showing user names (userName.userName) for nodes that have 1 as their node ID (nodeId) in the node information (nodeInfo) of the server information. If isNgUserName in the user name information (username) is true, the user name contains profanity. In that case, you must follow the Guidelines about profanity.

To delete specific sets of title information from the scan results, call the DeleteScanInfo() function. The scan results are deleted for the server with the MAC address specified by pMac. An example of when to use this is when information about a server has not been updated for a specified period of time. If there is no server with the specified MAC address, the function returns nn::dlp::ResultNoData. The function returns nn::dlp::ResultInvalidState if it is called when the state is something other than CLIENT_STATE_DISCONNECTED_NETWORK or CLIENT_STATE_SCANNING.

3.2.10.5. Joining a Session

To join a session, select a server for connection from the list of servers (title information) and call the StartFakeSession() function. From the server’s point of view, the fake client looks just like the actual clients that are participating in the system application’s Download Play session, but the fake client does not download the client program.

Note:

Before joining the session you must call StopScan() and stop scanning for servers.

Code 3-41. Joining in Session
static nn::Result nn::dlp::FakeClient::StartFakeSession(
                        const u8* pMac, u32 uniqueId, u8 childIndex);
static nn::Result nn::dlp::FakeClient::StopFakeSession(); 

The values specified for the pMac, uniqueId, and childIndex parameters can be obtained from the title information obtained by GetTitleInfo().

The following table shows the return values that can be returned by the StartFakeSession() function, and their causes.

Table 3-21. Return Values Returned by the StartFakeSession() Function

Return Value

Cause

IsSuccess() returns true

The process was successful.

ResultInvalidState

Function called in invalid state (in some state other than CLIENT_STATE_DISCONNECTED_NETWORK).

ResultNoData

The title as specified by pMac, uniqueId, and childIndex does not exist.

ResultWirelessOff

Wireless communication unavailable (sleep mode or wireless-disabled mode).

ResultNotFoundServer

The server as specified by pMac does not exist.

ResultServerIsFull

Cannot connect to the server because the maximum number of clients is already connected. Cannot connect unless the number of connected clients decreases.

ResultDeniedFromServer

Connection denied because the client program is being distributed by the server.

ResultConnectionTimeout

Connection was not established within a certain amount of time. This result can be returned when wireless reception is poor or when the communications load is heavy.

ResultInternalError

Error due to an inconsistency in the library.

If the state transitions have stopped during a session, call the StopFakeSession() function to stop the session. The IsSuccess() method on the instance returned by this function invariably returns true. Just as is the case with servers, you must implement fake clients so that users can end sessions at any time.

3.2.10.6. Getting Extraneous Information

Several functions have been prepared for getting extraneous information that is not essential to the implementation of fake clients.

Code 3-42. Getting Extraneous Information
static nn::Result nn::dlp::FakeClient::GetConnectingNodes(
                        u8* pNum, u16* pNodeIds, u16 size);
static nn::Result nn::dlp::FakeClient::GetNodeInfo(
                        nn::dlp::NodeInfo* pNodeInfo, u16 nodeId);
static nn::Result nn::dlp::FakeClient::GetLinkLevel(
                        nn::uds::LinkLevel* pLinkLevel); 

Use the GetConnectingNodes() function to get a list of nodes that are connected to a session.

Use the GetNodeInfo() function to get detailed information about the nodes that are connected to a session. The nn::dlp::NodeInfo returned to pNodeInfo is the same as the nn::uds::NodeInfomation in local communication. For more about this detailed node information, see 3.1.9.3. Getting the Node Information.

Use the GetLinkLevel() function to get the link level (the quality of the connection) with the server. For more information about the values returned to pLinkLevel, see 3.1.9.2. Getting the Signal Strength.

3.2.10.7. Ending Download

When the fake client has transitioned to the CLIENT_STATE_DOWNLOAD_COMPLETE state, it indicates only that the fake client itself has completed downloading. Other clients may still be downloading. The overall Download Play session has not completed yet, so wait until all clients connected to the server have finished downloading.

When all clients connected to the server have finished downloading, the server issues a reboot request to all clients. When the fake client receives this request, it transitions to the CLIENT_STATE_REBOOTING state. Only at this stage can you use the GetPassphrase() function to get the passphrase for local communications that was specified by the server.

After the passphrase has been obtained, call the Finalize() function and conduct the fake client finalization process.

Code 3-43. Getting Passphrase and Finalizing
static nn::Result nn::dlp::FakeClient::GetPassphrase(char* pPassphrase);
static nn::Result nn::dlp::FakeClient::Finalize(); 

For the pPassphrase parameter of GetPassphrase(), specify a buffer that is equal to or larger than MAX_CHILD_UDS_PASSPHRASE_LENGTH bytes. If the state is anything other than CLIENT_STATE_REBOOTING, the function returns ResultInvalidState.

Finalize() always succeeds. Consequently, the IsSuccess() method on the returned instance always returns true.

To conduct local communications with the parent (the server) at this stage, use the MAC address of the server that was selected when the session started and the passphrase that was obtained by the GetPassphrase() function.

Make sure that you call the Finalize() function and perform the finalization process when the state transitions to sleep mode and when the HOME Menu needs to start.

3.3. Automatic Connection

The AC (automatic connection) library is for automatically connecting to the Internet via the means specified in the system’s network settings, specifically via a wireless LAN access point, the Nintendo Wi-Fi USB Connector, the Nintendo Zone, a retail access point, or a publicly available wireless LAN access point. There are no libraries for directly accessing the Nintendo 3DS system’s wireless communication module, so you must use the ac library to establish an Internet (or LAN) connection to access external servers from a Nintendo 3DS unit.

The AC library accesses a wireless LAN access point or other network device according to the system’s network settings. Consequently, valid wireless LAN access point connection settings must be configured ahead of time.

Note:

Instead of a menu for configuring the system settings, Nintendo provides the NetworkSetting tool to change network settings. The AC library also provides the nn::ac::DebugSetNetworkSetting1() debugging function for changing network setting 1, the nn::ac::DebugSetNetworkArea() function for specifying whether the automatically established connection need be all the way to the Internet or just to the access point, and the nn::ac::DebugSetApType() function for specifying the types of access points to connect to as a bitwise OR of ApType enumerators (see Table 3-22. nn::ac::ApType Enumerated Type).

Note that these functions only work when the system is set to debugging mode (To set a system to debugging mode, open the Config tool, select Debug Settings, and set Debug Mode to enable).

If the AC library is used to establish an Internet connection, the daemon manager secures the wireless communication module in infrastructure communication mode. This may interfere with background communication such as is used for StreetPass connections.

AC library functions return their results as instances of the nn::Result class. IsSuccess() returns true if execution has completed successfully.

3.3.1. Initializing

Call the nn::ac::Initialize() function to initialize the AC library.

Code 3-44. Initializing the AC Library
nn::Result nn::ac::Initialize();
bool nn::ac::IsInitialized(); 

If nn::ac::ResultAlreadyInitialized is returned, the application has already initialized the AC library. Although this result is not an error, the Finalize() function must be called the same number of times as the Initialize() function is called because the library maintains a counter of the number of times initialization has been performed. Call nn::ac::IsInitialized() to determine whether the library has already been initialized.

3.3.2. Connecting Automatically

Automatic connections are established according to the conditions created by a call to the nn::ac::CreateDefaultConfig() function.

Code 3-45. Creating Connection Conditions
nn::Result nn::ac::CreateDefaultConfig(nn::ac::Config* config); 

The connection conditions are stored in the nn::ac::Config structure passed in the config parameter. The application must allocate this structure. If NULL is specified in this parameter, nn::ac::ResultInvalidData is returned.

Start connecting automatically by passing the created connection conditions in the config parameter to the nn::ac::Connect() or nn::ac::ConnectAsync() functions.

Code 3-46. Connecting Automatically
nn::Result nn::ac::Connect(nn::ac::Config& config);
nn::Result nn::ac::ConnectAsync(nn::ac::Config& config, nn::os::Event* event);
nn::Result nn::ac::CancelConnectAsync();
nn::Result nn::ac::GetConnectResult();
bool nn::ac::IsConnected(); 
Warning:

The EULA agreement is checked during automatic connection processing. The FS library must be initialized ahead of time.

The nn::ac::ConnectAsync() function is the asynchronous version of nn::ac::Connect(). When using the asynchronous version, the event passed to the event parameter enters the signaled state when the automatic connection attempt completes, regardless of whether the attempt was successful. After the event is signaled, make sure that you get the connection result by calling the nn::ac::GetConnectResult() function. Local communication fails to start and StreetPass does not operate until you get this result. Call the nn::ac::CancelConnectAsync() function to cancel a connection attempt that is in progress. The event passed to the nn::ac::ConnectAsync() function’s event parameter enters the signaled state when the connection is canceled or when the attempt succeeds before being canceled. nn::ac::ResultCanceled is returned as the execution result if execution is canceled.

Warning:

If the nn::ac::CancelConnectAsync() function is called when a background-communication connection request is being processed, it cancels the application's connection request and returns nn::ac::ResultSuccess.

Note:

The user-defined automatic connection target settings take priority over Nintendo Zone and hot spot settings. Because a connection priority order is not defined in the user settings, specifically designate one connection target in the user settings. The connection target can be specified with nn::ac::DebugSetApType() in debugging mode.

After the automatic connection attempt has succeeded, nn::ac::ResultWanConnected is returned as the execution result if an Internet connection has been established and nn::ac::ResultLanConnected is returned if a wireless LAN connection has been established. At this point, communication via the network is possible using socket communication or another wireless communication module. nn::ac::ResultNotFoundAccessPoint is returned if a connectable access point cannot be found.

Note:

Success may be returned immediately if a background-communication connection has already been established. Conversely, if a connection has not been established, the process could take some time, and it takes longer than normal when connecting to an access point that has not established an Internet connection.

If nn::ac::ResultWifiOff is returned as the execution result, the wireless communication module cannot be used because it is in wireless-disabled mode. If nn::ac::ResultNotAgreeEula is returned as the result, it could be because the EULA agreement check has failed or because an icon file has not been set in the application. For other execution results, see the CTR-SDK API Reference.

You can call the nn::ac::IsConnected() function to determine whether the application has connected to an access point through the automatic connection process. But even if it is determined that connection to an access point or another connection has been established through background communications, you still must have your application conduct the automatic connection process. If the application does not do this, the connection may be unexpectedly cut when the process connected in the background ends.

3.3.3. Getting the Connection State

Use the following functions to get the type of access point the system has automatically connected to, or the signal strength of the access point currently connected.

Code 3-47. Getting the Connection State
nn::Result nn::ac::GetConnectingApType(nn::ac::ApType* apType);
nn::Result nn::ac::GetConnectingNintendoZoneBeaconSubset(
                                nn::ac::NintendoZoneBeaconSubset* beacon);
nn::Result nn::ac::GetConnectingHotspotSubset(nn::ac::HotspotSubset* hotspot);
nn::Result nn::ac::GetLinkLevel(nn::ac::LinkLevel* linkLevel);
nn::ac::LinkLevel nn::ac::GetLinkLevel(); 

The nn::ac::GetConnectingApType() function returns the type of connected access point in the apType parameter. If called while unconnected, nn::ac::ResultNotConnecting is returned in the return value. Access point types are defined as nn::ac::ApType enumerators as shown in the following table. Access point types may be added in firmware updates, so note when implementing applications that you may get a state other than those listed.

Table 3-22. nn::ac::ApType Enumerated Type

Enumerator

Access Point

AP_TYPE_NONE

None

AP_TYPE_USER_SETTING_1

Network setting 1.

AP_TYPE_USER_SETTING_2

Network setting 2.

AP_TYPE_USER_SETTING_3

Network setting 3.

AP_TYPE_NINTENDO_WIFI_USB_CONNECTOR

Nintendo Wi-Fi USB Connector.

AP_TYPE_NINTENDO_ZONE

Nintendo Zone.

AP_TYPE_WIFI_STATION

Wi-Fi Station

AP_TYPE_FREESPOT

Free hot spot (consolidated into AP_TYPE_HOTSPOT).

AP_TYPE_HOTSPOT

Hot spot.

AP_TYPE_ALL

All of the above (nn::ac::DebugSetApType() only).

The nn::ac::GetConnectingNintendoZoneBeaconSubset() function stores the Nintendo Zone beacon during connection in beacon. The nn::ac::GetConnectingHotspotSubset() function stores the hot spot information during connection in hotspot. Both functions return nn::ac::ResultInvalidLocation if called while connected to other than a supported access point.

Call nn::ac::GetLinkLevel() to get the signal strength of the currently connected access point. The return value or the signal strength returned in the pLinkLevel parameter is an nn::ac::LinkLevel enumerated type, as defined in the following table. If an error occurs while getting the wireless signal strength, this function returns LINK_LEVEL_0. The value of pLinkLevel does not need to be reassigned when an error occurs.

Table 3-23. nn::ac::LinkLevel Enumerated Type

Enumerator

Signal Strength

LINK_LEVEL_0

Signal is extremely weak, or communication has not been established.

LINK_LEVEL_1

Weak signal.

LINK_LEVEL_2

Medium-strength signal.

LINK_LEVEL_3

Strong signal.

There will times when AP_TYPE_WIFI_STATION or AP_TYPE_NINTENDO_ZONE is returned when connecting to a Nintendo 3DS station. This is a result of the distance between the devices.

Either nn::ac::AP_TYPE_WIFI_STATION, nn::ac::AP_TYPE_NINTENDO_ZONE, or nn::ac::AP_TYPE_HOTSPOT will be returned when connecting to a SpotPass hub. Use the bitwise OR from these results to determine whether a connection is being made to a SpotPass hub.

3.3.4. Disconnecting

After an automatic connection has been established, the system may disconnect either by the application explicitly disconnecting or by deteriorating signal conditions or other external factors.

Code 3-48. Disconnecting
nn::Result nn::ac::Close();
nn::Result nn::ac::CloseAsync(nn::os::Event* event);
nn::Result nn::ac::GetCloseResult();
nn::Result nn::ac::RegisterDisconnectEvent(nn::os::Event* event); 

Call the nn::ac::Close() or nn::ac::CloseAsync() function to explicitly close an established connection of an application. The nn::ac::CloseAsync() function is the asynchronous version of nn::ac::Close(). When using the asynchronous version, the event passed to the event parameter enters the signaled state when the disconnection attempt completes, regardless of whether the attempt was successful. After the event is signaled, get the connection result by calling the nn::ac::GetCloseResult() function.

Whether the connection has been disconnected can only be determined by whether the IsSuccess() method on the instance of the returned execution result returns true. If connection is established automatically by a daemon running in the background, the status after connection is lost merely transitions to disconnected (nn::ac::STATUS_IDLE).

An established connection may also be disconnected due to deteriorating signal conditions, leaving the access point’s effective range, or by another external factor. The application is notified of a disconnection by the signaling of the event passed in the event parameter to the nn::ac::RegisterDisconnectEvent() function. After establishing an automatic connection, create a thread that waits to be notified by the event passed to this function to detect the cause of a disconnection. The application does not need to take any special steps before exiting the library because the connection to the access point is guaranteed to be ended at the time the event is signaled.

3.3.5. Finalizing

When you are no longer using the AC library (such as when you are no longer using a network connection), call the nn::ac::Finalize() function to finalize the library.

Code 3-49. Finalizing the AC Library
nn::Result nn::ac::Finalize(); 

If this function is called in an uninitialized state, nn::ac::ResultNotInitialized (not an error) is returned.

3.4. Reliable Local (RDT) Communication

The CTR-SDK SDK provides the RDT library for Reliable local (RDT) communications.

The rdt library is a higher-level library that uses the uds library to provide the following features.

  • Reliable, lossless data transmission over one-to-one connections.
  • Transmitted byte streams are guaranteed to be received in the order sent.
  • Unidirectional transmission from the sender to the receiver (bidirectional communication is possible by opening two one-way channels, but this doubles the memory and other resource requirements).

The RDT library is not designed for the regular sending of limited amounts of data, but rather can be used for sending larger amounts of data.

The RDT library uses the following terminology.

Table 3-24. Terminology Used With the RDT Library

Term

Description

Sender

Class of the system sending data via RDT communication.

Receiver

Class of the system receiving data via RDT communication.

3.4.1. Initializing

The RDT library uses UDS to implement reliable communication. You must establish a UDS network connection before you can use the RDT library. In other words, the host must have called nn::uds::CreateNetwork(), and the client must have called nn::uds::ConnectNetwork().

After establishing a network connection, initialize the Sender and Receiver. Create an instance of either the nn::rdt::Sender or nn::rdt::Receiver class, and call the Initialize() member function. A system may be either the Sender or Receiver depending on the direction of data transfer (regardless of host or client designations), so choose which class to instantiate accordingly.

When establishing two channels for bidirectional communication, specify different ports for a system’s Sender and Receiver instances.

Figure 3-4. Bidirectional Communication

CTR A CTR B port=0 Sender Receiver port=1 Receiver Sender

3.4.1.1. Initializing the Sender

Call the Sender object’s Initialize() member function to initialize it. After it is initialized successfully, the Sender object implicitly creates two nn::uds::EndpointDescriptor instances. The function calls nn::uds::Attach() internally, implicitly allocating a reception buffer. As a result, initialization fails and the function returns nn::uds::ResultOutOfMemory if the specified reception buffer is too small when the UDS library is initialized.

Code 3-50. Initializing the Sender
nn::Result nn::rdt::Sender::Initialize(const nn::rdt::SenderConfig &config); 

For the config parameter, specify an nn::rdt::SenderConfig structure storing all of the initialization parameters. The following sample code defines the structure.

Code 3-51. SenderConfig Structure
struct nn::rdt::SenderConfig
{
    void *pWorkBuf;
    void *pSendBuf;
    u16   sendBufSize;
    u16   nodeId;
    u8    port;
    u8    padding[3];
}; 

For the pWorkBuf member, specify the starting address of the working memory allocated for the Sender object. This working memory must be nn::rdt::Sender::SENDER_WORKBUF_ALIGNMENT (8-byte) aligned, and it must be at least as big as nn::rdt::Sender::SENDER_WORKBUF_SIZE (32,768 bytes).

For the pSendBuf and sendBufSize members, specify the starting address and size of the send buffer.

For the nodeId and port members, specify the node ID and port number for the Receiver system. These must be the same values as those specified for the UDS library.

Do not free the working memory and send buffer passed to Initialize until you call Finalize().

3.4.1.2. Initializing the Receiver

Call the Receiver object’s Initialize() member function to initialize it. After success, the Receiver object implicitly creates two nn::uds::EndpointDescriptor instances internally. The function calls nn::uds::Attach() internally, implicitly allocating a reception buffer. As a result, initialization fails and the function returns nn::uds::ResultOutOfMemory if the specified reception buffer is too small when the UDS library is initialized.

Code 3-52. Initializing the Receiver
nn::Result nn::rdt::Receiver::Initialize(
                        const nn::rdt::ReceiverConfig &config); 

For the config parameter, specify an nn::rdt::ReceiverConfig structure storing all of the initialization parameters. The following sample code defines the structure.

Code 3-53. ReceiverConfig Structure
struct nn::rdt::ReceiverConfig
{
    void *pWorkBuf;
    void *pRecvBuf;
    u16  recvBufSize;
    u16  nodeId;
    u8   port;
    u8   padding[3];
}; 

For the pWorkBuf member, specify the starting address of the working memory allocated for the Receiver object. This working memory must be nn::rdt::Receiver::RECEIVER_WORKBUF_ALIGNMENT (8-byte) aligned, and it must be at least as big as nn::rdt::Receiver::RECEIVER_WORKBUF_SIZE (128 bytes).

For the pRecvBuf and recvBufSize members, specify the starting address and size of the receive buffer.

For the nodeId and port members, specify the node ID and port number for the Sender system. These must be the same values as those specified for the UDS library.

Do not free the working memory and receive buffer passed to Initialize until you call Finalize().

3.4.2. Getting the State

The Sender and Receiver objects both have internal states. Call the GetStatus() member function included in both classes to get their state.

Code 3-54. Getting the State of Sender and Receiver Objects
enum nn::rdt::SenderState nn::rdt::Sender::GetStatus(void) const;
enum nn::rdt::ReceiverState nn::rdt::Receiver::GetStatus(void) const; 

The following figure illustrates typical state changes for Sender and Receiver objects.

Figure 3-5. Sender State Changes

NOT_INITIALIZED CLOSED OPEN_REQUESTED OPENING OPENED CLOSE_REQUESTED Initialize() Finalize() Close() Open() Request to connect to Receiver Check response from Receiver Connection established; data can be transferred Canceled by Cancel() CLOSING Notify Receiver that sending is finished Receiver denies connection

The Sender states are defined by nn::rdt::SenderState. These states are all prepended with SENDER_STATE_.

Table 3-25. Sender States Defined by nn::rdt::SenderState

Definition

Description

SENDER_STATE_NOT_INITIALIZED

Not yet initialized, such as right after instantiation.

SENDER_STATE_CLOSED

Initialized state.

SENDER_STATE_OPEN_REQUESTED

Right after a connection is started.

SENDER_STATE_OPENING

Connection request sent and awaiting response.

SENDER_STATE_OPENED

Connection established, and data can be transferred.

SENDER_STATE_CLOSE_REQUESTED

Right after requesting to close a connection with the Close() function.

SENDER_STATE_CLOSING

Sending is complete, and receiver has been notified.

Figure 3-6. Receiver State Changes

NOT_INITIALIZED CLOSED WAITING OPENED WAITING_FINISHED Initialize() Finalize() Close() Wait() Connection established; data can be transferred Send confirmation in response to notification from Sender that data transfer is complete FINISHED Wait long enough for confirmation to reach Sender Canceled by Cancel()

The Receiver states are defined by nn::rdt::ReceiverState. These states are all prepended with RECEIVER_STATE_.

Table 3-26. Receiver States Defined by nn::rdt::ReceiverState

Definition

Description

RECEIVER_STATE_NOT_INITIALIZED

Not yet initialized, such as right after instantiation.

RECEIVER_STATE_CLOSED

Initialized state.

RECEIVER_STATE_WAITING

Waiting for connection request.

RECEIVER_STATE_OPENED

Connection established, and data can be transferred.

RECEIVER_STATE_WAITING_FINISHED

Received notification that data transmission is ending, and sent confirmation response.

RECEIVER_STATE_FINISHED

Waited long enough for confirmation to reach the Sender.

The Receiver changes to the CLOSED state when a connection is marked as closed, such as by a call to the Cancel() function described later in this document.

The individual RDT functions can only be called in some states. Calling a function when the object is in an incompatible state results in an immediate nn::rdt::ResultUntimelyFunctionCall error.

3.4.3. Processing Communication

Call the Process() member function for both the Sender and Receiver classes to prompt the rdt library to proceed with communication. This function handles data transfer, confirmation, and other RDT communication processes. After initializing the Sender and Receiver objects, we recommend that the application call Process() at least once every frame (at 60 fps, roughly every 16.667 ms).

Code 3-55. Processing Communication
nn::Result nn::rdt::Sender::Process(void);
 nn::Result nn::rdt::Receiver::Process(void); 
Note:

Although Sender::Process() may return nn::uds::ResultBufferIsFull, you can safely ignore this error. If the arrival of data cannot be confirmed, that data is automatically resent.

3.4.4. Opening a Connection

Call the Open() member function of a Sender object for that object to attempt to open a connection with the node ID and port number specified for the Receiver in the initialization parameters. If the Receiver system has called the Wait() member function and is in the WAITING state, it accepts the connection request, both the Sender and Receiver transition to the OPENED state, and the Sender can send data.

If the Receiver is in the CLOSED state when the Sender requests a connection, the Receiver replies with a reset signal to deny the connection request. The Sender then transitions to the CLOSED state after it gets this signal. Note that this might just mean that the Receiver has not called Wait() yet, so make sure that you request a connection again if refused.

Code 3-56. Opening a Connection
nn::Result nn::rdt::Sender::Open(void);
nn::Result nn::rdt::Receiver::Wait(void); 

The following figure shows state transitions for both the Sender and Receiver when requesting a connection.

Figure 3-7. State Changes When Requesting a Connection

CLOSED CLOSED OPEN_REQUESTED WAITING OPENING OPENED OPENED Sender Receiver Open() Wait() Connection request Request accepted CLOSED CLOSED OPEN_REQUESTED OPENING CLOSED Sender Receiver Open() Connection request Request denied Receiver is waiting for a connection request (connection successful) Receiver is not waiting for a connection request (connection unsuccessful)

3.4.5. Transferring Data

Data can be transferred after both the Sender and Receiver are in the OPENED state.

Call nn::rdt::Sender::Send() to send data, and call nn::rdt::Receiver::Receive() to receive it. The Send() function simply writes data to the send buffer within the Sender object, and the Receive() function just reads data from the receive buffer within the Receiver object. Call the previously mentioned Process() function to carry out the raw data sending and receiving.

There are no byte boundaries when sending and receiving data via RDT communication. There is no guarantee that data sent in 1024-byte chunks will arrive in 1024-byte chunks.

Note:

Call the nn::rdt::Sender::Send() function on the sender side to send data in the order in which it was written to the send buffer. Call the nn::rdt::Receiver::Receive() function on the receiver side to receive data.

Code 3-57. Transferring Data
nn::Result nn::rdt::Sender::Send(const void* pBuf, size_t bufSize);
nn::Result nn::rdt::Receiver::Receive(
                        void* pBuf, size_t* pRecvSize, size_t bufSize); 

3.4.6. Canceling

If the user cancels communication or the UDS library detects a network disconnection, call the Cancel() member function for both the Sender and Receiver classes to stop RDT communication. The Cancel() function forces the object into the CLOSED state and sends a reset signal to the connection peer. Upon receiving this reset signal, the connection peer also changes to the CLOSED state, stopping communication.

Code 3-58. Canceling
void nn::rdt::Sender::Cancel(void);
void nn::rdt::Receiver::Cancel(void); 

3.4.7. Closing a Connection

Call nn::rdt::Sender::Close() after the Sender has finished sending data. The Close() function notifies the Sender that there is no more data to send. The Sender object changes to the CLOSE_REQUESTED state after Close() is called. Calling Process() after this transition sends a close request to the Receiver and prompts the Sender to change to the CLOSING state.

The Receiver changes to the WAITING_FINISHED state after it receives the close request. Calling Process() after this transition sends a confirmation response to the Sender, and (after waiting long enough for this to reach the Sender) the Receiver object changes to the FINISHED state. At this point, call Receive() again to make sure there is no data left to receive (in other words, the receive buffer is empty), and then call nn::rdt::Receiver::Close(). The Receiver object now changes to the CLOSED state and the connection is closed.

Do not call Close() to stop communication. Only call the nn::rdt::Sender::Close() function to notify the Sender object that all data has been sent. Only call the nn::rdt::Receiver::Close() function after making sure there is no data left to receive.

Code 3-59. Closing a Connection
nn::Result nn::rdt::Sender::Close(void);
nn::Result nn::rdt::Receiver::Close(void); 

The following figure shows state transitions for both the Sender and Receiver when closing a connection.

Figure 3-8. State Changes When Closing a Connection

OPENED OPENED WAITING_FINISHED CLOSING CLOSED CLOSED FINISHED Sender Receiver Close() Close() Close instruction Confirmation response Wait a fixed length of time

3.4.8. Finalizing

Call the Finalize() member function for both the Sender and Receiver classes to finalize the objects after you finish using the RDT library. Finalize() frees the nn::uds::EndpointDescriptor objects implicitly allocated by the instances created on initialization, removing the need to allocate the memory that was passed at initialization.

Code 3-60. Finalization
void nn::rdt::Sender::Finalize(void);
void nn::rdt::Receiver::Finalize(void);  

CONFIDENTIAL