feat(plugins/community/splatoon): upgrade s3si version

This commit is contained in:
lowlighter
2022-12-07 22:18:46 -05:00
parent 0e431bb594
commit 5e2ea64d8a

View File

@@ -1,7 +1,6 @@
// deno-fmt-ignore-file // deno-fmt-ignore-file
// deno-lint-ignore-file // deno-lint-ignore-file
// This code was bundled using `deno bundle` and it's not recommended to edit it manually // This code was bundled using `deno bundle` and it's not recommended to edit it manually
// https://raw.githubusercontent.com/spacemeowx2/s3si.ts/main/s3si.ts
class APIError extends Error { class APIError extends Error {
response; response;
@@ -13,9 +12,9 @@ class APIError extends Error {
} }
} }
const AGENT_NAME = "s3si.ts"; const AGENT_NAME = "s3si.ts";
const S3SI_VERSION = "0.1.36"; const S3SI_VERSION = "0.2.1";
const NSOAPP_VERSION = "2.3.1"; const NSOAPP_VERSION = "2.4.0";
const WEB_VIEW_VERSION = "2.0.0-8a061f6c"; const WEB_VIEW_VERSION = "2.0.0-18810d39";
const S3SI_LINK = "https://github.com/spacemeowx2/s3si.ts"; const S3SI_LINK = "https://github.com/spacemeowx2/s3si.ts";
const USERAGENT = `${AGENT_NAME}/${S3SI_VERSION} (${S3SI_LINK})`; const USERAGENT = `${AGENT_NAME}/${S3SI_VERSION} (${S3SI_LINK})`;
const DEFAULT_APP_USER_AGENT = "Mozilla/5.0 (Linux; Android 11; Pixel 5) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/94.0.4606.61 Mobile Safari/537.36"; const DEFAULT_APP_USER_AGENT = "Mozilla/5.0 (Linux; Android 11; Pixel 5) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/94.0.4606.61 Mobile Safari/537.36";
@@ -5033,6 +5032,11 @@ function s3siGameId(id) {
const tsUuid = fullId.slice(fullId.length - 52, fullId.length); const tsUuid = fullId.slice(fullId.length - 52, fullId.length);
return mod6.v5.generate(S3SI_NAMESPACE, tsUuid); return mod6.v5.generate(S3SI_NAMESPACE, tsUuid);
} }
function s3sCoopGameId(id) {
const fullId = mod.decode(id);
const tsUuid = fullId.slice(fullId.length - 52, fullId.length);
return mod6.v5.generate(COOP_NAMESPACE, tsUuid);
}
function parseHistoryDetailId(id) { function parseHistoryDetailId(id) {
const plainText = new TextDecoder().decode(mod.decode(id)); const plainText = new TextDecoder().decode(mod.decode(id));
const vsRE = /VsHistoryDetail-([a-z0-9-]+):(\w+):(\d{8}T\d{6})_([0-9a-f-]{36})/; const vsRE = /VsHistoryDetail-([a-z0-9-]+):(\w+):(\d{8}T\d{6})_([0-9a-f-]{36})/;
@@ -5460,6 +5464,7 @@ var Queries;
Queries["LatestBattleHistoriesQuery"] = "4f5f26e64bca394b45345a65a2f383bd"; Queries["LatestBattleHistoriesQuery"] = "4f5f26e64bca394b45345a65a2f383bd";
Queries["RegularBattleHistoriesQuery"] = "d5b795d09e67ce153e622a184b7e7dfa"; Queries["RegularBattleHistoriesQuery"] = "d5b795d09e67ce153e622a184b7e7dfa";
Queries["BankaraBattleHistoriesQuery"] = "de4754588109b77dbcb90fbe44b612ee"; Queries["BankaraBattleHistoriesQuery"] = "de4754588109b77dbcb90fbe44b612ee";
Queries["XBattleHistoriesQuery"] = "45c74fefb45a49073207229ca65f0a62";
Queries["PrivateBattleHistoriesQuery"] = "1d6ed57dc8b801863126ad4f351dfb9a"; Queries["PrivateBattleHistoriesQuery"] = "1d6ed57dc8b801863126ad4f351dfb9a";
Queries["VsHistoryDetailQuery"] = "291295ad311b99a6288fc95a5c4cb2d2"; Queries["VsHistoryDetailQuery"] = "291295ad311b99a6288fc95a5c4cb2d2";
Queries["CoopHistoryQuery"] = "6ed02537e4a65bbb5e7f4f23092f6154"; Queries["CoopHistoryQuery"] = "6ed02537e4a65bbb5e7f4f23092f6154";
@@ -5612,6 +5617,9 @@ class Splatnet3 {
const resp = await this.request(Queries.BankaraBattleHistoriesQuery); const resp = await this.request(Queries.BankaraBattleHistoriesQuery);
return resp; return resp;
} }
async getXBattleHistories() {
return await this.request(Queries.XBattleHistoriesQuery);
}
async getCoopHistories() { async getCoopHistories() {
const resp = await this.request(Queries.CoopHistoryQuery); const resp = await this.request(Queries.CoopHistoryQuery);
return resp; return resp;
@@ -5709,6 +5717,16 @@ const COOP_POINT_MAP = {
2: 0, 2: 0,
3: 20 3: 20
}; };
async function checkResponse(resp) {
if (Math.floor(resp.status / 100) !== 2) {
const json = await resp.json().catch(()=>undefined);
throw new APIError({
response: resp,
json,
message: "Failed to fetch data from stat.ink"
});
}
}
class StatInkAPI { class StatInkAPI {
statInk; statInk;
FETCH_LOCK; FETCH_LOCK;
@@ -5719,11 +5737,7 @@ class StatInkAPI {
this.statInk = "https://stat.ink"; this.statInk = "https://stat.ink";
this.FETCH_LOCK = new Mutex(); this.FETCH_LOCK = new Mutex();
this.cache = {}; this.cache = {};
this._specialMap = new Map();
this._salmonWeaponMap = new Map(); this._salmonWeaponMap = new Map();
this.getSpecial = ()=>{
throw new Error("Not implemented");
};
this.getSalmonWeapon = ()=>this._getCached(`${this.statInk}/api/v3/salmon/weapon?full=1`); this.getSalmonWeapon = ()=>this._getCached(`${this.statInk}/api/v3/salmon/weapon?full=1`);
this.getWeapon = ()=>this._getCached(`${this.statInk}/api/v3/weapon?full=1`); this.getWeapon = ()=>this._getCached(`${this.statInk}/api/v3/weapon?full=1`);
this.getAbility = ()=>this._getCached(`${this.statInk}/api/v3/ability?full=1`); this.getAbility = ()=>this._getCached(`${this.statInk}/api/v3/ability?full=1`);
@@ -5744,6 +5758,7 @@ class StatInkAPI {
url: type === "VsInfo" ? `${this.statInk}/api/v3/s3s/uuid-list` : `${this.statInk}/api/v3/salmon/uuid-list`, url: type === "VsInfo" ? `${this.statInk}/api/v3/s3s/uuid-list` : `${this.statInk}/api/v3/salmon/uuid-list`,
headers: this.requestHeaders() headers: this.requestHeaders()
}); });
await checkResponse(response);
const uuidResult = await response.json(); const uuidResult = await response.json();
if (!Array.isArray(uuidResult)) { if (!Array.isArray(uuidResult)) {
throw new APIError({ throw new APIError({
@@ -5819,6 +5834,7 @@ class StatInkAPI {
url, url,
headers: this.requestHeaders() headers: this.requestHeaders()
}); });
await checkResponse(resp);
const json = await resp.json(); const json = await resp.json();
this.cache[url] = json; this.cache[url] = json;
return json; return json;
@@ -5840,25 +5856,6 @@ class StatInkAPI {
]; ];
} }
} }
_specialMap;
async getSpecialMap() {
if (this._specialMap.size === 0) {
const specials = await this.getSpecial();
for (const special of specials){
for (const name of Object.values(special.name).flatMap((n)=>this._getAliasName(n))){
const prevKey = this._specialMap.get(name);
if (prevKey !== undefined && prevKey !== special.key) {
console.warn(`Duplicate weapon name: ${name}`);
}
this._specialMap.set(name, special.key);
}
}
if (this._specialMap.size === 0) {
throw new Error("Failed to get salmon weapon map");
}
}
return this._specialMap;
}
_salmonWeaponMap; _salmonWeaponMap;
async getSalmonWeaponMap() { async getSalmonWeaponMap() {
if (this._salmonWeaponMap.size === 0) { if (this._salmonWeaponMap.size === 0) {
@@ -5878,7 +5875,6 @@ class StatInkAPI {
} }
return this._salmonWeaponMap; return this._salmonWeaponMap;
} }
getSpecial;
getSalmonWeapon; getSalmonWeapon;
getWeapon; getWeapon;
getAbility; getAbility;
@@ -5926,7 +5922,8 @@ class StatInkExporter {
for (const id of list){ for (const id of list){
const s3sId = await gameId(id); const s3sId = await gameId(id);
const s3siId = await s3siGameId(id); const s3siId = await s3siGameId(id);
if (!uuid.includes(s3sId) && !uuid.includes(s3siId)) { const s3sCoopId = await s3sCoopGameId(id);
if (!uuid.includes(s3sId) && !uuid.includes(s3siId) && !uuid.includes(s3sCoopId)) {
out.push(id); out.push(id);
} }
} }
@@ -5960,6 +5957,8 @@ class StatInkExporter {
} else if (modeId === 8) { } else if (modeId === 8) {
throw new Error("Tri-color battle is not supported"); throw new Error("Tri-color battle is not supported");
} }
} else if (vsMode === "X_MATCH") {
return "xmatch";
} }
throw new TypeError(`Unknown vsMode ${vsMode}`); throw new TypeError(`Unknown vsMode ${vsMode}`);
} }
@@ -6021,7 +6020,7 @@ class StatInkExporter {
} }
return result; return result;
}; };
async mapBattle({ challengeProgress , bankaraMatchChallenge , listNode , detail: vsDetail , rankBeforeState , rankState }) { async mapBattle({ groupInfo , challengeProgress , bankaraMatchChallenge , listNode , detail: vsDetail , rankBeforeState , rankState }) {
const { knockout , vsRule: { rule } , myTeam , otherTeams , bankaraMatch , festMatch , playedTime } = vsDetail; const { knockout , vsRule: { rule } , myTeam , otherTeams , bankaraMatch , festMatch , playedTime } = vsDetail;
const self = vsDetail.myTeam.players.find((i)=>i.isMyself); const self = vsDetail.myTeam.players.find((i)=>i.isMyself);
if (!self) { if (!self) {
@@ -6073,7 +6072,7 @@ class StatInkExporter {
result.our_team_count = myTeam?.result?.score ?? undefined; result.our_team_count = myTeam?.result?.score ?? undefined;
result.their_team_count = otherTeams?.[0]?.result?.score ?? undefined; result.their_team_count = otherTeams?.[0]?.result?.score ?? undefined;
result.rank_exp_change = bankaraMatch?.earnedUdemaePoint ?? undefined; result.rank_exp_change = bankaraMatch?.earnedUdemaePoint ?? undefined;
if (listNode) { if (listNode?.udemae) {
[result.rank_before, result.rank_before_s_plus] = parseUdemae(listNode.udemae); [result.rank_before, result.rank_before_s_plus] = parseUdemae(listNode.udemae);
} }
if (bankaraMatchChallenge && challengeProgress) { if (bankaraMatchChallenge && challengeProgress) {
@@ -6085,9 +6084,17 @@ class StatInkExporter {
result.rank_after = result.rank_before; result.rank_after = result.rank_before;
result.rank_after_s_plus = result.rank_before_s_plus; result.rank_after_s_plus = result.rank_before_s_plus;
} }
}
if (challengeProgress) {
result.challenge_win = challengeProgress.winCount; result.challenge_win = challengeProgress.winCount;
result.challenge_lose = challengeProgress.loseCount; result.challenge_lose = challengeProgress.loseCount;
} }
if (vsDetail.xMatch) {
result.x_power_before = result.x_power_after = vsDetail.xMatch.lastXPower;
if (groupInfo?.xMatchMeasurement && groupInfo?.xMatchMeasurement.state === "COMPLETED" && challengeProgress?.index === 0) {
result.x_power_after = groupInfo.xMatchMeasurement.xPowerAfter;
}
}
if (rankBeforeState && rankState) { if (rankBeforeState && rankState) {
result.rank_before_exp = rankBeforeState.rankPoint; result.rank_before_exp = rankBeforeState.rankPoint;
result.rank_after_exp = rankState.rankPoint; result.rank_after_exp = rankState.rankPoint;
@@ -6103,22 +6110,22 @@ class StatInkExporter {
} }
return result; return result;
} }
isRandomWeapon(image) { isRandom(image) {
const RANDOM_WEAPON_FILENAME = "473fffb2442075078d8bb7125744905abdeae651b6a5b7453ae295582e45f7d1"; const RANDOM_FILENAME = "473fffb2442075078d8bb7125744905abdeae651b6a5b7453ae295582e45f7d1";
const url = image?.url; const url = image?.url;
if (typeof url === "string") { if (typeof url === "string") {
return url.includes(RANDOM_WEAPON_FILENAME); return url.includes(RANDOM_FILENAME);
} else if (url === undefined || url === null) { } else if (url === undefined || url === null) {
return false; return false;
} else { } else {
return url.pathname.includes(RANDOM_WEAPON_FILENAME); return url.pathname.includes(RANDOM_FILENAME);
} }
} }
async mapCoopWeapon({ name , image }) { async mapCoopWeapon({ name , image }) {
const weaponMap = await this.api.getSalmonWeaponMap(); const weaponMap = await this.api.getSalmonWeaponMap();
const weapon = weaponMap.get(name); const weapon = weaponMap.get(name);
if (!weapon) { if (!weapon) {
if (this.isRandomWeapon(image)) { if (this.isRandom(image)) {
return null; return null;
} }
throw new Error(`Weapon not found: ${name}`); throw new Error(`Weapon not found: ${name}`);
@@ -6131,13 +6138,23 @@ class StatInkExporter {
const hash = /\/(\w+)_0\.\w+/.exec(imageName)?.[1] ?? ""; const hash = /\/(\w+)_0\.\w+/.exec(imageName)?.[1] ?? "";
const special = SPLATNET3_STATINK_MAP.COOP_SPECIAL_MAP[hash]; const special = SPLATNET3_STATINK_MAP.COOP_SPECIAL_MAP[hash];
if (!special) { if (!special) {
if (this.isRandom(image)) {
return Promise.resolve(undefined);
}
throw new Error(`Special not found: ${name} (${imageName})`); throw new Error(`Special not found: ${name} (${imageName})`);
} }
return Promise.resolve(special); return Promise.resolve(special);
} }
async mapCoopPlayer({ player , weapons , specialWeapon , defeatEnemyCount , deliverCount , goldenAssistCount , goldenDeliverCount , rescueCount , rescuedCount }) { async mapCoopPlayer(isMyself, { player , weapons , specialWeapon , defeatEnemyCount , deliverCount , goldenAssistCount , goldenDeliverCount , rescueCount , rescuedCount }) {
const disconnected = [
goldenDeliverCount,
deliverCount,
rescueCount,
rescuedCount,
defeatEnemyCount
].every((v)=>v === 0) || !specialWeapon;
return { return {
me: player.isMyself ? "yes" : "no", me: isMyself ? "yes" : "no",
name: player.name, name: player.name,
number: player.nameId, number: player.nameId,
splashtag_title: player.byname, splashtag_title: player.byname,
@@ -6150,7 +6167,7 @@ class StatInkExporter {
rescue: rescueCount, rescue: rescueCount,
rescued: rescuedCount, rescued: rescuedCount,
defeat_boss: defeatEnemyCount, defeat_boss: defeatEnemyCount,
disconnected: specialWeapon ? "no" : "yes" disconnected: disconnected ? "yes" : "no"
}; };
} }
mapKing(id) { mapKing(id) {
@@ -6162,7 +6179,9 @@ class StatInkExporter {
} }
async mapWave(wave) { async mapWave(wave) {
const event = wave.eventWave ? SPLATNET3_STATINK_MAP.COOP_EVENT_MAP[b64Number(wave.eventWave.id)] : undefined; const event = wave.eventWave ? SPLATNET3_STATINK_MAP.COOP_EVENT_MAP[b64Number(wave.eventWave.id)] : undefined;
const special_uses = (await Promise.all(wave.specialWeapons.map((w)=>this.mapSpecial(w)))).reduce((p, key)=>({ const special_uses = (await Promise.all(wave.specialWeapons.map((w)=>this.mapSpecial(w)))).flatMap((key)=>key ? [
key
] : []).reduce((p, key)=>({
...p, ...p,
[key]: (p[key] ?? 0) + 1 [key]: (p[key] ?? 0) + 1
}), {}); }), {});
@@ -6199,12 +6218,16 @@ class StatInkExporter {
let title_before = undefined; let title_before = undefined;
let title_exp_before = undefined; let title_exp_before = undefined;
const expDiff = COOP_POINT_MAP[clear_waves]; const expDiff = COOP_POINT_MAP[clear_waves];
if (nonNullable(title_exp_after) && nonNullable(expDiff)) { if (nonNullable(title_after) && nonNullable(title_exp_after) && nonNullable(expDiff)) {
if (title_exp_after === 40 && expDiff === 20) {} else if (title_exp_after === 40 && expDiff < 0 && title_after !== "8") {} else if (title_exp_after === 999 && expDiff !== 0) { if (title_exp_after === 40 && expDiff === 20) {} else if (title_exp_after === 40 && expDiff < 0 && title_after !== "8") {} else if (title_exp_after === 999 && expDiff !== 0) {
title_before = title_after; title_before = title_after;
} else { } else {
title_before = title_after; if (title_exp_after - expDiff >= 0) {
title_exp_before = title_exp_after - expDiff; title_before = title_after;
title_exp_before = title_exp_after - expDiff;
} else {
title_before = (parseInt(title_after) - 1).toString();
}
} }
} }
let fail_reason = null; let fail_reason = null;
@@ -6240,8 +6263,8 @@ class StatInkExporter {
job_bonus: detail.jobBonus, job_bonus: detail.jobBonus,
waves: await Promise.all(detail.waveResults.map((w)=>this.mapWave(w))), waves: await Promise.all(detail.waveResults.map((w)=>this.mapWave(w))),
players: await Promise.all([ players: await Promise.all([
this.mapCoopPlayer(myResult), this.mapCoopPlayer(true, myResult),
...memberResults.map((p)=>this.mapCoopPlayer(p)) ...memberResults.map((p)=>this.mapCoopPlayer(false, p))
]), ]),
bosses, bosses,
agent: AGENT_NAME, agent: AGENT_NAME,
@@ -6387,7 +6410,7 @@ const splusParams = ()=>{
300 + level * 350, 300 + level * 350,
300 + (level + 1) * 350 300 + (level + 1) * 350
], ],
charge: 160 charge: 180
}; };
if (level === 9) { if (level === 9) {
item.promotion = true; item.promotion = true;
@@ -6400,7 +6423,7 @@ const splusParams = ()=>{
0, 0,
9999 9999
], ],
charge: 160 charge: 180
}); });
return out; return out;
}; };
@@ -6461,7 +6484,7 @@ const RANK_PARAMS = [
200, 200,
500 500
], ],
charge: 100 charge: 110
}, },
{ {
rank: "A", rank: "A",
@@ -6469,7 +6492,7 @@ const RANK_PARAMS = [
500, 500,
800 800
], ],
charge: 110 charge: 120
}, },
{ {
rank: "A+", rank: "A+",
@@ -6477,7 +6500,7 @@ const RANK_PARAMS = [
800, 800,
1100 1100
], ],
charge: 120, charge: 130,
promotion: true promotion: true
}, },
{ {
@@ -6486,14 +6509,14 @@ const RANK_PARAMS = [
300, 300,
1000 1000
], ],
charge: 150, charge: 170,
promotion: true promotion: true
}, },
...splusParams() ...splusParams()
]; ];
function addRank(state, delta) { function addRank(state, delta) {
const { rank , rankPoint } = state; const { rank , rankPoint } = state;
const { gameId , rankAfter , isPromotion , isRankUp , isChallengeFirst } = delta; const { gameId , timestamp , rankAfter , isPromotion , isRankUp , isChallengeFirst } = delta;
const rankIndex = RANK_PARAMS.findIndex((r)=>r.rank === rank); const rankIndex = RANK_PARAMS.findIndex((r)=>r.rank === rank);
if (rankIndex === -1) { if (rankIndex === -1) {
throw new Error(`Rank not found: ${rank}`); throw new Error(`Rank not found: ${rank}`);
@@ -6502,12 +6525,14 @@ function addRank(state, delta) {
if (isChallengeFirst) { if (isChallengeFirst) {
return { return {
gameId, gameId,
timestamp,
rank, rank,
rankPoint: rankPoint - rankParam.charge rankPoint: rankPoint - rankParam.charge
}; };
} }
if (rankIndex === RANK_PARAMS.length - 1) { if (rankIndex === RANK_PARAMS.length - 1) {
return { return {
timestamp,
gameId, gameId,
rank, rank,
rankPoint: Math.min(rankPoint + delta.rankPoint, rankParam.pointRange[1]) rankPoint: Math.min(rankPoint + delta.rankPoint, rankParam.pointRange[1])
@@ -6517,12 +6542,14 @@ function addRank(state, delta) {
const nextRankParam = RANK_PARAMS[rankIndex + 1]; const nextRankParam = RANK_PARAMS[rankIndex + 1];
return { return {
gameId, gameId,
timestamp,
rank: nextRankParam.rank, rank: nextRankParam.rank,
rankPoint: nextRankParam.pointRange[0] rankPoint: nextRankParam.pointRange[0]
}; };
} }
return { return {
gameId, gameId,
timestamp,
rank: rankAfter ?? rank, rank: rankAfter ?? rank,
rankPoint: rankPoint + delta.rankPoint rankPoint: rankPoint + delta.rankPoint
}; };
@@ -6547,6 +6574,7 @@ function generateDeltaList(state, flatten) {
let delta = { let delta = {
beforeGameId, beforeGameId,
gameId: i.gameId, gameId: i.gameId,
timestamp: Math.floor(i.time.getTime() / 1000),
rankPoint: 0, rankPoint: 0,
isPromotion: false, isPromotion: false,
isRankUp: false, isRankUp: false,
@@ -6582,12 +6610,16 @@ function generateDeltaList(state, flatten) {
} }
function getRankState(i) { function getRankState(i) {
const rank = i.detail.udemae; const rank = i.detail.udemae;
if (!rank) {
throw new Error("rank must be defined");
}
const param = RANK_PARAMS.find((i)=>i.rank === rank); const param = RANK_PARAMS.find((i)=>i.rank === rank);
if (!param) { if (!param) {
throw new Error(`Rank not found: ${rank}`); throw new Error(`Rank not found: ${rank}`);
} }
return { return {
gameId: i.gameId, gameId: i.gameId,
timestamp: Math.floor(i.time.getTime() / 1000),
rank, rank,
rankPoint: -1 rankPoint: -1
}; };
@@ -6627,6 +6659,7 @@ class RankTracker {
async updateState(history) { async updateState(history) {
const flatten = await Promise.all(history.flatMap(({ historyDetails , bankaraMatchChallenge })=>{ const flatten = await Promise.all(history.flatMap(({ historyDetails , bankaraMatchChallenge })=>{
return historyDetails.nodes.map((j, index)=>({ return historyDetails.nodes.map((j, index)=>({
id: j.id,
time: battleTime(j.id), time: battleTime(j.id),
gameId: gameId(j.id), gameId: gameId(j.id),
bankaraMatchChallenge, bankaraMatchChallenge,
@@ -6683,6 +6716,8 @@ class GameFetcher {
bankaraHistory; bankaraHistory;
coopLock = new Mutex(); coopLock = new Mutex();
coopHistory; coopHistory;
xMatchLock = new Mutex();
xMatchHistory;
constructor({ cache =new MemoryCache() , splatnet , state }){ constructor({ cache =new MemoryCache() , splatnet , state }){
this._splatnet = splatnet; this._splatnet = splatnet;
this.cache = cache; this.cache = cache;
@@ -6712,7 +6747,23 @@ class GameFetcher {
getRankStateById(id) { getRankStateById(id) {
return this.rankTracker.getRankStateById(id); return this.rankTracker.getRankStateById(id);
} }
getXMatchHistory() {
if (!this._splatnet) {
return [];
}
return this.xMatchLock.use(async ()=>{
if (this.xMatchHistory) {
return this.xMatchHistory;
}
const { xBattleHistories: { historyGroups } } = await this.splatnet.getXBattleHistories();
this.xMatchHistory = historyGroups.nodes;
return this.xMatchHistory;
});
}
getBankaraHistory() { getBankaraHistory() {
if (!this._splatnet) {
return [];
}
return this.bankaraLock.use(async ()=>{ return this.bankaraLock.use(async ()=>{
if (this.bankaraHistory) { if (this.bankaraHistory) {
return this.bankaraHistory; return this.bankaraHistory;
@@ -6723,6 +6774,9 @@ class GameFetcher {
}); });
} }
getCoopHistory() { getCoopHistory() {
if (!this._splatnet) {
return [];
}
return this.coopLock.use(async ()=>{ return this.coopLock.use(async ()=>{
if (this.coopHistory) { if (this.coopHistory) {
return this.coopHistory; return this.coopHistory;
@@ -6750,16 +6804,20 @@ class GameFetcher {
groupInfo groupInfo
}; };
} }
async getBattleMetaById(id) { async getBattleMetaById(id, vsMode) {
const gid = await gameId(id); const gid = await gameId(id);
const bankaraHistory = this._splatnet ? await this.getBankaraHistory() : [];
const gameIdMap = new Map(); const gameIdMap = new Map();
for (const i of bankaraHistory){ let group = null;
for (const j of i.historyDetails.nodes){ let listNode = null;
gameIdMap.set(j, await gameId(j.id)); if (vsMode === "BANKARA" || vsMode === "X_MATCH") {
const bankaraHistory = vsMode === "BANKARA" ? await this.getBankaraHistory() : await this.getXMatchHistory();
for (const i of bankaraHistory){
for (const j of i.historyDetails.nodes){
gameIdMap.set(j, await gameId(j.id));
}
} }
group = bankaraHistory.find((i)=>i.historyDetails.nodes.some((i)=>gameIdMap.get(i) === gid)) ?? null;
} }
const group = bankaraHistory.find((i)=>i.historyDetails.nodes.some((i)=>gameIdMap.get(i) === gid));
if (!group) { if (!group) {
return { return {
type: "VsInfo", type: "VsInfo",
@@ -6767,16 +6825,19 @@ class GameFetcher {
bankaraMatchChallenge: null, bankaraMatchChallenge: null,
listNode: null, listNode: null,
rankState: null, rankState: null,
rankBeforeState: null rankBeforeState: null,
groupInfo: null
}; };
} }
const { bankaraMatchChallenge } = group; const { bankaraMatchChallenge , xMatchMeasurement } = group;
const listNode = group.historyDetails.nodes.find((i)=>gameIdMap.get(i) === gid) ?? null; const { historyDetails , ...groupInfo } = group;
const index = group.historyDetails.nodes.indexOf(listNode); listNode = historyDetails.nodes.find((i)=>gameIdMap.get(i) === gid) ?? null;
const index = historyDetails.nodes.indexOf(listNode);
let challengeProgress = null; let challengeProgress = null;
if (bankaraMatchChallenge) { const challengeOrMeasurement = bankaraMatchChallenge || xMatchMeasurement;
const pastBattles = group.historyDetails.nodes.slice(0, index); if (challengeOrMeasurement) {
const { winCount , loseCount } = bankaraMatchChallenge; const pastBattles = historyDetails.nodes.slice(0, index);
const { winCount , loseCount } = challengeOrMeasurement;
challengeProgress = { challengeProgress = {
index, index,
winCount: winCount - pastBattles.filter((i)=>i.judgement == "WIN").length, winCount: winCount - pastBattles.filter((i)=>i.judgement == "WIN").length,
@@ -6793,7 +6854,8 @@ class GameFetcher {
listNode, listNode,
challengeProgress, challengeProgress,
rankState: after ?? null, rankState: after ?? null,
rankBeforeState: before ?? null rankBeforeState: before ?? null,
groupInfo
}; };
} }
cacheDetail(id, getter) { cacheDetail(id, getter) {
@@ -6820,7 +6882,7 @@ class GameFetcher {
} }
async fetchBattle(id) { async fetchBattle(id) {
const detail = await this.cacheDetail(id, ()=>this.splatnet.getBattleDetail(id).then((r)=>r.vsHistoryDetail)); const detail = await this.cacheDetail(id, ()=>this.splatnet.getBattleDetail(id).then((r)=>r.vsHistoryDetail));
const metadata = await this.getBattleMetaById(id); const metadata = await this.getBattleMetaById(id, detail.vsMode.mode);
const game = { const game = {
...metadata, ...metadata,
detail detail