Cocos2d-x 版 Client

Cocos2d-x によるクライアント実装例を紹介します。

このチュートリアルでは、Cocos2d-xについての基本的な知識を持つユーザーを前提条件としているため、Cocos2d-xの説明は割愛します。

Cocos2d-x 版クライアントの仕様

Cocos2d-x 版クライアントの大まかな機能は、次のとおりです。

  • ルーム一覧の取得
  • ルームを選択して入室
  • ルームの他の人の発言を見るビューワー(メッセージの購読)
  • ルームに発言
  • ルームから退室

開発環境の説明

MAGELLAN Client SDK for Cocos2d-xを使用するには、以下の環境が必要となります。

サンプルアプリのダウンロード

Cocos2d-xで簡単にお試しいただけるように、サンプルアプリのソースコードを用意しました。サンプルアプリのCocos2d-xのバージョンは、ver.2.2.6を使用しています。

サンプルアプリソースコード一式

サンプルアプリのソースコード一式は、GitHub で公開しています。

接続情報の確認

MAGELLAN にアクセスするためには、 consumer_keyconsumer_secret が必要となります。これらの情報は、magellan-cliproject list で確認できます。

$ magellan-cli project list
+---+----+--------------+-----------+----------+-----------------+----------------------------------+
| * | id | organization |   name    | icon_url |  consumer_key   |         consumer_secret          |
+---+----+--------------+-----------+----------+-----------------+----------------------------------+
| * | 3  | MyOrg        | MyProject |          | MyOrg.MyProject | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+---+----+--------------+-----------+----------+-----------------+----------------------------------+

Total: 1

MAGELLAN Client SDK for Cocos2d-x の導入手順

MAGELLAN Client SDK for Cocos2d-x の導入手順については以下のドキュメントをご参照ください。

接続情報の設定

MAGELLAN にアクセスするための情報を定義します。

サンプルアプリでは、Classes/MagellanSettings.h に定数として定義しています。

#ifndef MagellanChat_MagellanSettings_h
#define MagellanChat_MagellanSettings_h

// HTTP/HTTPS接続先
static const char *HTTP_SERVER_HOST = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

// MQTT接続先
static const char *MQTT_SERVER_HOST = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

// HTTP/HTTPS接続先ポート
static const int HTTP_SERVER_PORT = XXX;

// MQTT接続先ポート
static const int MQTT_SERVER_PORT = XXXX;

// MAGELLANのConsumer Key
static const char *CONSUMER_KEY = "XXXXXXXXXXXXXXXXXXXXX";

// MAGELLANのConsumer Secret
static const char *CONSUMER_SECRET = "XXXXXXXXXXXXXXXXXXXX";

// PubSub KeepAlive設定時間
static const int PUBSUB_KEEP_ALIVE = 30;

// PubSub QoS設定
static const MQTTQos MQTT_QOS = MQTT_QOS_FIRE_AND_FORGET;

// クライアントバージョン
static const char *CLIENT_VERSION = "0.0.1";

#endif

MAGELLAN にアクセスするときのサーバー情報(ホスト・ポート)については、「サーバー情報」を参照して設定してください。また、Consumer key、Consumer Secret については、「接続情報の確認」で確認した接続先を設定してください。

ルーム一覧の取得

Worker で実装したルーム一覧取得 API /rooms を呼び出してルーム一覧を取得します。

ルームの一覧を表示する処理は Classes/RoomListScene.hClasses/RoomListScene.cpp に実装しています。

まずは、Classes/RoomListScene.hを見てみましょう。

MAGELLANシステムを利用するためには、以下のようにインクルード定義でMagellan.hを読み込む必要があります。

#include <Magellan/Magellan.h>

MAGELLANシステムを構成する各種クラスは Magellan名前空間 に定義されています。 また、それに付随して一部 Network名前空間 に定義されています。 usingでMagellanとNetworkを指定します。

using namespace Magellan;
using namespace Network;

次にClasses/RoomListScene.cppを見てみましょう。

MAGELLANシステムの各機能は、MagellanManagerクラスを通じて呼び出されます。
はじめに MagellanManagerインスタンス を取得します。

MagellanManager *mng = MagellanManager::getInstance();

続いて、MagellanSettings.hで定義した接続先のホストポートConsumer KeyConsumer Secretを指定して、MAGELLANのクライアントハンドルを生成します。
クライアントハンドル生成は、MagellanManagerクラスの createClient メソッドで行われます。

クライアントハンドルが生成されると、返り値としてクライアントハンドルが得られます。クライアントハンドルは、後の処理で使うので、メンバ変数に格納しておきます。

