New match type: ddrSongHash (#1213)

* New match type: `ddrSongHash`

* style: sort `MatchTypesWithDifficulty`

* make `ddrSongHash` a song property instead of a chart property

* docs: add descriptions for match type `ddrSongHash`

* add `ddrSongHash` field to all applicable songs

* fix: enforce ddrSongHash is only used on DDR

---------

Co-authored-by: zk <zkldi.dev@gmail.com>
This commit is contained in:
Victor Yu
2024-12-18 12:54:20 +00:00
committed by GitHub
co-authored by zk
parent 36bf039135
commit 2111782bf0
10 changed files with 1332 additions and 3 deletions
+2 -1
View File
@@ -60,6 +60,7 @@ export const DDR_CONF = {
inGameID: zodNonNegativeInt,
flareCategory: DDR_FLARE_CATEGORIES,
basename: z.string().optional(),
ddrSongHash: z.string().optional(), // optional because konaste-only songs have no hashes
}),
} as const satisfies INTERNAL_GAME_CONFIG;
@@ -206,7 +207,7 @@ export const DDR_SP_CONF = {
preferences: z.strictObject({}),
scoreMeta: z.strictObject({}),
supportedMatchTypes: ["inGameID", "songTitle", "tachiSongID"],
supportedMatchTypes: ["inGameID", "songTitle", "tachiSongID", "ddrSongHash"],
} as const satisfies INTERNAL_GAME_PT_CONFIG;
export const DDR_DP_CONF = {
+2 -1
View File
@@ -875,7 +875,8 @@ const PR_BATCH_MANUAL_SCORE = (game: Game, playtype: Playtype): PrudenceSchema =
"inGameID",
"inGameStrID",
"uscChartHash",
"popnChartHash"
"popnChartHash",
"ddrSongHash"
),
identifier: "string",
comment: optNull(p.isBoundedString(3, 240)),
+1
View File
@@ -19,6 +19,7 @@ type MatchTypesNoDifficulty = "bmsChartHash" | "itgChartHash" | "popnChartHash"
// These MatchTypes need `difficulty` set in the batch manual.
type MatchTypesWithDifficulty =
| "ddrSongHash"
| "inGameID"
| "inGameStrID"
| "sdvxInGameID"
+5
View File
@@ -166,6 +166,11 @@ 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.
- ddrSongHash
This is a 32-character hash used by the official DDR e-amusement website to
identify a song; you must specify the difficulty for this chart as well.
## Example
A final example of a simple BATCH MANUAL format
@@ -31,6 +31,10 @@ Uses `identifier` as a SHA1 hash, since that's what USC uses.
These match types need both `identifier` and a `difficulty` defined.
### `ddrSongHash`
Looks up on the 32-character song hash for this chart, where the hash is from the official DDR e-amusement website.
### `inGameID`
Looks up on the in game ID for this chart.
+1
View File
@@ -84,3 +84,4 @@ The folowing judgements are defined:
- `inGameID`
- `songTitle`
- `tachiSongID`
- `ddrSongHash`
+1
View File
@@ -84,3 +84,4 @@ The folowing judgements are defined:
- `inGameID`
- `songTitle`
- `tachiSongID`
- `ddrSongHash`
File diff suppressed because it is too large Load Diff
+12 -1
View File
@@ -31,7 +31,7 @@ const MATCH_TYPE_CHECKS: Record<
MatchTypes,
| {
type: "SONGS";
fn: (s: SongDocument) => string | Array<string>;
fn: (s: any) => string | Array<string>;
}
| {
type: "CHARTS";
@@ -61,6 +61,17 @@ const MATCH_TYPE_CHECKS: Record<
},
},
uscChartHash: { type: "CHARTS", fn: (c) => c.data.hashSHA1 },
ddrSongHash: {
type: "SONGS",
fn: (s: SongDocument<"ddr">) => {
// if there's no ddrSongHash then it's a konaste song / we're missing seed data
// so just use the inGameID
if (s.data.ddrSongHash === undefined) {
return `${s.data.inGameID}`;
}
return s.data.ddrSongHash;
},
},
};
let exitCode = 0;
@@ -432,6 +432,66 @@ export async function ResolveMatchTypeToTachiData(
return { song, chart };
}
case "ddrSongHash": {
if (game !== "ddr") {
throw new InvalidScoreFailure(`ddrSongHash matchType can only be used on DDR.`);
}
const difficulty = AssertStrAsDifficulty(data.difficulty, game, context.playtype);
const song = await db.anySongs.ddr.findOne({
"data.ddrSongHash": data.identifier,
});
if (!song) {
throw new SongOrChartNotFoundFailure(
`Cannot find song with ddrSongHash ${data.identifier}.`,
importType,
data,
context
);
}
// check that a chart with the song's id exists
const chartSync = await db.anyCharts.ddr.findOne({
songID: song.id,
});
if (!chartSync) {
logger.severe(`Song-Chart desync on ${song.id}.`);
throw new InternalFailure(`Failed to get chart for a song that exists.`);
}
let chart;
if (context.version) {
chart = await db.anyCharts.ddr.findOne({
songID: song.id,
playtype: context.playtype,
difficulty,
versions: context.version,
});
} else {
chart = await db.anyCharts.ddr.findOne({
songID: song.id,
playtype: context.playtype,
difficulty,
isPrimary: true,
});
}
if (!chart) {
throw new SongOrChartNotFoundFailure(
`Found song with ddrSongHash ${data.identifier} but cannot find chart ${context.playtype} ${difficulty}.`,
importType,
data,
context
);
}
return { song, chart };
}
default: {
const { matchType } = data;