4. バックグラウンド通信

アプリケーションの裏で自動的に行われる無線通信(バックグラウンド通信)をアプリケーションで利用するために、以下のライブラリが用意されています。

  • すれちがい通信(CEC)
  • いつの間に通信(BOSS)
  • プレゼンス機能(FRIENDS)

この章では、それぞれのライブラリを使用したアプリケーションの開発に必要な情報とプログラミング手順について説明します。

バックグラウンドで動くデーモンによるアプリケーションへの影響

デーモンとはバックグラウンド動作している常駐モジュールのことで、3DS にはネットワークの処理を行う複数のデーモンが存在します。これらのデーモンを統括する特別なデーモンとして ndm(Network Daemon Manager)があり、SDK には ndm を制御するための NDM ライブラリが用意されています。

アプリケーションの動作する CPU コアと異なる CPU コアでデーモンは動作しているため、デーモンがアプリケーションの CPU 時間を直接奪うようなことはありません。しかし、ネットワーク(無線通信)や NAND へのアクセスは競合するため、デーモンの動作がアプリケーションのパフォーマンスに影響を与える場合があります。そのため、ネットワークや NAND へのアクセスパフォーマンスの低下が致命的である場合は、NDM ライブラリでデーモンの動作をアプリケーションで停止することができます。

補足:

明示的に停止を解除しなければ、アプリケーションの動作中はいつの間に通信が停止した状態になります。なお、スリープ中やタスクの即時実行は停止の影響を受けません。

デーモンは自由に停止することができる一方で、デーモンにはそれぞれ役割があり停止すると様々な制限が生じます。したがって、デーモンを停止する場合は、停止することによってどのようなデメリットが生じるのかを十分に理解していなければなりません。

以下に、現時点で判明しているデーモンによるアプリへの影響を示します。

表 4-1. 現時点で判明しているデーモンによるアプリへの影響

デーモン(nn::ndm::DaemonName)

ネットワークへの影響

NAND への影響

nn::ndm::DN_CEC

なし(インフラストラクチャ通信を使用しません)

中(すれちがい成立時に 10 KByte ~ 10 MByte 程度の NAND アクセスが発生)

nn::ndm::DN_BOSS

高(タスク実行時に HTTP 通信が発生)

高(タスク実行時に NAND 書き込みが発生)

nn::ndm::DN_NIM

高(タスク実行時に HTTP 通信が発生)

高(タスク実行時に NAND 書き込みが発生)

nn::ndm::DN_FRIENDS

低(断続的に UDP 通信が発生)

注意:

デーモンを制御する場合は、関数リファレンスに記載されている情報にも必ず目を通してください。

4.1. すれちがい通信

3DS のすれちがい通信は、ニンテンドーDS での同名の機能を強化し、より使いやすくしたものです。

バックグラウンドで通信を行いますので、起動中のアプリケーションや状態に関わらず、送信データを用意しておくだけでデータの送受信を行うことができます。ただし、アプリケーション独自の通信処理を実装することができないため、特殊な条件を必要とするような送受信方法を実現することはできない可能性があります。

注意:

すれちがい通信を動作させるためには EULA(End User License Agreement)の設定が必要です。CEC ライブラリを使用する場合は、Config ツールの「EULA Setting」で、「Agree Version」に 1.0 以上を設定してください。

開発用の本体(デバッガ、開発実機)と製品版の本体とでは、すれちがい通信は行われません。

4.1.1. 動作の概要

すれちがい通信は、すれちがいデーモン(バックグラウンドで動作する常駐プロセス)によって実際の通信処理を行っています。3DS 本体に用意されている領域にすれちがいボックスを作成し、そこに送信するメッセージとしてのすれちがいデータを登録することで、すれちがい通信が可能な状態になると、すれちがいデーモンが自動的に通信相手とすれちがいデータの送受信を行います。すれちがいデータは暗号化されて送信されますので、アプリケーションでデータの秘匿性を考慮する必要はありません。

すれちがいボックスは 12 個用意されており、原則的に 1 つのアプリケーションで 1 つのすれちがいボックスを占有することになります。すれちがい通信を行うためにすれちがいボックスを専有してデータを登録するときには、必ずユーザーの確認を取ってから行ってください。そのとき、12 個のすれちがいボックスがすべて埋まっていた場合は、「これ以上すれちがい通信をセットできません。本体設定のすれちがい通信管理で他のソフトの設定を消去してください」と表示し、本体設定でいらないすれちがい通信設定を消去する必要があることをユーザーに説明してください。不要なすれちがい通信設定をユーザーが消去するまで、すれちがいボックスを新たに作成することができません。

注意:

同じタイトルの複数のゲームカードで 1 台の 3DS を共有している場合のすれちがい通信は、すべて同じすれちがいボックスに対して処理されることに注意が必要です。

すれちがいボックスをどのアプリケーションが使用しているかは本体設定の「すれちがい通信管理」の画面で確認することができ、すれちがいボックス単位で、すれちがい通信設定の消去を行うことができます。消去されたすれちがい通信設定のおしらせはおしらせリストに表示されなくなります。本体設定ですれちがいボックスが消去される可能性を考慮し、アプリケーションはボックス内のデータが保持されていなくても動作するように実装しなければなりません。

すれちがいボックス内には送信ボックスと受信ボックスがあり、それぞれ、送信するすれちがいデータを登録する領域、受信したすれちがいデータを保存する領域として確保されます。

すれちがい通信可能な状態の本体同士が近づき、互いに相手を見つけると、最初に双方のシステム領域内にあるすれちがいボックスを走査します。そこで、通信相手にも同じアプリケーション(すれちがい通信 ID)で作成したすれちがいボックスがあれば、すれちがいデータの送受信が必要な分だけの通信が開始されます。すれちがい通信 ID が同じであれば、本体のリージョンが異なっていても通信が行われます。複数のすれちがいボックスからすれちがいデータが送信される場合は、そのサイズが小さいものから順に送信されます。すべてのすれちがいデータの送受信が完了するまで、双方が通信可能範囲に存在しているとは限りません。通信可能範囲に存在した時間が短い場合、最初の方に送信されるサイズの小さなデータだけが通信に成功する可能性があります。そのため、通信の成功率を上げるには、すれちがいデータのサイズをなるべく小さくすることが有効となります。また、1 つのすれちがいボックスからは、グループ化されていない限り、1 つのすれちがいデータが送信されます。

設定によっては受信のみを行うすれちがいデータを登録することもできますが、受信のみのデータ同士ではすれちがい通信が行われないことに注意してください。これは送信のみを行うすれちがいデータでも同じです。特に、最初に登録するデータが受信のみまたは送信のみで固定されている場合、すれちがい通信がいつまで経っても行われなくなってしまいます。

通信した相手のすれちがい通信用アドレス(8 時間ごとに再生成される)がアドレスフィルタに登録されるため、同じ相手とのすれちがい通信は平均 4 時間、最大 8 時間経過するまで行われません。アドレスフィルタは、アドレスフィルタをリセットしてすれちがい通信を開始する関数(デバッグ用途のみ)でクリアすることができます。製品版ではアプリケーションでアドレスフィルタの制御を行うことができませんので、すれちがい通信は、その場で設定したデータを目の前のユーザーに渡す用途には向いていません。

補足:

接続設定で登録されているアクセスポイントや周囲のニンテンドーゾーンに接続している場合、すれちがい通信が行われません。アプリケーションの動作中にすれちがい通信が行われることを確認するときは、接続設定を解除し、周囲にニンテンドーゾーン等がない環境にしてください。

ただし、アクセスポイントやニンテンドーゾーンに接続している場合でも、すれちがい通信専用モード(「4.1.3.7. すれちがい通信専用モード(デバッグ用途のみ)」)に切り替えたり、スリープするとすれちがい通信は行われます。

図 4-1. すれちがいボックスの走査

Application A TitleID=0x0000_0100 Application C TitleID=0x0000_0300 Application D TitleID=0x0000_0400 Application B TitleID=0x0000_0200 ユーザー A …… Application F TitleID=0x0000_0600 Application Z TitleID=0x0000_1A00 Application C TitleID=0x0000_0300 Application A TitleID=0x0000_0100 Application E TitleID=0x0000_0400 異なるアプリケーションでも、同じすれちがい通信 ID で登録したすれちがいボックス同士は、すれちがい通信の対象となる。 ユーザー B Application X TitleID=0x0000_1800 …… 1 つのアプリケーション(すれちがい通信 ID)で 1 つのすれちがいボックスを占有し、最大で 12 個のアプリケーションを登録することができる。

4.1.1.1. すれちがい通信に関する諸条件

すれちがい通信の開始や通信相手との接続、すれちがいデータの送信、受信データの破棄が行われる条件、おしらせランプが点灯する条件を示します。

すれちがい通信を開始する条件

以下の条件がすべて満たされていなければ、通信相手の検索が行われず、すれちがい通信は開始されません。

  • 無線オンモードである
  • ペアレンタルコントロールで、すれちがい通信が制限されていない
  • ユーザーが EULA に同意している
  • すれちがいボックスが存在し、送信可能回数が 1 以上のすれちがいデータが存在する

ニンテンドーゾーンの検索もすれちがいデーモンで行われるため、上記条件が満たされていなくても、デーモンの状態がスキャン中になることがあります。

通信相手と接続する条件

すれちがい通信の開始が可能な状態の本体同士が近付いたときに、以下の条件がすべて満たされていなければ、通信相手との接続が行われません。

  • 通信相手にも、同じすれちがい通信 ID のすれちがいボックスが存在する
  • 処理される順番にあるすれちがいデータ同士が、通信可能な送受信モードの組み合わせになっている
  • 通信相手のすれちがい通信用アドレスが、アドレスフィルタ(通信した相手のすれちがい通信用アドレスのリスト)に登録されていない
  • 通信相手が受信拒否リストに登録されていない

すれちがい通信用アドレスは 8 時間ごとに再生成されます。本体に保存されるアドレスフィルタには、最大 255 個のすれちがい通信用アドレスが登録(256 個目からは古いものに上書き)されます。すれちがいボックスに関係なく、登録されているすれちがい通信用アドレスの相手とのすれちがい通信は行われなくなります。そのため、新たにすれちがいボックスを作成しても、同じ相手とのすれちがい通信は平均 4 時間、最大 8 時間経過するまで行われません。

すれちがいデータを送信する条件

通信相手との接続が行われたあと、事前にデータのやり取りを行い、以下の条件がすべて満たされていなければ、すれちがいデータを相手に送信しません。

  • 通信相手がフレンドであり、送信対象の設定にフレンドのみを対象とするフラグが含まれている。または、通信相手がフレンドではなく、送信対象の設定にフレンド以外を対象とするフラグが含まれている
  • すれちがいデータを送信した場合に、通信相手の受信ボックスのデータサイズと個数の上限を超えない

送受信モードが「受信のみ」または「送受信」である場合、上記の条件を満たしていなくても、通信相手が上記の条件を満たしていればすれちがいデータの受信が開始されます。

条件を満たしていないために通信相手との送受信が開始されない場合は、この時点でアドレスフィルタにすれちがい通信用アドレスが登録されます。ただし、事前に行っているデータのやり取りに失敗している場合は登録されません。

送信または受信が開始された場合、すれちがいデータの送受信に成功したときにアドレスフィルタにすれちがい通信用アドレスが登録されます。送受信に失敗したときは登録されません。

受信したすれちがいデータを破棄する条件

以下の条件のいずれかを満たしている場合、受信したすれちがいデータは破棄されます。

  • すれちがいデータの署名が正しくない
  • 同じメッセージ ID のすれちがいデータが受信ボックスに存在する
おしらせランプが点灯する条件

いずれかのすれちがいボックスにデータを受信し、かつそのすれちがいデータが破棄されなかった場合におしらせランプが緑色に点灯します。

4.1.2. 初期化と終了

CEC ライブラリはすれちがい通信をアプリケーションで利用するためのライブラリです。ライブラリの初期化と終了は、nn::cec::Initialize()nn::cec::Finalize() で行います。

コード 4-1. 初期化と終了
static nn::Result nn::cec::Initialize(nn::fnd::IAllocator& cecAllocFunc);
nn::Result nn::cec::Finalize();

すれちがい通信でライブラリが使用するメモリの管理は、アプリケーション独自のメモリアロケータで行わなければなりません。ライブラリは cecAllocFunc に渡されたアロケータから、すれちがいデータの最大サイズの 2 倍程度のメモリをデータ処理などのために確保します。

nn::cec::Initialize() の呼び出しが成功したあとに、再度 nn::cec::Initialize() を呼び出した場合は、初期化済みであることを示すエラー(nn::cec::MakeResultAlreadyInitialized())が返されます。

4.1.3. すれちがいボックス

すれちがいボックスへのアクセスは nn::cec::MessageBox クラスを介して行います。

4.1.3.1. アクセスの開始(オープンと作成)

すれちがいボックスへのアクセスを開始する前に、すれちがいボックスを作成可能か nn::cec::CTR::MessageBox::CanCreateMessageBox() で確認できます。作成可能な場合は nn::cec::MessageBox::CreateMessageBox() ですれちがいボックスを作成できます。すれちがいボックスは作成に成功した時点でオープンされています。目的のゲームタイトルのすれちがいボックスがすでに存在している場合は nn::cec::MessageBox::OpenMessageBox() でオープンしてアクセスを開始します。すれちがいボックスの登録数が上限に達している場合は作成することができません。

すれちがいボックスをオープンすると、すれちがいデーモンが停止状態になります。停止状態はすれちがいボックスをクローズするまで継続します。すれちがいボックスをオープンしたまま HOMEメニューを表示すると、デーモンは停止された状態ですので、すれちがい通信が発生しません。すれちがいボックスをオープンしたままスリープ状態に移行した場合はデーモンが動作状態になり、スリープ状態から復帰したときに停止状態になります。スリープ中にすれちがい通信が行われてすれちがいボックスの内容が更新されたとしても、オープンしていた MessageBox クラスには反映されず、最新の情報を取得するには再度オープンする必要があります。すれちがいボックスへのアクセス結果が巻き戻され、すれちがいボックスの作成やすれちがいデータの登録・削除といった処理がなかったことになる可能性がありますので、HOMEメニューの表示やスリープ状態へ移行する前に、オープンしていたすれちがいボックスはクローズまたはコミットすることを推奨します。

補足:

すれちがいボックスの作成やすれちがいデータの登録や削除は、クローズまたはコミットされるまで反映されません。そのため、すれちがいボックスへのアクセスは、オープンからクローズまでをひとまとめにして行うことを推奨します。

注意:

すれちがい通信が行われたかどうかをリアルタイムで検知する場合は、GetCecEvent()(「4.1.6. 通信発生の通知」参照)を利用してください。すれちがいボックスのオープンとクローズを定期的に繰り返すと、オープンの度に通信が中止されるため、バックグラウンドでの通信が行われなくなります。

コード 4-2. すれちがいボックスのオープン、作成
nn::cec::TitleId nn::cec::MakeCecTitleId(bit32 id, bit8 variation = 0x0);
nn::Result nn::cec::MessageBox::CanCreateMessageBox(const TitleId cecTitleId);
nn::Result nn::cec::MessageBox::OpenMessageBox(const TitleId cecTitleId, 
                                               const u32 privateId);
nn::Result nn::cec::MessageBox::CreateMessageBox(
        const TitleId cecTitleId, const u32 privateId, const char* hmacKey,
        const void* icon, size_t iconSize, const wchar_t* name, size_t nameSize,
        size_t inboxSizeMax = CEC_INBOX_SIZE_DEFAULT, 
        size_t outboxSizeMax = CEC_OUTBOX_SIZE_DEFAULT, 
        size_t inboxMessNumMax = CEC_INBOX_MESSNUM_DEFAULT,
        size_t outboxMessNumMax = CEC_OUTBOX_MESSNUM_DEFAULT,
        size_t messageSizeMax = CEC_MESSSIZEMAX_DEFAULT);

cecTitleId には、任天堂から指定されたゲームタイトルを識別するための値(すれちがい通信 ID)を指定します。すれちがい通信 ID は業務部から割り当てられたユニーク ID(20 ビット)を引数に、nn::cec::MakeCecTitleId() を呼び出して生成された ID です。複数のタイトルで共通のすれちがい通信を行う場合は、そのうちのいずれかのユニーク ID を代表値として指定し、1 つのすれちがいボックスを共有することになります。

privateId は、すれちがいボックスへのアクセスに必要なキーとなる値です。この値は開発者が自由に決めることができます。この値の生成方法によっては、特定のセーブデータだけがアクセスできるようにしたり、特定のゲームカードだけがアクセスできるようにしたりすることが可能です。

上記の 2 つの引数の組み合わせが作成時のものと同じでなければ、すれちがいボックスはオープンすることができません。指定されたすれちがい通信 ID で作成されたすれちがいボックスが存在しない場合はデータなしのエラーが、すれちがいボックスは存在するが privateId が異なる場合はアクセス権なしのエラーが返されます。

hmacKey には、改竄防止のために使用される 32 バイトの文字列を指定します。文字列は開発者が自由に決めることができます。すれちがい通信の送信側、受信側ともに同じ文字列でなければなりません。つまり、同じすれちがい通信 ID ですれちがい通信を行うアプリケーションは同じ文字列を指定する必要があります。

icon iconSize にはそれぞれ、すれちがい通信管理画面で表示されるアイコンのデータとデータサイズを指定します。アイコンのデータは、サイズが 48×48 ピクセル、ピクセルフォーマットが RGB565 の PICA ネイティブフォーマットのテクスチャでなければなりません。通常はそのゲームタイトルのアイコンを使用しますが、シリーズ共通の場合はシリーズであることが判別できるアイコンを使用することができます。

name nameSize にはそれぞれ、すれちがい通信管理画面で表示されるタイトル名とその長さ(バイトサイズ)を指定します。タイトル名は、最大 64 文字(終端文字を含む)の文字列で指定します。ただし、実際に表示されるのは最大幅の内蔵フォント(日米欧リージョンは "%"、そのほかのリージョンはひらがな、漢字、ハングルなど)で 17 文字分の幅です。なお、タイトル名の言語は本体の言語設定に合わせても構いませんし、アプリケーション自身の言語設定に合わせても構いません。

inboxSizeMax outboxSizeMax にはそれぞれ、受信ボックスと送信ボックスの容量を指定します。2 つのボックスの合計サイズは 1 MByte 以内でなければなりません。すれちがいデータを伝播させる場合を除いて、受信ボックスに大きな容量を割り当てるのが基本です。引数の指定を省略したときは、デフォルトの 512 KByte を指定したことになります。

inboxMessNumMax outboxMessNumMax には、受信ボックスと送信ボックスに保存するすれちがいデータの数をそれぞれ指定します。ボックスの容量を一番大きなすれちがいデータのサイズで割った値を指定してください。引数の指定を省略したときは、デフォルトの 99 を指定したことになります。

messageSizeMax には、すれちがいデータの最大サイズを指定します。指定された値よりも大きなサイズのすれちがいデータは保存することができません。通常は引数の指定を省略し、デフォルトの 100 KByte を指定してください。ただし、現在の CTR-SDK では messageSizeMax の指定に関わらず、すれちがいデータの最大サイズが 100 KByte 固定となります。

nn::cec::MessageBox::OpenMessageBox() が返す可能性のあるエラーとその対処を下表に示します。

表 4-2. OpenMessageBox() が返す可能性のあるエラーとその対処

エラー

原因と対処

nn::cec::ResultNoData()

すれちがいボックスが存在しませんので、すれちがいボックスを作成してから、再度オープンしてください。

nn::cec::ResultNotAuthorized()

privateId が一致しないためオープンできません。すれちがいボックスを再作成する場合は、削除する必要があります。

nn::cec::ResultStateBusy()

複数のスレッドからアクセスしなければ返されないエラーです。時間を置いてリトライすることでオープンすることができるようになります。

上記以外

このエラーが返ってきた場合は失敗として扱い、この関数を使わずにアプリケーションを進行させる必要があります。アプリケーションの進行にこの関数が必須である場合は、FATAL エラーとして表示しても構いません。

nn::cec::MessageBox::CreateMessageBox() が返す可能性のあるエラーとその対処を下表に示します。

表 4-3. CreateMessageBox() が返す可能性のあるエラーとその対処

エラー

原因と対処

nn::cec::MakeResultBoxAlreadyExists()

同じ cecTitleIdprivateId で作成されたすれちがいボックスがすでに存在しています。

nn::cec::MessageBox::OpenMessageBox()nn::cec::ResultNoData() が返されている場合は、cecTitleId の variation のみが異なるすれちがいボックスが存在しています。

nn::cec::MakeResultBoxNumFull()

すでに 12 個のすれちがいボックスが作成されています。新規に作成することができませんので、すれちがい通信管理画面で不要なすれちがいボックスを削除するように、ユーザーを案内してください。

nn::cec::ResultStateBusy()

複数のスレッドからアクセスしなければ返されないエラーです。時間を置いてリトライすることで作成することができるようになります。

nn::cec::ResultInvalidArgument()

引数の指定に間違いがあります。

nn::cec::ResultTooLarge()
nn::cec::ResultInvalidData()

アイコン・名称の指定に間違いがあります。

上記以外

このエラーが返ってきた場合は失敗として扱い、この関数を使わずにアプリケーションを進行させる必要があります。アプリケーションの進行にこの関数が必須である場合は、FATAL エラーとして表示しても構いません。

4.1.3.2. 付随データの設定

すれちがい通信管理画面で表示されるときに使用されるアイコンとタイトル名を変更することができます。すれちがいボックスをオープンし、変更するアイコンやタイトル名をすれちがいボックスの付随データとして設定してください。

コード 4-3. 付随データの設定
nn::Result nn::cec::MessageBox::SetMessageBoxData(
                        u32 datatype, const void* data, size_t dataSize);

datatype には、付随データの種類を指定します。アイコンのデータならば nn::cec::BOXDATA_TYPE_ICON を、タイトル名ならば nn::cec::BOXDATA_TYPE_NAME_1 を指定してください。datadataSize には、付随データの先頭アドレスとそのサイズを指定します。

アイコンデータは、サイズが 48×48 ピクセル、ピクセルフォーマットが RGB565 の PICA ネイティブフォーマットのテクスチャでなければなりません。通常はそのゲームタイトルのアイコンを使用しますが、シリーズ共通の場合はシリーズであることが判別できるアイコンを使用することができます。

タイトル名は、最大 64 文字(終端文字を含む)の文字列で指定します。実際に表示されるのは最大幅の内蔵フォント(日米欧リージョンは "%"、そのほかのリージョンはひらがな、漢字、ハングルなど)で 17 文字分の幅です。なお、タイトル名の言語は本体の言語設定に合わせても構いませんし、アプリケーション自身の言語設定に合わせても構いません。

4.1.3.3. 送信ボックス / 受信ボックスの情報

作成時に指定した容量やメッセージの最大保存数、現時点でのボックスの使用量、保存されているすれちがいデータの数などの情報を取得することができます。

コード 4-4. 送信ボックス / 受信ボックスの情報の取得
u32 nn::cec::MessageBox::GetBoxSizeMax(nn::cec::CecBoxType boxType) const;
u32 nn::cec::MessageBox::GetBoxSize(nn::cec::CecBoxType boxType) const;
u32 nn::cec::MessageBox::GetBoxMessageNumMax(nn::cec::CecBoxType boxType) const;
u32 nn::cec::MessageBox::GetBoxMessageNum(nn::cec::CecBoxType boxType) const;

これらの関数のように、送信ボックスか受信ボックスかを指定する必要のあるものは、nn::cec::CecBoxType 列挙子を引数にとります。CEC_BOXTYPE_INBOX が受信ボックスの、CEC_BOXTYPE_OUTBOX が送信ボックスの指定に使われます。

GetBoxSizeMax()GetBoxSize() では、指定したボックスの容量と使用量を取得することができます。

GetBoxMessageNumMax()GetBoxMessageNum() では、指定したボックスに保存することのできるすれちがいデータの数と現時点で保存されているすれちがいデータの数を取得することができます。

これらの情報のうち、現時点で保存されているすれちがいデータの数でループを組み、nn::cec::MessageBox クラスの GetMessageId() でインデックスに対応するメッセージ ID を取得することで、ボックス内のデータを走査することができます。

