スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

5/18のアプデ以降に出撃中のキャラ数・装備数を取得する方法

完全に専ブラ関係者向けの記事です。

5/18のメンテ以降api_get_member/ship2が廃止され、かわりにapi_get_member/ship_deckが新説されました。ship_deckのAPIの構造自体は、api_ship_dataは母港API・api_shipの配列で、api_deck_dataが母港API・api_deck_portの配列というあんまship2と変わらない構造なのですが、大きな違いが1つあります。それは、ship2では所持している全艦のデータを送ってきたのに対して、ship_deckでは出撃している艦のデータのみ送ってくるようになりました。にもかかわらず運営頭おかしいので、ドロップした艦のデータをship_deckで送ってこないのです。したがって、ship_deckのみを追っていると、ドロップしても保有キャラ数の変動は捉えられても、保有装備数の変動は捉えられないという現象が起きます。これを解決する方法をまとめました。

おおまかなロジックは以下のとおりです。

(1) 初期装備とキャラの対応をデータベースとして記録しておく(この場合はapi_start2のapi_mst_shipのapi_idとの対応)
(2) 出撃前に母港状況をバッファリングしておく(api_req_map/startのタイミング)
(3) 戦闘結果で、データベースに初期装備のデータがあれば、そこから初期装備を推定して船を追加。なければ適当にダミーデータを放り込んで船を追加(api_req_sortie/battleresult や api_req_combined_battle/battleresultのタイミング)
(4) 帰投後に(2)と同じ方法で母港状況をバッファリングして、出撃前との差分を取る(api_portのタイミングで出撃前のバッファリングがnullかどうかで判定するのが簡単かと)
(5) 差分で新規の艦が存在すれば、データベースに初期装備を記録する

