imprementation of realtime_networkgame

63
リリリリリリリリリリリリリリリ 2016.5.28 リリリリ Aiming リリ リリ

Upload: satoshi-yamafuji

Post on 21-Apr-2017

15.002 views

Category:

Engineering


0 download

TRANSCRIPT

Page 1: Imprementation of realtime_networkgame

リアルタイム通信ゲームの実装例

2016.5.28株式会社 Aiming

山藤 智之

Page 2: Imprementation of realtime_networkgame

自己紹介

• 山藤 智之• 株式会社 Aiming 所属• 携わった作品– Blade Chronicle–剣と魔法のログレス( PC )–剣と魔法のログレス いにしえの女神

• 猫大好き!猫アレルギー系エンジニア

Page 3: Imprementation of realtime_networkgame

Aiming ?

• オンラインゲームの会社

• 各職人材募集中です!!

Page 4: Imprementation of realtime_networkgame

今日の内容

• オンラインゲームを作るのに必要になる通信技術のお話–通信規格–通信内容–通信ライブラリ• RPC ( Remote Procedure Call )• IDL ( Interface Definition Language )

Page 5: Imprementation of realtime_networkgame

通信規格

• UDP–単方向通信• 届いたか?までは責任持たない• 保証しようとすると TCP と同じ処理をアプリケー

ション側で適切に実装しないといけない

• TCP–双方向通信• 配送が順序通り届いた事を保証してくれる• UDP に比べて速度は遅い• UDP に比べて通信量は多い

Page 6: Imprementation of realtime_networkgame

常時接続 / 非常時接続

• 非常時接続(単方向通信)• リクエストに対してリプライ

• クライアントからの要求が必須

Page 7: Imprementation of realtime_networkgame

常時接続 / 非常時接続

• 常時接続(双方向通信)• リクエストに対してリプライ• サーバ側からのプッシュが可能

• クライアントに要求できる

Page 8: Imprementation of realtime_networkgame

通信内容

• キーフレーム形式– フレーム毎のコントローラーからの入力を交換し合う– 対戦格闘ゲームとかで有効

• メッセージ形式– 手続き呼び出しをメッセージ化して、必要なパラメー

タをやり取りする– MMO とかで有効– 手続き毎にコードを記述するので大規模向き

• ゲーム、プロジェクト規模に合わせて適切な企画・形式を選ぶ

Page 9: Imprementation of realtime_networkgame

メッセージ / キーフレーム

• 通信量の少なさ–キーフレーム > メッセージ

• 処理の簡単さ(高速)–キーフレーム > メッセージ

• 拡張の容易さ–メッセージ > キーフレーム

• 複雑なデータのやり取りのし易さ–メッセージ > キーフレーム

Page 10: Imprementation of realtime_networkgame

通信ライブラリ

• 作った理由–社内共通ライブラリにしたかった• アプリケーションの移植性向上

–当時はあまりちゃんとした物がなかった–送信・受信時のメモリ管理をキチンとやりた

かった–自社製品のそれまでのノウハウを活かした

かった

Page 11: Imprementation of realtime_networkgame

通信ライブラリ

• 要件–とりあえずメッセージベースの物–アプリケーションが変わっても、あまり変わ

らないネットワーク処理部分を分離させた物–クライアント端末が変わっても大丈夫な様に• PC• スマホ• 家庭用ゲーム機

Page 12: Imprementation of realtime_networkgame

通信ライブラリ

• 主にこの部分

Page 13: Imprementation of realtime_networkgame

通信ライブラリ

• 主にこの部分

Page 14: Imprementation of realtime_networkgame

通信ライブラリ

• 主にこの部分

WindowsWinSock

LinuxUnix Socket

MacBSD Socket

ソケットをラップした何か通信関数

ゲームアプリケーション

Page 15: Imprementation of realtime_networkgame

通信ライブラリ

• 主にこの部分

WindowsWinSock

LinuxUnix Socket

MacBSD Socket

ソケットをラップした何か通信関数

ゲームアプリケーション

Page 16: Imprementation of realtime_networkgame

通信ライブラリ

• メッセージ形式を利用したライブラリ–ソケット部分のコード• 幾つかのプラットフォームをサポート• 異なるプラットフォーム間での通信の提供

– RPC ( Remote Procedure Call )の提供• プログラム記述難易度の低減• 通信プロトコルの隠蔽

– HTTP / ゲーム専用プロトコル

– IDL ( Interface Definition Language )の提供

Page 17: Imprementation of realtime_networkgame

RPC (Remote Procedure Call)

• プログラムから別のマシンで動いているサブルーチン(手続き)を呼び出す仕組み