コード 4-5. インデックス指定によるメッセージ ID の取得
nn::Result nn::cec::MessageBox::GetMessageId(MessageId* messId, 
                        const CecBoxType boxType, const u32 messIndex);

取得したメッセージ ID を引数に ReadMessage() を呼び出してすれちがいデータを取得し、nn::cec::Message クラスを介してアクセスすることができるようになります。

コード 4-6. すれちがいデータの取得
u32 nn::cec::MessageBox::GetMessageSize(const CecBoxType boxType, 
                                            const u32 messIndex) const;
nn::Result nn::cec::MessageBox::ReadMessage(
                nn::cec::Message& cecMessage, void* buf, const size_t bufLen,
                const CecBoxType boxType, const MessageId& messageId);

cecMessage には、nn::cec::Message クラスのインスタンスを渡してください。

buf bufLen には、GetMessageSize() で取得したサイズのバッファを指定しなければなりません。これは、一度バイナリ配列として読み込んでからインスタンスに情報を設定するためです。

nn::cec::Message クラスを介した、すれちがいデータへのアクセスについては「4.1.4.3. 情報の取得」を参照してください。

4.1.3.4. 削除

すれちがいボックスは 12 個しかない有限のリソースですので、すれちがいデータを 1 つも登録していない状態やユーザーがすれちがい通信を行わないときはすれちがいボックスを削除し、ほかのアプリケーションがすれちがいボックスを作成できるようにしてください。

コード 4-7. すれちがいボックスの削除
nn::Result nn::cec::MessageBox::DeleteMessageBox();
nn::Result nn::cec::MessageBox::DeleteMessageBox(const TitleId cecTitleId);

cecTitleId には、削除するすれちがいボックスのすれちがい通信 ID を指定します。引数の指定のない関数では、そのクラスでオープンしているすれちがいボックスが削除されます。

注意:

アプリケーションで作成したすれちがいボックス以外を削除しないでください。

すれちがい通信 ID を指定したすれちがいボックスの削除は、すれちがいボックスの破損などが原因でオープンできなくなったときにのみ利用してください。

4.1.3.5. アクセスの終了(クローズとコミット)

すれちがいボックスへのアクセスが終了したあとは、必ずデーモンを動作状態に戻さなければなりません。動作状態に戻すには、nn::cec::MessageBox::CloseMessageBox() を呼び出して、オープンしていたすれちがいボックスをクローズします。

コード 4-8. すれちがいボックスのクローズ
void nn::cec::MessageBox::CloseMessageBox();

すれちがいボックスへの変更は、コミットが行われるまで保存領域に書き込まれません。そのため、コミットを行う前にアプリケーションを終了すると、前回コミットされた時点のデータに巻き戻ってしまう可能性があります。コミットを行うには nn::cec::MessageBox::CommitMessageBox() を呼び出しますが、すれちがいボックスのクローズ処理の中でもコミットが行われています。

コード 4-9. すれちがいボックスのコミット
nn::Result nn::cec::MessageBox::CommitMessageBox();

4.1.3.6. すれちがいデーモンとの関係

アプリケーションですれちがいボックスをオープンすると、すれちがいデーモンは停止状態になります。このとき、デーモンが処理を行っている途中であった場合は処理がキャンセルされ、受信したデータを破棄してしまう可能性があります。そのため、すれちがいボックスをオープンする前に nn::cec::CecControl::GetCecState() でデーモンが処理中(DAEMON_STATE_BUSY)や通信中(DAEMON_STATE_COMMUNICATING)でないことを確認することで、安全にすれちがいボックスへのアクセスを行うことができます。

4.1.3.7. すれちがい通信専用モード(デバッグ用途のみ)

デバッグ用途での使用に限定されていますが、以下の関数でバックグラウンド通信をすれちがい通信専用モードに切り替えることができます。

コード 4-10. すれちがい通信専用モードへの切り替え
static nn::Result nn::cec::CecControl::EnterExclusiveState();
static nn::Result nn::cec::CecControl::LeaveExclusiveState();

すでにバックグラウンド通信が排他的に使用されているときやローカル通信を行っているときは、すれちがい通信専用モードに切り替える際にエラーとなります。

4.1.4. すれちがいデータ

すれちがいデータへのアクセスは nn::cec::Message クラスを介して行います。

4.1.4.1. 新規作成

すれちがいデータを新規に作成するには、nn::cec::Message クラスのインスタンスを用意し、メンバ関数の NewMessage() を呼び出さなければなりません。

コード 4-11. すれちがいデータの新規作成
nn::Result nn::cec::Message::NewMessage(TitleId cecTitleId, u32 groupId, 
                        u8 messageTypeFlag, u8 sendMode, 
                        u8 sendCount, u8 propagationCount);
nn::Result nn::cec::Message::NewMessage(TitleId cecTitleId, u32 groupId, 
                        u8 messageTypeFlag, u8 sendMode, 
                        u8 sendCount, u8 propagationCount, 
                        const void* icon, size_t iconSize, 
                        const wchar_t* infoTextData, size_t infoTextSize);

cecTitleId には、すれちがいボックスをオープンしたときに指定したすれちがい通信 ID を指定します。

groupId には、すれちがいデータをグループ化する場合に、そのグループ番号を指定します。グループ化する場合は、同じグループで登録されたすれちがいデータの合計サイズが、1 回の通信で送信可能な最大サイズ(100 KByte)を超えないように注意しなければなりません。グループ番号に 0 を指定した場合はグループ化が行われず、グループ番号が 0 のすれちがいデータがほかに存在していても個別に送受信が行われます。

messageTypeFlag には、送信対象をフラグの論理和で指定します。フラグの組み合わせで、すれちがいデータを送信する相手をフレンドかフレンドでないかに限定、もしくは両方を指定することができます。この設定によって、フレンドとの通信とフレンドではない相手との通信で異なるすれちがいデータを送信することができ、グループ内で設定の異なるすれちがいデータを登録すると、フレンドには追加でコメントを送信するなどの制御が可能です。このフラグの指定は送信が行われるときにのみ考慮されます。フレンドであるかどうかを受信の条件にすることはできません。

表 4-4. 送信対象を設定するフラグ

フラグ

説明

下記 2 つのフラグの論理和を指定

フレンドであるかどうかを区別せずに送信する場合に設定します。

通常はこの設定を使用し、誰にでも送信するように設定します。

MESSAGE_TYPEFLAG_NON_FRIEND

通信相手がフレンドではない場合にのみ送信します。

同じグループのすれちがいデータで、フレンドとフレンド以外に対して異なるコメントを送信する場合などに設定します。

MESSAGE_TYPEFLAG_FRIEND

通信相手がフレンドである場合にのみ送信します。

誰にでも送信するすれちがいデータと同じグループに含めて、フレンドに対して送信したときだけコメントを追加する場合などに設定します。

注意:

MESSAGE_TYPEFLAG_FRIEND のみを設定する場合は、すれちがい通信中継での処理の違いがアプリケーションの仕様上問題ないかを確認してください。詳細については、「9. 付録:すれちがい通信中継」を参照してください。

sendMode には、すれちがいデータの送受信を制御する送受信モードを指定します。通信相手の送受信モードとの組み合わせによって、通信時にすれちがいデータの送信と受信が行われるのかが決定します。

表 4-5. 送受信モード

送受信モード

説明

CEC_SENDMODE_RECV

受信のみ。通信相手が「送信のみ」、「送受信」ならば受信します。

同じ設定の相手とはすれちがい通信が行われないため、設定を推奨していません。

※ すれちがいデータは送信ボックスに登録します。

CEC_SENDMODE_SEND

送信のみ。通信相手が「受信のみ」、「送受信」ならば送信します。

同じ設定の相手とはすれちがい通信が行われないため、設定を推奨していません。

CEC_SENDMODE_SENDRECV

送受信。通信相手が「受信のみ」ならば送信、「送信のみ」ならば受信、「送受信」ならば送信と受信を行います。

自作データの配布やゲーム内ハウスへの招待など、一方通行(送信だけ、もしくは受信だけ)の通信でもよい場合に設定します。

CEC_SENDMODE_EXCHANGE

交換。通信相手が同じ「交換」ならば、送信と受信を行います。双方で送信と受信が正しく行われなければ、すれちがい通信が成立しません。

ゲーム内ペットの交流やすれちがいでの自動対戦など、通信したデータ同士を特定する必要がある場合に設定します。

表 4-6. 送受信モードの組み合わせによって行われる送受信

A(縦)
B(横)

受信のみ

送信のみ

送受信

交換

受信のみ

×

A ← B

A ← B

×

送信のみ

A → B

×

A → B

×

送受信

A → B

A ← B

A ⇔ B

×

交換

×

×

×

A ⇔ B

相手にすれちがいデータが問題なく送信されたかどうかは保証されません。送受信モードが異なっていてもグループ化されるため、同じグループに属するデータの送受信モードをなるべく統一し、送受信モードが「受信のみ」のデータを含めないようにしてください。

注意:

送受信モードの「交換」は、同じ「交換」に設定された相手とのみ通信が発生します。同じすれちがいボックスに、「交換」とそのほかの設定(「送信のみ」、「受信のみ」、「送受信」)が混在すると、すれちがい通信が発生する確率が下がります。

sendCount には、すれちがいデータの送信可能回数を指定します。nn::cec::MESSAGE_SENDCOUNT_UNLIMITED を指定した場合は送信回数を制限しません。送信が行われると送信可能回数が 1 減少し、0 になると送信されなくなります。受信側が受け取ったデータを受信ボックスに登録したかどうかに関係なく、送信可能回数はデータを送信した時点で減少します。データを送信したときに減少しますので、送信モードが「送受信」の場合でも、相手の送信モードが「送信のみ」ならば減少しません。また、受信可能回数の限界に達しているなど、通信相手のボックスに空きがないときにはデータが送信されず、送信可能回数は減少しません。ただし、自分の受信ボックスに空きがあれば、相手からデータを受信する可能性はあります。送受信モードが「受信のみ」(CEC_SENDMODE_RECV)のときは受信回数の制限として機能し、すれちがいデータを受信するたびに 1 減少して 0 になるまでデータを受信します。

同じすれちがいデータを受信したときは、新しい方のすれちがいデータが破棄されます。すれちがいデータが破棄された場合でも、送信した側の sendCount は 1 減少します。

送信されたデータが相手に到達するかどうかは保証されていません。また、すれちがい通信が成立した直後に、すれちがいデータの保存や送信可能回数の減少処理の途中で電源が切断されたりすると、送信可能回数の残りと受信したデータの個数との整合性が取れなくなる場合があります。例えば、送信が完了するのとほぼ同時に通信が切れた場合、タイミングによっては下記のようなケースが発生することがあります。

  • 送信側は送信に失敗、受信側は受信に成功する。この場合、送信側の送信可能回数が減っていないのにすれちがいデータが受信されている。
  • 送信側は送信に成功、受信側は受信に失敗する。この場合、送信側の送信可能回数が減っているのにすれちがいデータは受信されていない。
補足:

アドレスフィルタは、その本体で送信または受信が1件でも成功したときに登録されます。

すれちがい通信した二つの端末で通信が途切れたために片方は送信は成功・受信は失敗、もう片方は受信は成功・送信は失敗した場合、送受信されなかったデータがまだほかに残っていてもアドレスフィルタはお互いに登録されます。

この場合、他のタイトルデータのすれちがい通信が行われていても、期待するタイトルデータのすれちがい通信が行われないままアドレスフィルタにより通信が制限され、その端末同士でのすれちがい通信が再開可能になるまで時間がかかるということが起こりえます。

propagationCount には、すれちがいデータの伝播可能回数を指定します。伝播可能回数は受信時に 1 減少し、1 以上ならば受信したデータを送信ボックスにコピーします。つまり、2 以上を指定した場合にすれちがいデータの伝播が行われることになります。受信したデータが受信ボックスだけでなく送信ボックスにも保存されることや、伝播経路によっては自分が送信したすれちがいデータを受信する可能性があることに注意してください。データの爆発的な広がりを防ぐため、sendCount または propagationCount のどちらかには必ず 1 を設定しなければならないように、ライブラリで制限されています。

伝播可能回数の設定されたすれちがいデータを受け取ったときに、受信ボックスに同じデータが存在している場合は新しいデータが破棄されるため伝播は行われません。受信ボックスに同じデータが存在せず、データを受信した場合でも送信ボックスの容量が足りなければ伝播は行われません。ただし、以前に同じデータを受け取って送信ボックスにコピーしていた場合は、新しいデータの伝播可能回数と送信可能回数(必ず 1)に更新されるため伝播は継続します。

図 4-2. 伝播可能回数に 2 を設定したときの伝播の様子

3DS A 3DS B 3DS C 送信ボックス 受信ボックス 受信ボックス 送信ボックス 受信ボックス 送信ボックス 伝播可能回数=2 伝播可能回数=1 伝播可能回数=1 伝播可能回数=0 送信 送信 コピー

nn::cec::Message::NewMessage() では、主に送受信の条件を設定しました。ほかにも、おしらせに表示される情報(アイコン、説明文)や送信するデータを設定しなければなりません。この 2 つの情報は、引数 icon, iconSize, infoTextData, infoTextSize を持つ nn::cec::Message::NewMessage() のオーバーロードならば、新規作成時に設定することができます。 infoTextData に設定する説明文の言語は本体の言語設定に合わせても構いませんし、アプリケーション自身の言語設定に合わせても構いません。ただし、設定可能なテキストは 1 言語分だけであるため、送信者と受信者で言語設定が異なる場合に、受信者側の設定と異なる言語の説明文が表示される可能性があります。

おしらせに表示される情報は拡張ヘッダ情報として、nn::cec::Message::SetExHeader() で設定します。

コード 4-12. 拡張ヘッダ情報の設定
nn::Result nn::cec::Message::SetExHeader(
        const u32 exhType, const size_t exhLen, const void* exhBody);

exhType には、拡張ヘッダ情報の種類を指定します。

表 4-7. 拡張ヘッダの種類

種類

説明

MESSAGE_EXHEADER_TYPE_ICON

アイコン。サイズが 40×40 ピクセル、ピクセルフォーマットが RGB565 の PICA ネイティブフォーマットのテクスチャ。

MESSAGE_EXHEADER_TYPE_INFO

説明文。最大幅の内蔵フォント(日米欧リージョンは "%"、そのほかのリージョンはひらがな、漢字、ハングルなど)16 文字分の幅で 2 行まで表示。改行と終端文字を含めて最大 128 文字。文字コードは UTF16-LE。

exhBody exhLen には、拡張ヘッダ情報のデータとそのサイズを指定します。

アイコンのデータは nn::cec::Message::SetIcon() の呼び出しで、説明文のデータは nn::cec::Message::SetInfoText() の呼び出しでも設定することができます。

すれちがい通信で送信したいデータは nn::cec::Message::SetMessageBody() で設定します。

コード 4-13. 送信データの設定
nn::Result nn::cec::Message::SetMessageBody(
                        const void* dataBody, const size_t size);

dataBody size には、送信したいデータのデータとそのサイズを指定します。サイズは nn::cec::Message::MESSAGE_BODY_UNITSIZE ( 4 ) の倍数でなければなりません。

送信条件などのヘッダ情報、拡張ヘッダ情報、送信データの合計は、すれちがいボックスの作成時に指定したすれちがいデータの最大サイズ(デフォルトは 100 KByte)以内でなければなりません。送信データの最大サイズは nn::cec::Message::MESSAGE_BODY_SIZE_MAX(96 KByte)です。ただし、すれちがいデータのサイズが大きくなると、すれちがいボックス間での送信順で後回しになる、送信にかかる時間が長くなるなど、通信の成功率が下がる可能性があります。

受信のみのすれちがいデータには、拡張ヘッダ情報と送信データを設定する必要はありません。

すれちがいデータにはほかにも、nn::cec::Message::SetTag() で任意の 16 ビットのデータを設定することができます。すれちがいデータを読み込まずにヘッダ情報へのアクセスで取得できますので、データ種別の判定など、自由に使用してください。

4.1.4.2. 送信ボックスへの登録

すれちがいデータをすれちがい通信で送受信するためには、nn::cec::MessageBox クラスのメンバ関数 WriteMessage() で送信ボックスに保存する必要があります。

コード 4-14. ボックスへのすれちがいデータの保存
nn::Result nn::cec::MessageBox::WriteMessage(const nn::cec::Message& cecMessage,
                        const CecBoxType boxType, MessageId& messageIdOut);

cecMessage には、作成したすれちがいデータを示す nn::cec::Message クラスのインスタンスを指定します。

boxType には通常、送信ボックス(CEC_BOXTYPE_OUTBOX)を指定します。アプリケーションで受信ボックスを指定するような操作が必要になることはまずありません。

messageIdOut には、この関数の呼び出しですれちがいデータが保存されたときに、すれちがいデータを識別するためのメッセージ ID が書き込まれます。

すれちがいデータの保存時に EULA の同意チェックが行われますので、事前に FS ライブラリの初期化が必要です。すれちがいデータの保存時に、ユーザーが利用規約に同意していない場合やアプリケーションにアイコンファイルが設定されていない場合は EULA 非同意(ResultNotAgreeEula)のエラーが、ペアレンタルコントロールですれちがい通信が制限されている場合はペアレンタルコントロールによる制限(ResultParentalControlCec)のエラーが返されます。

補足:

すれちがいデータの最大サイズは 100 KByte 固定となりました。そのため、データのサイズが 100 KByte を超えなければ、サイズ超過(ResultMessTooLarge)のエラーは返されません。

送信ボックスへの登録順と処理の順番について

すれちがいボックス内では、送信ボックスに登録された順番でリストが作成されます。基本的に、このリストの先頭のデータが処理されますが、先頭のデータの送信可能回数が 0 になっていた場合は、リストの順番で次にくるデータが処理の対象となります。処理の対象となったデータの送受信モードにより、実際に送受信が行われるかどうかが決定されます。そのため、送受信モードが「受信のみ」に設定されているデータがグループに含まれていると、通信相手にそのデータが送信されてしまいます。同じ送受信モードのデータだけでグループ化されるように、データの置き方を考慮してください。

処理対象のデータのグループ ID が 0 以外に設定されていると、リスト内に同じグループのデータがあればリストの末尾に到達するか、データの合計サイズが 1 回の送信で送信可能な最大サイズになるまでのデータが一緒に処理されます。先頭と同じグループのデータはリストの末尾に回され、リストの先頭のデータが入れ替わります。このとき、処理されなかったデータを含めて、処理されたデータと同じグループのデータすべての送信可能回数が 1 減少します。

処理対象のデータのグループ ID が 0 に設定されている場合はそのデータのみが処理されます。ほかにグループ ID が 0 に設定されているデータが存在していてもグループとはみなされません。処理されたデータのみがリストの末尾に回され、送信可能回数が 1 減少します。

新たにすれちがいデータを登録した場合は、リストの末尾に追加されます。また、受信したデータの中に減少後の伝播可能回数が 1 以上のデータが存在した場合は、そのデータがリストの末尾(送信ボックス)に追加され、以降のすれちがい通信時に別の本体から来たデータを送信することになります。

図 4-3. グループの設定と処理の順番

Group 1 SendCount=5 60 KByte Group 1 SendCount=5 30 KByte Group 1 SendCount=5 20 KByte Group 0 SendCount=5 10 KByte Group 0 SendCount=5 15 KByte ↓リスト の先頭 Group 1 SendCount=4 60 KByte Group 1 SendCount=4 30 KByte Group 1 SendCount=4 20 KByte Group 0 SendCount=5 10 KByte Group 0 SendCount=5 15 KByte 先頭と同じグループだったが、合計が送信可能データサイズを超えるために送信されなかった。しかし、同じグループだったので送信可能回数は減少する。 先頭と同じグループなのでグルーピングされる。 グループ 0 はグルーピングされない。 ↓リストの先頭 先頭と同じグループのデータはリストの末尾へ。

4.1.4.3. 情報の取得

受信ボックスを走査して取得した受信データにアクセスするために、nn::cec::Message クラスを介して必要な情報を取得します。

コード 4-15. 情報の取得
u32 nn::cec::Message::GetBodySize() const;
u32 nn::cec::Message::GetMessageBody(void* dataBody, size_t size) const;
MessageId nn::cec::Message::GetMessageId_Pair(MessageId* messIdPair) const;
u16 nn::cec::Message::GetTag() const;

すれちがいデータに設定された送信データを取得するには、メンバ関数の GetMessageBody() を呼び出します。

dataBody size には、送信データを格納するバッファとそのサイズを指定します。必要なバッファのサイズは GetBodySize() で取得してください。

すれちがいデータの送受信モードに「交換」(CEC_SENDMODE_EXCHANGE)が設定されていた場合は、GetMessageId_Pair() で交換相手となった送信データのメッセージ ID を取得してアクセスしてください。

GetTag() はすれちがいデータに設定されたタグを取得することができます。

4.1.4.4. 削除

送受信されたすれちがいデータは自動的に削除されることはありません。送信ボックス、受信ボックスともに保存可能な容量やデータ数には限りがありますので、送信可能回数が 0 になってしまった送信データや、読み込んでアプリケーションに反映し終わった受信データなど、不要になったすれちがいデータはアプリケーションで削除しなければなりません。すれちがいデータの削除は nn::cec::MessageBox クラスのメンバ関数 DeleteMessage() で行います。すれちがいボックス内のすべてのすれちがいデータを削除する場合は、nn::cec::MessageBox クラスのメンバ関数 DeleteAllMessages() を呼び出してください。

コード 4-16. すれちがいデータの削除
nn::Result nn::cec::MessageBox::DeleteMessage(
                const CecBoxType boxType, const MessageId& messageId);
nn::Result nn::cec::MessageBox::DeleteAllMessages(const CecBoxType boxType);

削除するすれちがいデータのメッセージ ID と、保存されているボックスの種類を messageIdboxType で指定してください。

すれちがいボックス内に 2 つ以上のすれちがいデータが存在し、そのすべてを削除する場合、DeleteMessage() で個別に削除するよりも、DeleteAllMessages() で削除する方が処理にかかる時間が短くなります。

4.1.4.5. 情報の更新

すれちがいボックス内のすれちがいデータの情報は nn::cec::Message クラスを介して更新することができます。

nn::cec::MessageBox クラスのメンバ関数 ReadMessage() で更新対象のすれちがいデータを読み込み、 取得できた nn::cec::Message クラスのインスタンスから更新する属性値を設定した後、そのインスタンスを nn::cec::MessageBox クラスのメンバ関数 WriteMessage() ですれちがいボックスに書き込むことで情報が更新されます。

書き込み後に nn::cec::MessageBox クラスのメンバ関数 CommitMessageBox() または CloseMessageBox() を実行することで、 更新した情報がすれちがいボックス内に確実にコミットされます。

以下に挙げるようなメンバ関数を使用することで、すれちがいデータの属性値が更新できます。

コード 4-17. すれちがいデータの設定
nn::Result nn::cec::Message::SetGroupID(u32 groupId);
nn::Result nn::cec::Message::SetMessageTypeFlag(MessageTypeFlag messTypeFlag);
nn::Result nn::cec::Message::SetSendMode(SendMode sendMode);
void nn::cec::Message::SetTag(u16 tag);
補足:

 すれちがいボックス内から取得した nn::cec::Message オブジェクトの一部属性(拡張ヘッダ情報など)は更新しようとしても編集不可(ResultNotAuthorized)のエラーが返されます。

 詳細は各属性の設定関数リファレンスを参照してください。

 

 

4.1.5. ヘッダ情報へのアクセス

nn::cec::MessageBox クラスでは、すれちがいデータのヘッダ情報にだけアクセスし、実際にデータを開かなくても、ある程度の情報を引き出すことができます。ただし、送信データや拡張ヘッダに設定されている情報にはアクセスすることができません。

すれちがいデータの情報のうち、すれちがいデータの総サイズなどを以下のメンバ関数で取得することができます。

コード 4-18. すれちがいデータへのアクセス
u32 nn::cec::MessageBox::GetMessageSize(
                        const CecBoxType boxType, const u32 messIndex) const;
u32 nn::cec::MessageBox::GetMessageBodySize(
                        const CecBoxType boxType, const u32 messIndex) const;
u32 nn::cec::MessageBox::GetMessageGroupId(
                        const CecBoxType boxType, const u32 messIndex) const;
