From 06bc83375a1b98ef4b179be19c89e57672dfc10d Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Fri, 6 Jan 2023 21:33:27 +0000 Subject: [PATCH] checkpoint: 17 --- bot/src/utils/misc.ts | 10 +- bot/src/utils/returnTypes.ts | 2 +- client/src/types/api-returns.ts | 58 ++++----- client/src/types/tables.ts | 28 ++--- client/src/util/asserts.ts | 4 +- client/src/util/data.ts | 8 +- client/src/util/grade-deltas.ts | 6 +- client/src/util/misc.ts | 4 +- client/src/util/tables/create-search.ts | 8 +- client/src/util/tierlist.ts | 2 +- common/src/config/config.ts | 9 +- common/src/types/batch-manual.ts | 3 +- common/src/utils/util.ts | 10 +- .../iidx/iidx-mdb-parse/merge-mdb.ts | 6 +- server/src/lib/orphan-queue/orphan-queue.ts | 24 ++-- .../calculated-data/calculated-data.ts | 8 +- .../framework/common/get-input-parser.ts | 4 +- .../score-import/framework/common/types.ts | 5 +- .../score-import/framework/express-wrapper.ts | 2 +- .../framework/pb/create-pb-doc.ts | 2 +- .../score-import/framework/score-import.ts | 4 +- .../common/batch-manual/converter.ts | 119 +++++++----------- .../common/eamusement-iidx-csv/converter.ts | 13 +- .../score-import/import-types/converters.ts | 2 +- .../import-types/file/mer-iidx/converter.ts | 5 + .../import-types/ir/kshook-sv6c/converter.ts | 2 +- server/src/lib/score-import/worker/types.ts | 6 +- server/src/lib/score-import/worker/worker.ts | 4 +- server/src/utils/misc.ts | 2 +- server/src/utils/queries/charts.ts | 18 +-- 30 files changed, 175 insertions(+), 203 deletions(-) diff --git a/bot/src/utils/misc.ts b/bot/src/utils/misc.ts index a7ec71be5..8c927bfe9 100644 --- a/bot/src/utils/misc.ts +++ b/bot/src/utils/misc.ts @@ -152,9 +152,9 @@ export function CreateChartLink(chart: ChartDocument, game: Game) { return `${BotConfig.TACHI_SERVER_LOCATION}/games/${game}/${chart.playtype}/songs/${chart.songID}/${chart.chartID}`; } -type ScOrPBDoc = PBScoreDocument | ScoreDocument; +type ScOrPBDoc = PBScoreDocument | ScoreDocument; -export function FormatScoreData(score: ScOrPBDoc) { +export function FormatScoreData(score: ScOrPBDoc) { const game = score.game; let lampStr: string = score.scoreData.lamp; @@ -194,7 +194,7 @@ function GetGradeFromPercent( game: Game, playtype: Playtype, percent: number -): Grades[I] { +): Grades[GPT] { const gptConfig = GetGamePTConfig(game, playtype); const boundaries = gptConfig.gradeBoundaries; const grades = gptConfig.grades; @@ -208,7 +208,7 @@ function GetGradeFromPercent( // (hey, this for loop is backwards!) for (let i = boundaries.length; i >= 0; i--) { if (percent + Number.EPSILON >= boundaries[i]!) { - return grades[i] as Grades[I]; + return grades[i] as Grades[GPT]; } } @@ -229,7 +229,7 @@ function FormatIIDXEXScore(exscore: integer, notecount: integer, playtype: Playt return `${closer === "lower" ? lower : upper} (${exscore}, ${percent.toFixed(2)}%)`; } -export function GetChartPertinentInfo(game: Game, chart: ChartDocument) { +export function GetChartPertinentInfo(game: Game, chart: ChartDocument) { if (game === "iidx") { const ch = chart as ChartDocument<"iidx:DP" | "iidx:SP">; diff --git a/bot/src/utils/returnTypes.ts b/bot/src/utils/returnTypes.ts index 1c087b0ab..97f47868a 100644 --- a/bot/src/utils/returnTypes.ts +++ b/bot/src/utils/returnTypes.ts @@ -50,7 +50,7 @@ export interface UGPTStats { firstScore: ScoreDocument; mostRecentScore: ScoreDocument; totalScores: integer; - rankingData: Record; + rankingData: Record; } export interface ChartQueryReturns { diff --git a/client/src/types/api-returns.ts b/client/src/types/api-returns.ts index 3d0db9219..47ba28c38 100644 --- a/client/src/types/api-returns.ts +++ b/client/src/types/api-returns.ts @@ -33,11 +33,11 @@ import { export interface UGPTStatsReturn { gameStats: UserGameStats; - firstScore: ScoreDocument; - mostRecentScore: ScoreDocument; + firstScore: ScoreDocument; + mostRecentScore: ScoreDocument; totalScores: number; rankingData: Record< - ProfileRatingLookup[I], + ProfileRatingLookup[GPT], { ranking: integer; outOf: integer; @@ -82,29 +82,29 @@ export interface SessionReturns { session: SessionDocument; scores: ScoreDocument[]; scoreInfo: Array; - songs: SongDocument[]; - charts: ChartDocument[]; + songs: SongDocument[]; + charts: ChartDocument[]; user: UserDocument; } export interface UGPTChartPBComposition { - scores: ScoreDocument[]; - chart: ChartDocument; - pb: PBScoreDocument; + scores: ScoreDocument[]; + chart: ChartDocument; + pb: PBScoreDocument; } -export type UGSWithRankingData = UserGameStats & { - __rankingData: Record; +export type UGSWithRankingData = UserGameStats & { + __rankingData: Record; }; export interface SongChartsSearch { - songs: SongDocument[]; - charts: ChartDocument[]; + songs: SongDocument[]; + charts: ChartDocument[]; } export interface FolderStatsInfo { - grades: Record; - lamps: Record; + grades: Record; + lamps: Record; folderID: string; chartCount: integer; } @@ -122,15 +122,15 @@ export interface UGPTTableReturns { export interface UGPTFolderReturns { folder: FolderDocument; - songs: SongDocument[]; - charts: ChartDocument[]; - pbs: PBScoreDocument[]; + songs: SongDocument[]; + charts: ChartDocument[]; + pbs: PBScoreDocument[]; } export interface GPTFolderReturns { folder: FolderDocument; - songs: SongDocument[]; - charts: ChartDocument[]; + songs: SongDocument[]; + charts: ChartDocument[]; } export interface GPTStatsReturn { @@ -146,32 +146,32 @@ export interface RecentClassesReturn { } export interface SongsReturn { - song: SongDocument; - charts: ChartDocument[]; + song: SongDocument; + charts: ChartDocument[]; } export interface ChartPBLeaderboardReturn { users: UserDocument[]; - pbs: PBScoreDocument[]; + pbs: PBScoreDocument[]; } export interface UGPTChartLeaderboardAdjacent { users: UserDocument[]; - pb: PBScoreDocument; - adjacentAbove: PBScoreDocument[]; - adjacentBelow: PBScoreDocument[]; + pb: PBScoreDocument; + adjacentAbove: PBScoreDocument[]; + adjacentBelow: PBScoreDocument[]; } export interface ScoreLeaderboardReturns { users: UserDocument[]; - songs: SongDocument[]; - charts: ChartDocument[]; - pbs: PBScoreDocument[]; + songs: SongDocument[]; + charts: ChartDocument[]; + pbs: PBScoreDocument[]; } export interface UserLeaderboardReturns { users: UserDocument[]; - gameStats: UserGameStats[]; + gameStats: UserGameStats[]; } export interface UserRecentSummary { diff --git a/client/src/types/tables.ts b/client/src/types/tables.ts index b0a725613..007cb66a7 100644 --- a/client/src/types/tables.ts +++ b/client/src/types/tables.ts @@ -15,40 +15,40 @@ import { QuestDocument, } from "tachi-common"; -export type PBDataset = (PBScoreDocument & { +export type PBDataset = (PBScoreDocument & { __related: { - chart: ChartDocument; - song: SongDocument; + chart: ChartDocument; + song: SongDocument; index: integer; user?: UserDocument; }; __playcount?: integer; })[]; -export type ScoreDataset = (ScoreDocument & { +export type ScoreDataset = (ScoreDocument & { __related: { - chart: ChartDocument; - song: SongDocument; + chart: ChartDocument; + song: SongDocument; index: integer; user: UserDocument; }; })[]; -export type FolderDataset = (ChartDocument & { +export type FolderDataset = (ChartDocument & { __related: { - pb: PBScoreDocument | null; - song: SongDocument; + pb: PBScoreDocument | null; + song: SongDocument; user: UserDocument; }; })[]; -export type ChartLeaderboardDataset = (PBScoreDocument & { +export type ChartLeaderboardDataset = (PBScoreDocument & { __related: { user: UserDocument; }; })[]; -export type UGSDataset = (UserGameStats & { +export type UGSDataset = (UserGameStats & { __related: { user: UserDocument; index: integer; @@ -57,14 +57,14 @@ export type UGSDataset = (UserGameStats & export type RivalChartDataset = (UserDocument & { __related: { - pb: PBScoreDocument | null; + pb: PBScoreDocument | null; index: number; }; })[]; export type ComparePBsDataset = Array<{ - base: PBScoreDocument | null; - compare: PBScoreDocument | null; + base: PBScoreDocument | null; + compare: PBScoreDocument | null; chart: ChartDocument; song: SongDocument; }>; diff --git a/client/src/util/asserts.ts b/client/src/util/asserts.ts index b056c8333..286e54a56 100644 --- a/client/src/util/asserts.ts +++ b/client/src/util/asserts.ts @@ -22,8 +22,8 @@ export function IsSupportedPlaytype( } export function IsScore( - pbOrScore: PBScoreDocument | ScoreDocument -): pbOrScore is ScoreDocument { + pbOrScore: PBScoreDocument | ScoreDocument +): pbOrScore is ScoreDocument { // @ts-expect-error thats the test... return !!pbOrScore.scoreMeta; } diff --git a/client/src/util/data.ts b/client/src/util/data.ts index 9b81b2d77..1aea25fba 100644 --- a/client/src/util/data.ts +++ b/client/src/util/data.ts @@ -56,8 +56,8 @@ export function CreateChartIDMap(arr: T[]): Map(charts: ChartDocument[]) { - const chartMap = new Map>(); +export function CreateChartMap(charts: ChartDocument[]) { + const chartMap = new Map>(); for (const chart of charts) { chartMap.set(chart.chartID, chart); @@ -66,8 +66,8 @@ export function CreateChartMap(charts: ChartD return chartMap; } -export function CreateScoreIDMap(scores: ScoreDocument[]) { - const scoreMap = new Map>(); +export function CreateScoreIDMap(scores: ScoreDocument[]) { + const scoreMap = new Map>(); for (const score of scores) { scoreMap.set(score.scoreID, score); diff --git a/client/src/util/grade-deltas.ts b/client/src/util/grade-deltas.ts index 20c91508b..b3fe5a0bc 100644 --- a/client/src/util/grade-deltas.ts +++ b/client/src/util/grade-deltas.ts @@ -5,7 +5,7 @@ export function AbsoluteGradeDelta( playtype: Playtypes[Game], score: number, percent: number, - gradeOrIndex: Grades[I] | integer + gradeOrIndex: Grades[GPT] | integer ) { const gptConfig = GetGamePTConfig(game, playtype); @@ -28,7 +28,7 @@ export function RelativeGradeDelta( playtype: Playtypes[Game], score: number, percent: number, - grade: Grades[I], + grade: Grades[GPT], relativeIndex: integer ) { const gptConfig = GetGamePTConfig(game, playtype); @@ -58,7 +58,7 @@ export function GenericFormatGradeDelta( playtype: Playtypes[Game], score: number, percent: number, - grade: Grades[I], + grade: Grades[GPT], formatNumFn: (n: number) => number = (s) => s ): { lower: string; diff --git a/client/src/util/misc.ts b/client/src/util/misc.ts index e83906831..5406c80e4 100644 --- a/client/src/util/misc.ts +++ b/client/src/util/misc.ts @@ -319,7 +319,7 @@ export function GetGradeFromPercent( game: Game, playtype: Playtype, percent: number -): Grades[I] { +): Grades[GPT] { const gptConfig = GetGamePTConfig(game, playtype); const boundaries = gptConfig.gradeBoundaries; const grades = gptConfig.grades; @@ -333,7 +333,7 @@ export function GetGradeFromPercent( // (hey, this for loop is backwards!) for (let i = boundaries.length; i >= 0; i--) { if (percent >= boundaries[i]) { - return grades[i] as Grades[I]; + return grades[i] as Grades[GPT]; } } diff --git a/client/src/util/tables/create-search.ts b/client/src/util/tables/create-search.ts index 95e852415..3f04e2e57 100644 --- a/client/src/util/tables/create-search.ts +++ b/client/src/util/tables/create-search.ts @@ -27,7 +27,7 @@ export function CreateDefaultScoreSearchParams[0]>> = { + const searchFunctions: Record[0]>> = { artist: (x) => x.__related.song.artist, title: (x) => x.__related.song.title, difficulty: (x) => x.__related.chart.difficulty, @@ -59,7 +59,7 @@ export function CreateDefaultPBSearchParams( ) { const gptConfig = GetGamePTConfig(game, playtype); - const searchFunctions: Record[0]>> = { + const searchFunctions: Record[0]>> = { artist: (x) => x.__related.song.artist, title: (x) => x.__related.song.title, difficulty: (x) => x.__related.chart.difficulty, @@ -92,7 +92,7 @@ export function CreatePBCompareSearchParams( game: Game, playtype: Playtype ) { - const searchFunctions: Record[0]>> = { + const searchFunctions: Record[0]>> = { artist: (x) => x.song.artist, title: (x) => x.song.title, difficulty: (x) => x.chart.difficulty, @@ -112,7 +112,7 @@ export function CreateDefaultFolderSearchParams[0]>> = { + const searchFunctions: Record[0]>> = { artist: (x) => x.__related.song.artist, title: (x) => x.__related.song.title, difficulty: (x) => x.difficulty, diff --git a/client/src/util/tierlist.ts b/client/src/util/tierlist.ts index 05d7ef3bd..d20cfb065 100644 --- a/client/src/util/tierlist.ts +++ b/client/src/util/tierlist.ts @@ -10,7 +10,7 @@ import { type ScaleAchievedFns = { [I in GPTString]: { - [K in GPTTierlists[I]]: ((k: ScoreDocument) => boolean) | null; + [K in GPTTierlists[GPT]]: ((k: ScoreDocument) => boolean) | null; }; }; diff --git a/common/src/config/config.ts b/common/src/config/config.ts index cad348cef..1b79afdd8 100644 --- a/common/src/config/config.ts +++ b/common/src/config/config.ts @@ -105,13 +105,8 @@ export function GetGPTConfig(gptString: GPTString): GamePTConfig { * Returns the configuration for this specific Game + Playtype. This type is narrowed * down to its least generic form, and is instead for gpt-specific use cases. */ -export function GetSpecificGPTConfig( - game: GPTStringToGame[GPT], - playtype: GPTStringToPlaytype[GPT] -) { - const gptString = GetGPTString(game, playtype); - - return GAME_PT_CONFIGS[gptString] as unknown as SpecificGamePTConfig; +export function GetSpecificGPTConfig(gpt: GPTString) { + return GAME_PT_CONFIGS[gpt] as unknown as SpecificGamePTConfig; } export const allSupportedGames = Object.keys(GAME_CONFIGS) as Array; diff --git a/common/src/types/batch-manual.ts b/common/src/types/batch-manual.ts index ccc142116..0aaf0c93c 100644 --- a/common/src/types/batch-manual.ts +++ b/common/src/types/batch-manual.ts @@ -12,6 +12,7 @@ import type { ScoreMeta, } from "./game-config"; import type { ExtractMetrics } from "./metrics"; +import type { AllFieldsNullableOptional } from "./utils"; // These MatchTypes don't need `difficulty` set in the batch manual. type MatchTypesNoDifficulty = "bmsChartHash" | "itgChartHash" | "popnChartHash" | "uscChartHash"; @@ -28,7 +29,7 @@ export type BatchManualScore = ExtractMetrics comment?: string | null; judgements?: Record; timeAchieved?: number | null; - additionalMetrics?: Partial>; + optional?: AllFieldsNullableOptional>; scoreMeta?: Partial; } & ( | { diff --git a/common/src/utils/util.ts b/common/src/utils/util.ts index de97a68b7..4093ddacc 100644 --- a/common/src/utils/util.ts +++ b/common/src/utils/util.ts @@ -1,4 +1,9 @@ -import { GetGameConfig, GetGamePTConfig, GetSpecificGPTConfig } from "../config/config"; +import { + GetGPTString, + GetGameConfig, + GetGamePTConfig, + GetSpecificGPTConfig, +} from "../config/config"; import type { ChartDocument, GPTStrings, @@ -33,8 +38,7 @@ export function FormatDifficulty(chart: ChartDocument, game: Game): string { const ch = chart as ChartDocument; const gptConfig = GetSpecificGPTConfig( - game, - chart.playtype as Playtypes["gitadora"] + GetGPTString(game, chart.playtype) as GPTStrings["gitadora"] ); // @ts-expect-error maybe this new config format was a mistake. diff --git a/database-seeds/scripts/rerunners/iidx/iidx-mdb-parse/merge-mdb.ts b/database-seeds/scripts/rerunners/iidx/iidx-mdb-parse/merge-mdb.ts index 4f34b55b6..2c6323483 100644 --- a/database-seeds/scripts/rerunners/iidx/iidx-mdb-parse/merge-mdb.ts +++ b/database-seeds/scripts/rerunners/iidx/iidx-mdb-parse/merge-mdb.ts @@ -48,11 +48,9 @@ const options = program.opts() as { const iidxConfig = GetGamePTConfig("iidx", "SP"); -if (!iidxConfig.orderedSupportedVersions.includes(options.version)) { +if (!iidxConfig.versions.includes(options.version)) { throw new Error( - `Invalid version of '${ - options.version - }'. Expected any of ${iidxConfig.orderedSupportedVersions.join( + `Invalid version of '${options.version}'. Expected any of ${iidxConfig.versions.join( ", " )}. If you're adding a new version, go update common/src/config.ts.` ); diff --git a/server/src/lib/orphan-queue/orphan-queue.ts b/server/src/lib/orphan-queue/orphan-queue.ts index 96431fc39..b35f398b8 100644 --- a/server/src/lib/orphan-queue/orphan-queue.ts +++ b/server/src/lib/orphan-queue/orphan-queue.ts @@ -3,7 +3,6 @@ import CreateLogCtx from "lib/logger/logger"; import { GetNextCounterValue } from "utils/db"; import { DedupeArr } from "utils/misc"; import type { FilterQuery } from "mongodb"; -import type { WithID } from "monk"; import type { ChartDocument, GPTString, @@ -28,11 +27,11 @@ const logger = CreateLogCtx(__filename); * played it, unorphan the chart, and return it. */ export async function HandleOrphanQueue( - gptString: I, - game: GPTStringToGame[I], - chartDoc: ChartDocument, - songDoc: SongDocument, - orphanMatchCriteria: FilterQuery>, + gptString: GPT, + game: GPTStringToGame[GPT], + chartDoc: ChartDocument, + songDoc: SongDocument, + orphanMatchCriteria: FilterQuery>, queueSize: integer, userID: integer, chartName: string @@ -81,11 +80,12 @@ export async function HandleOrphanQueue( await db.anySongs[game].insert(songDoc); await db.anyCharts[game].insert(chartDoc); + await db["orphan-chart-queue"].remove({ _id: orphanChart._id, }); - return chartDoc as ChartDocument; + return chartDoc as ChartDocument; } // otherwise, update the state of this orphan. @@ -112,10 +112,10 @@ export async function HandleOrphanQueue( * regardless of how many people have played the chart. */ export async function DeorphanIfInQueue( - gptString: I, - game: GPTStringToGame[I], - orphanMatchCriteria: FilterQuery> -): Promise | null> { + gptString: GPT, + game: GPTStringToGame[GPT], + orphanMatchCriteria: FilterQuery> +): Promise | null> { const orphanChart = await db["orphan-chart-queue"].findOne( { gptString, ...orphanMatchCriteria }, { @@ -143,5 +143,5 @@ export async function DeorphanIfInQueue( _id: orphanChart._id, }); - return chartDoc; + return chartDoc as ChartDocument; } diff --git a/server/src/lib/score-import/framework/calculated-data/calculated-data.ts b/server/src/lib/score-import/framework/calculated-data/calculated-data.ts index 7e0711271..987cb3d7f 100644 --- a/server/src/lib/score-import/framework/calculated-data/calculated-data.ts +++ b/server/src/lib/score-import/framework/calculated-data/calculated-data.ts @@ -43,10 +43,10 @@ export async function CreateCalculatedData( type CalculatedDataFunctionsType = { [I in GPTString]: ( - dryScore: DryScore, - chart: ChartDocument, + dryScore: DryScore, + chart: ChartDocument, logger: KtLogger - ) => Promise["calculatedData"]> | ScoreDocument["calculatedData"]; + ) => Promise["calculatedData"]> | ScoreDocument["calculatedData"]; }; const CalculatedDataFunctions: CalculatedDataFunctionsType = { @@ -87,7 +87,7 @@ export async function CalculateDataForGamePT( return CalculatedDataFunctions[`${game}:${playtype}` as GPTString](dryScore, chart, logger); } -type CalculatedData = Required["calculatedData"]>; +type CalculatedData = Required["calculatedData"]>; function CalculateDataIIDX( dryScore: DryScore, diff --git a/server/src/lib/score-import/framework/common/get-input-parser.ts b/server/src/lib/score-import/framework/common/get-input-parser.ts index b1ec35ff3..d227378f5 100644 --- a/server/src/lib/score-import/framework/common/get-input-parser.ts +++ b/server/src/lib/score-import/framework/common/get-input-parser.ts @@ -8,7 +8,7 @@ import type { import type { ScoreImportJobData } from "lib/score-import/worker/types"; import type { ImportTypes } from "tachi-common"; -export function GetInputParser(jobData: ScoreImportJobData) { +export function GetInputParser(jobData: ScoreImportJobData) { // Retrieve the set parser function for this import type. const ParserFunction = Parsers[jobData.importType]; @@ -20,7 +20,7 @@ export function GetInputParser(jobData: ScoreImportJobDat // achieve the dynamic passing we need to, so lets just override // it here. ParserFunction(...jobData.parserArguments, logger) as Promise< - ParserFunctionReturns + ParserFunctionReturns >; return InputParser; diff --git a/server/src/lib/score-import/framework/common/types.ts b/server/src/lib/score-import/framework/common/types.ts index 3d2970bfe..641e7b4d4 100644 --- a/server/src/lib/score-import/framework/common/types.ts +++ b/server/src/lib/score-import/framework/common/types.ts @@ -8,6 +8,7 @@ import type { OptionalMetrics, } from "tachi-common"; import type { EnumValue } from "tachi-common/types/metrics"; +import type { Mutable } from "utils/types"; /** * For dry scores, we don't want to make converters fill out enum values as @@ -16,12 +17,12 @@ import type { EnumValue } from "tachi-common/types/metrics"; * * The index part can *always* be derived. This function turns */ -type ExtractEnumValues> = { +export type ExtractEnumValues> = { [K in keyof TMetrics]: TMetrics[K] extends EnumValue ? V : TMetrics[K]; }; type DryScoreData = ExtractEnumValues & { - optional: ExtractEnumValues; + optional: Mutable>; judgements: Partial>; }; diff --git a/server/src/lib/score-import/framework/express-wrapper.ts b/server/src/lib/score-import/framework/express-wrapper.ts index 680644557..85f60f24b 100644 --- a/server/src/lib/score-import/framework/express-wrapper.ts +++ b/server/src/lib/score-import/framework/express-wrapper.ts @@ -27,7 +27,7 @@ export async function ExpressWrappedScoreImportMain( userID: integer, userIntent: boolean, importType: I, - parserArguments: ParserArguments + parserArguments: ParserArguments ): Promise { const importID = Random20Hex(); diff --git a/server/src/lib/score-import/framework/pb/create-pb-doc.ts b/server/src/lib/score-import/framework/pb/create-pb-doc.ts index 8e12b5389..289f2f915 100644 --- a/server/src/lib/score-import/framework/pb/create-pb-doc.ts +++ b/server/src/lib/score-import/framework/pb/create-pb-doc.ts @@ -20,7 +20,7 @@ import type { } from "tachi-common"; export type PBScoreDocumentNoRank = Omit< - PBScoreDocument, + PBScoreDocument, "rankingData" >; diff --git a/server/src/lib/score-import/framework/score-import.ts b/server/src/lib/score-import/framework/score-import.ts index 00b0f6f2a..316360cf9 100644 --- a/server/src/lib/score-import/framework/score-import.ts +++ b/server/src/lib/score-import/framework/score-import.ts @@ -29,7 +29,7 @@ const logger = CreateLogCtx(__filename); * the client, make it poll /api/v1/ongoing-imports/:importID. */ export async function MakeScoreImport( - jobData: ScoreImportJobData + jobData: ScoreImportJobData ): Promise { await StartTrackingImport(jobData); @@ -53,7 +53,7 @@ export async function MakeScoreImport( * by the import-tracking code, as it's useful for errors. */ async function MakeScoreImportInner( - jobData: ScoreImportJobData + jobData: ScoreImportJobData ): Promise { if (ServerConfig.USE_EXTERNAL_SCORE_IMPORT_WORKER && process.env.IS_JOB === undefined) { let timesAttempted = 1; diff --git a/server/src/lib/score-import/import-types/common/batch-manual/converter.ts b/server/src/lib/score-import/import-types/common/batch-manual/converter.ts index 3b26b7315..f51fae88b 100644 --- a/server/src/lib/score-import/import-types/common/batch-manual/converter.ts +++ b/server/src/lib/score-import/import-types/common/batch-manual/converter.ts @@ -3,14 +3,13 @@ import { InvalidScoreFailure, SongOrChartNotFoundFailure, } from "../../../framework/common/converter-failures"; -import { GenericGetGradeAndPercent, JubeatGetGrade } from "../../../framework/common/score-utils"; import { AssertStrAsDifficulty, AssertStrAsPositiveInt, } from "../../../framework/common/string-asserts"; import db from "external/mongo/db"; +import ScoreImportFatalError from "lib/score-import/framework/score-importing/score-import-error"; import { FormatGame, GetGamePTConfig } from "tachi-common"; -import { FloorToNDP } from "utils/misc"; import { FindBMSChartOnHash, FindChartWithPTDF, @@ -20,7 +19,7 @@ import { FindSDVXChartOnInGameIDVersion, } from "utils/queries/charts"; import { FindSongOnID, FindSongOnTitleInsensitive } from "utils/queries/songs"; -import type { DryScore } from "../../../framework/common/types"; +import type { DryScore, ExtractEnumValues } from "../../../framework/common/types"; import type { ConverterFunction } from "../types"; import type { BatchManualContext } from "./types"; import type { KtLogger } from "lib/logger/logger"; @@ -28,11 +27,11 @@ import type { BatchManualScore, ChartDocument, Difficulties, - Versions, - Grades, - GPTString, ImportTypes, SongDocument, + Versions, + GPTString, + ProvidedMetrics, } from "tachi-common"; /** @@ -47,64 +46,10 @@ export const ConverterBatchManual: ConverterFunction { - const game = context.game; + const { game, playtype } = context; const { song, chart } = await ResolveMatchTypeToTachiData(data, context, importType, logger); - // yet another temporary hack, jubeat's percent is not a function of score, - // it's actually an entirely separate metric. We need to support this, so we'll - // run like this. - - let percent: number; - let grade: Grades[GPTString]; - - if (game === "jubeat") { - if (data.percent === undefined) { - throw new InvalidScoreFailure( - `The percent field must be filled out for jubeat scores.` - ); - } - - const accidentallyDividedTooMuchPercent = (chart as ChartDocument<"jubeat:Single">).data - .isHardMode - ? 1.2 - : 1; - - if (data.percent <= accidentallyDividedTooMuchPercent && data.score >= 100_000) { - throw new InvalidScoreFailure( - `The percent you passed for this jubeat score was less than 1, but the score was above 100k. This is not possible. Have you sent percent as a number between 0 and 1?` - ); - } - - if (data.percent > 100 && !(chart as ChartDocument<"jubeat:Single">).data.isHardMode) { - throw new InvalidScoreFailure(`The percent field must be <= 100 for normal mode.`); - } - - // Since GenericGetGradeAndPercent also handles validating percent, we need - // to handle validating percent here. - if (data.score > 1_000_000) { - throw new InvalidScoreFailure( - `The score field must be a positive integer between 0 and 1 million.` - ); - } - - // jubeat music rate is forcibly floored to 1dp. Funny. - percent = FloorToNDP(data.percent, 1); - - grade = JubeatGetGrade(data.score); - } else { - ({ percent, grade } = GenericGetGradeAndPercent(context.game, data.score, chart)); - - // Temporary hack -- Pop'n upper bounds grades like this. We need to tuck this - // away further inside genericgetgradeandpercent or something. - if (game === "popn" && data.lamp === "FAILED" && percent >= 90) { - grade = "A"; - } else if (game === "itg" && data.lamp === "FAILED") { - // a fail in ITG is **always** an F. no matter what. - grade = "F"; - } - } - let service = context.service; if (importType === "ir/direct-manual") { @@ -113,6 +58,17 @@ export const ConverterBatchManual: ConverterFunction = {}; + + const config = GetGamePTConfig(game, playtype); + + for (const key of Object.keys(config.providedMetrics)) { + // @ts-expect-error hacky type messery + metrics[key] = data[key]; + } + const dryScore: DryScore = { game, service, @@ -122,10 +78,7 @@ export const ConverterBatchManual: ConverterFunction> = new Map([ ["NO PLAY", "NO PLAY"], @@ -131,8 +129,6 @@ const ConvertEamIIDXCSV: ConverterFunction< ); } - const { percent, grade } = GenericGetGradeAndPercent("iidx", exscore, tachiChart); - const timestamp = ParseDateFromString(data.timestamp); const dryScore: DryScore<"iidx:DP" | "iidx:SP"> = { @@ -148,8 +144,6 @@ const ConvertEamIIDXCSV: ConverterFunction< great, }, optional: {}, - percent, - grade, }, scoreMeta: {}, @@ -175,7 +169,6 @@ const ConvertEamIIDXCSV: ConverterFunction< logger.info(`Skipped assigning BP for score. Had unexpected value of ${eamScore.bp}.`); } - // ts thinks tachiSong might be null. It's not, though! return { chart: tachiChart, dryScore, song: tachiSong }; }; diff --git a/server/src/lib/score-import/import-types/converters.ts b/server/src/lib/score-import/import-types/converters.ts index 149f7c68c..21740682e 100644 --- a/server/src/lib/score-import/import-types/converters.ts +++ b/server/src/lib/score-import/import-types/converters.ts @@ -22,7 +22,7 @@ import type { ConverterFunction, ImportTypeContextMap, ImportTypeDataMap } from import type { ImportTypes } from "tachi-common"; export type ConverterMap = { - [I in ImportTypes]: ConverterFunction; + [I in ImportTypes]: ConverterFunction; }; export const Converters: ConverterMap = { diff --git a/server/src/lib/score-import/import-types/file/mer-iidx/converter.ts b/server/src/lib/score-import/import-types/file/mer-iidx/converter.ts index 6967372f8..8eae9d408 100644 --- a/server/src/lib/score-import/import-types/file/mer-iidx/converter.ts +++ b/server/src/lib/score-import/import-types/file/mer-iidx/converter.ts @@ -1,5 +1,6 @@ import { InternalFailure, + SkipScoreFailure, SongOrChartNotFoundFailure, } from "../../../framework/common/converter-failures"; import { ParseDateFromString } from "../../../framework/common/score-utils"; @@ -27,6 +28,10 @@ export const ConvertFileMerIIDX: ConverterFunction = asyn ) => { const playtype = data.play_type === "SINGLE" ? "SP" : "DP"; + if (data.diff_type === "BEGINNER") { + throw new SkipScoreFailure(`BEGINNER scores are not supported.`); + } + const chart = await FindIIDXChartOnInGameID(data.music_id, playtype, data.diff_type); if (!chart) { diff --git a/server/src/lib/score-import/import-types/ir/kshook-sv6c/converter.ts b/server/src/lib/score-import/import-types/ir/kshook-sv6c/converter.ts index 11eaeaf01..5fb7a6456 100644 --- a/server/src/lib/score-import/import-types/ir/kshook-sv6c/converter.ts +++ b/server/src/lib/score-import/import-types/ir/kshook-sv6c/converter.ts @@ -17,7 +17,7 @@ export const ConverterIRKsHookSV6C: ConverterFunction { const diff = SV6CConvertDifficulty(data.difficulty); - const chart = await FindSDVXChartOnInGameIDVersion(data.music_id, diff, "konaste"); + const chart = await FindSDVXChartOnInGameIDVersion(data.music_id, diff, "Konaste"); if (!chart) { throw new SongOrChartNotFoundFailure( diff --git a/server/src/lib/score-import/worker/types.ts b/server/src/lib/score-import/worker/types.ts index bc1511492..66a103d4a 100644 --- a/server/src/lib/score-import/worker/types.ts +++ b/server/src/lib/score-import/worker/types.ts @@ -16,7 +16,7 @@ type RemoveLast> = T extends [...infer RemoveLast, unkn // "ir/usc". // And then, using RemoveLast, we can remove that logger argument we don't // want to provide. -export type ParserArguments = RemoveLast>; +export type ParserArguments = RemoveLast>; // Depending on how you look at it, this is either beautiful // TypeScript power, or brutal TypeScript abuse... @@ -33,10 +33,10 @@ export interface ScoreImportJobData { // from different sources! As such, we need to pass the parser // arguments in an array, and then it can be respreaded into // the parser by the import code. - parserArguments: ParserArguments; + parserArguments: ParserArguments; } -export type ScoreImportJob = Job>; +export type ScoreImportJob = Job>; export interface ScoreImportProgress { description: string; diff --git a/server/src/lib/score-import/worker/worker.ts b/server/src/lib/score-import/worker/worker.ts index 8cb80ba43..348c99820 100644 --- a/server/src/lib/score-import/worker/worker.ts +++ b/server/src/lib/score-import/worker/worker.ts @@ -45,7 +45,7 @@ if (require.main !== module) { */ export const worker = new Worker( ScoreImportQueue.name, - async (job: ScoreImportJob) => { + async (job: ScoreImportJob) => { const user = await GetUserWithID(job.data.userID); if (!user) { @@ -76,7 +76,7 @@ export const worker = new Worker( } } - job.data.parserArguments = processedArgs as ScoreImportJobData["parserArguments"]; + job.data.parserArguments = processedArgs as ScoreImportJobData["parserArguments"]; // Create a logger that we can pass around for context. // This helps us debug what score import did what! diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 7b77e59c5..2038e722f 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -328,7 +328,7 @@ export function StringIsGameVersion( const gptConfig = GetGamePTConfig(game, playtype); // @ts-expect-error yes, we know! - return gptConfig.orderedSupportedVersions.includes(version); + return gptConfig.versions.includes(version); } /** diff --git a/server/src/utils/queries/charts.ts b/server/src/utils/queries/charts.ts index 5c4eee2c4..1614420a3 100644 --- a/server/src/utils/queries/charts.ts +++ b/server/src/utils/queries/charts.ts @@ -24,7 +24,7 @@ export function FindChartWithPTDF< G extends Game = Game, P extends Playtypes[G] = Playtypes[G], GPT extends GPTString = GPTString ->(game: G, songID: integer, playtype: P, difficulty: Difficulties[I]) { +>(game: G, songID: integer, playtype: P, difficulty: Difficulties[GPT]) { return db.anyCharts[game].findOne({ songID, playtype, @@ -41,7 +41,7 @@ export function FindChartWithPTDFVersion< G extends Game = Game, P extends Playtypes[G] = Playtypes[G], GPT extends GPTString = GPTString ->(game: G, songID: integer, playtype: P, difficulty: Difficulties[I], version: Versions[I]) { +>(game: G, songID: integer, playtype: P, difficulty: Difficulties[GPT], version: Versions[GPT]) { return db.anyCharts[game].findOne({ songID, playtype, @@ -92,8 +92,8 @@ export function FindChartOnInGameID( */ export function FindIIDXChartOnInGameID( inGameID: number, - playtype: Playtype, - difficulty: Difficulties[GPTString] + playtype: Playtypes["iidx"], + difficulty: Difficulties["iidx:DP" | "iidx:SP"] ) { return db.charts.iidx.findOne({ "data.inGameID": inGameID, @@ -110,9 +110,9 @@ export function FindIIDXChartOnInGameID( */ export function FindIIDXChartOnInGameIDVersion( inGameID: number, - playtype: Playtype, - difficulty: Difficulties[GPTString], - version: Versions[GPTString] + playtype: Playtypes["iidx"], + difficulty: Difficulties["iidx:DP" | "iidx:SP"], + version: Versions["iidx:DP" | "iidx:SP"] ) { return db.charts.iidx.findOne({ "data.inGameID": inGameID, @@ -130,8 +130,8 @@ export function FindChartOnInGameIDVersion( game: Game, inGameID: number, playtype: Playtype, - difficulty: Difficulties[I], - version: Versions[I] + difficulty: Difficulties[GPT], + version: Versions[GPT] ) { return db.anyCharts[game].findOne({ "data.inGameID": inGameID,