6.19.1. Basic Mesh Network Features

Setup

Setting up a mesh network is the same as when using sessions. For more information, see 6.3. Basic Features - Initializing and Finalizing.

Initialization

After the mesh network has been set up correctly, initialize it. Specify the NetworkFactory derived class that matches the communication method you want to use for the pNetworkFactory member of the Setting structure, which is passed when an instance of the Mesh class is created. The Setting structure and NetworkFactory derived class are used only when an instance of Mesh is initialized. You can delete these objects after Mesh has been initialized. Specify whether to enable the bit rate detection feature in the bitRateCheckMode member variable. The default setting is disabled.

Code 6-45. Initializing the Mesh
// Preprocessing and initialization of the Session module already completed.

result = nn::pia::session::BeginSetup();
if (result.IsFailure())
{
    // Error handling.
    // Will not fail if called at the appropriate time.
}

// Configure the appropriate settings in meshSetting.
nn::pia::session::Mesh::Setting meshSetting;

// Class derived from transport::NetFactory.
nn::pia::inet::NexNetworkFactory nexFactory; // NetworkFactory for Internet communications.
nn::pia::local::UdsNetworkFactory udsFactory; // NetworkFactory for UDS communications.
if (networkType == INTERNET)
{
    meshSetting.pNetworkFactory = &nexFactory;
}
else if (networkType == LOCAL)
{
    meshSetting.pNetworkFactory = &udsFactory;
}

meshSetting.networkTopology = nn::pia::session::NetworkTopology_FullMesh;

// Setting for the bit rate detection feature.
meshSetting.bitRateCheckMode = nn::pia::session::BitRateCheck_Disable;

result = nn::pia::session::Mesh::CreateInstance(meshSetting);
if (result.IsFailure())
{
    // Error handling.
    // Will not fail if called at the appropriate time with the appropriately configured Setting structure passed.
}
result = nn::pia::session::EndSetup();
if (result.IsFailure())
{
    // Error handling.
    // Will not fail if called at the appropriate time.
}
 
/* The instance of the NetworkFactory derived class configured in the Setting structure is never used after initialization. */

Startup

Perform startup before creating and joining a mesh network. In addition, startup is needed for the Facade class for each communication method. For more information about the startup process for the Facade class, see the programming manuals for each module and the API Reference.

The startup process includes configuring the host migration feature, associating a name with the local station, configuring packet encryption settings, and specifying whether to use the packet signature feature.

Configure the required settings in nn:pia::session::Mesh::StartupSetting.

Set whether to enable the host migration feature in StartupSetting.bUsingHostMigration.

Specify nn::pia::transport::Station::PlayerName in StartupSetting.pPlayerName to associate a name with the local station. This name is shared among other stations in the mesh network. The name must be no longer than 16 characters (not including the terminating null character).

Specify an instance of the nn::pia::common::CryptoSetting class in StartupSetting.pCryptoSetting. This setting determines whether the application uses Pia to encrypt all of the packets used to send and receive data. As with the signature key described below, using a unique encryption key for each session provides strong protection against packet parsing and other attacks. A fixed encryption key defined within the application can also be used.

Specify an instance of the nn::pia::common::SignatureSetting class in StartupSetting.signatureSetting. Using key data that is unique to each mesh network results in the strongest signatures.

The following examples are of key data that is unique to each session.

You can also use fixed key data in the application if it does not change with the session.

Warning:

When using Internet communications, make sure to add a signature to the packets. Keep the length of the key data to within 64 bytes. Startup fails if you attempt to configure unsigned packets to use during Internet communications.

You can set the station identification token received from each of the session participants after the process of joining the session has been completed in StartupSetting.pToken. For more information, see 6.14. Advanced Features - Station Identification Token.

To use the bit rate detection feature, set the minimum bit rate required by the application in StartupSetting.uplinkBitRateLowerLimit, and the size of IP packet to send when measuring the bit rate in StartupSetting.bitRateCheckPacketSize. For more information, see 6.20. Advanced Features - Bit Rate Detection Feature.

