GS2 Blog Game Server Services(https://gs2.io/) の最新情報をお届けします 2024-03-18T20:47:04+09:00 kazutomo Hatena::Blog hatenablog://blog/10328749687181239785 GS2-Inventory の巨大インベントリのアイテム入手・消費時のスクリプトを実装できるようになりました hatenablog://entry/6801883189091800317 2024-03-18T20:47:04+09:00 2024-03-18T20:47:04+09:00 はじめに GS2-Inventory はアイテムの所持数量を管理するマイクロサービスです。 なかでも巨大インベントリは 64bit整数値 の範囲を超えるアイテムの所持数量を管理するマイクロサービスです。クッキークリッカーを代表するインフレ系ゲームのようなゲームでクッキーの所持数量を管理するために利用できます。 今回の更新で解消された課題 これまで巨大インベントリのアイテム入手・消費時のスクリプトは設定できませんでした。 追加された機能の詳細 新しく巨大インベントリのアイテム入手・消費時のスクリプトを設定できるようになりました。 スクリプトの戻り値では、処理を継続するかに加えて、入手量・消費量の… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Inventory はアイテムの所持数量を管理するマイクロサービスです。<br /> なかでも巨大インベントリは 64bit整数値 の範囲を超えるアイテムの所持数量を管理するマイクロサービスです。</p><p>クッキー<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%AF%A5%EA%A5%C3%A5%AB%A1%BC">クリッカー</a>を代表するインフレ系ゲームのようなゲームでクッキーの所持数量を管理するために利用できます。</p> </div> <div class="section"> <h3 id="今回の更新で解消された課題">今回の更新で解消された課題</h3> <p>これまで巨大インベントリのアイテム入手・消費時の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は設定できませんでした。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>新しく巨大インベントリのアイテム入手・消費時の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を設定できるようになりました。<br /> <a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の戻り値では、処理を継続するかに加えて、入手量・消費量の対して倍率による補正をかけられるようになりました。</p> <pre class="code lang-lua" data-lang="lua" data-unlink>result = <span class="synType">{</span> permit=<span class="synConstant">true</span>, rate=<span class="synConstant">1.5</span> <span class="synType">}</span> </pre><p>のように<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を設定することで、報酬の量を1.5倍にすることができます。<br /> ただし、巨大インベントリが扱う値の性質上若干の計算誤差が生じます。</p> </div> kazutomo GS2-Quest でクエスト失敗時の報酬量調整が可能となりました hatenablog://entry/6801883189091798542 2024-03-18T20:39:42+09:00 2024-03-18T20:39:42+09:00 はじめに GS2-Quest はインゲームの挑戦料・報酬・クリア状況を管理するためのマイクロサービスです。 今回の更新で解消された課題 GS2-Quest はクエストごとに報酬として「クリア時に得られる報酬」「初めてクリアした時に得られる報酬」「失敗した時に得られる報酬」の3種類を設定できます。一番直感から外れるのが今回改善が行われた「失敗した時に得られる報酬」でしょう。 元々、クエストに失敗した場合に開始時に消費したスタミナを返却するような用途を想定して機能を実装していました。クリア時の報酬はインゲームで宝箱を開け損ねたりした時のためにマスターデータに定義するのは入手可能な最大量で、クエスト… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Quest はインゲームの挑戦料・報酬・クリア状況を管理するためのマイクロサービスです。</p> </div> <div class="section"> <h3 id="今回の更新で解消された課題">今回の更新で解消された課題</h3> <p>GS2-Quest はク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トごとに報酬として「クリア時に得られる報酬」「初めてクリアした時に得られる報酬」「失敗した時に得られる報酬」の3種類を設定できます。</p><p>一番直感から外れるのが今回改善が行われた「失敗した時に得られる報酬」でしょう。<br /> 元々、ク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トに失敗した場合に開始時に消費したスタミナを返却するような用途を想定して機能を実装していました。</p><p>クリア時の報酬はインゲームで宝箱を開け損ねたりした時のためにマスターデータに定義するのは入手可能な最大量で、ク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トの終了報告<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>でマスターデータで定義された報酬を最大量として、それ以下であれば任意の数量に結果を変更することができるようにしていました。<br /> しかし、前述の通り失敗時の報酬はスタミナの返却を想定していたため、失敗報告をした際にマスターデータに定義された全数量を報酬として入手する仕様としてました。</p><p>しかし、利用者のさまざまな<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E6%A1%BC%A5%B9%A5%B1%A1%BC%A5%B9">ユースケース</a>を伺っているうちに「失敗時にスタミナを返却しない代わりにインゲーム内で入手したゴールドだけは入手できるようにしたい」というような当初想定していなかった仕様があることがわかりました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>ク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トの終了報告<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>に失敗報告する際にも数量報告が可能となりました。<br /> そのため、さきほどのゴールドの仕様であれば「クリア時に得られる報酬」「失敗した時に得られる報酬」にそれぞれゴールドの入手可能最大量を指定しておき終了報告にはク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トの成功・失敗に関わらずインゲームで入手したゴールドの量を報告することで一部の報酬を得ることができるようになりました。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Fapi_reference%2Fquest%2Fgame_engine%2F%23end" title="GS2-Quest SDK for Game Engine API リファレンス" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/api_reference/quest/game_engine/#end">docs.gs2.io</a></cite></p> </div> kazutomo GS2-Exchange の非同期交換に待機時間の部分スキップ機能が追加されました hatenablog://entry/6801883189091795843 2024-03-18T20:29:00+09:00 2024-03-18T20:29:00+09:00 はじめに GS2-Exchange はなんらかの対価としてプレイヤーが所持するリソースを消費して、何らかのリソースを入手するための交換処理を実装するためのマイクロサービスです。GS2-Exchange の交換には即時交換と、非同期交換の2モードが存在し非同期交換モードでは交換APIを呼び出してから現実時間で一定時間経過後にリソースの入手が行えます。 遠征や合成などゲーム内にリアル時間の経過を求める機能を実装するために活用できます。 これまで有していた課題 交換の待機時間を短縮する仕組み自体はありましたが、即座に受け取り可能とするもののみで「4時間の待機時間のうち2時間を短縮する」「待機時間の5… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Exchange はなんらかの対価としてプレイヤーが所持するリソースを消費して、何らかのリソースを入手するための交換処理を実装するためのマイクロサービスです。</p><p>GS2-Exchange の交換には即時交換と、非同期交換の2モードが存在し非同期交換モードでは交換<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出してから現実時間で一定時間経過後にリソースの入手が行えます。<br /> 遠征や合成などゲーム内にリアル時間の経過を求める機能を実装するために活用できます。</p> </div> <div class="section"> <h3 id="これまで有していた課題">これまで有していた課題</h3> <p>交換の待機時間を短縮する仕組み自体はありましたが、即座に受け取り可能とするもののみで「4時間の待機時間のうち2時間を短縮する」「待機時間の50%を短縮する」といった操作は行えませんでした。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>新しく GS2-Exchange の入手アクションに SkipByUserId が追加されました。<br /> <a href="https://docs.gs2.io/ja/api_reference/exchange/stamp_sheet/#gs2exchangeskipbyuserid">https://docs.gs2.io/ja/api_reference/exchange/stamp_sheet/#gs2exchangeskipbyuserid</a></p><p>このアクションではスキップの方法として4つの方式を用意しています。</p> <table> <tr> <th> complete </th> <td> 即座に受け取り可能な状態にする </td> </tr> <tr> <th> minutes </th> <td> スキップする絶対分数を指定して待機時間を短縮する </td> </tr> <tr> <th> totalRate </th> <td> 待機時間全体における割合を指定して待機時間を短縮する </td> </tr> <tr> <th> remainRate </th> <td> 残りの待機時間における割合を指定して待機時間を短縮する </td> </tr> </table><p>この機能を利用することで 「待機時間の5分スキップチケット」 といったアイテムを実装することが可能となります。</p> </div> kazutomo 時刻補正が可能なAPIの種類が追加されました hatenablog://entry/6801883189090175109 2024-03-12T16:33:54+09:00 2024-03-12T16:33:54+09:00 はじめに 運営型のゲームにおいてイベントのような時刻に関わる処理は重要な要素です。 プロジェクトによってはQAのためにメンテナンスモードにしてプレイヤーを追い出し、サーバーの時刻設定を変更して動作確認するという話すら聞いたことがあり、時刻が関わる処理はデバッグやQAがしづらいという問題があります。GS2ではこのような問題を解決するために、アクセストークン発行時に時刻のオフセットを指定できるようにしています。 そのため、QAスタッフは1週間進んだタイムゾーンで動作確認できたり、ユニットテストですぐさま1時間後のアクセスとしてAPIを呼び出すようなことができるようになっています。 これまでの課題 … <div class="section"> <h3 id="はじめに">はじめに</h3> <p>運営型のゲームにおいてイベントのような時刻に関わる処理は重要な要素です。<br /> プロジェクトによってはQAのためにメンテナンスモードにしてプレイヤーを追い出し、サーバーの時刻設定を変更して動作確認するという話すら聞いたことがあり、時刻が関わる処理は<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C7%A5%D0%A5%C3%A5%B0">デバッグ</a>やQAがしづらいという問題があります。</p><p>GS2ではこのような問題を解決するために、アクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ン発行時に時刻のオフセットを指定できるようにしています。<br /> そのため、QAスタッフは1週間進んだ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%BF%A5%A4%A5%E0%A5%BE%A1%BC%A5%F3">タイムゾーン</a>で動作確認できたり、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E6%A5%CB%A5%C3%A5%C8%A5%C6%A5%B9%A5%C8">ユニットテスト</a>ですぐさま1時間後のアクセスとして<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出すようなことができるようになっています。</p> </div> <div class="section"> <h3 id="これまでの課題">これまでの課題</h3> <p>時刻オフセットはアクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンに含まれているため、アクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを使用しないユーザーIDを直値で指定するようなアクセスでは時刻オフセットを適用できない課題がありました。<br /> これは、時刻オフセットを適用するためにいちいちアクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを作成して<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出す必要があり、しかもアクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを使ったアクセス手段のない<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>には時刻オフセットを適用できない課題がありました。</p> </div> <div class="section"> <h3 id="追加された機能">追加された機能</h3> <p>新しく「時刻オフセット<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ン」が追加されました。<br /> 時刻オフセット<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンはアクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンに非常に似た機能でユーザー認可機能をもたず、時刻補正のみを担う<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンでアクセス<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ン同様 GS2-Auth を使って発行できます。</p><p>~ByUserId のようなユーザーIDを指定して<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出せるインターフェースに時刻オフセット<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを指定するパラメーターが追加されており、ここに時刻オフセット<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを指定することで時刻補正が可能となります。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Fapi_reference%2Fauth%2Fsdk%2F%23issuetimeoffsettokenbyuserid" title="GS2-Auth SDK API リファレンス" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/api_reference/auth/sdk/#issuetimeoffsettokenbyuserid">docs.gs2.io</a></cite></p> </div> kazutomo シーズンレーティング機能の提供を開始しました hatenablog://entry/6801883189086892276 2024-02-28T22:23:38+09:00 2024-02-28T22:23:38+09:00 はじめに 近年のPvPゲームではシーズンごとにランクマッチが実施され、ブロンズ→シルバー→ゴールド→プラチナ のように昇格していくスタイルが一般化しました。 GS2 も元々レーティング機能は持っていましたが、シーズンで区切ることはできず、ランクではなくレート値の増減のみで表現するレートマッチのみの提供でした。そこで、近年増えてきたランクマッチ用に GS2-SeasonRating 機能の提供を開始しました。 機能の概要 まずはシーズンを定義します。シーズンにはランクごとの情報を表す複数ティアーと、開催期間を設定します。 ティアーには勝負によって変動するポイントの増減量のレンジと、ティアーに昇格… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>近年の<a class="keyword" href="https://d.hatena.ne.jp/keyword/PvP">PvP</a>ゲームではシーズンごとにランクマッチが実施され、ブロンズ→シルバー→ゴールド→プラチナ のように昇格していくスタイルが一般化しました。<br /> GS2 も元々レーティング機能は持っていましたが、シーズンで区切ることはできず、ランクではなくレート値の増減のみで表現するレートマッチのみの提供でした。</p><p>そこで、近年増えてきたランクマッチ用に GS2-SeasonRating 機能の提供を開始しました。</p> </div> <div class="section"> <h3 id="機能の概要">機能の概要</h3> <p>まずはシーズンを定義します。シーズンにはランクごとの情報を表す複数ティアーと、開催期間を設定します。<br /> ティアーには勝負によって変動するポイントの増減量のレンジと、ティアーに昇格した際に得られるボーナスポイントを持つことができます。<br /> また、ティアーごとに試合に参加するだけでポイントが減算される参加料を設定することも可能です。<br /> ポイントは GS2-Experience の経験値として管理され、ティアーの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%EF%E7%C3%CD">閾値</a>も GS2-Experience のランクアップ経験値<a class="keyword" href="https://d.hatena.ne.jp/keyword/%EF%E7%C3%CD">閾値</a>をつかって管理します。</p><p>つまり、GS2-SeasonRating は GS2-Experience の経験値を試合結果に応じて増減するインターフェースということになります。</p> </div> <div class="section"> <h3 id="機能の詳細">機能の詳細</h3> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>仕様は従来のレーティング機能と対して変わりません。<br /> プレイヤーは対戦開始時に投票用紙(Ballot)を取得し、対戦終了時に Ballot を使って投票(Vote)します。</p><p>全員の試合結果投票を受け付けるか、最初の投票を受け付けてから5分が経過すると集計処理が実行されます。<br /> 集計処理は受け付けた試合結果の内容を多数決で判定し、多数派が報告した結果を採用します。</p><p>結果に応じて ティアーの変動幅を参加人数(順位のパターン数)で均等に分け、各プレイヤーの順位に基づいてポイントの変動量を決定し、GS2-Experience の経験値を加減算します。</p> </div> <div class="section"> <h3 id="使い方">使い方</h3> <div class="section"> <h4 id="ランクマッチを開始">ランクマッチを開始</h4> <p>GS2-Matchmaking のネームスペース設定に GS2-SeasonRating との連携設定が増えています。<br /> ここを有効化し、対象となる GS2-SeasonRating のネームスペースを設定することで、マッチメイキング完了時に GS2-SeasonRating に GS2-Matchmaking のギャザリング名と同じ名前でランクマッチセッションが作成されます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20240228/20240228222218.png" width="503" height="564" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>以降は GS2-Matchmaking のギャザリング名を sessionName として指定して<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出してください。</p> </div> <div class="section"> <h4 id="投票用紙を取得">投票用紙を取得</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> item <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.SeasonRating.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Ballot( <span class="synStatement"> seasonName:</span> <span class="synConstant">&quot;rating-0001&quot;</span>, <span class="synStatement"> sessionName:</span> gathering.Name, <span class="synStatement"> numberOfPlayer:</span> <span class="synConstant">4</span> ).ModelAsync(); <span class="synType">var</span> body <span class="synStatement">=</span> item.Body; <span class="synComment">// 投票用紙本体</span> <span class="synType">var</span> signature <span class="synStatement">=</span> item.Signature; <span class="synComment">// 投票用紙の妥当性を検証する署名</span> </pre> </div> <div class="section"> <h4 id="投票を実行">投票を実行</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> result <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.SeasonRating.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).VoteAsync( <span class="synStatement"> ballotBody:</span> ballot.Body, <span class="synComment">// 投票用紙本体</span> <span class="synStatement"> ballotSignature:</span> ballot.Signature, <span class="synComment">// 投票用紙の妥当性を検証する署名</span> <span class="synStatement"> gameResults:</span> <span class="synStatement">new</span> List&lt;Gs2.Unity.Gs2SeasonRating.Model.EzGameResult&gt; { <span class="synStatement">new</span> Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() { Rank <span class="synStatement">=</span> <span class="synConstant">1</span>, UserId <span class="synStatement">=</span> <span class="synConstant">&quot;user-0001&quot;</span>, }, <span class="synStatement">new</span> Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() { Rank <span class="synStatement">=</span> <span class="synConstant">2</span>, UserId <span class="synStatement">=</span> <span class="synConstant">&quot;user-0002&quot;</span>, }, <span class="synStatement">new</span> Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() { Rank <span class="synStatement">=</span> <span class="synConstant">2</span>, UserId <span class="synStatement">=</span> <span class="synConstant">&quot;user-0003&quot;</span>, }, <span class="synStatement">new</span> Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() { Rank <span class="synStatement">=</span> <span class="synConstant">3</span>, UserId <span class="synStatement">=</span> <span class="synConstant">&quot;user-0004&quot;</span>, }, } ); </pre> </div> <div class="section"> <h4 id="レート値の変化のハンドリング">レート値の変化のハンドリング</h4> <p>GS2-Experience のステータスを Subscribe して判定できます。</p> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> item <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.Experience.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Status( <span class="synStatement"> experienceName:</span> <span class="synConstant">&quot;rating&quot;</span>, <span class="synStatement"> propertyId:</span> seasonModel.SeasonModeld, ).Subscribe( changedStatus <span class="synStatement">=&gt;</span> { <span class="synType">var</span> experienceValue <span class="synStatement">=</span> changedStatus.ExperienceValue; <span class="synComment">// ランクポイント</span> <span class="synType">var</span> experienceValue <span class="synStatement">=</span> changedStatus.RankValue; <span class="synComment">// ランク</span> } ); </pre> </div> </div> <div class="section"> <h3 id="参考情報">参考情報</h3> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Fmicroservices%2Fseason_rating%2F" title="GS2-SeasonRating" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/microservices/season_rating/">docs.gs2.io</a></cite></p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Fapi_reference%2Fseason_rating%2F" title="GS2-SeasonRating リファレンス" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/api_reference/season_rating/">docs.gs2.io</a></cite></p> </div> kazutomo パフォーマンスチューニング用のツール提供を開始しました hatenablog://entry/6801883189085304105 2024-02-22T19:50:37+09:00 2024-02-23T01:31:07+09:00 はじめに 通信時間を最適化する一番のポイントは通信処理の並列化です。 依存関係のないデータ取得ではなるべく並列化することで、通信の待ち時間を最適化できます。ただ、今の実装が最適化できているかどうかを正確に判断するのは困難が伴います。 追加された機能 GS2-Log のアクセスログを APM(Application Performance Monitoring) で可視化するためのツールとして GS2-Telemetry をオープンソースで公開しました。 GS2-Telemetry は Open Telemetry というオープンなテレメトリデータ収集プロトコルに則って動作し、さまざまなオープン… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>通信時間を最適化する一番のポイントは通信処理の並列化です。<br /> 依存関係のないデータ取得ではなるべく並列化することで、通信の待ち時間を最適化できます。</p><p>ただ、今の実装が最適化できているかどうかを正確に判断するのは困難が伴います。</p> </div> <div class="section"> <h3 id="追加された機能">追加された機能</h3> <p>GS2-Log の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%AF%A5%BB%A5%B9%A5%ED%A5%B0">アクセスログ</a>を <a class="keyword" href="https://d.hatena.ne.jp/keyword/APM">APM</a>(Application Performance Monitoring) で可視化するためのツールとして GS2-Telemetry を<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>で公開しました。<br /> GS2-Telemetry は Open Telemetry というオープンなテレメトリデータ収集<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%C8%A5%B3%A5%EB">プロトコル</a>に則って動作し、さまざまな<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>を使ってテレメトリを可視化できます。</p> <div class="section"> <h4 id="リポジトリ"><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a></h4> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fgs2io%2Fgs2-telemetry" title="GitHub - gs2io/gs2-telemetry" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/gs2io/gs2-telemetry">github.com</a></cite></p><p>GS2-Telemetry に GS2-Log からデータを取得する期間などのパラメータを指定して起動すると、<br /> 指定したホスト・ポートに対して Open Telemetry <a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%C8%A5%B3%A5%EB">プロトコル</a>に基づいてテレメトリデータに変換し、送信します。</p><p>Open Telemetry サーバーであり、ビジュアライズツール機能を持つ Jaeger でデータを受け止めると、以下のようにユーザーIDごとにテレメトリが分類されます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20240222/20240222194606.png" width="1200" height="645" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>任意のユーザーを選択することで、そのユーザーの通信履歴を<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B7%A1%BC%A5%B1%A5%F3%A5%B5%A1%BC">シーケンサー</a>のようなビジュアルで可視化できます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20240222/20240222194844.png" width="1023" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>いつ頃どういった<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出し、処理にどれだけ時間がかかったかをわかるだけでなく<br /> GS2-Exchange で交換処理を実行した後、各マイクロサービスでアイテムの消費や入手といった処理がどいった時系列で発生したかをツリー形式で整理された状態で閲覧できます。</p><p>この機能はうまく活用すれば、パフォーマンスチューニングだけでなく開発中の動作確認も容易に行えるようなるはずです。</p> </div> </div> kazutomo GS2-Script でトランザクションを発行できるようになりました hatenablog://entry/6801883189076225502 2024-01-19T10:54:45+09:00 2024-01-19T10:54:45+09:00 はじめに GS2-Script は GS2 の機能を拡張するもので、任意のタイミングでクライアントから呼び出せるようなものではなく レベルアップした時や、アカウント引き継ぎを登録したときなどあらかじめ用意されたトリガーと関連づけることで、イベント駆動で実行できるスクリプトです。トランザクションとは、GS2内のリソースを増減する処理を指します。 機能追加された背景 GS2-Script からは GS2-SDK を利用できるため、もともとアイテムの増減といった処理は実装可能でした。 しかし、スクリプトの実行内容はクライアントから把握できないため、キャッシュへの値反映を行いたければ GS2-Exch… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Script は GS2 の機能を拡張するもので、任意のタイミングでクライアントから呼び出せるようなものではなく<br /> レベルアップした時や、アカウント引き継ぎを登録したときなどあらかじめ用意されたトリガーと関連づけることで、イベント駆動で実行できる<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>です。</p><p><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>とは、GS2内のリソースを増減する処理を指します。</p> </div> <div class="section"> <h3 id="機能追加された背景">機能追加された背景</h3> <p>GS2-Script からは GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> を利用できるため、もともとアイテムの増減といった処理は実装可能でした。<br /> しかし、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の実行内容はクライアントから把握できないため、キャッシュへの値反映を行いたければ<br /> GS2-Exchange で交換レートを用意して、交換処理を実行することで、交換処理の完了通知などを持ってクライアントに結果を反映する必要がありました。</p> </div> <div class="section"> <h3 id="機能追加された内容の詳細">機能追加された内容の詳細</h3> <p>GS2-Script のネームスペースに<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>設定が追加されました。<br /> さらに、GS2-Script に<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>実行関連の記法が追加されました。</p> <pre class="code lang-lua" data-lang="lua" data-unlink>transaction.execute(<span class="synType">{</span> consumeActions=<span class="synType">{}</span>, acquireActions=<span class="synType">{</span> transaction.service(<span class="synConstant">&quot;inventory&quot;</span>).acquire.acquire_simple_items_by_user_id(<span class="synType">{</span> namespaceName=<span class="synConstant">&quot;namespace&quot;</span>, inventoryName=<span class="synConstant">&quot;inventory&quot;</span>, acquireCounts=<span class="synType">{</span> <span class="synType">{</span> itemName=<span class="synConstant">&quot;item&quot;</span>, count=<span class="synConstant">1</span>, <span class="synType">}</span>, <span class="synType">}</span>, <span class="synType">}</span>) <span class="synType">}</span> <span class="synType">}</span>) </pre><p>acquireActions や consumeActions には各マイクロサービスの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>アクションを設定可能で<br /> ドキュメントにも GS2-Script での記述方法のサンプルが追加されています。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Fapi_reference%2Finventory%2Fstamp_sheet%2F" title="GS2-Inventory トランザクションアクション" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/api_reference/inventory/stamp_sheet/">docs.gs2.io</a></cite></p><p>これによって、GS2-Exchange などに交換レートを用意しなくても<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>から<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>を発行できるようになります。</p> </div> kazutomo GS2-Inventory のシンプルインベントリに入手・消費時のスクリプトを設定できるようになりました hatenablog://entry/6801883189075471824 2024-01-16T11:26:48+09:00 2024-01-16T11:26:48+09:00 はじめに GS2-Inventory はプレイヤーの所持品を管理するマイクロサービスです。 なかでも、シンプルインベントリはアイテム所持数の上限やインベントリの容量といった機能は持たない代わりに複数のアイテムをまとめて増減できる特徴があります。機能の詳細は以下の記事を参照ください。gs2.hatenablog.comスクリプトは、GS2 内のマイクロサービスで何かイベントが生じた時に追加の処理をする仕組みです。 GS2-Script に Lua で定義したスクリプト、もしくは Amazon Event Bridge を使用して AWS 上で処理を実行できます。 追加された機能の詳細 これまでス… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Inventory はプレイヤーの所持品を管理するマイクロサービスです。<br /> なかでも、シンプルインベントリはアイテム所持数の上限やインベントリの容量といった機能は持たない代わりに複数のアイテムをまとめて増減できる特徴があります。</p><p>機能の詳細は以下の記事を参照ください。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgs2.hatenablog.com%2Fentry%2F2023%2F07%2F10%2F000000" title="GS2-Inventory にシンプルインベントリ機能を追加しました - GS2 Blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://gs2.hatenablog.com/entry/2023/07/10/000000">gs2.hatenablog.com</a></cite></p><p><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は、GS2 内のマイクロサービスで何かイベントが生じた時に追加の処理をする仕組みです。<br /> GS2-Script に <a class="keyword" href="https://d.hatena.ne.jp/keyword/Lua">Lua</a> で定義した<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>、もしくは <a class="keyword" href="https://d.hatena.ne.jp/keyword/Amazon">Amazon</a> Event Bridge を使用して <a class="keyword" href="https://d.hatena.ne.jp/keyword/AWS">AWS</a> 上で処理を実行できます。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>これまでスタンダードインベントリの増減時の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は定義可能でしたが、シンプルインベントリに関しては未対応でした。<br /> 今回シンプルインベントリも<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の実行に対応しました。</p> <div class="section"> <h4 id="入手時に実行されるスクリプト">入手時に実行される<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a></h4> <pre class="code lang-lua" data-lang="lua" data-unlink>namespace = args.namespace inventoryName = args.inventoryName simpleItems = args.simpleItems userId = args.userId acquireCounts = args.acquireCounts result = <span class="synType">{</span> permit=permit, overrideAcquireCounts=overrideAcquireCounts <span class="synType">}</span> </pre> </div> <div class="section"> <h4 id="消費時に実行されるスクリプト">消費時に実行される<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a></h4> <pre class="code lang-lua" data-lang="lua" data-unlink>namespace = args.namespace inventoryName = args.inventoryName simpleItems = args.simpleItems userId = args.userId consumeCounts = args.consumeCounts result = <span class="synType">{</span> permit=permit, overrideConsumeCounts=overrideConsumeCounts <span class="synType">}</span> </pre> </div> </div> kazutomo 限界突破(凸)機能が追加されました hatenablog://entry/6801883189070035473 2023-12-27T11:00:00+09:00 2023-12-27T11:00:00+09:00 はじめに 限界突破とは、同一キャラクターを合成したり、特定の進化素材を使用することでキャラクターを強化する仕組みを指します。 限界突破を行うと、キャラクターのレベル上限が引き上げられ、より強力に育成することが可能となります。 機能追加の背景 GS2-Experience はレベルキャップの引き上げはサポートしていましたが、レアリティごとに初期値が異なったりするケースの取り回しは必ずしも扱いやすい状態とは言えませんでした。今回 GS2-Grade というマイクロサービスを追加することで、レベル上限の管理をより直感的に行えるようにしました。 追加された機能の詳細 GS2-Grade はグレードとレ… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>限界突破とは、同一キャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターを合成したり、特定の進化素材を使用することでキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターを強化する仕組みを指します。<br /> 限界突破を行うと、キャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターのレベル上限が引き上げられ、より強力に育成することが可能となります。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>GS2-Experience はレベルキャップの引き上げはサポートしていましたが、レアリティごとに初期値が異なったりするケースの取り回しは必ずしも扱いやすい状態とは言えませんでした。</p><p>今回 GS2-Grade というマイクロサービスを追加することで、レベル上限の管理をより直感的に行えるようにしました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>GS2-Grade はグレードとレベルキャップの組み合わせのみを管理し、グレードの上昇方法についてはスコープ外としています。<br /> 初期グレードはプロパティIDの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>にマッチするかどうかで設定が可能となっています。</p><p>具体的な例を示しましょう。<br /> 以下のような条件でグレードが定義されていたと仮定します。</p> <div class="section"> <h4 id="GradeEntryModel">GradeEntryModel</h4> <table> <tr> <th> グレード </th> <th> ゲーム上の表現 </th> <th> レベルキャップ </th> </tr> <tr> <td> 1 </td> <td> ☆☆★ </td> <td> 50 </td> </tr> <tr> <td> 2 </td> <td> ☆★★ </td> <td> 60 </td> </tr> <tr> <td> 3 </td> <td> ★★★ </td> <td> 70 </td> </tr> <tr> <td> 4 </td> <td> ★★★+1 </td> <td> 75 </td> </tr> <tr> <td> 5 </td> <td> ★★★+2 </td> <td> 80 </td> </tr> </table><p>キャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターには以下の種類があるとします。</p> </div> <div class="section"> <h4 id="ItemModel">ItemModel</h4> <table> <tr> <th> キャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターID </th> <th> 初期グレード </th> </tr> <tr> <td> N-0001 </td> <td> ☆☆★ </td> </tr> <tr> <td> N-0002 </td> <td> ☆☆★ </td> </tr> <tr> <td> N-0003 </td> <td> ☆☆★ </td> </tr> <tr> <td> R-0001 </td> <td> ☆★★ </td> </tr> <tr> <td> R-0002 </td> <td> ☆★★ </td> </tr> <tr> <td> R-0003 </td> <td> ☆★★ </td> </tr> <tr> <td> SR-0001 </td> <td> ★★★ </td> </tr> <tr> <td> SR-0002 </td> <td> ★★★ </td> </tr> <tr> <td> SR-0003 </td> <td> ★★★ </td> </tr> </table><p>この場合、初期グレード用の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>は</p> </div> <div class="section"> <h4 id="DefaultGrade">DefaultGrade</h4> <table> <tr> <th> グレード </th> <th> ItemModel に対して適用する<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a> </th> </tr> <tr> <td> 2 </td> <td> ^R-.*$ </td> </tr> <tr> <td> 3 </td> <td> ^SR-.*$ </td> </tr> </table><p>となります。(いずれにも該当しない場合初期グレードは1になります)</p><p>このような情報を組み合わせた型として</p> </div> <div class="section"> <h4 id="GradeModel">GradeModel</h4> <table> <tr> <th> 名前 </th> <th> 型 </th> </tr> <tr> <td> defaultGrades </td> <td> DefaultGrade[] </td> </tr> <tr> <td> experienceModelId </td> <td> レベルキャップを反映するGS2-Experience の経験値モデルID </td> </tr> <tr> <td> gradeEntries </td> <td> GradeEntryModel[] </td> </tr> </table><p>このようなデータをマスターデータとして GS2-Grade に登録します。</p><p>GS2-Grade にはグレードの更新<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>(加算・減算・上書き)が用意されており、グレードの値を書き換えると同一プロパティIDの GS2-Experience のランクキャップが自動的に更新されます。</p> </div> </div> <div class="section"> <h3 id="GS2-Enhance-の更新内容">GS2-Enhance の更新内容</h3> <p>GS2-Grade をより効率的に扱うための機能追加が GS2-Enhance に対して行われました。<br /> GS2-Enhance は素材となるアイテムやキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターを消費して経験値を得る仕組みでしたが、新しく限界突破(凸)をするための専用のインターフェースが追加されました。</p><p>GS2-Enhance がサポートする限界突破は同一キャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターや同一属性を合成するようなケースで利用できるもので、素材を使用した限界突破であれば、GS2-Exchangeなどを使用して素材を消費してグレードを加算する交換レートを用意するだけで実装できます。</p><p>GS2-Enhance は素材が同種のキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターであることを検証した上でグレードを上昇させる<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>を発行する仕組みをより簡便に実装できるようにすることを目的に機能追加されています。</p><p>そのため、GS2-Grade の GradeEntryModel には素材が同種であることを判定するための<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>を定義することが可能です。<br /> グレードごとに条件が設定できますので、グレード1 の時は同属性であればどんなキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターでもいいが、グレード3以降は完全に同種のキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターでなければ限界突破できないような設定も可能です。</p><p>GS2-Enhance で利用する同種判定のための<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>は以下の構造を持ちます。</p> <div class="section"> <h4 id="GradeEntryModel-1">GradeEntryModel</h4> <table> <tr> <th> 名前 </th> <th> 型 </th> </tr> <tr> <td> propertyIdRegex </td> <td> 限界突破対象のプロパティIDから変数を取り出すための<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a> </td> </tr> <tr> <td> gradeUpPropertyIdRegex </td> <td> グレードアップに利用可能な素材を判定するための<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a> </td> </tr> </table><p>propertyIdRegex の入力例</p> <pre class="code" data-lang="" data-unlink>grn:gs2:{region}:{ownerId}:inventory:namespace-0001:user:(.*):inventory:character:item:(.*):.*</pre><p>gradeUpPropertyIdRegex の入力例</p> <pre class="code" data-lang="" data-unlink>grn:gs2:{region}:{ownerId}:inventory:namespace-0001:user:$1:inventory:character:item:$2:.*</pre><p>プロパティIDから、propertyIdRegex でユーザーIDとアイテム名をキャプチャし、gradeUpPropertyIdRegex の $1, $2 に値を反映して<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>を作成しています。<br /> この<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>にマッチするプロパティIDのリソースのみが限界突破に利用できる素材となります。</p> </div> </div> kazutomo GS2-StateMachine に投機的実行機能が追加されました hatenablog://entry/6801883189067916994 2023-12-19T10:00:00+09:00 2023-12-19T10:00:00+09:00 はじめに GS2-StateMachine はステートマシンをサーバーで管理することで、ゲーム開発者が定義した独自のゲームロジックを、チート耐性が高い状態で実行する仕組みです。GS2-StateMachine の詳細は以下の記事で解説しています。gs2.hatenablog.com 機能追加の背景 ステートマシン管理機能は柔軟な処理を、安全に実行できる仕組みです。 しかし、欠点としてステートマシンにメッセージを送信するたびに通信をしなければならないため、通信頻度の高いゲーム性ではプレイ体験の面でもGS2のAPI呼び出しコストの面でも課題がありました。 追加された機能の詳細 ステートマシン定義を… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-StateMachine はステートマシンをサーバーで管理することで、ゲーム開発者が定義した独自のゲームロジックを、チート耐性が高い状態で実行する仕組みです。</p><p>GS2-StateMachine の詳細は以下の記事で解説しています。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgs2.hatenablog.com%2Fentry%2F2023%2F07%2F26%2F183534" title="ステートマシン管理機能が追加されました - GS2 Blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://gs2.hatenablog.com/entry/2023/07/26/183534">gs2.hatenablog.com</a></cite><br /> </p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>ステートマシン管理機能は柔軟な処理を、安全に実行できる仕組みです。<br /> しかし、欠点としてステートマシンにメッセージを送信するたびに通信をしなければならないため、通信頻度の高いゲーム性ではプレイ体験の面でもGS2の<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>呼び出しコストの面でも課題がありました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>ステートマシン定義をサーバーとゲームで共有し、ゲームはステートマシン定義に基づいてステートマシンを実行することで通信待ちせずに投擲に処理を実行し<br /> ステートマシンに送信したメッセージや、状態遷移した際の状態変数の変化を一定間隔でまとめて GS2-StateMachine に送信し、GS2-StateMachine は受け取ったイベントを元に同じステートマシン定義を使用したステートマシンで状態を再現しつつ、状態変数の変化が正しいかを検証することができるようになりました。</p><p>以下のメ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%AB%A5%CB">カニ</a>ズムで処理が実行されます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231218/20231218205703.png" width="1200" height="919" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>これにより、ステートマシンの実行にあたって通信待ちをすることなく、通信回数を減らすことが可能となり、GS2-StateMachine がより扱いやすいものになりました。</p><p>詳細なドキュメントは以下をご確認ください。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Fmicroservices%2Fstate_machine%2F" title="GS2-StateMachine" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/microservices/state_machine/">docs.gs2.io</a></cite></p> </div> kazutomo トランザクションをより細かい粒度で待てるようになりました hatenablog://entry/6801883189066331643 2023-12-13T10:00:00+09:00 2023-12-13T10:00:03+09:00 はじめに GS2 におけるトランザクションとは、GS2-Showcase の商品購入APIの戻り値 や GS2-Exchange の交換実行API の戻り値です。 このような複数のマイクロサービスの値を書き換える動作は非同期処理で実行されます。この処理単位を 「トランザクション」 と呼んでいます。gs2.hatenablog.comそして、8月には非同期処理の完了を待てるように SDK を拡張しました。 機能追加の背景 ガチャを例に説明します。ガチャは一般的に以下のプロセスで実行されます。消費「GS2-Money から課金通貨を300減らす」 入手「GS2-Lottery でガチャを実行する」… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2 における<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>とは、GS2-Showcase の商品購入<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>の戻り値 や GS2-Exchange の交換実行<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a> の戻り値です。<br /> このような複数のマイクロサービスの値を書き換える動作は非同期処理で実行されます。</p><p>この処理単位を 「<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>」 と呼んでいます。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgs2.hatenablog.com%2Fentry%2F2023%2F08%2F17%2F110000" title="C# SDK でサーバーサイド非同期処理の完了を待てるようになりました - GS2 Blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://gs2.hatenablog.com/entry/2023/08/17/110000">gs2.hatenablog.com</a></cite></p><p>そして、8月には非同期処理の完了を待てるように <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> を拡張しました。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>ガチャを例に説明します。ガチャは一般的に以下のプロセスで実行されます。</p><p>消費「GS2-Money から課金通貨を300減らす」<br /> 入手「GS2-Lottery でガチャを実行する」</p><p>そして、ガチャを実行すると新しい<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>が発行されます。</p><p>消費 なし<br /> 入手「ガチャで排出されたキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターを GS2-Inventory に格納する」</p><p>8月に追加した内容は、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>内で新しい<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>が発生した時にその結果も待つかどうかのオプションがあります。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">var</span> transaction <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.Showcase( <span class="synConstant">&quot;namespace&quot;</span> ).Me( gameSession ).Showcase( <span class="synConstant">&quot;showcase&quot;</span> ).DisplayItem( <span class="synConstant">&quot;displayItem&quot;</span> ).BuyAsync( <span class="synConstant">1</span> ); <span class="synStatement">await</span> transaction.WaitAsync(); </pre><p>このように記述すると</p> <pre class="code" data-lang="" data-unlink>消費「GS2-Money から課金通貨を300減らす」 入手「GS2-Lottery でガチャを実行する」</pre><p>の部分だけを待って、アイテムの入手処理は終わっていない状態で処理が返ります。<br /> これによって、通信待ちの時間を最小にして抽選結果に応じた演出を開始し、抽選の演出している間にアイテムの付与が行えました。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">var</span> transaction <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.Showcase( <span class="synConstant">&quot;namespace&quot;</span> ).Me( gameSession ).Showcase( <span class="synConstant">&quot;showcase&quot;</span> ).DisplayItem( <span class="synConstant">&quot;lottery1&quot;</span> ).BuyAsync( <span class="synConstant">1</span> ); <span class="synStatement">await</span> transaction.WaitAsync(<span class="synConstant">true</span>); </pre><p>このように記述すると</p> <pre class="code" data-lang="" data-unlink>消費「GS2-Money から課金通貨を300減らす」 入手「GS2-Lottery でガチャを実行する」 消費 なし 入手「ガチャで排出されたキャラクターを GS2-Inventory に格納する」</pre><p>の全てを待つことができました。<br /> しかし、これでは<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>がもう少し複雑な構造を持っている場合に、完全に処理を待つことしかできませんでした。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>WaitAsync の第一引数に何も指定しない場合に続きの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>オブジェクトを返すようになりました。</p><p>この機能の実例として複雑な<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>を追いながら解説します。<br /> 10連ガチャで9個と1個で異なる GS2-Lottery の抽選ロジックを適用する場合を考えてみましょう。</p><p>最初の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>は</p> <pre class="code" data-lang="" data-unlink>消費「GS2-Money から課金通貨を3000減らす」 入手「GS2-Lottery で LotteryA を9回実行する」 入手「GS2-Lottery で LotteryB を1回実行する」</pre><p>となりますが、このような入手処理が複数存在する場合は GS2 においてはジョブキューを経由するように変換されます。<br /> つまり、以下のようになります。</p> <pre class="code" data-lang="" data-unlink>消費「GS2-Money から課金通貨を3000減らす」 入手「GS2-JobQueue に《GS2-Lottery で LotteryA を9回実行する》と《GS2-Lottery で LotteryB を1回実行する》ジョブを登録」</pre><pre class="code" data-lang="" data-unlink>JobQueue を実行し 《GS2-Lottery で LotteryA を9回実行する》と《GS2-Lottery で LotteryB を1回実行する》を実行し、最大10個のトランザクションを発行 (最大としているのは同一アイテムが排出された場合、トランザクション処理の最適化処理で数が減ることがあるため)</pre><pre class="code" data-lang="" data-unlink>入手「ガチャで排出されたキャラクターを GS2-Inventory に格納する」× 最大10個</pre><p>内部的には 最大11個の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>、2個のジョブキューで構成されますが<br /> <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>上は開発者がより直感的に扱えるように、上記の3段階の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>ステップで扱い、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>ステップ単位で処理を待つことができるようになりました。</p><p>つまり、改めてコードを示すと</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">var</span> transaction <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.Showcase( <span class="synConstant">&quot;namespace&quot;</span> ).Me( gameSession ).Showcase( <span class="synConstant">&quot;showcase&quot;</span> ).DisplayItem( <span class="synConstant">&quot;lottery10&quot;</span> ).BuyAsync( <span class="synConstant">1</span> ); <span class="synType">var</span> transaction2 <span class="synStatement">=</span> <span class="synStatement">await</span> transaction.WaitAsync(); <span class="synStatement">await</span> transaction2.WaitAsync(); </pre><p>と記述することで、2つ目の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>ステップまで実行を待つことができます。<br /> つまり</p> <pre class="code" data-lang="" data-unlink>消費「GS2-Money から課金通貨を3000減らす」 入手「GS2-JobQueue に《GS2-Lottery で LotteryA を9回実行する》と《GS2-Lottery で LotteryB を1回実行する》ジョブを登録」 JobQueue を実行し 《GS2-Lottery で LotteryA を9回実行する》と《GS2-Lottery で LotteryB を1回実行する》を実行し、最大10個のトランザクションを発行</pre><p>までということになります。</p><p>これで、ガチャの抽選結果が確定しつつ、アイテムをインベントリに追加する前まで<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理を待つことができるようになりました。</p> </div> kazutomo SimpleInventory / BigInventory に任意の所持数量を設定できるようになりました hatenablog://entry/6801883189065912936 2023-12-11T16:19:28+09:00 2023-12-11T16:19:28+09:00 はじめに SimpleInventory はアイテムの所持数量を管理する仕組みで、通常の Inventory が持つスタックサイズの制限や、Inventory の容量制限の管理の仕組みがない代わりに、複数のアイテムを同時に増減することが可能なインベントリです。BigInventory はアイテムの所持数量を int64 の範囲を超えて保持できるインベントリです。 今回の更新の背景 アイテムの所持数量の操作は、これまで増減APIを通して実行することになっていました。 しかし、任意の数量を持っている状況を作り出したい時に、すでに所有している数量を考慮した上で増減量を調整して処理を実行するのはロジッ… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>SimpleInventory はアイテムの所持数量を管理する仕組みで、通常の Inventory が持つスタックサイズの制限や、Inventory の容量制限の管理の仕組みがない代わりに、複数のアイテムを同時に増減することが可能なインベントリです。</p><p>BigInventory はアイテムの所持数量を int64 の範囲を超えて保持できるインベントリです。</p> </div> <div class="section"> <h3 id="今回の更新の背景">今回の更新の背景</h3> <p>アイテムの所持数量の操作は、これまで増減<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を通して実行することになっていました。<br /> しかし、任意の数量を持っている状況を作り出したい時に、すでに所有している数量を考慮した上で増減量を調整して処理を実行するのはロジックが関わり不具合の原因となりかねない処理を組み込む必要がありました。</p> </div> <div class="section"> <h3 id="今回の更新の詳細">今回の更新の詳細</h3> <p>setSimpleItemsByUserId<br /> <a href="https://docs.gs2.io/ja/api_reference/inventory/sdk/#setsimpleitemsbyuserid">GS2-Inventory SDK API &#x30EA;&#x30D5;&#x30A1;&#x30EC;&#x30F3;&#x30B9; | Game Server Services | Docs</a></p><p>setBigItemByUserId<br /> <a href="https://docs.gs2.io/ja/api_reference/inventory/sdk/#setbigitembyuserid">GS2-Inventory SDK API &#x30EA;&#x30D5;&#x30A1;&#x30EC;&#x30F3;&#x30B9; | Game Server Services | Docs</a></p><p>の<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>が追加され、それぞれに対応した<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>入手アクションとして</p><p>Gs2Inventory:SetSimpleItemsByUserId<br /> <a href="https://docs.gs2.io/ja/api_reference/inventory/stamp_sheet/#gs2inventorysetsimpleitemsbyuserid">GS2-Inventory &#x30C8;&#x30E9;&#x30F3;&#x30B6;&#x30AF;&#x30B7;&#x30E7;&#x30F3;&#x30A2;&#x30AF;&#x30B7;&#x30E7;&#x30F3; | Game Server Services | Docs</a></p><p>Gs2Inventory:SetBigItemByUserId<br /> <a href="https://docs.gs2.io/ja/api_reference/inventory/stamp_sheet/#gs2inventorysetbigitembyuserid">GS2-Inventory &#x30C8;&#x30E9;&#x30F3;&#x30B6;&#x30AF;&#x30B7;&#x30E7;&#x30F3;&#x30A2;&#x30AF;&#x30B7;&#x30E7;&#x30F3; | Game Server Services | Docs</a></p><p>が追加されました。</p> </div> kazutomo サーバーから取得した時刻を基準とした現在時刻取得機能が追加されました hatenablog://entry/6801883189064407530 2023-12-05T23:49:19+09:00 2023-12-05T23:49:19+09:00 はじめに イベントの開始時刻や終了時刻など運営型ゲームでは時間の取り扱いは重要です。 機能追加の背景 一方で、ゲームが動作するデバイスの時刻設定は必ずしも正確とは言えず、正しくUIを表現するのは難しいです。 サーバーから取得した時刻を使用して、現在時間を補正することが一般的に行われており、それによってより正確なUIを表現することができるようになります。GS2 では元々 GS2-Realtime::Now というAPIで現在時刻を取得できました。 しかし、それはその瞬間の時刻の取得であり、刻々と変化する時刻やスリープ復帰後にも問題なく動作するような仕組みはゲーム開発者が構築する必要がありました。… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>イベントの開始時刻や終了時刻など運営型ゲームでは時間の取り扱いは重要です。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>一方で、ゲームが動作するデ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%D0%A5%A4%A5%B9">バイス</a>の時刻設定は必ずしも正確とは言えず、正しくUIを表現するのは難しいです。<br /> サーバーから取得した時刻を使用して、現在時間を補正することが一般的に行われており、それによってより正確なUIを表現することができるようになります。</p><p>GS2 では元々 GS2-Realtime::Now という<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>で現在時刻を取得できました。<br /> しかし、それはその瞬間の時刻の取得であり、刻々と変化する時刻やスリープ復帰後にも問題なく動作するような仕組みはゲーム開発者が構築する必要がありました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <pre class="code lang-cs" data-lang="cs" data-unlink> Gs2DateTime datetime <span class="synStatement">=</span> <span class="synStatement">await</span> application.Realtime.Gs2DateTimeAsync( GameSession ); DateTimeOffset now <span class="synStatement">=</span> datetime.Current; </pre><p>Gs2DateTimeAsync を呼び出した時に GS2-Realtime::Now を呼び出して現在時刻を取得し Gs2DateTime オブジェクトを返します。<br /> Gs2DateTime の Current にアクセスすることで、補正が適用された現在時刻を取得できます。</p><p>QA目的で GS2-Account や GS2-Auth を使用して現在時刻のオフセットを設定している場合、Gs2DateTime オブジェクトが返す時刻にもオフセットが適用されます。</p> </div> kazutomo Nifty Cloud mobile backend をご利用の皆さんへ hatenablog://entry/6801883189063004296 2023-11-30T22:56:42+09:00 2023-11-30T22:56:42+09:00 本日、Nifty Cloud mobile backend(NCMB) のサービス終了がアナウンスされました。 多くのインディーデベロッパーさんを支えてきたサービスで、移行先を検討される方も多くいらっしゃると思います。GS2 がその移行先として選んでいただくにあたって、必要となるであろう情報をこちらにまとめます。 料金(無料枠) まず、重要なのは利用料金です。 API 無料で利用可能な範囲についてですが、NCMB は 100万回 ⁄ 月 が無料で利用できます。 GS2 も月2万円の無料枠があり、APIリクエスト 0.02円 が基本となっているため、同じく100万回/月 が無料で利用可能です。 … <p>本日、<a class="keyword" href="https://d.hatena.ne.jp/keyword/Nifty">Nifty</a> Cloud mobile backend(NCMB) のサービス終了がアナウンスされました。<br /> 多くのインディー<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C7%A5%D9%A5%ED%A5%C3%A5%D1">デベロッパ</a>ーさんを支えてきたサービスで、移行先を検討される方も多くいらっしゃると思います。</p><p>GS2 がその移行先として選んでいただくにあたって、必要となるであろう情報をこちらにまとめます。</p> <div class="section"> <h3 id="料金無料枠">料金(無料枠)</h3> <p>まず、重要なのは利用料金です。</p> <div class="section"> <h4 id="API"><a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a></h4> <p>無料で利用可能な範囲についてですが、NCMB は 100万回 ⁄ 月 が無料で利用できます。<br /> GS2 も月2万円の無料枠があり、<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>リク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>ト 0.02円 が基本となっているため、同じく100万回/月 が無料で利用可能です。</p> </div> <div class="section"> <h4 id="ストレージ">ストレージ</h4> <p>NCMB ではストレージ機能が提供されており、容量制限があります。<br /> しかし、GS2 が提供するストレージ機能である GS2-Datastore には容量制限はありません。<br /> 代わりに 「データの保存容量 1GB 10円」の料金が発生し、無料枠の消費で充当が可能です。</p> </div> <div class="section"> <h4 id="データベース">データベース</h4> <p>NCMB ではデータベース機能があり、データベースにインデックスを構築して検索速度を向上するのは無料プランでは利用できません。<br /> GS2 では任意のデータベースを作成できない代わりに、ゲームの用途に最適化された30を超えるサービスが提供されており、常に最適化されたインデックスを使用してデータベースにアクセスします。<br /> そのため、サービス利用者は検索速度などを考える必要がなく、同様の料金プランも存在しません。</p> </div> <div class="section"> <h4 id="スクリプト"><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a></h4> <p>NCMB では<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>機能があり、無料プランでは「5万回 ⁄ 月」が利用可能です。<br /> GS2 では、通常の<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>リク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トと同様にカウントされます。<br /> 追加で「<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の実行時間 1秒 0.005円」の追加料金が発生しますが、こちらも無料枠の消費で充当が可能です。<br /> NCMBにあるような累計時間の制限はありません。</p> </div> <div class="section"> <h4 id="インディー開発者向けプラン">インディー開発者向けプラン</h4> <p>GS2 には NCMB にない料金プランとしてインディー開発者向けの料金プランがあります。<br /> この料金プランは過去12ヶ月の売り上げが 1000万円未満の個人又は法人が利用可能なプランで、一定の条件を満たす限りGS2のあらゆる機能が無料で利用できます。</p> <div class="section"> <h5 id="一定の条件">一定の条件</h5> </div> </div> </div> <div class="section"> <h3 id="アクセス数の上限"></h3> <pre class="code" data-lang="" data-unlink>月の平均リクエスト回数が秒間10回または、一定期間継続して秒間リクエスト回数が100回を超える場合は Individual プランの対象外となります。</pre><p>という、通常であれば売り上げ1000万円を超える規模の売り上げになっていてもおかしくない規模のアクセスが発生しているにもかかわらず、そのような申告をいただいていないケース向けの制限です。</p> </div> <div class="section"> <h3 id="ロゴ表示"></h3> <pre class="code" data-lang="" data-unlink>起動シーケンス もしくは タイトル画面 に GS2 のロゴを掲載する必要があります。 ゲームのリリースの目処がたったタイミングで、チャットよりロゴデータと表示に伴うガイドラインを受け取ってください。</pre><p>に従ってゲーム内にGS2のロゴを掲載する必要があります。<br /> 詳細は以下を参照してください。</p><p><a href="https://docs.gs2.io/ja/pricing/">GS2&#x306E;&#x5229;&#x7528;&#x6599;&#x91D1; | Game Server Services | Docs</a><br /> </p> </div> <div class="section"> <h3 id="利用方法">利用方法</h3> <p>GS2 は <a href="https://gs2.io">https://gs2.io</a> でサインアップすることで、すぐにサービスを利用開始できます。</p> <div class="section"> <h4 id="サンプル">サンプル</h4> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fgs2io%2Fgs2-sample%2Ftree%2Fmaster%2Funity" title="gs2-sample/unity at master · gs2io/gs2-sample" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/gs2io/gs2-sample/tree/master/unity">github.com</a></cite><br /> <br /> こちらにサンプルコードを用意しました。<br /> NCMB を利用して実現していた一般的な内容が含まれているはずです。</p> </div> <div class="section"> <h4 id="実行例">実行例</h4> <p>GS2 では環境のセットアップに管理画面からポチポチ登録するだけでなく、定義ファイルのアップロードでも対応できます。<br /> 今回は CloudSave プロジェクトの実行までの流れを説明します。</p> <div class="section"> <h5 id="環境のセットアップ">環境のセットアップ</h5> <p>サンプルに template.<a class="keyword" href="https://d.hatena.ne.jp/keyword/yaml">yaml</a> というファイルがあります。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">GS2TemplateFormatVersion</span><span class="synSpecial">:</span> <span class="synConstant">&quot;2019-05-01&quot;</span> <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">AccountNamespace</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Account::Namespace <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> CloudSave-Account <span class="synIdentifier">DatastoreNamespace</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Datastore::Namespace <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> CloudSave-Datastore </pre><p>こちらは、GS2-Account と GS2-Datastore のセットアップをする内容が記述されています。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130223535.png" width="1200" height="1121" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>GS2 のマネージメントコンソール(管理画面)にアクセスします。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130223617.png" width="412" height="243" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>サイドメニューより「Deploy -> Stacks」を選択します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130223707.png" width="1200" height="454" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>「スタックの新規作成」を選択します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130223738.png" width="1200" height="808" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>スタックの名前を「CloudSave」として、template.<a class="keyword" href="https://d.hatena.ne.jp/keyword/yaml">yaml</a> をアップロードします。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130223857.png" width="880" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>スタックの詳細画面で、実行状態が「作成完了」や、イベントリストに「CREATE_COMPLETE」のような表示が行われればセットアップは完了です。</p> </div> <div class="section"> <h5 id="実行">実行</h5> <p>サンプル<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>をチェックアウトし、CloudSave.unity を Unity Editor で開きます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224035.png" width="1200" height="1049" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>シーン内に存在する GameObject を選択し、インスペクターを確認します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224109.png" width="309" height="344" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>ClientID / Client Secret にはあらかじめサンプルの動作確認用の値が設定されています。(この値は定期的に変更されるため、動作確認以外の利用は避けてください)<br /> ここを自分のプロジェクトの ClientID / ClientSecret に書き換えましょう。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224317.png" width="880" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>マネージメントコンソールの Home にプロジェクトで使用できる ClientID / ClientSecret が表示されますので、そちらを設定してください。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224434.png" width="472" height="113" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>実行すると「ABC」という文字列がコンソールに書き出されます。</p> </div> <div class="section"> <h5 id="実装の確認">実装の確認</h5> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synComment">/*</span> <span class="synComment"> * Copyright 2016 Game Server Services, Inc. or its affiliates. All Rights</span> <span class="synComment"> * Reserved.</span> <span class="synComment"> *</span> <span class="synComment"> * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;).</span> <span class="synComment"> * You may not use this file except in compliance with the License.</span> <span class="synComment"> * A copy of the License is located at</span> <span class="synComment"> *</span> <span class="synComment"> * http://www.apache.org/licenses/LICENSE-2.0</span> <span class="synComment"> *</span> <span class="synComment"> * or in the &quot;license&quot; file accompanying this file. This file is distributed</span> <span class="synComment"> * on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either</span> <span class="synComment"> * express or implied. See the License for the specific language governing</span> <span class="synComment"> * permissions and limitations under the License.</span> <span class="synComment"> */</span> <span class="synPreProc">#if GS2_ENABLE_UNITASK</span> <span class="synStatement">using</span> System.Collections.Generic; <span class="synStatement">using</span> System.Text; <span class="synStatement">using</span> Cysharp.Threading.Tasks.Linq; <span class="synStatement">using</span> Gs2.Core.Model; <span class="synStatement">using</span> Gs2.Unity.Core; <span class="synStatement">using</span> Gs2.Unity.Util; <span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> Cysharp.Threading.Tasks; <span class="synStatement">using</span> UnityEditor; <span class="synType">namespace</span> Gs2.Sample.Simple { <span class="synType">public</span> <span class="synType">class</span> <span class="synType">CloudSaveAsync </span><span class="synStatement">:</span> MonoBehaviour { <span class="synType">public</span> <span class="synType">string</span> clientId; <span class="synType">public</span> <span class="synType">string</span> clientSecret; <span class="synType">public</span> <span class="synType">string</span> accountNamespaceName; <span class="synType">public</span> <span class="synType">string</span> datastoreNamespaceName; <span class="synType">private</span> <span class="synStatement">async</span> UniTask ProcessAsync() { <span class="synComment">// Initialize GS2 SDK</span> <span class="synType">var</span> gs2 <span class="synStatement">=</span> <span class="synStatement">await</span> Gs2Client.CreateAsync( <span class="synStatement">new</span> BasicGs2Credential( clientId, clientSecret ), Region.ApNortheast1 ); <span class="synComment">// define GS2-Account namespace</span> <span class="synType">var</span> gs2Account <span class="synStatement">=</span> gs2.Account.Namespace( <span class="synStatement">this</span>.accountNamespaceName ); <span class="synComment">// Create an anonymous account</span> <span class="synType">var</span> account <span class="synStatement">=</span> <span class="synStatement">await</span> ( <span class="synStatement">await</span> gs2Account.CreateAsync() ).ModelAsync(); <span class="synComment">// login</span> <span class="synType">var</span> gameSession <span class="synStatement">=</span> <span class="synStatement">await</span> gs2.LoginAsync( <span class="synStatement">new</span> Gs2AccountAuthenticator( <span class="synStatement"> accountSetting:</span> <span class="synStatement">new</span> AccountSetting { accountNamespaceName <span class="synStatement">=</span> <span class="synStatement">this</span>.accountNamespaceName, } ), account.UserId, account.Password ); <span class="synComment">// define GS2-Datastore namespace</span> <span class="synType">var</span> gs2Datastore <span class="synStatement">=</span> gs2.Datastore.Namespace( <span class="synStatement">this</span>.datastoreNamespaceName ); <span class="synComment">// define save data</span> <span class="synType">var</span> saveData <span class="synStatement">=</span> <span class="synStatement">new</span> byte[] { (<span class="synType">byte</span>) <span class="synConstant">'A'</span>, (<span class="synType">byte</span>) <span class="synConstant">'B'</span>, (<span class="synType">byte</span>) <span class="synConstant">'C'</span> }; <span class="synComment">// upload save data</span> <span class="synStatement">await</span> gs2Datastore.Me( gameSession ).UploadAsync( <span class="synConstant">&quot;public&quot;</span>, <span class="synStatement">new</span> List&lt;<span class="synType">string</span>&gt;(), saveData, <span class="synConstant">&quot;PublicData&quot;</span> ); <span class="synComment">// download save data</span> <span class="synType">var</span> downloadedSaveData <span class="synStatement">=</span> <span class="synStatement">await</span> gs2Datastore.Me( gameSession ).DataObject( <span class="synConstant">&quot;PublicData&quot;</span> ).DownloadAsync(); Debug.Log(Encoding.ASCII.GetString(downloadedSaveData)); } <span class="synType">public</span> <span class="synType">void</span> Start() { StartCoroutine(ProcessAsync().ToCoroutine()); } } } <span class="synPreProc">#endif</span> </pre><p>実は ABC という文字列は GS2-Datastore に保存し、ダウンロードした内容を表示していました。</p> </div> <div class="section"> <h5 id="データの確認">データの確認</h5> <p>上記プログラムでは GS2-Account でログインし、GS2-Datastore にデータをアップロードしていました。<br /> マネージメントコンソールでデータを確認してみましょう。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224611.png" width="368" height="449" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>まずは GS2-Account の管理メニューを開くためにサイドメニューから「Account -> Namespaces」を選択します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224731.png" width="1171" height="71" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>CloudSave-Account がこのサンプルでアカウントを保存している領域です。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224808.png" width="1169" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>「bc708ed6-a319-4480-bd64-bd5727afec09」というユーザーIDのプレイヤーが作成されていることがわかります。</p><p>次に、GS2-Datastore の管理メニューを開きます「Datastore -> Namespaces」を選択し、「CloudSave-Datastore」を開きます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130224917.png" width="979" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>ユーザーデータタブの Target User ID に「bc708ed6-a319-4480-bd64-bd5727afec09」を指定すると、このプレイヤーが GS2-Datastore にアップロードしたデータを参照できます。</p><p>「PublicData」 という名前のファイルがアップロードされていることがわかります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231130/20231130225048.png" width="880" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>3バイトのデータがアップロードされていることがわかります。</p> </div> </div> <div class="section"> <h4 id="最後に">最後に</h4> <p>まずはぜひ触ってみてください!</p> </div> </div> kazutomo マイクロサービス間の連携の設定が簡素化されました hatenablog://entry/6801883189062965773 2023-11-30T20:37:26+09:00 2023-11-30T20:37:26+09:00 はじめに GS2 は多数のマイクロサービスを提供しており、多くのケースはトランザクション処理として、《消費アクション》と《入手アクション》として抽象化されています。一方で、トランザクションを実行するために必要な GS2-Distributor や GS2-JobQueue 各種暗号処理で利用する GS2-Key の暗号鍵、各種通知処理で使用する GS2-Gateway といったマイクロサービスとの関連付けは各マイクロサービスのネームスペース設定として設定が必要です。この設定漏れや誤りによる不具合報告を多数頂戴する状況から、利用者にとってもGS2にとってもボトルネックになっていると感じていました… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2 は多数のマイクロサービスを提供しており、多くのケースは<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理として、《消費アクション》と《入手アクション》として抽象化されています。</p><p>一方で、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>を実行するために必要な GS2-Distributor や GS2-JobQueue<br /> 各種暗号処理で利用する GS2-Key の暗号鍵、各種通知処理で使用する GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/Gateway">Gateway</a> といったマイクロサービスとの関連付けは各マイクロサービスのネームスペース設定として設定が必要です。</p><p>この設定漏れや誤りによる不具合報告を多数頂戴する状況から、利用者にとってもGS2にとっても<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%DC%A5%C8%A5%EB%A5%CD%A5%C3%A5%AF">ボトルネック</a>になっていると感じていました。</p> <div class="section"> <h4 id="変更の内容">変更の内容</h4> <p>2023年8月にはプロジェクトの作成時にこれらの設定のややこしいマイクロサービスに「default」という名前でネームスペースを作成するようになりました。<br /> これによって、マネージメントコンソールを初めて操作する際でも、とりあえず選択可能な項目を選択することで設定を完了できるようになりました。<br /> しかし、実際には GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> を利用する際にもこれらの値を正しく設定しなければ、意図する動作にならないケースがあり、まだこの対応では不十分でした。</p><p>今回の更新で、サーバー・<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>を共に大規模な更新を行い、これらの連携用の設定が未設定の場合は「default」ネームスペースを使用して動作するようになります。</p> </div> <div class="section"> <h4 id="変更の適用方法">変更の適用方法</h4> <p>サーバーサイドは自動的に未設定の場合でもデフォルトネームスペースが利用されます。<br /> もし、プロジェクトの作成が 2023年8月 より前で、デフォルトネームスペースが作成されていない場合は以下の GS2-Deploy テンプレートを適用することで、デフォルトネームスペースを後から作成することができます。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">GS2TemplateFormatVersion</span><span class="synSpecial">:</span> <span class="synConstant">&quot;2019-05-01&quot;</span> <span class="synIdentifier">Globals</span><span class="synSpecial">:</span> <span class="synIdentifier">Alias</span><span class="synSpecial">:</span> <span class="synIdentifier">ApplicationUserName</span><span class="synSpecial">:</span> default <span class="synIdentifier">KeyNamespaceName</span><span class="synSpecial">:</span> default <span class="synIdentifier">KeyName</span><span class="synSpecial">:</span> default <span class="synIdentifier">DistributorNamespaceName</span><span class="synSpecial">:</span> default <span class="synIdentifier">GatewayNamespaceName</span><span class="synSpecial">:</span> default <span class="synIdentifier">JobQueueNamespaceName</span><span class="synSpecial">:</span> default <span class="synIdentifier">Resources</span><span class="synSpecial">:</span> <span class="synIdentifier">IdentifierApplicationUser</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Identifier::User <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> ${ApplicationUserName} <span class="synIdentifier">IdentifierApplicationUserAttachPolicy</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Identifier::AttachSecurityPolicy <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">UserName</span><span class="synSpecial">:</span> ${ApplicationUserName} <span class="synIdentifier">SecurityPolicyId</span><span class="synSpecial">:</span> grn:gs2::system:identifier:securityPolicy:ApplicationAccess <span class="synIdentifier">DependsOn</span><span class="synSpecial">:</span> <span class="synStatement">- </span>IdentifierApplicationUser <span class="synIdentifier">IdentifierApplicationIdentifier</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Identifier::Identifier <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">UserName</span><span class="synSpecial">:</span> ${ApplicationUserName} <span class="synIdentifier">DependsOn</span><span class="synSpecial">:</span> <span class="synStatement">- </span>IdentifierApplicationUser <span class="synIdentifier">KeyNamespace</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Key::Namespace <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> ${KeyNamespaceName} <span class="synIdentifier">Key</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Key::Key <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">NamespaceName</span><span class="synSpecial">:</span> ${KeyNamespaceName} <span class="synIdentifier">Name</span><span class="synSpecial">:</span> ${KeyName} <span class="synIdentifier">DependsOn</span><span class="synSpecial">:</span> <span class="synStatement">- </span>KeyNamespace <span class="synIdentifier">GatewayNamespace</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Gateway::Namespace <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> ${GatewayNamespaceName} <span class="synIdentifier">DistributorNamespace</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::Distributor::Namespace <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> ${DistributorNamespaceName} <span class="synIdentifier">AutoRunStampSheetNotification</span><span class="synSpecial">:</span> <span class="synIdentifier">GatewayNamespaceId</span><span class="synSpecial">:</span> <span class="synType">!GetAttr</span> GatewayNamespace.Item.NamespaceId <span class="synIdentifier">JobQueueNamespace</span><span class="synSpecial">:</span> <span class="synIdentifier">Type</span><span class="synSpecial">:</span> GS2::JobQueue::Namespace <span class="synIdentifier">Properties</span><span class="synSpecial">:</span> <span class="synIdentifier">Name</span><span class="synSpecial">:</span> ${JobQueueNamespaceName} <span class="synIdentifier">EnableAutoRun</span><span class="synSpecial">:</span> <span class="synConstant">true</span> <span class="synIdentifier">Outputs</span><span class="synSpecial">:</span> <span class="synIdentifier">ApplicationClientId</span><span class="synSpecial">:</span> <span class="synType">!GetAttr</span> IdentifierApplicationIdentifier.Item.ClientId <span class="synIdentifier">ApplicationClientSecret</span><span class="synSpecial">:</span> <span class="synType">!GetAttr</span> IdentifierApplicationIdentifier.ClientSecret </pre><p>あとは、GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> を更新してください。Unity の場合は「v2023.11.7」以降を利用するようにしてください。</p> </div> </div> kazutomo トランザクション処理の投機的実行機能が Unreal Engine 5 をサポートしました hatenablog://entry/6801883189058637428 2023-11-14T09:40:11+09:00 2023-11-14T09:40:11+09:00 gs2.hatenablog.com先日、通信待ちをすることなく通信後の結果を想定して GS2-SDK がもつローカルキャッシュを投機的に更新することで SDKから取得できる値を先行して更新後の値を取得できるようにする仕組みのリリースを発表しました。前回リリースの段階では、Unity 向けの SDK でのみ本機能が実装されていましたが この度 Unreal Engine 向けの SDK でも同様の対応が行われました。細かな仕様は Unity と同様ですので、昨日の詳細については前述のブログ記事を参照ください。 <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgs2.hatenablog.com%2Fentry%2F2023%2F11%2F07%2F110000" title="トランザクション処理の投機的実行機能が追加されました - GS2 Blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://gs2.hatenablog.com/entry/2023/11/07/110000">gs2.hatenablog.com</a></cite></p><p>先日、通信待ちをすることなく通信後の結果を想定して GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> がもつローカルキャッシュを投機的に更新することで<br /> <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>から取得できる値を先行して更新後の値を取得できるようにする仕組みのリリースを発表しました。</p><p>前回リリースの段階では、Unity 向けの <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> でのみ本機能が実装されていましたが<br /> この度 <a class="keyword" href="https://d.hatena.ne.jp/keyword/Unreal%20Engine">Unreal Engine</a> 向けの <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> でも同様の対応が行われました。</p><p>細かな仕様は Unity と同様ですので、昨日の詳細については前述のブログ記事を参照ください。</p> kazutomo トランザクション処理の投機的実行機能が追加されました hatenablog://entry/6801883189056676240 2023-11-07T11:00:00+09:00 2023-11-07T11:00:02+09:00 はじめに GS2におけるトランザクション処理というのはユーザーデータの増減処理を指します。 たとえば、「課金通貨を消費してスタミナを回復する」というような操作です。 機能追加の背景 GS2はレスポンスタイムの最適化を日々行なっており、数百msのオーダーで処理が実行されます。 しかし、通信が完了するまでUIを更新しないとこの数百msのラグが違和感を生んでしまいます。そこで、ユーザー体験を最適化するために投機的実行機能を実装しました。 追加された機能の詳細 GS2-Showcase の Buy や GS2-Exchange の Exchange 関数に第二引数 bool speculativeEx… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2における<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理というのはユーザーデータの増減処理を指します。<br /> たとえば、「課金通貨を消費してスタミナを回復する」というような操作です。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>GS2はレスポンスタイムの最適化を日々行なっており、数百msのオーダーで処理が実行されます。<br /> しかし、通信が完了するまでUIを更新しないとこの数百msのラグが違和感を生んでしまいます。</p><p>そこで、ユーザー体験を最適化するために投機的実行機能を実装しました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>GS2-Showcase の Buy や GS2-Exchange の Exchange 関数に第二引数 </p> <pre class="code" data-lang="" data-unlink>bool speculativeExecute = true</pre><p>が追加されました。ここで投機実行機能を有効化するかを指定することができます。<br /> 記載の通り、デフォルトで有効化されます。</p> <div class="section"> <h4 id="投機的実行のメカニズム">投機的実行のメ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%AB%A5%CB">カニ</a>ズム</h4> <p>投機的実行処理を有効にして<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理を実行すると、GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> は通信を開始する前に<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>実行後に期待される値で<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>がもつローカルキャッシュを更新します。<br /> これによって、GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> を経由して取得される値も先行して期待される値に更新されます。</p><p>ローカルキャッシュを書き換えますが、このキャッシュの有効期限は10秒に設定され<br /> 10秒経っても<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理が正常に完了しない場合は改めて最新の値をサーバーから取得します。<br /> 10秒以内に<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>が完了すれば、サーバーからの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>完了通知を受け取ってキャッシュの有効期限がデフォルト値であれば15分に延長されます。</p> </div> <div class="section"> <h4 id="投機的実行できない処理">投機的実行できない処理</h4> <p>GS2-Money のストアプラットフォームのレシート検証や、GS2-Lottery の抽選処理などサーバーが処理しなければ結果がわからない処理については投機的実行は行われません。<br /> 投機的実行ができない<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理が含まれていた場合、以下の警告がコンソールに出力されます。</p> <pre class="code" data-lang="" data-unlink>Speculative execution not supported on this action: XXX:XXX</pre><p>投機的実行ができないだけで処理自体は今まで通り行われますので、サーバーからの完了通知をもってローカルキャッシュの更新が行われます。<br /> そのため、この警告については深く気にする必要はありません。</p> </div> <div class="section"> <h4 id="投機的実行の入れ子">投機的実行の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C6%FE%A4%EC%BB%D2">入れ子</a></h4> <p>投機的実行処理は<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理が<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C6%FE%A4%EC%BB%D2">入れ子</a>構造になっていても適用されます。<br /> たとえば、GS2-Showcase で GS2-Exchange の交換処理を実行する入手アクションが設定された商品が登録されており、その商品を購入した場合は、GS2-Exchange の交換処理についても投機的実行が行われます。</p> </div> <div class="section"> <h4 id="連続的な処理でのチャタリング">連続的な処理での<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C1%A5%E3%A5%BF%A5%EA%A5%F3%A5%B0">チャタリング</a></h4> <p>投機的実行処理によって通信処理が完了しなくてもUI上は想定される最新の値を表示するようになったことで<br /> 交換処理の<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を連打した時にUIの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C1%A5%E3%A5%BF%A5%EA%A5%F3%A5%B0">チャタリング</a>が生じるようになる可能性があります。</p><p>具体的には以下のような順番で処理が発生した場合を想定してください。</p><p>アイテムを1個入手(処理A) -> アイテム数量1<br /> アイテムを1個入手(処理B) -> アイテム数量2<br /> サーバーで処理Aが完了 -> アイテム数量1<br /> サーバーで処理Bが完了 -> アイテム数量2</p><p>この場合、一瞬アイテムの数量が 2 から 1 に巻き戻って表示されます。</p><p>このように高頻度(1秒に何度も)<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出すのであれば、<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>呼び出しをバッファリングし GS2-Showcase であれば Quantity、GS2-Exchange であれば Count の値を指定するようにすることで通信回数を削減で、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C1%A5%E3%A5%BF%A5%EA%A5%F3%A5%B0">チャタリング</a>の発生を回避できます。</p> </div> <div class="section"> <h4 id="新機能への対応に関する注意事項">新機能への対応に関する注意事項</h4> <p>GS2 では定期的に新機能が追加されており、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理も追加されています。<br /> 未知の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理が含まれている場合は、投機的実行ができない<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>アクションと同様の警告が表示されるだけで動作自体には大きな問題は生じません。<br /> しかし、積極的に<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>を最新に更新することで、最新の投機的実行処理にも対応でき、最適なUXを実現できます。<br /> 不具合の修正も頻繁に行われていますので、出来る限り最新の<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>を利用するようにしてください。</p> </div> </div> kazutomo GS2-StateMachine で利用する GSL にスクリプトを直値で指定できるようになりました hatenablog://entry/6801883189053200670 2023-10-25T11:00:00+09:00 2023-10-25T11:00:02+09:00 はじめに GS2-StateMachine はステートマシンをサーバーサイドで管理し、クライアントから届くメッセージを元に状態遷移することで チート行為に対する耐性をたかめつつ、自由度の高いスクリプティングを実現する機能を提供しています。 機能追加の背景 GS2-StateMachine が管理するステートマシンは GS2 State Language(GSL) を使用して定義します。 新しいステートに遷移した際にスクリプトを実行して、ステートマシンが持つ状態変数を書き換えることができます。 従来はこのスクリプトの指定には GS2-Script が管理するスクリプトのIDを指定することで定義し… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-StateMachine はステートマシンをサーバーサイドで管理し、クライアントから届くメッセージを元に状態遷移することで<br /> チート行為に対する耐性をたかめつつ、自由度の高い<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C6%A5%A3%A5%F3%A5%B0">スクリプティング</a>を実現する機能を提供しています。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>GS2-StateMachine が管理するステートマシンは GS2 State Language(GSL) を使用して定義します。<br /> 新しいステートに遷移した際に<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を実行して、ステートマシンが持つ状態変数を書き換えることができます。<br /> 従来はこの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の指定には GS2-Script が管理する<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>のIDを指定することで定義していました。</p><p>しかし、GSLと<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>の実体が分離してしまい、見通しが悪くなってしまう状況が発生していました。</p> </div> <div class="section"> <h3 id="機能追加の詳細">機能追加の詳細</h3> <pre class="code" data-lang="" data-unlink>StateMachine MainStateMachine { Variables { } EntryPoint Task1; Task Task1(int initCounter) { Event StartLoop(); Event Error(string Reason); Payload { result = { event=&#34;Pass&#34;, params={}, updatedVariables=args.variables } } } PassTask Pass; ErrorTask Error(string reason); Transition Task1 handling Pass -&gt; Pass; }</pre><p>こちらの例のように Task 内の Payload セクションで直接 <a class="keyword" href="https://d.hatena.ne.jp/keyword/Lua">Lua</a> <a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を記述できるようになりました。<br /> 例では Task1 に遷移した際に以下の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を実行します。</p> <pre class="code" data-lang="" data-unlink> result = { event=&#34;Pass&#34;, params={}, updatedVariables=args.variables }</pre><p><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を実行した結果、Pass というメッセージを自身のステートマシンへ発します。</p> <pre class="code" data-lang="" data-unlink> Transition Task1 handling Pass -&gt; Pass;</pre><p>Task1 ステートにいる時に Pass というメッセージを受け取ると、Pass ステートに遷移します。<br /> Pass ステートは PassTask なので、ステートマシンの終端を表すステートに遷移しステートマシンは起動直後に終了します。</p> </div> kazutomo エクスポートしたユーザーデータをインポートできるようになりました hatenablog://entry/6801883189053118395 2023-10-24T19:41:16+09:00 2023-10-24T19:41:16+09:00 機能追加の背景 gs2.hatenablog.com先日各国の法令遵守のために、ユーザーデータをエクスポートして提供できるようにしたり、ユーザーデータを完全に削除する機能の提供開始をアナウンスしました。 今回の更新はこのエクスポートしたユーザーデータを異なるアカウントやプロジェクトにインポートする機能の提供開始をお知らせします。 追加された機能の詳細 この機能の追加によって、お客様が何らかの問題を報告してきた際に製品環境でユーザーデータをエクスポートして QA環境や開発環境にユーザーデータをインポートして、実際にお客様のプレイデータを再現した状態で動作確認をすることが可能となります。先日、エク… <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgs2.hatenablog.com%2Fentry%2F2023%2F10%2F13%2F093705" title="国外の個人情報保護のための法令に対応するための機能が追加されました - GS2 Blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://gs2.hatenablog.com/entry/2023/10/13/093705">gs2.hatenablog.com</a></cite></p><p>先日各国の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%CB%A1%CE%E1%BD%E5%BC%E9">法令遵守</a>のために、ユーザーデータをエクスポートして提供できるようにしたり、ユーザーデータを完全に削除する機能の提供開始をアナウンスしました。<br /> 今回の更新はこのエクスポートしたユーザーデータを異なるアカウントやプロジェクトにインポートする機能の提供開始をお知らせします。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>この機能の追加によって、お客様が何らかの問題を報告してきた際に製品環境でユーザーデータをエクスポートして<br /> QA環境や開発環境にユーザーデータをインポートして、実際にお客様のプレイデータを再現した状態で動作確認をすることが可能となります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231024/20231024193705.png" width="330" height="240" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>先日、エクスポートメニューがプロジェクトメニューに追加されたと告知しましたが、その隣にインポートメニューが追加されました。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231024/20231024193750.png" width="1130" height="353" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>インポートを実行したいプロジェクトがアクティブな状態でインポートメニューを選択すると、インポート履歴ページが開きますので「ユーザーデータのインポートを開始」を選択します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231024/20231024193833.png" width="1125" height="488" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>ユーザーデータをインポートする際には元のユーザーデータとは異なるユーザーIDとしてインポートが可能です。<br /> あとは、他のプロジェクトでエクスポートした zip ファイルを選択してインポートを実行するだけです。</p> </div> kazutomo 国外の個人情報保護のための法令に対応するための機能が追加されました hatenablog://entry/6801883189050021638 2023-10-13T09:37:05+09:00 2023-10-13T09:39:24+09:00 機能追加の背景 この10年の間に 欧州では GDPR、米国では CCPA などの個人情報を取り扱う上で課題となる法令が次々と制定されています。 その中には対応するのに相当な工数を必要とする内容もあり、サービス提供者を悩ませています。 追加された機能の詳細 GS2 が提供する全てのマイクロサービスから、特定のユーザーIDに関連づけられたデータの全データをzip形式で取得 GS2 が提供する全てのマイクロサービスから、特定のユーザーIDに関連づけられたデータの完全な削除 上記2つの機能が追加されました。完全な削除については、完全な削除を必要とする法律と一定期間保存しなければならない法律が国ごとにあ… <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>この10年の間に 欧州では <a class="keyword" href="https://d.hatena.ne.jp/keyword/GDPR">GDPR</a>、米国では CCPA などの個人情報を取り扱う上で課題となる法令が次々と制定されています。<br /> その中には対応するのに相当な<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B9%A9%BF%F4">工数</a>を必要とする内容もあり、サービス提供者を悩ませています。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <ul> <li>GS2 が提供する全てのマイクロサービスから、特定のユーザーIDに関連づけられたデータの全データをzip形式で取得</li> <li>GS2 が提供する全てのマイクロサービスから、特定のユーザーIDに関連づけられたデータの完全な削除</li> </ul><p>上記2つの機能が追加されました。</p><p>完全な削除については、完全な削除を必要とする法律と一定期間保存しなければならない法律が国ごとにあり整合性が取れていないのが現状です。<br /> そのため、顧問弁護士やリーガル部門の指導のもと機能を利用することを強く推奨します。</p><p>また、ここで提供する機能は全て GS2 内部で管理しているデータについてとなり、GS2 の外に持ち出されたデータについては関与しません。</p> </div> <div class="section"> <h3 id="追加された機能の利用方法">追加された機能の利用方法</h3> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013092955.png" width="491" height="337" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>マネージメントコンソールのサイドメニューのプロジェクトメニュー内に「ユーザーデータをエクスポート」「ユーザーデータを完全に削除」の2つの項目が追加されています。</p> <div class="section"> <h4 id="ユーザーデータをエクスポート">ユーザーデータをエクスポート</h4> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093119.png" width="1138" height="603" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>「ユーザーデータをエクスポート」 を選択するとエクスポート履歴が表示されます。<br /> 「ユーザーデータのエクスポートを開始」を選択します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093211.png" width="1139" height="381" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>エクスポートを実行したいユーザーのユーザーIDを入力して「開始」ボタンを押します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093119.png" width="1138" height="603" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>全てのマイクロサービスから出力が完了すれば、エクスポートデータが24時間の間 DL可能となります。</p> </div> <div class="section"> <h4 id="ユーザーデータを完全に削除">ユーザーデータを完全に削除</h4> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093414.png" width="1138" height="514" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>「ユーザーデータを完全に削除」 を選択すると削除履歴が表示されます。<br /> 「ユーザーデータの削除を開始」を選択します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093455.png" width="677" height="219" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>削除を実行したいユーザーのユーザーIDを入力して「開始」ボタンを押します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093525.png" width="1135" height="530" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>UIがエクスポートと類似しているため、削除の場合は一段確認の行程が追加されています。<br /> 本当に実行したい場合は「開始」ボタンを押します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231013/20231013093414.png" width="1138" height="514" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>全てのマイクロサービスで削除が完了していれば、該当のユーザーに関するデータはGS2上から完全に削除されています。</p> </div> </div> kazutomo 強化処理の UI Kit サンプルが追加されました hatenablog://entry/820878482974504840 2023-10-10T10:05:07+09:00 2023-10-10T10:10:50+09:00 github.comサンプルを実行すると、自動的に3つの武器の入手処理が行われ一覧表示が行われます。武器を選択すると、強化素材の選択メニューが表示されます。スライダーを動かすと、予測経験値獲得量が連動します。Execute をクリックすると、強化が実行されます。Unleash タブを選択すると、レベルキャップの引き上げができますが まだレベルキャップに到達していないので、実行できません。レベルキャップまで強化をしますすると、Unleash タブで進化素材を消費する画面になります。Execute を選択すると、レベルキャップが5上がります。再度レベルキャップまで強化します。レベル10 -> 15… <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fgs2io%2Fgs2-uikit-for-unity-sample%2Ftree%2Fmain%2FUIKit%2FEnhance" title="gs2-uikit-for-unity-sample/UIKit/Enhance at main · gs2io/gs2-uikit-for-unity-sample" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://github.com/gs2io/gs2-uikit-for-unity-sample/tree/main/UIKit/Enhance">github.com</a></cite></p><p>サンプルを実行すると、自動的に3つの武器の入手処理が行われ一覧表示が行われます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010095449.png" width="607" height="1130" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>武器を選択すると、強化素材の選択メニューが表示されます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010095627.png" width="605" height="1128" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>スライダーを動かすと、予測経験値獲得量が連動します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010095717.png" width="605" height="1130" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>Execute をクリックすると、強化が実行されます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010095756.png" width="606" height="1129" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>Unleash タブを選択すると、レベルキャップの引き上げができますが<br /> まだレベルキャップに到達していないので、実行できません。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010095842.png" width="604" height="1129" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>レベルキャップまで強化をします</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010095922.png" width="604" height="1126" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>すると、Unleash タブで進化素材を消費する画面になります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010100015.png" width="607" height="1126" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>Execute を選択すると、レベルキャップが5上がります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010100046.png" width="602" height="1127" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>再度レベルキャップまで強化します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010100122.png" width="605" height="1132" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>レベル10 -> 15 と レベル 15->20 では異なる進化素材が必要となります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/k/kazutomo/20231010/20231010100203.png" width="604" height="1129" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p><p>このサンプルではレベル30まで進化できるようになっています。</p> kazutomo 値チェック系のトランザクションアクションが追加されました hatenablog://entry/820878482972792193 2023-10-03T21:07:05+09:00 2023-10-03T21:07:05+09:00 はじめに GS2におけるトランザクションは、ゲーム内のリソースの増減の組み合わせを定義したものです。 消費「アイテムを減らす」入手「経験値が増える」のようにプレイヤーにとってデメリットがある消費アクションと、プレイヤーにとってメリットがある入手アクションを組み合わせて定義します。 機能追加の背景 レベルキャップの引き上げの交換レートを定義する際に、現在のレベルキャップを元に交換に必要なコストを決定したいことがあります。 しかし、これまではこれを素直に実装する手段はなく、GS2-Limitの交換回数制限機能でキャラクターごとにレベルキャップの引き上げを実行した回数で制限をかけるといった工夫が必要… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2における<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>は、ゲーム内のリソースの増減の組み合わせを定義したものです。<br /> 消費「アイテムを減らす」入手「経験値が増える」のようにプレイヤーにとってデメリットがある消費アクションと、プレイヤーにとってメリットがある入手アクションを組み合わせて定義します。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>レベルキャップの引き上げの交換レートを定義する際に、現在のレベルキャップを元に交換に必要なコストを決定したいことがあります。<br /> しかし、これまではこれを素直に実装する手段はなく、GS2-Limitの交換回数制限機能でキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターごとにレベルキャップの引き上げを実行した回数で制限をかけるといった工夫が必要でした。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>今回以下の値チェック系の消費アクションが追加されました。<br /> あわせて、値チェック系の消費アクションの実行優先度があがり、どのような順番で消費アクションを記述したとしてもまずは値チェックが実行されるようになりました。</p> <ul> <li>GS2-Dictionary: エントリーが記録済みか</li> <li>GS2-Experience: ランクが一定以下か、一定以上か、あるいは一致しているか、一致しないか</li> <li>GS2-Experience: ランクキャップが一定以下か、一定以上か、あるいは一致しているか、一致しないか</li> <li>GS2-Inventory: アイテムの所持数量が一定以下か、一定以上か、あるいは一致しているか、一致しないか</li> <li>GS2-Limit: カウンターが一定以下か、一定以上か、あるいは一致しているか、一致しないか</li> </ul><p>注意するべき点として、これから消費する消費アクションのチェックには使用するべきではありません。<br /> アイテムの所持数量が消費アクションの消費量を上回っているか確認する といった用途は引き続き<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>の発行前にチェックするべきです。<br /> このような用途の場合、上記の入力値のチェックがなくても消費アクションの実行時にリソースが不足しておりエラーとなり、入手アクションを実行することはできません。</p><p>今回の機能追加の意図として、レベルキャップの引き上げの前提条件として現在のレベルキャップが50を想定した交換レートであり、レベルキャップが55になっている場合はより高コストな交換レートを使用してレベルキャップの引き上げを行なって欲しいことをより適切に定義できるようになることを目指しています。</p> </div> kazutomo UI Kit for Unity がイベント駆動に対応しました hatenablog://entry/820878482971662975 2023-09-29T20:08:49+09:00 2023-09-29T20:08:49+09:00 はじめに UI Kit for Unity は Unity 用の高レベルなSDKで、Unity Editor 上で GS2 のリソースを宣言的に利用することが可能なSDKです。 たとえば、スタミナを表示したい場合は スタミナコンテキストコンポーネント をゲームオブジェクトに割り当て、操作対象となるスタミナの種類を設定します。 その横に スタミナラベルコンポーネントをゲームオブジェクトに割り当て、整形する文字列フォーマットを指定することでテキストコンポーネントに GS2から取得したスタミナの値をテキストコンポーネントに反映することができます。詳細なサンプルは以下のURLをご確認ください。docs… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>UI Kit for Unity は Unity 用の高レベルな<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>で、Unity Editor 上で GS2 のリソースを宣言的に利用することが可能な<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>です。<br /> たとえば、スタミナを表示したい場合は スタミナコンテキスト<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a> をゲームオブジェクトに割り当て、操作対象となるスタミナの種類を設定します。<br /> その横に スタミナラベル<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>をゲームオブジェクトに割り当て、整形する文字列フォーマットを指定することでテキスト<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>に GS2から取得したスタミナの値をテキスト<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>に反映することができます。</p><p>詳細なサンプルは以下のURLをご確認ください。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.gs2.io%2Fja%2Farticles%2Fsample%2Fuikit_project%2F" title="GS2 UIKit for Unity の実装サンプル" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://docs.gs2.io/ja/articles/sample/uikit_project/">docs.gs2.io</a></cite><br /> </p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>これまで UI Kit は Unity の Update 関数で GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> をポーリングし、最新の値をUIに反映していました。<br /> この方法はシンプルですが、貴重なCPU資源を毎フレーム消費してしまっていました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>先日 GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> にデータの変更を通知するイベントが追加されました。<br /> 詳細は以下のURLをご確認ください。</p><p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgs2.hatenablog.com%2Fentry%2F2023%2F09%2F28%2F110000" title="Game Engine 用 SDK でイベント駆動実装ができるようになりました - GS2 Blog" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://gs2.hatenablog.com/entry/2023/09/28/110000">gs2.hatenablog.com</a></cite></p><p>この機能を利用して、値に変化があった時にイベント駆動で UI Kit の各種<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>が駆動するように実装を見直しました。<br /> その結果、これまでより圧倒的に効率的に処理を行うようになりました。</p><p>CPU処理の削減はもちろん、ケースによっては<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%AC%A5%D9%A1%BC%A5%B8%A5%B3%A5%EC%A5%AF%A5%B7%A5%E7%A5%F3">ガベージコレクション</a>が実行される頻度も低下しているはずです。</p><p>利用にあたって特に手続きはなく、最新の<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>を利用するだけで効果を得ることが可能です。</p> </div> kazutomo Game Engine 用 SDK でイベント駆動実装ができるようになりました hatenablog://entry/820878482971024151 2023-09-28T11:00:00+09:00 2023-09-28T11:00:00+09:00 はじめに GS2 SDK for Game Engine は Unity と Unreal Engine 用の SDK です。 これらの SDK を使用することで、GS2を利用したプログラミングをするのが容易になります。 機能追加の背景 これまでの GS2 SDK for Game Engine では、ポーリングスタイルで値を取得するのが主な実装でした。 GS2 の API の多くは遅延実行される設計のため、UIに最新の値を反映するにはポーリングするのが主な手段で ポーリングをしても無駄なサーバーアクセスが発生しないようにキャッシュレイヤーが設けられていました。 追加された機能の詳細 キャッシ… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2 <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> for Game Engine は Unity と <a class="keyword" href="https://d.hatena.ne.jp/keyword/Unreal%20Engine">Unreal Engine</a> 用の <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> です。<br /> これらの <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> を使用することで、GS2を利用したプログラミングをするのが容易になります。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>これまでの GS2 <a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> for Game Engine では、ポーリングスタイルで値を取得するのが主な実装でした。<br /> GS2 の <a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a> の多くは遅延実行される設計のため、UIに最新の値を反映するにはポーリングするのが主な手段で<br /> ポーリングをしても無駄なサーバーアクセスが発生しないようにキャッシュレイヤーが設けられていました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>キャッシュに更新が発生した際にコールバックされる<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A4%A5%D9%A5%F3%A5%C8%A5%CF%A5%F3%A5%C9%A5%E9">イベントハンドラ</a>が新設されました。<br /> キャッシュは遅延実行処理が完了した際に自動的に更新されるため、イベントをハンドリングするだけで常に最新のデータを利用可能になります。</p> </div> <div class="section"> <h3 id="実装例">実装例</h3> <p>GS2-Inventory のアイテムの所持数量が変化した時にコールバックを受け取る</p> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Inventory.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;Inventory&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Inventory( <span class="synStatement"> inventoryName:</span> <span class="synConstant">&quot;Bag&quot;</span> ).ItemSet( <span class="synStatement"> itemName:</span> <span class="synConstant">&quot;Potion&quot;</span> ); <span class="synComment">// イベントハンドラを登録</span> <span class="synType">var</span> callbackId <span class="synStatement">=</span> domain.Subscribe(items <span class="synStatement">=&gt;</span> { <span class="synComment">// Potion の所持数量に変化があった時にコールバック</span> }); <span class="synComment">// イベントハンドラを解除</span> domain.Unsubscribe(callbackId); </pre><p>GS2-Inventory のアイテムの一覧が変化した時にコールバックを受け取る</p> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Inventory.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;Inventory&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).SimpleInventory( <span class="synStatement"> inventoryName:</span> <span class="synConstant">&quot;Bag&quot;</span> ); <span class="synComment">// イベントハンドラを登録</span> <span class="synType">var</span> callbackId <span class="synStatement">=</span> domain.SubscribeSimpleItems(() <span class="synStatement">=&gt;</span> { <span class="synComment">// Bag 内のアイテム一覧に変化があった時にコールバック</span> }); <span class="synComment">// イベントハンドラを解除</span> domain.UnsubscribeSimpleItems(callbackId); </pre> </div> kazutomo GS2-Account のアカウントBANに理由と期間を設定できるようになりました hatenablog://entry/820878482970447739 2023-09-25T12:56:03+09:00 2023-09-25T12:56:03+09:00 はじめに GS2-Account は匿名アカウント機能を実現するためのマイクロサービスです。 アカウント情報の保持や認証機能に加えて、各種プラットフォーマーのアカウントを使用した引き継ぎ機能を実現できます。 機能追加の背景 GS2-Account は元々アカウントBAN機能を提供していました。 しかし、期間設定はできず永久BANの機能しか有していませんでした。 追加された機能の詳細 軽微な不正行為に対して、理由の提示と1週間などの期間を設けた利用制限を実行できるようになりました。 実装例 アカウントBAN情報の登録は Untiy などのゲームエンジンからは行えません。 代わりに各種プログラミン… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Account は匿名アカウント機能を実現するためのマイクロサービスです。<br /> アカウント情報の保持や認証機能に加えて、各種<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%C3%A5%C8%A5%D5%A5%A9%A1%BC%A5%DE%A1%BC">プラットフォーマー</a>のアカウントを使用した引き継ぎ機能を実現できます。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>GS2-Account は元々アカウントBAN機能を提供していました。<br /> しかし、期間設定はできず永久BANの機能しか有していませんでした。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>軽微な不正行為に対して、理由の提示と1週間などの期間を設けた利用制限を実行できるようになりました。</p> </div> <div class="section"> <h3 id="実装例">実装例</h3> <p>アカウントBAN情報の登録は Untiy などの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B2%A1%BC%A5%E0%A5%A8%A5%F3%A5%B8%A5%F3">ゲームエンジン</a>からは行えません。<br /> 代わりに各種<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%C0%B8%EC">プログラミング言語</a>用の<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>を利用ください。</p> <div class="section"> <h4 id="アカウントBANを登録Pythonの例">アカウントBANを登録(<a class="keyword" href="https://d.hatena.ne.jp/keyword/Python">Python</a>の例)</h4> <pre class="code lang-python" data-lang="python" data-unlink><span class="synPreProc">from</span> gs2 <span class="synPreProc">import</span> core <span class="synPreProc">from</span> gs2 <span class="synPreProc">import</span> account session = core.Gs2RestSession( core.BasicGs2Credential( <span class="synConstant">'your client id'</span>, <span class="synConstant">'your client secret'</span> ), <span class="synConstant">&quot;ap-northeast-1&quot;</span>, ) session.connect() client = account.Gs2AccountRestClient(session) <span class="synStatement">try</span>: result = client.add_ban( account.AddBanRequest() .with_namespace_name(self.hash1) .with_user_id(self.account1.user_id) .with_ban_status( BanStatus( reason=<span class="synConstant">'reason'</span>, releaseTimestamp=<span class="synConstant">1000000000000</span>, ) ) ) item = result.item <span class="synStatement">except</span> core.Gs2Exception <span class="synStatement">as</span> e: exit(<span class="synConstant">1</span>) </pre> </div> </div> kazutomo バージョンチェックにGS2-Scheduleとの連携機能が追加されました hatenablog://entry/820878482969039065 2023-09-21T10:00:00+09:00 2023-09-21T09:40:26+09:00 はじめに GS2-Version はアプリやアセット、利用規約のバージョン管理や、メンテナンスモードの切り替えなどを行えるマイクロサービスです。 機能追加の背景 バージョン更新や、メンテナンスは予定を持って実行されることが一般的です。 しかし、これまで GS2-Version には予定時刻に要求バージョンを切り替える仕組みがありませんでしたので、バージョン更新やメンテナンスの進行に合わせてマスターデータを切り替える必要がありました。 追加された機能の詳細 そこで、VersionModel に type フィールドが追加され、従来のバージョンチェックを行える simple と、新しく sched… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Version はアプリやアセット、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%CD%F8%CD%D1%B5%AC%CC%F3">利用規約</a>のバージョン管理や、メンテナンスモードの切り替えなどを行えるマイクロサービスです。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>バージョン更新や、メンテナンスは予定を持って実行されることが一般的です。<br /> しかし、これまで GS2-Version には予定時刻に要求バージョンを切り替える仕組みがありませんでしたので、バージョン更新やメンテナンスの進行に合わせてマスターデータを切り替える必要がありました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>そこで、VersionModel に type フィールドが追加され、従来のバージョンチェックを行える <strong>simple</strong> と、新しく <strong>schedule</strong> を選択できるようになりました。</p><p>schedule を選択すると、scheduleVersions: List[ScheduleVersion] を指定する必要があります。</p><p>ScheduleVersion は 《現在のバージョン》《警告をだすバージョン<a class="keyword" href="https://d.hatena.ne.jp/keyword/%EF%E7%C3%CD">閾値</a>》《エラーとするバージョン<a class="keyword" href="https://d.hatena.ne.jp/keyword/%EF%E7%C3%CD">閾値</a>》《GS2-Schedule のイベントGRN》 のフィールドを持ち、配列の先頭から順番に評価し《GS2-Schedule のイベントGRN》が開催状態もしくは未指定の ScheduleVersion の値を使用してバージョンチェックを行います。</p> </div> <div class="section"> <h3 id="マスターデータの例">マスターデータの例</h3> <pre class="code lang-json" data-lang="json" data-unlink><span class="synSpecial">{</span> &quot;<span class="synStatement">version</span>&quot;: &quot;<span class="synConstant">2019-10-09</span>&quot;, &quot;<span class="synStatement">versionModels</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">name</span>&quot;: &quot;<span class="synConstant">[string]バージョンモデル名</span>&quot;, &quot;<span class="synStatement">metadata</span>&quot;: &quot;<span class="synConstant">[string?]メタデータ</span>&quot;, &quot;<span class="synStatement">scope</span>&quot;: &quot;<span class="synConstant">[string]判定に使用するバージョン値の種類(passive/active)</span>&quot;, <span class="synError">// ここから追加</span> &quot;<span class="synStatement">type</span>&quot;: &quot;<span class="synConstant">[string]バージョンチェックモード(simple/schedule)</span>&quot;, &quot;<span class="synStatement">scheduleVersions</span>&quot;: <span class="synSpecial">[</span> <span class="synSpecial">{</span> &quot;<span class="synStatement">currentVersion</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">major</span>&quot;: &quot;<span class="synConstant">[int]メジャーバージョン</span>&quot;, &quot;<span class="synStatement">minor</span>&quot;: &quot;<span class="synConstant">[int]マイナーバージョン</span>&quot;, &quot;<span class="synStatement">micro</span>&quot;: &quot;<span class="synConstant">[int]マイクロバージョン</span>&quot; <span class="synSpecial">}</span>, &quot;<span class="synStatement">warningVersion</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">major</span>&quot;: &quot;<span class="synConstant">[int]メジャーバージョン</span>&quot;, &quot;<span class="synStatement">minor</span>&quot;: &quot;<span class="synConstant">[int]マイナーバージョン</span>&quot;, &quot;<span class="synStatement">micro</span>&quot;: &quot;<span class="synConstant">[int]マイクロバージョン</span>&quot; <span class="synSpecial">}</span>, &quot;<span class="synStatement">errorVersion</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">major</span>&quot;: &quot;<span class="synConstant">[int]メジャーバージョン</span>&quot;, &quot;<span class="synStatement">minor</span>&quot;: &quot;<span class="synConstant">[int]マイナーバージョン</span>&quot;, &quot;<span class="synStatement">micro</span>&quot;: &quot;<span class="synConstant">[int]マイクロバージョン</span>&quot; <span class="synSpecial">}</span>, &quot;<span class="synStatement">scheduleEventId</span>&quot;: &quot;<span class="synConstant">[string?]バージョンチェックを有効化する期間を設定した GS2-Schedule のイベント</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">]</span>, <span class="synError">// ここまで追加</span> &quot;<span class="synStatement">needSignature</span>&quot;: &quot;<span class="synConstant">[bool]判定するバージョン値に署名検証を必要とするか</span>&quot;, &quot;<span class="synStatement">signatureKeyId</span>&quot;: &quot;<span class="synConstant">[string]暗号鍵GRN</span>&quot;, &quot;<span class="synStatement">currentVersion</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">major</span>&quot;: &quot;<span class="synConstant">[int]メジャーバージョン</span>&quot;, &quot;<span class="synStatement">minor</span>&quot;: &quot;<span class="synConstant">[int]マイナーバージョン</span>&quot;, &quot;<span class="synStatement">micro</span>&quot;: &quot;<span class="synConstant">[int]マイクロバージョン</span>&quot; <span class="synSpecial">}</span>, &quot;<span class="synStatement">warningVersion</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">major</span>&quot;: &quot;<span class="synConstant">[int]メジャーバージョン</span>&quot;, &quot;<span class="synStatement">minor</span>&quot;: &quot;<span class="synConstant">[int]マイナーバージョン</span>&quot;, &quot;<span class="synStatement">micro</span>&quot;: &quot;<span class="synConstant">[int]マイクロバージョン</span>&quot; <span class="synSpecial">}</span>, &quot;<span class="synStatement">errorVersion</span>&quot;: <span class="synSpecial">{</span> &quot;<span class="synStatement">major</span>&quot;: &quot;<span class="synConstant">[int]メジャーバージョン</span>&quot;, &quot;<span class="synStatement">minor</span>&quot;: &quot;<span class="synConstant">[int]マイナーバージョン</span>&quot;, &quot;<span class="synStatement">micro</span>&quot;: &quot;<span class="synConstant">[int]マイクロバージョン</span>&quot; <span class="synSpecial">}</span> <span class="synSpecial">}</span><span class="synError">,</span> <span class="synError"> ]</span> } </pre> </div> <div class="section"> <h3 id="Unity-からの使用例">Unity からの使用例</h3> <p>クライアントの実装はこれまでと変わりません。</p> <div class="section"> <h4 id="バージョンチェックを実行">バージョンチェックを実行</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Version.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Checker( ); <span class="synType">var</span> result <span class="synStatement">=</span> <span class="synStatement">await</span> domain.CheckVersionAsync( <span class="synStatement"> targetVersions:</span> <span class="synStatement">new</span> Gs2.Unity.Gs2Version.Model.EzTargetVersion[] { <span class="synStatement">new</span> Gs2.Unity.Gs2Version.Model.EzTargetVersion { VersionName <span class="synStatement">=</span> <span class="synConstant">&quot;app&quot;</span>, Version <span class="synStatement">=</span> { Major <span class="synStatement">=</span> <span class="synConstant">1</span>, Minor <span class="synStatement">=</span> <span class="synConstant">2</span>, Micro <span class="synStatement">=</span> <span class="synConstant">3</span>, }, }, <span class="synStatement">new</span> Gs2.Unity.Gs2Version.Model.EzTargetVersion { VersionName <span class="synStatement">=</span> <span class="synConstant">&quot;asset&quot;</span>, Version <span class="synStatement">=</span> { Major <span class="synStatement">=</span> <span class="synConstant">1</span>, Minor <span class="synStatement">=</span> <span class="synConstant">2</span>, Micro <span class="synStatement">=</span> <span class="synConstant">3</span>, }, }, } ); <span class="synType">var</span> projectToken <span class="synStatement">=</span> result.ProjectToken; <span class="synType">var</span> warnings <span class="synStatement">=</span> result.Warnings; <span class="synType">var</span> errors <span class="synStatement">=</span> result.Errors; </pre> </div> <div class="section"> <h4 id="利用規約に同意"><a class="keyword" href="https://d.hatena.ne.jp/keyword/%CD%F8%CD%D1%B5%AC%CC%F3">利用規約</a>に同意</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Version.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).AcceptVersion( <span class="synStatement"> versionName:</span> <span class="synConstant">&quot;eula&quot;</span> ); <span class="synType">var</span> result <span class="synStatement">=</span> <span class="synStatement">await</span> domain.AcceptAsync( ); <span class="synType">var</span> item <span class="synStatement">=</span> <span class="synStatement">await</span> result.ModelAsync(); </pre><p>関連資料<br /> <a href="https://docs.gs2.io/ja/microservices/version/">GS2-Version &#x306E;&#x6982;&#x8981;</a><br /> <a href="https://docs.gs2.io/ja/api_reference/version/">GS2-Version &#x306E;API&#x30EA;&#x30D5;&#x30A1;&#x30EC;&#x30F3;&#x30B9;</a></p> </div> </div> kazutomo 広告視聴報酬機能が追加されました hatenablog://entry/820878482968761664 2023-09-19T11:34:36+09:00 2023-09-19T11:34:36+09:00 はじめに モバイルゲームのマネタイズ手法としてプレイヤーに広告を視聴してもらい、広告プラットフォームから報酬を得ることも一般的になりました。 広告が正しく視聴された時に広告プラットフォームからサーバー間連携で通知をもらい、GS2に報酬を付与することでチート行為を防ぐことができます。 追加された機能の詳細 一般的に GS2 では対価と報酬を設定してリソースの交換を行っていますが、サーバー間通信の仕様が広告プラットフォームごとに異なり、データの粒度も異なるため GS2-Reward では広告の視聴が確認できた際に《視聴ポイント》を1ポイント加算するようになっています。獲得した《視聴ポイント》は G… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>モバイルゲームのマネタイズ手法としてプレイヤーに広告を視聴してもらい、広告プラットフォームから報酬を得ることも一般的になりました。<br /> 広告が正しく視聴された時に広告プラットフォームからサーバー間連携で通知をもらい、GS2に報酬を付与することでチート行為を防ぐことができます。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>一般的に GS2 では対価と報酬を設定してリソースの交換を行っていますが、サーバー間通信の仕様が広告プラットフォームごとに異なり、データの粒度も異なるため GS2-Reward では広告の視聴が確認できた際に《視聴ポイント》を1ポイント加算するようになっています。</p><p>獲得した《視聴ポイント》は GS2-Exchange や GS2-Showcase などで使用可能な消費アクションとして消費できます。</p> <div class="section"> <h4 id="対応している広告プラットフォーム">対応している広告プラットフォーム</h4> <ul> <li>AdMob(<a class="keyword" href="https://d.hatena.ne.jp/keyword/Google">Google</a> Mobile Ads)</li> <li>Unity Ads</li> </ul> </div> </div> <div class="section"> <h3 id="Unity-からの使用例">Unity からの使用例</h3> <div class="section"> <h4 id="動画の視聴を開始">動画の視聴を開始</h4> <p>各広告プラットフォームの<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a>を直接利用して動画を視聴しても問題ありません。 <br /> ここでの実装例は GS2-<a class="keyword" href="https://d.hatena.ne.jp/keyword/SDK">SDK</a> で提供しているユーティリティクラスを使用した実装例を示します。</p> <div class="section"> <h5 id="AdMob">AdMob</h5> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synStatement">await</span> AdMobUtil.InitializeAsync( <span class="synStatement">new</span> RequestConfiguration() { TestDeviceIds <span class="synStatement">=</span> <span class="synStatement">new</span> List&lt;<span class="synType">string</span>&gt; { <span class="synConstant">&quot;4cd8a25ecc6250e3c140e365e5a543ff&quot;</span>, <span class="synComment">// テストデバイスID</span> }, } ); <span class="synStatement">await</span> AdMobUtil.ViewFuture( <span class="synConstant">&quot;ca-app-pub-8090851552121537/9708453802&quot;</span>, <span class="synComment">// 広告ユニットID</span> GameSession <span class="synComment">// ログインセッション</span> ); </pre> </div> <div class="section"> <h5 id="Unity-Ads">Unity Ads</h5> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synStatement">await</span> UnityAdUtil.InitializeAsync( <span class="synConstant">&quot;5416096&quot;</span> <span class="synComment">// Unity Ads のゲームID</span> ); <span class="synStatement">await</span> UnityAdUtil.ViewAsync( <span class="synConstant">&quot;test&quot;</span>, <span class="synComment">// Placement ID</span> GameSession <span class="synComment">// ログインセッション</span> ); </pre> </div> </div> <div class="section"> <h4 id="現在の広告ポイントを取得">現在の広告ポイントを取得</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.AdReward.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Point( ); <span class="synType">var</span> item <span class="synStatement">=</span> <span class="synStatement">await</span> domain.ModelAsync(); </pre> </div> <div class="section"> <h4 id="視聴ポイントの加算コールバック">視聴ポイントの加算コールバック</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> gs2.AdReward.OnChangePointNotification <span class="synStatement">+=</span> notification <span class="synStatement">=&gt;</span> { <span class="synType">var</span> namespaceName <span class="synStatement">=</span> notification.NamespaceName; }; </pre> </div> </div> <div class="section"> <h3 id="関連資料">関連資料</h3> <p><a href="https://docs.gs2.io/ja/microservices/ad_reward/">GS2-AdReward &#x306E;&#x6982;&#x8981;</a><br /> <a href="https://docs.gs2.io/ja/api_reference/ad_reward/">GS2-AdReward &#x306E;API&#x30EA;&#x30D5;&#x30A1;&#x30EC;&#x30F3;&#x30B9;</a></p> </div> kazutomo スキルツリー管理機能が追加されました hatenablog://entry/820878482967428201 2023-09-14T18:28:43+09:00 2023-09-14T18:28:43+09:00 はじめに キャラクターや装備、スキルの育成要素としてスキルツリーが用いられることが一般的です。 これまで GS2 にはスキルツリーを実現するために適切なマイクロサービスがない状態がつづていました。 今回、はスキルツリーを実現するための専用のマイクロサービスを追加することで、より様々な仕様に対応できるようにしました。 追加された機能の詳細 依存関係のあるノードをマスターデータとして定義し、ノードを解放するために必要なコストを設定します。 ノードを解放した際にゲーム内で得られる効果については、ノードのメタデータに格納できます。ノードの解放APIを呼び出すと、コストを消費しノードを解放状態に変更しま… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>キャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターや装備、スキルの育成要素としてスキルツリーが用いられることが一般的です。<br /> これまで GS2 にはスキルツリーを実現するために適切なマイクロサービスがない状態がつづていました。<br /> 今回、はスキルツリーを実現するための専用のマイクロサービスを追加することで、より様々な仕様に対応できるようにしました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>依存関係のあるノードをマスターデータとして定義し、ノードを解放するために必要なコストを設定します。<br /> ノードを解放した際にゲーム内で得られる効果については、ノードの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E1%A5%BF%A5%C7%A1%BC%A5%BF">メタデータ</a>に格納できます。</p><p>ノードの解放<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>を呼び出すと、コストを消費しノードを解放状態に変更します。<br /> ゲーム内では解放済みノードの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E1%A5%BF%A5%C7%A1%BC%A5%BF">メタデータ</a>からステータスの補正を行えます。</p><p>解放したノードを未解放に戻す機能も有しています。<br /> この場合、ノードごとに個別に設定可能な返却率に基づいてコストとして設定したリソースの返却を行えます。</p> </div> <div class="section"> <h3 id="Unity-からの使用例">Unity からの使用例</h3> <div class="section"> <h4 id="ノードの解放状態を取得">ノードの解放状態を取得</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.SkillTree.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Status( ); <span class="synType">var</span> item <span class="synStatement">=</span> <span class="synStatement">await</span> domain.ModelAsync(); </pre> </div> <div class="section"> <h4 id="ノードを解放">ノードを解放</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.SkillTree.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Status( ); <span class="synType">var</span> result <span class="synStatement">=</span> <span class="synStatement">await</span> domain.ReleaseAsync( <span class="synStatement"> nodeModelNames:</span> <span class="synStatement">new</span> string[] { <span class="synConstant">&quot;node-0001&quot;</span>, } ); </pre> </div> <div class="section"> <h4 id="ノードを未解放に戻す">ノードを未解放に戻す</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.SkillTree.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Status( ); <span class="synType">var</span> result <span class="synStatement">=</span> <span class="synStatement">await</span> domain.RestrainAsync( <span class="synStatement"> nodeModelNames:</span> <span class="synStatement">new</span> string[] { <span class="synConstant">&quot;node-0001&quot;</span>, } ); </pre> </div> <div class="section"> <h4 id="ノードの解放状態をリセットする">ノードの解放状態をリセットする</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.SkillTree.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ).Status( ); <span class="synType">var</span> result <span class="synStatement">=</span> <span class="synStatement">await</span> domain.ResetAsync( ); </pre><p>関連資料<br /> <a href="https://docs.gs2.io/ja/microservices/skill_tree/">GS2-SkillTree &#x306E;&#x6982;&#x8981;</a><br /> <a href="https://docs.gs2.io/ja/api_reference/skill_tree/">GS2-SkillTree &#x306E;API&#x30EA;&#x30D5;&#x30A1;&#x30EC;&#x30F3;&#x30B9;</a></p> </div> </div> kazutomo GS2-Inbox のメッセージ取得時に既読状態でフィルタする機能を追加しました hatenablog://entry/820878482966281182 2023-09-11T11:00:00+09:00 2023-09-11T11:00:00+09:00 はじめに GS2-Inbox はプレゼントボックスやメッセージボックスの機能を提供するマイクロサービスです。 機能追加の背景 GS2-Inbox の受信済みメッセージの一覧取得は、既読・未読問わず新しいものから順番に取得することしかできませんでした。 しかし、UXの観点から既読メッセージと、未読メッセージを分けて表示したいことがあります。 追加された機能の詳細 メッセージ一覧を取得するAPIに isRead 引数を追加しました。 この値を null を指定、または省略することでこれまで通り、既読未読問わず一覧を取得できます。 true を指定することで既読メッセージのみ、false を指定する… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2-Inbox はプレゼントボックスやメッセージボックスの機能を提供するマイクロサービスです。</p> </div> <div class="section"> <h3 id="機能追加の背景">機能追加の背景</h3> <p>GS2-Inbox の受信済みメッセージの一覧取得は、既読・未読問わず新しいものから順番に取得することしかできませんでした。<br /> しかし、UXの観点から既読メッセージと、未読メッセージを分けて表示したいことがあります。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <p>メッセージ一覧を取得する<a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>に isRead 引数を追加しました。<br /> この値を null を指定、または省略することでこれまで通り、既読未読問わず一覧を取得できます。<br /> true を指定することで既読メッセージのみ、false を指定することで未読メッセージのみを取得できます。</p> </div> <div class="section"> <h3 id="Unity-からの使用例">Unity からの使用例</h3> <div class="section"> <h4 id="全てのメッセージ一覧を取得">全てのメッセージ一覧を取得</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Inbox.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ); <span class="synType">var</span> items <span class="synStatement">=</span> <span class="synStatement">await</span> domain.MessagesAsync( ).ToListAsync(); </pre> </div> <div class="section"> <h4 id="未読メッセージ一覧を取得">未読メッセージ一覧を取得</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Inbox.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ); <span class="synType">var</span> items <span class="synStatement">=</span> <span class="synStatement">await</span> domain.MessagesAsync( <span class="synStatement"> isRead:</span> <span class="synConstant">false</span> ).ToListAsync(); </pre> </div> <div class="section"> <h4 id="既読メッセージ一覧を取得">既読メッセージ一覧を取得</h4> <pre class="code lang-cs" data-lang="cs" data-unlink> <span class="synType">var</span> domain <span class="synStatement">=</span> gs2.Inbox.Namespace( <span class="synStatement"> namespaceName:</span> <span class="synConstant">&quot;namespace-0001&quot;</span> ).Me( <span class="synStatement"> gameSession:</span> GameSession ); <span class="synType">var</span> items <span class="synStatement">=</span> <span class="synStatement">await</span> domain.MessagesAsync( <span class="synStatement"> isRead:</span> <span class="synConstant">true</span> ).ToListAsync(); </pre> </div> </div> kazutomo 新しいトランザクションアクションが多数追加されました hatenablog://entry/820878482965008611 2023-09-06T11:00:00+09:00 2023-09-06T11:00:01+09:00 はじめに GS2 のトランザクション処理は「消費アクション」と「入手アクション」を組み合わせたものを定義し アイテムの購入や、クエストの開始、クエストの終了など様々なタイミングで実行することでマイクロサービス間の連携を実現します。 今回のアップデートでは、トランザクションに指定可能な「消費アクション」と「入手アクション」を多数追加しました。 追加された機能の詳細 消費アクション Gs2Dictionary:DeleteEntriesByUserId GS2-Dictionary のエントリーには全てを削除するリセットしかありませんでしたが、個別のエントリを削除する消費アクションが追加されました… <div class="section"> <h3 id="はじめに">はじめに</h3> <p>GS2 の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>処理は「消費アクション」と「入手アクション」を組み合わせたものを定義し<br /> アイテムの購入や、ク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トの開始、ク<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トの終了など様々なタイミングで実行することでマイクロサービス間の連携を実現します。<br /> 今回のアップデートでは、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%F3%A5%B6%A5%AF%A5%B7%A5%E7%A5%F3">トランザクション</a>に指定可能な「消費アクション」と「入手アクション」を多数追加しました。</p> </div> <div class="section"> <h3 id="追加された機能の詳細">追加された機能の詳細</h3> <div class="section"> <h4 id="消費アクション">消費アクション</h4> <div class="section"> <h5 id="Gs2DictionaryDeleteEntriesByUserId">Gs2Dictionary:DeleteEntriesByUserId</h5> <p>GS2-Dictionary のエントリーには全てを削除するリセットしかありませんでしたが、個別のエントリを削除する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2ExperienceSubExperienceByUserId">Gs2Experience:SubExperienceByUserId</h5> <p>GS2-Experience に経験値を減算する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2ExperienceSubRankCapByUserId">Gs2Experience:SubRankCapByUserId</h5> <p>GS2-Experience にランクキャップを減算する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2FormationSubMoldCapacityByUserId">Gs2Formation:SubMoldCapacityByUserId</h5> <p>GS2-Formation に編成の保存領域を減算する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2IdleDecreaseMaximumIdleMinutesByUserId">Gs2Idle:DecreaseMaximumIdleMinutesByUserId</h5> <p>GS2-Idle に放置時間の最大値を減算する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2InboxDeleteMessageByUserId">Gs2Inbox:DeleteMessageByUserId</h5> <p>GS2-Inbox にメッセージを削除する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2JobQueueDeleteJobByUserId">Gs2JobQueue:DeleteJobByUserId</h5> <p>GS2-JobQueue にジョブを削除する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2MissionDecreaseCounterByUserId">Gs2Mission:DecreaseCounterByUserId</h5> <p>GS2-Mission にカウンターを減算する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2ScheduleDeleteTriggerByUserId">Gs2Schedule:DeleteTriggerByUserId</h5> <p>GS2-Schedule にトリガー済みのフラグを削除する消費アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2StaminaDecreaseMaxValueByUserId">Gs2Stamina:DecreaseMaxValueByUserId</h5> <p>GS2-Stamina にスタミナ値の最大値を減算する消費アクションが追加されました。</p> </div> </div> <div class="section"> <h4 id="入手アクション">入手アクション</h4> <div class="section"> <h5 id="Gs2LimitCountDownByUserId">Gs2Limit:CountDownByUserId</h5> <p>GS2-Limit に回数制限のカウンターを減算する入手アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2LoginRewardUnmarkReceivedByUserId">Gs2LoginReward:UnmarkReceivedByUserId</h5> <p>GS2-LoginReward に受け取り済みフラグを削除する入手アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2MissionRevertReceiveByUserId">Gs2Mission:RevertReceiveByUserId</h5> <p>GS2-Mission に報酬の受け取り済みフラグを削除する入手アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2MoneyRevertRecordReceipt">Gs2Money:RevertRecordReceipt</h5> <p>GS2-Money に記録済みのプラットフォームレシート情報を削除する入手アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2SerialKeyRevertUseByUserId">Gs2SerialKey:RevertUseByUserId</h5> <p>GS2-SerialKey にシリアルコードの使用済みフラグを削除する入手アクションが追加されました。</p> </div> <div class="section"> <h5 id="Gs2ShowcaseDecrementPurchaseCountByUserId">Gs2Showcase:DecrementPurchaseCountByUserId</h5> <p>GS2-Showcase のランダム陳列棚の購入回数を減算する入手アクションが追加されました。</p> </div> </div> </div> kazutomo