(1)のデータをユーザーに1から取らせないといけないので、ある程度の初期データを与えておいたほうがよいでしょう。そこで艦これt・old(http://www51.atpages.jp/kancollev/)から2014年9月頃の初期装備があったときのマスターデータを取得し、定数として与えておきます。艦これt oldは普通のテーブルですが、これをExcel等で動的にコードを作成したものがこちら↓

KancolleDOld.cs
https://www.dropbox.com/s/g16s85fcol6q75i/KancolleDOld.cs?dl=0

ここらへんはライブラリ化したいよね。

つぎに差分を求める部分です。
DefaultSlotitemDataBase.cs
https://www.dropbox.com/s/mrkibk5bddqci0r/DefaultSlotitemDataBase.cs?dl=0

出撃前にはこのBeforeSortieProcess()を読ませて、帰投後にはAfterReturnProcess()を読ませます。戦闘終了後のドロップ(battleresult)と建造で入手したタイミング(getship)でAddCollectionを読ませます。シリアル化にProtoBufを使っていますが、適宜お使いのライブラリに読み替えてください。開発者の方みんなすごいんでそこまで解説しなくてもわかるんじゃないかな…(解説めんどいだけ)

あとこのデータベースから船のデータオブジェクトを初期化する操作は各自実装してください。適当にstaticなメソッド作っておけばいいんで。

(追記)このDBから船のインスタンスを作るところで少しはまる可能性があるので、ほっぽアルファで使ってるソースを書いておきます。ApiShipや艦船データのコレクションの定義は環境によって異なるとは思いますが適宜読み替えてください。

    public class ApiShip
{
public int api_id { get; set; }
public int api_sortno { get; set; }
public int api_ship_id { get; set; }
public int api_lv { get; set; }
public List api_exp { get; set; }
public int api_nowhp { get; set; }
public int api_maxhp { get; set; }
public int api_leng { get; set; }
public List api_slot { get; set; }
public List api_onslot { get; set; }
public List api_kyouka { get; set; }
public int api_backs { get; set; }
public int api_fuel { get; set; }
public int api_bull { get; set; }
public int api_slotnum { get; set; }
public int api_ndock_time { get; set; }
public List api_ndock_item { get; set; }
public int api_srate { get; set; }
public int api_cond { get; set; }
public List api_karyoku { get; set; }
public List api_raisou { get; set; }
public List api_taiku { get; set; }
public List api_soukou { get; set; }
public List api_kaihi { get; set; }
public List api_taisen { get; set; }
public List api_sakuteki { get; set; }
public List api_lucky { get; set; }
public int api_locked { get; set; }
public int api_locked_equip { get; set; }
public int api_sally_area { get; set; }

//ディープコピー
public ApiShip Clone()
{
// シリアル化した内容を保持しておくためのMemoryStreamを作成
using (MemoryStream stream = new MemoryStream())
{
// バイナリシリアル化を行うためのフォーマッタを作成
BinaryFormatter f = new BinaryFormatter();
// 現在のインスタンスをシリアル化してMemoryStreamに格納
f.Serialize(stream, this);
// ストリームの位置を先頭に戻す
stream.Position = 0L;
// MemoryStreamに格納されている内容を逆シリアル化する
return (ApiShip)f.Deserialize(stream);
}
}

//完全なダミーデータ
public static ApiShip MakeDummy()
{
ApiShip oship = new ApiShip();
//初期値でNULLになるプロパティの初期化
oship.api_exp = Enumerable.Repeat(0, 3).ToList();
oship.api_slot = Enumerable.Repeat(-1, 5).ToList();
oship.api_onslot = Enumerable.Repeat(0, 5).ToList();
oship.api_kyouka = Enumerable.Repeat(0, 5).ToList();
oship.api_ndock_item = Enumerable.Repeat(0, 2).ToList();

oship.api_karyoku = Enumerable.Repeat(0, 2).ToList();
oship.api_raisou = Enumerable.Repeat(0, 2).ToList();
oship.api_taiku = Enumerable.Repeat(0, 2).ToList();
oship.api_soukou = Enumerable.Repeat(0, 2).ToList();
oship.api_kaihi = Enumerable.Repeat(0, 2).ToList();
oship.api_taisen = Enumerable.Repeat(0, 2).ToList();
oship.api_sakuteki = Enumerable.Repeat(0, 2).ToList();
oship.api_lucky = Enumerable.Repeat(0, 2).ToList();
//値を与えておかないと大変なことになるプロパティ
oship.api_id = APIPort.ShipsDictionary.Keys.Max() + 100;//キーの最大値+100 1だと轟沈したとき面倒なことになる可能性あり

return oship;
}

//マスターIDを指定して新規艦の作成
public static ApiShip MakeNewShip(int shipid)
{
ApiShip oship = MakeDummy();

//マスターIDに入っているか
ApiMstShip dship;
if (!APIMaster.MstShipsDictionary.TryGetValue(shipid, out dship)) return oship;
//装備IDの最大値の取得
int slotid = APIPort.ShipsDictionary.Values.SelectMany(x => x.api_slot).Max() + 1;
//装備スロットの作成
List slots = oship.api_slot.Select(x => x).ToList();
DefaultSlotitemTable.DefaultSlotitemRecord records;
if(DefaultSlotitemDataBase.Collection.Items.TryGetValue(shipid, out records))
{
foreach(int i in Enumerable.Range(0, Math.Min(slots.Count, records.MasterSlotitemID.Count)))
{
if(records.MasterSlotitemID[i] != -1)
{
slots[i] = slotid;
slotid++;
}
}
}

//dshipからパラメーターのラッピング
//api_idはダミーで初期化済み
oship.api_sortno = dship.api_sortno;
oship.api_ship_id = shipid;
oship.api_lv = 1;
//api_expはダミー 5
oship.api_nowhp = dship.api_taik[0];
oship.api_maxhp = oship.api_nowhp;
oship.api_leng = dship.api_leng;
oship.api_slot = slots;
oship.api_onslot = dship.api_maxeq.Select(x => x).ToList();//そのままリスト指定だと参照渡しになってしまうため 10
//api_kyoukaはそのまま
oship.api_backs = dship.api_backs;
oship.api_fuel = dship.api_fuel_max;
oship.api_bull = dship.api_bull_max;
oship.api_slotnum = dship.api_slot_num;//15
//api_ndock_timeは0なのでそのまま
//api_ndock_itemも初期値なのでそのまま
//api_srateはよくわからないのでそのまま
oship.api_cond = 40;//入手時のcond
oship.api_karyoku = dship.api_houg.Select(x => x).ToList();//20
oship.api_raisou = dship.api_raig.Select(x => x).ToList();
oship.api_taiku = dship.api_tyku.Select(x => x).ToList();
oship.api_soukou = dship.api_souk.Select(x => x).ToList();
//api_kaihiは消えてるのでそのまま
//api_taisenも消えてるのでそのまま 25
//api_sakutekiも(ry
oship.api_lucky = dship.api_luck.Select(x => x).ToList();
//api_lockedも0なので
//api_locked_equipも同様
//api_sally_areaも同様 30

return oship;
}
}


注意点
・マスターデータからインスタンスを作る際、参照型の変数はディープコピー(この例ではLINQでディープコピーしています)してあげないと、個々の船のパラメーターが書き換えられたときに、マスターデータ側も書き換わってしまうため注意
・船のインスタンスに与えるID(api_id)ですが、正確な値が取得できないので、艦船データのコレクションからIDの最大値+100で与えています。最大値+1にしてしまうと次のような不具合がおこる可能性があります。
出撃時:第1艦隊 ID1,.2,3

1戦目:第1艦隊 ID3が大破 ドロップなし

2戦目:第1艦隊 ID3が轟沈 ドロップあり(このときドロップ艦のIDが3として推定されてしまう可能性がある)
本当はApiShipにIDのキャレット的なパラメーターを用意してあげればいいけど、1周でドロップ100隻はまずあり得ないのでそこら辺の離したパラメーターでOKだと思います。
・帰投後、母港APIが呼ばれたときにIDと装備の答え合わせが行われますが、出撃中は装備コレクションにはデータがないので、装備検索等で未反映のドロップ艦の装備を呼び出す場合はエラー対策が必要。ただ、これはship2の環境でも同様。

・艦船データをDictionaryとして与えているのは、ApiShipのapi_idをキーとさせて、O(1)操作で艦船データを取り出すため。Listで定義してLINQでいちいち検索しているとO(n)になって遅くなると思います。
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。