← 戻る

GameLiftのGo用Server SDKを作ってみたい(1)

2020-02-11

AWS に GameLift というサービスがある。これを使うとゲームサーバ(\*)を勝手にスケールしてくれるなどいい感じにマネージしてくれるらしい。

(\*GameLift がいうところのゲームサーバは、いわゆる API サーバではなく、いわゆる Dedicated Game Server の方。Ark やマインクラフトなどマルチプレイ用サーバを自分で立てられるゲームで遊んだことがある人ならイメージしやすいと思う)

GameLift を使うには、GameLift Server SDK を組み込んだゲームサーバを作って、GameLift にアップロードする必要がある。GameLift Server SDK というのは、GameLift がゲームサーバを管理するための各種処理をやるやつで、公式には C++と C#の SDK が提供されている。あと非公式な port として[Node.js 版](https://github.com/dplusic/GameLift-Nodejs-ServerSDK)がある。[前回の記事](https://www.neguse.dev/post/189553676723/elm%E3%81%A8go%E3%81%A7web%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E7%94%A8%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B7%E3%83%A5%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%9F%E8%A9%B1)の通り Go でサーバもクライアントも書きたい気分なので、今回は Go 用の Server SDK を作ってみたい。

Server SDK の内容は、おおまかには以下の通り。

- ローカルホストに起動している GameLift 管理サーバに対して、Socket.IO の接続を 2 本確立する。1 本は受信用、もう 1 本は送信用として利用する()
- ゲームサーバの準備ができたタイミングで GameLift に通知すると、GameLift からセッション作成要求が来た際にコールバックが呼ばれる
- 接続を確立したあとは定期的に死活チェックを行う
- 通信されるメッセージは Protocol Buffers でシリアライズを行う

ということは、Socket.IO と Protocol Buffers がなんとかなればよさそう。

幸い Go には Socket.IO のクライアントを実装した[gomasio](https://github.com/orisano/gomasio)というライブラリがあって、Protocol Buffers は Google がサポートしているライブラリがあり、Protocol の定義ファイルは[前述の Node.js 版の著者が公開してる](https://github.com/dplusic/GameLift-Server-Protobuf)しで、割と簡単にいけそうな気がする。

[https://gist.github.com/neguse/1881c8d291eebf15a2a3f8c36a8ee4bf](https://gist.github.com/neguse/1881c8d291eebf15a2a3f8c36a8ee4bf)

ということでやってみている途中のコードがこちらになります。

とりあえずテスト用のローカルで起動する GameLiftLocal というサーバに接続して、StartGameSession が呼ばれるところまでは確認できた。次に Ack を返さないといけないらしいのだけど、ちょっと方法が見つからなくて、どうすれバイインダーというところで今日は以上です。

- GameLift Server SDK は Process ID や SDK のバージョンを URL のクエリストリングで受け取るようになっているが、現状 gomasio にはクエリストリングを渡す方法が無いため、無理やり RawQuery を上書きしている。
- gomasio はイベント受信時のコールバックに渡される Context 引数経由でイベントを Emit できるようになっているが、ヘルスチェックやサーバの準備ができたことの通知などでコールバック外からイベントを Emit したい。socketio.NewContext()を呼ぶことで無理やり外部から Context を作っているが、これでいいのか感がある。packet 引数に nil 渡すと死ぬし…
- Socket.IO と、それのベースになっている Engine.IO はシンプルそうなので、自前で実装してもよいかもしれない。(だんだん本題からそれていきそう)