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 explains 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.
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 and Nintendo DSi.
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 and Nintendo DSi.
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.
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.
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. |
Intermediate State Name |
Description |
---|---|
Initializing |
Initializing the UDS library. When it is successful, this state transitions to |
Finalizing |
Shutting down the UDS library. After it is finished, this state transitions to |
Creating Network |
Creating a network based on the settings. After it is finished, this state transitions to |
Connecting Network |
Connecting to an existing network as a client or spectator node. When it is successful, this state transitions to |
Destroying Network |
Disconnects all client and spectator nodes from the network and then destroys the network. After it is finished, this state transitions to |
Disconnecting Network |
Leaving the current network connection. After it is finished, this state transitions to 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.
State Name |
Definition Name |
---|---|
None |
|
Disconnected |
|
Host |
|
Client |
|
Spectator |
|
Initializing |
(The application never gets this state.) |
Finalizing |
(The application never gets this state.) |
Creating network |
|
Connecting to network |
|
Destroying the network |
|
Disconnecting from the network |
|
3.1.2. Initializing
Call nn::uds::Initialize
to initialize 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 explain 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.
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.
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.
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.
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
.
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.
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.
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.
Setting Value |
Description |
---|---|
|
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 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).
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.
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.
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 next sections explain 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
.
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
.
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.
Use nn::uds::DestroyEndpoint
to destroy your endpoint when you no longer need to conduct communication through it, such as when disconnecting from the network. 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.
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 asUDS_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 usingnn::uds::SetMaxSendDelay
.
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, in order 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 argument 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.
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 need to get the node ID of the sender. This section explains the nn::uds::ReceiveFrom()
function.
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.
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 explains 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.
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.
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 destroy a network. Call nn::uds::DestroyNetwork
to destroy the network.
nn::Result nn::uds::DestroyNetwork(void);
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.
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.
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 the wireless switch, 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 terminated using nn::uds::Finalize
, and then reinitiated. If the system was disconnected by being changed to wireless-disabled mode, communication must be reinitialized after it changes back to wireless-enabled mode.
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.
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.
Reserved Node IDs |
Description |
---|---|
0 |
A nonexistent node. Used for internal library processing. |
1 |
This is a fixed value assigned to the host. |
|
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.
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.
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.
Value |
Disconnection Reason |
---|---|
|
No communication is established. |
|
Not disconnected. Connection is maintained. |
|
Disconnected due to the local host’s own operation. |
|
Disconnected due to a request from the 3DS system, such as for transition to Sleep Mode or wireless-disabled mode. |
|
Disconnected due to an operation on the host system. |
|
Connection could not be maintained due to deteriorating signal conditions. |
|
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- 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").
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.
Value |
Description |
---|---|
|
Signal strength is very low, or communication has not been established. |
|
Signal strength is low. |
|
Signal strength is medium. |
|
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.
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
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. 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).
Nintendo 3DS Download Play cannot be performed between a development CTR system (debugger or development system) and a retail CTR system.
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).
When 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.
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.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
The wireless communication module is secured by another wireless process. Terminate infrastructure communication or other communications. |
|
Out-of-range value specified in argument. |
|
Invalid pointer specified in argument. |
|
Invalid handle specified in argument. |
|
Unrecoverable error in library. |
|
Either the client program having the specified index does not exist, or the server card has been removed. |
|
The size of the client program exceeds the maximum size that can be distributed. |
|
The regions of the host and client programs differ. |
|
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.
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.
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.
Definition |
Description |
---|---|
|
Not initialized yet. |
|
Server initialization has completed. |
|
The server is accepting requests to join a session. Clients can join only in this state. |
|
The server is distributing. |
|
The server has finished distributing. |
|
The server is rebooting the clients after distributing. Confirm that all clients have been disconnected before you finalize 3DS Download Play. |
|
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 |
The following table shows the return values that might be returned by the GetEventDesc
and GetState()
functions, and their causes.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
No data to be obtained. |
|
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
and CloseSessions()
functions to start and stop 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.
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
and CloseSessions
, and their causes.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
The function called an invalid state. |
|
Out-of-range value specified in argument. |
|
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.
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 clients
. 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
and GetClientState()
functions 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 to 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
.
Definition |
Description |
---|---|
|
Accepted by the server to participate in a session and waiting to be invited by the host to participate in a session. |
|
The client is connected to the network and waiting for the server to accept participation in the session. |
|
Joined in the session. |
|
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. |
|
Downloading has completed. |
The following table shows the return values that might be returned by the GetConnectingClients
, GetClientInfo
, and GetClientState()
functions, and their causes.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
The function called an invalid state. |
|
Invalid pointer specified in argument. |
|
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.
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
and DisconnectClient
, and their causes.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
The function called an invalid state. |
|
Not connected to a client with the specified node ID. |
|
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.
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.
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.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
The function was called in an inappropriate state, or no client exists to be distributed. |
|
Wireless communication unavailable (Sleep Mode or wireless-disabled mode). |
|
Failed to access media. The server card may have been removed. |
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 RebootAllClient()
function to reboot all clients that have joined the session.
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 .
Return Value |
Cause |
---|---|
|
The process was successful. |
|
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.
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.
Return Value |
Cause |
---|---|
|
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.
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.
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 enable forcible import with 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.
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 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.
When revising a title that uses a fake client with a patch 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).
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.
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.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
The wireless communication module is secured by another wireless process. Terminate infrastructure communication or other communications. |
|
Out-of-range value specified in argument. |
|
Invalid pointer specified in argument. |
|
Invalid handle specified in argument. |
|
Unrecoverable error in library. |
|
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.
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.
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.
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.
Definition |
Description |
---|---|
|
Not initialized yet. |
|
Initialization completed, or disconnected from the server. |
|
Scanning for servers. |
|
Waiting to connect to the network. |
|
Waiting to be invited to join the session. |
|
Joined in the session. |
|
Distribution has started and downloading is in progress. Fake clients do not receive data from the server. |
|
Downloading has completed. |
|
Fake clients do not transition to this state. |
|
Reboot request received from the server. |
|
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 |
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 initiated Download Play sessions.
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
.
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.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
Function called in invalid state (in some state other than |
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
.
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 ending 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.
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.
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 information at that point in time. 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 a number of 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.
Before joining the session you must call StopScan
and stop scanning for servers.
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.
Return Value |
Cause |
---|---|
|
The process was successful. |
|
Function called in invalid state (in some state other than |
|
The title as specified by |
|
Wireless communication unavailable (Sleep Mode or wireless-disabled mode). |
|
The server as specified by |
|
Cannot connect to the server because the maximum number of clients is already connected. Cannot connect unless the number of connected clients decreases. |
|
Connection denied because the client program is being distributed by the server. |
|
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. |
|
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.
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.
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.
Be sure to call the Finalize()
function and perform the finalization process when the state transitions to Sleep Mode and when the HOME Menu needs to start up.
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 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 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.
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.
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.
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.
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();
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.
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
.
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.
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.
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 below. Access point types may be added in firmware updates, so note when implementing applications that you may get a state other than those listed.
Enumerator |
Access Point |
---|---|
|
None |
|
Network setting 1. |
|
Network setting 2. |
|
Network setting 3. |
|
Nintendo Wi-Fi USB Connector. |
|
Nintendo Zone. |
|
Wi-Fi Station |
|
Free hot spot (consolidated into |
|
Hot spot. |
|
All of the above. ( |
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.
Enumerator |
Signal Strength |
---|---|
|
Signal is extremely weak, or communication has not been established. |
|
Weak signal. |
|
Medium-strength signal. |
|
Strong signal. |
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.
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 terminating the library, because the connection to the access point is guaranteed to be terminated 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.
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.
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.
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.
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.
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. Once successful, 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.
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.
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.
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.
The Sender
states are defined by nn::rdt::SenderState
. These states are all prepended with SENDER_STATE_
.
Definition |
Description |
---|---|
|
Not yet initialized, such as right after instantiation. |
|
Initialized state. |
|
Right after a connection is started. |
|
Connection request sent and awaiting response. |
|
Connection established, and data can be transferred. |
|
Right after requesting to close a connection with the |
|
Sending is complete, and receiver has been notified. |
The Receiver
states are defined by nn::rdt::ReceiverState
. These states are all prepended with RECEIVER_STATE_
.
Definition |
Description |
---|---|
|
Not yet initialized, such as right after instantiation. |
|
Initialized state. |
|
Waiting for connection request. |
|
Connection established, and data can be transferred. |
|
Received notification that data transmission is ending, and sent confirmation response. |
|
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).
nn::Result nn::rdt::Sender::Process(void); nn::Result nn::rdt::Receiver::Process(void);
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.
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.
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 actual 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.
Call the nn::rdt::Sender::Send()
function on the sender side to send data in the order written to the send buffer. Call the nn::rdt::Receiver::Receive()
function on the receiver side to receive 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.
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.
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.
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.
void nn::rdt::Sender::Finalize(void); void nn::rdt::Receiver::Finalize(void);