feat: add X match types

main
spacemeowx2 2022-12-01 15:52:04 +08:00 committed by imspace
parent 8f1d1429bd
commit 057bfa508e
4 changed files with 113 additions and 29 deletions

View File

@ -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,

View File

@ -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}`);

View File

@ -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);

View File

@ -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"