• 通常はネットワーク越しに、通信対象のマシンで動作するプログラムの手続きを呼び出す

Page 18: Imprementation of realtime_networkgame

RPC (Remote Procedure Call)

Network.function_call(“ デザイナー” ); void function_call(std::string types){ printf(“%s 募集中 \n” , types);}こんな感じに書くと

コレが実行される

Page 19: Imprementation of realtime_networkgame

RPC を実現するために

• RPC を実現する為に必要になりそうな情報–どの関数を呼ぶ?–各関数に渡さないといけない引数リスト

• 呼び出し順序の保障• 適切な単位(メッセージ毎)の受信–引数が途中で途切れたりしない様に

• バイトオーダーの吸収–リトルエンディアン / ビッグエンディアン

Page 20: Imprementation of realtime_networkgame

リトルエンディアン8bit

16bit

• バイトオーダーに影響を受けない数値の受け渡し方

ビッグエンディアン

データの表現

0A 0B 0C 0D

0A0B 0C0D

0D 0C 0B 0A

0C0D 0A0B

8bit

16bit整数値 0x 0A0B0C0D

Page 21: Imprementation of realtime_networkgame

データの表現

• バイトオーダーが異なる2台で通信すると…?

0D 0C 0B 0A 整数値 0x 0A0B0C0D

リトルエンディアン 8 Bit

リトルエンディアン 16 Bit

0D0C 0B0A 整数値 0x 0B0A0D0C

メモリのイメージをそのまま移すと

Page 22: Imprementation of realtime_networkgame

データの表現

• バイトオーダーが異なる2台で通信すると…?

0D 0C 0B 0A

リトルエンディアン 8 Bit

リトルエンディアン 16 Bit

0D0C 0B0A

メモリのイメージをそのまま移すと表現したい数値が変わってしまう!!

整数値 0x 0A0B0C0D

整数値 0x 0B0A0D0C

Page 23: Imprementation of realtime_networkgame

• バイトオーダーに影響を受けない数値の受け渡し方

データの表現

0D 0C 0B 0A 送りたい数値リトルエンディアン 8 Bit

リトルエンディアン 16 Bit

Page 24: Imprementation of realtime_networkgame

• バイトオーダーに影響を受けない数値の受け渡し方

データの表現

0A 0B 0C 0D

0D 0C 0B 0A 送りたい数値

ネットワーク経路上一度整列

リトルエンディアン 8 Bit

リトルエンディアン 16 Bit

Page 25: Imprementation of realtime_networkgame

• バイトオーダーに影響を受けない数値の受け渡し方

データの表現

0A 0B 0C 0D

0D 0C 0B 0A

0C0D 0A0B

送りたい数値

受信側受け取り時

リトルエンディアン 8 Bit

リトルエンディアン 16 Bit

ネットワーク経路上一度整列

Page 26: Imprementation of realtime_networkgame

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

整数値 0x 00000A0B

データの表現

0A 0B 00 00

Page 27: Imprementation of realtime_networkgame

整数値 0x 00000A0B

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

データの表現

0A 0B 00 00

0000_0000 0000_0000 0000_1010 0000_1011

Page 28: Imprementation of realtime_networkgame

整数値 0x 00000A0B

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

データの表現

0A 0B 00 00

0000_0000 0000_0000 0000_1010 0000_1011

011000_0101

後続するデータがあるので最上位は1

Page 29: Imprementation of realtime_networkgame

整数値 0x 00000A0B

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

データの表現

0A 0B 00 00

0000_0000 0000_0000 0000_1010 0000_1011

0101100_00101000_0101

後続するデータがあるので最上位は1

Page 30: Imprementation of realtime_networkgame

整数値 0x 00000A0B

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

データの表現

0A 0B 00 00

0000_0000 0000_0000 0000_1010 0000_1011

0100_00001100_0010 00001000_0101

後続するデータが無いので最上位は0

Page 31: Imprementation of realtime_networkgame

整数値 0x 00000A0B

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

データの表現

0A 0B 00 00

0000_0000 0000_0000 0000_1010 0000_1011

0100_00001100_0010 0000_00001000_0101 0000_0---

Page 32: Imprementation of realtime_networkgame

整数値 0x 00000A0B

• 数値をやり取りする際の容量軽減–小さい数値のやり取りが多い場合有効

データの表現

0A 0B 00 00

0000_0000 0000_0000 0000_1010 0000_1011

0000_00000100_00001100_0010 0000_00001000_0101

この2 Byte は送らなくても良くなる

Page 33: Imprementation of realtime_networkgame

パケット構造

• RPC を実現する為に必要になる通信内容– プロトコルバージョン番号

