Just like in the other Pia protocols, first create and initialize an instance of CloneProtocol.
nn::pia::clone::Initialize();
nn::pia::clone::BeginSetup(); u32 handle = nn::pia::transport::Transport::GetInstance()->CreateProtocol<nn::pia::clone::CloneProtocol>(); nn::pia::clone::CloneProtocol* pCloneProtocol = nn::pia::transport::Transport::GetInstance()->GetProtocol<nn::pia::clone::CloneProtocol>(handle); nn::pia::clone::CloneProtocol::Setting setting; // Specify all your settings in setting. (For more information, see the API reference.) pCloneProtocol->Initialize(setting); nn::pia::clone::EndSetup(); |
CloneProtocol finalization is also similar to the finalization process for other Pia protocols.
pCloneProtocol->Finalize();
nn::pia::transport::Transport::GetInstance()->DestroyProtocol(handle); nn::pia::clone::Finalize(); |
Instances of clones and clone elements can be created and destroyed at any time (even before and after starting communications). Do not destroy instances of these objects while they are still in CloneProtocol, however. This can cause erroneous memory access issues.
Add clone elements to clones before using them. Add clone elements or remove them from clones before adding the clones to CloneProtocol.
nn::pia::clone::SendClone sendClone;
nn::pia::clone::ReceiveClone receiveClone; nn::pia::clone::UnreliableCloneElement<u32> elementForSend; nn::pia::clone::UnreliableCloneElement<u32> elementForReceive; sendClone.RegisterElement(&elementForSend, 0x0001); receiveClone.RegisterElement(&elementForReceive, 0x0001); |
Add clones to CloneProtocol before using them. You can add or remove clones from CloneProtocol at any time between the CloneProtocol::Initialize() and the CloneProtocol::Finalize() function calls.
If you remove a clone from CloneProtocol during communications using that instance of CloneProtocol, communications may not stop immediately because CloneProtocol takes measures to ensure consistency of data on the sending and receiving stations. Use the CloneBase::IsRegisteredWithProtocol() function to determine when the clone has finished being removed from CloneProtocol. This function returns false after the clone has been removed. Wait for the clone to be removed before destroying the current instance or adding another instance of that clone.
pCloneProtocol->RegisterSendClone(&sendClone, 0x00000001);
pCloneProtocol->RegisterReceiveClone(&receiveClone, nn::pia::STATION_INDEX_1, 0x00000001); pCloneProtocol->UnregisterSendClone(&sendClone); pCloneProtocol->UnregisterReceiveClone(&receiveClone); |
After joining a session using the PiaSession module, call the CloneProtocol::Start() function to start CloneProtocol communications. When you call the Start() function, CloneProtocol transitions to the STATE_START state. This synchronizes the clock time between any stations that are already communicating using CloneProtocol. After synchronization is complete, you can call the CloneProtocol::GetClock() function to get the clock time. After all the stations have been notified that CloneProtocol communications have started, CloneProtocol transitions to the STATE_ACTIVE state. If any clones have been added to CloneProtocol, the clone connection processes start at this time.
Call the CloneProtocol::Stop() function to stop CloneProtocol communications. When you call the Stop() function, CloneProtocol transitions to the STATE_STOP state. After any remaining clone element data has been sent and the clones have been disconnected, the stations are notified that CloneProtocol has been stopped. CloneProtocol transitions to the STATE_INACTIVE state at this time.
Whenever possible, only remove stations from the session while CloneProtocol is in the STATE_INACTIVE state. If you remove a station from the session while CloneProtocol is in another state, extra communications have to be sent to try to maintain the consistency of data on all the remaining stations. (For more information, see PiaClone - Guaranteeing That Data Arrives Safely in 8.3. PiaClone Detailed Specifications.) We recommend removing stations from the session according to the normal sequence: stop CloneProtocol communications, confirm that CloneProtocol is in the STATE_INACTIVE state, and then remove stations from the session.
Figure 8-2 shows CloneProtocol state transitions.
You must call the CloneProtocol::UpdateClock() function once per game frame while CloneProtocol is in the STATE_START, STATE_ACTIVE, or STATE_STOP state. Note that if you do not call the CloneProtocol::UpdateClock() and the common::Scheduler::Dispatch() functions regularly during these times, other stations may enter a state where they are stuck waiting for packets to arrive.
The CloneBase::IsRegisteredWithProtocol() function returns true for any clone it is called on while that clone is registered with an instance of the CloneProtocol class. The CloneBase::IsActive() function also returns true for any instances of CloneProtocol it is called on while those instances are actively communicating.
Use the IsConnected() function on each type of clone to determine whether it is connected to a partner clone with which it can share data. (For more information, see the API reference.) Clones are guaranteed to be able to send and receive values to any partner clone to which they are connected. However, clones may also sometimes receive data from partner clones to which they are not currently connected depending on when the data was actually sent.
The CloneElementBase::IsRegisteredWithCloneBase() function returns true for any clone element it is called on while that clone element is registered with a clone. Also, the CloneElementBase::IsRegisteredWithProtocol() function returns true for any clone it is called on while that clone is registered with an instance of the CloneProtocol class.
The CloneElementBase::IsReadyToSetValue() function returns true if values can be set to the clone element registered with the clone. You can call the SetValue() function to set the values in this state. Note that some values may not be sent or received correctly if the buffer overflows. Make sure to manage the buffer appropriately to prevent this.
The IsValidValue() function returns true if valid values can be received from the clone element. Use the GetValue() function to get values from the clone element while it is in this state. For EventCloneElement, the HandleNext() function returns a valid address when a new event arrives.
You can specify the serialization method used to pack values set to clone elements into packets and send them. Use the SerializePolicy template parameter in each clone element template class to specify the serialization method.
There are three SerializePolicy classes for specifying the serialization method that differ in how they order bytes. These classes are template classes that take the types to serialize as template parameters.
HostByteOrderSerializePolicy
Serialize data using the byte order on the host device as is. This method is fast because the byte order does not need to be converted but cannot be used to send data between devices that employ different native byte orders. This method is used by default if you do not explicitly specify a SerializePolicy.
NetworkByteOrderSerializePolicy
Convert to network byte order (big-endian) to serialize data regardless of the byte order on the host device. This method converts the byte order during serialization and deserialization on devices that use little-endian byte order.
LittleEndianSerializePolicy
Convert to little-endian byte order to serialize data regardless of the byte order on the host device. Nintendo platforms currently employ big-endian byte order on consoles and little-endian byte order on handheld devices. If you try to send data between a console and a handheld device using big-endian byte order, the less powerful handheld device has to convert the byte order to be able to use the data. Use LittleEndianSerializePolicy to send data in little-endian byte order and let the console, which has more processing power, take care of converting byte order.
SerializePolicy classes that include a byte order conversion inherit from the ReverseSerializePolicy class. To convert the byte order of structures and other unique objects defined in your application, however, you must also implement an appropriately customized version of the ReverseSerializePolicy<Type>::Reverse() function in your application.
For example:
struct Val
{ u32 a; f32 b; }; |
To convert the byte order shown above…
template<> void ReverseSerializePolicy<Val>::Reverse(Val* pTo, const Val* cpFrom)
{ ReverseValue(&pTo->a, &cpFrom->a); ReverseValue(&pTo->b, &cpFrom->b); } |
…code this kind of implementation. Note that the pTo and cpFrom arguments can be incorrectly aligned.
You can also create your own custom SerializePolicy for your application. For more information, see Defining a Custom SerializePolicy Class in Your Application in 8.3. PiaClone Detailed Specifications.
The CloneProtocol class maintains a synchronized clock time between stations. This clock time is used to determine in what order values were set to clone elements, and in what order stations try to get the AtomicSharingClone lock, and so on.
Your application must call the CloneProtocol::UpdateClock() function once per game frame while using CloneProtocol for communication between stations. Calling the UpdateClock() function updates the current clock time by one game frame. Never update the clock time at any time other than the call to the UpdateClock() function once per game frame.
You can choose from one of two clock types for clock time management. You can specify the clock type when you call the CloneProtocol::Initialize() function. Choose the clock type that is best suited to your application. The two clock types differ primarily in how they advance the clock time each time the UpdateClock() function is called. The clock types are described in more detail below.
Real-time Clock (RTC)
Advances the current clock time by the amount of real time that has elapsed since the last UpdateClock() function call. Set amount that the time advances for each second in CloneProtocol::Setting::clockPerSec.
The clock time is synchronized between the stations when CloneProtocol communication starts. After that, the clock time advances individually on each station based on that station's RTC. As a result, you can assume that the clock time continues to remain synchronized between the stations (ignoring small differences in individual RTC calculations). You do not have to synchronize the clock time between the stations again. However, if one of the stations is hit with an abnormally large processor load and skips the UpdateClock() function call, or the stations get out of sync for another reason, the amount of time that each station calculates has elapsed since the first UpdateClock() function call may be different. In particular, if the clock time precision is set approximately equal to the average interval at which the UpdateClock() function is called, the clock time may not change when the UpdateClock() function is called.
This type of clock is best suited to applications in which the amount of data that must be updated in each game frame can change depending on how much clock time has elapsed, or applications in which slight differences in station synchronization will not make the application unplayable. The RTC clock is the default CloneProtocol clock.
Frame Clock
Advances the clock time by a fixed amount each time the UpdateClock() function is called. Set the amount that the time advances for each frame in CloneProtocol::Setting::clockPerFrame.
This works well in applications in which the number of UpdateClock() function calls over a particular time period is guaranteed to be the same for all stations. If a station skips the UpdateClock() function call for whatever reason, however, the clock time is off by that much between all of the stations.
This clock type is best suited to applications in which it is important that stations maintain exactly the same synchronization and the same amount of data be sent in each game frame.
If clock time synchronization is off, you can call the CloneProtocol::RegulateClock() function to resynchronize the clock time between the stations. The CloneProtocol::RegulateClock() function measures how far the clock time is off between the stations and then runs the clock either slower or faster to gradually remove the offset.
The clock time starts from 0 the first time the CloneProtocol::Start() function is called within a session. Then, as stations call the Start() function, the clock time is synchronized with whichever station called the Start() function first.
After all stations end communication by calling the CloneProtocol::Stop() function, the clock time restarts from 0 the next time the Start() function is called. In applications that leave the CloneProtocol clock running for long periods of time, however, the clock time values can actually overflow. When this happens, the CloneProtocol::GetError() function returns ERROR_TYPE_CLOCK_OVERFLOW. In this state, clock time values are likely to be invalid. Call the CloneProtocol::Stop() function to end the session. The clock time only overflows if you leave CloneProtocol communication running for periods of time on the order of several months, or otherwise set an excessively large value by which to advance the clock time each frame.
For more information about specifying the clock type and configuring the clock, see the CloneProtocol::Setting API reference.