u32 nn::cec::MessageBox::GetMessageSessionId(
                        const CecBoxType boxType, const u32 messIndex) const;
u8 nn::cec::MessageBox::GetMessageTypeFlag(
                        const CecBoxType boxType, const u32 messIndex) const;
u8 nn::cec::MessageBox::GetMessageSendMode(
                        const CecBoxType boxType, const u32 messIndex) const;
u8 nn::cec::MessageBox::GetMessageSendCount(
                        const CecBoxType boxType, const u32 messIndex) const;
u8 nn::cec::MessageBox::GetMessagePropagationCount(
                        const CecBoxType boxType, const u32 messIndex) const;
bit16 nn::cec::MessageBox::GetMessageTag(
                        const CecBoxType boxType, const u32 messIndex) const;
nn::fnd::DateTimeParameters nn::cec::MessageBox::GetMessageSendDate(
                        const CecBoxType boxType, const u32 messIndex) const;
nn::fnd::DateTimeParameters nn::cec::MessageBox::GetMessageRecvDate(
                        const CecBoxType boxType, const u32 messIndex) const;
nn::fnd::DateTimeParameters nn::cec::MessageBox::GetMessageCreateDate(
                        const CecBoxType boxType, const u32 messIndex) const;
MessageId nn::cec::MessageBox::GetMessageIdPair(
                        const CecBoxType boxType, const u32 messIndex) const;
nn::Result nn::cec::MessageBox::GetMessageIdPair(
        MessageId* messId, const CecBoxType boxType, const u32 messIndex) const;
MessageId nn::cec::MessageBox::GetMessageId(
                        const CecBoxType boxType, const u32 messIndex) const;
nn::Result nn::cec::MessageBox::GetMessageId(
        MessageId* messId, const CecBoxType boxType, const u32 messIndex) const;
u32 nn::cec::MessageBox::GetMessageIndex(
                        CecBoxType boxType, const MessageId& messId) const;
u32 nn::cec::MessageBox::GetMessageIndex(CecBoxType boxType, u8* messId) const;

4.1.6. 通信発生の通知

すれちがいデータの通信が行われたことを示す通知をイベントで受け取ることができます。また、最新の受信データについての情報を取得することもできます。

コード 4-19. 通信発生の通知を受け取るイベント、受信データの取得
nn::Result nn::cec::GetCecEvent(nn::os::Event* event);
nn::Result nn::cec::GetCecInfoBuffer(
                        u32 cecTitleId, u8 pCecInfoBuffer[], size_t size);

event に指定する nn::os::Event クラスのインスタンスには、初期化されていないものを指定してください。このイベントは、イベントを取得したアプリケーションが登録したすれちがいボックスにすれちがいデータを受信したときだけでなく、ほかのアプリケーションが登録したすれちがいボックスに受信したときや、すれちがいデータの送信が行われたときにもシグナル状態になることに注意してください。このイベントによる通知は、アプリケーションの動作中にバックグラウンドで行われたすれちがい通信で、すれちがいデータを受信したことを即座に知りたい場合に利用します。

最新の受信データの情報は nn::cec::GetCecInfoBuffer() で取得することができます。この関数は、直前に行われたすれちがい通信についての情報を pCecInfoBuffer に指定されたバッファに格納します。通常、上記のイベントですれちがい通信が行われたことを検知したあと、自分のすれちがいボックスにすれちがいデータが受信されたかどうかを調べるために、cecTitleId を指定してこの関数を呼び出します。また、この関数は、すれちがい通信のデーモンプロセスを停止状態にしなくても呼び出すことができます。

格納される情報は nn::cec::CecNotificationData 構造体ですので、pCecInfoBuffersize には構造体を格納できるだけのバッファとサイズを指定してください。

nn::cec::CecNotificationData 構造体の num メンバには構造体に格納された情報の数が、count メンバには本体が起動されてから発生したすれちがい通信の回数が格納されています。param メンバには、先頭から num 番目までの nn::cec::CecNotificationParam 構造体にすれちがいデータについての情報が格納されています。

nn::cec::CecNotificationParam 構造体の recvDate メンバには、すれちがいデータを受信した日時が格納されています。cecTitleId メンバにはすれちがいボックスのすれちがい通信 ID が、messageId メンバにはすれちがいデータのメッセージ ID が格納されています。

補足:

CecNotificationParam 構造体の messageId メンバには、そのすれちがいボックスに受信した最新のすれちがいデータのメッセージ ID が格納されます。ただし、グルーピングされたすれちがいデータを受け取った場合でも、1 件分の情報しか格納されません。

実際に受信したすれちがいデータにアクセスするには、すれちがいボックスのオープンなどの処理を行う必要があります。

4.1.7. すれちがい通信での送受信の例

すれちがいボックスに複数のアプリケーションからすれちがいデータを登録している状態で、ほかのユーザーと 2 回すれちがったときの送受信の例を以下に示します。

図 4-4. すれちがいデータの送受信の例

最初に出会ったユーザー 自分 2番目に出会ったユーザー アプリケーション B 受信ボックス 送信ボックス アプリケーション C 受信ボックス 送信ボックス アプリケーション A 受信ボックス 送信ボックス A1 * 2 C1 * 3 アプリケーション B 送信ボックス アプリケーション D 受信ボックス 送信ボックス アプリケーション A 受信ボックス 送信ボックス A1 * 2 B1-a * 2 B1-b * 2 B1-c * 2 アプリケーション B 受信ボックス 送信ボックス アプリケーション D 受信ボックス 送信ボックス アプリケーション A 受信ボックス 送信ボックス A1 D1 * 1 D2 * 2 A1 A1 A1 A1 * 2 B1-a B1-b B1-c B1-a B1-b D2 受信ボックス B1-a * 0 B1-b * 0 B1-c * 0 B1-a * 2 B1-b * 2 B1-c * 2 B1-a B1-b B1-c D1 * 2 D2 * 2 D1 D3 * 2 D3 * 2 B1-c

青い線が送信、赤い線が受信を示し、黄色いタグがすれちがいデータを示します。すれちがいデータの送信モードはすべて送受信に設定されているとします。すれちがいデータの表記では、先頭の英字に続く数字がグループを示し、ハイフンに続く英字が同じグループ内の異なるデータを、アスタリスクに続く数字が送信可能回数を示します。送信可能回数は、自分が登録時の値、出会ったユーザーがすれちがった時点での値です。

アプリケーション A は、すれちがったユーザーの両方が登録していたので、送信と受信が 2 回行われました。

アプリケーション B は、すれちがったユーザーの両方が登録していましたが、最初にすれちがったユーザーは送信可能回数がすでに 0 になっていたため、自分からの送信だけが行われました。2 番目に出会ったユーザーとは、双方の送信可能回数が 0 ではなかったので、送信と受信を行いました。

アプリケーション C は、最初に出会ったユーザーだけが登録していたので、送信も受信も行われませんでした。

アプリケーション D は、2 番目に出会ったユーザーと送信と受信を行いましたが、自分が送信するデータの順番が "D1" で、相手が送信するデータの順番が "D2" だったため、互いに異なるデータを受信しました。

4.1.8. 効果的な設定

3DS のすれちがい通信はバックグラウンド処理で動作するため、動作中のアプリケーション以外にもすれちがいの機会が与えられることになります。しかし、すれちがい通信は複雑な設定項目が多く、効果的にすれちがいデータの送受信が行われるようにするには工夫が必要です。ここでは、すれちがい通信の機会が訪れたときにデータの送受信につながるような設定や、設定項目の有効な利用方法について説明します。

また、ライブラリには多様な目的のための機能が用意されています。開発者は、それらの機能を利用すると、データの送受信がどの程度の頻度でどのくらいのユーザー間で発生するかを常に意識する必要があります。

4.1.8.1. 推奨設定

ユーザーがすれちがい通信でやり取りしたいデータを選択し、自ら登録するような一般的なケースを取り上げます。この場合、ユーザーにとってすれちがい通信の結果が分かりやすく、送受信がもっとも活発に行われるような設定として、以下の設定を推奨します。

(1) 登録するデータ数は 1 つだけ。

ユーザーにとっても分かりやすく、すれちがい通信の仕様が持つ、多くの制約を回避することができます。
見かけ上複数のデータを登録させたい場合は、1 つのデータに統合して登録してください。また、グループ化を利用する方法もあります。グループ化せずに複数のデータを登録すると、一度のすれちがい通信で 1 つだけしか送受信されなくなってしまいます。

(2) 送受信モードは「送受信」。

「送受信」は互いの受信ボックスの状態に関わらず送受信が発生するため、機会損失がありません。「交換」は「送受信」とほぼ同じ状況で送受信が発生しますが、片方の受信ボックスに空きがない場合など、両方が通信に成功しないとすれちがい通信が成立しません。つまり、受信ボックスに空きがあったユーザーはデータを受け取る機会を損失してしまいます。
「送信のみ」と「受信のみ」は、同じ送受信モードの相手とすれ違ってもすれちがい通信が発生しません。ユーザーがどちらかを選択できるようにした場合でも、全体的に見たときにどちらかに大きく偏る可能性があり、結果として送受信の機会を損失してしまいます。

(3) 送信可能回数は無制限。

常にすれちがい通信を行おうとするため、機会損失がありません。また、データの登録を解除するまで有効になることは、ユーザーにとっても分かりやすい設定方法です。
無制限にしなかった場合は、いつの間にか送信可能回数が 0 になり、気付かないうちに配布を終了してしまいます。終了したことはユーザーに分かりにくく、送信可能回数が 0 のユーザーとすれ違ったユーザーはデータを受け取る機会を損失してしまいます。

(4) 送信対象はフレンドと非フレンドの両方。

送信対象を限定しないため、機会損失がありません。
非フレンドに限定すると、顔を合わせる機会が多いと思われる、フレンド関係にあるユーザーとの送受信が行われなくなってしまいます。逆にフレンドに限定すると、たくさんの一般のユーザーとのすれちがい通信が行われなくなってしまいます。

4.1.8.2. 複数のデータを登録する

一人の相手に対して複数のデータを一度に送りたい場合は、グループ化よりもデータを 1 つにパックすることをお勧めします。データをまとめることで、グループ化では無駄となるヘッダ情報を削減でき、全体のデータ量を抑えることができます。

複数のデータを登録する際に一番気をつけなければならないことは、3DS のすれちがい通信では一度に 1 つのデータしか送信されず、先頭のデータが送信されるまで後続のデータが送信されることがないということです。つまり、送受信される機会の極端に低いデータが先頭にある場合は、後続のデータに順番が回りにくくなってしまいます。

また、同じ相手とは最大で 8 時間、平均 4 時間経つまで再度すれちがい通信をしないため、同じ相手には先頭から 2 つ目以降のデータが連続して届きません。ユーザーが複数データを登録した場合、どちらが相手に届くかはどちらがそのとき先頭にあるかで決まるため、偶然の要素が大きくなります。そのため、ユーザーにとっては分かりにくくなる恐れがあります。

4.1.8.3. 送信対象の設定を利用する

すれちがいデータを送信する相手を限定する送信対象の設定ですが、グループ化と組み合わせることで、フレンドとそうでないユーザーとに異なるデータを送信することができます。

具体的な設定例としては、グループの先頭データは送信対象を限定しない共通部分とし、後続のデータにフレンド用と非フレンド用の 2 つのデータを加えます。すれちがい通信は送受信の判断を先頭のデータで行いますので、先頭に来るデータにはなるべく機会損失の少ない設定を行ってください。

4.1.8.4. 伝播可能回数の設定を利用する

伝播可能回数を複数回に設定したすれちがいデータは、遠くのユーザーが登録したものを、複数のユーザーを介して受信する可能性があるという面白みがあります。しかし、伝播の途中でデータが削除されたり、送信ボックスに空きがなかったり、すでに同じデータを受信していたりすると伝播が途絶えてしまいます。また、送信ボックスにほかユーザーのデータが登録されることで、送信の順番によってはユーザーが登録したデータがすぐには送信されない可能性があります。

伝播するデータは、運営者などの限られたユーザーから特別に配信したり、アプリケーションが低い確率で登録したりするスペシャルデータを特定の地域でのみ流通させたい場合などに利用することができます。

ユーザーが登録するすれちがいデータに紛れて配信する場合は特別な設定は必要ありません。グループ化すれば、ユーザーの登録したデータと一緒に送信することもできます。

ユーザーがすれちがいデータを登録せずに、伝播されてくるデータを待ち受ける場合、待ち受ける側はダミーデータを登録しなければならないことに注意してください。たとえば運営者からの特別配信を行う場合、運営者は「送信のみ、伝播可能回数 2 以上、送信可能回数 1」で設定し、待ち受ける側は「受信のみ、伝播可能回数 1、送信可能回数 1」に設定することで、運営者とすれちがったユーザーからほかのユーザーに伝播するデータを登録することができます。

4.2. いつの間に通信

いつの間に通信は BOSS(Background Online Service System の略)と呼ばれる機能を利用して行われる通信の総称です。アプリケーションは BOSS を利用することで、任天堂が提供するデータサーバー(以降、BOSSデータサーバーと呼びます)との通信処理をバックグラウンドで行わせることができます。

アプリケーションから BOSS を利用するには、行わせたい通信処理に関する情報のセットである「タスク」を BOSS に登録し、そのタスクを開始させます。タスクの設定には実行間隔や消尽回数(タスクの残り実行回数)といった情報があり、BOSS はそれらの情報をもとに、タスクを定期的にバックグラウンドで実行します。

補足:

ここでいう「バックグラウンド」とは、スリープ中や HOMEメニューの表示中を指します。つまり、アプリケーションが実行中ではない状態です。

図 4-5. BOSS の機能を利用したデータのダウンロード

BOSSデータサーバー BOSS インフラストラクチャ通信 データ (4)アプリケーション データサーバーからデータをダウンロード (1)タスクを登録して開始させる タスク タスク タスク タスク データサーバーへの接続と切断はBOSSが自動的に行います。 (2)タスクの実行が完了するまで待機 (6)データを取得 (5)タスクの実行 完了を通知 (3)実行時間を迎えたタスクを実行 待機中に、ほかの処理を行うことができます。

補足:

EULA(End User License Agreement)に同意していない場合、BOSS に登録されたタスクの実行は、EULA への同意によって実行が許可されるまで保留されます。

データをダウンロードするタスクについては、アプリケーションは新規データのダウンロード完了を、タスクの状態の確認やタスクの実行結果の確認、新着データイベントによる通知などで把握することができます。また、そのデータを BOSS 経由で読み込むことができます。
現在、以下のタスクが用意されています。

 

補足:

データアップロードタスク、DataStore アップロードタスク、DataStore ダウンロードタスクにつきましては、サーバー環境のセットアップが必要ですので、製品に利用する場合は弊社窓口までご相談ください。

タスクは基本的にバックグラウンドで実行されますが、登録済みのタスクをアプリケーションから任意のタイミングでただちに実行させることもできます。この場合、タスクによる通信処理はフォアグラウンドで実行されることになります。

また、これらのタスクをフォアグラウンドで実行(アプリケーションの実行中にタスクを登録し、ただちに実行)するために、専用のタスク登録関数(即時実行専用タスクとして登録する関数)も用意されています。

BOSS を利用した基本的なサービス例には、以下のものが考えられます。

  • バックグラウンドで、定期的におしらせや追加データを配信する(NADL タスク)
  • ニンテンドーゾーンを利用して、事業者ごとに異なるデータを配信する(NADL タスク)
  • ユーザー間のデータのやり取りをバックグラウンドで行う(DataStore アップロード/ダウンロードタスク)

4.2.1. すべてのタスクで共通する処理

ここでは、タスクの種類に関わらず共通する処理について説明します。

タスクを利用するにあたって、アプリケーションが行うべき処理の内容を以下に示します。

基本的にアプリケーションでは、上記の内容を上から順に処理することになります。

4.2.1.1. BOSS ライブラリの初期化

BOSS を利用するためには、nn::boss::Initialize() で BOSS ライブラリの初期化を行う必要があります。

コード 4-20. BOSS ライブラリの初期化
nn::Result nn::boss::Initialize(void);

初期化成功前に BOSS ライブラリの関数を呼び出すと、nn::boss::ResultIpcNotSessionInitialized が返されます。

4.2.1.2. タスクのプロパティ設定

タスクのプロパティは 3 つのクラスに分けて設定を行います。設定を行った 3 つのクラスを引数に nn::boss::RegisterTask() を呼び出すことで、1 つのタスクが BOSS に登録されます。

  • 基本情報(nn::boss::Task クラス)
  • 実行方針(nn::boss::TaskPolicy クラス)
  • 動作設定(nn::boss::TaskAction クラス)
図 4-6. タスクのプロパティ構成図

taskId execInterval execCount priority taskPermission rootCA clientCert infoAP httpRequestHeader lastModified nn::boss::Taskクラス nn::boss::TaskPolicyクラス nn::boss::TaskActionクラス nn::boss::TaskActionの派生クラス 初回のHTTPリクエストに付与する”If-Modified-Since”の値(未設定の場合、初回のHTTPリクエストに”If-Modified-Since”は付与されません) HTTPリクエストに付与するAP情報クエリの設定 タスクID(最長 7 文字の文字列) 実行間隔(単位は時間) 消尽回数 実行優先度(デフォルト以外に設定する場合は、弊社の許可が必要です) 実行許可条件 HTTPS通信用のCA証明書設定 HTTPS通信用のクライアント証明書設定 HTTPリクエストに付与する独自のヘッダ タスクの種類ごとに用意されています。 NADLタスク(nn::boss::NsaDownloadAction) データアップロードタスク(nn::boss::UploadAction) DataStoreアップロードタスク(nn::boss::DataStoreUploadAction) DataStoreダウンロードタスク(nn::boss::DataStoreDownloadAction)

実際にアプリケーションが使用しても構わないプロパティ値には制約があります。

下表は、NADL タスクを登録する際の制約をまとめたものです。

表 4-8. NADL タスクを登録する際のプロパティ値の制約

プロパティ

設定値に対する制約

taskId

弊社に申請したタスク ID を利用してください。

execInterval

1 時間~168 時間(1 週間)の間で設定してください。

デバッグ用途で提供している、秒単位の実行間隔を設定できる関数を使用している場合はロットチェックを通りません。

execCount

1 回~100 回の間で設定してください。

priority

デフォルトの設定(PRIORITY_MEDIUM)のままにしてください。

この設定値以外を設定する場合は、事前に弊社窓口までご相談ください。

taskPermission

デフォルトの設定(TASK_PERMISSION_IN_PARENTAL_CONTROL)のままにしてください。

nn::boss::TaskPolicy クラスの SetProperty 関数で再設定するときには、必ず TASK_PERMISSION_IN_PARENTAL_CONTROL を含めてください。

infoAP

通常のタスクでは、AP 情報を付与する設定にはしないでください。

AP 情報を付与することで、ニンテンドーゾーンの場所や店舗に応じた個別のデータを配信することができます。AP 情報を付与する NADL タスクを利用する場合は、企画検討の早い段階で弊社窓口までご相談ください。

基本情報(nn::boss::Task クラス)

タスクの基本情報を設定する nn::boss::Task クラスは、タスクの識別子(タスク ID)をプロパティに持っています。タスク ID の設定は、nn::boss::Task クラスのインスタンスを生成したあとの、Initialize() による初期化時に行います。

コード 4-21. nn::boss::Task クラスの初期化
class nn::boss::Task
{
    nn::Result Initialize(const char* pTaskId);
}

タスク ID は半角英数字(0~9、A~Z、a~z)とアンダーバー(_)およびハイフン(-)で構成された最長 7 文字(終端文字を含めると 8 文字)の文字列です。アプリケーションのタスクを一意に表す ID ですので、アプリケーションで登録する各タスクで一意となる値を指定してください。また、タスク ID を途中で変更することはできません。

注意:

タスク ID には、必ず弊社に申請したタスク ID を利用してください。申請されていないタスク ID が設定された場合はロットチェックを通りません。

実行方針(nn::boss::TaskPolicy クラス)

タスクの実行方針を設定する nn::boss::TaskPolicy クラスは、タスクの実行間隔や消尽回数をプロパティに持っています。これらのプロパティの設定は、nn::boss::TaskPolicy クラスのインスタンスを生成したあとの、Initialize() による初期化時に行います。初期化後に個別に変更することも可能です。実行間隔と消尽回数の変更については、「4.2.1.7. タスクの変更」を参照してください。

コード 4-22. nn::boss::TaskPolicy クラスの初期化
class nn::boss::TaskPolicy
{
    nn::Result Initialize(u32 interval, u32 count);
}

interval に実行間隔、count に消尽回数を指定します。実行間隔は 1 時間単位で 1~168 時間(7 日)、消尽回数は 1~100 回です。消尽回数に UNLIMITED_COUNT は設定しないでください。消尽回数はタスクが実行されるたびに 1 減少し、0 になるとそのタスクは実行されなくなります。

補足:

タスクが実行され、実行終了状態 (nn::boss::Task::GetState() で得られる タスクの状態nn::boss::TASK_DONEnn::boss::TASK_ERROR) になると、消尽回数が 1 差し引かれます。タスクの実行中に中断しても減少しません。

注意:

デバッグ用途のみですが、InitializeWithSecInterval() で実行間隔を秒単位で設定することができます。ただし、実行間隔を秒単位で登録している場合はロットチェックを通りません。

これまでに紹介したプロパティを含め、すべてのプロパティは SetProperty()GetProperty() で設定と取得を行うことができます。

コード 4-23. プロパティの設定、取得
class nn::boss::TaskPolicy
{
    nn::Result SetProperty(PropertyType type, void* pValue, size_t size);
    nn::Result GetProperty(PropertyType type, void* pValue, size_t size);
}

type に指定されたプロパティ識別子でアクセスするプロパティが決定します。pValuesize には、プロパティの値を格納する変数とそのバイトサイズ(プロパティの型により変化)を指定します。

表 4-9. nn::boss::TaskPolicy クラスで指定可能なプロパティ識別子

プロパティ識別子

プロパティ

TASK_EXEC_INTERVAL

u32

実行間隔(秒単位)

TASK_EXEC_COUNT

u32

消尽回数

TASK_PERMISSION

TaskPermission

※ 表外の説明を参照してください

プロパティ識別子の TASK_PERMISSION は、ペアレンタルコントロールで「他ユーザーとのインターネット通信」が制限されている場合の動作を制御するためにアクセスします。何も設定しない場合、ペアレンタルコントロールで「他ユーザーとのインターネット通信」が制限されていると、そのタスクは処理されません。

NADL タスクはユーザー間でデータ交換を行わないタスクですので、デフォルトで TASK_PERMISSION 識別子の値に TASK_PERMISSION_IN_PARENTAL_CONTROL を付加されています。そのためデフォルトのままであれば、ペアレンタルコントロールで「他ユーザーとのインターネット通信」が制限されていても、NADL タスクは処理されます。

注意:

NADL タスクでは、プロパティ識別子 TASK_PERMISSIONTASK_PERMISSION_IN_ PARENTAL_CONTROL に設定されていない場合はロットチェックを通りません。プロパティ識別子 TASK_PERMISSION の設定を変更するときは、TASK_PERMISSION_IN_PARENTAL_CONTROL を含める必要があります。

プロパティ識別子の TASK_PERMISSION では、EULA 非同意時の動作を設定することができますが、アプリケーションが登録するタスクでは EULA 非同意時の動作設定は行わないでください。動作設定を行わない場合、登録するタスクは EULA 非同意時には処理されないタスクとなります。

動作設定(nn::boss::TaskAction クラス)

タスクの動作に関するプロパティ設定は、タスクの種類に対応した nn::boss::TaskAction の派生クラスを介して行います。現在、以下のタスクが用意されています。

表 4-10. nn::boss::TaskAction の派生クラスと対応するタスクの種類

派生クラス

タスクの種類

nn::boss::NsaDownloadAction

Nintendo アーカイブダウンロードタスク(NADL タスク)

nn::boss::UploadAction

アップロードタスク

nn::boss::DataStoreUploadAction

DataStore アップロードタスク

nn::boss::DataStoreDownloadAction

DataStore ダウンロードタスク

それぞれのタスクの利用方法やプロパティ設定については、後述する各タスクの説明を参照してください。
アップロードタスクは弊社窓口にご相談いただいた際に、個別に提供しています。