• エンコード、デコード形式– 制御用カウンタ

• 何回目の通信なのか?– どの関数を呼ぶ?

• 関数番号( ID )– 関数に合わせた引数

• 関数毎に引数の数、型、長さが変わる– 1手続きを実行するのに必要なデータ総容量

• 正しく受信するために必要

Page 34: Imprementation of realtime_networkgame

圧縮有

圧縮無

パケット構造

Version(1Byte )

PacketNumber(4 Byte ) PayloadSize(4 Byte )

Compress(1 Byte ) Size(4 Byte ) CompressData( Size )

MessageID(4 Byte ) ParametersCompress(1 Byte )

MessageID(4 Byte ) Parameters

Payload( PayloadSize )

Page 35: Imprementation of realtime_networkgame

レイヤー構造による処理

• 送信 / 受信で処理する順にレイヤを構築

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

Page 36: Imprementation of realtime_networkgame

レイヤー構造による処理

• 各レイヤーで行う処理

必要に応じて圧縮メッセージデータをバイナリストリーム化

ネットワークに流す形式に変換

送信

受信

ネットワークから流れてきたデータの変換必要に応じて伸長バイナリストリームのメッセージ化

OS 低レベル API による送受信処理

Page 37: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

MessageID(4Byte ) Parameters

Page 38: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

MessageID(4Byte ) ParametersCompress(1 Byte )

Page 39: Imprementation of realtime_networkgame

データの流れ

Version(1Byte )

PacketNumber(4 Byte )PayloadSize(4

Byte )MessageID(4

Byte ) ParametersCompress(1 Byte )

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

Page 40: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

Version(1Byte )

PacketNumber(4 Byte )PayloadSize(4

Byte )MessageID(4

Byte ) ParametersCompress(1 Byte )

Page 41: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

Version(1Byte )

PacketNumber(4 Byte )PayloadSize(4

Byte )MessageID(4

Byte ) ParametersCompress(1 Byte )

Page 42: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

MessageID(4Byte ) ParametersCompress(1 Byte )

Page 43: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

MessageID(4Byte ) Parameters

Page 44: Imprementation of realtime_networkgame

データの流れ

Compressorデータの圧縮

SplitToContractメッセージ一つを処理

Packetize送受信ヘッダー処理

送信

受信

関数呼び出し

MessageID(4Byte ) Parameters

Page 45: Imprementation of realtime_networkgame

実装における問題点

• 通信内容を一つ追加する都度、追加・修正しないといけないコード量が多い–送信関数–受信関数–受信関数の呼び分け部分( Dispatcher)

• これを簡易化する為に IDL ( Interface Definition Language )を用意する

Page 46: Imprementation of realtime_networkgame

IDL• IDL ( Interface Definition

Language )–通信に載るメッセージ( RPC )の内容定義

を抽象化した物–マルチプラットフォーム向けに変数定義

(型)も抽象化する• この定義を元に、最低限必要になるコー

ドを自動生成する仕組み• 定義ファイルとコードジェネレータ

Page 47: Imprementation of realtime_networkgame

IDL• 送信側–受信側が識別可能な一意なメッセージ ID–メッセージ毎のパラメータ格納

• 受信側–どのメッセージを受信したのか?(適切な手続

きの呼び出し)–メッセージ毎のパラメータ展開

• 受信後の処理はアプリケーションレベルの実装

Page 48: Imprementation of realtime_networkgame

IDLサーバ用 受信 クライアント用 送信

メッセージ定義---------------------WANTED string Occupation---------------------

Generator

Page 49: Imprementation of realtime_networkgame

IDL

メッセージ定義---------------------WANTED string Occupation---------------------

C++

C

C++

C

サーバ用 受信 クライアント用 送信

Generator

Page 50: Imprementation of realtime_networkgame

メッセージ定義---------------------WANTED string Occupation---------------------

Generator

IDL

C++

C

C++

void send_WANTED(std::string Occupation);

C

void send_WANTED(char* Occupation);

サーバ用 受信

void WANTED(char* Occupation);

クライアント用 送信

void recv_WANTED(std::string Occupation);

Page 51: Imprementation of realtime_networkgame

IDL• ゲーム内の操作は、こんな感じでメッ

セージ化されています。( MMO の場合)MessageID Message 概要 パラメータ

1 CHAT_SEND チャットに発言 Msg :発言内容2 CHAR_MOVE 移動 X : X 座標

Y : Y 座標3 CHAT_SHOW 誰かがチャットし

たWho :誰が?Msg :何て言った?

4 ATTACK キャラクターが攻撃

Target ;誰に攻撃する?How :どうやって攻撃する?

Page 52: Imprementation of realtime_networkgame

