GS2-Datastore

バイナリデータストレージ機能

GS2-Datastore を利用することで、任意のバイナリデータをサーバーに保存できます。

アップロードしたデータにはアクセス権限設定が可能で、「全員に公開」「指定したユーザーIDのプレイヤーに公開」「自分のみ」のいずれかを指定できます。

GS2-Datastore は UGC やレースゲームのゴーストデータのようなデータをアップロードするのが主たる目的で、プレイヤーのユーザーデータを保存するのには必ずしも適切ではありません。

なぜなら、所持品の所持数量をバイナリデータとして保存していると、セーブデータやアプリ本体の改竄でアイテムを増殖したり、入手量を不正に増加させた改造アプリでのプレイを許してしまうためです。 所持品の所持数量であれば、GS2-Inventory のような専用のマイクロサービスを利用することで、そのような不正行為は行えなくなります。

ユーザーデータの中でも、コンフィグの設定値など、改竄されてもゲームバランスに影響を与えることがないデータについて保存することを否定するわけではありません。

アーキテクチャ

GS2-Datastore はバイナリデータの保存場所などを記録したメタデータを管理しており、実際のバイナリデータの保存には外部のクラウドストレージを利用しています。 そのため、アップロード・ダウンロード処理は複数のステップを必要とします。

この処理の流れは、Game Engine 向けの SDK ではラップした高レベルなAPIが用意されているため、気にする必要はありませんが 各種プログラミング言語向けの SDK には高レベルなAPIは用意されていませんので、利用者自身で複数ステップを処理する必要があります。

アップロードプロセス

actor Player
participant "GS2-Datastore#Namespace"
participant "Cloud Storage"
Player -> "GS2-Datastore#Namespace" : Prepare upload
"GS2-Datastore#Namespace" -> Player : Cloud Storage URL
Player -> "Cloud Storage" : Upload Payload
"Cloud Storage" -> Player : OK
Player -> "GS2-Datastore#Namespace" : Done upload report
"GS2-Datastore#Namespace" -> "Cloud Storage" : Check exists
"GS2-Datastore#Namespace" -> Player : OK

ダウンロードプロセス

actor Player
participant "GS2-Datastore#Namespace"
participant "Cloud Storage"
Player -> "GS2-Datastore#Namespace" : Prepare download
"GS2-Datastore#Namespace" -> Player : Cloud Storage URL
Player -> "Cloud Storage" : Download
"Cloud Storage" -> Player : Payload

アップロード処理中のダウンロード

GS2-Datastore はすでにアップロードしたデータを更新することができます。 Prepare ReUpload を呼び出してから Done Upload を呼び出すまでの間は、更新前の古いファイルがダウンロード可能な状態となり、中途半端なデータがダウンロードされることはありません。

アップロードデータの過去バージョンの取得

GS2-Datastore では過去30日分の過去バージョンにアクセスできるようになっています。 これは削除済みのデータにも適用されており、削除リクエスト後 30 日後に実際に削除されます。 ただし、法的要件によってデータの削除を行った場合は、この条件に当てはまらないことがあります。

実装例

データのアップロード

    var result = await gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).UploadAsync(
        name: "dataObject-0001",
        scope: "public",
        data: data
    );

    var item = await result.ModelAsync();
    var dataObjectId = item.DataObjectId;
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto Future = Domain->Upload(
        "dataObject-0001", // name
        data, // data
        "public", // scope
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

データのダウンロード(データオブジェクトID指定)

    var binary = await gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DownloadAsync(
        dataObjectId: dataObjectId
    );
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto Future = Domain->Download(
        dataObjectId, // dataObjectId
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

データのダウンロード(ユーザーIDとデータオブジェクト名を指定)

    var binary = await gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: "user-0001"
    ).DataObject(
        dataObjectName: "dataObject-0001"
    ).DownloadByUserIdAndDataObjectNameAsync(
    );
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto Future = Domain->DownloadByUserIdAndDataObjectName(
        "dataObject-0001", // dataObjectName
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

自分がアップロードしたデータのダウンロード(データオブジェクト名を指定)

    var binary = await gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    ).DownloadOwnData(
    );
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->DownloadOwnData(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

詳細なリファレンス