Code 6-46. Mesh Startup
// Mesh startup.
if (NetworkType == INTERNET)
{
    // Mesh startup for Internet communications, including the Startup member function of the NexFacade class.
    // For more information, see the section about using mesh networks for Internet communications.
}
else if (NetworkType == LOCAL)
{
    // You must be connected to a USD network before calling the Startup function of the LocalFacade class.
    result = nn::pia::local::LocalFacade::GetInstance()->Startup();
    if (result.IsFailure())
    {
        // Error handling.
    }
}

// The setting for the name associated with the local station (the name shown in the game).
nn::pia::transport::Station::PlayerName playerName;
 
// Set a name of up to 16 characters, together with the language code.
// In the sample demo, the name of the Mii is set as an example.
GetLocalPlayerName(&playerName);

// The packet encryption settings.
// For the encryption key, use a data string that is hard to guess.
char sampleCryptoKeyData[] = "session crypto key";
nn::pia::common::CryptoSetting sampleCryptoSetting;
sampleCryptoSetting.m_Mode = nn::pia::common::CryptoSetting::MODE_AES_128;
::std::memcpy(sampleCryptoSetting.m_Key, sampleCryptoKeyData, sampleCryptoSetting.KEY_SIZE);
// In this example, the part of the sampleCryptoKeyData string that is longer than sampleCryptoSetting.KEY_SIZE is ignored.
 
// Settings for the packet signature feature.
// For the key data, use a data string that is hard to guess.
char sampleSignatureKeyData[] = "session signature key";
nn::pia::common::SignatureSetting sampleSignatureSetting(
    nn::pia::common::SignatureSetting::MODE_HMAC_MD5, // Signature algorithm.
    static_cast<const void*>(sampleSignatureKeyData), // Address of the key data.
    sizeof(sampleSignatureKeyData) // Size of the key data.
);

// The startup setting.
nn::pia::session::Mesh::StartupSetting startupSetting;
startupSetting.bUsingHostMigration = true; // The host migration setting.
startupSetting.pPlayerName = &playerName; // The player name to use for communications.
startupSetting.pCryptoSetting = &sampleCryptoSetting; // The packet encryption setting.
startupSetting.signatureSetting = sampleSignatureSetting; // The packet signature feature setting.
startupSetting.pToken = &token; // The identification token.
startupSetting.uplinkBitRateLowerLimit = 200 * 1000; // The bit rate required by the application. In bits per second.
startupSetting.bitRateCheckPacketSize = 1024; // IP packet size to use when measuring the bit rate. The value is in bytes.

// Startup of the mesh instance.
result = nn::pia::session::Mesh::GetInstance()->Startup(startupSetting);
if (result.IsFailure())
{
    // Error handling.
}
 
/* If mesh startup has succeeded, you can proceed with the processes to create or join a mesh. */

Creating the Mesh Network

This process creates a new mesh network with the local station as the host. Creating a mesh is an asynchronous process. Call the CreateMeshAsync function to start the asynchronous mesh creation process. Call the IsCompletedCreateMesh function to determine whether the mesh creation process is complete.

Code 6-47. Creating a Mesh
// Creating a mesh. (Starting the asynchronous process.)
result = nn::pia::session::Mesh::GetInstance()->CreateMeshAsync();
if (result.IsFailure())
{
    // Error handling.
}

// If the asynchronous process has started successfully, you must call the dispatch function periodically and wait for the asynchronous process to proceed.
while (nn::pia::session::Mesh::GetInstance()->IsCompletedCreateMesh() == false)
{
    if (NetworkType == INTERNET)
    {
        // If you are using Internet communications, you must call the NEX scheduler dispatch function for keep-alive communication with the server.
        nn::nex::Scheduler::GetInstance()->DispatchAll();
    }
    nn::pia::common::Scheduler::GetInstance()->Dispatch();
 
    // We recommend periodically checking the status of the network connection even while waiting on an asynchronous process.
    result = nn::pia::transport::Transport::GetInstance()->CheckConnectionError();
    if (result.IsFailure())
    {
        // Error handling.
    }
}
// Check the result of the asynchronous process.
if (nn::pia::session::Mesh::GetInstance()->GetCreateMeshResult().IsSuccess() == false)
{
    // Error handling.
}
 