IDL• 実際の例

<?xml version=“1.0” encoding=“utf-8” standalone=“yes” ?><contract name=“GameMessage”>

<party primary=“Client” secondary=“Server” />

<call name=“CHAT_SEND”> <parameter name=“Msg” type=“String” /> </call>

<typedefine name=“id” type=“int” /> <receive name=“CHAT_SHOW”> <parameter name=“Who” type=“id” /> <parameter name=“Msg” type=“String” /> </receive>

</contract>

Page 53: Imprementation of realtime_networkgame

<?xml version=“1.0” encoding=“utf-8” standalone=“yes” ?><contract name=“GameMessage”>

<party primary=“Client” secondary=“Server” />

<call name=“CHAT_SEND”> <parameter name=“Msg” type=“String” /> </call>

<typedefine name=“id” type=“int” /> <receive name=“CHAT_SHOW”> <parameter name=“Who” type=“id” /> <parameter name=“Msg” type=“String” /> </receive>

</contract>

IDL• 実際の例このメッセージ群(通信定義)の名前

Page 54: Imprementation of realtime_networkgame

<?xml version=“1.0” encoding=“utf-8” standalone=“yes” ?><contract name=“GameMessage”>

<party primary=“Client” secondary=“Server” />

<call name=“CHAT_SEND”> <parameter name=“Msg” type=“String” /> </call>

<typedefine name=“id” type=“int” /> <receive name=“CHAT_SHOW”> <parameter name=“Who” type=“id” /> <parameter name=“Msg” type=“String” /> </receive>

</contract>

IDL• 実際の例 以下に記すメッセージ定義の方向指定、このファイルの場合 “ Client to Server”

このメッセージ群(通信定義)の名前

Page 55: Imprementation of realtime_networkgame

<?xml version=“1.0” encoding=“utf-8” standalone=“yes” ?><contract name=“GameMessage”>

<party primary=“Client” secondary=“Server” />

<call name=“CHAT_SEND”> <parameter name=“Msg” type=“String” /> </call>

<typedefine name=“id” type=“int” /> <receive name=“CHAT_SHOW”> <parameter name=“Who” type=“id” /> <parameter name=“Msg” type=“String” /> </receive>

</contract>

IDL• 実際の例 以下に記すメッセージ定義の方向指定、このファイルの場合 “ Client to Server”

このメッセージ群(通信定義)の名前

call なので Client -> Server

Server -> Client

Page 56: Imprementation of realtime_networkgame

• クライアント

typedef id int;

class GameMessageReceiver {public: void dispatch(network_data& data) { function_number = data.pop<int>(); switch (function_number) { case 789012: id Who = data.pop<id>(); std::string Msg = data.pop<std::string>(); CHAT_SHOW(Who, Msg); break; } }

virtual void CHAT_SHOW(id Who, std::string Msg) = 0;};

IDL

class GameMessageSender {public: void CHAT_SEND(std::string Msg) { push_args<int>(123456); // function number push_args<std::string>(Msg); }};

Page 57: Imprementation of realtime_networkgame

• サーバー<Client_RPC.h>

typedef id int;

class GameMessageReceiver {public: void dispatch(network_data& data) { function_number = data.pop<int>(); switch (function_number) { case 123456: std::string Msg = data.pop<std::string>(); CHAT_SEND(Msg); } } virtual void CHAT_SEND(std::string Msg) = 0;};

IDL

class GameMessageSender {public: void CHAT_SHOW(id Who, std::string Msg) { push_args<int>(789012); // function number push_args<id>(Who); push_args<std::string>(Msg); }};

Page 58: Imprementation of realtime_networkgame

IDLGameMessageSender::CHAT_SEND(“ 募集中” );

Page 59: Imprementation of realtime_networkgame

IDL

GameMessageReceiver::CHAT_SEND(std::string){ GameMessageSender::CHAT_SHOW(user_id, string);}

Page 60: Imprementation of realtime_networkgame

IDL

GameMessageReceiver::CHAT_SHOW(id Who, std::string){ std::string name = get_charname(Who); Display(“%s %s”, name, string);}

エンジニア 募集中 エンジニア 募集中

Page 61: Imprementation of realtime_networkgame

まとめ

• 通信方式( TCP/UDP/ 常時 / 非常時)• 通信内容(メッセージ / キーフレーム)• 通信ライブラリ– RPC• データの表現方法(バイトオーダー)• メッセージの表現(パケット構造)• メッセージを処理するレイヤー構造

– IDL• IDL の例

Page 62: Imprementation of realtime_networkgame

御清聴ありがとうございました

Page 63: Imprementation of realtime_networkgame

質疑応答