4.2.1.3. タスクの動作設定

タスクの動作に関する設定を行う nn::boss::TaskAction クラスでは、サーバーへの接続時に付与する情報の設定や証明書の設定などを行うことができます。

コード 4-24. タスクの動作の設定関数
class nn::boss::TaskAction
{
    nn::Result SetApInfo(ApInfoType info);
    nn::Result SetCfgInfo(CfgInfoType info);
    nn::Result AddHeaderField(const char* pLabel, const char* pValue);
    nn::Result SetLastModifiedTime(const char* pLastModifiedTime);
    nn::Result SetRootCa(u32 inCaCertId);
    nn::Result SetClientCert(u32 inClientCertId);
}

nn::boss::TaskAction クラスのすべてのプロパティは、SetProperty()GetProperty() で設定と取得を行うことができます。関数の定義は nn::boss::TaskPolicy クラスでの定義と同じです。

AP 情報の付与

SetApInfo() を呼び出して、「HTTP クエリーへの AP(アクセスポイント)情報付与」を指定することができます。この指定により、ニンテンドーゾーンのアクセスポイントを経由してインフラストラクチャ通信が行われている場合に、HTTP リクエストのクエリとして付与される、ニンテンドーゾーン AP から BOSSデータサーバーに送られる AP 情報を選択することができます。付与されるクエリは、ニンテンドーゾーン経由の接続時にのみタスクの処理に変化を加えたい場合に利用することができます。たとえば、「ニンテンドーゾーン接続時のみ異なるデータをダウンロードする」といった使い方が想定されます。

注意:

AP 情報が付与されているとロットチェックを通りません。AP 情報を付与したタスクを利用する場合は、企画検討の早い段階で事前に弊社窓口までご相談ください。

表 4-11. HTTP リクエストに付与される AP 情報

クエリ

付与される情報

ap

AP 単位で識別可能な情報

apgroup

AP のグループ(企業など)単位で識別可能な情報

付与する AP 情報は、以下の定義から 1 つを選択して指定します。

表 4-12. HTTP リクエストに付与する AP 情報の指定

定義

説明

APINFOTYPE_AP

ap を付与する。
ニンテンドーゾーン事業者別配信を申請した場合に指定してください。

APINFOTYPE_APGROUP

通常は指定しないでください。
本体設定情報の付与

SetCfgInfo() を呼び出して、「HTTP クエリーへの本体設定情報付与」を指定することができます。この指定により、HTTP リクエストのクエリに本体情報を付与することができます。たとえば、「独自サーバーに接続された本体の言語設定によって異なるデータをダウンロードする」といった使い方が想定されます。

補足:

この機能は独自サーバーを使った BOSS サービス専用の機能です。任天堂サーバーを用いた BOSS サービスでは、本体設定情報を付与しても効果はありません。

付与された情報を使う独自サーバーを検討されている場合は、弊社窓口までご相談ください。

付与する本体情報の指定には、以下の定義を使用します。なお、複数の値を論理和で設定することで、複数の情報を同時に付与することができます。

表 4-13. HTTP リクエストに付与する本体設定情報の指定

定義

説明

CFGINFOTYPE_NONE

本体設定情報を付与しない。

CFGINFOTYPE_COUNTRY

本体設定情報の国コードを付与する。

CFGINFOTYPE_LANGUAGE

本体設定情報の言語コードを付与する。

HTTP リクエストヘッダの追加

AddHeaderField() を呼び出して、独自の HTTP リクエストヘッダを接続に含めることができます。追加することのできる HTTP リクエストヘッダのラベルは最長 32 文字、値は最長 256 文字、個数は最大 3 つです。

初回実行時の最終更新時刻の指定

SetLastModifiedTime() を呼び出して、タスクの初回実行時の "If-Modified-Since" ヘッダフィールドに入る値を文字列で指定することができます。この指定を行わなかった場合は、初回実行時に "If-Modified-Since" ヘッダフィールドが付与されません。2 回目以降の実行時には、前回実行時に取得したデータの最終更新時刻(HTTP レスポンスの "Last-Modified" フィールドの値)が "If-Modified-Since" ヘッダフィールドとして付与されます。タスクを実行した結果、未更新(サーバーから "304 Not Modified" のステータスが返された場合)のためにデータを取得しなかった場合、タスクの消尽回数は 1 減少します。

そのほかにも、機器内蔵の証明書に関するプロパティの設定関数(nn::boss::TaskAction クラスから継承)が利用可能です。

コード 4-25. 機器内蔵の証明書に関するプロパティの設定関数
class nn::boss::TaskAction
{
    nn::Result SetRootCa(u32 inCaCertId);
    nn::Result SetClientCert(u32 inClientCertId);
}

機器に内蔵されている証明書のうちのどれを使用するのかは、SetRootCa()SetClientCert() で設定します。ルート CA 証明書は 3 つまで、クライアント証明書は 1 つだけ設定することができます。

独自証明書の設定

タスク実行時の接続で独自のルート CA 証明書またはクライアント証明書を使用する場合、以下の関数で独自の証明書を設定することができます。独自の証明書を使用しない場合は呼び出す必要はありません。

コード 4-26. 独自証明書の設定
nn::Result nn::boss::RegisterPrivateRootCa(
                        const u8* pCertData, size_t certDataSize);
nn::Result nn::boss::RegisterPrivateClientCert(
                        const u8* pCertData, size_t certDataSize,
                        const u8* pPrivateKeyData, size_t privateKeyDataSize);

独自の証明書と機器に内蔵されている証明書は併用することができます。ルート CA 証明書、クライアント証明書ともに独自の証明書として設定できるのは 1 つだけです。重複して設定した場合は、最後に設定した証明書が有効になります。

TaskActionBase クラスの SetPrivateRootCa() または SetPrivateClientCert() を呼び出すことで、独自証明書を使用することができます。なお、ルート CA 証明書のみ内蔵の証明書と併用することができます。

補足:

現在、任天堂が提供する BOSSデータサーバー以外に接続するタスクの登録は許可されていません。

4.2.1.4. タスクの登録と実行

4 つのクラスに設定されたタスクのプロパティをもとに、nn::boss::RegisterTask() でタスクを登録します。

コード 4-27. タスクの登録
nn::Result nn::boss::RegisterTask(nn::boss::Task* pTask,
                                  nn::boss::TaskPolicy* pPolicy,
                                  nn::boss::TaskAction* pAction,
                                  nn::boss::TaskOption* pOption=NULL,
                                  u8 taskStep=DEFAULT_STEP_ID);
補足:

pOption taskStep は機能拡張用の引数のため、現状未対応です。指定しないでください。

登録されただけではタスクは実行されません。nn::boss::Task クラスの Start() または StartImmediate() で、BOSS に実行を指示する必要があります。

コード 4-28. タスクの実行、中止、完了待ち
class nn::boss::Task
{
    nn::Result Start(void);
    nn::Result StartBgImmediate(void);
    nn::Result StartImmediate(void);
    nn::Result Cancel(void);
    nn::Result WaitFinish(void);
    nn::Result WaitFinish(const nn::fnd::TimeSpan& timeout);
}

Start() で開始を指示されたタスクは、BOSS によるバックグラウンドでのタスク制御(スケジューリングと実行)の対象となります。バックグラウンドでタスクを実行する場合は、BOSS がインフラストラクチャ通信の接続と切断を自動的に行います。タスクの実行中に通信が切断された場合、そのタスクはレジューム状態となります。レジューム状態のタスクは、次回のインフラストラクチャ通信の接続時に、切断された時点の状態から実行を再開します。

Start() で開始が指示されているタスクに対しては、スケジューリングや実行の中止を Cancel() で指示することができます。中止後に Start() を呼び出した場合は、再度スケジューリングや実行の対象となります。

StartImmediate() はフォアグラウンドでタスクを実行するために用意されています。StartImmediate() で開始を指示されたタスクはすぐ実行に移ります。フォアグラウンド通信によるタスク実行となりますので、インフラストラクチャ通信の接続と切断はアプリケーションで行わなければなりません(BOSS は実施しません)。StartImmediate() でタスクを実行した場合は Start() によるタスクのバックグラウンド実行とは異なり、タスクの実行中に通信が切断された場合はエラーが返され、レジューム状態にはなりません。

Start() で開始を指示していたタスクを、次回の実行時刻を待たずにすぐに実行したい場合にも、StartImmediate() を使用することができます。ただし、この場合でもフォアグラウンドでタスクを実行することになりますので、インフラストラクチャ通信の接続と切断はアプリケーションで行わなければなりません。

補足:

タスクの実行中にほかのタスクが実行開始日時を迎えても、実行中のタスクが完了するまで実行されません。ただし、StartImmediate() でタスクを即時実行した場合、バックグラウンドで実行されていたタスクの実行は中止されますが、即時実行したタスクの完了後に再度実行されます。

注意:

Start() で開始を指示していたタスクを StartImmediate() で実行する際、そのタスクがちょうど実行中であった場合、タスクはリトライ状態に遷移してしまいます。通常、アプリケーションの動作中に BOSS はタスクを実行することがないため問題になりませんが、アプリケーションの動作中でも BOSS が動作できるように許可(NDM ライブラリで設定可能)している場合は、この点への配慮が必要になります。そのようなアプリケーションは、以下のような対応を行ってください。

  • そのタスクを Cancel() で中止してから、StartImmediate() で実行する。

バックグラウンドでのタスクの即時実行をデーモンに指示するには StartBgImmediate() を呼び出してください。この関数は、タスクの初回スケジュール実行を即時に行うための関数です。そのため、すでに Start()StartImmediate() によってスケジューリングの対象となっているタスクに対して呼び出しても、再び即時に実行されるようなことはありません。呼び出しのたびにタスクを即時に実行する場合は、StartImmediate() を使用してください。また、すでにスケジューリング対象となっているタスクに対し、再びバックグラウンドでのタスク即時実行を指示する場合は、そのタスクの実行を Cancel() で中止してから、StartBgImmediate() を呼び出してください。なお、StartImmediate() はほかのタスクの実行をキャンセルしますが、StartBgImmediate() ではほかのタスクが実行中であれば、そのタスクの実行が完了したあとにタスクが実行されます。

WaitFinish() はタスクの 1 回の実行が完了するのを待ちます。消尽回数が 1 回のタスクや、StartImmediate() で即時実行を指示したときなど、アプリケーションでタスクの完了を待つ場合に使用することができます。この関数を呼び出すと、タスクの実行が完了するタイミングまで制御が戻らないことに注意してください。WaitFinish() の実行には、専用のスレッドを用意するか、引数 timeout を持つオーバーロードを使用することを推奨します。オーバーロードでは、指定された時間内にタスクの 1 回の実行が完了しなかった場合に ResultWaitFinishTimeout を返し、アプリケーション側に制御が戻ります。

タスクの実行完了を待つ方法として、新しいデータの到着を利用することができます。新着データの確認には nn::boss::GetNewArrivalFlag()nn::boss::RegisterNewArrivalEvent() を利用してください。これらの関数の詳細については「4.2.2.6. 新着フラグのチェック、データ新着イベントによる待ち受け 」を参照してください。

ほかにも、nn::boss::GetState()nn::boss::GetStateDetail() を利用して、タスクの状態をポーリングする方法があります。

即時実行専用タスクの登録

即時実行のみ可能なタスクを登録する nn::boss::RegisterImmediateTask() が用意されています。即時実行専用タスクは必ずこの関数で登録し、フォアグラウンドで実行してください。1 つのアプリケーションが登録できるタスク数はガイドラインで制限されていますが、この関数で登録する即時実行専用タスクはそのタスク数にはカウントされません。

コード 4-29. 即時実行専用タスクの登録
nn::Result nn::boss::RegisterImmediateTask(
        nn::boss::Task* pTask, nn::boss::TaskAction* pAction,
        nn::boss::TaskPolicy* pPolicy=NULL, nn::boss::TaskOption* pOption=NULL,
        u8 taskStep=DEFAULT_STEP_ID);

pTask に指定するのは nn::boss:Task クラスではなく、そのサブクラスの nn::boss::FgOnlyTask クラスです。このクラスを使った場合、nn::boss::FG_ONLY_TASK_ID で定義された即時実行専用のタスク ID が自動的に指定されますので、タスク ID を指定する必要はありません。また、pPolicy で消尽回数や実行間隔を設定しても無効となります。

タスクの実行は nn::boss::FgOnlyTask::StartImmediate() で行わなければなりません。それ以外の関数でタスクを実行するとエラーが返されます。

登録できるタスクの最大数と自動登録解除

BOSS に登録することのできるタスクの数は最大で 127 個(即時実行専用タスクは含みません)です。この数はすべてのアプリケーション(内蔵アプリケーション含む)で登録されたタスクの総計の最大値です。なお、1 つのアプリケーションが登録できるタスクの最大数はガイドラインによって制限されています。登録されているタスクが最大数に達している状態で、アプリケーションが新たなタスクを登録しようとした場合、BOSS は「消尽回数が 0 のタスク」を自動的に登録解除して、新たなタスクの登録を受け容れます。登録しようとしているアプリケーションのタスクに限らず、すべてのタスクの中から該当するタスクを探して登録解除します。消尽回数が 0 のタスクが存在しない場合は、消尽回数と実行間隔の乗算結果がもっとも小さなタスクを登録解除し、新たなタスクの登録を受け付けます。

上記の自動登録解除により、アプリケーションを長期間起動しなかった場合は、以前登録したタスクが BOSS によって登録解除されている可能性があります。そのため、BOSS を利用するアプリケーションは、タスクが登録解除されているケースに対応する必要があります。たとえば、アプリケーション起動時にタスクが登録されているかを確認し、未登録の場合はユーザーにタスクを登録するかどうかを確認するような処理が必要となります。

4.2.1.5. タスクの情報

状態や実行結果などのタスクの情報は nn::boss::Task クラスのメンバ関数で取得することができます。

コード 4-30. タスクの情報の取得
class nn::boss::Task
{
    TaskServiceStatus GetServiceStatus(void);
    TaskStateCode     GetState(bool acknowledge=false, 
                               u32* pCount=NULL, u8* pStepID=NULL);
    u32               GetHttpStatusCode(u32* pCount=NULL, u8* pStepID=NULL);
    TaskResultCode    GetResult(u32* pCount=NULL, u8* pStepID=NULL);
    nn::Result        GetStateDetail(TaskStatus* pStatus, 
                                     bool acknowledge=false, u8* pStepID=NULL,
                                     u8 taskStep=CURRENT_STEP_ID);
    nn::Result        GetError(TaskError* pTaskError, u8* pStepID=NULL,
                               u8 taskStep=CURRENT_STEP_ID);
    nn::Result        GetInfo(TaskPolicy* pPolicy, TaskAction* pAction,
                              TaskOption* pOption, u8 taskStep=CURRENT_STEP_ID);
    nn::Result        GetActivePriority(TaskPriority* pPriority);
}

上記の関数のうち、引数に pCount がある関数は pCountNULL ではなく u32 型変数へのポインタを渡すとタスクの消尽回数を返します。pStepID を現在利用することはできません。

注意:

pCount に返されるタスクの消尽回数と取得したタスクの情報は、ライブラリ内でそれぞれ個別に取得されています。そのため、わずかにタイムラグが存在し、まったく同一の情報であるとは限りません。

GetServiceStatus() を呼び出すことで、タスクの接続先サーバーのサービスの状態(「サービスが利用可能」、「サービスが終了している」など)を取得することができます。サービスの状態はタスクが実行されたときにサーバー側から送信されます。

SERVICE_AVAILABLE が返されたときは、サービスは稼働中です。SERVICE_TERMINATED が返されたときは、サービスは終了しています。タスクがまだ実行されていないときや、ネットワークエラーで接続に失敗したときは、SERVICE_UNKNOWN が返されます。関数の呼び出し自体に失敗したときは GET_SERVICE_STATUS_ERROR が返されます。

注意:

サービス終了の判定は、タスク実行時のサーバーからのレスポンスにサービス終了を示すフラグ(サービス終了フラグ)が含まれているかどうかで行われます。サービス終了フラグはアプリケーションの開発者が BOSSデータサーバーを使って付与設定を行うことになります。

GetState() を呼び出すことで、タスクの状態を取得することができます。関数の処理自体に失敗したときは GET_TASK_STATE_ERROR が返されます。タスクの実行結果が確実に取得できるように、タスクの実行が終わってから次のタスク実行時間になるまでの間(つまり状態が TASK_WAITING_TIMER の間)、タスクの前回実行時の結果を保持しています。そのため、実際の状態が TASK_WAITING_TIMER であっても、GetState() は前回実行時の結果(TASK_DONE/TASK_ERROR/TASK_RETRY)を返します。

保持された結果ではなく、実際の状態を取得できるようにするためには、引数の acknowledgetrue を指定して GetState() を呼び出します。ただし、保持が解除されて実際の状態(TASK_WAITING_TIMER)が取得できるようになるのは、そのあとに GetState() でタスクの状態を取得したときです。

表 4-14. タスクの状態

定義

説明

TASK_STOPPED

停止中のため、タスクはスケジューリングされていません。

TASK_WAITING_TIMER

タスクの実行時刻を待っています。

TASK_WAITING

すでに実行時刻になっていますが、インフラストラクチャ通信の未接続や高い優先度を持った別のタスクの実行中などの理由で、実行を待っています。

TASK_RUNNING

タスクは実行中です。

TASK_PAUSED

タスクの実行を一時停止しています。

TASK_REGISTERED

タスクを登録した直後の状態(開始前)です。

TASK_DONE

タスクの実行が完了しました。

TASK_ERROR

タスクの実行でリトライ不可能なエラー(異常終了)が発生しました。

TASK_RETRY

前回の実行で通信が中断されたため、リトライ(レジューム)を待っています。

GetHttpStatusCode() を呼び出すことで、タスク実行時の HTTP ステータスコードを取得することができます。関数の処理自体に失敗したときは U32_CANNOT_GET_DATA が返されます。

GetResult() を呼び出すことで、タスクの実行結果を取得することができます。関数の処理自体に失敗したときは GET_TASK_RESULT_ERROR が返されます。返り値の定義は nn/boss/boss_Const.h の TaskResultCode 列挙子を参照してください。BOSS ライブラリの中で呼び出されている、ほかのライブラリの関数がエラーを返した場合の返り値は、TaskResultCode 列挙子に存在する値よりも大きい値(UNKNOWN_ERROR +「エラーが発生したモジュールのモジュール ID」。モジュール ID の定義については nn/Result.h を参照)になります。また、以下のような原因によって、タスク実行時のファイル操作でエラーが発生した場合は FS_UNKNOWN_ERROR が返されます。

  • SD カードの空き容量が無い
  • SD カードが Write プロテクトされている
  • SD カードが入っていない。
  • タスク登録時の SD カードとは別の SD カードが入っているなどで、対象タスクのデータを書き込んだり、読み込んだりするための BOSS ストレージ(後述)がない。
  • SD カードが壊れている。

GetStateDetail() を呼び出すことで、タスクの状態を取得することができます。GetState() との違いは pStatus に返される nn::boss::TaskStatus クラスのインスタンスの GetProperty() を介して詳細な情報を取得できることです。

表 4-15. nn::boss::TaskStatus クラスで指定可能なプロパティ識別子

プロパティ識別子

プロパティ

TASK_STATE_CODE

TaskStateCode

タスクの状態(GetState() と同じ)

TASK_RESULT_CODE

TaskResultCode

実行結果(GetResult() と同じ)

TASK_COMM_ERROR_CODE

u32

通信エラーコード(GetHttpStatusCode() と同じ)

TASK_CURRENT_PRIORITY

u32

現在の実行優先度

TASK_EXECUTE_COUNT

u32

現在の消尽回数

(最新の値が反映されるまでに時間がかかりますので、nn::boss::TaskPolicy クラスで同名のプロパティを取得することを推奨します)

TASK_PENDING_TIME

u32

タスク実行の保留時間(秒)

TASK_START_TIME

s64

タスク開始日時(2000/1/1 基点。秒単位)

TASK_PROGRESS

u32

ダウンロード済みデータのサイズ

TASK_DATA_SIZE

u32

ダウンロードデータのサイズ(Content-Length)

TASK_SERVICE_STATUS

TaskServiceStatus

サービス利用可能ステータス

TASK_SERVICE_TERMINATED

bool

サービス終了フラグ

TASK_LAST_MODIFIED_TIME

char[MAX_LASTMODIFIED_LENGTH]

ダウンロードデータの最終更新日時(HTTP レスポンスの "Last-Modified" フィールドの値)

GetError() を呼び出すことで、タスク実行時のエラーを取得することができます。GetCommErrorCode()GetResult() との違いは pTaskError に返される nn::boss::TaskError クラスの GetProperty() を介して詳細な情報を取得できることです。

表 4-16. nn::boss::TaskError クラスで指定可能なプロパティ識別子

プロパティ識別子

プロパティ

TASK_ERROR_RESULT_CODE

TaskResultCode

実行結果(エラーのみ。GetResult() と同じ)

TASK_ERROR_CODE

bit32

タスクの実行でエラーとなった原因

TASK_ERROR_MESSAGE

char[MAX_ERROR_MESSAGE]

タスク実行時の通信エラーメッセージ

GetActivePriority() を呼び出すことで、タスクの現在の実行優先度を取得することができます。

4.2.1.6. タスク一覧の取得

アプリケーションで登録しているタスクの一覧を取得するには nn::boss::GetTaskIdList() を呼び出してください。

コード 4-31. タスク一覧の取得
nn::Result nn::boss::GetTaskIdList(nn::boss::TaskIdList* pTaskIdList);

nn::boss::GetTaskIdList() の引数 pTaskIdList には、nn::boss::TaskIdList クラスのインスタンス(アプリケーションで生成)へのポインタを渡します。インスタンスのサイズは 1 KByte 程度ありますので、インスタンスの生成時にはスタックやヒープのサイズに配慮が必要です。nn::boss::TaskIdList クラスは以下のように定義されています。

コード 4-32. nn::boss::TaskIdList クラス
class nn::boss::TaskIdList
{
    explicit TaskIdList(void);
    virtual ~TaskIdList(void);
    u16 GetSize(void);
    char* GetTaskId(u16 index);
}

一覧に格納されたタスクの数は GetSize() で取得することができます。GetTaskId() では、一覧に格納されたタスクのタスク ID をインデックス指定で取得することができます。範囲外のインデックスを指定した場合は NULL が返されます。

補足:

現在、nn::boss::GetStepIdList() を使用することはできません。

4.2.1.7. タスクの変更

nn::boss::ReconfigureTask() を呼び出してタスクのプロパティ設定を変更することができます。変更することができるのは nn::boss::TaskPolicy で設定可能なプロパティのみです。ステップ ID は指定しないでください。

コード 4-33. タスクの変更
nn::Result nn::boss::ReconfigureTask(nn::boss::Task* pTask,
                                     nn::boss::TaskPolicy* pPolicy,
                                     u8 taskStep=DEFAULT_STEP_ID);

消尽回数と実行間隔は nn::boss::Task クラスのメンバ関数でも変更することができます。

コード 4-34. nn::boss::Task クラスでのプロパティ設定
class nn::boss::Task
{
    nn::Result UpdateInterval(u32 interval);
    nn::Result UpdateIntervalWithSec(u32 intervalSec);
    nn::Result UpdateCount(u32 count);
    u32 GetInterval(void);
    u32 GetIntervalSec(void);
    u32 GetCount(void);
}

UpdateIntervalWithSec() では秒単位で実行間隔を指定することができますが、この関数はデバッグ用途でのみ利用可能です。

4.2.1.8. タスクの登録解除

nn::boss::UnregisterTask() を呼び出してタスクの登録を解除することができます。

コード 4-35. タスクの登録解除
nn::Result nn::boss::UnregisterTask(nn::boss::Task* pTask,
                                    u8 taskStep=DEFAULT_STEP_ID);

タスクの登録が解除されるときに、BOSS ストレージ内に作成された、そのタスク用の作業用ファイルが削除されます。BOSS ストレージが存在する SD カードが抜かれているなど、作業用ファイルの削除に失敗した場合はエラー(nn::boss::ResultFileAccess)が返されますが、タスクの登録解除には成功しています。作業用ファイルは拡張セーブデータが削除されるタイミングでも削除されますので、このエラーのハンドリングは必須ではありません。作業用ファイルは直接削除することはできませんが、再度登録した同じタスク ID のタスクの登録解除で作業用ファイルを削除することはできます。