/* If successful, the mesh becomes receptive to other stations joining as clients. */

Searching for Mesh Networks

To find a mesh network to join, use the NEX matchmaking API for Internet communications. Use pia::local::LocalNetwork for local communications. For more information, see the programming manual for each module and the API Reference.

Joining a Mesh Network

This section describes the process for when the local station joins a mesh network as a client. Joining a mesh network is an asynchronous process. Call the JoinMeshAsync function to start the asynchronous mesh join process. Call the IsCompletedJoinMesh function to determine whether the mesh join process is complete.

When the local station has successfully connected to all stations in the mesh, GetJoinMeshResult returns a Result object for which the IsSuccess member function returns true, and the station can start communicating with the other stations. Call the CancelJoinMeshAsync function to cancel this asynchronous process at any time. Wait for asynchronous processing to complete, and then perform the mesh network cleanup.

Code 6-48. Joining a Mesh
// Joining a mesh.
// Get the mesh host's StationConnectionInfo.
nn::pia::transport::StationConnectionInfo hostStationConnectionInfo;
if (NetworkType == INTERNET)
{
    // If you are using Internet communications, matchmaking is ended in order to join the mesh.
    // Must be able to join a valid matchmaking session.
    // The mesh host is the host of the matchmaking session.
    nn::nex::qList<nn::nex::StationURL> hostURLs;
    // Query the matchmaking server to get the host's StationURL.
    // (Details omitted.)
    result = nn::pia::inet::NexFacade::ConvertNexStationURLToStationConnectionInfo(
        hostURLs,
        &hostStationConnectionInfo);
    if (result.IsFailure())
    {
        // Error handling.
    }
}
else if (NetworkType == LOCAL)
{
    // If you are using local communication, the station must be connected to a local network in order to join a mesh.
    // The mesh host is always the host of the local network (the UDS master)
    result = nn::pia::local::LocalFacade::GetInstance()->GetHostStationConnectionInfo(&hostStationConnectionInfo);
    if (result.IsFailure())
    {
        // Error handling.
    }
}
// Finish getting the mesh host's StationConnectionInfo.
 
// Used the obtained StationConnectionInfo to join the mesh (to start the asynchronous process).
result = nn::pia::session::Mesh::GetInstance()->JoinMeshAsync(hostStationConnectionInfo);
if (result.IsFailure())
{
    // Error handling.
}
 
// If the asynchronous process has started successfully, you must call the dispatch function periodically and wait for the asynchronous process to proceed.
while (nn::pia::session::Mesh::GetInstance()->IsCompletedJoinMesh() == false)
{
    if (NetworkType == INTERNET)
    {
        // If you are using Internet communications, you must call the NEX scheduler dispatch function for keep-alive communication with the server.
        nn::nex::Scheduler::GetInstance()->DispatchAll();
    }
    nn::pia::common::Scheduler::GetInstance()->Dispatch();

    // We recommend periodically checking the status of the network connection even while waiting on an asynchronous process.
    result = nn::pia::transport::Transport::GetInstance()->CheckConnectionError();
    if (result.IsFailure())
    {
         // Error handling.
    }
}
// Check the result of the asynchronous process.
if (nn::pia::session::Mesh::GetInstance()->GetJoinMeshResult().IsSuccess() == false)
{
    // Error handling.
}
 
/* If the asynchronous process succeeds, communication with other stations is now possible. */

Checking Participation Status in a Mesh Network

Using PiaTransport enables you to send and receive data between stations while they are participating in a mesh network. For more information about the sending and receiving methods, see the Programming Manual and the API Reference for PiaTransport.

There are several ways to check the status (such as the connection state) within a mesh network.

Checking the State of a Mesh Network

To determine whether you are connected to a station, check the state of the Mesh class instance. Sometimes the local station can get disconnected from the mesh for external reasons, such as when the mesh host destroys the session. Implement the following cleanup process for when the mesh network gets disconnected.

