GS2-News

おしらせ配信機能

ゲーム内のお知らせをHTML形式で配信するための仕組みです。 配信するHTMLコンテンツは hugo(https://gohugo.io/) で生成する必要があります。

GS2-News は記事データをアップロードすると、GS2 側で hugo によるビルドを行い、静的な Web コンテンツとしてホスティングします。ゲームクライアントは配信された一時 URL に対してアクセスすることでお知らせを閲覧できます。

graph LR
  Author["運営者"] -- "ZIP アップロード" --> GS2["GS2-News"]
  GS2 -- "hugo build (公開/非公開の<br/>全パターンを事前生成)" --> CDN["CDN (HTML / ZIP)"]
  Player["プレイヤー"] -- "GetContentsUrl" --> GS2
  GS2 -- "URL + Cookie<br/>(1時間有効)" --> Player
  Player -- "WebView or ZIP DL" --> CDN

hugo

hugo は静的 Webページを生成するためのジェネレーターです。

ページのレイアウトやデザインを決定するためのテンプレートと、マークダウン形式で記述した記事データをビルドすることでHTMLファイルを生成します。 hugo の利用方法については、hugoの公式サイト または 各種解説サイトをご確認ください。

GS2 では最小限の hugo テンプレートと記事データのサンプルを GitHub で公開しています。

https://github.com/gs2io/gs2-news-sample

イベントと連動する記事

GS2-News では、GS2-Schedule で管理するイベントと連動する記事データを管理できます。

記事データのマークダウンファイルの Front Matter に以下の記述をすることで、連携を有効化できます。

x_gs2_scheduleEventId: ${GS2-Schedule のイベントGRN}

また、GS2-Schedule の Event ID には以下のプレースホルダが利用できます。

プレースホルダ 置換される値
{region} リージョン名
{ownerId} GS2のオーナーID

記述例は以下です。

x_gs2_scheduleEventId: grn:gs2:{region}:{ownerId}:schedule:schedule-0001:event:event-0001

イベントが開催中の場合のみ表示する記事や、特定イベント期間のみ非表示にする記事などを、運用ツールから切り替え操作することなく自動制御できます。

記事データのサイズ制限

GS2-News にアップロードする記事データは全体で 100MB以内に収める必要があります。

また、記事データのビルドには --buildDrafts オプションは含まれません。Front Matter で draft: true と記述されたコンテンツは配信されません。

記事データのビルド

GS2-News で配信する記事データはzip で圧縮した hugo のテンプレートと記事ファイルをアップロードすることで更新します。 その後、GS2-News は記事データをビルドして配信します。

記事データ内に x_gs2_scheduleEventId の記述がある場合は、各記事が公開状態・非公開状態 のそれぞれのバージョンを事前にビルドしてWebサーバーに配置します。

その後、プレイヤーが記事データにアクセスしようとした時に現在のイベントの開催状況から最も適切なコンテンツのURLをプレイヤーに応答します。

sequenceDiagram
  participant U as 運営者
  participant N as GS2-News
  participant CDN as CDN
  U->>N: PrepareUpdateCurrentNewsMaster
  N-->>U: アップロード用URL
  U->>N: HTTP PUT (zip)
  U->>N: UpdateCurrentNewsMaster
  N->>N: hugo で全パターン<br/>ビルド
  N->>CDN: 静的コンテンツ配置

進捗状況は Progress モデル(generated / patternCount)で確認でき、長時間のビルドが完了したかをポーリングできます。

記事データのアクセス権

記事データは事前に全てのパターンをビルドして配信すると説明しました。 悪意のあるプレイヤーがURLを特定して、本来ならまだ配信してはならないコンテンツにアクセスできてはいけません。

この問題を避けるために、GS2-News は Cookie を用いたアクセス制限を実装しています。 現在有効なコンテンツのURLを取得するとともに、そのURLにアクセスするための Cookie 情報を配信します。 ブラウザにその Cookie を設定した上でコンテンツURLにアクセスすることで、コンテンツのダウンロードが可能となります。

取得した Cookie は有効期限が設定されており、1時間が経過するとその Cookie ではコンテンツにアクセスできなくなります。 長時間ゲームをプレイしているプレイヤーに対しては、再取得を行ってください。

Zip形式で Webコンテンツをダウンロード

GS2-News がホスティング した HTML に WebView を使用してアクセスする方法だけでなく、GS2-News がビルドした HTMLファイル全体を zip 形式でダウンロードすることも可能です。

こちらの方法を利用すれば、zip でダウンロードした HTML コンテンツをローカルストレージに展開し、それをブラウザで表示することでより快適にコンテンツを閲覧できるようになるとともに、同じコンテンツを何度もダウンロードしなくて良くなります。

zip ファイルをダウンロードしなおすべきかを判断するために、GS2-News はテンプレートファイルのハッシュ値と、GS2-Scheduleに基づいて公開状態になっているコンテンツのハッシュ値を取得できるようにしています。 これらの値を使用することで、zip ファイルを再ダウンロードするべきか判断することが可能となります。

ハッシュ値 説明
TemplateHash アップロードされたテンプレート ZIP のハッシュ値。テンプレート自体が更新された場合に変化します。
ContentHash 現在のイベント開催状況から決定される、公開状態のコンテンツ集合のハッシュ値。イベントの開始・終了で変化します。

スクリプトトリガー

GS2-News ではスクリプトトリガーは提供していません。

マスターデータ管理

マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。

マスターデータの種類には以下があります。

  • テンプレート: ニュース表示のHugoテンプレート
  • 記事: Markdown形式のニュース記事

マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。

トランザクションアクション

GS2-News ではトランザクションアクションを提供していません。

実装例

コンテンツURLを取得

GetContentsUrl の戻り値には Cookie のリストと、BrowserUrl(WebView での閲覧用)/ZipUrl(ZIP ダウンロード用)が含まれます。 取得した Cookie を WebView もしくは HTTP クライアントに登録した上で、それぞれの URL にアクセスしてください。

    var domain = gs2.News.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).News(
    );
    var result = await domain.GetContentsUrlAsync(
    );

    List<EzSetCookieRequestEntry> cookies = new List<EzSetCookieRequestEntry>();
    var items = result.ToList();
    foreach (var item in items)
    {
        var entry = await item.ModelAsync();
        cookies.Add(entry);
    }
    var browserUrl = domain.BrowserUrl;
    var zipUrl = domain.ZipUrl;
    const auto Domain = Gs2->News->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->News(
    );
    const auto Future = Domain->GetContentsUrl(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    TArray<EzSetCookieRequestEntryPtr> Cookies;
    const auto It = Future->GetTask().Result();
    foreach (auto Item in It)
    {
        const auto Future2 = Item.Model();
        Future2->StartSynchronousTask();
        if (Future2->GetTask().IsError()) return false;
        Cookies.Add(Future->GetTask().Result());
    }
    const auto BrowserUrl = Domain->BrowserUrl;
    const auto ZipUrl = Domain->ZipUrl;

WebView での表示例 (Unity)

unity-webview を使用して、取得した Cookie を WebView に設定してから BrowserUrl を開くことで、お知らせを表示できます。

    foreach (var cookie in cookies) {
        webView.SetCookie(browserUrl, cookie.Key, cookie.Value);
    }
    webView.LoadURL(browserUrl);
    webView.SetVisibility(true);

記事データの一覧を取得

各記事は SectionTitleContentFrontMatter などを保持しています。タイトル一覧を独自 UI で表示し、選択された記事のみ WebView で表示するといった構成も可能です。

    var domain = gs2.News.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.NewsesAsync(
    ).ToListAsync();
    var templateHash = domain.TemplateHash;
    var contentHash = domain.ContentHash;
    const auto Domain = Gs2->News->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto It = Domain->Newses(
    );
    TArray<Gs2::UE5::News::Model::FEzNewsPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

キャッシュ再ダウンロード判定

ZIP ダウンロード方式を採用する場合、TemplateHashContentHash をローカルに保存しておき、起動時に再取得した値と比較することで、不要な ZIP の再ダウンロードを避けることができます。

    if (savedTemplateHash != domain.TemplateHash ||
        savedContentHash != domain.ContentHash) {
        // 再ダウンロードを実行
    }

詳細なリファレンス