// クライアントの作成
mHandle = mng->createClient(HTTP_SERVER_HOST, HTTP_SERVER_PORT, CONSUMER_KEY, CONSUMER_SECRET);

Workerへアクセスする場合、createClient後に必ず1度初期化処理をMagellanManagerクラスinitializeWorker で実行する必要があります。

引数には、クライアントハンドルとアクセスするWorkerのクライアントバージョンを指定します。

// Workerの初期化
mng->initializeWorker(mHandle, CLIENT_VERSION);

それでは、Workerからルームの一覧を取得します。

//RoomList Request
req = new WorkerRequest("rooms", HTTP_METHOD_GET, HTTP_PROTOCOL_VERSION1, BASE_REQUEST_SCHEME_HTTPS);
req->setResponseCallback(getRoomListCallBack);
req->setErrorCallback(getRoomListErrCallback);
mng->sendWorkerRequest(mHandle, req);

while (!req->isDone());

Workerへアクセスする際の手順を解説していきます。

Workerへアクセスするには、WorkerRequestクラスを使用します。 APIのURLディレクトリ部リクエストメソッドプロトコルスキームを指定して、リクエスト用の WorkerRequestインスタンス を作成します。

req = new WorkerRequest("rooms", HTTP_METHOD_GET, HTTP_PROTOCOL_VERSION1, BASE_REQUEST_SCHEME_HTTPS);

WorkerRequestインスタンスには、setResponseCallback メソッドでレスポンス受信時に実行されるコールバックを設定出来ます。

req->setResponseCallback(getRoomListCallBack);
//get roomlist success callback
void  getRoomListCallBack(HTTPRequest *req)
{
  DEBUG_LOG("Succsess Callback:%d\n%s\n", req->getResponseCode(), req->getResponseData());

  Json *listJson = Json_create(req->getResponseData());
  ・
  ・
  ・
}

また、setErrorCallback メソッドでエラーが発生した際のコールバックを設定出来ます。

req->setErrorCallback(getRoomListErrCallback);
//get roomlist error callback
void getRoomListErrCallback(HTTPRequest*req)
{
  DEBUG_LOG("Error Callback:%d\n%s\n", req->getResponseCode(), req->getResponseData());
}

リクエストの送信は、MagellanManagerクラスを通じて行われます。
MagellanManagerクラスの sendWorkerRequest メソッドに、引数としてクライアントハンドルとWorkerRequestインスタンスを指定します。

mng->sendWorkerRequest(mHandle, req);

これで、MAGELLANにアクセスしてルームの一覧が取得できました。

structure

ルーム選択と入室

入室処理はルーム選択時に呼び出されるtableCellTouched メソッドで /rooms/room_id/usersパスに対してルームIDと入室ユーザ名を指定してPOSTメソッドでリクエストを送信して行います。

void RoomListScene::tableCellTouched(CCTableView* table, CCTableViewCell* cell)
{
  ・
  ・
  req = new WorkerRequest(path->m_sString.c_str(), HTTP_METHOD_POST, HTTP_PROTOCOL_VERSION1, BASE_REQUEST_SCHEME_HTTPS);
  req->addPostData("name", userName.c_str());
  req->setResponseCallback(selectRoomCallback);
  req->setErrorCallback(selectRoomErrCallback);
  mng->sendWorkerRequest(mHandle, req);

  while (!req->isDone());

  if(req->getResponseCode() == 200)
  {
    bool result = Json_getItem(Json_create(req->getResponseData()), "result");

    if(result)
    {
      gRoomList.clear();
      CCDirector::sharedDirector()->replaceScene( ChatScene::scene());
    }
    else
    {
      DEBUG_LOG("%d\n%s\n", req->getResponseCode(), req->getResponseData());
    }
  }
  else
  {
    DEBUG_LOG("%d\n%s\n", req->getResponseCode(), req->getResponseData());
  }
}

Workerリクエスト時にはリクエストに必要な情報をQueryパラメータとして付与できます。
そのため、ユーザー名(name)を以下のようにWorkerインスタンスに追加します。

req->addPostData("name", userName.c_str());

その後、Workerリクエストを行います。
reuslttrue の場合は正常に入室できているので、チャット画面(ChatScene)へ遷移します。 (resultfalse のときは、入室失敗となります。)

Subscribe の開始

入室に成功すると、チャット画面に遷移できました。

structure

チャット画面は、Classes/ChatScene.hClasses/ChatScene.cppとなります。

入室したという情報だけでは、実際にそのルームのメッセージを受け取ることはできません。
入室に成功したら、PubSubへの接続処理メッセージの購読(Subscribe)を開始します。 ChatScene.cppを見てみましょう。

PubSubへの接続開始は init メソッドで行っています。

