UE5 WebRTC4Unrealでオンラインゲームを作ろう
EOS、SteamP2Pは本質ではない
SteamのネイティブなロビーP2P機能はSteamアカウントへの依存が強いです。EOSのP2PやロビーはEpic Gamesアカウントを必須とせず各プラットフォームのアカウントや独自認証と組み合わせられますがEOS固有のユーザーIDやリレー、ロビー基盤には依存するため、サービス基盤としてのベンダーロックインは残ります。 又ベンダーのサーバーにロックインされるため、サービスが落ちた際やメンテナンスなどをコントロールできない問題も発生します。
WebRTC
ゲームの世界から目を広げると、WebRTCというP2P標準規格があります。これはビデオ会議などのリアルタイム用途に制定された後述するTURNやNAT越えのためプロトコルです。ゲーム独自に実装するよりこれに乗る方が実装が楽なのでこれを使用します。
WebRTC4Unrealというプラグインを作りました
EOSのコードを見るとわかりますが、UEは高度に抽象化されているためNetDriverとNetConnectionを差し替えればその上のアプリケーションレイヤーは特に変えることなく動かすことができます。
https://github.com/mizuamedesu/WebRTC4Unreal
詳しくはRiku Ishikawaさんのスライドを見るとわかりやすいです。 https://www.docswell.com/s/strvert/K7V3JJ-unrealengine-network-architecture#p32
通常のListen ServerではIpNetDriverがUDPでパケットを運びます。WebRTC4Unrealでは独自のWebRTC4UnrealNetDriverが同じパケットを受け取り、WebRTC DataChannelへ渡します。受信側ではDataChannelから取り出したバイト列を対応するUNetConnectionへ戻します。ゲーム側から見ると、通信路がUDPからWebRTCへ変わっただけです
又車輪の再発名をしても意味がないのでUnreal EngineにはPixel Streaming用のlibwebrtcがあり、これを利用しました

マルチクラウドに対応する
特定サービス専用のプラグインにすると、結局別のベンダーロックインを作ることになります。そこでWebRTC4Unrealでは、ルームを作成・解決するProviderと、Unreal Packetを運ぶTransportをインターフェースで分離しました。
初期では、Cloudflare用の実装とHeteroCloud用の実装を入れました。上記の通り拡張することが可能です。
HeteroCloud Flowで実装する
https://heterocloud.mizuame.app
HeteroCloudは私がオーバーレイネットワークからクラウド基盤、サービスまで展開している次世代のクラウドです。IPA産業サイバーセキュリティセンターサイバー技術研究室での客員研究員としての現在進行形での成果であり、すべてのソースがMITライセンスで公開されています。HeteroNetworkは、自宅、サーバールーム、クラウドなど、異なるLANやNATの内側にある計算機を1つの安全なオーバーレイネットワークとして接続することができます。
この仕組みにより、設置場所、回線、性能、所有者が異なる計算機でも、到達可能性を自動判定しながら1つの基盤として利用できます。特に各拠点に点在していて単独では活用しにくい余剰PCや小型サーバーを束ね、Kubernetesの計算資源として利用できるため、クラウドベンダーが達成し得ない超低価格でサービスを提供できます。
https://github.com/orgs/IPA-CyberLab/repositories
HeteroCloudのサービス、FlowではWebRTC機能を提供しており、シグナリングからTURNまですべて無料で行えます。 OpenAPIによる提供を行っているため、jsonを渡せばLLMが勝手に実装してくれると思います。
https://flow.heterocloud.mizuame.app/openapi.json https://flow.heterocloud.mizuame.app/docs/
流れを理解する
コードでの説明はいらないと思うので省略しますが、以下が流れです
- 長期開発者資格情報は、バックエンドサーバーだけが保持します。バックエンドはHeteroCloudへ要求し、Unrealクライアントごとに、必要最小限の権限と有効期限を持つ短期アクセス資格情報を発行します。
- ルーム作成者は短期アクセス資格情報を使い、p2pモードのルームを作成します。作成後は自分自身もルームへ参加し、Unreal側をListen Serverとして起動します。
- 参加クライアントも自分専用の短期アクセス資格情報を取得し、作成者から共有されたルームIDを使ってルームへ参加します。
- HostとClientはFlowのシグナリングサーバーへ接続し、SDP Offer、SDP Answer、ICE Candidateを交換します。
- ICEはSTUNを利用して直接接続を試みます。NATやファイアウォールの条件によって直接接続できない場合に限り、HeteroCloudのTURNサーバーへ自動的にフォールバックします。
- WebRTC DataChannelが開通すると、WebRTC4UnrealのNetDriverがUnrealのReplicationやRPCのパケットをDataChannel経由で送受信します。
バックエンドサーバーは今回はデモのためローカルで簡易的なものを立てました。本来はクライアントに一時トークンを発行する際にIdPなどによる認証が入ります