Code 6-49. Checking the State of the Mesh
// Get the current state of the mesh and check the connection status.
nn::Result result = nn::pia::session::Mesh::GetInstance()->CheckConnectionError();
if (result == nn::ResultMeshConnectionIsLost())
{
    // The status is disconnected.
    // You cannot communicate with other stations in this state, so clean up the mesh.
}

Checking the Network Connection Status

A station can check the current state of its own network connection by querying the instance of the transport::Transport class. The network connection may end up in an abnormal state because of external causes such as a communication failure. Implement the following cleanup process for when the network enters an abnormal state.

Code 6-50. Checking the Network Connection Status
// Query the Transport class and check the connection status of the network.
nn::Result result = nn::pia::transport::Transport::GetInstance()->CheckConnectionError();
if (result.IsFailure())
{
    // The network is in an abnormal connection state.
    // You cannot communicate with other stations in this state, so clean up the mesh.
}

Checking Stations Participating in the Mesh Network

By querying the instance of the Mesh class, you can get information such as the number of stations within the session and whether a particular StationId is within the session.

Code 6-51. Checking Stations Participating in the Mesh Network
// The number of valid stations in the mesh.
u16 stationNum = nn::pia::session::Mesh::GetInstance()->GetStationNum();
 
// The StationId of the host of the mesh.
nn::pia::StationId hostStationId = nn::pia::session::Mesh::GetInstance()->GetHostStationId();
// The StationId of the local station.
nn::pia::StationId localStationId = nn::pia::session::Mesh::GetInstance()->GetLocalStationId();
 
// Determine whether a particular StationId exists in the mesh.
nn::pia::StationId targetStationId; // The target StationId exists.
bool isValid = nn::pia::session::Mesh::GetInstance()->CheckStationIdIsValid(targetStationId);

Handling Mesh State Change Events

You can add callback functions to the instance of the Mesh class that are called when the state of the mesh network changes. State change event notifications are issued in situations such as when a station joins or leaves the mesh network.

Warning:

Do not include any processes that would block for a long time within the callback function. This can negatively affect system processes.

The processes that connect a station that is joining a mesh with stations that are already in the mesh occur in parallel with the mesh join process. As a result, the EVENT_JOIN event (which indicates that a new station has joined the mesh) may occur at a different time for each of the stations that have already joined the mesh. Also, depending on how far the connection process has progressed, the EVENT_JOIN event may not actually occur for all of the stations that are already in the mesh if the joining station fails to join the mesh for any reason (such as if it fails to connect to one of the other stations in the mesh). (In this case, the stations where an EVENT_JOIN occurred experience an EVENT_LEAVE a short time later.)

Code 6-52. Example of a Mesh State Change Event Callback Function
// Example of a callback function for a mesh state-change event.
void sampleMeshEventCallback(nn::pia::session::Mesh::EventType eventType, nn::pia::StationId id)
{
    // The eventType parameter is an enum type representing the details of the change in state event.
    // The id parameter represents the StationId of the station for which the state change occurred.
    switch(eventType)
    {
    case nn::pia::session::Mesh::EVENT_JOIN:
        {
            // The station assigned StationId: stationId joined the mesh.
        }break;
    case nn::pia::session::Mesh::EVENT_LEAVE:
        {
            // The station assigned StationId: id left the mesh.
        }break;
    }
}
Code 6-53. Registering and Unregistering an Event Callback Function
// Register the event callback function.
nn::pia::session::Mesh::GetInstance()->RegisterMeshEventCallback(sampleMeshEventCallback);
 
// From the time it is registered until the time it is unregistered, this callback function is called when the mesh state has changed.
 
// Unregister the event callback function.
nn::pia::session::Mesh::GetInstance()->UnregisterMeshEventCallback();

Leaving a Mesh Network

This process occurs when a mesh client leaves the mesh it was participating in. Leaving a mesh is an asynchronous process. Call the LeaveMeshAsync function to start the asynchronous leave mesh process. Call the IsCompletedLeaveMesh function to determine whether the leave mesh process is complete.

