7.3. Advanced Features of PiaSync

This section describes how to use the advanced features of PiaSync.

Changing the Delay While Synchronized

Usually, SyncProtocol::Start sets the delay when synchronized communication starts, but the delay can be changed during synchronized communication by using the following procedure. To use this feature, set m_IsUsingChangeDelay to true in the Setting structure that is passed to PiaSync on initialization.

Code 7-7. Enabling the Feature to Change the Delay While Synchronized
// Prepare the settings to pass to Initialize.
nn::pia::sync::SyncProtocol::Setting setting;
setting.m_MaxDelay = 10;
setting.m_TimeoutFrame = 240;
// You can configure up to SyncProtocol::DATA_ID_NUM of the m_DataUnitSize object.
// Set the data size for each data ID. In this example, values are set for the 0th, 1st, and 2nd data IDs.
// You can use the SyncProtocol::GetDataUnitSizeMax() function to find out the largest size this value can be set to.
setting.m_DataUnitSize[0] = 16;
setting.m_DataUnitSize[1] = 32;
setting.m_DataUnitSize[2] = 32;

// Enable use of the feature that allows you to change the delay while synchronized.
setting.m_IsUsingChangeDelay = true;

// Initialize SyncProtocol.
result = pTransport->GetProtocol<nn::pia::sync::SyncProtocol>(m_hSyncProtocol)->Initialize(setting);
PIA_ASSERT(result.IsSuccess());

 

To request a delay change while synchronized, call SyncProtocol::RequestToChangeDelay when you configure the synchronization data to send. The requested delay value must be equal to or less than the value set in SyncProtocol::Setting::m_MaxDelay. If there are delay change requests from multiple stations in the same frame, the largest value is used.

Note:

To use the delay change while synchronized feature, you must call SyncProtocol::RequestToChangeDelay every frame. If you do not want to request a delay change for that frame, set the parameter to 0.

Code 7-8. Requesting a Delay Change While Synchronized
// Specify the data to send.
if (pSyncProtocol->NeedSetData())
{
    // Prepare the data to send, and specify it to the SyncProtocol object.
    for (u32 i = 0; i < 3; ++i)
    {
        // Check whether the local station is supposed to send this data, and if so, set it.
        // Provide a dataBuffer variable of the appropriate size for the data ID.
        if (pSyncProtocol->NeedSetData(i))
        {
            result = pSyncProtocol->SetData(i, &dataBuffer);
            PIA_ASSERT(result.IsSuccess());
        }
    }
 
    // Request a delay change.
    u32 newDelay = 4;
    result = pSyncProtocol->RequestToChangeDelay(newDelay);
    PIA_ASSERT(result.IsSuccess());
}

// Advance one frame.
result = pSyncProtocol->Step();

 

Different procedures are used when changing the delay from a larger value to a smaller value and from a smaller value to a larger value.

Figure 7-4. Changing Delay (From 6 to 2) shows an example of changing the delay from a larger value to a smaller value. Figure 7-5. Changing Delay (From 2 to 6) shows an example of changing it from a smaller value to a larger value.

Figure 7-4. Changing Delay (From 6 to 2)

 

 

Figure 7-5. Changing Delay (From 2 to 6)

  

 

As the figures show, in some frames the synchronization data cannot be configured when changing the delay from a larger value to a smaller value, and in other frames the synchronization data cannot be retrieved when changing the delay from a smaller value to a larger value. You must carefully consider how to handle input data that is used on frames where synchronization data cannot be retrieved. For this reason, when using this feature, run a check to determine whether the current frame is a frame where synchronization data cannot be retrieved.

Code 7-9. Checking Whether Synchronization Data Cannot Be Retrieved on the Current Frame
if (pSyncProtocol->CanGetData())
{
    bool isNoDataFrame;
    result = pSyncProtocol->GetIsNoDataFrame(&isNoDataFrame);
    PIA_ASSERT(result.IsSuccess());
    if (isNoDataFrame)
    {
        // Cannot retrieve synchronization data on this frame.
    }
}

 

 

