feat: add X match types
parent
8f1d1429bd
commit
057bfa508e
|
|
@ -7,8 +7,9 @@ import {
|
|||
CoopHistoryGroups,
|
||||
CoopInfo,
|
||||
Game,
|
||||
HistoryGroups,
|
||||
HistoryGroupItem,
|
||||
VsInfo,
|
||||
VsMode,
|
||||
} from "./types.ts";
|
||||
import { Cache, MemoryCache } from "./cache.ts";
|
||||
import { gameId } from "./utils.ts";
|
||||
|
|
@ -25,9 +26,11 @@ export class GameFetcher {
|
|||
|
||||
private lock: Record<string, Mutex | undefined> = {};
|
||||
private bankaraLock = new Mutex();
|
||||
private bankaraHistory?: HistoryGroups<BattleListNode>["nodes"];
|
||||
private bankaraHistory?: HistoryGroupItem<BattleListNode>[];
|
||||
private coopLock = new Mutex();
|
||||
private coopHistory?: CoopHistoryGroups["nodes"];
|
||||
private xMatchLock = new Mutex();
|
||||
private xMatchHistory?: HistoryGroupItem<BattleListNode>[];
|
||||
|
||||
constructor(
|
||||
{ cache = new MemoryCache(), splatnet, state }: {
|
||||
|
|
@ -72,7 +75,27 @@ export class GameFetcher {
|
|||
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() {
|
||||
if (!this._splatnet) {
|
||||
return [];
|
||||
}
|
||||
return this.bankaraLock.use(async () => {
|
||||
if (this.bankaraHistory) {
|
||||
return this.bankaraHistory;
|
||||
|
|
@ -87,6 +110,9 @@ export class GameFetcher {
|
|||
});
|
||||
}
|
||||
getCoopHistory() {
|
||||
if (!this._splatnet) {
|
||||
return [];
|
||||
}
|
||||
return this.coopLock.use(async () => {
|
||||
if (this.coopHistory) {
|
||||
return this.coopHistory;
|
||||
|
|
@ -124,20 +150,33 @@ export class GameFetcher {
|
|||
groupInfo,
|
||||
};
|
||||
}
|
||||
async getBattleMetaById(id: string): Promise<Omit<VsInfo, "detail">> {
|
||||
async getBattleMetaById(
|
||||
id: string,
|
||||
vsMode: VsMode,
|
||||
): Promise<Omit<VsInfo, "detail">> {
|
||||
const gid = await gameId(id);
|
||||
const bankaraHistory = this._splatnet ? await this.getBankaraHistory() : [];
|
||||
|
||||
const gameIdMap = new Map<BattleListNode, string>();
|
||||
let group: HistoryGroupItem<BattleListNode> | null = null;
|
||||
let listNode: BattleListNode | null = null;
|
||||
|
||||
for (const i of bankaraHistory) {
|
||||
for (const j of i.historyDetails.nodes) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const group = bankaraHistory.find((i) =>
|
||||
i.historyDetails.nodes.some((i) => gameIdMap.get(i) === gid)
|
||||
);
|
||||
group = bankaraHistory.find((i) =>
|
||||
i.historyDetails.nodes.some((i) =>
|
||||
gameIdMap.get(i) === gid
|
||||
)
|
||||
) ?? null;
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
return {
|
||||
|
|
@ -147,19 +186,21 @@ export class GameFetcher {
|
|||
listNode: null,
|
||||
rankState: null,
|
||||
rankBeforeState: null,
|
||||
groupInfo: null,
|
||||
};
|
||||
}
|
||||
|
||||
const { bankaraMatchChallenge } = group;
|
||||
const listNode =
|
||||
group.historyDetails.nodes.find((i) => gameIdMap.get(i) === gid) ??
|
||||
null;
|
||||
const index = group.historyDetails.nodes.indexOf(listNode!);
|
||||
const { bankaraMatchChallenge, xMatchMeasurement } = group;
|
||||
const { historyDetails, ...groupInfo } = group;
|
||||
listNode = historyDetails.nodes.find((i) => gameIdMap.get(i) === gid) ??
|
||||
null;
|
||||
const index = historyDetails.nodes.indexOf(listNode!);
|
||||
|
||||
let challengeProgress: null | ChallengeProgress = null;
|
||||
if (bankaraMatchChallenge) {
|
||||
const pastBattles = group.historyDetails.nodes.slice(0, index);
|
||||
const { winCount, loseCount } = bankaraMatchChallenge;
|
||||
const challengeOrMeasurement = bankaraMatchChallenge || xMatchMeasurement;
|
||||
if (challengeOrMeasurement) {
|
||||
const pastBattles = historyDetails.nodes.slice(0, index);
|
||||
const { winCount, loseCount } = challengeOrMeasurement;
|
||||
challengeProgress = {
|
||||
index,
|
||||
winCount: winCount -
|
||||
|
|
@ -171,7 +212,8 @@ export class GameFetcher {
|
|||
};
|
||||
}
|
||||
|
||||
const { before, after } = await this.rankTracker.getRankStateById(id) ?? {};
|
||||
const { before, after } = await this.rankTracker.getRankStateById(id) ??
|
||||
{};
|
||||
|
||||
return {
|
||||
type: "VsInfo",
|
||||
|
|
@ -180,6 +222,7 @@ export class GameFetcher {
|
|||
challengeProgress,
|
||||
rankState: after ?? null,
|
||||
rankBeforeState: before ?? null,
|
||||
groupInfo,
|
||||
};
|
||||
}
|
||||
private cacheDetail<T>(
|
||||
|
|
@ -216,7 +259,7 @@ export class GameFetcher {
|
|||
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: VsInfo = {
|
||||
...metadata,
|
||||
|
|
|
|||
|
|
@ -348,6 +348,8 @@ export class StatInkExporter implements GameExporter {
|
|||
} else if (modeId === 8) {
|
||||
throw new Error("Tri-color battle is not supported");
|
||||
}
|
||||
} else if (vsMode === "X_MATCH") {
|
||||
return "xmatch";
|
||||
}
|
||||
|
||||
throw new TypeError(`Unknown vsMode ${vsMode}`);
|
||||
|
|
|
|||
|
|
@ -196,6 +196,10 @@ export class Splatnet3 {
|
|||
return resp;
|
||||
}
|
||||
|
||||
async getXBattleHistories() {
|
||||
return await this.request(Queries.XBattleHistoriesQuery);
|
||||
}
|
||||
|
||||
async getCoopHistories() {
|
||||
const resp = await this.request(Queries.CoopHistoryQuery);
|
||||
|
||||
|
|
|
|||
51
src/types.ts
51
src/types.ts
|
|
@ -5,6 +5,7 @@ export enum Queries {
|
|||
LatestBattleHistoriesQuery = "4f5f26e64bca394b45345a65a2f383bd",
|
||||
RegularBattleHistoriesQuery = "d5b795d09e67ce153e622a184b7e7dfa",
|
||||
BankaraBattleHistoriesQuery = "de4754588109b77dbcb90fbe44b612ee",
|
||||
XBattleHistoriesQuery = "45c74fefb45a49073207229ca65f0a62",
|
||||
PrivateBattleHistoriesQuery = "1d6ed57dc8b801863126ad4f351dfb9a",
|
||||
VsHistoryDetailQuery = "291295ad311b99a6288fc95a5c4cb2d2",
|
||||
CoopHistoryQuery = "6ed02537e4a65bbb5e7f4f23092f6154",
|
||||
|
|
@ -20,6 +21,7 @@ export type VarsMap = {
|
|||
[Queries.LatestBattleHistoriesQuery]: [];
|
||||
[Queries.RegularBattleHistoriesQuery]: [];
|
||||
[Queries.BankaraBattleHistoriesQuery]: [];
|
||||
[Queries.XBattleHistoriesQuery]: [];
|
||||
[Queries.PrivateBattleHistoriesQuery]: [];
|
||||
[Queries.VsHistoryDetailQuery]: [{
|
||||
vsResultId: string;
|
||||
|
|
@ -50,6 +52,21 @@ export type BankaraMatchChallenge = {
|
|||
udemaeAfter: string | null;
|
||||
earnedUdemaePoint: number | null;
|
||||
};
|
||||
export type XMatchMeasurement = {
|
||||
state: "COMPLETED" | "INPROGRESS";
|
||||
xPowerAfter: null | number;
|
||||
isInitial: boolean;
|
||||
winCount: number;
|
||||
loseCount: number;
|
||||
maxInitialBattleCount: number;
|
||||
maxWinCount: number;
|
||||
maxLoseCount: number;
|
||||
vsRule: {
|
||||
name: string;
|
||||
rule: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
export type BattleListNode = {
|
||||
id: string;
|
||||
udemae: string;
|
||||
|
|
@ -61,15 +78,18 @@ export type BattleListNode = {
|
|||
export type CoopListNode = {
|
||||
id: string;
|
||||
};
|
||||
export type HistoryGroups<T> = {
|
||||
nodes: {
|
||||
bankaraMatchChallenge: null | BankaraMatchChallenge;
|
||||
export type HistoryGroupItem<T> = {
|
||||
bankaraMatchChallenge: null | BankaraMatchChallenge;
|
||||
xMatchMeasurement: null | XMatchMeasurement;
|
||||
|
||||
historyDetails: {
|
||||
nodes: T[];
|
||||
};
|
||||
}[];
|
||||
historyDetails: {
|
||||
nodes: T[];
|
||||
};
|
||||
};
|
||||
export type Nodes<T> = {
|
||||
nodes: T[];
|
||||
};
|
||||
export type HistoryGroups<T> = Nodes<HistoryGroupItem<T>>;
|
||||
export type CoopHistoryGroup = {
|
||||
startTime: null | string;
|
||||
endTime: null | string;
|
||||
|
|
@ -159,6 +179,7 @@ export type ChallengeProgress = {
|
|||
// With challenge info
|
||||
export type VsInfo = {
|
||||
type: "VsInfo";
|
||||
groupInfo: null | Omit<HistoryGroupItem<BattleListNode>, "historyDetails">;
|
||||
listNode: null | BattleListNode;
|
||||
bankaraMatchChallenge: null | BankaraMatchChallenge;
|
||||
challengeProgress: null | ChallengeProgress;
|
||||
|
|
@ -174,6 +195,7 @@ export type CoopInfo = {
|
|||
detail: CoopHistoryDetail;
|
||||
};
|
||||
export type Game = VsInfo | CoopInfo;
|
||||
export type VsMode = "REGULAR" | "BANKARA" | "PRIVATE" | "FEST" | "X_MATCH";
|
||||
export type VsHistoryDetail = {
|
||||
id: string;
|
||||
vsRule: {
|
||||
|
|
@ -183,13 +205,16 @@ export type VsHistoryDetail = {
|
|||
};
|
||||
vsMode: {
|
||||
id: string;
|
||||
mode: "REGULAR" | "BANKARA" | "PRIVATE" | "FEST";
|
||||
mode: VsMode;
|
||||
};
|
||||
vsStage: {
|
||||
id: string;
|
||||
name: string;
|
||||
image: Image;
|
||||
};
|
||||
xMatch: null | {
|
||||
lastXPower: null | number;
|
||||
};
|
||||
playedTime: string; // 2021-01-01T00:00:00Z
|
||||
|
||||
bankaraMatch: {
|
||||
|
|
@ -331,6 +356,12 @@ export type BankaraBattleHistories = {
|
|||
};
|
||||
};
|
||||
|
||||
export type XBattleHistories = {
|
||||
xBattleHistories: {
|
||||
historyGroups: HistoryGroups<BattleListNode>;
|
||||
};
|
||||
};
|
||||
|
||||
export type RespMap = {
|
||||
[Queries.HomeQuery]: {
|
||||
currentPlayer: {
|
||||
|
|
@ -361,6 +392,7 @@ export type RespMap = {
|
|||
};
|
||||
};
|
||||
[Queries.BankaraBattleHistoriesQuery]: BankaraBattleHistories;
|
||||
[Queries.XBattleHistoriesQuery]: XBattleHistories;
|
||||
[Queries.PrivateBattleHistoriesQuery]: {
|
||||
privateBattleHistories: {
|
||||
historyGroups: HistoryGroups<BattleListNode>;
|
||||
|
|
@ -698,6 +730,7 @@ export type StatInkPostBody = {
|
|||
| "regular"
|
||||
| "bankara_challenge"
|
||||
| "bankara_open"
|
||||
| "xmatch"
|
||||
| "splatfest_challenge"
|
||||
| "splatfest_open"
|
||||
| "private";
|
||||
|
|
@ -732,6 +765,8 @@ export type StatInkPostBody = {
|
|||
rank_up_battle?: "yes" | "no"; // Set "yes" if now "Rank-up Battle" mode.
|
||||
challenge_win?: number; // Win count for Anarchy (Series) If rank_up_battle is truthy("yes"), the value range is limited to [0, 3].
|
||||
challenge_lose?: number;
|
||||
x_power_before?: number;
|
||||
x_power_after?: number;
|
||||
fest_power?: number; // Splatfest Power (Pro)
|
||||
fest_dragon?:
|
||||
| "10x"
|
||||
|
|
|
|||
Loading…
Reference in New Issue