12.3. NEX NetZ から PiaSession に移行するには

この章では NetZ を利用して開発を進めていたアプリケーションが PiaSession に移行する場合の参考情報を説明します。

マッチメイク

NEX マッチメイク API 呼び出し部分は、PiaSession のセッション API に置き換える必要があります。例えば、nex::MatchmakeExtensionClient::AutoMatchmake() は pia::session::Session::JoinRandomSessionAsync() に置き換えることになります。

データ送信

NetZ の DirectStream に対応する Pia の API は、transport::UnreliableProtocol と transport::ReliableProtocol になります。送信 API のエラーハンドリングには次の違いがあります。

Pia は使用するメモリやバッファ数の上限を初期化時に確定させておく設計となっています。そのため、送信が短時間で多数回実行されるなどして、Pia 内部のバッファが枯渇すると、送信 API は ResultBufferIsFull を返すようになります。このバッファ枯渇は一時的なものです。仮にバッファが枯渇した場合でも、 pia::common::Scheduler::Dispatch() を定期的に呼び出し続けていれば、バッファの要素は順次処理されていきますので、いずれバッファに空きは生じます。

アンリライアブルな送信(transport::UnreliableProtocol の送信)ならば、特に何も対処せず ResultBufferIsFull を無視してもパケロスと同様の扱いとなり、問題ありません。

リライブルな送信(transport::ReliableProtocol の送信)であれば、ResultBufferIsFull を返してきた場合、送信に失敗したデータはアプリ側で保持しておき、次回の送信タイミングで再送信するといった対応が必要となります。

 

コード 12-1. Piaでのデータ送信
nn::pia::Result r = pReliableProtocol->Send(destStationId, pData, dataSize);
if (r.IsSuccess())
{
    // 送信に成功したので、ポインタを次のデータへ進めます。
    pData = getNextData(pData);
}
else if (r == nn::pia::ResultBufferIsFull())
{
    // 送信バッファが捌けるペースを超えて Send() を実行しようとすると、
    // ReliableProtocol::Send() に失敗します。この理由で Send() に失敗したデータは、
    // アプリ側で責任を持って再送しなければなりません。そのため、この場合は pData を変更せず、
    // 次の機会で再送を試みます。
}
else
{
    PIA_TRACE_RESULT(r);
    PIA_ASSERTMSG(false, "Programming error.");
}

データ受信

NetZ の DirectStream ではアプリケーションが GetReceivedData() を呼び出しポーリング方式で受信していました。Pia でもポーリング方式でデータを受信します。例えば、transport::ReliableProtocol や transport::UnreliableProtocol であれば Receive() 関数が用意されていますので、これを定期的に呼び出すことになります。受信データ未到着のためにデータを取得できなかった場合は、 ResultNoData が返されますので適切にハンドリングする必要があります。

 

コード 12-2. Piaでのデータ受信
// 到着したデータの受信。
nn::pia::Result r = pUnreliableProtocol->Receive(&srcId, reinterpret_cast<uint8_t*>(buf), sizeof(buf), &receivedSize);
if (r.IsSuccess())
{
    // 無事にデータを受信できたので、データの送り主や、データの内容をプリントします。
    PIA_CACHED_PRINTF("Received from 0x%016llX. Data(%d bytes): [%s]\n", srcId, receivedSize, buf);
}
else if (r == nn::pia::ResultNoData())
{
    // データがまだ到着していないこともあり得ます。その場合は何もしません。
}
else
{
    PIA_TRACE_RESULT(r);
    PIA_ASSERTMSG(false, "Programming error.");
}

ディスパッチ関数

PiaSession を利用するためにはディスパッチ関数 pia::common::Scheduler::Dispatch() を少なくとも毎ゲームフレームに 1 回の間隔で呼び出す必要があります。インターネット通信時には NEX サーバーサービスのために NEX のディスパッチ関数 nex::Scheduler::Diapatch() の呼び出しも必要です。

NAT セッションの開始/終了

Pia でインターネット通信を利用する際は pia::inet::NexFacade が NAT トラバーサル関連処理を行います。pia::inet::NexFacade が NetZ の nex::NATTraversalClient と nex::ConnectivityManager に相当する機能を備えています。動作が競合するため、アプリケーション側では nex::NATTraversalClient オブジェクトを用意してはいけません。同様に、nex::ConnectivityManager::StartNATSession()、StopNATSession() も呼び出してはいけません。

 

コード 12-3. NAT セッションの開始/終了の違い
// NetZ
s_pNatTraversalClient = qNew nn::nex::NATTraversalClient();
s_pNatTraversalClient->Bind(s_pNgsFacade->GetCredentials());
nn::nex::ConnectivityManager::GetInstance()->StartNATSession(&callContext);
    ...
nn::nex::ConnectivityManager::GetInstance()->StopNATSession();
s_pNatTraversalClient->Unbind();
qDelete s_pNatTraversalClient;

// Pia
// セッション構築/参加 API 呼び出し時に自動的に処理が行われるためアプリケーションが呼ぶ必要はありません。

ステーション識別トークン

Pia でもステーションを識別するためのトークンを利用できます。NetZ では nex::String の文字列として設定しますが、Pia では 32 バイトの uint8_t 型配列として設定します。

コード 12-4. 識別トークンの設定
// セッションのスタートアップ時に設定します
nn::pia::session::Session::StartupSetting startupSetting;

// 自分自身を示す識別トークンの設定
nn::pia::transport::Station::IdentificationToken localToken;
// LocalTokenData は IdentificationTokenMaxDataSize [byte] の識別トークン用データ列の先頭アドレスであるとします
::std::memcpy(localToken.data, LOCAL_TOKEN_DATA, nn::pia::transport::Station::IdentificationTokenMaxDataSize);
startupSetting.pToken = &localToken;

result = nn::pia::session::Session::GetInstance()->Startup(startupSetting);
if (result.IsFailure())
{
    // エラー処理
}

Pia に割り当てるメモリサイズの調節

Pia は NEX と異なり、通信中の動的メモリ確保は行わず、Pia 初期化時にメモリ使用量を確定させておく設計となっています。Pia に割り当てるメモリサイズを調節するための説明を、 6. PiaTransport 編に記載しています。

エラーハンドリング

Pia のエラーハンドリングは NEX とは異なります。NEX の API を Pia の API に置き換えた場所でエラーハンドリングを Pia 形式に修正する必要があります。 Pia のエラーハンドリングについては、 1.8. エラー処理 を参照してください。