diff --git a/common/src/config/game-support/sdvx.ts b/common/src/config/game-support/sdvx.ts index ea7ac7d53..c1200f70f 100644 --- a/common/src/config/game-support/sdvx.ts +++ b/common/src/config/game-support/sdvx.ts @@ -13,7 +13,7 @@ export const SDVX_CONF = { }), } as const satisfies INTERNAL_GAME_CONFIG; -const SDVXDans = [ +export const SDVXDans = [ ClassValue("DAN_1", "LV.01", "1st Dan"), ClassValue("DAN_2", "LV.02", "2nd Dan"), ClassValue("DAN_3", "LV.03", "3rd Dan"), diff --git a/server/src/lib/score-import/framework/calculated-data/types.ts b/server/src/lib/score-import/framework/calculated-data/types.ts index 7e8ba4ae9..20c6513c9 100644 --- a/server/src/lib/score-import/framework/calculated-data/types.ts +++ b/server/src/lib/score-import/framework/calculated-data/types.ts @@ -1,12 +1,15 @@ import type { KtLogger } from "lib/logger/logger"; -import type { ClassConfigs, GPTString, integer } from "tachi-common"; +import type { ClassConfigs, Classes, GPTString, integer } from "tachi-common"; import type { DerivedClassConfig, ProvidedClassConfig } from "tachi-common/types/game-config-utils"; -type RecordClassProvider = { - [C in keyof ClassConfigs[GPT] as ClassConfigs[GPT][C] extends DerivedClassConfig - ? C - : never]: ClassConfigs[GPT][C] extends ProvidedClassConfig ? V : never; -}; +// type RecordClassProvider = { +// [C in keyof ClassConfigs[GPT] as ClassConfigs[GPT][C] extends ProvidedClassConfig +// ? C +// : never]: ClassConfigs[GPT][C] extends ProvidedClassConfig ? V : never; +// }; + +// couldn't figure out how to get this typesafe, sorry. +type RecordClassProvider = Record; export type ClassProvider = ( gptString: GPT, diff --git a/server/src/lib/score-import/framework/profile-calculated-data/classes.test.ts b/server/src/lib/score-import/framework/profile-calculated-data/classes.test.ts index e2e806f8d..79d11b1cb 100644 --- a/server/src/lib/score-import/framework/profile-calculated-data/classes.test.ts +++ b/server/src/lib/score-import/framework/profile-calculated-data/classes.test.ts @@ -17,9 +17,16 @@ t.test("#CalculateUGPTClasses", (t) => { }); t.test("Should call and merge the ClassHandler", async (t) => { - const res = await CalculateUGPTClasses("iidx", "SP", 1, {}, () => ({ dan: 2 }), logger); + const res = await CalculateUGPTClasses( + "iidx", + "SP", + 1, + {}, + () => ({ dan: "DAN_2" }), + logger + ); - t.strictSame(res, { dan: 2 }); + t.strictSame(res, { dan: "DAN_2" }); t.end(); }); @@ -36,7 +43,7 @@ t.test("#CalculateUGPTClasses", (t) => { logger ); - t.strictSame(res, { colour: GITADORA_COLOURS.RAINBOW }); + t.strictSame(res, { colour: "RAINBOW" }); t.end(); }); diff --git a/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.ts b/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.ts index b3775d5de..c4add7a6e 100644 --- a/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.ts +++ b/server/src/lib/score-import/import-types/common/api-kai/iidx/class-handler.ts @@ -1,5 +1,6 @@ import { KaiTypeToBaseURL } from "../utils"; import { IIDX_DANS } from "tachi-common"; +import { IIDXDans } from "tachi-common/config/game-support/iidx"; import nodeFetch from "utils/fetch"; import { IsRecord } from "utils/misc"; import type { KaiAPIReauthFunction } from "../traverse-api"; @@ -102,8 +103,17 @@ export async function CreateKaiIIDXClassProvider( return {}; } + const value = IIDXDans[iidxDan]; + + if (!value) { + logger.warn( + `${baseUrl} returned a dan of ${iidxDan}, which has no corresponding value.` + ); + return {}; + } + return { - dan: iidxDan, + dan: value.id, }; }; } diff --git a/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.ts b/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.ts index 96a460b7b..ab80bdc66 100644 --- a/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.ts +++ b/server/src/lib/score-import/import-types/common/api-kai/sdvx/class-handler.ts @@ -1,5 +1,6 @@ import { KaiTypeToBaseURL } from "../utils"; import { SDVX_DANS } from "tachi-common"; +import { SDVXDans } from "tachi-common/config/game-support/sdvx"; import nodeFetch from "utils/fetch"; import { IsRecord } from "utils/misc"; import type { KaiAPIReauthFunction } from "../traverse-api"; @@ -95,8 +96,17 @@ export async function CreateKaiSDVXClassProvider( return {}; } + const value = SDVXDans[sdvxDan]; + + if (!value) { + logger.warn( + `${baseUrl} returned a dan of ${sdvxDan}, which has no corresponding value.` + ); + return {}; + } + return { - dan: sdvxDan, + dan: value.id, }; }; } diff --git a/server/src/lib/score-import/import-types/common/batch-manual/class-handler.ts b/server/src/lib/score-import/import-types/common/batch-manual/class-handler.ts index 2df2960f8..86e875880 100644 --- a/server/src/lib/score-import/import-types/common/batch-manual/class-handler.ts +++ b/server/src/lib/score-import/import-types/common/batch-manual/class-handler.ts @@ -11,7 +11,7 @@ export function CreateBatchManualClassProvider( return (gptString, userID, ratings, logger) => { const gptConfig = GetGPTConfig(gptString); - const newObj: Partial> = {}; + const newObj: Partial> = {}; for (const [s, classID] of Object.entries(classes)) { if (classID === null) { diff --git a/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/class-handler.ts b/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/class-handler.ts index adec45778..17dcd9694 100644 --- a/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/class-handler.ts +++ b/server/src/lib/score-import/import-types/file/wacca-mypage-scraper/class-handler.ts @@ -1,5 +1,6 @@ import ScoreImportFatalError from "../../../framework/score-importing/score-import-error"; import { WACCA_STAGEUPS } from "tachi-common"; +import { WaccaStageUps } from "tachi-common/config/game-support/wacca"; import type { MyPagePlayerStage } from "./types"; import type { ClassProvider } from "lib/score-import/framework/calculated-data/types"; @@ -52,7 +53,8 @@ export function CreateMyPageScraperClassProvider(stage: MyPagePlayerStage): Clas ); } - const stageEnum = STAGES[stage.id]; + // cheeky -1 + const stageEnum = WaccaStageUps[stage.id - 1]; if (stageEnum === undefined) { // If we can find the stage name but not the enum value, something is @@ -64,7 +66,7 @@ export function CreateMyPageScraperClassProvider(stage: MyPagePlayerStage): Clas } return { - stageUp: stageEnum, + stageUp: stageEnum.id, }; }; } diff --git a/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.ts b/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.ts index 790a0873f..ed57b7e0c 100644 --- a/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.ts +++ b/server/src/lib/score-import/import-types/ir/fervidex-static/class-handler.ts @@ -8,6 +8,7 @@ export function CreateFerStaticClassProvider(body: Record): Cla if (gptString === "iidx:SP") { index = body.sp_dan; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (gptString === "iidx:DP") { index = body.dp_dan; } else { @@ -34,7 +35,7 @@ export function CreateFerStaticClassProvider(body: Record): Cla } return { - dan, + dan: dan.id, }; }; } diff --git a/server/src/lib/targets/goals.ts b/server/src/lib/targets/goals.ts index 2d495b9cd..19585a391 100644 --- a/server/src/lib/targets/goals.ts +++ b/server/src/lib/targets/goals.ts @@ -19,6 +19,7 @@ import type { QuestDocument, QuestSubscriptionDocument, GPTString, + ScoreData, } from "tachi-common"; const logger = CreateLogCtx(__filename); @@ -73,6 +74,14 @@ export async function EvaluateGoalForUser( }; const gptString = GetGPTString(goal.game, goal.playtype); + const gptConfig = GetGPTConfig(gptString); + const scoreConf = GetScoreMetricConf(gptConfig, goal.criteria.key); + + if (!scoreConf) { + throw new Error( + `Invalid goal.criteria.key, got '${goal.criteria.key}', but no config exists for this metric for ${gptString}.` + ); + } switch (goal.criteria.mode) { case "single": { @@ -82,12 +91,18 @@ export async function EvaluateGoalForUser( const outOfHuman = HumaniseGoalOutOf(gptString, goal.criteria.key, goal.criteria.value); + const key = goal.criteria.key; + if (res) { return { achieved: true, outOf: goal.criteria.value, - // @ts-expect-error completely ok, as it'll definitely be on these props. - progress: res.scoreData[goal.criteria.key], + progress: + scoreConf.type === "ENUM" + ? // @ts-expect-error this is always correct but the typesystem is rightfully concerned + res.scoreData.enumIndexes[key] + : // @ts-expect-error see above + res.scoreData[key], outOfHuman, progressHuman: HumaniseGoalProgress( gptString, @@ -126,8 +141,12 @@ export async function EvaluateGoalForUser( achieved: false, outOf: goal.criteria.value, outOfHuman, - // @ts-expect-error completely ok, as it'll definitely be on these props. - progress: nextBestScore.scoreData[goal.criteria.key], + progress: + scoreConf.type === "ENUM" + ? // @ts-expect-error this is always correct but the typesystem is rightfully concerned + nextBestScore.scoreData.enumIndexes[key] + : // @ts-expect-error see above + nextBestScore.scoreData[key], progressHuman: HumaniseGoalProgress( gptString, goal.criteria.key, diff --git a/server/src/server/router/ir/usc/_playtype/router.test.ts b/server/src/server/router/ir/usc/_playtype/router.test.ts index 0ba256b9a..5624ed61b 100644 --- a/server/src/server/router/ir/usc/_playtype/router.test.ts +++ b/server/src/server/router/ir/usc/_playtype/router.test.ts @@ -127,7 +127,7 @@ t.test("GET /ir/usc/Controller/charts/:chartHash", (t) => { t.end(); }); -const USC_SCORE_PB: PBScoreDocument = { +const USC_SCORE_PB: PBScoreDocument<"usc:Controller"> = { chartID: "USC_CHART_ID", rankingData: { rank: 1, @@ -140,23 +140,24 @@ const USC_SCORE_PB: PBScoreDocument = { playtype: "Controller", game: "usc", highlight: false, - composedFrom: { - scorePB: "usc_score_pb", - lampPB: "bar", - }, + composedFrom: [ + { name: "Best Score", scoreID: "usc_score_pb" }, + { name: "Best Lamp", scoreID: "bar" }, + ], calculatedData: { VF6: null, }, isPrimary: true, scoreData: { score: 9_000_000, - percent: 90, grade: "A+", lamp: "EXCESSIVE CLEAR", - lampIndex: 2, - // idk, lazy - gradeIndex: 4, + enumIndexes: { + // are these even correct? lazy. + lamp: 2, + grade: 4, + }, judgements: { critical: 50, @@ -167,6 +168,7 @@ const USC_SCORE_PB: PBScoreDocument = { gauge: 50, fast: 50, slow: 20, + enumIndexes: {}, }, }, }; @@ -303,7 +305,6 @@ t.test("GET /charts/:chartHash/leaderboard", (t) => { userID: 2, scoreData: { score: 8_000_000, - percent: 80, }, rankingData: { rank: 2 }, composedFrom: { scorePB: "other_usc_score_pb" }, @@ -570,7 +571,6 @@ t.test("POST /scores", (t) => { t.hasStrict(dbScore, { scoreData: { score: 9_000_000, - percent: 90, lamp: "FAILED", }, scoreMeta: { @@ -727,7 +727,6 @@ t.test("POST /scores", (t) => { t.hasStrict(score, { scoreData: { score: 9_000_000, - percent: 90, lamp: "FAILED", }, scoreMeta: { @@ -867,7 +866,6 @@ t.test("POST /scores", (t) => { t.hasStrict(score, { scoreData: { score: 9_000_000, - percent: 90, lamp: "FAILED", }, scoreMeta: { diff --git a/server/src/server/router/ir/usc/_playtype/usc.test.ts b/server/src/server/router/ir/usc/_playtype/usc.test.ts index 84ab55d46..d9f76ced9 100644 --- a/server/src/server/router/ir/usc/_playtype/usc.test.ts +++ b/server/src/server/router/ir/usc/_playtype/usc.test.ts @@ -10,10 +10,10 @@ const mockScorePB: PBScoreDocument<"usc:Controller" | "usc:Keyboard"> = { calculatedData: { VF6: 0, }, - composedFrom: { - scorePB: "USC_EXAMPLE_SCORE_PB_ID", - lampPB: "USC_EXAMPLE_LAMP_PB_ID", - }, + composedFrom: [ + { name: "Best Score", scoreID: "USC_EXAMPLE_SCORE_PB_ID" }, + { name: "Best Lamp", scoreID: "USC_EXAMPLE_LAMP_PB_ID" }, + ], game: "usc", highlight: false, isPrimary: true, @@ -25,16 +25,19 @@ const mockScorePB: PBScoreDocument<"usc:Controller" | "usc:Keyboard"> = { }, scoreData: { grade: "AAA+", - gradeIndex: 7, judgements: { critical: 100, miss: 15, }, - optional: {}, + enumIndexes: { + grade: 7, + lamp: 3, + }, lamp: "EXCESSIVE CLEAR", - lampIndex: 3, - percent: 95, score: 9_500_000, + optional: { + enumIndexes: {}, + }, }, songID: 1, timeAchieved: null, @@ -271,7 +274,7 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { ranking: 1, }, isServerRecord: false, - isPB: false, + isPB: true, sendReplay: "USER_1_SCORE_PB", adjacentAbove: [ { score: 9_300_000, username: "4", ranking: 2 }, @@ -310,7 +313,7 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { ranking: 1, }, isServerRecord: false, - isPB: false, + isPB: true, sendReplay: "USER_4_SCORE_PB", }); @@ -349,7 +352,7 @@ t.test("#CreatePOSTScoresResponseBody", async (t) => { ranking: 1, }, isServerRecord: false, - isPB: false, + isPB: true, sendReplay: "USER_3_SCORE_PB", }); diff --git a/server/src/test-utils/mock-db/users.json b/server/src/test-utils/mock-db/users.json index 0fb771965..5cf7c1229 100644 --- a/server/src/test-utils/mock-db/users.json +++ b/server/src/test-utils/mock-db/users.json @@ -10,7 +10,6 @@ "status": "default status", "customPfpLocation": null, "customBannerLocation": null, - "clan": null, "badges": [], "authLevel": 1 }