mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-11 00:38:28 +03:00
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:
@@ -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 = {
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -19,6 +19,7 @@ type MatchTypesNoDifficulty = "bmsChartHash" | "itgChartHash" | "popnChartHash"
|
||||
|
||||
// These MatchTypes need `difficulty` set in the batch manual.
|
||||
type MatchTypesWithDifficulty =
|
||||
| "ddrSongHash"
|
||||
| "inGameID"
|
||||
| "inGameStrID"
|
||||
| "sdvxInGameID"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -84,3 +84,4 @@ The folowing judgements are defined:
|
||||
- `inGameID`
|
||||
- `songTitle`
|
||||
- `tachiSongID`
|
||||
- `ddrSongHash`
|
||||
|
||||
@@ -84,3 +84,4 @@ The folowing judgements are defined:
|
||||
- `inGameID`
|
||||
- `songTitle`
|
||||
- `tachiSongID`
|
||||
- `ddrSongHash`
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user