この記事は TLB Enjoy Developers Advent Calendar 2022 9日目の記事です。
概要
前回に引き続き、2Dバーチャルオフィスっぽいツールを作っていきます。
JavaとgRPCで2Dバーチャルオフィスっぽい何かを作る ①概要編
今回の目的
今回はgRPCの双方向通信を用いてプレイヤー同士が位置を共有する方法について書きます。
調べたところ同期方式にもいくつかの選択肢があるようですが、今回は非同期でクライアントからサーバーに位置を送信するような方式を選択しました。多人数が接続する上に対戦する訳ではないので、厳密な同期は不要と判断したからです。
また同期間隔について、多人数参加型のオンラインゲームなどでは1フレーム事に同期を取っているわけではなく、数フレームごとに同期を取ることでわずかなラグを許容しているようです。したがって今回は60FPSで10フレームごとにクライアントからサーバーに同期するような実装を目指します。
以下のサイトが参考になりました。
試しに作ったもの
未完成ですが、ソースコードは以下になります。
https://github.com/kdr250/grpc-2d-sample
gRPC Spring Boot Starter
gRPC Spring Boot Starterを使用しました。
チュートリアルに従って、インターフェース定義用のプロジェクト、サーバープロジェクト、クライアントプロジェクトのようにマルチプロジェクト化しています。
protobufによるインターフェースの定義
gRPCのインターフェース定義はprotobufで行います。
generateProto
タスクを実行することでjavaファイルが自動生成されます。
Player.proto
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.example.shared";
option java_outer_classname = "PlayerProto";
service Player {
rpc Initialize (GrpcPlayer) returns (AddEvent);
rpc Sync (stream PlayerSyncRequest) returns (stream PlayerSyncResponse);
}
message PlayerSyncRequest {
GrpcPlayer player = 1;
repeated string otherPlayerIdList = 2;
}
message PlayerSyncResponse {
oneof event {
AddEvent addEvent = 1;
MoveEvent moveEvent = 2;
}
}
message AddEvent {
GrpcPlayer otherPlayer = 1;
repeated GrpcImageType imageType = 2;
}
message MoveEvent {
GrpcPlayer otherPlayer = 1;
}
message GrpcPlayer {
string id = 1;
string name = 2;
GrpcLocation location = 3;
}
message GrpcLocation {
int32 x = 1;
int32 y = 2;
}
message GrpcImageType {
string name = 1;
string base64Image = 2;
}
gRPC bidirectional streamingについて
bidirectional streamingで永続的にstreamを維持する方法がわからず苦戦したのですが、クライアント側で以下の手順を踏む事で解決しました。
- 最初にクライアント側のスタブでリクエストする。
- スタブの戻り値のStreamObserverを変数化して保持しておく。
- 以降、2で保持したStreamObserverを使ってサーバー側に送信する。
以下のサイトが参考になりました。
感想
gRPC難しい...!