補足:

現在、引数 taskStep は使用されません。この関数を呼び出すときは、何も指定しないでください。

4.2.1.9. BOSS ライブラリの終了

BOSS の利用が終了した際には、必ず nn::boss::Finalize() を呼び出して BOSS ライブラリの終了処理を行ってください。

コード 4-36. BOSS ライブラリの終了
nn::Result nn::boss::Finalize(void);

4.2.1.10. BOSS ライブラリのエラーハンドリング

BOSS ライブラリのエラーハンドリング方法は大きく分けて4種類あります。

  • プログラミングが原因のエラー
    BOSS ライブラリの使い方が原因となるアサート級のエラーです。アプリケーションで必ず修正してください。
    nn::boss::ResultInvalidTaskIdnn::boss::ResultIpcNotSessionInitializednn::boss::ResultStorageNotFound などがこれにあたります。
    上記は一例です。他の該当リザルトは API リファレンスで確認してください。
  • API ごとに独自にハンドリングすべきエラー
    nn::boss::ResultNsDataListSizeShortagenn::boss::ResultNsDataListUpdatednn::boss::ResultTaskIdAlreadyExist などがこれにあたります。
    上記は一例です。他の該当リザルトや対応についての詳細は API リファレンスで確認してください。
  • ファイルアクセスエラー
    ファイルシステムのエラーにより BOSS ライブラリの API が返す可能性があります。nn::boss::ResultFileAccess のみが該当します。
    nn::boss::TaskResultCodenn::boss::FS_UNKNOWN_ERROR を指定して nn::boss::GetErrorCode() でエラーコードを取得し、エラー・EULA アプレットでエラーを表示してください。
  • 上記以外のエラー
    予期せぬエラーです。
    nn::boss::TaskResultCodenn::boss::SEVERE_ERROR を指定して nn::boss::GetErrorCode() でエラーコードを取得し、エラー・EULA アプレットでエラーを表示してください。

nn::boss::GetNsDataIdList() のエラーハンドリング例を次に示します。
プログラミングが原因のエラー、API ごとに独自にハンドリングすべきエラーについては、API ごとに異なるので必ず API リファレンスで確認してください。

コード 4-37. nn::boss::GetNsDataIdList() のエラーハンドリング例
static const u32 MAX_DATA_ID = 32;
u32 idBuf[MAX_DATA_ID];// シリアル ID 格納用バッファ。アプリケーションで用意します。大きなバッファを用意すれば、一括で多数のシリアル ID を取得できます。
nn::boss::NsDataIdList serialIdList(idBuf, MAX_DATA_ID);
 
do
{
    nn::Result result = nn::boss::GetNsDataIdList(nn::boss::DATA_TYPE_ALL, &serialIdList);
    if(result.IsFailure())
    {
        if(result == nn::boss::ResultStorageNotFound() ||
                result == nn::boss::ResultInvalidNsDataIdList() ||
                result == nn::boss::ResultIpcNotSessionInitialized())
        {
            // プログラミングが原因のエラー( API ごとに異なるので詳細は API リファレンスを参照してください)
 
            // BOSS ストレージが登録されていない、NS データリスト情報のポインタが NULL、BOSS ライブラリが初期化されていないなど、アプリケーション側の実装ミスによるエラーです。
            // アサート級のエラーなので開発段階で必ず修正してください。
        }
        else if(result == nn::boss::ResultNsDataListSizeShortage())
        {
            // API ごとに独自にハンドリングすべきエラーのハンドリング( API ごとに異なるので詳細は API リファレンスを参照してください)
 
            // ID 一覧取得に成功しているが、NsDataIdList にすべての ID を格納できなかったケース。
            // 必要なら nn::boss::GetNsDataIdList をもう一度実行してください。
            // この例では do-while を抜けずに再度 GetNsDataIdList を実行するようにしています。
        }
        else if(result == nn::boss::ResultNsDataListUpdated())
        {
            // API ごとに独自にハンドリングすべきエラーのハンドリング( API ごとに異なるので詳細は API リファレンスを参照してください)
 
            // ID 一覧取得中に BOSS ストレージ内の NS データに追加や削除があったケース。
            // リストを初期化し、シリアル ID 一覧を初めから取り直してください。
            serialIdList.Initialize();
            result = nn::boss::ResultNsDataListSizeShortage();// do-while ループを抜けないように、result の値を更新。
            continue;
        }
        else if(result == nn::boss::ResultFileAccess())
        {
            // ファイルアクセスのエラーのハンドリング
 
            // ファイルアクセスエラーを示すエラーコード( nn::boss::FS_UNKNOWN_ERROR のエラーコード)を エラー・EULA アプレットでエラーを表示してください。
            u32 errCode = 0;
            nn::Result getErrCodeResult = nn::boss::GetErrorCode( &errCode, nn::boss::FS_UNKNOWN_ERROR);
            NN_UTIL_PANIC_IF_FAILED(getErrCodeResult);// boss::GetErrorCode() は使い方が間違っている場合にしか失敗しません。詳細は API リファレンスを参照してください。
 
            // エラー・EULA アプレットのコードは省略します
 
            break;
        }
        else
        {
            // 予期せぬエラーのハンドリング
 
            // 上記以外のエラーのエラーはそのことを示すエラーコード( nn::boss::SEVERE_ERROR のエラーコード)を エラー・EULA アプレットでエラーを表示してください。
            u32 errCode = 0;
            nn::Result getErrCodeResult = nn::boss::GetErrorCode( &errCode, nn::boss::SEVERE_ERROR);
            NN_UTIL_PANIC_IF_FAILED(getErrCodeResult);// boss::GetErrorCode() は使い方が間違っている場合にしか失敗しません。詳細はAPIリファレンスを参照してください。
 
            // エラー・EULA アプレットのコードは省略します
 
            break;
        }
    }
 
    {
        // 取得した serialIdList に対する処理
    }
}while(result == nn::boss::ResultNsDataListSizeShortage());// この例では ResultNsDataListSizeShortage の場合は GetNsDataIdList() を再実行します。

4.2.2. Nintendo アーカイブダウンロードタスク(NADL タスク)

NADL タスクは BOSSデータサーバーから HTTP/HTTPS 接続でデータをダウンロードするタスクです。

アプリケーションにダウンロードさせたいデータは BOSSデータサーバーに登録されていなければなりません。BOSSデータサーバーに登録されたデータは、Nintendo Serendipitous アーカイブ(略称 NS アーカイブ)という任天堂が独自に定義したフォーマットに変換されます。NS アーカイブには複数のデータを含ませることができますので、一度のタスク実行で複数のデータ(たとえば、アプリケーション用のデータと、そのおしらせデータなど)をダウンロードさせたい場合には、複数のデータをまとめて BOSSデータサーバーに登録します。

図 4-7. NADL タスクでダウンロードされたデータの流れ

データ BOSSデータサーバー BOSSはNSアーカイブを復号してデータを検証し、NSデータを振り分けます。 NSデータ 1 NSデータ 1(追加データ) NSデータ 2(おしらせデータ) NSアーカイブ NSAヘッダ NSデータ 2 NSアーカイブには複数のデータを含めることができます。上記は追加データとおしらせデータで構成された例です。 アプリケーション 拡張セーブデータ領域 NSD BOSSストレージ システム 拡張セーブデータ領域 NSD BOSSストレージ BOSS

BOSSデータサーバーの使い方については、BOSSデータサーバーにログインした後に閲覧できる「ヘルプ」を参照してください。なお、「ヘルプ」には配信データや運用に関する説明も書かれていますので、実際に運用を開始する前に必ず読むようにしてください。

管理ツールでは、主に以下の操作を行うことができます。

  • おしらせ、配信データの作成
  • おしらせ、配信データの配信スケジュールの管理
  • アプリケーション開発中のテスト配信
  • 実運用開始後のユーザーへの配信ログの取得

BOSSデータサーバーを利用するには、BOSS タスクを利用する旨を OMAS(Online title MAnagement System)で申請した際に、「BOSSデータサーバーにアクセスできるユーザー」の項目を申請してください。申請が許可され次第、BOSSデータサーバーのアクセス情報がメールで送信されます。

4.2.2.1. NS アーカイブ、NS データ

NS アーカイブに含まれるデータ(NS データ)はすべて暗号化され、署名とハッシュ値が付与されます。よって、BOSS は NS データの復号化だけでなく、不正なデータがダウンロードされないように改竄検知やなりすまし検知を行います。

NS アーカイブには新着フラグを設定することができます。新着フラグが有効に設定されていると、そのデータがダウンロードされたときに、アプリケーションのアイコン上に新着を示すマークが表示されます。新着フラグは BOSS が管理しているアプリケーションごとに設定され、システムはその設定を確認して新着マークを表示します。これらの処理はすべてシステム側で行われますので、新着マークの表示に関してはアプリケーション側で処理を行う必要はありません。

なお、おしらせデータが含まれている NS データをダウンロードすると、新着フラグが OFF(無効に設定)であっても、おしらせリストには常にマークが表示されます。ただし、これはおしらせリスト上に表示されるマークについてのみであり、アプリケーションのアイコン上に表示されるマークは新着フラグの設定で制御されます。

NS データには以下のものがあります。

表 4-17. NS データの種類

種類

説明

追加データ

追加アイテムや追加コースなど、アプリケーションで使用する様々なデータ。

おしらせデータ

BOSS 経由でおしらせを通知するためのデータ。

DL 拡張バナーデータ

CTR タイトルバナーで表示されるテキストやテクスチャを入れ替えるためのデータ。

DataStore データ

DataStore ダウンロードタスクでダウンロードされたデータ。

NS データはデータタイプという 32 bit の情報を持っています。データタイプは NS データの種類を示すグローバルデータタイプ(上位 16 bit)と、プライベートデータタイプ(下位 16 bit)の 2 部構成となっています。グローバルデータタイプはすべてのアプリケーションで共通の値で、上表のいずれかから指定します。プライベートデータタイプに指定する値はアプリケーションで自由に決めることができます。データタイプは「4.2.2.6. 新着フラグのチェック、データ新着イベントによる待ち受け」で説明する NS データ検索の検索条件として利用します。プライベートデータタイプの割り当てを工夫することで、NS データ検索を効率よく行うことができるようになります。

アプリケーションが一度も起動されたことがない、アプリケーションは起動させたが BOSS ストレージを登録していない、アプリケーションの拡張セーブデータをユーザーが削除したなど、BOSS が NS データを振り分ける際にデータを受け取るアプリケーションの BOSS ストレージが存在していない場合、その NS データは破棄されてしまうことに注意してください。また、NS データにはそのデータの宛先となるアプリケーションのユニーク ID が埋め込まれています。NS データのユニーク ID には、サーバーによって BOSS を使用するアプリケーションのユニーク ID が自動的に埋め込まれます。そのため、アプリケーション開発者がデータに任意のユニーク ID(宛先)を指定することはできません。ダウンロードした NS データは、どのアプリケーションが登録したタスクでダウンロードしたかに関わらず、埋め込まれたユニーク ID のアプリケーションの BOSS ストレージに保存されます。

注意:

タスク登録時に誤った URL を指定し、ほかのアプリケーション宛の NS データをダウンロードしてくることがないように注意してください。

NS データにはアプリケーションごとに一意なシリアル ID とバージョン(共に 32 bit の情報)が付与されます。シリアル ID はアプリケーションが NS データを読み込んだり、削除したりする際のデータの指定に利用します。バージョンは BOSS が NS データの更新を判断する際に利用します。ダウンロードしたデータと同じシリアル ID の NS データがすでに存在していた場合、BOSS はバージョンを比較し、ダウンロードしたデータが新しいバージョンであった場合にのみ、NS データを更新(上書き)します。

補足:

NS データのシリアル ID とバージョンは BOSS 管理サイト上で設定できます。ただし、おしらせデータおよび DataStore データのシリアル ID とバージョンは自動的に設定されるため、開発者の手で設定することができません。詳細は BOSS 管理サイトから取得できるマニュアルを参照してください。

注意:

4294901760 ~ 4294967295(0xFFFF0000 ~ 0xFFFFFFFF)の範囲のシリアル ID はシステムリザーブ領域として一般アプリケーションでの使用が禁止されています。BOSS データサーバーの BOSS 管理サイト上でシリアル ID を設定する際には、この領域以外の値(1 ~ 4294901759(0xFFFEFFFF))を使用してください。

NS データからはデータの内容以外にも、シリアル ID やデータの種類、データサイズなどのプロパティも取得することができます。ただし、おしらせデータはシステム側で自動的に処理しますので、アプリケーションから直接読み込むことはできません。DL 拡張バナーデータもシステム側で自動的に処理されますが、アプリケーションから読み込むことができます。

BOSS には、NS アーカイブに含まれているおしらせデータを処理しない(おしらせを表示しない)ようにするためのフラグ(OptOut フラグ)を、BOSS ストレージごとに設定することができます。

図 4-8. OptOut フラグが有効に設定されている場合の動作

データ BOSSデータサーバー NSデータ 1 NSデータ 1(追加データ) NSデータ 2(おしらせデータ) NSアーカイブ NSAヘッダ NSデータ 2 OptOutフラグが有効になっているタスクでダウンロードした場合、NSアーカイブに含まれているおしらせデータは破棄されます。 アプリケーション 拡張セーブデータ領域 NSD BOSSストレージ システム 拡張セーブデータ領域 BOSSストレージ BOSS タスク OptOutフラグ有効

OptOut フラグの設定と取得は、nn::boss::SetOptoutFlag()nn::boss::GetOptoutFlag() の呼び出しで行うことができます。

コード 4-38. OptOut フラグの設定と取得
nn::Result nn::boss::SetOptoutFlag(bool flag);
nn::Result nn::boss::GetOptoutFlag(bool* pFlag);

nn::boss::SetOptoutFlag() の引数 flagtrue を渡して呼び出すと OptOut フラグが有効になり、NS アーカイブにおしらせデータが含まれていても無視します(BOSS はそのおしらせデータを破棄します)。おしらせリストの「このソフトからのおしらせを受け取らない」を設定した場合と同じ挙動になります。
なお、現在の設定は nn::boss::GetOptoutFlag() で取得することができます。

4.2.2.2. BOSS ストレージ

追加データと DL 拡張バナーデータは、アプリケーションから指定された拡張セーブデータ領域に作成された、BOSS ストレージと呼ばれるアーカイブ領域内に保存されます。BOSS ストレージはアプリケーションの拡張セーブデータ領域内に作成されますが、アプリケーションからは直接アクセスすることができません。拡張セーブデータ領域内のどれだけのサイズの領域を BOSS ストレージとして使用するかは、アプリケーションが BOSS に指定します。

BOSS ストレージがデータで一杯になると、BOSS は古いデータ(シリアル ID の若いデータ)から順に削除して、指定されたサイズ以上の領域を使わないようにします。また、アプリケーションから BOSS ストレージの登録時に「最大データ数」を指定することができます。データを保存すると最大データ数を超えるときは古いデータから順に BOSS が削除し、最大データ数以上のデータが BOSS ストレージ内に保存されないようになります。最大データ数を指定しない場合は 2000 が適用されます。これらのことを考慮し、アプリケーションはダウンロードするデータのサイズや最大データ数に合わせて、BOSS ストレージのサイズやファイル数を指定する必要があります。また、BOSS ストレージが作成される拡張セーブデータは、新規作成時に「拡張セーブデータ内に作成するファイル数の最大値」を設定します。BOSS ストレージ内の NS データの数も、拡張セーブデータ内のファイル数とカウントされますので、「拡張セーブデータ内に作成するファイル数の最大値」を設定する際には、BOSS ストレージ内に保存される NS データの数も考慮した値を設定してください。

注意:

古いデータを削除してもダウンロード対象のデータを格納するだけの空き領域が確保できない場合、タスクは中断されエラー(NSA_ERROR_STORAGE_INSUFFICIENCY)となります。これは以下のケースで発生します。

  • ダウンロード対象のデータのサイズが BOSS ストレージのサイズより大きい場合
  • ダウンロード対象のデータがダウンロード済みのデータのバージョン更新版であり、旧バージョンのデータのサイズと新バージョンのデータのサイズの合計が BOSS ストレージのサイズよりも大きい場合

前者は当たり前と言えますが、後者はデータのバージョン更新の可能性がある場合には注意してください。ダウンロード済みデータのバージョン更新版のダウンロードでは、新バージョンのデータが一時ファイルとしてダウンロードされたあとに旧バージョンのデータが削除され、一時ファイルのファイル名が正式なファイル名に変更されます。よって一時的に旧バージョンと新バージョンのデータが同時に BOSS ストレージに存在することになりますので、それらの合計サイズが BOSS ストレージサイズ未満である必要があります。

なお、BOSS ストレージのサイズをダウンロードする NS データの最大サイズの 2 倍以上とすれば、このエラーになることはありません。

ダウンロードされた NS データは、振り分けられる前にアプリケーションの BOSS ストレージに一時的に保存されます。これにより、図 4-7 のようにおしらせデータはシステムの BOSS ストレージに振り分けられますが、一時的にはアプリケーションの BOSS ストレージに保存されることになります。最大データ数が N の BOSS ストレージに N 個の NS データが存在する場合、そこにおしらせデータをダウンロードすると一時的に BOSS ストレージに保存するために既存の NS データを一つ削除し、そのあとに振り分けで削除されるため、ダウンロード完了後のアプリケーションの BOSS ストレージの NS データは(N – 1)個になります。おしらせデータを配信するタスクについては、この挙動を考慮して BOSS ストレージの設定を決めてください。

補足:

一時的に BOSS ストレージに保存されているおしらせデータは、すぐにシステムの拡張セーブデータに移動されます。タスクを削除して再登録しない限りは以前にダウンロードしたときの情報が残っているので、シリアル ID とバージョンの比較が行われ、前回受信したおしらせを再受信することはありません。

注意:

NS データも含んだ拡張セーブデータ内のファイル数が最大値に達してしまうと、それ以降にダウンロードされた NS データは保存できず、各タスクの実行結果がエラーとなります。

BOSS ストレージに保存される NS データには、ファイルの先頭に 52 Byte のヘッダ部が付与されています。そのため、BOSS ストレージのサイズを指定する際には、各 NS データがヘッダ部を持っていることを考慮してください。

BOSS ストレージはあくまでも BOSS の作業領域ですので、データが自動的に削除される可能性があります。そのため、追加データを永続化したい場合は、BOSS ストレージ内の NS データとして保存するのではなく、BOSS 経由で NS データを読み込んで、拡張セーブデータ領域にファイルとしてコピーすることを強く推奨します。また、多数のデータが BOSS ストレージ内に保存されている場合、BOSS ストレージ内の全データを対象とした処理の効率が低下してしまいます。そのため、使い終わったデータは BOSS ストレージ内から削除することも推奨します。上記のように NS データを BOSS ストレージ外に永続化することで、そのデータを BOSS ストレージ内から削除することができます。

シリーズタイトルで拡張セーブデータ領域を共有している場合は、自身でダウンロードしたデータだけでなく、シリーズに属するタイトルがダウンロードしたデータにもアクセスすることができます。

弊社に BOSS サービスの使用を申請していただき、申請に対してアプリケーションごとに専用の URL を提供します。通信には HTTPS 通信を使用し、CA 証明書は 3DS に内蔵されている証明書を使用します。

BOSS サービスを使用するアプリケーションに対しては、申請どおりの設定でタスクが登録されているかどうかが、ロットチェック時に検査されます。

注意:

NS データのシリアル ID には、アプリケーション単位で(シリーズタイトルの場合はシリーズ単位で)一意の値を指定してください。

おしらせのみをダウンロードする場合

おしらせデータはシステムの拡張セーブデータに保存されるため、アプリケーションがおしらせデータのみをダウンロードする場合は、そのアプリケーションの BOSS ストレージには NS データは貯まりません。ただしこの場合でも、おしらせデータを一時的に保存できるだけのサイズを持った BOSS ストレージの登録が必要となります。これは、NS データが BOSS ストレージに一時的に保存されたあと、ハッシュや署名の検証が行われるためです。それらが成功して初めて、NS データは保存されるべき BOSS ストレージに移動されます。つまり、タスクを登録したアプリケーション用のデータであれば、保存されるべき BOSS ストレージは同じですので、NS データはそのまま残ります。おしらせデータの場合は、システムの拡張セーブデータに移動されます。

最大サイズのおしらせデータ(50 KByte の添付データを利用)をダウンロードするために必要な BOSS ストレージのサイズは 60 KByte です。

注意:

おしらせを配信するためだけに拡張セーブデータを使用する場合は、定期的(起動時など)に拡張セーブデータが正常にマウントできるかどうかを確認してください。これはダウンロードされたデータが拡張セーブデータに一時的に保存されるため、拡張セーブデータが破壊されていたり、削除されていたりするとタスクが残っていてもおしらせを受け取ることができなくなってしまうためです。

4.2.2.3. NADL タスクの処理に必要な手順

BOSS に NADL タスクを登録し、ダウンロードされたデータを処理するまでに必要な手順は以下のようになっています。

図 4-9. NADL タスクの処理に必要な手順

拡張セーブデータの作成 BOSS ストレージの登録 BOSS利用のための準備 NADLタスクの登録 新着フラグのチェック ダウンロードデータのチェック データ新着イベントによる待ち受け タスク実行完了の待ち受け 新規ダウンロードデータの確認 ダウンロードデータの処理 タスクの実行結果をポーリングする場合タスクの実行完了を待ち受ける場合 ダウンロードデータの読み込み ダウンロードデータの処理 新規ダウンロードデータあり 新規ダウンロードデータなし タスクの状態のポーリング

図中の各処理の概要は以下のとおりです。

表 4-18. NADL タスクの各処理の概要

処理

概要

拡張セーブデータの作成

NADL タスクでダウンロードしたデータを保存する BOSS ストレージを登録するために、拡張セーブデータを作成する必要があります。

拡張セーブデータの作成方法については、「3DS プログラミングマニュアル - システム編」を参照してください。

BOSS ストレージの登録

NADL タスクでダウンロードしたデータを保存する BOSS ストレージを BOSS に登録します。

詳細については、「4.2.2.4. BOSS 利用のための準備」を参照してください。

NADL タスクの登録

NADL タスクの動作設定を行い、BOSS に登録します。

詳細については、「4.2.2.5. NADL タスクの登録」を参照してください。

新着フラグのチェック

ライブラリで管理されている新着フラグをチェックします。

詳細については、「4.2.2.6. 新着フラグのチェック、データ新着イベントによる待ち受け」を参照してください。

ダウンロードデータのチェック

ダウンロードされたデータの一覧を取得し、新たにダウンロードされたデータが存在するかどうかをチェックします。処理済みのデータに対して、処理の都度削除する、シリアル ID を記録する、既読設定を行うなどの対応をアプリケーションで行う必要があります。

詳細については、「4.2.2.7. ダウンロードデータのチェック」を参照してください。

タスクの状態のポーリング

NADL タスクの状態をポーリングし、状態が完了になっているかをチェックします。

詳細については、「4.2.1.5. タスクの情報」を参照してください。

データ新着イベントによる待ち受け

ライブラリにイベントを登録し、そのイベントがシグナル状態になるのを待ち受けます。

詳細については、「4.2.2.6. 新着フラグのチェック、データ新着イベントによる待ち受け」を参照してください。

タスク実行完了の待ち受け

NADL タスクをフォアグラウンドで実行し、その完了を待ち受けます。

詳細については、「4.2.1.4. タスクの登録と実行」を参照してください。

ダウンロードデータの読み込み

BOSS ストレージに格納されている NS データにアクセスし、アプリケーションで用意したバッファに読み込みます。

詳細については、「4.2.2.8. ダウンロードデータの読み込み」を参照してください。

ダウンロードデータの処理

取得したダウンロードデータをアプリケーションで処理します。

4.2.2.4. BOSS 利用のための準備

NADL タスクを登録・実行するには、BOSS ストレージを先に登録しておかなければなりません。

