mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-02 11:58:12 +03:00
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:
+1
-1
@@ -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];
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user