ちょっと気になるツイートを見かけたのでそれ絡みで。
https://twitter.com/Sikushima/status/374120078976880640
「艦これ」から、ソーシャル系のサーバ構成を考える – SQLer 生島勘富 の日記
http://d.hatena.ne.jp/Sikushima/20130901/1378021345
艦これサーバーのトラブル云々には同意しかねるのですが、素直にMySQLのSQLで組んだほうが、下手にデータ構造を考えてAPIを作ったりするよりは断然早い、ところの同感です。更新系は兎も角、参照関係は断然RDBMSは早く作られているので、下手のO/RマッピングやるよりもSQL直で書いたほうが手早くかつ実行スピードがすばやくできます。ええ、SQL文を書ければ、の前提なのですが。
で、艦これの同時セッション絡みの部分、いくらか艦これをやっていて、いくらかFlashの通信部分を覗いてみると、さほどセッション/DBコネクション自体には負荷がかかっていないことが分かります。つーか、アクションRPGと違って、艦これの場合、サーバーとの通信が極端に少なくて、ガラ携のWEBゲームぐらい通信は少ない。なので、同時接続数(アクティブ数が30%だとしても3万人ぐらい)だとしても、DBコネクション等で輻輳しているところは更に1/100以下ぐらい、もっと少ないかもしれません。艦これの場合、Flashから各サーバーに送るのは、普通にRESTで送って、JSONで返ってきます。詳しく見ると cookie を使っていません。GET するときの URL に token が入っているのですが、多分ユーザIDへの紐づけかと。なので、非常にDBに負荷の少ない設計(意図的…ということにしておきましょう)になっています。ぷよぷよクエストとか、バウモンとか、レガリアを iPhone でやっていると、やたらに「ログイン中です…」が出るのですが、艦これの場合は全く出てきません。そういう意味では、パズドラも出てこないですね。優秀です。
このあたり、通常のソーシャルゲームにある、
- ユーザーごとのチャット機能
- 他ユーザーのデータを参照して、何かやる(仲間にするとか)する機能
- リアルタイムに他ユーザーと何かやる機能
というのがごっそり抜けています。これは、各鎮守府サーバー(ログイン先のサーバー)間で通信が発生しない(発生できない)のと、まあ、そのあたりを設計しなかっった(意図的に…としておきます)ためです。なので、極端な話で言えば、ロードバランサーすら必要なくて(あるのかな?)、ひとつのサーバーで10万人程度受け持つ、という予算の中で組んで、開いてみたら思わずアクセスがあって、順々にサーバーを増やす、という作りになっているかと。と想像するわけですが、そのあたりは ツチノコブログ | DMM.comラボのインフラエンジニアブログ を参照して頂くということで。
なので、参照系のデータとしては艦船データやマップデータで、更新系は各ユーザーのHPやログインデータぐらいなもので、データベースへの輻輳の率を考えると、それほどシビアな形で使わないくてもうまくいのかな、と想像します。MySQL Clustor を使う位だから、それなりに負荷は掛かっているのでしょうが、同時コネクション数は、100程度あれば十分かも。
■戦闘の開始(battole)と結果(battleresult)
戦闘開始
svdata={
"api_result":1,
"api_result_msg":"成功",
"api_data":{
"api_dock_id":1,
"api_ship_ke":[-1,509,509,502,502,502,-1],
"api_ship_lv":[-1,1,1,1,1,1,-1],
"api_nowhps":[-1,66,36,36,37,25,43,58,58,22,22,22,-1],
"api_maxhps":[-1,75,40,36,37,25,63,58,58,22,22,22,-1],
"api_midnight_flag":0,
"api_eSlot":[[505,513,525,-1,-1],[505,513,525,-1,-1],[502,-1,-1,-1,-1],[502,-1,-1,-1,-1],[502,-1,-1,-1,-1],[-1,-1,-1,-1,-1]],
"api_eKyouka":[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],
"api_fParam":[[82,0,50,81],[3,0,26,25],[32,19,19,28],[39,24,31,30],[24,43,31,17],[74,0,38,69]],
"api_eParam":[[32,32,16,28],[32,32,16,28],[7,16,7,6],[7,16,7,6],[7,16,7,6],[0,0,0,0]],
"api_search":[1,1],
"api_formation":[3,2,1],
"api_stage_flag":[1,1,1],
"api_kouku":{
"api_plane_from":[[2],[-1]],
"api_stage1":{
"api_f_count":58,
"api_f_lostcount":0,
"api_e_count":0,
"api_e_lostcount":0,
"api_disp_seiku":1
},
"api_stage2":{
"api_f_count":46,
"api_f_lostcount":0,
"api_e_count":0,
"api_e_lostcount":0
},
"api_stage3":{
"api_frai_flag":[-1,0,0,0,0,0,0],
"api_erai_flag":[-1,0,0,1,0,0,0],
"api_fbak_flag":[-1,0,0,0,0,0,0],
"api_ebak_flag":[-1,0,0,0,0,1,0],
"api_fcl_flag":[-1,0,0,0,0,0,0],
"api_ecl_flag":[-1,0,0,0,0,0,0],
"api_fdam":[-1,0,0,0,0,0,0],
"api_edam":[-1,0,0,25,0,0,0]
}
},
"api_support_flag":0,
"api_support_info":null,
"api_opening_flag":0,
"api_opening_atack":null,
"api_hourai_flag":[1,1,0,0],
"api_hougeki1":{
"api_at_list":[-1,1,7,6,8,4,3,5,-1,-1,-1,-1,-1],
"api_df_list":[-1,11,6,10,2,8,8,7,-1,-1,-1,-1,-1],
"api_si_list":[-1,8,505,7,505,6,6,4,-1,-1,-1,-1,-1],
"api_cl_list":[-1,1,2,1,1,1,1,1,-1,-1,-1,-1,-1],
"api_damage":[-1,85,2,61,16,15,5,5,0,0,0,0,0]
},
"api_hougeki2":{
"api_at_list":[-1,1,8,3,4,5,6,-1,-1,-1,-1,-1,-1],
"api_df_list":[-1,7,2,8,8,8,8,-1,-1,-1,-1,-1,-1],
"api_si_list":[-1,12,505,6,6,4,11,-1,-1,-1,-1,-1,-1],
"api_cl_list":[-1,1,0,1,1,1,2,-1,-1,-1,-1,-1,-1],
"api_damage":[-1,66,0,4,8,2,69,0,0,0,0,0,0]
},
"api_hougeki3":null,
"api_raigeki":null
}
}
戦闘開始のAPIを呼び出すと、いきなり戦闘結果が返ってきます。api_hougeki1 と api_hougeki2 のあたりがそれですね。サーバーにはユーザーの艦娘のデータが保持されているので、鎮守府サーバーで戦闘の計算を出して返してきます。なので、戦闘をしてズル…はできないと思います。api_hougeki1 などの結果で、クライアントの Flash でアニメーションをしています。ということは、このあたりのデータを解析すれば、戦闘シーンは復元可能ってことです。
戦闘結果
svdata={
"api_result":1,
"api_result_msg":"成功",
"api_data":{
"api_ship_id":[-1,509,509,502,502,502,-1],
"api_win_rank":"S",
"api_get_exp":60,
"api_mvp":1,
"api_member_lv":24,
"api_member_exp":29431,
"api_get_base_exp":120,
"api_get_ship_exp":[-1,432,144,144,144,144,144],
"api_get_exp_lvup":[[69933,70300,74100],[2471,2800],[2574,2800],[8773,9100],[9330,10500],[12372,13600]],
"api_dests":5,
"api_destsf":1,
"api_lost_flag":[-1,0,0,0,0,0,0],
"api_quest_name":"カムラン半島",
"api_quest_level":3,
"api_enemy_info":{
"api_level":"",
"api_rank":"",
"api_deck_name":"敵前衛部隊"
},
"api_first_clear":0,
"api_get_flag":[0,1,0],
"api_get_ship":{
"api_ship_id":32,
"api_ship_type":"駆逐艦",
"api_ship_name":"初雪",
"api_ship_getmes":"初雪……です……よろしく。"
},
"api_get_eventflag":0
}
}
戦闘のアニメーションが終わったら、戦闘結果≒経験値を取得します。この計算も既に鎮守府サーバーで行った結果を返してきて、結果のアニメーションを出します。新しい艦娘が出て来たときには、この JSON に書いてあります。ランクとか経験値が書いてあるので、先の戦闘前の戦闘結果(ってややこしい)と、経験値の結果をあわせると、自分の艦娘の戦歴が作成できます。
多分、サーバーのほうでは、この手の履歴は保持ってないと思うんですよね。なので、いつ、どんな風に戦闘を勝ち抜いてきたのか、という戦歴は、この方法でしか取れないと思います。
■敷波復活
そんな訳で、この通信キャプチャを遣っている間に「敷波さん」をGetッ!!! 英霊により復活であります。
まだ、Get したばっかりなので、レベル1ですが、ええ、「ブラ鎮」≒「鎮守府をぶらぶら歩くこと」(銀ぶらと同じですねッ!!! 決して、ブラック鎮守府ではありませぬー)して遠征に出れる位までには御育て致しましょう。
2014/03/11 追記
艦これのバトルJSONを再解析(祥鳳改小破編) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5517