bool ChatScene::init()
{
  ・
  ・
  ・
  //pubsub
  MagellanManager *mng = MagellanManager::getInstance();
  mClientHandle = mng->createClient(MQTT_SERVER_HOST, MQTT_SERVER_PORT, CONSUMER_KEY, CONSUMER_SECRET);

  mng->initializePubSub(mClientHandle, CLIENT_VERSION);

  mng->setPubSubConnectCallback(mClientHandle, pubsub_connect_callback);
  mng->setPubSubDisconnectCallback(mClientHandle, pubsub_disconnect_callback);
  mng->connectPubSub(mClientHandle, PUBSUB_KEEP_ALIVE);

  gSubscriber = new PubSub_Subscriber(topic->m_sString.c_str(), MQTT_QOS);
  gSubscriber->setSubscribeCallback(mqttSubscribeCallback);
  gSubscriber->setRegisterCallback(mqttRegisterSubscribeCallback);
  gSubscriber->setUnregisterCallback(mqttUnregisterSubscribeCallback);

}

PubSubへの接続リクエストを行うにはPubSub用のクライアントハンドルが必要になります。
PubSub用のクライアントハンドル生成は、MagellanManagerクラスの createClient メソッドで行われます。

引数には、MagellanSettings.hで定義したMQTT_SERVER_HOSTMQTT_SERVER_PORTを指定します。 返り値として得たクライアントハンドルをメンバ変数に格納しておきます。

mClientHandle = mng->createClient(MQTT_SERVER_HOST, MQTT_SERVER_PORT, CONSUMER_KEY, CONSUMER_SECRET);

PubSubを使用する場合、createClient後に必ず1度初期化処理をMagellanManagerクラスinitializePubSub で実行する必要があります。

引数には、クライアントハンドル、MagellanSettings.hで定義したCLIENT_VERSIONを指定します。

mng->initializePubSub(mClientHandle, CLIENT_VERSION);

PubSub接続リクエストを行う際、接続コールバックと切断コールバックを指定します。
接続コールバックはMagellanManagerクラスsetPubSubConnectCallback に設定します。

サンプルアプリではpubsub_connect_callback()をコールバックとして登録しています。

mng->setPubSubConnectCallback(mClientHandle, pubsub_connect_callback);

切断コールバックはMagellanManagerクラスsetPubSubDisconnectCallback に設定します。

サンプルアプリではpubsub_disconnect_callback()をコールバックとして登録しています。

mng->setPubSubDisconnectCallback(mClientHandle, pubsub_disconnect_callback);

コールバック設定後、MagellanManagerクラスconnectPubSub メソッドでPubSubへの接続を開始します。

引数には、クライアントハンドル、MagellanSettings.hで定義したPUBSUB_KEEP_ALIVEを指定します。

mng->connectPubSub(mClientHandle, PUBSUB_KEEP_ALIVE);

PubSubへの接続は、connectPubSub 実行後に先ほど設定した接続コールバックが呼び出されたタイミングで完了となります。

次にメッセージの購読(Subscribe)を行います。 メッセージの購読(Subscribe)は前述のPubSub接続確立後に実行します。

メッセージの購読(Subscribe)を行う為にまず、TopicQoSを指定して PubSub_Subscriberインスタンス を生成します。

サンプルアプリではTopicroomes/room名/meを、QoSMagellanSettings.hで定義した MQTT_QOS を設定しています。

CCString *topic = CCString::createWithFormat("rooms/%s/me", roomName.c_str());
gSubscriber = new PubSub_Subscriber(topic->m_sString.c_str(), MQTT_QOS);

生成したPubSub_Subscriberインスタンス(gSubscriber)は後述のSubscribeの登録で引数として使用します。 続いて、下記3つのコールバック設定を行います。

  • Subscribe登録完了
  • Subscribe登録解除完了
  • Subscribeメッセージ受信

Subscribe登録完了コールバックはMagellanManagerクラスsetRegisterCallback メソッドを使って設定します。

Subscribe登録解除完了コールバックはMagellanManagerクラスsetUnregisterCallback メソッドを使って設定します。

Subscribeメッセージ受信コールバックはMagellanManagerクラスsetSubscribeCallback メソッドを使って設定します。

gSubscriber->setSubscribeCallback(mqttSubscribeCallback);
gSubscriber->setRegisterCallback(mqttRegisterSubscribeCallback);
gSubscriber->setUnregisterCallback(mqttUnregisterSubscribeCallback);

Subscriber登録はPubSub接続コールバック時(pubsub_connect_callback())に、 コールバック第1引数MagellanHostクラスregisterPubSub_Subscriberを通じて行います。

引数には先ほど生成したPubSub_Subscriberインスタンス(gSubscriber)を指定します。