Code 6-54. Leaving a Mesh
// Leave the mesh. (Start the asynchronous process.)
result = nn::pia::session::Mesh::GetInstance()->LeaveMeshAsync();
if (result.IsFailure())
{
    // Error handling.
}
 
// If the asynchronous process has started successfully, you must call the dispatch function periodically and wait for the asynchronous process to proceed.
while (nn::pia::session::Mesh::GetInstance()->IsCompletedLeaveMesh() == false)
{
    if (NetworkType == INTERNET)
    {
        // If you are using Internet communication, you must call the NEX scheduler dispatch function for keep-alive communications with the server.
        nn::nex::Scheduler::GetInstance()->DispatchAll();
    }
    nn::pia::common::Scheduler::GetInstance()->Dispatch();

    // We recommend periodically checking the status of the network connection even while waiting on an asynchronous process.
    result = nn::pia::transport::Transport::GetInstance()->CheckConnectionError();
    if (result.IsFailure())
    {
        // Error handling.
    }
}
 
// Check the result of the asynchronous process.
if (nn::pia::session::Mesh::GetInstance()->GetLeaveMeshResult().IsSuccess() == false)
{
    // Error handling.
    // If the asynchronous process of leaving the mesh has started successfully, the asynchronous process itself will not fail.
}

Freeing a Mesh Network

This process occurs when the mesh host frees the mesh it was participating in (hosting). Freeing a mesh is an asynchronous process. Call the DestroyMeshAsync function to start the asynchronous, mesh-freeing process. Call the IsCompletedDestroyMesh function to determine whether the mesh-freeing process is complete.

Clients that receive a mesh-freed notification from the mesh host transition to a disconnected state.

Code 6-55. Destroying a Mesh
// Destroy the mesh. (Start the asynchronous process.)
result = nn::pia::session::Mesh::GetInstance()->DestroyMeshAsync();
if (result.IsFailure())
{
    // Error handling.
}
// If the asynchronous process has started successfully, you must call the dispatch function periodically and wait for the asynchronous process to proceed.
while (nn::pia::session::Mesh::GetInstance()->IsCompletedDestroyMesh() == false)
{
    if (NetworkType == INTERNET)
    {
        // If you are using Internet communications, you must call the NEX scheduler dispatch function for keep-alive communication with the server.
        nn::nex::Scheduler::GetInstance()->DispatchAll();
    }
    nn::pia::common::Scheduler::GetInstance()->Dispatch();
 
    // We recommend periodically checking the status of the network connection even while waiting on an asynchronous process.
    result = nn::pia::transport::Transport::GetInstance()->CheckConnectionError();
    if (result.IsFailure())
    {
        // Error handling.
    }
}
 
// Check the result of the asynchronous process.
if (nn::pia::session::Mesh::GetInstance()->GetDestroyMeshResult().IsSuccess() == false)
{
    // Error handling.
    // If the asynchronous process of destroying the mesh has started successfully, the asynchronous process itself will not fail.
}

 

Cleanup

After becoming disconnected from a session, perform cleanup to restore the state prior to starting a session. After cleanup, you can go through startup again to create and join new mesh networks. Even when communication completely fails, proceed to the finalization process noted below after cleanup is done.

Code 6-56. Cleaning Up the Mesh
// Clean up the mesh.
nn::pia::session::Mesh::GetInstance()->Cleanup();
 
// After this step, perform the processing that is necessary for the communication method.
if (NetworkType == INTERNET)
{
    // In the case of Internet communication, the necessary processes include leaving the matchmaking mesh and calling the NexFacade class Cleanup function.
    // (Omitted.)
}
else if (NetworkType == LOCAL)
{
    // In the case of UDS communication, the necessary processes include calling the LocalFacade class Cleanup function and leaving the UDS network.
    // (Omitted.)
}

Finalization

When you have finished using the mesh network, finalize it.

Code 6-57. Finalizing a Mesh
// The finalization process never fails.
nn::pia::session::Mesh::DestroyInstance();

Post-Processing

Mesh network post-processing is the same as when using sessions. For more information, see 6.3. Basic Features - Initializing and Finalizing.

 

Getting Key Data for Packet Signatures From the NEX Matchmaking Server

 

