本文書はmrs_roomが提供する関数やコマンドの概要を説明します。
各OSにおける開発環境の設定方法,
関数リファレンス も合わせて参照してください。
また、MRSベースのサーバに任意のレコードを送受信するためのコマンドラインツールである mrs_pusher については
PUSHER_Overview.html を参照してください。
MRS Roomは、マルチプレイゲームのキャラクター同期やチャットなど、任意のデータをグループ内で互いに送り合うためのサーバーです。
MRS Roomは「ルーム」と呼ぶ論理的なグループを実装しているだけで、実際の通信はMRSを用いて行われるため、MRSがサポートしているTCPとUDP、RUDP,WebSocket(wsまたはwss)を使うことができます。通信に暗号化を使うようにも設定できます。
以下の説明では、MRS Roomのプロセスを「Roomサーバー」と呼びます。
全体の構成は次の図で示されます。
まず1つのOSインスタンスの中で、Roomサーバープロセスは複数個同時に起動することができます。図では、"Room server process 1", "Room server process 2"などがそれにあたります。
複数個を同時に起動する場合は、それぞれが使用するTCP/UDPポート番号は重複させることはできません。 もし同じポート番号を使用するように設定されていた場合は、起動時にエラーになります。
それぞれのRoomサーバープロセスは、複数のルームを提供することができます。
それが図の "Room 1", "Room 2"などにあたります。
それぞれのRoomには、複数のプレイヤーが参加しています。図では、 "Player 1", "Player 2"などが該当します。
それぞれのプレイヤーは、それに対応したクライアントが存在します。図では、 "Client Player 1", "Client Player 2"などが該当します。
クライアントは典型的にはUnityでできているゲームや、スマホで動作しているゲームなどです。
Roomサーバーの仕様として、1つのクライアントプログラムから複数のルームに同時に参加することも可能です。
MRS Roomの配布物にはビルド済みのサンプルプログラムが含まれます。
実行形式ファイルが格納されているディレクトリの位置はLinux CentOS7であれば、 sample/cpp/linux/centos7_4.8.5
などです。
ディレクトリには room_server
, room_client
, room_client_owner
などが含まれています。
それぞれのソースコードは、同じ名前で sample/cpp/source/
に格納されています。
動作確認は、以下の手順で行います。
room_server
を引数無しで起動する。room_client_owner
を別画面で引数無しで起動する。room_client
をさらに別画面で引数無しで起動する。上記でエラーが表示されなければ動作確認は終了です。
まず1.のroom_server
を引数無しで起動すると、
端末に次のようなログが表示されます。
sleep_msec=1 server_addr=0.0.0.0 server_port=33333 backlog=10
TCP listening on 0.0.0.0 33333
UDP ready on 0.0.0.0 33333
WS listening on 0.0.0.0 33334
WSS listening on 0.0.0.0 33335
TCPポート33333で通常のTCP、UDPポート33333でUDP、TCPポート33334でWS、TCPポート33335でWSSが待ち受けていることがわかります。
次に、別画面でroom_client_owner
を起動します。
このプログラムは、ルームを作成してルームの所有者になります。
起動すると、次のようなログが端末に出力されます。
connection_type:1 sleep_msec:1 server_addr:127.0.0.1 server_port:33333 timeout_msec:5000
on_fallback_connect connection_type=1 addr=127.0.0.1 port=33333 timeout_msec=5000
on_connect
on_key_exchange
CREATE_ROOM room_id=65535 owner_player_id=1 player_id=1
作成したルームはIDが65535, プレイヤーのIDは1で所有者(owner)もIDが1になっていることがわかります。
次にroom_client
を別の端末画面で起動します。
すると次のようなログが出力されます。
connection_type:1 sleep_msec:1 server_addr:127.0.0.1 server_port:33333 timeout_msec:5000
on_fallback_connect connection_type=1 addr=127.0.0.1 port=33333 timeout_msec=5000
on_connect
on_key_exchange
JOIN_ROOM_OTHER_PLAYER room_id=65535 owner_player_id=1 player_id=1
JOIN_ROOM room_id=65535 owner_player_id=1 player_id=2
SET_OWNER_PLAYER room_id=65535 owner_player_id=1
on_room_read_record write_player_id=2 seqnum=4 options=0x00 payload=100/25/broadcast: join player 2
on_room_read_record write_player_id=2 seqnum=5 options=0x00 payload=400/25/multicast: join player 2
SET_OWNER_PLAYER room_id=65535 owner_player_id=2
JOIN_ROOM_OTHER_PLAYER
は、Roomサーバからの通知です。すでにidが1のプレイヤーがidが65535のルームに参加していることがわかります。
JOIN_ROOM
はルームに参加できたことを示しています。
on_room_read_record
はルームからのメッセージを受信したことを意味しています。
broadcastメッセージをまず受信し、次にmulticastメッセージを受信したことがログからわかります。
SET_OWNER_PLAYER
は、ルームの所有者が変更されたことの通知を受け取ったログです。
このログに出ているものも含め、すべての操作について次の項目で説明します。
MRS RoomクライアントAPIは、Roomのクライアントを作るためのAPIです。
Roomのクライアントとは、通常はゲームのクライアントプログラムです。
RoomクライアントAPIの使用方法について、
実際に動作するプログラムの全体が、sample/cpp/source/room_client.cpp
にあります。
これは room_client
プログラムのソースです。
以下の説明のコード例は、コードの抜粋なので、そのままでは動作しないことに注意してください。
Roomクライアントを実装するには、次の手順が必要です。
クライアントで必要なのはひとつだけです。
#include <client/mrs/room.hpp>
main関数では、MRSのエコー通信クライアントのサンプルと同様に、
mrs_initialize
してから mrs_connect
し、
各種のコールバック関数をセットし、 メインループで mrs_update
を呼ぶだけです。
mrs_connect
関数以降の重要な部分を抜粋すると、次のようになります。
MrsConnection tcp_client = mrs_connect( MRS_CONNECTION_TYPE_TCP, arg_server_addr, server_port, timeout_msec );
mrs_set_connect_callback( tcp_client, on_connect);
mrs_set_disconnect_callback(tcp_client,on_disconnect);
mrs_set_error_callback( tcp_client, on_error);
mrs_set_read_record_callback( tcp_client, on_read_record );
mrs_room_client_set_event_callback( on_room_event );
mrs_room_client_set_read_record_callback( on_room_read_record );
mrs_connectの第1引数は MRS_CONNECTION_TYPE_TCP
で、TCPを用いて通信するように設定しています。
TCP以外を使う場合は、 MRS_CONNECTION_TYPE_UDP
や MRS_CONNETION_TYPE_WS
や MRS_CONNETION_TYPE_WSS
を指定してください。
arg_server_addr
は "192.168.1.232"のような文字列、 server_port
はポート番号、
timeout_msec
は接続確立までの最大待ち時間です。
接続完了を検知するコールバック関数として on_connect
関数を、
切断を検知するコールバックとして on_disconnect
関数を、
エラーを検知するコールバックとして on_error
関数を、
レコード受信を検知するコールバックとして on_read_record
関数を設定します。
上記がMRSの動作に必要なコールバック関数で、
その次にRoomクライアントの動作に必要なコールバック関数を設定します。
on_room_event
は、ルームにおいてプレイヤーの入退出があった場合などのイベントを通知するためのコールバック関数です。
on_room_read_record
は、ルームに対して送信されたレコードを受信するためのコールバック関数です。
この2つのコールバック関数は、現在のRoomクライアントAPIでは、
MrsConnectionごとに異なる関数を設定することはできず、
全体でひとつしか設定できません(将来変わる可能性があります)。
Roomクライアントではいくつものコールバックが続けて呼び出されますが、
それぞれのコールバック関数には呼び出し順序の関係があります。
サンプルの room_client.cpp
では暗号化通信を使用しているので、
次の順序でコールバックが呼ばれ、処理が進行します。
mrs_connect
を呼ぶon_connect
が呼ばれる。 on_connect
関数の中では、 mrs_key_exchange
を呼び鍵交換を開始する。on_key_exchange
が呼ばれる。その中でRoomクライアントAPIの mrs_room_client_join
関数を呼びだし、ルームに参加する。on_room_event
が呼ばれて通知される。on_room_read_record
が呼ばれて通知される。暗号化を使わない場合は、 on_key_exchange
の中ではなく、
on_connect
関数の中でmrs_room_client_join
関数を呼びます。
MRSのエコー通信サンプルと異なるのは、 on_key_exchange
コールバック関数の中身です。
on_key_exchange
関数の中では、ルームに参加するためのAPIである、
mrs_room_client_join
関数を呼び出します。
void on_key_exchange( MrsConnection connection, void* connection_data ){
mrs_room_client_join( connection, g_RoomId, g_PlayerId, NULL, 0 );
}
この関数は、Roomクライアントライブラリが提供しているAPIで、
内部でMRSのレコードを一つ送信します。Roomサーバは、このレコードを受信するとすでに存在しているルームを検索し、
指定されたプレイヤーを参加させ、プレイヤーが1人参加したという事象を全員に通知します。
このような事象を検知するためのコールバック関数が on_room_event
関数です。
この関数の内容は以下のようにアプリケーション側で定義します。
void on_room_event( MrsConnection connection, void* connection_data, MrsRoomId room_id, MrsRoomPlayerId owner_player_id, const MrsRoomPlayerId* joined_player_ids, MrsRoomPlayerIdsLen joined_player_ids_len, MrsRoomEvent evt, MrsRoomPlayerId player_id, MrsRoomResult result, const void* data, uint32 data_len ){
switch ( evt ){
case MRS_ROOM_EVENT_JOIN_ROOM:{
......
case MRS_ROOM_EVENT_JOIN_ROOM_ERROR:
......
}
}
MrsConnectionが受信したレコードは、 on_read_record
コールバックですべて捕まえることができます。
room_client
サンプルでは、このコールバックの内容は次のようになっています。
void on_read_record( MrsConnection connection, void* connection_data, uint32 seqnum, uint16 options, uint16 payload_type, const void* payload, uint32 payload_len ){
mrs_room_client_parse_record( connection, connection_data, seqnum, options, payload_type, payload, payload_len );
}
ここでは、 mrs_room_client_parse_record
という、RoomクライアントAPIが提供する関数に、
レコードの内容をすべて渡しています。この関数が、Roomサーバから受信したレコードの内容を解析し、
on_room_event
を呼び出したり、 on_room_read_record
を呼び出したりを切り替えます。
on_room_read_record
コールバック関数は、次のようなプロトタイプになっています。
void on_room_read_record( MrsConnection connection, void* connection_data, MrsRoomId room_id, MrsRoomPlayerId owner_player_id, const MrsRoomPlayerId* joined_player_ids, MrsRoomPlayerIdsLen joined_player_ids_len, MrsRoomPlayerId write_player_id, uint32 seqnum, uint16 options, uint16 payload_type, const void* payload, uint32 payload_len ){
// ここにアプリケーションのデータ処理部分の本体を記述します。
// アプリケーションでは、payload_typeに自由な値を設定して処理を分岐させます。
}
on_room_read_record
コールバック関数の中では、 payload_type
を用いてアプリケーションの処理を切り替えて、ゲームの内容を実装します。 ほかのゲームクライアントが、mrs_room_client_multicast
などのRoomクライアントAPI関数を用いて送信したデータを受信することができます。
ルームに参加して必要なコールバックの設定をした後は、 mrs_room_client_multicast
など各種の送信関数を呼び出して、さまざまなパターンのデータ送信を行うことができます。 レコード送信は、 multicast, unicast, broadcastなど数種類の送信モードを選択することができます。
Roomクライアントの全ての関数の詳細については、関数リファレンスを参照してください。
C#版(フレームワークにつきましては.NET Core 2.0を推奨いたします)のMRS Roomの実装方法についてですが、以下のクラスから派生などをしてサーバーやクライアント用のROOM APIを使用する事ができます。
サーバー側の派生元クラス
public class MrsRoomServer : MrsRoom {
...
}
クライアント側の派生元クラス
public class MrsRoomClient : MrsRoom {
...
}
以下のフォルダに配置されています。
mrs_room\sample\csharp\libmrs\Monobit\mrs_room
三つのファイルが必要となります。
MrsRoom.cs
MrsRoomClient.cs
MrsRoomServer.cs
Mrsクラス内でmrs.dllをDllImportして使用するため、C++版のROOM APIと使い勝手は変わりません。
ただし言語の違いがあるため、引数にあるポインタに関してはIntPtrに変更されています。