Ending Synchronization on a Single Station

Normally, calling the SyncProtocol::End on one station also ends synchronization on all other stations. You can use the following technique to end synchronization on only a single station.

Call the SyncProtocol::EndAlone function on the station where you want to end synchronization. This sets a frame where synchronization will end on that station, and notifies the other stations that the station plans to desynchronize. The final frame is set to the last frame with send data already set. Notify the peers of the end frame, and after confirming that all peers have reached the end frame, transition to the STATE_ENDED_ALONE state.

Note:

After a successful call to SyncProtocol::EndAlone, calls to the SyncProtocol::Step function fail during the time the calling station is in the STATE_SYNCHRONIZED state, and the function cannot advance the game frame. Note that as a result, the station that calls this function never reaches the final game frame. Nonetheless, the SyncProtocol::Step function must still be called every game frame to advance internal processes in the library.

Also, while you can still get synchronization data for the frame in which SyncProtocol::EndAlone was called, you cannot get synchronization data after any of the other stations are notified that a station has ended synchronization alone, which can result in unintended desynchronization. Always make sure that you call the SyncProtocol::EndAlone function only after getting any synchronization data that has been sent in that frame.

After this station is in the STATE_ENDED_ALONE state, it transitions to the STATE_NOT_SYNCHRONIZED state after it successfully desynchronizes with the other stations. While in the STATE_ENDED_ALONE or STATE_NOT_SYNCHRONIZED state, the other stations can remain synchronized even if the station leaves the session.

If a new station joins the session while the desynchronized station is in the STATE_ENDED_ALONE state, and another station tries to resynchronize all the stations in the session, they all transition to the STATE_WAITING state, and then the desynchronized station leaves the session. However, the other stations lose synchronization and transition to the NOT_SYNCHRONIZED state.

After the desynchronized station transitions from the STATE_ENDED_ALONE state to the STATE_NOT_SYNCHRONIZED state, it can be resynchronized. If it is not resynchronized or does not leave the session, however, the other stations cannot resynchronize either.

Code 7-10. Ending Synchronization on a Single Station
result = pSyncProtocol->EndAlone();
if (result.IsFailure())
{
    // The EndAlone function call failed.
}

while (true)
{
    if (nn::nex::Scheduler::GetInstance() != NULL)
    {
        nn::nex::Scheduler::GetInstance()->DispatchAll();
    }
    // Call the nn::pia::common::Scheduler::Dispatch() function at regular intervals.
    nn::pia::common::Scheduler::GetInstance()->Dispatch();

    result = pSyncProtocol->Step();
    if (result == nn::pia::ResultTemporaryUnavailable())
    {
        // This Result may be returned after calling the SyncProtocol::EndAlone function, but it does not need to be handled.
    }
 
    if (pSyncProtocol->GetState() == nn::pia::sync::SyncProtocol::STATE_ENDED_ALONE)
    {
        // The single station was successfully desynchronized.
        // The other stations can maintain synchronization even if this station leaves the session.
        break;
    }
    else if (pSyncProtocol->GetState() == nn::pia::sync::SyncProtocol::STATE_ENDING)
    {
        // The desynchronization process started before the single station could be desynchronized.
    }
    else if (pSyncProtocol->GetState() == nn::pia::sync::SyncProtocol::STATE_NOT_SYNCHRONIZED)
    {
        // Other stations were also desynchronized.
        if (pSyncProtocol->GetLastEndReason() == nn::pia::sync::SyncProtocol::END_REASON_END_ALONE)
        {
            // After the single station was desynchronized, the other stations were also desynchronized.
        }
        else
        {
            // The other stations were desynchronized before the single station could be successfully desynchronized.
        }
        break;
    }
 
    Sleep(33);
}