$30 off During Our Annual Pro Sale. View Details »

メタバースプラットフォーム 「INSPIX WORLD」はPHPもC++もまとめてC#に統一! ~MagicOnionが支えるバックエンド最適化手法~

Pulse Co., Ltd.
September 21, 2023

メタバースプラットフォーム 「INSPIX WORLD」はPHPもC++もまとめてC#に統一! ~MagicOnionが支えるバックエンド最適化手法~

8/26開催 CEDEC2023にて登壇した資料となります。
Pulseが展開する仮想空間『INSPIX WORLD』のエンジニアリーダーによる
当該プロジェクトの大改修についてご紹介します!

Pulse Co., Ltd.

September 21, 2023
Tweet

Other Decks in Programming

Transcript

  1. メタバースプラットフォーム
    「INSPIX WORLD」はPHPもC++もまとめてC#に統一!
    ~MagicOnionが支えるバックエンド最適化手法~
    2023.08.23
    小柴 祐二 / 河合 宜文

    View Slide

  2. ❖ INSPIX WORLDの光と闇
    ➢ 長期運用に向かない問題
    ➢ 当初のサーバー設計
    ❖ 再開発でC#大統一へ
    ➢ APIとDBの改修
    ➢ ログ構造の改修
    ➢ 開発環境の統一化
    ❖ リアルタイムサーバーの大改修
    ➢ MagicOnionとLogicLooper
    ➢ フルスケーラブルメタバースアーキテクチャ
    ➢ INSPIX WORLDにおける設計とパフォーマンス
    ❖ まとめ
    アジェンダ

    View Slide

  3. 小柴 祐二 / Koshiba Yuji
    パルス株式会社
    メタバース事業部 エンジニアリーダー
    韓国PCオンラインゲームパブリッシャーにて
    MMORPGの開発・運用
    現在(INSPIX WORLD)
    2020年
    ・エンジニアとしてコンテンツ開発に参画
    2021年
    ・エンジニアリーダーに就任
    ・講演内容に関わるサーバーサイド大改修を行う
    自己紹介

    View Slide

  4. 自己紹介
    株式会社Cysharp
    CEO/CTO
    @neuecc
    Microsoft MVP for Developer Technologies(C#) 2011-
    CEDEC AWARDS 2022 エンジニアリング部門優秀賞
    「.NETのクラスライブラリ設計 改訂新版」監訳
    多数のC#向けOSS開発
    MessagePack for C#
    MagicOnion
    MemoryPack
    UniRx
    UniTask
    AlterNats
    河合 宜文 / Kawai Yoshifumi

    View Slide

  5. INSPIX WORLDとは
    ボイスチャットで楽しめる人狼ゲームや脱出ゲームなど
    様々なコンテンツを鋭意開発中
    3Dメタバースプラットフォーム
    アバター&ゲームで多彩なコミュニケーション
    ※2023年8月現在:新バージョンに向けて長期メンテナンスを実施中です

    View Slide

  6. ヒプノシスマイク
    予選ライブチケット販売数(全て有料)
    INSPIX WORLDの歴史
    2021年4月にアーリーアクセスを開始
    初手のVRライブは好調にスタート
    まるで順風満帆なスタート
    しかし…
    その後、技術統括となり全体を見渡した際に
    深淵を垣間見てしまう…

    View Slide

  7. 本当はヤバかったINSPIX WORLD
    結果、メタバースとして長期運用に向かない状態になっていた
    UGC廃止後も再設計を行わず開発を継続
    着手当初は
    ユーザー生成コンテンツ
    (以下、UGC)を想定
    スケジュールの都合上
    パフォーマンスは二の次で開発
    ver.1.0
    その後、プロジェクト方針で仕
    様を変更することに
    モックver.
    UGC時代の処

    UGC時代の処理
    新機能①
    新機能②

    View Slide

  8. 不要な機能・仕様が多数
    拡張開発が必ず工数大
    データロックが多発
    操作不可の待ち時間が
    高頻度で発生
    APIレスポンス速度が遅く
    リアルタイム性が悪い
    アップデートコスト増大 ユーザー体感が悪い 快適さ皆無のサーバー設計
    幾度とない仕様変更による不要機能が多く
    アップデートを行う際には必ず仕様見直しを測る事態に
    lock!
    基本
    工数
    見直し
    工数
    基本
    工数
    処理待ち…
    やりたい事があっても根幹の問題で”開発自体が行えない”事態も発生
    長期運用に向かなかった原因

    View Slide

  9. リアルタイムサーバーの
    許容接続者数に上限があった
    メタバース空間として遊んでほしいが
    多数のプレイヤー接続に対応できない
    情報保持など追加の処理が必要になり
    クライアント側の開発工数を圧迫する事態に
    キリが無くなってくるのでこの辺で…
    初期の設計では接続人数を無制限にするため
    ロジックをほぼ持たせず
    同期や通知のリレー等のサーバーとして設計
    サーバー主導でイベント発火ができない
    クライアント主導での実装が必要だった
    その他にも問題は多数
    リレーサーバー形式だった為、クライアントホス
    ト型としての実装しかできなかった

    View Slide

  10. 課題を解決するために…
    改造までのストーリー
    サーバーサイドを全て作り直すしかない

    メタバース&運用に対して
    課題になるポイントを整理
    大規模な改修を決定
    サーバーサイドのPHP⇒C#への
    マイグレーションが決定
    マイグレーションのリファクタリングと同時に新規
    開発も並走することが決定
    API総数200以上のマイグレーション&
    新規開発で1からの再設計も行う事に

    View Slide

  11. 目指すべきものを再整理
    高速でストレスのないプレイ体験
    急激なユーザー増加にも耐えうるスケーラブルなシステム
    機能拡張・拡充がスムーズに行える設計
    問題を解決するために目標設定

    View Slide

  12. リリース当初のサーバー設計

    View Slide

  13. リリース初期の INSPIX WORLD のサーバー構成
    C#
    クライアント C++
    PHP
    当初のサーバー構成図

    View Slide

  14. クライアントはリアルタイムサーバーと通信する際、
    このリバースプロキシへを通じて通信
    C#
    クライアント
    当初のサーバー構成図
    C++
    PHP

    View Slide

  15. AreaServerはルーム内の位置情報を管理して同期を行うためのサーバーになり、
    ロジックは持たず、同じルームに所属していユーザー間との同期通知に専念します。
    C#
    クライアント
    当初のサーバー構成図
    ルーム内の位置情報同期に専念
    C++
    PHP

    View Slide

  16. コンテンツへの参加や招待機能など
    基本的なユーザーの行動はAPIを経由してやりとりを行います。
    C#
    クライアント
    当初のサーバー構成図
    C++
    PHP

    View Slide

  17. WorldServerは、接続している全ユーザーの接続先情報・管理や通知に必要な全ての接続グループ情報を保
    持しています。
    全ての情報はこのサーバーが保持しているため、本構成での要となります。
    C#
    クライアント
    当初のサーバー構成図
    C++
    PHP
    全てのセッション情報を保持

    View Slide

  18. クライアントへの通知、APIからWorldサーバーへ送信し
    APIから受信した情報を元に、RevProxyを経由してクライアントへメッセージを送信
    C#
    クライアント
    当初のサーバー構成図
    C++
    PHP

    View Slide

  19. 開発途中で大規模なメタバースアプリへ方針が変更となりましたが、
    モックで制作した基本設計をベースにサーバーが構築されており各所に問題を抱える状態に
    C#
    クライアント C++
    PHP
    リレーサーバー形式による
    実装コストの増大
    当初のサーバー構成図

    View Slide

  20. 開発途中で大規模なメタバースアプリへ方針が変更となりましたが、
    モックで制作した基本設計をベースにサーバーが構築されており各所に問題を抱える状態に
    C#
    クライアント C++
    PHP
    単一障害点の発生
    当初のサーバー構成図

    View Slide

  21. 開発途中で大規模なメタバースアプリへ方針が変更となりましたが、
    モックで制作した基本設計をベースにサーバーが構築されており各所に問題を抱える状態に
    C#
    クライアント
    コネクション非維持による
    処理速度の低下
    メタバースアプリに適していない
    データ構造等
    当初のサーバー構成図
    C++
    PHP

    View Slide

  22. 開発途中で大規模なメタバースアプリへ方針が変更となりましたが、
    モックで制作した基本設計をベースにサーバーが構築されており各所に問題を抱える状態に
    C#
    クライアント
    言語が異なる事で管理が煩雑化
    当初のサーバー構成図
    C++
    PHP

    View Slide

  23. 全てにおいて詰んでました…

    View Slide

  24. 処理速度が遅くなるなど直接的な影響をユーザーに及ぼしている
    当初の設計ではメタバースとしての長期運用が難しい
    バックエンドを丸ごとイチから再設計

    View Slide

  25. 別の言語で作成されていたAPI、リアルタイムサーバーを全てC#に統一することで
    IDEの統一、クライアント、サーバー間でのコード共有などもC#で開発を完結!
    クライアントの開発はUnityを使用
    リアルタイムサーバーは
    MagicOnionを採用しC#へ
    バックエンドを全てC#へ
    クライアントとの連携向上のため、API
    サーバーをC#へ
    インターフェース実装でクライアントとサーバー間の
    コード共有が可能になり並行開発等も容易に
    バックエンドを全てC#統一することで
    同一プロジェクトとして管理できるように
    APIをPHPからC#に改修決定 JSONからMessagePackへの変更で
    クライアント⇔API⇔リアルタイムサーバー全ての
    通信プロトコルを共通化
    MagicOnion製作者であるCysharp社の河合さんにも
    バックエンドの監修に参画していただけることに
    PHPもC++もまとめてC#に統一!で全部解決

    View Slide

  26. まずはAPIとDBを改修

    View Slide

  27. APIサーバー:やるべきこと
    言語の違いによる連携コストの発生
    送受信ともに言語の違いからデータ変換周
    りで度々トラブルがあった。
    バグの調査も言語の違いからお互いに頼り
    きり状態に
    APIもC#に書き換えプロトコルを共通化
    サーバー⇔クライアントの開発効率を考慮
    リアルタイムサーバー同様に言語を
    C#に共通

    データベース:やるべきこと
    各DBの役割や情報を整理
    色々な箇所に散らばっていた
    状況を整理し、DB毎の役割を分別
    一般的なMMOのようなDB構造へ
    分別したデータを整理
    スキーマを正しく分離することで
    一元管理と負荷分散を行えるように
    APIとDBの改修について

    View Slide

  28. アイテムの上限数が統一されていない
    アイテムIDが目まぐるしく変わる
    水平分割すべきではないデータの無用な分割
    … 無制限では延々と
    アイテムが増加
     通信時間が増大していく
    所持上限や倉庫機能を作成
    長時間プレイの負荷を軽減
    A 12345
    例えば”トレード”を行うと
    アイテムのユニークIDが変動
     調査が困難×追跡不可能
    A B
    C
    常に12345
    アイテムのユニークIDを固定
    ログを柔軟に追跡できる改修
    欲しいデータ
    構造整理を行えておらず
    不要なデータも参照
     余計な負荷が発生
    参照データ スキーマを整理し
    必要な参照のみを
    行う形で最適化
    B 67890
    改善された内容の一部

    View Slide

  29. データ参照時、Redisを都度参照しておりロスが多く発生
    cache data
    A B
    メタバースには”多数のユーザーが存在”
    ⇒キャッシュから参照するように調整
     ロスを最大限削減できよう改修
    C D
    APIからリアルタイムサーバーへのコネクションを都度生成&破棄していた
    コネクション生成 破棄
    リアルタイムサーバーへの
    RPS(秒間リクエスト数)の向上
    ⇒リアルタイムサーバーとの
     セッションを維持するように改修
    処理速度が大幅に減少
    cache
    APCuのキャッシュ機構:未実装
    Redis
    常に全件取得
    改善された内容の一部

    View Slide

  30. モック時代の不要なデータが大量:膨大な処理負荷に
    UGC時代の処理
    新機能①
    新機能②
    新機能③
    ・UGCベースの不要な処理残
    ・削除せずに新機能を追加
     削除もしづらい状態&
     無駄な処理負荷が増大
    不要なコード破棄&再整理
    ・200個のAPIを移植
    ・100個のAPIを新規追加
    異なる言語によってClientとServer間で実装難度が高くなっていた
    ClientとServerがそれぞれ
    インターフェースを実装し
    パラメータ表を確認するアナログ管理
     ヒューマンエラーの多発
    C#に統一したことで
    PJ内のプロトコルの共通化
    (JSONからMessagePackに移行)
    ⇒エラーが激減&効率が大幅向上
    CL
    SV
    Error!
    改善された内容の一部

    View Slide

  31. 前提
    旧構成のAPI検証画面
    ここまでの改修を終えた後、旧サーバー構成と新サーバー構成でパフォーマンス比較を実施
    ・旧サーバー構成と新サーバー構成を構築、マシンは同スペックに設定
    ・テストシナリオ:アカウント作成 → ログイン → キャラクター作成まで
    2,000人 5,000人 10,000人
    仮想環境:同時接続人数
    10,000人 5,000人
    10,000人
    5,000人
    10,000人
    5,000人
    5,000人以上ではエラーが発生
    レスポンス速度も遅い結果に
    2,000人では特にエラーは見られず
    旧構成+仮想スペックでは
    “3,000人”が限度
    エラー レスポンス
    秒間Request数:366 (2000人)
    改修によるパフォーマンス比較
    APIサーバー
    DB
    2,000人
    DB負荷がほぼ100%に

    View Slide

  32. 新構成のAPI検証画面
    レスポンス
    エラー
    2,000人
    5,000人 10,000人
    エラーなし
    10,000人
    10,000人までエラーは見られず
    レスポンスも旧構成と比べて極端に低くなった
    API及びDBの総合的な改修で、10,000人のリクエストも許容できる最適化を達成
    また、50,000人でも殆どエラーが発生せずリクエストを受け付けることが出来た
    秒間Request数:604 (2000人)
    改修によるパフォーマンス比較
    5,000人 2,000人
    APIサーバー DB
    DB負荷は最大60%で打ち止め
    新構成では10,000人以上の
    リクエストを許容できる結果に

    View Slide

  33. APIとDBのある程度の最適化
    DB構造も変えたし、運営効率をあげる為にも
    ログ構造も改修しよう
    長期運用を行うにはログ分析ができないといけない…
    完了!

    View Slide

  34. 長期運用を見据えたログ構造の最適化

    View Slide

  35. ▼ユーザーログをDBに書き込み
    API
    ログの書き込み
    DB
    書き込み完了
    書き込み終わるまで
    APIの処理は止まる
    APIサーバーからDBへログを直接書き込む形式
    一連の動作の中で書き込み待ちが発生
    運用が長期化する=データサイズの肥大化
    運用コスト面での懸念もある
    数ミリ秒単位の小さい遅延だがより良いパフォーマンスを実現したかった
    当初のログ保存
    ログ保存方法の変更

    View Slide

  36. AzureEventHubs(以下Hub)とAzureDataExplorerを採用したことで
    APIでログをQueueに詰め込んで非同期でHubへログを送るように改善
    ⇒APIからDBへの書き込み時に発生していた遅延が0となり負荷を軽減することもできた
    非同期でのログ保存により、APIサーバーの応答性と低レイテンシが
    確保され最大限のパフォーマンスに
    ログデータの保存方法を変更
    連携により非同期の
    ログ保存/バッファリングが
    できるように
    ▼AzureDataExplorerを用いて
    ・リアルタイムに
     解析ができるよう最適化
    ・分析ツールも
     同時に連携させ分析も可能
    ログ保存方法の変更

    View Slide

  37. 「アイテムの取得」「売却」「取引」など、一連のログが1か所に保存されており
    ログの種別が増えた場合はカラムが増えてしまう構造に…
    1つのカテゴリに
    同種のログを詰め込んでいた
    不要なデータが多く
    状況把握も適切に行えない
    dataがNullになっていて不要
    分析やサポートが
    難しい状態
    Statusでのアイテム取得情報が
    分かりづらい
    ログ種別が増えると
    カラムが増えてしまい可読性が落ちる
    不要データが含まれている
    当初のログ構造
    運営や分析を前提としたログ構造になっていなかった

    View Slide

  38. 旧ログ 新ログ 細分化されたログ (参考として一部のみ記載 )
    ・同種のログに詰め込む形式を破棄
    ・ログの細分化を行うよう改善
    まずはログの構成整理:”ログの細分化”に着手
    Item - アイテム情報
    購入 獲得 売却
    使用情報 などの全情報
    ItemBuy - アイテムの購入
    ItemGet - アイテム獲得
    ItemSell - アイテム売却
    ItemUse - アイテム使用
    Room - ルーム情報 RoomIn - ルーム入室
    RoomOut - ルーム退室
    一定のフォーマットを前提として細分化することでログ結合が可能となり
    後述の”管理ツール”でのデータ取得/解析が容易にできるよう最適化
    入室 退室 などの全情報
    ログ構造の刷新

    View Slide

  39. 不要データが含まれている
    どのメソッドを経由して
    [ItemGet - アイテム獲得]ログの参考
    だれが
    どのアイテムを
    いくつ獲得・いくつ持っている
    なにで
    どこで
    例えば「アイテム獲得を調査したい」と考えた際にどのような情報が必要になるか
    運営的な視点も加えてログ構成を検討
    ログ構造の刷新

    View Slide

  40. カスタマーサポート等でログを解析する為に、
    運営チームが欲しい情報を取得できるよう
    ”管理ツール”を開発
    その際下記3つを必須項目として開発を進めることになりました
    ログの細分化により
    情報が整理しやすくなった
    管理ツールによる
    データの取得/解析の効率化
    NEXT
    専門知識不要・誰でも簡単
    完成した
    ツール
    三原則 最新の情報が常に取得可能 抽出情報を自由に選べる
    管理ツールの開発

    View Slide

  41. 日時を選択
    どんなログを抽出するか
    どのユーザーを対象に
    (AccountIDやCharacterID)
    どの情報を対象に
    (InstanceIDやItemID等)
    ログを時系列で表示
    管理ツールで何ができるのか:参考
    管理ツールの開発

    View Slide

  42. ガチャ
    ※ゲーム画面は開発中のものとなります
    ログを時系列に表示
    ガチャ/ゲーム報酬のログ種を選択
    トレード
    売却
    これらの行動も
    全てリアルタイム確認可能
    管理ツールで何ができるのか:参考
    ゲーム
    管理ツールの開発

    View Slide

  43. ユーザーアクションの流れ
    例:様々なアイテムの獲得 トレードや売却などの各種行動
    ・誰でも簡単にユーザー行動を
     全て時系列でログとして可視化
    ・調査やカスタマーサポートなどが
     迅速に行える
    管理ツールの開発

    View Slide

  44. C#統一による開発環境の効率化

    View Slide

  45. デバッグを行うだけでも複雑な手順が求められていた
    移行前
    ・PHP Stormはバックエンドの
     デバッグを行う際に
     ツールを挟むなど複雑な手順が必要
    ・XDebugのVer.とも合わせる必要があり
     インストールが必要になる
    ・開発効率が多少なりとも落ちる
    C#統一後
    Visual Studio(VS)に
    一本化
    ・C#に統一したことで
     VSひとつで完結したデバッグが可能に
    ・IDEの変更でClientもServerも同じ
     Editorで作業、連携効率が向上した
    ・INSPIX WORLDではAzureを採用しており
     MS社製でビルド&デプロイが容易に回せる
    Copyright © 2023 JetBrains s.r.o. PhpStorm and the PhpStorm logo are registered trademarks of JetBrains s.r.o.
    IDE(統合開発環境)をPHPStormからVisualStudioへ移行

    View Slide

  46. 環境構築の作業コストが非常に高かった
    PHPにて制作 C#統一後
    Vagrantにて仮想のLinux環境を作成し
    Dockerコンテナを展開
    環境によって異なるトラブルが多発
    解決のために多くのリソースを割く事に
    再開発時にDocker Desktopでダイレクトに
    コンテナを展開する方向に
    VisualStudioとの連携により環境構築が
    Dockerのインストールのみでできるように
    環境破棄から再生成間での構築時間が
    10倍以上早くなる結果に
    開発環境がVagrantからDockerへ移行

    View Slide

  47. 言語が異なる事でプロジェクト内のコード管理が煩雑化していた
    CL:C#
    SV:PHP,C++ Clientでのデータ利用は
    ステップを挟んで実装する必要があった
    CL/SV:C#
    C#統一により同一のソリューションで
    ・定数やクラス、ロジック等を ClientとServerで共通化
    ・APIとリアルタイムサーバー間で定数やロジックも共通化
    ⇒開発効率が大幅に向上
    JsonUtilityで変換
    変換時にデータ不整合等の連携エラーが多発
    C#統一化による共通プロジェクト化

    View Slide

  48. ・ネットワーク通信
    ・ストレージ利用の最適化
    ⇒異なるプラットフォームとの
     データ交換がスムーズに行える
     ライブラリとなっている
    ※Githubより
    https://github.com/MessagePack-CSharp/
    MessagePack-CSharp
    MessagePack for C#
    C#向けに最適化された、 JSONと比べ高速なデータ処理  / コンパクトなデータサイズを実現
    できるハイパフォーマンスのシリアライザです。
    後述するMagicOnionでもMessagePack for C#が採用されています。
    MessagePackとは…

    View Slide

  49. ここから本題
    重要なリアルタイムサーバーの改修を…

    View Slide

  50. とにかく大人数を捌く事を重視し、最小限のリアルタイムサーバー台数で
    最大限の人数効率を出すために”リレーサーバー形式”を採用
    再開発のため、問題点を一旦整理
    ユーザー同士のゲームプレイなど、コミュニケーション重視に切り替えた結果、
    様々な機能で”クライアントホスト型”での実装が必要に
    クライアント側の実装コストが悪くなる状態へ
    新規コンテンツ開発&アップデートに対して
    コストが異常に高くなってしまう結果に
    問題点の整理

    View Slide

  51. INSPIX WORLDはリレーサーバー形式のクライアントホスト型だった
    ホスト兼サーバー
    ゲスト ゲスト ゲスト
    クライアントのうち1台がホストとなり
    同時にサーバーとしての役割も担う
    ▼主な特徴
    ・サーバーをクライアントが担っているので
     サーバー費用が抑えられる
    ・クライアント側での処理が複雑になる
    ・クライアント改造などのチートを防げない 等
    リレーサーバー
    リレーサーバーを介してゲストとの
    情報送受信を行い、ゲーム進行の同期
    簡易図解
    リレーサーバー形式のクライアントホスト型とは

    View Slide

  52. リレーサーバー形式のクライアントホスト型で開発はかなり複雑に…
    INSPIX WORLDの人狼ゲームは”8人プレイ”
    ホスト
    ゲスト ゲスト ゲスト ゲスト
    8人全員のデータを常に同期
    ▼ホストが切断
    復帰した元ホスト
    ▼復帰した元ホストに新ホストがデータを復元
    ……
    基本方針:復帰した際にゲームに戻れることが必要だった
    ゲスト 新ホスト
    ホスト
    ▼ゲーム継続のためゲストを新ホストに
    新ホスト
    ▼多くの懸念
    ・プレイ時間中、常に多人数の情報の保持/同期
    ・データの整合性が取れないとゲームが進行しない
    ・”切断⇒復帰”時のプレイヤーデータの修復のため
     ホスト以外も含めて常に情報の同期が必要だった
    修正やアップデートが慎重になり
    対応コストが増大していく
    クライアントホスト型における懸念

    View Slide

  53. MagicOnionとLogicLooperを組み合わせた構成で
    サーバーを構築することに
    ユーザー同士が様々な体験を共有できる
    メタバース空間を実現する為に
    サーバー主導でリアルタイム通信を成立させたい

    View Slide

  54. MagicOnion & LogicLooperとは

    View Slide

  55. ”MagicOnion”はこれらの要件を満たすだけでなく
    C#への言語統一が出来るなど多くのメリットがある事から採用を決定!
    通信フレームワークは超重要!
    多数のユーザーが
    同じ空間を共有できる”同期処理”
    ストレスなく遊べる
    ユーザー体験に直結する”処理速度”
    メタバースアプリで重要なこと

    View Slide

  56. MagicOnionで利用できるAPI
    ”Service”と”StreamingHub”の2種のAPIが利用可能で、それぞれ特徴があります
    UnaryRPC(1Request - 1Response) 
    リクエストを行ったクライアントにのみレスポンスを返せる
    Service
    リアルタイム通信に特化
    コネクションを常に維持しCL⇔SVで自由にデータが送受信可能
    全体へのブロードキャストもできる
    StreamingHub
    CL
    MagicOnion
    Request
    Response
    MagicOnion
    Message
    Broadcast
    Requestを送った人にResponseを返すのみ
    CLがサーバーを介して相互通信
    MagicOnionとは
    CLとSV間のリアルタイム通信を容易に実現するためのフレームワーク
    ⇒バイナリ形式のシリアライズと gRPCプロトコルを用いて簡潔なコードで
     高速なリアルタイム通信を実現することができます
    CL
    MagicOnion
    https://github.com/Cysharp/MagicOnion

    View Slide

  57. MagicOnionのメリット
    ・APIサーバー同様にMessagePackが利用可能となり、同一プロジェクトで管理可能に
    ・インタフェース定義された C#ファイルをCL⇔SV間で共有することができ開発効率向上に寄与
    ・CL側からgRPC経由でサーバーメソッドを呼び出すだけで簡単に通信処理を実行できる
    MagicOnionが向いているゲーム
    ・プレイヤーが戦術や戦略をリアルタイムで指定しユニットに指示を与える
    ・指示に合わせたユニットの動きを迅速に同期
    上記は一例:リアルタイムで多人数がプレイするゲームに最も適しています
    MO
    リアルタイムストラテジー
    ・多数のプレイヤーが同時に参加するオンラインゲーム
    ・リアルタイムな対戦や協力プレイ、
     チャット等の通信を効率的に行う事ができる
    INSPIX WORLD:仕組み的に近似
    MagicOnion

    View Slide

  58. ただ、MagicOnionは高速・多人数でのリアルタイム通信は実現できるものの、サー
    バ側の複雑なロジックを効率よく実装するには別の何かが欲しい。
    LogicLooperの登場

    View Slide

  59. State等で管理すればLooperがゲーム進行を管理することも可能
    CLにロジックを持たせなければチートの抑制にも繋がる
    LogicLooperとは
    .NETでのサーバーアプリケーションでループを使用した
    プログラミングモデルを実装するためのライブラリ
    主にサーバーサイドにロジックがあるゲームサーバーのようなユースケースにフォーカスしています
    LogicLooper
    MagicOnion
    クライアント
    Updateのようにフレーム毎に処理を繰り返す
    引用:https://github.com/Cysharp/LogicLooper/blob/master/README.ja.md
    State変更 State変更
    通知を受けて
    次の進行へ
    変更後の
    処理を通知
    サーバーサイドでゲーム進行を行う為のロジックを実装することができ
    CPUコア数に応じてスレッドに負荷分散が可能
    Example
    LogicLooper

    View Slide

  60. LogicLooperが向いているゲーム
    LogicLooperのメリット
    ・ゲーム進行を制御する為のロジックループを簡単に実装できる
    ・ゲーム動作をリアルタイムに制御する事が可能です
    ・CPUコア数に応じてマルチスレッド処理が出来る為、高頻度の計算処理等に適している
    ・複雑なゲームパートをリアルタイムで進行させる必要があるゲームでは
     計算や非同期の処理を
    Logiclooperで行う事でゲーム進行が簡潔に実装できる
    リアルタイムマルチプレイヤーゲーム
    非同期処理やタスクのスケジューリングが効率的に出来るため
    高頻度でデータの同期と非同期処理が必要になるようなゲームでは
    同期やデータ更新をスムーズに行う事が可能になる
    シミュレーションゲーム
    INSPIX WORLD
    LogicLooper

    View Slide

  61. 通信コストを抑えて、且つ水平分割可能な仕組みが必要に
    MagicOnion & LogicLooperでの実装
    ①複数のユーザーを同一空間上で同期させる場合同じMagicOnionに接続する必要があり
     接続人数を増加する為にはサーバースペックが要求され運用コストが高くなってしまう
    ②別のMagicOnionに接続しているユーザーとの同期にはMagicOnion同士のセッションを
     常時繋ぐ必要があり通信コストが増大してしまう
    MagicOnionとLogicLooperのみで実装を進めた場合、スケーラブルをどうするかが肝に…

    View Slide

  62. ではどうするか…を考えていたら
    河合さんからタイムリーな連絡が!!

    View Slide

  63. フルスケーラブルメタバースアーキテクチャが
    完成しました!!

    View Slide

  64. APIサーバーはステートレスだが、リアルタイムサーバーもステートレスにできるものもある。例えば Chatなどの
    メッセージの伝搬や、クライアント間のリレーには向いている。しかし状態 (ステート)を持てないため、ロジックを
    サーバー側に実装する場合は向いていない
    ステートレスサーバー
    MagicOnion
    (State)
    Client
    Client
    Client
    MagicOnion
    (Stateless)
    MagicOnion
    (State)
    MagicOnion
    (Stateless)
    MagicOnion
    (Stateless)
    ロードバランサー
    PubSub
    クライアントは異なる MagicOnion
    サーバーに繋ぐ
    Good: クライアントの接続先の調整不要
    Bad: ステートフルにしづらい
    Bad: PubSubのパフォーマンスが気になる
    各サーバーの裏の PubSubミドルウェアに
    よってメッセージを繋ぐ
    gRPC(HTTP/2)

    View Slide

  65. いわゆるDedicated Serverと同じく、ステートやロジックを持った単一サーバーにクライアントが繋ぎに行く構成。
    1ゲームセッション(ルーム)に対するサーバーIPの管理に工夫が必要、特に Kubernetes環境の場合に難しくな
    る。
    ステートフルサーバー
    Client
    Client
    Client
    MagicOnion
    (LogicLooper)
    単一サーバー接続
    LogicLooperを同居させて
    ステートを持たせる
    Good: シンプル
    Bad: クライアントの接続先管理が別途必要
    Bad: 単一サーバーに集約されるのでスケーリン
    グしづらい、1セッション1プロセスの場合は、コス
    ト面のバランスが悪いことも

    View Slide

  66. メタバースアーキテクチャ
    MagicOnion
    (State)
    Client
    Client
    Client MagicOnion
    MagicOnion
    (State)
    MagicOnion
    MagicOnion
    ロジックサーバーを単体で分離して、
    ワールド全体のステート管理、ゲーム
    ループを用いたフレーム間のバッチ処理
    などを行う
    PubSub
    繋いでいるクライアント固有のステート管理(そのユーザーに不可視のデー
    タであればカリングして送信しないようにする、など)
    LogicLooper
    ステートレスサーバーの管理の容易さとステートフルサーバーの機能性の両立といういいとこ取り(?)
    クライアントとの送受信処理とロジック処理を分離することでスケーリング性も高まった

    View Slide

  67. メタバースアーキテクチャ
    MagicOnion
    (State)
    Client
    Client
    Client MagicOnion
    MagicOnion
    (State)
    MagicOnion
    MagicOnion
    ロジックサーバーを単体で分離して、
    ワールド全体のステート管理、ゲーム
    ループを用いたフレーム間のバッチ処理
    などを行う
    繋いでいるクライアント固有のステート管理(そのユーザーに不可視のデー
    タであればカリングして送信しないようにする、など)
    LogicLooper
    ステートレスサーバーの管理の容易さとステートフルサーバーの機能性の両立といういいとこ取り(?)
    クライアントとの送受信処理とロジック処理を分離することでスケーリング性も高まった
    PubSub
    ただしMagicOnionとLogicLooperを繋ぐPubSubの
    パフォーマンスは依然として気になる ……

    View Slide

  68. NATS / AlterNats / MemoryPack
    PubSub部分のパフォーマンスを高めれば接点が増えることによるロスを打ち消せる
    一般的に使われるRedis PubSubではなく、PubSub専用の高速なミドルウェア NATSを採用
    更にクライアント側の性能を高めるために独自クライアント (AlterNats)を開発(後に公式クライアントに採用
    )
    同時期に、より高速なシリアライザー MemoryPackを開発
    これらの組み合わせで PubSub自体のロスを極限まで抑えた
    MagicOnion
    MagicOnion
    MagicOnion NATS LogicLooper
    AlterNats(nats.net.v2) + MemoryPack
    AlterNats(nats.net.v2) + MemoryPack
    https://nats.io/
    https://github.com/Cysharp/AlterNats/
    https://github.com/Cysharp/MemoryPack
    特にメタバース的な座標データの配列な
    どで数百倍のパフォーマンス向上

    View Slide

  69. Dedicated(Headless) Server vs .NET Server
    Unity Dedicated(Headless) Server
    クライアントアプリケーションをそのままサーバーでホスティング
    利点:
    クライアント開発をそのままシームレスにサーバーに持ち込める
    PhysicsやAIロジック、NavMeshなどをそのままサーバーで実行可能
    欠点:
    ビルドやデプロイがサーバー開発用環境に比べて複雑
    パッケージマネージャの不足など 3rd Party Libraryの利用が難しい
    ロギング・モニタリング・ DB通信など基本的なライブラリ郡からして不足気味
    サーバー専用と比べてパフォーマンス上の無駄が多い
    特にUnityの場合はランタイムが古いため性能面でかなり差が出る
    Dedicated Serverに上げた欠点は全てない(利点)
    今回のメタバースアーキテクチャのような自由度の高い構成を実現可能
    PhysicsやAIロジック、NavMeshなどの話は何らかの解決が必要
    ある程度のところはC#同士の共有で解決できるところもある(できないものもある)
    クライアントがC#なら.NETを選ばない理由ないのでは!?
    .NET Server(MagicOnion, LogicLooper)
    餅は餅屋、みたいなところがあるので、原則 .NET
    Serverで組んだほうがいいと個人的には思います
    他言語でのServer(Node.js, C++, Go, Rust, etc…)

    View Slide

  70. このアーキテクチャでメタバースを実現する

    View Slide

  71. INSPIX WORLDで採用しているメタバースアーキテクチャで構成したリアルタイムサーバーは完全なるフルス
    ケーラブル構成となり下記のようなフローになります。
    新INSPIX WORLDの構成
    C#
    クライアント C#
    C#

    View Slide

  72. MagicOnionはClientとNATS&LogicLooperの間に立ち
    リバースプロキシの役割を果たします
    クライアントが接続する MagicOnionはリ
    バースプロキシの役割を果たす
    新INSPIX WORLDの構成
    C#
    クライアント C#
    C#

    View Slide

  73. LogicLooperはゲームループのロジックに専念させる
    接続先はNATSに任せるので気にしない
    新INSPIX WORLDの構成
    C#
    クライアント C#
    C#
    ゲームループのロジックに専念

    View Slide

  74. C#
    クライアント
    MagicOnionとLogicLooperは直接繋がっておらず、お互いがどのマシンを把握していません
    ここの通信を担保する為に、 NATSの存在が欠かせません
     直接は接続していない
    新INSPIX WORLDの構成

    View Slide

  75. LogicLooperからのメッセージ、MagicOnionからのメッセージはNATSを経由することでPub/Subにて間接的に
    メッセージのやり取りを行う事で各々の役割が明確に分離され実装がシンプルになります
    Publish/Subscribe
    新INSPIX WORLDの構成
    C#
    クライアント

    View Slide

  76. 今後追加されるコンテンツでは1~200人以上が参加できるような物もあり
    アトラクションでは切断後の復帰機能も必須となる
    INSPIX WORLDは現在ロビー、ハウジング可能なマイルーム、人狼ゲームや脱出ゲームが
    実装されており、その他のコンテンツは
    Ver3.0にて追加される予定となります
    ロビー
    200人
    マイルーム
    8人
    脱出ゲーム
    4人
    人狼ゲーム
    8人
    追加コンテンツ
    追加コンテンツは複数
    鋭意開発中
    1~200人
    INSPIX WORLDのコンテンツ

    View Slide

  77. メタバースアーキテクチャの設計の概要について
    MagicOnionではどのような通信を行うかの定義を StreamingHubで行っており、
    サーバ上の対応する処理との通信を行います。
    INSPIX WORLDでは用途に分けて大まかに 3種類のStreaming Hubを用意しています。
    クライアントA
    BasicHub
    マイルームHub
    (ContentsHub)
    ModuleHub
    クライアントB
    BasicHub
    人狼Hub
    (ContentsHub)
    ModuleHub
    MagicOnion
    BasicProcess
    マイルームHub
    (ContentsHub)
    ModuleHub
    人狼Hub
    (ContentsHub)
    NATS
    LogicLooper
    マイルームLogic
    マイルームProcess
    (ContentsProcess)
    ModuleProcess
    人狼Process
    (ContentsProcess)
    ModuleProcess
    人狼Logic
    WebAPI

    View Slide

  78. メタバースアーキテクチャの設計の概要について
    BasicHubはMagicOnionとクライアント間の通信に関する基本的な処理を行うためのものです。
    リアルタイム通信全体の開始と終了、認証や通信が中断した際の再接続などを行います。
    また、WebAPIから送られてきた通知も BasicHubを通じて受け取ります。
    クライアントA
    BasicHub
    マイルームHub
    (ContentsHub)
    ModuleHub
    クライアントB
    BasicHub
    人狼Hub
    (ContentsHub)
    ModuleHub
    MagicOnion
    BasicProcess
    マイルームHub
    (ContentsHub)
    ModuleHub
    人狼Hub
    (ContentsHub)
    NATS
    LogicLooper
    マイルームLogic
    マイルームProcess
    (ContentsProcess)
    ModuleProcess
    人狼Process
    (ContentsProcess)
    ModuleProcess
    人狼Logic
    WebAPI

    View Slide

  79. メタバースアーキテクチャの設計の概要について
    ContentsHubはLogicLooper上に存在するコンテンツの処理との通信を行うためのものです。
    INSPIX WORLDではマイルームでの家具配置の変更や人狼ゲームでのステート制御など、ユーザーがその
    時利用しているコンテンツに応じた通信を行います。
    クライアントA
    BasicHub
    マイルームHub
    (ContentsHub)
    ModuleHub
    クライアントB
    BasicHub
    人狼Hub
    (ContentsHub)
    ModuleHub
    MagicOnion
    BasicProcess
    マイルームHub
    (ContentsHub)
    ModuleHub
    人狼Hub
    (ContentsHub)
    NATS
    LogicLooper
    マイルームLogic
    マイルームProcess
    (ContentsProcess)
    ModuleProcess
    人狼Process
    (ContentsProcess)
    ModuleProcess
    人狼Logic
    WebAPI

    View Slide

  80. メタバースアーキテクチャの設計の概要について
    ContentsHubはLogicLooper上に存在するコンテンツの処理との通信を行うためのものです。
    INSPIX WORLDではマイルームでの家具配置の変更や人狼ゲームでのステート制御など、ユーザーがその
    時利用しているコンテンツに応じた通信を行います。
    クライアントA
    BasicHub
    マイルームHub
    (ContentsHub)
    ModuleHub
    クライアントB
    BasicHub
    人狼Hub
    (ContentsHub)
    ModuleHub
    MagicOnion
    BasicProcess
    マイルームHub
    (ContentsHub)
    ModuleHub
    人狼Hub
    (ContentsHub)
    NATS
    LogicLooper
    マイルームLogic
    マイルームProcess
    (ContentsProcess)
    ModuleProcess
    人狼Process
    (ContentsProcess)
    ModuleProcess
    人狼Logic
    WebAPI
    処理の内容が異なる為コンテンツ毎に
    StreamingHubを作成
    MagicOnionではデータ中継時に
    ユーザーに不要なものはカリングできる
    対応したHubを持つクライアントへ
    NATSを介してデータ送信

    View Slide

  81. メタバースアーキテクチャの設計の概要について
    ContentsHubは複数同時に持つ場合もあります。
    通信は全てMO経由で一本化されているため、通信のコネクションが増えるといった事はありません。
    クライアントA
    BasicHub
    マイルームHub
    (ContentsHub)
    ModuleHub
    MagicOnion
    BasicProcess
    マイルームHub
    (ContentsHub)
    ModuleHub
    マッチングHub
    (ContentsHub)
    NATS
    LogicLooper
    コンテンツLogic
    マイルームProcess
    (ContentsProcess)
    ModuleProcess
    マッチングProcess
    (ContentsProcess)
    マッチングLogic
    WebAPI
    マッチングHub
    (ContentsHub)
    ContentsHubは複数同時に持つ場合も

    View Slide

  82. メタバースアーキテクチャの設計の概要について
    ModuleHubはアバターの座標同期やアニメーション同期といった、複数のコンテンツで共通する
    処理を行うための物です。サーバ側では各コンテンツのロジック上で動作します。
    クライアントA
    BasicHub
    マイルームHub
    (ContentsHub)
    アバター同期Hub
    (ModuleHub)
    MagicOnion
    BasicProcess
    マイルームHub
    (ContentsHub)
    人狼Hub
    (ContentsHub)
    NATS
    LogicLooper
    マイルームLogic
    マイルームProcess
    (ContentsProcess)
    アバター同期Process
    ModuleProcess
    人狼Process
    (ContentsProcess)
    人狼Logic
    WebAPI
    アバター同期Hub
    (ModuleHub)
    アバター同期Process
    ModuleProcess
    コンテンツの処理とは
    別の共通処理を行うために実装
    アバターの位置同期はどのコンテンツを
    プレイしていようと共通処理を行うため
    別途実装を行うのは非効率
    複数コンテンツで共通する処理は
    機能毎にModuleHubを実装して使い分ける形に

    View Slide

  83. 同一の空間に1500体のアバターを生成し、同期処理が正常に行えるかの検証を行いました
    クライアント/サーバー共にまだ最適化を は行っていませんが、 同期は問題ない結果となりました
    1500人の同一空間同期テスト
    図2:アバターが動いている様子
    図1:ゲーム画面と接続人数
    アーキテクチャ採用後の同期数テスト

    View Slide

  84. LogicLooperによるロジックサーバーの負荷分散
    ▼ゲーム進行等のロジックを全て LogicLooperに任せることができるようになった
    ・CPUのコア数に応じたマルチスレッド処理で
     プロセス内で複数のロジックが効率的に並列実行されるように
    ・一つのLogicLooperに負荷が大きくかかったとしても
     オートスケールにより台数を増やせば簡潔に解決可能
    ・更にクライアントホストが持っていたゲーム同期ロジックが
     Serverに移動したことで、ホスト端末の負荷も軽減しました。
    ロジックの開発に専念
    前述した設計において、同期周りを MagicOnionのStreamingHubにまとめることで
    ・共通での処理ができるようになり、各コンテンツ開発においては
     それらのアバター同期等を考慮せずロジックのみの開発に専念出来るようになった
     ⇒新規開発及びアップデートにおける人的コストの削減に繋がりました
    リアルタイムサーバーの改修後

    View Slide

  85. 市場的にサーバーエンジニアが少ないこともあり人員不足の解消にも寄与!?
    サーバー&
    クライアント
    サーバー
    クライアント
    サーバー
    CL
    SV
    or
    クライアントとサーバーの並行作業または単一での作業完結が可能に
    機能実装時、担当者の不在や作業の遅れによって担当者の手が止まってしまう事もあったが
    MagicOnionの導入によりInterfaceを共有することで
    両者の作業を並行して進めることができるようになり開発効率の大きな向上に
    旧:実装
    フロー
    担当1
    クライアント
    担当2
    基本的にSVが先に実装を行い次にクライアントが作業
    新:実装
    フロー
    担当 担当
    MagicOnionの利用で並行作業が可能に
    エンジニアによっては両作業を 1人でも可能に
    CL、SV共に同じInterfaceを実装することで共通化
    共通化したことでCL⇔SV間での実装精度も向上
    C#
    リアルタイムサーバーの改修後

    View Slide

  86. 各サーバーのCPUを監視し、あらかじめ設定された 閾値を超えると自動でスケールが行われる
    ⇒ゲームへのユーザー接続上限数を実質 ”無制限”にすることが可能になった
    NATSはクラスタ化可能
    自動でスケールできるが最適化は必須!
    MagicOnion+NATS+LogicLooperを用いて理論上人数無制限のオートスケールが可能に
    自動でスケール
    リアルタイムサーバーの改修後

    View Slide

  87. まとめ

    View Slide

  88. 高速でストレスのないプレイ体験
    急激なユーザー増加にも耐えうるスケーラブルなシステム
    機能拡張・拡充がスムーズに行える設計
    まとめ
    最適化によりINSPIX WORLDが掲げた目標を達成
    クライアントホスト型からの脱却及び
    C#統一化による開発効率向上
    APIやDBの改修によるデータ構造及びリアルタイムサーバーの改修
    メタバースアーキテクチャで実現

    View Slide

  89. まとめ
    当たり前の形にする為に時には再開発の提案も必要に!
    プロジェクトの長期運用を目指さない開発はいないと思いますが、
    当初の方針から大きく舵が切られてしまう事も多々あると思います。
    そんな時は、一度全てを捨て再開発する勇気も時には必要です
    C#大統一によるプロジェクト統一化
    C#への統一に起因してコードやプロトコル等あらゆる共通化を進めた事で
    クライアントとサーバー間の作業効率やミスの削減に繋がり、
    再開発もスピード感を持って対応できる環境が構築できる結果となりました
    フルスケーラブルメタバースアーキテクチャでメタバースを実現
    技術の革新が進み、モバイル端末でも据え置き機と変わらないクオリティに
    なってきたとはいえ、人数がボトルネックになりがちなメタバース

    そんな問題も解決できるかもしれないこのアーキテクチャを是非試してみてください!

    View Slide

  90. ご清聴ありがとうございました!

    View Slide