Fiddlerを使って艦これのバトルJSONを解析(敷波復活編)

ちょっと気になるツイートを見かけたのでそれ絡みで。

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ッ!!! 英霊により復活であります。

image

まだ、Get したばっかりなので、レベル1ですが、ええ、「ブラ鎮」≒「鎮守府をぶらぶら歩くこと」(銀ぶらと同じですねッ!!! 決して、ブラック鎮守府ではありませぬー)して遠征に出れる位までには御育て致しましょう。

2014/03/11 追記
艦これのバトルJSONを再解析(祥鳳改小破編) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5517

 

カテゴリー: 艦これ パーマリンク