爲誰鍊金戰鬥自動化的 MMA 實現
Aug 26, 2017 · 1 min read
起因
玩過的大概都直到爲誰鍊金是一個肝爆炸的遊戲,筆者因爲帳號繼承出現失誤,把號弄沒了只能重新再來,又無法忍受從0開始重耍,便有了這個東西。主要用的 MMA 的 HTTPRequest 功能和 ExportString 和 ImportString 實現 JSON 和 Association 之間的轉換。
封包分析
共有
也就是每個封包 Headers 里都有的信息,包括一下幾個:
- x-app-ver: app版本
- X-GUMI-TRANSACTION: 隨便拿個東西生成 UUID/GUID 就成
- X-Unity-Version: Unity 版本,一般不會變
- x-asset-ver: 資源版本
- Content-Type: 用 “application/json; charset=utf-8” 就成
- Content-Length: 請求 body 的長度
這些數據都可以自己生成/抓包取得,遊戲更新時重抓一次就行。
然後便能寫好兩個通用 HTTP 請求函數,需要 session id 的和不需要的:
tagatameRequestWithoutSSID[path_,body_]:=Block[{req},
req=HTTPRequest[
<|"Scheme"->"https",
"Domain"->"alchemist.gu3.jp",
"Path"->path,
Method -> "POST",
"Headers"->{
"x-app-ver" -> "5f3debcd:a",
"X-GUMI-TRANSACTION"-> CreateUUID[],
"X-Unity-Version"-> "5.3.6p1",
"x-asset-ver"->"99d08f149a67de3c1d4b312638ea51a6ca83519b_gumi",
"Content-Type"->"application/json; charset=utf-8",
"Content-Length"->Length@ImportString[body,{"Binary","Byte"}]},
"Body"-> body,
VerifySecurityCertificates -> False
|>];
ImportString[URLRead[req,"Body"],"RawJSON"]
];
tagatameRequest[path_,body_]:=Block[{req},
req=HTTPRequest[
<|"Scheme"->"https",
"Domain"->"alchemist.gu3.jp",
"Path"->path,
Method -> "POST",
"Headers"->{
"x-app-ver" -> "5f3debcd:a",
"X-GUMI-TRANSACTION"-> CreateUUID[],
"Authorization"->"gumi "<>sessionId,
"X-Unity-Version"-> "5.3.6p1",
"x-asset-ver"->"99d08f149a67de3c1d4b312638ea51a6ca83519b_gumi",
"Content-Type"->"application/json; charset=utf-8",
"Content-Length"->Length@ImportString[body,{"Binary","Byte"}]},
"Body"-> body,
VerifySecurityCertificates -> False
|>];
ImportString[URLRead[req,"Body"],"RawJSON"]
];
登錄
登錄也就是生成 session id,除了登錄之外的所有通信都需要 session id.
登錄 需要 device_id 和secret_key,也可以通過抓包獲得。
generateSSID[deviceid_:"a7b429b5-88a0-4a5b-9082-495f526dc8a0",secretkey_:"ef9ac5ff-0818-46e0-9837-e1b20fd47b5a"]:=
Block[{json,body},
ticket=ticket+1;
json=<|"ticket"->ticket,"access_token"->"", "param"-><|"device_id"->deviceid,"secret_key"->secretkey|>|>;
body=ExportString[json,"RawJSON","Compact"->True] ;
json=tagatameRequestWithoutSSID["/gauth/accesstoken",body];
sessionId=json["body"]["access_token"];
tagatameRequest["/product","{\"ticket\":"<>ToString[ticket]<>"}"];
Return[sessionId];
];戰鬥
戰鬥請求數據里有一個時間數據,抓包兩次相減就能獲得算法,結果如下:
getTime[]:=QuantityMagnitude[DateObject[Now,"Second"]-DateObject[{2017,8,11,12,12,03},"Second"]]+1502424733;然後戰鬥請求的數據根據抓包把格式弄好即可。不過如果要同步成就,就要先獲取所有成就,然後戰鬥後將所有成就推進一次(實測一次直接推進N次也是可以的。)
getTrophyToReward[]:=Block[
{result},
ticket++;
result=tagatameRequest["/login/param","{\"ticket\":"<>ToString[ticket]<>"}"];
result=result["body"]["trophyprogs"];
Select[result,!IntegerQ[#["rewarded_at"]]&]
];battleStart[questid_]:=Block[{json,body,result},
ticket++;
json=<|"ticket"->ticket,"param"-><|"iname"->questid,"partyid"->0,"req_at"->getTime[],"btlparam"-><|"help"-><|"fuid"->""|>|>,"location"-><|"lat"->0,"lng"->0|>|>|>;
body=ExportString[json,"RawJSON","Compact"->True] ;
result=tagatameRequest["/btl/com/req",body];
battleId=result["body"]["btlid"];
battleResult=result["body"]
];battleEnd[mission_,trophytime_]:=Block[
{json,number,trophyprogs,body,today},
ticket++;
number=Length[battleResult["btlinfo"]["drops"]];
today=StringJoin[IntegerString[#,10,2]&/@DateValue[{"Year","Month","Day"}]];
trophyprogs=getTrophyToReward[];
(*trophyprogs=Select[trophyprogs,StringContainsQ[#["iname"],StringTake[battleResult["btlinfo"]["qid"],{7,-1}]]&];*)
trophyprogs=<|"iname"->#["iname"],"pts"->#["pts"]+trophytime,"ymd"->#["ymd"]|>&/@trophyprogs;
json=<|"ticket"->ticket,"param"-><|"btlid"->battleId,"btlendparam"-><|"time"->0,"result"->"win","beats"->Table[1,number],"steals"-><|"items"->Table[0,number],"golds"->Table[0,number]|>,"missions"->{mission,mission,mission},"inputs"->{}|>,"trophyprogs"->trophyprogs|>|>;
body=ExportString[json,"RawJSON","Compact"->True];
tagatameRequest["/btl/com/end",body]
];
