More Related Content
Similar to DeNAのサーバー"コード"レスアーキテクチャ (20)
DeNAのサーバー"コード"レスアーキテクチャ
- 3. 自己紹介
3
• 大竹 悠人(Haruto Otake)
• 共通基盤部 ゲームデベロッパーライブラリグループ
• a.k.a @Trapezoid
• 経歴
• 2009/4 ドワンゴに新卒入社
• 2013/5 DeNAに中途入社
• 仕事
• Unity向けの様々な内製ライブラリの実装・保守
• DeNAの内製ゲームサーバーフレームワーク TakashoのクライアントSDK開発
• Unityを使ったゲームタイトルへの技術面でのサポート(火消し業)
• DeNA TechCon 2020でも喋ります
- 8. モバイルゲームで主流なアーキテクチャ
● クライアント
○ プレイヤーデータを直接改変する権利を持たない
○ サーバーが持つ情報を必要に応じて取得する
○ 得られた情報に基づいてクライアントがゲームを進行する
■ バトルや、バトル外のUIなど
○ 進行に応じてプレイヤーデータ等に変更を加える場合には、
その内容に応じた種類のサーバーのAPIを進行に応じたパラメータを付けて呼び出す
● サーバー
○ プレイヤーデータを直接改変する権利を持つ
○ プレイヤーデータの更新や取得の内容に応じてAPIを実装する
○ バトルの結果などは、クライアントのパラメータを信じつつ、バリデーションを行
う
8
- 9. このアーキテクチャのPros & Cons
• Pros
• サーバー側にでプレイヤーデータの変更を行うので、ユーザーの手元でのデータの
改変が難しい
• チート対策になる
• モバイルゲームによくある非同期なプレイヤー間連携機能を、サーバー側で自由に
実装することで、クライアントからその複雑さを隠蔽できる
• サーバーでAPIを実装して、クライアントはそれを呼ぶだけで複雑なイベントな
ども実現できる
• 多くのエンジニアに馴染みがある
• Cons
• サーバーとクライアントの実装の分断が発生する
• 実装言語もパラダイムも違うので、設計やチームが分断するポイントになる
• サーバーロジックだけでは完結せず,どこまでサーバーでやるか?という判断が難し
い
• バトルはクライアントで持つが、サーバーでバリデーションもかける...など
• 社内でもチームごとにノウハウが散らばる
• アプリケーションとしての品質もチーム次第に 9
- 11. どのようなアーキテクチャか?
• クライアントコード中心とは?
• サーバーとクライアントのどちらに寄せるかを悩むものを、クライアントに寄せる
• クライアントはサーバーからプレイヤーデータそのものを取得する
• プレイヤーデータに基づいてクライアントがゲームを進行する
• 進行に応じてプレイヤーデータに変更を加える場合には、プレイヤーが更新後のプ
レイヤーデータそのものを生成し、その内容をサーバー側にAPIを通じて保存する
• ゲーム用mBaaS(PlayFab, GameSparks, GS2)に近い
• ターゲットが異なる
• mBaaSはどうしても簡単に動くものを作る、ということにフォーカスされがち
• サーバー”コード”レスはグローバル展開する大規模タイトルもターゲット
• サーバー”コード”レスでは、クライアントでプレイヤーデータの大部分を制御する
• 多くのゲーム用mBaaSでは、サーバー側に多くの設定や実装を持つ
• FaaS的に振る舞いを設定できるようにしたり
• mBaaS側にゲームドメインに沿った広範な機能が実装されていたり
11
- 23. 1. プレイヤーデータのテーブル設計
• プレイヤーデータの定義はクライアント側で行う
• 扱う場所と構造の定義の場所は近くする
• RDBMSの構造(表)以上の表現力があり、安全に扱える形でスキーマ設計を行う
• Data Access Objectの記述はコード生成などを使って自動化する
• 配列や構造体のような構造も利用する。縛られる理由が薄い
• プレイヤーデータは必要なものだけを読み込めるようにする
• イベントなど、運用時にデータ量の飛躍的な増加が想定されるものでは必須
• 失敗事例
• エンジニアが手作業でテーブルごとのシリアライズ処理を書いていた
• 実装上の凡ミスによる不具合が発生
• 安全に倒すと扱える表現が貧弱に(リストやObjectといった構造が扱い辛く)
• 起動時に過去のイベントデータをロードしていた
• 起動が遅くなりユーザー体験が悪化, メモリを過剰に消費してクラッシュ
• 扱うデータサイズが増えることで、サーバー負荷も増大
23
- 24. 2. プレイヤーデータの一貫性の保証
• プレイヤーデータの更新時に一貫性が担保されないと、重大な不具合につながる
• プレイヤーデータの更新は、異なる2つのリソース間での交換であることが多い
• 強化素材を消費して強化する など
• プレイヤーデータ更新を伴うリクエストには冪等性が必要
• 更新時に、クライアントから更新の成功を100%検知することは不可能
• ネットワークエラー時など、サーバー側では成功している可能性がある
• 成功していた更新をもう一度実行しても、何も起こらないようにする
• 失敗事例
• リトライ時に意図せず、二重に仮想通貨が消費されてしまう
• 複数テーブルに跨ったプレイヤーデータの更新が、片方のテーブルだけ更新されて
しまい整合性が崩壊
24
- 25. 3. ドメインロジックのレイヤー設計
• クライアントが持つロジックの量が重い
• クライアント側でテーブルの定義を全て持ち、データの変更処理なども書く
• サーバーでやってきた事をクライアントで書く以上、それを保守できる設計が必要
• 酷い設計/実装をしたときの影響が相対的に大きい
• 扱える範囲が広い分、プレイに致命的な影響を及ぼしうる
• ユーザーからの信頼に毀損するような不具合に繋がる
• 失敗事例
• コントローラに処理が集中、プレイヤーデータの操作とUIが密結合になりがちに
• プレイヤーデータの変更内容が予測がつかない
25
- 26. 4. プレイヤーデータのチート対策
• サーバー”コード”レスでは、クライアントチートされる危険性が高い
• メモリをチートツールで書き換えると、そのままその結果がサーバー上に反映でき
る
• クライアントは実行バイナリが手元にあるので、逆アセンブルなどでの解析が可能
• できる対策を可能な限り行う必要がある
• https://www.slideshare.net/dena_tech/dena-techcon-2019-132194701
• メモリ書き換えの検知
• ハッシュ値チェックや、マスキングなどを徹底する
• デバッガ/エミュレータの検出
• チートの前提になる環境を検出して、チートできなくする
26
- 27. 5. 時刻の正確性の保証
• 安易に信頼できる時計が存在しなくなる
• サーバー側の時間が知れるのは、サーバーと通信を行った瞬間のみ
• クライアント側でリアルタイムに時間を知りたい場合には、頼りにならない
• 毎回サーバーに問い合わせるのも非現実的
• ユーザーの端末の時間は信用できない
• 悪意を持って調整してくる可能性も
• サーバー側の時間と、それを知った瞬間からの経過時間によってリアルタイムな時
刻を判定する
• 経過時間を調整されることも防ぐ必要がある
27
- 30. D4Lの誕生
• DeNA Domain Driven Design Library
• これまで述べてきたようなサーバー”コード”レスでクライアントを実装する上での課題
を解決するためのライブラリ
• と、それを使った実装パターン
• (未リリースの..)ゲームタイトルの開発用に開発した草の根ライブラリ
• 先立って開発されていたSakasho採用タイトルでの知見を踏まえて実装
• 単体のライブラリとして社内で公開したところ、様々なタイトルで使われるように
• 累計約10タイトルで採用。5年の歴史を持つ。大規模なゲームでの利用事例も
• 追加で発生していった問題は都度カバーできるようにしていった
• 現在ではSakashoの後継フレームワークのTakasho向けのバージョンを開発/保守
• Takasho SDKの一部として提供
30
- 31. プレイヤーデータのテーブル設計
• D4L.Definition.Compiler
• パース用のDAO(Data Access Object)と、そのLoaderを自動生成するモジュール
• クライアントから読み込むマスターデータも定義できる
• テーブル指向
• テーブルがカラムの定義と複数の行を持ち、行はカラムの定義に合わせた値を持つ
• 表ではない。RDBMSと違って、構造体やリストをカラムとして持てる
• データの反映は行単位で行える
• スキーマの定義方法
• C#コードを記述
• できるだけUnityエンジニアが直感的にかけるように
• C#のクラスがテーブルに、そのフィールドがカラムになる
31
- 34. スキーマ定義に使える型
• C#のプリミティブな型
• string / (u)int / (u)short / (u)long / (s)byte / double / float / char / bool
• C#のプリミティブな型に対応するメモリ改ざん保護型
• Guard~ (GuardString等), Guard~Array(GuardStringArray等)
• 構造体 (SubSchema属性を付けた定義から構造体が生成される)
• enum
• 上記すべてのリスト
• Dictionary<TKey, TValue>
• TKeyはstringか整数型, TValueは任意の型
34
- 37. 垂直分割
37
所持装備 キャラ倉庫 キャラごとの育成度 キャラごとの装備情報
キャラ倉庫 キャラごとの育成度 キャラごとの装備情報所持装備
所持品コンテキスト 倉庫コンテキスト キャラコンテキスト
まとめて取得したい単位
(コンテキスト)に分類
シーンや場面に応じて読み込むテーブルを分けることができる
(この分類はあくまで例です)
- 40. D4Lによるレイヤ設計
• D4Lは軽量DDDによる設計を推奨している
• Repository / ValueObject / Specificationの実装の為の基礎実装が存在
• 個別のビジネスロジックはDataContextやDAOに直接アクセスはしない
• Repositoryを経由させ、ビジネスロジックから扱うEntitiyとDAOを分離する
• プレイヤーデータは運用していくと頻繁にテーブルの種類が追加される
• テーブルの追加がEntityの実装に与える
影響を最小化できる
• 強く強制はしていない
• ゲームによって準拠度はまちまち
40
- 42. D4L以外によるチート対策
• 社内のセキュリティ部と協力して様々なチート対策を徹底的に実施
• エミュレータ/ルート化の検出
• 署名の検証
• apkの署名のシグネチャが、想定のものかを検証
• 実行バイナリのダイジェスト検証
• コード領域のダイジェストを計算して、想定のものかを検証
• コードの難読化や、改ざん検出ソリューションの導入
• 内製化も進めている
• これらの複合的に組み合わせて相互に守り合わせることで、突破をより難しくする
• チートユーザーの動向を観察して継続的な対策を実施
• チート検出を回避されたら、回避策への対処を検討
• いたちごっこを地道に繰り返す
• https://www.slideshare.net/dena_tech/dena-techcon-2019-132194701 42
- 44. D4Lによる時刻の正確性の保証
● D4L.Moment
○ 信頼できる時間軸を扱うライブラリ
● 実行中の自プロセスの経過時間(ProcessTime)を信頼する
○ 時間が十分な精度で書き換えられずに進行するような相対時刻
○ 経過した時間が分かるだけで、現在の日時が分かるわけではない
● あるProcessTimeの時点での日時を記録しておく
○ 都度現在のProcessTimeと記録済みのProcessTimeの差を記録された日時に足すこ
とで現在の日時を計算できる
44
- 50. サーバー”コード”レスアーキテクチャの臨界点
• サーバーに寄せたい機能 != 共通機能
• ソーシャル性の高い機能はサーバーがあったほうがやりやすい
• このような機能が無いと、今の市場で戦うのは厳しい
• 共通機能として意識して設計しようとすると、難易度も速度感も損なわれる
• 頑張って共通機能として実装しても、実質1タイトルしか使わないことも
• Sakashoはマルチテナントなので、下手を打つと全社影響が出る
• ゲーム側のエンジニアがSakashoチーム内にやってきてAPIを追加するようにな
った
• ゲームの為の実装だけをゲーム開発チームの意思決定で行いたい
• 一方で、サーバー”コード”レス自体は一定成功を収めている
• 同じ基準で、クライアントで書きやすい機能を実装するにはやはり効率的
• 基本路線は保ちつつ、案件ごとの独自APIも気軽に定義できるようにしたい
• 必要なことが、必要なところに、書くべき人が書けるようであるべき
50
- 51. SakashoからTakashoへ
• Sakashoの後継となるプロダクト、Takashoを開発中
• マルチテナントからシングルテナントへ。他のゲームへの影響が無くなる
• FeatureSetという概念で複数のAPIセットをカプセル化
• 共通機能と独自機能を簡単に共存/保守できるようなサーバーフレームワーク & 共通
機能セット
• サーバー”コード”レスは基本にしつつ、独自APIを気軽に案件ごとに足していける
• 大量に独自APIを作る使い方も、殆ど共通APIで済ませる使い方も選択できる
• D4LもTakashoに合わせて再設計
• 殆ど書き直した...
• ライブラリ無しでSakashoを使う時の苦労から、TakashoからはD4Lは必ず提供さ
れるようになった
• Takashoについての既存の資料
• https://www.slideshare.net/dena_tech/dena-87961203
• https://speakerdeck.com/kyotak/xin-kemusahaji-pan-takashotefalsegoyan-yu-huo-
yong-shi-li-falseshao-jie
51
Editor's Notes
- どこまでサーバーでやるか?というのはどういうことかというと
それこそブラウザゲームの時代はバトルなどのゲーム本体の処理も含めて全てサーバーで処理を行って、クライアントにはその経過や結果を表示しているだけでした。
今はアプリになり、さらにインタラクティブ性が高まっていった結果として、このような純粋なサーバーロジックのみの作り方はできなくなりました。
ゲームバトル前にパラメータを受け取ってバトル後に結果をサーバーに渡す、ということをよくするかと思いますが、これはバトルの内部の処理はクライアントを信頼することで成り立っています。
このギャップを埋めるために一部サーバーで結果のバリデーションなどもすることになったりするわけですが、このあたりが人や案件ごとに解釈を分ける要因になってしまいます。