コード 4-39. BOSS ストレージの登録と登録解除
nn::Result nn::boss::GetStorageInfo(size_t* pStorageSize=NULL);
nn::Result nn::boss::GetStorageInfo(size_t* pStorageSize, u16* pEntryCount);
nn::Result nn::boss::RegisterStorage(
        bit32 storageId, size_t storageSize, 
        nn::boss::StorageType storageType=nn::boss::USER_EXT_SAVEDATA_STORAGE);
nn::Result nn::boss::RegisterStorage(
        bit32 storageId, size_t storageSize, u16 entryCount, 
        nn::boss::StorageType storageType=nn::boss::USER_EXT_SAVEDATA_STORAGE);
nn::Result nn::boss::UnregisterStorage(void);

BOSS ストレージの登録を行う前に、nn::boss::GetStorageInfo() を呼び出して BOSS ストレージが作成済みかどうかを確認してください。未作成の場合、nn::boss::ResultStorageNotFound が返されます。なお、pStorageSizepEntryCount にはそれぞれ、BOSS ストレージのサイズと最大データ数を格納するための変数へのポインタを指定してください。

nn::boss::RegisterStorage() の引数 storageId で指定された拡張セーブデータ領域に、全ファイルサイズの合計が storageSize バイトまで保存可能な BOSS ストレージが作成されます。また、引数 entryCount を持つオーバーライドでは BOSS ストレージ内に登録可能なデータの最大数を指定することができます。

nn::boss::UnregisterStorage() は BOSS ストレージの登録を解除します。アプリケーションがアクセス権を持たない拡張セーブデータ領域に BOSS ストレージを登録しようとした場合は nn::boss::ResultStorageAccessPermission のエラーが返されます。

1 つのアプリケーションで登録することのできる BOSS ストレージは 1 つです。アプリケーションが BOSS ストレージとして用いる拡張セーブデータ領域を切り替えたい場合は、nn::boss::UnregisterStorage() を呼び出して登録を解除したあとに、再度 nn::boss::RegisterStorage() を呼び出す必要があります。

注意:

nn::boss::UnregisterStorage() で BOSS ストレージの登録解除をした後、登録されたタスクが残っていると、以下のような挙動となります。

  • BOSS ストレージがないままタスクが実行され、ダウンロードデータは破棄されます。以降のタスク実行では重複ダウンロード防止の処理が働くことがあります。
    アプリケーションが配信データを受け取っていないとしても、重複ダウンロード防止の処理が働いている限りは、該当の配信データを受信することができません。タスクの再登録を行うと再受信することができます。
  • BOSS ストレージを再登録した後でも、以前から残っていたタスクは前の BOSS ストレージサイズを覚えており、それに従います。
    以前の BOSS ストレージサイズのタスクと、新しい BOSS ストレージサイズのタスクが混在している場合、現在の BOSS ストレージサイズとは異なるサイズでデータが押し出されて消えてしまう可能性があります。

上記の制限があるので、BOSS ストレージの登録解除を行う際は、あわせて全てのタスクの登録解除を行うことを強く推奨します。

BOSS ストレージのサイズ変更は、以下の手順で安全に行うことができます。

  1. 全てのタスクの登録解除を行う
  2. BOSS ストレージの登録解除を行う
  3. 新しいサイズで BOSS ストレージの登録を行う
  4. タスクの再登録を行う
注意:

BOSS ストレージとして用いる拡張セーブデータ領域を切り替えると、切り替え前の BOSS ストレージに保存されている NS データにアクセスすることができなくなります。

BOSS ストレージは 1 つの拡張セーブデータ領域に作成したものを使い続けることを推奨します。

補足:

拡張セーブデータ領域へのアクセス方法は「3DS プログラミングマニュアル - システム編」を参照してください。

「おしらせデータ」については「アプレット仕様書」を、「DL 拡張バナーデータ」については「Banner オーバービュー」および「ctr_makedlexbanner」を参照してください。

ユーザーによる拡張セーブデータ削除への対応について

ユーザーが「本体設定」で拡張セーブデータを削除する可能性があることに注意しなければなりません。削除された場合、アプリケーションが BOSS を再度利用するためには拡張セーブデータを再作成する必要があります。そのため、BOSS を利用するアプリケーションは必ず、ユーザーによる拡張セーブデータの削除に対応してください。たとえば、アプリケーション起動時に拡張セーブデータが存在しているかを確認し、存在しない場合はユーザーに拡張セーブデータを作成するかどうかを確認するような処理が必要となります。

登録できる BOSS ストレージの最大数と自動登録解除

システムに登録できる BOSS ストレージ の数は最大で127個です。この数はすべてのアプリケーション(内蔵アプリケーション含む)で登録された BOSS ストレージの総計の最大値です。登録されている BOSS ストレージが最大数に達している状態で、アプリケーションが新たに BOSS ストレージを登録しようとした場合、BOSS は「タスク登録のないアプリケーションの BOSS ストレージ」を自動的に登録を解除して、新たな BOSS ストレージを登録します。タスク登録のないアプリケーションが存在しない場合は、タスクの消尽回数と実行間隔の乗算結果がもっとも小さなタスクの登録解除をし続け、タスク登録のないアプリケーションが現れた時点で、そのアプリケーションの BOSS ストレージを登録解除し、新たな BOSS ストレージを登録します。
上記の自動登録解除により、アプリケーションを長時間起動しなかった場合には、以前登録した BOSS ストレージが BOSS によって登録解除されている可能性があります。その間、タスク登録も解除されているのでタスク実行はされません。そのため、BOSS を利用する場合にはアプリケーション起動時に BOSS ストレージが登録されているかを確認してください。

4.2.2.5. NADL タスクの登録

NADL タスクの動作設定は nn::boss::NsaDownloadAction クラスで行います。クラスのインスタンスを生成したあと、Initialize() による初期化時に URL を指定します。

コード 4-40. nn::boss::NsaDownloadAction クラスの初期化
class nn::boss::NsaDownloadAction : public nn::boss::TaskAction
{
    nn::Result Initialize(const char* pUrl);
}

pUrl には、ダウンロードする NS アーカイブの URL を指定します。指定可能な URL は終端の NULL 文字を含めて 512 文字までです。

NS アーカイブの URL は、以下の書式となっています。

https://<CDN ドメイン>/<環境 ID>/<共通パラメータ>/<BOSS コード>/<タスク ID>/<任意のファイル名>

言語別に配信する場合は、言語情報(ISO 639-1 alpha-2)を含めた書式である必要があります。

https://<CDN ドメイン>/<環境 ID>/<共通パラメータ>/<BOSS コード>/<タスク ID>/<言語情報>/<任意のファイル名>

言語別かつ繁体字と簡体字の両方でおしらせを配信する場合は、言語情報に加えて、国情報(ISO 3166-1 alpha-2)を含めた書式である必要があります。これは、繁体字と簡体字が ISO 639-1 alpha-2 ではどちらも中国語(zh)であるため、言語情報だけでは BOSSデータサーバーがどちらの文字体で配信すべきかを判断できないからです。

https://<CDN ドメイン>/<環境 ID>/<共通パラメータ>/<BOSS コード>/<タスク ID>/<国情報>/<言語情報>/<任意のファイル名>

 

注意:

ユーザーが本体設定で言語設定や国設定を変更した場合、タスクの再登録を行わなければ、言語別の配信を行っているタスクは本体設定と異なる言語のデータをダウンロードすることになります。

補足:

言語別の配信を行う場合は OMAS で言語別配信の利用を申請する必要があります。さらに、繁体字と簡体字の両方で配信する場合は利用申請の際に使い方の欄にその旨を記載してください。

おしらせリストの表示は、中国リージョン(中国)の本体では簡体字、台湾リージョン(台湾・香港)の本体では繁体字で行われます。

NsaDownloadAction クラスは nn::boss::TaskAction クラスを継承しており、タスク共通の動作設定を行うことができます。

表 4-19. nn::boss::NsaDownloadAction クラスで指定可能なプロパティ識別子

プロパティ識別子

プロパティ

ACTION_URL

char[MAX_URL_LENGTH]

接続先の URL

ACTION_HTTP_HEADER

struct httpRequestHeader[MAX_HTTP_HEADER]

追加の HTTP リクエストヘッダ

ACTION_CLIENT_CERT

u32

機器内蔵のクライアント証明書 ID

ACTION_ROOT_CA

u32

機器内蔵のルート CA 証明書 ID

ACTION_AP_INFO

ApInfoType

HTTP リクエストに付与される AP 情報

ACTION_CLIENT_CERT_NUM

u32

機器内蔵のクライアント証明書の登録数

ACTION_ROOT_CA_NUM

u32

機器内蔵のルート CA 証明書の登録数


以下に、NADL タスク特有の動作設定を説明します。

使用可能なルート CA 証明書

NADL タスクのルート CA 証明書はライブラリが自動で登録しますので、アプリケーションでの登録は不要です。

ライブラリによって登録されるルート CA 証明書は、製品向けの本配信用 BOSSデータサーバーへの接続で使用する CA ルート証明書と、テスト配信用 BOSSデータサーバーへの接続で使用する CA ルート証明書の 2 つです。なお、本配信データは BOSSデータサーバーから製品実機でのみダウンロードすることができます。ただし、BOSS ライブラリのサンプルデモは例外として、開発機で本配信用のデータをダウンロードすることができます。テスト配信データは本体設定で専用の DNS サーバーを設定した開発機および製品実機でのみダウンロードすることができます。 DNS サーバーの IP アドレスについては BOSSデータサーバーの「BOSS 管理ツール」にてテスト配信を行うと確認できます。

4.2.2.6. 新着フラグのチェック、データ新着イベントによる待ち受け

新たなデータがダウンロードされたかどうかの確認には、以下の関数を使用することができます。

コード 4-41. 新着データの確認
nn::Result nn::boss::GetNewArrivalFlag(bool* pFlag);
nn::Result nn::boss::RegisterNewArrivalEvent(nn::os::Event* event);

nn::boss::GetNewArrivalFlag() は、新たなデータが存在する場合に pFlagtrue を格納します。

nn::boss::RegisterNewArrivalEvent() は、新たなデータがダウンロードされると event に渡されたイベントクラスをシグナル状態にします。イベントクラスは事前にアプリケーションで初期化してください。

補足:

ダウンロードした NS アーカイブの新着フラグが OFF(無効)の場合、そのデータは新着扱いにはなりません。そのようなデータがダウンロードされても、GetNewArrivalFlag() で取得するフラグは false のままですが、RegisterNewArrivalEvent() に渡されたイベントはシグナル状態になります。

4.2.2.7. ダウンロードデータのチェック

タスクの実行が完了した時点で、タスクに指定されていた拡張セーブデータ領域の BOSS ストレージにダウンロードされた NS データが保存されています。BOSS ストレージに保存されている NS データにアクセスするには、まず NS データのシリアル ID の一覧を取得しなければなりません。

コード 4-42. NS データのシリアル ID 一覧の取得
nn::Result nn::boss::GetNsDataIdList(
                        u32 dataType, nn::boss::NsDataIdList* pNsDataId);
nn::Result nn::boss::GetNewDataNsDataIdList(
                        u32 dataType, nn::boss::NsDataIdList* pNsDataId);

nn::boss::GetNsDataIdList()nn::boss::GetNewDataNsDataIdList() は、アプリケーションから読み込むことのできる NS データの一覧を取得します。前者はシリアル ID の降順ですべてを、後者は未読分のみ取得します。

これらの関数を呼び出したときは、アプリケーションのアイコンに表示されていた新着マークがクリア(nn::boss::GetNewArribalFlag() で取得される新着フラグが false に設定)されます。新たにダウンロードされた NS データは未読となります。未読・既読の設定は nn::boss::NsData::SetReadFlag() で行い、アプリケーションで既読に設定しない限り、NS データは未読のままです。

補足:

ダウンロードデータの処理が必要かどうかをシリアル ID の一覧で判断する場合は、データを処理したあとに毎回削除する、処理済みのシリアル ID を記録しておく、NS データを既読に設定するなどの対応が必要です。

シリーズタイトルで拡張セーブデータ領域を共有している場合、シリーズのあるタイトルが登録したタスクでダウンロードされた NS データは、シリーズ内のほかのタイトルからもアクセスすることができます。詳細については「NS データの共有」を参照してください。

これらの関数はBOSS ストレージ内の全データに対して検索を行いますので、BOSS ストレージ内に保存されているデータの数が多いほど処理に時間がかかります。アプリケーションの処理効率を高めるためにも、BOSS ストレージ内の使い終わったデータは逐次削除することを推奨します。

どの関数も dataType には取得の対象とする NS データの検索条件を指定し、pNsDataId には一覧を格納するための nn::boss::NsDataIdList クラスのインスタンスへのポインタを指定します。

NS データの検索条件は以下のように指定します。

すべての NS データを検索対象にする場合は DATA_TYPE_ALL を指定します。

それ以外の場合は、グローバルデータタイプ(データ種別)とプライベートデータタイプの論理和で検索対象を指定します。グローバルデータタイプが一致し、かつプライベートデータタイプによるマスク処理でも取得対象と判断された NS データの ID のみが一覧に格納されます。

グローバルデータタイプ(データ種別)には以下の 3 種類があります。

表 4-20. NS データの種別

定義

説明

DATA_TYPE_APPDATA

追加データ

DATA_TYPE_NEWS

おしらせに表示されるデータ(アプリケーションでは取得できません)

DATA_TYPE_EXBANNER

DL 拡張バナーデータ

DATA_TYPE_DATASTORE

DataStore ダウンロードタスクで取得したデータ

プライベートデータタイプは 16 bit の値で、NS データのプライベートデータタイプ(データタイプの下位 16 bit)のマスク値として扱われます。指定したプライベートデータタイプと NS データのプライベートデータタイプの同じ位置のビットが 1 であるものが 1 箇所でもある(つまりプライベートデータタイプ同士の論理積が 0 ではない)場合、その NS データは取得対象になります。たとえば、すべての追加データを取得したい場合は、dataType に "DATA_TYPE_APPDATA | 0x0000FFFF" を指定します。NS データのデータタイプを活用することで、アプリケーションは追加データを独自に分類した種類ごとに取得することができます。

補足:

SD カードが抜けたなどで BOSS ストレージに正常にアクセスできなかった場合、nn::boss::GetNsDataIdList()nn::boss::ResultFileAccess エラーを返します。この状況をアプリケーションで復旧させることはできませんので、エラーコードをユーザー向けに表示するようにしてください。このエラーとそのほかの予期せぬエラーに対するエラーコード表示の実装例については、boss のサンプルデモの sample_nadl_simple(CTR-SDK 7.1 以降のもの)を参照してください。

nn::boss::NsDataIdList クラスのインスタンスの生成には、シリアル ID を格納するための u32 型の配列が必要です。配列のサイズが一度の検索で取得することのできるシリアル ID の個数です。

検索結果のシリアル ID すべてをインスタンスに格納することができなかった場合、検索関数は nn::boss::ResultNsDataListSizeShortage を返します。その場合、同じインスタンスで再検索する(再度同じ関数を実行する)ことで、前回取得した分の続きからシリアル ID の一覧を取得することができます。

検索の実行中に新しい NS データが BOSS ストレージに格納された場合、検索関数は nn::boss::ResultNsDataListUpdated を返します。その場合は nn::boss::NsDataIdList インスタンスのメンバ関数 Intialize() を呼び出してリストの初期化を行ってから、再度先頭からシリアル ID の一覧を取得しなおしてください。

最後のシリアル ID まで取得した場合、検索関数の返す nn::Result のインスタンスは IsSuccess()true を返します。

コード 4-43. nn::boss::NsDataIdList クラス
class nn::boss::NsDataIdList
{
    explicit NsDataIdList(u32* pSerial, u16 size);
    virtual ~NsDataIdList(void);
    void Initialize(void);
    u16 GetSize(void);
    u32 GetNsDataId(u16 index);
}

インスタンスを生成したあとは、必ず Initialize() で初期化してください。検索に使用したインスタンスを Initialize() で初期化すると、そのインスタンスで再び先頭からシリアル ID の一覧を取得することができます。

取得したシリアル ID の個数は GetSize() で確認することができます。

シリアル ID を取得するには、さらに配列内のインデックスを指定して GetNsDataId() を呼び出します。範囲外のインデックスを指定した場合は INVALID_SERIAL_ID が返されます。

NS データの共有

シリーズタイトルで拡張セーブデータを共有している場合、nn::boss::GetNsDataIdList()nn::boss::GetNewDataNsDataIdList() は、関数を呼び出したアプリケーション以外のシリーズタイトルが登録したタスクでダウンロードされた NS データも含めて検索を行います。また、nn::boss::NsData クラスを用いて、シリーズのほかのタイトルが登録したタスクでダウンロードされた NS データへのアクセスも可能になります。つまりシリーズタイトル内では、NADL タスクでダウンロードされた NS データが共有されることになります。

注意:

シリーズタイトル内で NS データは共有されますので、シリーズタイトル用の NS データのシリアル ID は、シリーズタイトル内で一意(つまりタイトルごとではなく、シリーズ全体で一意)になるように付与してください。

シリーズタイトル内のほかのタイトルの NS データが不要な場合、nn::boss::GetOwnNsDataIdList()nn::boss::GetOwnNewDataNsDataIdList() は、そのアプリケーション自身が登録したタスクでダウンロードされた NS データのみを検索対象にすることができます。

コード 4-44. NS データのシリアル ID 一覧の取得(アプリケーション限定)
nn::Result nn::boss::GetOwnNsDataIdList(
                        u32 dataType, nn::boss::NsDataIdList* pNsDataId);
nn::Result nn::boss::GetOwnNewDataNsDataIdList(
                        u32 dataType, nn::boss::NsDataIdList* pNsDataId);

上記の 2 関数は、nn::boss::GetNsDataIdList()nn::boss::GetNewDataNsDataIdList() と検索対象が異なるだけで、呼び出した際の動作に変わりません。ただし、NS データが関数を呼び出したアプリケーションのタスクでダウンロードされたものかどうかを確認する処理が追加で実行されるため、多少処理速度が劣ります。

4.2.2.8. ダウンロードデータの読み込み

nn::boss::NsData クラスのインスタンスを作成し、取得した NS データのシリアル ID を引数に Initialize() を呼び出して初期化すると、そのインスタンスを介して NS データにアクセスすることができるようになります。

コード 4-45. nn::boss::NsData クラスの初期化と読み込み関連関数
class nn::boss::NsData
{
    nn::Result Initialize(u32 serial);
    nn::Result GetHeaderInfo(HeaderInfoType type, void* pValue, size_t size);
    nn::Result SetReadDataPosition(s64 position, PositionBase base);
    s32 ReadData(u8* pDataBuf, size_t bufLen);
    nn::Result SetReadFlag(bool flag);
    nn::Result GetReadFlag(bool* pFlag);
}

NS データに関するプロパティ情報は GetHeaderInfo() で取得できるヘッダ情報から取得します。type に指定されたヘッダ種別でアクセスするヘッダ情報が決定します。pValuesize には、ヘッダ情報を格納する変数とそのバイトサイズ(ヘッダ情報の型により変化)を指定します。

表 4-21. ヘッダ種別

ヘッダ種別

説明

NSD_TITLEID

s64

タイトル ID

NSD_FLAGS

u32

NSD フラグ(未使用)

NSD_DATATYPE

DataType

データタイプ

NSD_LENGTH

u32

データ長

NSD_SERIALID

u32

シリアル ID

NSD_VERSION

u32

バージョン番号

NS データの読み込みは ReadData() の呼び出しで行います。pDataBuf に読み込みバッファを、bufLen にそのサイズを指定してください。返り値は読み込んだバイト数です。0 が返された場合はデータの最後まで読み込みが完了しています。保存領域を確保するために自動的に削除されたデータなど、存在しないデータを読み込もうとした場合はエラーとなり、負の値が返されます。読み込み中のデータが新しいダウンロードデータで上書きされた場合はエラーとなり、NN_BOSS_NSDATA_READ_ERROR_UPDATED が返されます。

また、SetReadDataPosition() は、NS データの読み込み位置を指定することができます(ファイルシステムの Seek() に相当する関数です)。

注意:

ファイルシステムの Seek() と同様に、SetReadDataPosition() で設定された読み込み位置がファイルの先頭から 4 の倍数でない場合、NS データの読み込みがかなり遅くなります。

NS データを既読設定にするには、SetReadFlag() を、引数 flagtrue を指定して呼び出します。NS データが既読かどうかは、GetReadFlag() で確認することができます。pFlagtrue が返されれば、その NS データは既読です。

NS データの読み込みに関するもの以外には、以下の関数が用意されています。

コード 4-46. 付加情報の設定・取得とデータの削除
class nn::boss::NsData
{
    nn::Result SetAdditionalInfo(u32 info);
    nn::Result GetAdditionalInfo(u32* pInfo);
    nn::Result GetLastUpdated(nn::fnd::DateTime* PTime);
    nn::Result Delete(void);
}

SetAdditionalInfo()GetAdditionalInfo() で設定および取得可能な u32 型の値は、NS データにアプリケーションが自由に付加することのできる情報です。付加された情報は、新しいバージョンのデータで上書きされたときに削除されることに注意してください。

GetLastUpdated() を呼び出して、NS データが作成された日時を取得することができます。新しいバージョンのデータで上書きされている場合は、その上書きが実施された日時を取得することになります。

NS データを BOSS ストレージから削除するには、Delete() を呼び出します。明示的に削除を行わなくても、BOSS ストレージが NS データで一杯になる(NS データの合計サイズが nn::boss::RegisterStorage() で指定したサイズ以上になる)と、BOSS が自動的に古いデータ(シリアル ID の若いデータ)から順に NS データを削除し、BOSS ストレージ内の NS データの合計サイズが nn::boss::RegisterStorage() で指定されたサイズ以上にならないように制御します。ただし、不要な NS データを明示的に削除することで、そのデータを以降の BOSS の処理対象から除外することができ、処理の効率を高めることができます。たとえば、シリアル ID 一覧の取得処理の対象から除外されることで、シリアル ID 一覧の検索処理の効率が上がります。

重複ダウンロードの防止

BOSS は HTTP リクエストの If-Modified-Since フィールドを用いて、同じ NS アーカイブが複数回ダウンロードされるのを防いでいます。

ただし、以下の条件が同時に満たされた場合は、同じ NS アーカイブが再度ダウンロードされてしまいます。

  • 前回の NS アーカイブのダウンロード後に、そのタスクで別の NS アーカイブを 50 個以上ダウンロードしている。
  • 最後にダウンロードした NS アーカイブよりも新しい NS アーカイブがサーバー上に登録されている。つまりサーバー上の NS アーカイブの更新時刻が、最後にダウンロードした NS アーカイブの LastModifiedTime よりも未来の時刻になっている。

アプリケーションが意識する必要はありませんが、NS アーカイブには個別に ID が付与されており、同じ NS アーカイブが再度ダウンロードされないように制御されています。ID はタスクごとに最新の 50 個までしか記録されないため、異なる NS アーカイブを 50 個ダウンロードしたあとに同じ NS アーカイブが配信されると、そのデータを再度ダウンロードしてしまいます。ただし、NS アーカイブの LastModifiedTime が最後にダウンロードした NS アーカイブよりも未来の時刻に更新されていなければ、If-Modified-Since フィールドによって重複ダウンロードは防止されます。

上記の条件が通常の運用では満たされない場合でも、BOSSデータサーバーへのデータ登録時のオペレーションミスや予期せぬ不具合によって、同じ NS アーカイブや NS データが複数回ダウンロードされてしまう可能性があります。そのためアプリケーションには、このような状況が生じても致命的な不具合とならないように対応してください。たとえば、すでに取得したデータのシリアル ID を記録しておき、同じシリアル ID のデータを再度取得した場合には、そのデータを削除するような対応です。

注意:

タスクを削除して再度タスクを登録した場合、以前にダウンロードしたときの情報も削除されるため、基本的には重複受信防止機能が正しく働かなくなることに注意してください。

おしらせを何度も受信しないように、おしらせを受信するタスクはできるだけ削除を行わない実装にしてください。たとえば起動時に毎回タスクの削除と登録を行うような実装は避けてください。

ダウンロードの中断

ダウンロードは電源が切断されたときやバッテリー切れ、通信環境の悪化が原因でアクセスポイントとの接続が切断されたときに中断されます。通常は 10 分後にリトライしますが、通信環境が悪いときには次回のタスク実行時刻までリトライされない可能性があります。

