MRS Room 解説

本文書はmrs_roomが提供する関数やコマンドの概要を説明します。
各OSにおける開発環境の設定方法,
関数リファレンス も合わせて参照してください。

また、MRSベースのサーバに任意のレコードを送受信するためのコマンドラインツールである mrs_pusher については
PUSHER_Overview.html を参照してください。

MRS Room 概要

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の動作確認方法

MRS Roomの配布物にはビルド済みのサンプルプログラムが含まれます。
実行形式ファイルが格納されているディレクトリの位置はLinux CentOS7であれば、 sample/cpp/linux/centos7_4.8.5 などです。

ディレクトリには room_server, room_client, room_client_owner などが含まれています。

それぞれのソースコードは、同じ名前で sample/cpp/source/に格納されています。

動作確認は、以下の手順で行います。

  1. room_serverを引数無しで起動する。
  2. room_client_owner を別画面で引数無しで起動する。
  3. room_client をさらに別画面で引数無しで起動する。
  4. Ctrl-Cですべてを停止する。

上記でエラーが表示されなければ動作確認は終了です。

まず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

MRS RoomクライアントAPIは、Roomのクライアントを作るためのAPIです。
Roomのクライアントとは、通常はゲームのクライアントプログラムです。

RoomクライアントAPIの使用方法について、
実際に動作するプログラムの全体が、sample/cpp/source/room_client.cpp にあります。
これは room_client プログラムのソースです。

以下の説明のコード例は、コードの抜粋なので、そのままでは動作しないことに注意してください。

Roomクライアントを実装するには、次の手順が必要です。

  1. ヘッダファイルをincludeする
  2. MRSで共通に必要な設定を行う
  3. コールバック関数の中身を定義する
  4. その他の必要な関数をアプリケーションから呼び出す

1.必要なヘッダファイルをincludeする

クライアントで必要なのはひとつだけです。

#include <client/mrs/room.hpp>

2. MRSで共通に必要な設定を行う

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_UDPMRS_CONNETION_TYPE_WSMRS_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ごとに異なる関数を設定することはできず、
全体でひとつしか設定できません(将来変わる可能性があります)。

3. コールバック関数の中身を定義する

Roomクライアントではいくつものコールバックが続けて呼び出されますが、
それぞれのコールバック関数には呼び出し順序の関係があります。

サンプルの room_client.cpp では暗号化通信を使用しているので、
次の順序でコールバックが呼ばれ、処理が進行します。

  1. mrs_connect を呼ぶ
  2. 接続が完了すると、on_connect が呼ばれる。 on_connect 関数の中では、 mrs_key_exchange を呼び鍵交換を開始する。
  3. 鍵交換が完了するとon_key_exchange が呼ばれる。その中でRoomクライアントAPIの mrs_room_client_join 関数を呼びだし、ルームに参加する。
  4. ルームにおいてレコード受信以外の事象が起きたときはon_room_event が呼ばれて通知される。
  5. ルームにおいてレコードを受信したときは 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関数を用いて送信したデータを受信することができます。

4. その他の必要な関数を呼び出す

ルームに参加して必要なコールバックの設定をした後は、 mrs_room_client_multicast など各種の送信関数を呼び出して、さまざまなパターンのデータ送信を行うことができます。 レコード送信は、 multicast, unicast, broadcastなど数種類の送信モードを選択することができます。

Roomクライアントの全ての関数の詳細については、関数リファレンスを参照してください。

5. C#版でのMRS 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

6. C++版とC#版との違い

Mrsクラス内でmrs.dllをDllImportして使用するため、C++版のROOM APIと使い勝手は変わりません。

ただし言語の違いがあるため、引数にあるポインタに関してはIntPtrに変更されています。


Copyright © 2019 MONOBIT ENGINE Inc.