提供されているソーシャル機能
スマートフォンアプリ版のMobageで提供されているAPIはCommon APIとLocal APIの2つに大きく分かれています。日本だけでなく中国やグローバルのMobageプラットフォーム上で使える共通のAPIを総称してCommon APIと呼んでいます。
一方のLocal APIは、それぞれの国のソーシャルプラットフォームに特化した機能が提供され、プラットフォーム間で互換性が維持されないAPIです。つまり、X-border(注1)でアプリケーションを展開するにはCommon APIを中心に開発しておいたほうがよいことになります。
DeNAの掲げる戦略の一つで、同一の仕様のプラットフォームを世界に展開し、国を越えてMobage上でデベロッパーが開発したゲームを利用できるようにすることを目指したもの。
以下に、提供されているAPIのモジュールと機能概要を列挙します。
Common API
-
Social.Common.People
ユーザー、友達の情報を取得するAPI
-
Social.Common.Appdata
Key/Valueの永続化データのCRUDを行うAPI
-
Social.Common.Blacklist
ユーザー間のブラックリストをチェックするAPI
-
Social.Common.Profanity
NGワードをチェックするAPI
-
Social.Common.Service
Mobageのコミュニティ機能を持つUIを呼び出すAPI
-
Bank.Debit
モバコインによるアイテム課金を行うAPI
-
Bank.Inventory
Mobageプラットフォームに登録した課金アイテムの情報を取得するAPI
日本のLocal API
-
Social.JP.Avatar
アバターの詳細画像を取得するAPI
-
Social.JP.Service
日記投稿、ミニメール送信、特商法(注2)の表記などをUIに表示するAPI
「特定商取引に関する法律」の略称。
上記以外にも、現在Mobage GlobalのLocal APIとして提供されているLeaderboard API、GameGraph APIやPush Notificationに加え、Activity APIなどのフィーチャーが今後Common APIとして続々と追加されていく予定です。
また、これらのAPIはngCoreのクライアントアプリケーションから直接呼び出し可能なJavaScriptのAPIとして提供されていますが、UIを含まない部分のCommon APIに関しては、同等の機能がRESTful APIとしても提供されており、ゲーム用の対向サーバー(以下、Game Server)からも利用することが可能です。
Sandbox環境への接続設定
Mobageプラットフォームでは、ソーシャル機能のテストのためのSandbox環境を用意しています。Sandboxに接続してソーシャルAPIを動作させるためには、ターゲットとするプラットフォームのデベロッパーサイトからアプリケーションを登録し、その情報をクライアントアプリケーションに設定する必要があります。
今回はせっかくですのでMobage GlobalのSandboxに接続する方法を紹介したいと思います。
アプリケーションの登録
まず、SDKをダウンロードした際に作成したデベロッパーのアカウントを使用して、MobageのDeveloper Portalへログインします。ログイン後、サイト上で「Upgrade Account」を行うと「Apps」タブが表示されるので、その「Apps」タブをクリックしてアプリケーションの登録を行います(図1)。
今回は「MyFirstMobageApp」というApp Keyで新規にアプリケーションを登録してみました。登録したアプリケーションのConsumer Key、Consumer Secretは後のアプリケーションの設定で必要になるため、詳細画面の「Client Applications」で確認してください(図2)。
configuration.jsonの設定
Developer Portal上でアプリケーションの登録が完了したら、ngCoreで開発しているアプリケーションのconfiguration.jsonに登録したアプリケーション情報を設定します。手始めにSDKに付属しているサンプルのSamples/HelloWorld/configuration.jsonをLIST1のように編集します。
{ "appId":"MyFirstMobageApp", "socialConsumerKey": "p2dj3eZCu779QuZ8F3HIw", "socialConsumerSecret": "cjnNqjvGCRZpTvFbRiUDyG1NjqbaBN5Rxx0LZYIc3Uw", "requireMobageLogin": true }
「requireMobageLogin」はMobageへのログインを有効化する設定項目です。ソーシャル機能を動かす時には必ずtrueを設定しておきましょう。
configuration.jsonはbootconfig.xmlでアクセスする最初のアプリケーションに設定されているものが有効になります。例えば、Samples/HelloWorld/configuration.jsonにアプリケーションの設定をしても、Samples/Launcher経由でHelloWorldを実行した場合は、Samples/Launcher/configuration.jsonの設定が有効になるので注意してください。
Regionの切り替え
configuration.jsonの設定が終わったら、次にngCoreのライブラリのRegionの切り替えを行います。今回は、Mobage GlobalのSandbox環境に接続しに行くため、LIST2のコマンドでMobage Globalのソーシャル機能へアクセスする実装に切り替えます。
$ cd SDK $ make toggle-us cd NGCore/Client/ && rm -f Social.js && ln -s Social/US.js Social.js cd NGCore/Client/ && rm -f Bank.js && ln -s Bank/US.js Bank.js
内部的には、SocialとBankモジュールをシンボリックリンクでMobage Global実装に切り替えています。お察しの通り、日本向けのプラットフォームに切り替えるには、「make toggle-jp」を使用します。中国であれば、「maketoggle-cn」です。
たったコマンド1行でターゲットとするプラットフォーム用のライブラリに切り替えることができるので便利です。
テストアカウントの作成とログイン
クライアントのゲーム部分の開発の時と同じように、makeserverでNode.jsを立ち上げ、bootconfig.xmlをSamples/HelloWorldに向けてアプリケーションを立ち上げてみましょう。ランタイムは、「US_LIVE_SAND_SDK」という名前を含むapkファイル、またはappファイルを使用します。
例えば、iPhone Simulatorで実行する場合は、webgame_US_LIVE_SAND_SDK-iPhone-Simulator.app内のbootconfig.xmlを編集して起動します。図3のようなログイン画面が出てくれば成功です。
テストユーザーのアカウントがないため、この画面からテストユーザーを作成してみましょう。ユーザー名とパスワードを入力してSign Upボタンをタップすると、ウィザード形式でテストユーザーを作成できます。作成したテストユーザーでログインするとHelloWorldのアプリケーションが今までと同じように起動します。見た目はクライアントの開発時と何も変わりませんが、Sandbox環境にアクセスするソーシャルAPIが有効になっています。
ClientAPIを使ってユーザー情報を表示してみよう
Sandbox環境への接続に必要な設定が整ったので、早速クライアントアプリケーションからソーシャルAPIを呼び出してみましょう。
Mobageコミュニティボタンを表示する
Mobageのコミュニティボタンを表示するには、LIST3のようにshowCommunityButton関数をコールします。
// Socialモジュールをrequireする var Social = require('../NGCore/Client/Social').Social; // Mobageコミュニティボタンを左上に表示 Social.Common.Service.showCommunityButton( UI.ViewGeometry.Gravity.TopLeft, "default");
第1引数に配置位置の定数であるUIモジュールの定数「TopLeft」を指定し、第2引数のThemeには「default」を指定します。表示されたコミュニティボタン(図4)をタップするとMobage GlobalのコミュニティのUIが立ち上がり、他のテストユーザーに友達リクエストを送信したり、ゲームにテストユーザーを招待したりするなど、本番と同等のコミュニティ機能を利用できます。
ユーザー情報を取得して表示する
次に、ログインしているユーザーのプロフィール情報をデータとして取得してみましょう。
LIST4のように、getCurrentUser関数をコールしてログインしたユーザーのプロフィール情報を取得し、UIモジュールを使って表示してみます。
var Core = require('../NGCore/Client/Core').Core; var UI = require('../NGCore/Client/UI').UI; var Social = require('../NGCore/Client/Social').Social; function main(){ // Mobageコミュニティボタンを表示 Social.Common.Service.showCommunityButton( UI.ViewGeometry.Gravity.TopLeft, "default"); // ユーザー情報を取得して表示 Social.Common.People.getCurrentUser( ["nickname", "thumbnailUrl"], function(error, user){ if(error){ // エラーの場合はログに出力して終了 console.log(JSON.stringify(error)); return; } var w = UI.Window.getWidth(); var h = UI.Window.getHeight(); // ニックネームをラベルで表示 var nickname = user.nickname; var label = new UI.Label(); label.setFrame(0, 0, w, 50); label.setText(nickname); UI.Window.document.addChild(label); // サムネイル画像を表示 var thumbnailUrl = user.thumbnailUrl; var image = new UI.Image(); image.setFrame([(w - 128)/2, 50, 128, 128]); image.setImage(thumbnailUrl); UI.Window.document.addChild(image); }); }
getCurrentUserの第1引数には取得したいプロフィールのフィールドを指定しています。nicknameやthumbnailUrlの他にも、ユーザーIDや誕生日などの情報が取得可能です。APIとしてはCommon APIですが、取得できるフィールドはプラットフォームによって異なる場合があります。
例えば、血液型などは日本でのみ提供されているフィールドになり、Mobage Globalでは取得できません。第2引数には、実行結果を取得して画面に表示する処理を行うcallback関数を指定しています。AjaxのXMLHttpRequestを使ったWebアプリケーションを開発したことがある開発者の方にはおなじみのコーディングスタイルかと思います。実行すると図5のような結果が得られます。
日本のプラットフォームは、Mobageでフィーチャーフォンの頃から使用されているアバター画像が使用されます。一方のMobage Globalでは、写真や画像などを選択してアップロードするスタイルでアバター画像はありません。海外のユーザーは、バーチャルなゲーム向けソーシャルグラフでも自分の顔写真を載せる人が多いようです。
RESTful APIを使ってユーザー情報を表示してみよう
クライアントAPIで実装したユーザー情報の表示と同じ処理を、Game ServerからRESTful APIを呼び出す形で実装する方法を紹介します。今回は、開発時のアプリケーション配布の用途だけでなく、アプリケーションサーバーとしてもNode.jsを利用して、JavaScriptでGame Serverのロジックを実装してみようと思います。
OAuthを使ったRESTful APIの認証
Mobageのスマートフォンアプリ版で提供されているRESTful APIにアクセスするためには、3-legged OAuth(注3)に準拠した認証フローを実装する必要があります。
RFC5849に準拠。
今までのフィーチャーフォンやブラウザ版とは違いgadgetサーバーによるクライアント認証が存在しないため、クライアントアプリケーション、Game Server、Mobageプラットフォームの3者間でデータをやり取りしながらの認証を行うアーキテクチャを採用しています。ここでは、簡単に今回実装する認証フローを説明します(図6)。
- RequestTokenを取得してクライアントに返す
- クライアント側で認可処理を行い、得られた検証コードをGame Serverに送信する
- クライアントから渡された検証コードを使ってAccessTokenを取得する
- AccessTokenを使用してPeople APIを呼び出す
RESTful API用のConsumer KeyとSecretの発行
RESTful APIにアクセスするためには、クライアントAPI用とは別に、サーバー用のConsumer KeyとConsumerSecretのペアをもう一つ作成する必要があります。
Mobage GlobalのDeveloper Portalで先ほど作成したアプリケーションの詳細画面を開き、「New Client Application」をクリックして、Consumer KeyとSecretを作成しましょう(図7)。
この時に、「Server」チェックボックスにチェックを入れて作成してください。
Node.jsのモジュールのインストール
Node.jsからAPIサーバーへリクエストする際には、OAuth 1.0の署名を生成・付与する必要があるので、今回はnode-oauthというライブラリを使用することにします。
また、Node.js用のWebアプリケーションフレームワークとして人気のあるexpressも使用したいと思います。
Node.jsのモジュールのインストールにはnpmコマンドが便利なので、まずはLIST5のコマンドでnpmをインストールしてください。
$ sudo curl http://npmjs.org/install.sh | sudo sh
次に適当な作業ディレクトリーに移動し、npmコマンドを使用して「oauth」と「express」をインストールします(LIST6)。ついでに、Game Serverの実装をするGameServer.jsも作成しておきましょう。
$ npm install oauth oauth@0.9.5 ./node_modules/oauth $ npm install express express@2.4.4 ./node_modules/express ├── mime@1.2.2 ├── qs@0.3.1 └── connect@1.6.2 $ touch GameServer.js $ ls GameServer.js node_modules
Game Serverを実装する
ここまで来たら実装の準備は完了です。まずは、表1のようにURLを設計してみました。
Path | 処理概要 |
getOAuthRequestToken | APIサーバーからRequestTokenを取得し返す |
getOAuthAccessToken?oauth_verifier={verifier} | 検証コードを使用してAPIサーバーからAccessTokenを取得し、「3-legged OAuth success!!」と返す |
getCurrentUser | 認証済みユーザーのニックネームとサムネイル画像のURLをJSON形式で返す |
RESTful API 用に作成したConsumer Key とConsumerSecretを使って、LIST7のような実装に落とし込んでいきます。
var express = require("express"); var OAuth = require("oauth").OAuth; var OAUTH_BASE_URL = "https://app-sandbox.mobage.com/1/MyFirstMobageApp"; var OAUTH_CONSUMER_KEY = "jIvRpIQkvxU8LFipWoeXsQ"; var OAUTH_CONSUMER_SECRET = "9BYusiUlQe6J2yqnIUroKdshbJ0S9Jh395ru7ciwpg"; var OAUTH_VERSION = "1.0"; var OAUTH_CALLBACK = "oob"; var OAUTH_SIGNATURE_METHOD = "HMAC-SHA1"; var SOCIAL_BASE_URL = "https://app-sandbox.mobage.com/1/MyFirstMobageApp/opensocial"; // OAuth Counsumerを作成 function createConsumer(){ return new OAuth( OAUTH_BASE_URL + "/request_temporary_credential", OAUTH_BASE_URL + "/request_token", OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, OAUTH_VERSION, OAUTH_CALLBACK, OAUTH_SIGNATURE_METHOD); } // サーバーの作成 var server = express.createServer(); server.use(express.logger()); server.use(express.bodyParser()); server.use(express.cookieParser()); server.use(express.session({ secret: "l3udyzHdkbkbmslz7Rjzycpt" })); // RequestTokenを取得して、クライアントに返す server.get("/getOAuthRequestToken", function(req, res) { // RequestTokenを取得する createConsumer().getOAuthRequestToken( function(error, oauth_token, oauth_token_secret, results){ if(error) { // エラーの場合はAPIのレスポンスをそのまま返す res.writeHead(error.statusCode, {"Content-Type": "text/plain"}); res.write(error.data); res.end(); return; } // セッションにRequestTokenを保存 req.session.oauth_request_token = oauth_token; req.session.oauth_request_token_secret = oauth_token_secret; // RequestTokenをクライアントに返す res.writeHead(200, {"Content-Type": "text/plain"}); res.write(oauth_token); res.end(); } ); }); // クライアントから渡された検証コードを使ってAccessTokenを取得する server.get("/getOAuthAccessToken", function(req, res) { // リクエストパラメーターから検証コードを取得する var oauth_verifier = req.param("oauth_verifier"); // セッションからRequestTokenを取得する var oauth_request_token = req.session.oauth_request_token; var oauth_request_token_secret = req.session.oauth_request_token_secret; // AccessTokenを取得する createConsumer().getOAuthAccessToken( oauth_request_token, oauth_request_token_secret, oauth_verifier, function(error, oauth_token, oauth_token_secret, results){ if(error) { // エラーの場合はAPIのレスポンスをそのまま返す res.writeHead(error.statusCode, {"Content-Type": "text/plain"}); res.write(error.data); res.end(); return; } // セッションにAccessTokenを保存 req.session.oauth_access_token = oauth_token; req.session.oauth_access_token_secret = oauth_token_secret; // RequestTokenをクライアントに返す res.writeHead(200, {"Content-Type": "text/plain"}); res.write("3-legged OAuth success!!"); res.end(); } ); }); // AccessTokenを使用して、ログインユーザーのプロフィール情報を取得する server.get("/getCurrentUser", function(req, res) { // セッションからAccessTokenを取得する var oauth_access_token = req.session.oauth_access_token; var oauth_access_token_secret = req.session.oauth_access_token_secret; // ログインユーザーのプロフィール情報を取得する createConsumer().getProtectedResource( SOCIAL_BASE_URL + "/people/@me/@self?fields=nickname,thumbnailUrl", "GET", oauth_access_token, oauth_access_token_secret, function (error, data, response) { if(error) { // エラーの場合はAPIのレスポンスをそのまま返す res.writeHead(response.statusCode, {"Content-Type": "application/json"}); res.write(error.data); res.end(); return; } // APIサーバーからのHTTPステータス、レスポンスデータをそのまま返す res.writeHead(response.statusCode, {"Content-Type": "application/json"}); res.write(data); res.end(); } ); }); // サーバーの開始 server.listen(3000); console.log("running on http://localhost:3000");
今回は簡易的にサーバー側のセッションにOAuthのTokenを保存して認証フローを実現していますが、実運用でスケールされたサーバー環境の場合は、RDBやmemcachedなどの永続化メカニズムを使用することが一般的です。では、Game Serverを起動して動作確認をしてみましょう(LIST8)。
$ node GameServer.js running on http://localhost:3000
ブラウザからhttp://localhost:3000/getOAuthRequestTokenを開き、問題なくRequestTokenが取得できるかテストしてみましょう(図8)。
クライアントアプリケーションを実装する
最後に、一連の認証フローを行い、ログインユーザーのプロフィールの表示を行うクライアント側の処理を実装します(LIST9)。
var Core = require('../NGCore/Client/Core').Core; var UI = require('../NGCore/Client/UI').UI; var XHR = require('../NGCore/Client/Network/XHR').XHR; var Social = require('../NGCore/Client/Social').Social; var GAME_SERVER_URL = "http://localhost:3000"; function main(){ // Mobageコミュニティボタンを表示 Social.Common.Service.showCommunityButton( UI.ViewGeometry.Gravity.TopLeft, "default"); // RequestTokenの取得を要求する getRequestToken(); } function getRequestToken(){ var req = new XHR(); req.onreadystatechange = function(){ if (req.readyState != 4) { return; } if (req.status != 200) { console.log(JSON.stringify(req)); return; } // クライアント側で認可処理を行い、得られた検証コードをGame Serverに送信する Social.Common.Auth.authorizeToken(req.responseText, function(error, verifier){ if (error) { console.log(JSON.stringify(error)); return; } // AccessTokenの取得を要求する getAccessToken(verifier); }); }; req.open("GET", GAME_SERVER_URL + "/getOAuthRequestToken", true); req.send(null); } function getAccessToken(verifier){ var req = new XHR(); req.onreadystatechange = function(){ if (req.readyState != 4) { return; } if (req.status != 200) { console.log(JSON.stringify(error)); return; } // 認証成功のメッセージを表示する new UI.Toast({ "text" : req.responseText }).show(); // ユーザー情報を取得して表示する getCurrentUser(); }; req.open("GET", GAME_SERVER_URL + "/getOAuthAccessToken?oauth_verifier=" + verifier, true); req.send(null); } function getCurrentUser(){ var req = new XHR(); req.onreadystatechange = function(){ if (req.readyState != 4) { return; } if (req.status != 200) { console.log(JSON.stringify(error)); return; } // ユーザー情報を解析 var user = JSON.parse(req.responseText); var w = UI.Window.getWidth(); var h = UI.Window.getHeight(); // ニックネームをラベルで表示 var nickname = user.nickname; var label = new UI.Label(); label.setFrame(0, 0, w, 50); label.setText(nickname); UI.Window.document.addChild(label); // サムネイル画像を表示 var thumbnailUrl = user.thumbnailUrl; var image = new UI.Image(); image.setFrame([(w - 128)/2, 50, 128, 128]); image.setImage(thumbnailUrl); UI.Window.document.addChild(image); }; req.open("GET", GAME_SERVER_URL + "/getCurrentUser", true); req.send(null); }
Game ServerのURL設計に合わせると以下のような処理概要になります。
- /getOAuthRequestTokenにアクセスしてRequestTokenを取得
- Social.Common.Auth.authorizeTokenにRequestTokenを渡して検証コードを取得
- /getOAuthAccessTokenにアクセスして検証コードを送信、AccessTokenを取得
- /getCurrentUserにアクセスしてユーザー情報を表示する
クライアントアプリケーションからのHTTP通信には、ngCoreのAPIで提供されているNetwork.XHRを利用します。こちらも、ブラウザに組み込まれているXMLHttpRequestとほぼ同じように扱うことができるので、Web系の開発者の方にはおなじみのコーディングになります。コーディングが終わったら、make serverでアプリケーションを配布するためのNode.jsを立ち上げて、クライアントアプリケーションをBakeして実行してみてください。
ToastのUIに、「3-legged OAuth success!!」と表示された後、クライアントAPIと同じようにニックネームとサムネイル画像が表示されれば完璧です。Game Serverを使った場合の実装は、OAuthの手続きがやや面倒に感じるかもしれません。しかし、バグの修正や機能拡張がオンデマンドに対応できる点、ユーザーが機種変更時にアプリケーションのデータが移行可能である点やチートされにくいといった観点で、Game Serverを立ててサーバーサイドにアプリケーションのロジックを寄せるメリットはあります。開発するゲームの要件や特性に合わせてクライアントAPIとRESTful APIをうまく使い分けることが重要です。
まとめ
今回はMobage GlobalのSandbox環境にアクセスしてスマートフォンアプリケーションにソーシャル機能を組み込む方法を紹介しました。ngCoreによるクライアントアプリケーションだけでなくサーバーサイドのロジックもJavaScriptで十分コーディングができることがご理解いただけたと思います。
今、最も注目を集めているWeb系プログラミング言語で、AndroidとiOS向けのクライアント開発も、サーバーサイド開発も、そして、日本以外のグローバルプラットフォームもワンソースでコーディングできる環境が整っているということは非常に魅力的なことだと思います。今までWebアプリケーションを中心にソーシャルゲームを開発されてきた開発者の方々も、ngCoreとNode.jsを使ってスマートフォンアプリケーションの開発にチャレンジしてみてはいかがでしょうか。
ngCoreで開発されたゲームをAndroid/iOSアプリとしてだけでなく、スマートフォンのブラウザ上で動作させるためにHTML5実装のRuntimeを提供予定です。HTML5に対応することにより、ライブアップデート機能の強化、WindowsPhoneなどの他デバイスへの対応、決済手段の拡充などが期待されます。