スリープの影響

ダウンロード中にスリープ状態になったときはダウンロードが一度中断されますが、アクセスポイントが見つかればすぐに再開されます。スリープ状態から復帰したときに、スリープ前に同じアクセスポイントに接続されていた場合はそのままダウンロードを継続します。スリープ前にオフラインだった場合は復帰したときに(スリープ中に接続していた)アクセスポイントとの接続が切断されますが、次にアクセスポイントと接続できたタイミングでダウンロードが再開されます。

NS データの破損、改竄

NS データが破損している、もしくは改竄されていることは、以下の手順で検知することができます。

  1. nn::boss::GetNsDataIdList() で NS データの ID を取得する。
  2. nn::boss::NsData::GetHeaderInfo() もしくは nn::boss::NsData::ReadData() で、使い方が不正であるエラー
    • GetHeaderInfo() の場合、ResultInvalidNsDataValueResultIpcNotSessionInitializedResultStorageAccessPermission のいずれか以外のエラーが返されたときに NS データが壊れていると判断してください。
    • ReadData() の場合、NsData::NN_BOSS_NSDATA_READ_ERROR_READ_DATA が返ったときにデータが壊れていると判断してください。NsData::NN_BOSS_NSDATA_READ_ERROR_GET_HEADER が返った場合には、詳細なエラーを GetHeaderInfo() で取得して破損、改竄の判定を行ってください。

NS データが破損、改竄されていることを検知した場合は、必ず以下のように対応してください。

  1. 破損、改竄されていた NS データを nn::boss::NsData::Delete() で削除する。
  2. 「おしらせ」が同時に届いている場合や、アプリケーション内で「データ」が届いていることを事前に知らせていた場合は、ユーザーに「データ」が壊れていたので削除する旨を通知する。

なお、NS データを削除しても、「重複ダウンロードの防止」によって、同じタスクで同じ NS アーカイブを再びダウンロードすることは基本的に(条件がそろわない限り)できません。そのため、削除した NS データを再ダウンロードする場合は、対象のタスクを nn::boss::UnregisterTask() で登録解除してから、nn::boss::RegisterTask() で再登録してください。ただし、サーバー上の NS データが更新され、削除したデータとは異なるデータになっている可能性がありますので、その点を考慮して再ダウンロード処理を実装してください。

4.2.3. NSA リスト

NSA リストは、BOSSデータサーバー上にある NS アーカイブの一覧を取得する機能です。いつの間に通信の利用申請時に割り振られる BOSS コードやタスク ID、属性を指定して検索し、条件に合致した NS アーカイブのリストを返します。

補足:

BOSS コードや NSA リストの設定方法については「OMAS ヘルプ」を参照してください。

4.2.3.1. NsaList クラス

NSA リストの機能は nn::boss::NsaList クラスで定義されています。

コード 4-47. NsaList クラスのコンストラクタ/デストラクタ
class nn::boss::NsaList
{
    NsaList(const char* nsaListFilePath);
    ~NsaList(void);
}

コンストラクタの引数 nsaListFilePath には、取得した NSA リストの保存先パスを指定します。拡張セーブデータのように、アプリケーションからの書き込みが可能なアーカイブ内のパスを指定してください。

4.2.3.2. NSA リストの取得

指定された BOSS コードとタスク ID の NSA リストを Download() で取得します。即時実行専用タスクとしてフォアグラウンドで処理されるため、事前に AC ライブラリでインターネット接続を確立しておかなければなりません。
取得する際、自動的に本体設定に登録されている国・言語情報をクエリストリングに埋め込みます。

コード 4-48. NSA リストを取得する関数
nn::Result nn::boss::NsaList::Download(
        const char* bossCode, const char* taskId, 
        const nn::boss::NsaList::SearchAttributes* attributes = NULL, 
        u32 waitTimeoutSec = NN_BOSS_NSALIST_WAIT_FINISH_TIMEOUT_DEFAULT_SEC,
        s64 fileSize = NN_BOSS_NSALIST_MAX_SIZE);
nn::Result nn::boss::NsaList::Cancel(void);

bossCode には BOSS コードを、taskId にはタスク ID を指定します。

waitTimeoutSec には、ダウンロードのタイムアウト時間を秒単位で指定します。引数を省略したときは nn::boss::NN_BOSS_NSALIST_WAIT_FINISH_TIMEOUT_DEFAULT_SEC(現状は 30 秒)が指定されます。0 を指定するとタイムアウトしません。

attributes には、3 つの属性(attribute1~attribute3。各属性は ASCII で 9 文字までの文字列)による検索条件を指定します。Download() で指定する属性と NS アーカイブに指定されている属性が一致するかどうかで取得すべきかが決定されます。それぞれの属性は組み合わせではなく順列として扱われますので、attribute1 が "item" というアーカイブの NSA リストを取得する場合、attribute2 や attribute3 に "item" が指定されていても、attribute1 が "item" でない場合はリストアップの対象にはなりません。さらに attribute1 = "A"、attribute2 = "B"、attribute3 = "C" という条件で NSA リストを取得する場合、条件はすべて and で連結されますので、すべての属性の順列が一致したアーカイブのみがリストアップの対象になります。属性を指定した場合は一致するもののみを取得し、指定しなかった(NULL または空文字列)場合は一致判定を行いません。ただし、アーカイブ側に指定がない属性に対して、属性を指定した Download() を行うと、その NS アーカイブはリストに含まれません。

それぞれの属性で以下の判定を行い、3 属性とも「取得する」と判定された NS アーカイブがリストに追加されます。

表 4-22. 属性の指定と取得判定

アーカイブ側の属性

Download() 側の属性

取得判定

指定なし

指定なし

取得する

指定なし

指定あり

取得しない

指定あり

指定なし

取得する

指定あり

指定あり

一致した場合は取得する

Download() での属性の指定はリスト作成時の抽出条件であり、1 つの属性には 1 つしか条件を指定できません。1 つの属性に複数の条件を指定する場合は、属性を指定せずに取得したあと、リストの内容を解析する必要があります。

fileSize には、作成する NSA リストの最大サイズを指定します。NSA リスト機能では、このサイズの空ファイルを作成したあと、そのファイルに取得された NS アーカイブのリストを書き込みます。省略した場合は、NSA リストの最大サイズである nn::boss::NN_BOSS_NSALIST_MAX_SIZE の値が設定されたことになります。

NSA リストのサイズは以下の式で計算することができます。

NSA リストのサイズ = 54 + 236 * リストに含まれる NS アーカイブの数

最大 1000 個の NS アーカイブの情報を含んだリストを取得しますので、NSA リストの最大サイズは 236,054 Byte となります。最大個数の NS アーカイブの情報を含んだリストが取得される可能性がなく、また NSA リスト用に確保されるファイル容量を削減したい場合のみ、明示的に値を設定してください。

Cancel()Download() の処理を中断することができます。中断の要求は非同期で処理されるため、この関数は必ず成功します。

4.2.3.3. 取得結果のチェック

NSA リストが取得できたかどうかは GetResult() で確認することができます。

コード 4-49. 取得結果のチェックに使用する関数
nn::boss::TaskResultCode nn::boss::NsaList::GetResult(void);
u32 nn::boss::NsaList::GetHttpStatusCode(void);

GetResult() の返り値は NSA リストがダウンロードで使用したタスクの実行結果です。TASK_SUCCESS が返されたときは NSA リストの取得が完了しています。

GetResult()HTTP_ERROR_*SSL_ERROR_* を返したときは通信中のエラーが発生しています。また、GetHttpStatusCode() では、サーバーから返ってきた HTTP ステータスコードを取得することができます。エラー時に詳細情報を取得する場合に使用してください。

4.2.3.4. 正当性と更新のチェック

ダウンロードした NSA リストの正当性は CheckValidity() でチェックすることができます。また、NSA リストの更新の確認には、GetDigest() で取得するダイジェスト値を利用することができます。

コード 4-50. 正当性と更新のチェックに使用する関数
bool nn::boss::NsaList::CheckValidity(void* pWorkBuf, size_t workBufSize);
nn::Result nn::boss::NsaList::GetDigest(u8* pDigestBuf, size_t digestBufSize);

pWorkBuf workBufSize には、作業用バッファとそのサイズを指定します。作業用バッファのサイズは 256 バイト以上でなければなりません。また、取得した NSA リストのサイズが大きい場合は、それ以上のサイズで確保することで処理速度が速くなる可能性があります。CheckValidity()false を返した場合、リストの内容が壊れている可能性があります。

GetDigest() で取得するダイジェスト値は、NSA リストの更新の確認に利用することができます。ダイジェスト値は英数字 40 文字で返されますので、pDigestBufdigestBufSize で指定するバッファは 41 バイト以上のサイズである必要があります。

ダイジェスト値は、同じ属性の指定で取得したリストに更新がなければ同じ値となります。リスト全体の更新を判断することはできますが、NS アーカイブごとの更新の判断はできません。

4.2.3.5. NSA リストの解析

取得した NSA リストの解析は Parse() で行います。

コード 4-51. NSA リストの解析に使用する関数
nn::boss::NsaList::ParseResult nn::boss::NsaList::Parse(
        u32* pOutParseCount, 
        nn::boss::NsaList::NsaInformation pNsaInformationArray[], 
        u32 nsaInformationArraySize, 
        void* pWorkBuf, size_t workBufSize, u32 nsaFirstPos = 0);

pNsaInformationArray nsaInformationArraySize には、解析結果を格納する NsaInformation 構造体の配列とその要素数を指定します。配列に格納されたリストの件数は pOutParseCount に格納されます。

pWorkBuf workBufSize には、解析で使用する作業用バッファとそのサイズを指定します。作業用バッファのサイズは 256 バイト以上でなければなりません。作業用バッファのサイズを大きくすることで、取得した NSA リストの件数が多くなったときの処理速度を速くすることができます。

返り値に 0 が返されたときはすでに全件の解析が完了しています。負の値が返されたときはエラーが発生したことを示しています。正の値が返されたときは pNsaInformationArray のサイズが小さく、一度の呼び出しで解析が完了できなかったことを示しています。この場合は nsaFirstPos に前回の返り値を指定することで、続きから解析を続行することができます。

NsaInformation 構造体は以下のように定義されています。

コード 4-52. NsaInformation 構造体の定義
struct nn::boss::NsaList::NsaInformation
{
    char fileName[32];
    u32  fileSize;
    u32  updateEpochTime;
    char attribute1[10];
    char attribute2[10];
    char attribute3[10];
    u8   caption[150];
};

それぞれのメンバに NS アーカイブの情報が格納されています。

表 4-23. NsaInformation 構造体のメンバに格納されている情報

メンバ

格納されている情報

fileName

ファイル名

fileSize

ファイルサイズ

updataEpochTime

更新日時(1970/01/01 00:00:00(GMT)からの経過秒数)

attribute1attribute3

アーカイブに設定されている属性 1 ~ 属性 3 の内容

caption

説明文(文字コードは UTF-8、最大 50 文字)

NS アーカイブの URL

NADL タスクで指定する NS アーカイブの URL は以下のように構成されています。

https://npdl.cdn.nintendowifi.net/p01/nsa/(BOSS コード)/(タスク ID)/(ファイル名)?(クエリ文字列)

クエリ文字列のパラメータには「lm」があります。このパラメータに更新日時(updateEpochTime の値)を指定した NADL タスクでアーカイブを取得すると、更新日時が異なる場合に 404 (Not Found) を返すようになります。これは NS アーカイブを差し換えて CDN キャッシュと NS アーカイブの内容が一致しない期間が発生したときに、NSA リストで取得した内容とアーカイブの内容に齟齬が生じないようにするために指定します。

キャッシュ制御への利用

前回の取得時に保存しておいた NsaInformation 構造体の updateEpochTime メンバの内容と今回の内容を比較することで、NS アーカイブの簡易なキャッシュ制御を行うことができます。ただし、3DS 本体で計時している時計は誤差やユーザーの手による変更の影響があるため、更新日時の比較は updateEpochTime の値同士で行う必要があります。

4.2.4. データアップロードタスク

データアップロードタスクは任意のデータサーバーに HTTP/HTTPS 接続でデータをアップロードするタスクです。

図 4-10. データアップロードタスクによるアップロード

アップロードデータ 受信サーバー BOSS データ (4) 実行時刻を迎えると、指定されたURLへBOSSがデータをアップロードする (1) タスクとデータを登録する タスク (2) BOSSがBOSSストレージに保存する (3) タスクの実行を指示する データ アプリケーション 拡張セーブデータ領域 BOSSストレージ データ タスク

データアップロードタスクでは、消尽回数が残っている限りアップロードが実行されますが、一度アップロードに成功するとタスクを再登録しない限り同じデータを再度アップロードすることはありません。
また、すでにタスクに登録されているデータとは異なるデータをアップロードしたい場合、タスクの再利用はできませんので、新たにタスクを登録する必要があります。

タスクの登録直後に実行(開始の指示)を行うように実装してください。タスクの登録中に電源が切断された場合はタスクやデータの情報が不正なものになります。電源が切断されたことで開始できなかったタスクは nn::boss::Task::GetState()TASK_REGISTERED を返すかどうかで識別することができます。再度タスクを登録する場合は、UnregisterTask() で該当のタスクを削除してから行ってください。

補足:

データアップロードタスクを利用するには、弊社への申請が必要です。

4.2.5. DataStore アップロードタスク

DataStore アップロードタスクは、NEX ライブラリのデータストア機能を利用して、データをアップロードするタスクです。

アップロードしたデータを参照できるユーザーを指定することができ、ユーザー間のデータのやり取りに利用することができます。

DataStore アップロードタスクは、いつの間に通信を経由することで、アプリケーションが起動していないタイミング(スリープ中など)でも NEX ライブラリのデータストア機能を利用することができます。

図 4-11. DataStore アップロードタスクによるアップロード

DataStoreサーバー BOSS (5) DataStoreライブラリがデータをDataStoreサーバーにアップロードする (1) タスクとデータを登録する タスク (2) BOSSがBOSSストレージに保存する (3) タスクの実行を指示する データ アプリケーション 拡張セーブデータ領域 BOSSストレージ データ タスク NEX(DataStoreライブラリ) (4) 実行時刻を迎えると、BOSSがDataStoreライブラリにアップロードを指示する データ

DataStore アップロードタスクでは、消尽回数が残っている限りアップロードが実行されますが、一度アップロードに成功するとタスクを再登録しない限り同じデータを再度アップロードすることはありません。
また、すでにタスクに登録されているデータとは異なるデータをアップロードしたい場合、タスクの再利用はできませんので、新たにタスクを登録する必要があります。

タスクの登録直後に実行(開始の指示)を行うように実装してください。タスクの登録中に電源が切断された場合はタスクやデータの情報が不正なものになります。電源が切断されたことで開始できなかったタスクは nn::boss::Task::GetState()TASK_REGISTERED を返すかどうかで識別することができます。再度タスクを登録する場合は、UnregisterTask() で該当のタスクを削除してから行ってください。

アップロードしたデータがダウンロード可能になるまでには、最大で 20 秒のタイムラグがあります。

補足:

NEX ライブラリの機能を利用していますが、アプリケーションに NEX ライブラリ自体を含める必要はありません。

データストア機能の詳細については、NEX ライブラリのドキュメントを参照してください。

4.2.6. DataStore ダウンロードタスク

DataStore ダウンロードタスクは、NEX ライブラリのデータストア機能を利用して、データをダウンロードするタスクです。

ダウンロードしたデータの情報を記録していますので、ユーザーが参照できるデータのうち、まだダウンロードされていないものだけをダウンロードします。

DataStore ダウンロードタスクは、いつの間に通信を経由することで、アプリケーションが起動していないタイミング(スリープ中など)でも NEX ライブラリのデータストア機能を利用することができます。

図 4-12. DataStore ダウンロードタスクによるダウンロード

DataStoreサーバー BOSS データ (3) DataStoreサーバーからデータをダウンロードする (1) タスクを登録して開始させる タスク (4) ダウンロードされたデータはDataStoreライブラリ経由でBOSSに渡される アプリケーション 拡張セーブデータ領域 BOSSストレージ NSD タスク NEX(DataStoreライブラリ) (2) 実行時刻を迎えると、BOSSがDataStoreライブラリにデータのダウンロードを指示する (5) BOSSがデータをNSDフォーマットで保存する データ

ダウンロードされたデータは NSD フォーマットで BOSS ストレージに保存されます。そのため、データの読み込みには NADL タスクでダウンロードしたデータと同様の関数を利用します。また、NADL タスクでダウンロードされたデータと同様に、BOSS ストレージが一杯になったときは古いデータ(シリアル ID の若いデータ)から自動的に削除されます。

タスクの登録時におしらせ発行設定を行うことで、新着データがダウンロードされた際におしらせリストにおしらせを投稿することができます。

補足:

NEX ライブラリの機能を利用していますが、アプリケーションに NEX ライブラリ自体を含める必要はありません。

データストア機能の詳細については、NEX ライブラリのドキュメントを参照してください。

4.3. プレゼンス機能

ニンテンドー DS の NITRO-DWC や TWL-DWC ではアプリケーションごとに実装して管理する必要があったフレンド間通信を、3DS では本体機能と SDK でサポートします。

補足:

プレゼンス機能については、「システムアプリ・アプレット仕様書」を一読しておくことを推奨します。

4.3.1. 概要

3DS ではすべてのゲームから利用できる共通のフレンドリストを本体に持ち、本体標準の機能として HOMEメニューからほかのユーザーを登録したり削除したりすることができます。したがって、各アプリケーションがフレンドリストを管理するインターフェースを個別に実装する必要はなく、ユーザーがアプリケーションごとに登録をしなおす必要もなくなりました。

また、フレンドの Mii やひとことコメント、今遊んでいるゲームのタイトルなどを、本体機能として取得・表示できるようになりました。アプリケーションはこのうちの一部の情報を API を呼ぶだけで設定・取得でき、自分とフレンドの情報は自動で同期が行われます。このための通信処理はデーモンプロセスがバックグラウンドで行うため、送受信の詳細についてアプリケーションが気にする必要はありません。

ネットワーク機能の拡張ライブラリである NEX と連携し、オンライン対戦中の自分のグループにフレンドを招待することや、逆にフレンドのグループに合流することをサポートする合流機能があります。合流機能の使用方法については、「NEX プログラミングマニュアル - サーバサービス機能編」を参照してください。

4.3.2. ユーザーアカウント

プレゼンス機能は独自のアカウントを持ち、そのアカウント内の情報をフレンドと共有します。アカウントの ID はシステムから自動で付与されます。

4.3.2.1. ユーザー ID の種類

プレゼンス機能のユーザーを識別する ID として、プリンシパル IDローカルフレンドコードの 2 種類の ID があります。どちらの ID も、本体初期化ごとに別の値が割り振られます。

プリンシパル ID

フレンドサーバーに初回接続したときに割り振られる、完全固有の ID です。他者と衝突することはありませんが、フレンドサーバーに初めて接続するまでは無効な値となっており、使用することができません。

ローカルフレンドコード

本体に固有の ID を元に生成される、プレゼンス機能用のユーザー識別 ID です。発生頻度は極めて低いものの、衝突を完全に防ぐことはできません。

 

また、これらの ID を元に構成される副次的な ID として、フレンドキーフレンドコードがあります。

フレンドキー

プリンシパル ID とローカルフレンドコードを包括的に扱う概念です。原則的には、プリンシパル ID が発行されるまではローカルフレンドコードを使用し、発行後はプリンシパル ID を使用します。

フレンドコード

プリンシパル ID から生成される、人間が扱うための 12 桁の数字です。プリンシパル ID と一対一で相互変換可能で、簡易な誤り検出能力を持ちます。

4.3.2.2. アカウント内情報

プレゼンス機能を介してフレンドに公開したりフレンドから取得できたりする情報には、以下のようなものがあります。

  • フレンドキー
  • プレイ中ゲームのタイトル
  • ゲームモード説明文字列
  • プレイ中ゲームの合流情報(NEX との連携用)
  • Mii
  • 表示名(Mii の名前)
  • 本体プロフィール情報(国や地域設定など)
  • フレンド関係
  • お気に入りゲーム
  • ひとことコメント

アプリケーションはこのうちの一部を設定・取得することができます。また、アプリケーションが直接設定・取得できない項目でも、HOMEメニューなどの本体機能で設定・確認が可能なものがあります。

表 4-24. プレゼンス情報の設定と取得

項目

自分の情報の設定・編集

フレンドの情報の取得・確認

アプリケーション

本体機能

アプリケーション

本体機能

フレンドコード

不可

不可

不可

フレンドキー

不可

不可

不可

プレイ中ゲームのタイトル

不可

不可

ゲームモード説明文字列

不可

不可

プレイ中ゲームの合流情報

不可

不可

Mii

不可

表示名(Mii の名前)

不可

本体プロフィール情報
(国や地域設定など)

不可

不可

フレンド関係

不可

お気に入りゲーム

不可

不可

ひとことコメント

不可

不可

4.3.3. フレンドリストの管理

3DS では、フレンドリストへのユーザーの登録や削除はフレンドリスト(システムアプレット)で行われ、本体に保存されます。

このフレンドリストは 3DS のすべてのゲームタイトルに共通で利用することができます。

4.3.3.1. フレンドリストへの登録

フレンドリストにほかのユーザーを登録するには、以下の 3 通りの方法があります。

フレンドコードの入力

フレンドリスト(システムアプレット)で相手のフレンドコードを入力することで、その相手をフレンドリストに登録することができます。入力時にはフレンドコードの有効性をフレンドサーバーに接続して確認するため、インターネットに接続できる環境が必要です。自分のフレンドコードはフレンドリスト(システムアプレット)で確認することができます。

先に相手のフレンドコードを入力した側は、相手が自分をフレンドリストに追加してくれるまでその相手とはフレンド関係成立待ち状態となり、双方向に相手を登録したのが確認された時点でフレンド関係が成立します。

ローカル通信での登録

フレンドリスト(システムアプレット)で「近くの人とフレンド登録」を選択することにより、ローカル通信を利用して相手をフレンドリストに登録することができます。この場合にインターネット接続は必要ありません。

こちらの方法ではその場で双方がフレンドリストに登録されるので、成立待ちをすることなくフレンド関係が成立します。

アプリケーション内での登録

フレンドリストへの登録に必要な情報を送受信し、アプリケーション内でフレンドリストへの登録を行うことができます。

この方法による登録を行う場合は、必ず双方のユーザーの同意を確認した上で行ってください。

補足:

アプリケーションでの登録方法については「4.3.9. フレンド登録」を参照してください。 登録に関する UI はアプリケーションで実装する必要があります。

注意:

アプリケーション内でのフレンドの登録を、すれちがい通信や事前に相手を特定できない状態でのローカル通信を介して行うことは禁止されています。

詳細については、ガイドラインの「インターネット通信」にある「フレンド登録」を参照してください。

4.3.3.2. フレンド関係の状態

フレンド関係は、双方が相手をフレンドリストに登録することで成立します。それぞれの登録状況により、フレンド関係は以下のような状態を遷移します。

フレンド関係成立待ち

先に相手をフレンドリストへ登録して、相手が自分をフレンドリストに追加してくれるのを待っている状態です。相手が自分をフレンドリストに追加したことはフレンドサーバーに登録されるので、相手がフレンドリストへ追加後、自分が最初にフレンドサーバーへ接続したときにフレンド関係成立済みへ移行します。

フレンド関係成立済み

双方が相手をフレンドとして登録している状態です。接続状態やプレイ中のゲームタイトルなどをリアルタイムに通知するにはこの状態になっている必要があります。

補足:

フレンドサーバーでは、各ユーザーがフレンドリストに登録している他ユーザーのリストを保持します。そして、互いにフレンドリストに登録しているユーザー同士をフレンド関係成立済みとして扱います。したがって、ローカル通信で成立したフレンド関係は、成立後に各々のユーザーが一度でもオンラインになるまで、フレンドサーバーではフレンド関係成立済みとして扱われません。通常、この状態を意識する必要はありませんが、特別に区別する場合にはローカルフレンド状態と呼びます。

フレンド関係解消