When NEX is used with Internet communication, you can get unique key data for each matchmaking session from the NEX matchmaking server. This key data can be used as the key for signed packets.

Warning:

You can also use an application-specific data string for the key data, but for security reasons, use key data obtained from the NEX matchmaking server.

Code 6-58. Getting Key Data for the Packet Signature Feature From the NEX Matchmaking Server
nn::nex::MatchmakeExtensionClient* pMatchmakeClient;
// Creating the MatchmakeExtensionClient instance, logging in to the server,
// handling function call errors, and other such processes have been omitted here.
 
// Pointer to the MatchmakeSession instance for storing information about the matchmaking session that was joined.
nn::nex::MatchmakeSession* pOutMatchmakeSession = common::NewObj<nn::nex::MatchmakeSession>();
 
// If creating a matchmaking session.
if (COMMAND == CREATE_MATCHMAKE_SESSION)
{
    // Configure the conditions for creating a matchmaking session. Omitted here.
    nn::nex::CreateMatchmakeSessionParam createMatchmakeSessionParam;
 
    pMatchmakeClient->CreateMatchmakeSession(
        &protocolCallContext,
        createMatchmakeSessionParam,
        pOutMatchmakeSession);
}
// If joining a matchmaking setting.
else if (COMMAND == JOIN_MATCHMAKE_SESSION)
{
    // Configure the conditions for joining a matchmaking session. Omitted here.
    nn::nex::JoinMatchmakeSessionParam* pJoinMatchmakeSessionParam = common::NewObj<nn::nex::JoinMatchmakeSessionParam>();
 
    pMatchmakeClient->JoinMatchmakeSession(
        &protocolCallContext,
        *(pJoinMatchmakeSessionParam),
        pOutMatchmakeSession);
}
// If using AutoMatch.
else if (COMMAND == AUTO_MATCHMAKE)
{
    // Configure the conditions for auto-matchmaking. Omitted here.
    nn::nex::AutoMatchmakeParam autoMatchmakeParam;
 
    pMatchMakingClient->AutoMatchmake(
        &m_ProtocolCallContext,
        autoMatchmakeParam,
        pOutMatchmakeSession);
}
 
// Wait for the asynchronous process to complete.
 
// Set the name associated with the local station (the name that appears in the game).
nn::pia::transport::Station::PlayerName playerName;
GetLocalPlayerName(&playerName);
 
// Convert to key data format for the Pia signature feature.
const u32 SIGNATURE_KEY_DATA_SIZE = 32;
nn::pia::common::SignatureSettingWithKeyBuffer<SIGNATURE_KEY_DATA_SIZE> sampleSignatureSetting;
nn::Result result = nn::pia::inet::NexFacade::ConvertNexSignatureKeyToSignatureSetting(
    pOutMatchmakeSession->GetSessionKey(), // For the conversion source, specify the key data obtained from the NEX server.
    &sampleSignatureSetting); // Stores the key data resulting from the conversion process.

if (result.IsFailure())
{
    // ErrorHandling
    // Will not fail if the conversion source data is properly passed and there are no problems with where the converted key data is to be stored.
}
 
// Use as an encryption key for the packet encryption feature.
nn::pia::common::CryptoSetting sampleCryptoSetting;
sampleCryptoSetting.m_Mode = nn::pia::common::CryptoSetting::MODE_AES_128;
::std::memcpy(sampleCryptoSetting.m_Key, sampleSignatureSetting.GetKeyData(), sampleCryptoSetting.KEY_SIZE);
 
// Set at session startup.
nn::pia::session::Mesh::StartupSetting startupSetting;
startupSetting.bUsingHostMigration = true; // Host migration setting.
startupSetting.pPlayerName = &playerName; // Player name to use for communication.
startupSetting.pCryptoSetting = &sampleCryptoSetting; // Packet encryption setting.
startupSetting.signatureSetting = sampleSignatureSetting; // Packet signature feature setting.
 
// Mesh instance startup.
result = nn::pia::session::Mesh::GetInstance()->Startup(startupSetting);
if (result.IsFailure())
{
    // ErrorHandling
}