Cloudflareで実装する
CloudflareでもHeteroCloud Flow版と同じWebRTC4Unreal Coreを利用できます。ただし、Flowがルーム、シグナリング、STUN、TURNを一体のAPIとして提供しているのに対し、Cloudflare版では複数のサービスを組み合わせて接続基盤を構築します。
今回のCloudflareDirectProviderでは、次の3つを使用しました。
- Cloudflare Workers:Unrealクライアントからのルーム作成・参加要求を受け付けるHTTP API
- Durable Objects:ルーム単位の状態管理とWebSocketシグナリング
- Cloudflare TURN:直接接続できなかった場合に使用する中継サーバー
Durable Objectsは、IDごとに一意なインスタンスを割り当てられるため、ルームのように複数クライアントが同じ状態を共有する用途と相性がよいです。今回はルームIDからDirectRoomDurable Objectを決定し、ルーム情報、Host、参加者、最大参加人数、有効期限をSQLiteへ保存しています。
参加者ごとにランダムな参加トークンを発行しますが、Durable Objectへ保存するのはそのハッシュだけです。各クライアントは自分の参加トークンを使ってシグナリング用WebSocketへ接続し、SDP Offer、SDP Answer、ICE Candidateを交換します。WebSocketにはHibernation APIを利用しているため、接続を維持したまま、通信のないDurable Objectを休止させられます。ルームの有効期限が切れた場合はAlarmによってWebSocketと保存状態を破棄します。
接続の流れを理解する
- HostがWorkerへルーム作成を要求すると、WorkerはルームID、Host用の参加者ID、参加トークンを発行し、そのルームに対応するDurable Objectを作成します。
- Hostはシグナリング用WebSocketへ接続し、Unreal側をListen Serverとして起動します。画面には、相手へ共有するルームIDだけを表示します。
- Clientは受け取ったルームIDを参加欄へ貼り付けてWorkerへJoinを要求します。WorkerはClient専用の参加者IDと参加トークンを発行します。
- HostとClientは同じDurable ObjectへWebSocket接続し、SDPとICE Candidateを交換します。
- WebRTCのICEは、最初にHost CandidateやSTUNで得たServer Reflexive Candidateを使った直接接続を試します。
- NATやファイアウォールの条件によって直接接続できなかった場合だけ、Cloudflare TURNを経由します。
- DataChannelが開通すると、WebRTC4UnrealNetDriverがClientごとにUNetConnectionを作成し、通常のListen Serverと同じReplicationとRPCを開始します。
どちらもListen Serverなので、接続形状はHostを中心としたスター型です。Client同士が直接接続するのではなく、それぞれのClientがHostとWebRTC接続を確立します。また、Hostが終了すればゲームセッションも終了します。
HeteroCloud+Cloudflareで実装する
最初に書いた通り、この本質はベンダーロックインからの解放です。HeteroCloudはまだベータ段階ですし、無料なので現時点では高い品質保証はできません。しかし、CloudflareのTURNやDurable Object、workerは無料枠はあるにせよある程度の範疇を超えるとお金がかかります。 そこでベンダーロックインからの解放の優位性を生かし、デフォはHeteroCloud Flowで動かし、フォールバック時にCloudflareに移行するworkerを作ってみましょう!!!
https://github.com/mizuamedesu/WebRTC4Unreal/tree/main/Backend/Worker

このようにworkerが認証情報を持ってFlowに委託し、失敗した場合のみCloudflare Durable Object+TURNの構成にフォールバックします。 これにより通常時はworkerのリクエストのみにコストがかかり、Flowがダウンした際にはCloudflareにフォールバックする低コストHAになりました。
もちろん現状ですとworkerが単一障害点になっていますが、例えばこれを他のクラウド(AWS Lambda)も混ぜたフェイルオーバー構成にすれば、単一障害点をなくすことができます。
最後に
WebRTC自体はそこまで難しい技術ではないので、LLMで爆速実装ができる今日、ベンダーロックインによって受けられる抽象化の恩恵が薄くなってきていると思います。より多様な選択肢が選べるようになったとも捉えられ、意思決定の重要性が増しています。