フレンド関係が成立していた相手のフレンドリストから、自分が削除された状態です。自分のフレンドリストから勝手に相手が削除されることはありませんが、この状態になると、実際には相手がオンラインになっていたとしても、あたかもオフラインであるかのように見えます。フレンド関係が成立していたときに相手から受け取った情報には引き続きアクセスできます。相手がもう一度自分のフレンドコードを入力することで、フレンド関係成立済みへと戻ります。

4.3.4. 初期化と終了

FRIENDS ライブラリの API を使用するには、事前に nn::friends::Initialize() を呼んでライブラリを初期化する必要があります。また、ライブラリをそれ以上使用しない場合は nn::friends::Finalize() で終了処理を行ってください。

FRIENDS ライブラリは、内部に初期化回数のカウンタを持ちます。nn::friends::Initialize() を複数回呼んだ場合は、ライブラリを完全に終了するまでに同じ回数 nn::friends::Finalize() を呼ぶ必要があります。ライブラリが初期化済み状態であるかを調べるには nn::friends::IsInitialized() を使用します。

コード 4-53. 初期化と終了の API
nn::Result nn::friends::Initialize();
nn::Result nn::friends::Finalize();
bool nn::friends::IsInitialized();

4.3.5. オンラインとオフライン

3DS がフレンドサーバーに接続している状態をオンライン、接続していない状態をオフラインと呼びます。オンライン状態では、そのときプレイ中のゲームのタイトルやモードなどがフレンドに公開される一方、同時にオンラインになっているフレンドからも同様の情報を受け取ることができます。オフライン状態でも、フレンドの名前や Mii、プロフィール情報などの一部の情報は取得可能ですが、それらが最新のものであることは保証されません。

4.3.5.1. 自律接続とログイン

3DS がオンライン状態になるには、以下の 2 通りの方法があります。

自律接続

フレンドプレゼンスデーモンは、アプリケーションから明示的な要求がなくても、可能であれば自律的にフレンドサーバーとの接続を試みてオンライン状態になろうとします。この挙動はデーモンマネージャによって管理され、デーモンマネージャの判断によって確認なく停止される可能性があります。

また、アプリケーションが明示的にこの挙動を停止したい場合は、デーモンマネージャにフレンドプレゼンスデーモンを停止させるよう指示する必要があります。

ログイン

アプリケーションが能動的にプレゼンスの機能を使用する場合、自律接続の状態とは関係なくオンライン状態になるためにログイン要求を出すことができます。

ログイン要求を出すには nn::friends::Login() を使用します。この関数が成功するとバックグラウンドで接続処理が走り、処理が終了した時点で pEvent に渡した nn::os::Event オブジェクトがシグナル状態になります。非同期処理の結果は nn::friends::GetLastResponseResult() で取得できます。ログイン要求を出した上でオンライン状態になっている場合、nn::friends::HasLoggedIn()true を返します。

ただし、オンライン対応タイトルでない場合はログイン要求を出さないでください。ログイン要求を出すには、事前に AC(自動接続)ライブラリを使用してインターネット接続を確立しておく必要があります。そのため、ログイン要求は、特にインターネット接続を要求する部分でのみ行うようにしてください。

ログインが必要なくなった場合、ログアウトすることでログイン要求を取り消すことができます。また、明示的にログアウトしなくても、FRIENDS ライブラリの終了時やアプリケーションの終了時にもログイン要求は取り消されます。ログアウト後も自律接続が有効な場合はそのままオンライン状態が維持されます。

コード 4-54. ログイン API
nn::Result nn::friends::Login(nn::os::Event* pEvent);
nn::Result nn::friends::Logout();
nn::Result nn::friends::GetLastResponseResult();
bool nn::friends::HasLoggedIn();
コード 4-55. ログイン処理のサンプルコード
nn::Result result;
nn::os::Event event(false);

result = nn::friends::Login(&event);
if (result.IsSuccess())
{
    // Login が成功した場合は非同期処理が発生
    if (event.Wait(nn::fnd::TimeSpan::FromMinutes(2)))
    {
        result = nn::friends::GetLastResponseResult(); 
        if (result.IsSuccess())
        {
            // ログイン成功
        }
        else
        {
            // エラー処理
        }
    }
    else
    {
        // ログイン処理の失敗が確定したわけではないので要求のキャンセルが必要
        nn::friends::Logout();
    }
}
else
{
    // エラー処理
} 
event.Finalize();
注意:

nn::friends::Login() で開始される非同期処理は、自律接続によってすでにオンライン状態だった場合はすぐに終了しますが、この操作によりオンライン状態へ遷移する場合は数秒から数十秒程度の時間が必要になります。この非同期処理の終了を待たずにログイン要求を取り消す場合は、バックグラウンドではフレンドサーバーへの接続処理が継続中ですので、明示的に nn::friends::Logout() でログイン要求を取り消してください。

4.3.5.2. フレンドサーバーやフレンドとの情報の同期

ユーザーの情報はオンライン時にフレンドサーバーに送られ、フレンドサーバーを介してフレンドに共有されます。

自分の情報の更新はオフラインでも行えますが、その更新はすぐにフレンドサーバーへは送信されません。オフライン中に行われた自分の情報の更新は、フレンドサーバーに接続されてオンラインになったときにフレンドサーバーに送信されて同期されます。

一方、オンライン中に行われた更新は、フレンドプレゼンスデーモンのバックグラウンド動作によって随時フレンドサーバーに送信されます。ただし、同一項目のフレンドサーバーへの送信間隔は 10 秒に 1 回以下になるようにライブラリで制限されているため、これより細かい間隔で情報を更新しても、フレンドサーバーやフレンドに対して随時性のある同期はできません。あまり高い頻度でフレンドサーバーと通信しないでください。詳しくはガイドラインを参照してください。

コード 4-56. 自分情報の更新 API
nn::Result nn::friends::UpdateGameModeDescription(
        const char16 description[nn::friends::MODE_DESCRIPTION_SIZE]);

nn::friends::UpdateGameModeDescription() は、自分のゲームモード説明文字列を更新します。

description で指定した文字列は、自分のフレンドカードの表示に反映されます。フレンドカードには、最大幅の内蔵フォント(日米欧リージョンは "%"、そのほかのリージョンはひらがな、漢字、ハングルなど)で 16 文字分の幅で 2 行まで表示されます。なお、フレンドカードで表示することのできる文字は本体のリージョンによって以下のように異なり、表示できない文字は "?"(0xE011)に変換されます。

表 4-25. フレンドカードに表示することのできる文字

リージョン

表示することのできる文字

日本・米州・欧州

内蔵フォントの日米欧文字に含まれる文字

中国

内蔵フォントの簡体字に含まれる文字

韓国

内蔵フォントのハングルに含まれる文字

台湾

内蔵フォントの繁体字に含まれる文字

図 4-13. 短い間隔で自分のゲームモード説明文字列を更新した場合のバックグラウンド通信の例

UpdateGameModeDescription("MODE1") 時間 [秒] UpdateGameModeDescription("MODE2") UpdateGameModeDescription("MODE3") UpdateGameModeDescription("MODE4") "MODE1" 0 12 16 20 22 "MODE2" "MODE4"

ユーザー情報の一部はフレンドサーバーに保存されます。これにより、フレンドと同時にオンラインにならなくても、相手が最後にオンラインだった時点の情報を、自分がオンラインになったときにフレンドサーバーから受け取ることができます。また、最後に受け取ったフレンドの情報は 3DS 本体に保存されるため、自分がオフラインであってもフレンドの情報を取得することができます。ただし、双方がオンラインでない状態で取得したフレンドの情報は、最新のものではない可能性があります。プレイ中のゲームモードなど、自分とフレンドが同時にオンラインになっていないと意味がない情報はフレンドサーバーに保存されず、双方がオンラインであるときにしか取得できません。

  • 双方がオンラインでないと取得できない情報
    • プレイ中ゲームのタイトル
    • ゲームモード説明文字列
    • プレイ中ゲームの合流情報
  • いつでも取得できる情報
    • フレンドキー
    • Mii
    • 表示名(Mii の名前)
    • 本体プロフィール情報(国や地域設定など)
    • フレンド関係
    • お気に入りゲーム
    • ひとことコメント

フレンドの情報を取得するための API については、「4.3.6. フレンドの情報の取得」を参照してください。

4.3.5.3. 非公開モード

自分の情報はフレンドリスト(システムアプレット)から非公開に指定することができます。

公開レベルには、

  • プレイ中ゲームのみ非公開
  • プレゼンス情報全体の非公開

の 2 段階があります。

プレゼンス情報全体を非公開にした場合、自分がオンラインであってもフレンドにはオフラインであるかのように見えます。すなわち、プレイ中のゲームに関する情報はフレンドに送られず、接続状態もオフラインとして扱われます。また、フレンドサーバーにも自分の情報の更新をしないため、Mii や本体プロフィール情報なども最後に公開設定でオンラインになったときに指定していたものが伝えられます。フレンドの情報は、プレゼンス情報を公開しているときと同様に取得することができます。

4.3.5.4. 自分の情報の取得

自分の情報を取得するための API には、以下のものが用意されています。

コード 4-57. 自分の情報を取得する API
nn::friends::PrincipalId nn::friends::GetMyPrincipalId();
bool nn::friends::IsMyPreferenceValid();
nn::Result nn::friends::GetMyPreference(
        bool* pIsPublicMode, bool* pIsShowGameName);
nn::Result nn::friends::GetMyProfile(nn::friends::Profile* pProfile);
nn::Result nn::friends::GetMyPresence(nn::friends::MyPresence* pMyPresence);
nn::Result nn::friends::GetMyScreenName(char16 screenName[SCREEN_NAME_SIZE]);
nn::Result nn::friends::GetMyMii(nn::mii::StoreData* pMiiData);

フレンドサーバーに一度も接続されていない場合、プリンシパル ID には INVALID_PRINCIPAL_ID が返されます。

フレンドリスト(システムアプレット)を一度も起動していない場合、フレンド同士の通知設定がユーザー自身の手で行われていないため、IsMyPreferenceValid()false を返します。また、GetMyPreference() の各引数にはデフォルトの値(true)が格納されます。

「じぶんの Mii」を作成していない場合やフレンドリスト(システムアプレット)を一度も起動していない場合でも、GetMyProfile() で取得する本体プロフィール情報には、本体の初回起動時や本体設定での設定内容が反映されます。

「Mii スタジオ」で「じぶんの Mii」を作成していない場合、GetMyScreenName() は先頭が NULL の空文字列(2 文字目以降は不定)を返します。GetMyMii() は空の Mii データを返します。取得した Mii データが有効であるかどうかの判断や表示を行うには、CFL ライブラリを利用しなければなりません。

4.3.6. フレンドの情報の取得

FRIENDS ライブラリを使用してフレンドの情報を取得する場合、フレンドキーもしくはプリンシパル ID でフレンドを指定します。フレンドリストに登録済みのユーザーのフレンドキーは nn::friends::GetFriendKeyList() で取得できますが、通信で外部から受け取ったフレンドキーやプリンシパル ID を指定することもできます。

4.3.6.1. フレンドキーの取得

フレンドリストに登録されたユーザーのフレンドキーを取得するには nn::friends::GetFriendKeyList() を使用します。この関数は、フレンドリストの offset 番目から size 人分のユーザーのフレンドキーを順番に pFriendKeyList が指すバッファへ格納しようと試み、実際に格納したフレンドキーの個数を pNum が指すバッファに返します。取得できたフレンドキーの個数が指定した size よりも少なかった場合、フレンドキーが格納されなかったバッファの値は保証されません。

コード 4-58. フレンドキーの取得 API
nn::Result nn::friends::GetFriendKeyList(
        nn::friends::FriendKey* pFriendKeyList, size_t* pNum,
        size_t offset = 0, size_t size = nn::friends::FRIEND_LIST_SIZE);

ローカル通信で得られるローカルフレンドコードは、プライバシーを保護する意味合いからスクランブルがかけられているため、そのままではフレンドを特定するためのローカルフレンドコードとして利用することができません。そのため、FRIENDS ライブラリにはスクランブルを解除するための API が用意されています。

コード 4-59. ローカルフレンドコードのスクランブルを解除する API
nn::Result nn::friends::UnscrambleLocalFriendCode(
        nn::friends::LocalFriendCode* pLocalFriendCodeList,
        const nn::uds::ScrambledLocalFriendCode* pScrambledLocalFriendCodeList,
        size_t size = 1);

pScrambledLocalFriendCodeList size には、ローカル通信で取得したローカルフレンドコード(スクランブル解除前)のリストとその個数を指定します。

スクランブルを解除した結果は pLocalFriendCodeList に格納されますので、size で指定した個数を格納できるように指定してください。フレンド以外のスクランブルは解除できません。フレンドリストに登録されていないユーザーのローカルフレンドコードは INVALID_LOCAL_FRIEND_CODE となります。

複数のフレンドが同じローカルフレンドコードを持つ可能性を考慮して、アプリケーションを実装してください。スクランブル解除後のローカルフレンドコードは一意の値ではありませんので、極めて低い確率で同じローカルフレンドコードを持つ複数のフレンドがフレンドリストに登録されている可能性があります。

4.3.6.2. フレンドの情報の取得

フレンドの情報を取得するには、取得 API にフレンドキーもしくはプリンシパル ID のリストを渡します。結果は、渡したフレンドキーもしくはプリンシパル ID のリストと同じ個数・順番で、各関数の第 1 引数に渡したバッファに格納されます。指定したフレンドキーやプリンシパル ID に該当するユーザーがフレンドリストに含まれていなかった場合は、無効なデータが返ります。

補足:

ローカルフレンドコードのリストを指定してフレンドの情報を取得する API は用意されていません。ローカルフレンドコードをフレンドキーに格納して渡すことは可能ですが、発生頻度は極めて低いものの衝突を完全には回避できないローカルフレンドコードの性質上、同じローカルフレンドコードを持つ複数のユーザーがフレンドリストに登録されている可能性があります。フレンドリストから取得したフレンドキーであれば、ローカルフレンドコードが等しいユーザーがリスト内に複数いたとしても、その中でプリンシパル ID を持たないユーザーは必ず 1 人以下となる(フレンド関係成立前に衝突した場合、あとから登録したユーザーが残ります)ため、一意にリスト内のユーザーを特定することができます。

コード 4-60. フレンドの情報取得 API
nn::Result nn::friends::GetFriendPresence(
        nn::friends::FriendPresence* pFriendPresenceList,
        const nn::friends::FriendKey* pFriendKeyList, size_t size = 1);
nn::Result nn::friends::GetFriendPresence(
        nn::friends::FriendPresence* pFriendPresenceList,
        const nn::friends::PrincipalId* pPrincipalIdList, size_t size = 1);
nn::Result nn::friends::GetFriendScreenName(
        char16 (*pScreenNameList)[nn::friends::SCREEN_NAME_SIZE],
        const nn::friends::FriendKey* pFriendKeyList, size_t size = 1,
        bool replaceForeignCharacters = true, u8* pFontRegionList = NULL);
nn::Result nn::friends::GetFriendScreenName(
        char16 (*pScreenNameList)[nn::friends::SCREEN_NAME_SIZE],
        const nn::friends::PrincipalId* pPrincipalIdList, size_t size = 1,
        bool replaceForeignCharacters = true, u8* pFontRegionList = NULL);
nn::Result nn::friends::GetFriendMii(
        nn::mii::StoreData* pMiiDataList,
        const nn::friends::FriendKey* pFriendKeyList, size_t size = 1);
nn::Result nn::friends::GetFriendMii(
        nn::mii::StoreData* pMiiDataList,
        const nn::friends::PrincipalId* pPrincipalIdList, size_t size = 1);
nn::Result nn::friends::GetFriendProfile(
        nn::friends::Profile* pProfileList,
        const nn::friends::FriendKey* pFriendKeyList, size_t size = 1);
nn::Result nn::friends::GetFriendProfile(
        nn::friends::Profile* pProfileList,
        const nn::friends::PrincipalId* pPrincipalIdList, size_t size = 1);
nn::Result nn::friends::GetFriendAttributeFlags(
        bit32* pAttributeFlagsList,
        const nn::friends::FriendKey* pFriendKeyList, size_t size = 1);
nn::Result nn::friends::GetFriendAttributeFlags(
        bit32* pAttributeFlagsList,
        const nn::friends::PrincipalId* pPrincipalIdList, size_t size = 1);

4.3.7. 通知

フレンドサーバーとの接続状態の変化や、フレンド情報の更新をポーリングすることなく把握するために、アプリケーションが必要とする項目を指定して、フレンドプレゼンスデーモンから変更通知を受け取ることができます。

4.3.7.1. 通知の種類

アプリケーションが受け取れる通知は以下の 9 種類です。変更の種類と、フレンドが関係する通知ではそのフレンドのフレンドキーが一緒に通知されます。通知が届いた場合、それより以前に API から取得したフレンドの情報は古くなっている可能性がありますので、アプリケーションの都合にあわせて適切なタイミングで取得しなおしてください。

表 4-26. 通知の種類

定義

説明(再取得が必要な API)

NOTIFICATION_ONLINE

自分がオンライン状態になったことを表します。フレンドキーには無効な値が格納されます。

NOTIFICATION_OFFLINE

自分がオフライン状態になったことを表します。フレンドキーには無効な値が格納されます。

NOTIFICATION_FRIEND_ONLINE

フレンドがオンラインになったことを表します。

GetFriendPresence(), GetFriendScreenName(), GetFriendMii(), GetFriendProfile(), GetFriendAttributeFlags()

NOTIFICATION_FRIEND_PRESENCE

フレンドのゲームモードが変更されたことを表します。

GetFriendPresence()

NOTIFICATION_FRIEND_MII

フレンドの Mii が変更されたことを表します。

GetFriendScreenName(), GetFriendMii()

NOTIFICATION_FRIEND_PROFILE

フレンドのプロフィールが変更されたことを表します。

GetFriendProfile()

NOTIFICATION_FRIEND_OFFLINE

フレンドがオフラインになったことを表します。

GetFriendPresence()

NOTIFICATION_BECOME_FRIEND

ユーザーとフレンド関係が成立したことを表します。

GetFriendPresence(), GetFriendScreenName(), GetFriendMii(), GetFriendProfile(), GetFriendAttributeFlags()

NOTIFICATION_INVITATION

フレンドからお誘いを受けたことを表します。

GetFriendPresence()

4.3.7.2. 通知に関する API

フレンドプレゼンスデーモンからの通知を受け取るためには、以下の API を使用します。

コード 4-61. 通知 API
nn::Result nn::friends::AttachToEventNotification(nn::os::Event* pEvent);
nn::Result nn::friends::SetNotificationMask(bit32 mask);
u32 nn::friends::GetEventNotification(
        nn::friends::EventNotification* pEventNotificationList,
        size_t size = 1, bool* pHasOverflowed = NULL);
イベントの登録

nn::friends::AttachToEventNotification()pEvent に初期化済みの nn::os::Event オブジェクトへのポインタを渡すと、通知発生時にこのイベントがシグナル状態になります。

通知マスクの設定

受け取りたい通知の種類を指定するには、nn::friends::SetNotificationMask()masknn::friends::NotificationMask 列挙体の各列挙子の論理和を渡します。ここで有効にしなかった種類の通知は、デーモンから届いても nn::friends::AttachToEventNotification() で渡した nn::os::Event オブジェクトはシグナル状態にならず、nn::friends::GetEventNotification() で取得される通知履歴からも除外されます。

通知内容の取得

通知の内容は、nn::friends::GetEventNotification() で取得できます。実際に取得できた通知の数が返り値になるので、この関数が 0 を返すまで繰り返しコールすれば、デーモンのバッファにたまっている通知を発生順に残らず取得することができます。

補足:

通知は nn::friends::Initialize() で FRIENDS ライブラリを初期化したときからたまり始めます。ある時点から新しく通知を取得し始めたい場合は、nn::friends::SetNotificationMask()mask に 0 を渡してすべての通知を除外した上で nn::friends::GetEventNotification() を 1 回呼べば、過去に届いた通知をすべて消去できます。

注意:

通知履歴は、システムに届くプライベートな通知も含めて 128 件までデーモンのバッファに保存され、あふれた場合は古いものから順に消去されます。通知があふれた場合は、情報の古くなった箇所がわからなくなりますので、速やかに自分の接続状態とフレンドの情報を再取得してください。

4.3.8. エラーハンドリング

FRIENDS ライブラリの API が失敗を返した場合、その nn::Result オブジェクトを nn::friends::ResultToErrorCode()result に渡すことでエラーコードが取得できます。

コード 4-62. エラーコード取得 API
u32 ResultToErrorCode(const nn::Result& result);

この関数が 0 を返した場合は、エラーコードを表示する必要はありません。また、ログインに失敗したがすぐに自動でリトライするときなど、ユーザーが不都合を感じずにゲームを継続できる場合も、エラーコードを表示する必要はありません。

注意:

nn::Result オブジェクトの内容をエラーコードへ変換する際、nn::friends::ResultToErrorCode() は、そのときのデーモンの状態に関する情報を付加することがあります。したがって、エラーコードを取得するときは、失敗の結果を受け取ったあと速やかに nn::friends::ResultToErrorCode() を呼んでください。

4.3.9. フレンド登録

アプリケーション内でのフレンド登録は、nn::friends::GetMyApproachContext() で取得したフレンド登録用の情報を相互に受信し合い、双方のユーザーが受信した情報を nn::friends::AddFriendWithApproach() に渡して呼び出すことで行います。

コード 4-63. アプリケーション内でのフレンド登録に使用する関数
nn::Result nn::friends::GetMyApproachContext(
                        nn::friends::ApproachContext* pApproachContext);
nn::Result nn::friends::AddFriendWithApproach(
                        nn::os::Event* pEvent,
                        const nn::friends::ApproachContext& approachContext);

これらの関数を使用するには、FRIENDS ライブラリの初期化は必要ですが、フレンドサーバーへのログインは必要ありません。また、事前に nn::cfg::Initialize() で CFG ライブラリの初期化を行っておく必要があります。

GetMyApproachContext() で取得する nn::friends::ApproachContext 構造体は、すでに暗号化されていますので、送受信のためにアプリケーション側で暗号化などを行わなくてもかまいません。

AddFriendWithApproach() の呼び出しが成功する(返された ResultIsSuccess()true を返す)とフレンドが登録され、非同期処理が発生します。それ以外の Result が返された場合には、非同期処理は発生しません。非同期処理の完了は、pEvent に渡したイベントがシグナル状態になることでアプリケーションに通知されます。非同期処理の結果は nn::friends::GetLastResponseResult() で取得しますが、ライブラリの仕様上、複数の非同期処理を同時にハンドリングすることができないことに注意してください。

なお、この関数で登録されたフレンドはローカルフレンドですので、双方がインターネットに接続し、フレンドサーバーでのフレンド関係が成立するまでプレゼンス情報を取得することができません。非同期処理ではフレンドサーバーへの登録処理を試みます。双方のユーザーがオンライン状態で登録処理が成功した場合でも、非同期処理の完了直後はプレゼンス情報を取得できない可能性があります。

受信したフレンド登録用の情報(ApproachContext 構造体)をアプリケーションで表示するために、以下の関数で表示名や Mii のデータを取得することができます。

コード 4-64. フレンド登録用の情報から情報を取得する関数
nn::Result nn::friends::GetApproachContextScreenName(
                        char16 screenName[nn::friends::SCREEN_NAME_SIZE],
                        const nn::friends::ApproachContext& approachContext,
                        bool replaceForeignCharacters, u8* pFontRegion);
nn::Result nn::friends::GetApproachContextMii(
                        nn::mii::StoreData* pMiiData,
                        const nn::friends::ApproachContext& approachContext);

GetApproachContextScreenName() は、フレンドの表示名を screenName に渡したバッファに返します。このとき、replaceForeignCharacterstrue を指定していた場合は、取得する表示名に含まれている、本体リージョンの標準フォントでは表示できない文字を「?」に置き換えて取得することができます。置き換えを行わず、ほかのリージョンのであっても表示できるようにする場合は false を指定し、pFontRegionu8 型の変数へのポインタを渡してください。pFontRegion には、どのリージョンの標準フォントであれば表示可能なのかが nn::friends::FontRegion 列挙体の値で返されます。

GetApproachContextMii() は、フレンドの Mii を表示するための情報を pMiiData に渡したバッファに返します。取得した Mii の情報を扱うには、別途「似顔絵ライブラリ」が必要です。