feat: support sdvxInGameID as a matchType

this allows ANY_INF to be passed as a difficulty, which is convenient
for scripts that import from services that consider all of INF/GRV/HVN...
to be equivalent
This commit is contained in:
zkldi
2022-10-14 00:04:34 +01:00
parent 7591ad69b2
commit 28116909a1
6 changed files with 197 additions and 23 deletions
+1 -1
View File
@@ -1500,7 +1500,7 @@ export type BatchManualScore<I extends IDStrings = IDStrings> = {
difficulty?: undefined; // hack to stop ts from screaming when this is accessed sometimes
}
| {
matchType: "ddrSongHash" | "inGameID" | "songTitle" | "tachiSongID";
matchType: "ddrSongHash" | "inGameID" | "sdvxInGameID" | "songTitle" | "tachiSongID";
difficulty: Difficulties[I];
}
);
+21 -13
View File
@@ -90,7 +90,7 @@ The properties are described as this:
#### Match Type
There are five match types, and they all use identifier
There are many match types, and they all use identifier
in a different way.
- songTitle
@@ -124,23 +124,31 @@ This match type can only be used for BMS.
This looks for the chart SHA1 that USC uses. As expected, this
can only be used for USC.
- ddrSongHash
This looks for the DDR 'song hash'. This is an identifier
used on the e-amusement website, and references a song,
not a chart. As such:
This match type *necessitates* that `difficulty` be defined
and set to a valid difficulty for DDR.
This match type can only be used for DDR.
- inGameID
This uses the in-game-ID for this **SONG**. You, therefore,
**MUST** specify the difficulty for this chart aswell.
At the moment, this match type can only be used on `iidx`.
This is expected to change before release.
This is supported for the following games:
- IIDX
- Pop'n Music
- Jubeat
- CHUNITHM
- GITADORA
- maimai
- MUSECA
- sdvxInGameID
This uses the in-game-ID for this SDVX song. You must specify
the difficulty for this chart aswell.
The reason SDVX gets its own special `matchType` is because this
matchType supports `difficulty: "ANY_INF"`. This special difficulty
means that it will check for any of `INF/GRV/HVN/VVD/XCD` for this song.
This is useful for services that store all of those as the same difficulty.
## Example
@@ -1,13 +1,20 @@
import { ConverterBatchManual, ResolveChartFromSong, ResolveMatchTypeToKTData } from "./converter";
import { InvalidScoreFailure } from "../../../framework/common/converter-failures";
import deepmerge from "deepmerge";
import db from "external/mongo/db";
import CreateLogCtx from "lib/logger/logger";
import t from "tap";
import ResetDBState from "test-utils/resets";
import { BMSGazerChart, BMSGazerSong, Testing511Song, Testing511SPA } from "test-utils/test-data";
import {
BMSGazerChart,
BMSGazerSong,
Testing511Song,
Testing511SPA,
TestingSDVXAlbidaChart,
} from "test-utils/test-data";
import { EscapeStringRegexp } from "utils/misc";
import type { BatchManualContext } from "./types";
import type { BatchManualScore, Game } from "tachi-common";
import type { BatchManualScore, ChartDocument, Game } from "tachi-common";
const baseBatchManualScore = {
score: 500,
@@ -103,6 +110,79 @@ t.test("#ResolveMatchTypeToKTData", (t) => {
t.end();
});
t.test("Should resolve for the sdvx inGameID if matchType is sdvxInGameID", async (t) => {
const res = await ResolveMatchTypeToKTData(
{
matchType: "sdvxInGameID",
identifier: "1",
difficulty: "ADV",
lamp: "CLEAR",
score: 9_000_001,
},
context,
importType,
logger
);
t.hasStrict(
res,
{ song: { id: 1 }, chart: { data: { inGameID: 1 } } },
"Should return the right song and chart."
);
t.rejects(
() =>
ResolveMatchTypeToKTData(
{
matchType: "sdvxInGameID",
identifier: "9999999",
difficulty: "ADV",
lamp: "CLEAR",
score: 9_000_001,
},
context,
importType,
logger
),
ktdWrap("Cannot find SDVX chart with inGameID 9999999")
);
t.end();
});
t.test("Should support ANY_INF if matchType is sdvxInGameID", async (t) => {
await db.charts.sdvx.insert(
deepmerge(TestingSDVXAlbidaChart, {
chartID: "fake_xcd",
data: {
arcChartID: "fake222",
},
difficulty: "XCD",
} as ChartDocument<"sdvx:Single">)
);
const res = await ResolveMatchTypeToKTData(
{
matchType: "sdvxInGameID",
identifier: "1",
difficulty: "ANY_INF",
lamp: "CLEAR",
score: 9_000_001,
},
context,
importType,
logger
);
t.hasStrict(
res,
{ song: { id: 1 }, chart: { data: { inGameID: 1 }, difficulty: "XCD" } },
"Should return the right song and chart."
);
t.end();
});
t.test("Should resolve for the bms chartHash if the matchType is bmsChartHash", async (t) => {
const GAZER17MD5 = "38616b85332037cc12924f2ae2840262";
const GAZER17SHA256 = "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d";
@@ -9,12 +9,15 @@ import {
AssertStrAsPositiveInt,
} from "../../../framework/common/string-asserts";
import db from "external/mongo/db";
import { GetGamePTConfig } from "tachi-common";
import {
FindBMSChartOnHash,
FindChartWithPTDF,
FindChartWithPTDFVersion,
FindDDRChartOnSongHash,
FindITGChartOnHash,
FindSDVXChartOnInGameID,
FindSDVXChartOnInGameIDVersion,
} from "utils/queries/charts";
import { FindSongOnID, FindSongOnTitleInsensitive } from "utils/queries/songs";
import type { DryScore } from "../../../framework/common/types";
@@ -28,6 +31,8 @@ import type {
IDStrings,
ImportTypes,
SongDocument,
Difficulties,
GPTSupportedVersions,
} from "tachi-common";
/**
@@ -312,12 +317,71 @@ export async function ResolveMatchTypeToKTData(
return { song, chart };
}
case "sdvxInGameID": {
if (game !== "sdvx") {
throw new InvalidScoreFailure(
`A matchType of sdvxInGameID is only supported by SDVX.`
);
}
let chart: ChartDocument | null;
const identifier = Number(data.identifier);
const config = GetGamePTConfig("sdvx", "Single");
if (!config.difficulties.includes(data.difficulty) && data.difficulty !== "ANY_INF") {
throw new InvalidScoreFailure(
`Invalid difficulty '${
data.difficulty
}', Expected any of ${config.difficulties.join(", ")} or ANY_INF`
);
}
const diff = data.difficulty as Difficulties["sdvx:Single"] | "ANY_INF";
if (context.version) {
if (!config.supportedVersions.includes(context.version)) {
throw new InvalidScoreFailure(
`Unsupported version ${
context.version
}. Expected any of ${config.supportedVersions.join(", ")}.`
);
}
chart = await FindSDVXChartOnInGameIDVersion(
identifier,
diff,
context.version as GPTSupportedVersions["sdvx:Single"]
);
} else {
chart = await FindSDVXChartOnInGameID(identifier, diff);
}
if (!chart) {
throw new KTDataNotFoundFailure(
`Cannot find SDVX chart with inGameID ${identifier}, difficulty ${diff} and version ${context.version}.`,
importType,
data,
context
);
}
const song = await db.songs[game].findOne({ id: chart.songID });
if (!song) {
logger.severe(`Song-Chart desync on ${chart.songID}.`);
throw new InternalFailure(`Failed to get song for a chart that exists.`);
}
return { song, chart };
}
case "inGameID": {
const gamesWithInGameIDSupport = [
"iidx",
"popn",
"ddr",
"sdvx",
"jubeat",
"chunithm",
"gitadora",
@@ -325,6 +389,12 @@ export async function ResolveMatchTypeToKTData(
"museca",
];
if (game === "sdvx") {
throw new InvalidScoreFailure(
`Cannot use inGameID as a matchType for SDVX. Use matchType: 'sdvxInGameID' instead.`
);
}
if (!gamesWithInGameIDSupport.includes(game)) {
throw new InvalidScoreFailure(
`Cannot use inGameID on game ${game}. The game may not have a concept of an in game ID, or support just might not exist yet.`
@@ -334,7 +404,6 @@ export async function ResolveMatchTypeToKTData(
let identifier: number | string = data.identifier;
// ddr uses weird strings as IDs instead of numbers
if (game !== "ddr") {
identifier = Number(data.identifier);
}
+17
View File
@@ -167,6 +167,23 @@ export const TestingSDVXScore: ScoreDocument<"sdvx:Single"> = {
importType: "ir/direct-manual",
};
export const TestingSDVXAlbidaChart: ChartDocument<"sdvx:Single"> = {
rgcID: null,
chartID: "5088a4d0e1ee9d0cc2f625934306e45b1a60699b",
difficulty: "ADV",
songID: 1,
playtype: "Single",
levelNum: 10,
level: "10",
data: {
inGameID: 1,
arcChartID: "EOZ7FixCDpv",
},
isPrimary: true,
versions: ["booth", "inf", "gw", "heaven", "vivid", "exceed", "konaste"],
tierlistInfo: {},
};
export const TestingKsHookSV6CScore: KsHookSV6CScore = {
clear: "CLEAR_EXCESSIVE",
critical: 1184,
+5 -5
View File
@@ -197,7 +197,7 @@ export function FindSDVXChartOnInGameID(
) {
const diffQuery =
difficulty === "ANY_INF"
? { $in: ["INF", "GRV", "HVN", "VVD"] as Array<Difficulties["sdvx:Single"]> }
? { $in: ["INF", "GRV", "HVN", "VVD", "XCD"] as Array<Difficulties["sdvx:Single"]> }
: difficulty;
return db.charts.sdvx.findOne({
@@ -209,12 +209,12 @@ export function FindSDVXChartOnInGameID(
export function FindSDVXChartOnInGameIDVersion(
inGameID: number,
difficulty: "ADV" | "ANY_INF" | "EXH" | "MXM" | "NOV",
difficulty: Difficulties["sdvx:Single"] | "ANY_INF",
version: GPTSupportedVersions["sdvx:Single"]
) {
const diffQuery =
difficulty === "ANY_INF"
? { $in: ["INF", "GRV", "HVN", "VVD"] as Array<Difficulties["sdvx:Single"]> }
? { $in: ["INF", "GRV", "HVN", "VVD", "XCD"] as Array<Difficulties["sdvx:Single"]> }
: difficulty;
return db.charts.sdvx.findOne({
@@ -226,12 +226,12 @@ export function FindSDVXChartOnInGameIDVersion(
export function FindSDVXChartOnDFVersion(
songID: integer,
difficulty: "ADV" | "ANY_INF" | "EXH" | "MXM" | "NOV",
difficulty: Difficulties["sdvx:Single"] | "ANY_INF",
version: GPTSupportedVersions["sdvx:Single"]
) {
const diffQuery =
difficulty === "ANY_INF"
? { $in: ["INF", "GRV", "HVN", "VVD"] as Array<Difficulties["sdvx:Single"]> }
? { $in: ["INF", "GRV", "HVN", "VVD", "XCD"] as Array<Difficulties["sdvx:Single"]> }
: difficulty;
return db.charts.sdvx.findOne({