//pubsub connect callback
void pubsub_connect_callback(MagellanHost* host, MQTTConnectStatus status)
{
  DEBUG_LOG("connect:%s,%d\n",host->getHostName(), status);

  host->registerPubSub_Subscriber(gSubscriber);
}

これで、Subscriberの登録が行われ、メッセージが受信出来るようになりました。

ルームに発言する

Subscribeが開始されたので、次はルームに発言(publish)してみましょう。
ルームへの発言はChatScene.cpp内の sendで行っています。

void ChatScene::send(CCObject* pSender)
{
  if(strlen(inputBox->getText()) > 0)
  {
    ・
    ・
    ・
    CCString *sendMessage = CCString::createWithFormat("{ \"user\":\"%s\", \"message\":\"%s\", \"date\":\"%s\" }", userName.c_str(), inputBox->getText(), nowTime.str().c_str());
    CCString *topic = CCString::createWithFormat("rooms/%s/me", roomName.c_str());

    MagellanManager *mng = MagellanManager::getInstance();
    PubSub_PublishRequest *req = new PubSub_PublishRequest(topic->m_sString.c_str(), MQTT_QOS);
    req->setPublishCallback(mqttPublishCallback);
    req->setPublishData(sendMessage->m_sString.c_str(), sendMessage->length());
    mng->sendPubSub_PublishRequest(mClientHandle, req);

    inputBox->setText("");
  }
}

画面のテキストフォームに入力されたメッセージを取得します。 サンプルアプリでは、ユーザ名、発言時刻も表示するため、JSONデータに変換しています。

メッセージをPublishするには、PubSub_PublishRequestクラス を使用します。 Publish 対象の TopicQoSPublish するメッセージを指定して PubSub_PublishRequestインスタンス を生成します。

PubSub_PublishRequest *req = new PubSub_PublishRequest(topic->m_sString.c_str(), MQTT_QOS);

PubSub_PublishRequestインスタンスには、送信完了時に実行されるコールバックを登録することができます。

req->setPublishCallback(mqttPublishCallback);

PubSub_PublishRequestインスタンスに、Publishするデータを設定します。 setPublishData に、送信するメッセージを渡します。

req->setPublishData(sendMessage->m_sString.c_str(), sendMessage->length());

Publishの送信は、MagellanManager クラスを通じて行われます。 SendPubSub_PublishRequest メソッドに、PubSub_PublishRequestインスタンスを指定し、リクエストを送信します。

mng->sendPubSub_PublishRequest(mClientHandle, req);

テキストフォームにメッセージを入力して、送信してみましょう。 structure

ルームから退出する

次にルームから退出する処理を見てみましょう。 ルームの退出処理は、ChatScene.cpp内の roomOut メソッドで行っています。

void ChatScene::roomOut(CCObject* pSender)
{
  MagellanManager *mng = MagellanManager::getInstance();
  mHandle = mng->createClient(HTTP_SERVER_HOST, HTTP_SERVER_PORT, CONSUMER_KEY, CONSUMER_SECRET);
  mng->initializeWorker(mHandle, CLIENT_VERSION);

  //get select room info
  CCUserDefault *data = CCUserDefault::sharedUserDefault();
  string roomID = data->getStringForKey("roomID");
  string userName = data->getStringForKey("userName");

  //select room info
  CCString *path = CCString::createWithFormat("rooms/%s/users/%s", roomID.c_str(), userName.c_str());
  DEBUG_LOG("Path:%s\n", path->m_sString.c_str());

  //worker request
  req = new WorkerRequest(path->m_sString.c_str(), HTTP_METHOD_DELETE, HTTP_PROTOCOL_VERSION1, BASE_REQUEST_SCHEME_HTTPS);
  req->addPostData("name", userName.c_str());
  req->setResponseCallback(roomOutCallback);
  req->setErrorCallback(roomOutErrCallback);
  mng->sendWorkerRequest(mHandle, req);

  while (!req->isDone());

  if(req->getResponseCode() == 200)
  {
    bool result = Json_getItem(Json_create(req->getResponseData()), "result");

    if(result)
    {
      gMessageList.clear();
      CCDirector::sharedDirector()->replaceScene( RoomListScene::scene());
    }
  }
  else
  {
    DEBUG_LOG("%d\n%s\n", req->getResponseCode(), req->getResponseData());
  }
}

退出処理では、必要なパラメータ(name)をDELETEで送信します。 レスポンスをチェックし、resultがtrueの場合は、ルーム一覧(RoomListScene.cpp)へ遷移します。

これで、ルームから退室出来るようになりました。

以上で、Cocos2d-x 版クライアントの完成です。