diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index aa68ace92..856d5355e 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -61,7 +61,7 @@ dependencies: express-session: 1.17.1 fast-json-stable-hash: 1.0.2 fast-xml-parser: 3.19.0 - kamaitachi-common: github.com/zkldi/Kamaitachi-common/6f1ca95f0432ae61eef586bb3535ac5a159ad764 + kamaitachi-common: github.com/zkldi/Kamaitachi-common/309bc3610aca940ceeb5244ff7d337770cf27201 mongodb: 3.6.6 monk: 7.3.4 multer: 1.4.2 @@ -4344,8 +4344,8 @@ packages: engines: {node: '>=6'} dev: true - github.com/zkldi/Kamaitachi-common/6f1ca95f0432ae61eef586bb3535ac5a159ad764: - resolution: {tarball: https://codeload.github.com/zkldi/Kamaitachi-common/tar.gz/6f1ca95f0432ae61eef586bb3535ac5a159ad764} + github.com/zkldi/Kamaitachi-common/309bc3610aca940ceeb5244ff7d337770cf27201: + resolution: {tarball: https://codeload.github.com/zkldi/Kamaitachi-common/tar.gz/309bc3610aca940ceeb5244ff7d337770cf27201} name: kamaitachi-common version: 0.1.0 dependencies: diff --git a/server/src/lib/constants/classes.ts b/server/src/lib/constants/classes.ts new file mode 100644 index 000000000..0f08443a9 --- /dev/null +++ b/server/src/lib/constants/classes.ts @@ -0,0 +1,161 @@ +export enum IIDXClasses { + KYU_7, + KYU_6, + KYU_5, + KYU_4, + KYU_3, + KYU_2, + KYU_1, + + DAN_1, + DAN_2, + DAN_3, + DAN_4, + DAN_5, + DAN_6, + DAN_7, + DAN_8, + DAN_9, + DAN_10, + + CHUUDEN, + KAIDEN, +} + +export enum GitadoraColours { + WHITE, + + ORANGE, + ORANGE_GRADIENT, + YELLOW, + YELLOW_GRADIENT, + GREEN, + GREEN_GRADIENT, + BLUE, + BLUE_GRADIENT, + PURPLE, + PURPLE_GRADIENT, + RED, + RED_GRADIENT, + + BRONZE, + SILVER, + GOLD, + RAINBOW, +} + +export enum BMSGenocideDans { + NORMAL_1, + NORMAL_2, + NORMAL_3, + NORMAL_4, + NORMAL_5, + NORMAL_6, + NORMAL_7, + NORMAL_8, + NORMAL_9, + NORMAL_10, + + INSANE_1, + INSANE_2, + INSANE_3, + INSANE_4, + INSANE_5, + INSANE_6, + INSANE_7, + INSANE_8, + INSANE_9, + INSANE_10, + INSANE_KAIDEN, + + OVERJOY, +} + +export enum BMSStSlDans { + SL_0, + SL_1, + SL_2, + SL_3, + SL_4, + SL_5, + SL_6, + SL_7, + SL_8, + SL_9, + SL_10, + SL_11, + SL_12, + + ST_0, + ST_1, + ST_2, + ST_3, + ST_4, + ST_5, + ST_6, + ST_7, + ST_8, + ST_9, + ST_10, + ST_11, + ST_12, +} + +export enum SDVXDans { + DAN_1, + DAN_2, + DAN_3, + DAN_4, + DAN_5, + DAN_6, + DAN_7, + DAN_8, + DAN_9, + DAN_10, + DAN_11, + INF, +} + +// ewww +export enum SDVXVFClasses { + SIENNA_I, + SIENNA_II, + SIENNA_III, + SIENNA_IV, + COBALT_I, + COBALT_II, + COBALT_III, + COBALT_IV, + DANDELION_I, + DANDELION_II, + DANDELION_III, + DANDELION_IV, + CYAN_I, + CYAN_II, + CYAN_III, + CYAN_IV, + SCARLET_I, + SCARLET_II, + SCARLET_III, + SCARLET_IV, + CORAL_I, + CORAL_II, + CORAL_III, + CORAL_IV, + ARGENTO_I, + ARGENTO_II, + ARGENTO_III, + ARGENTO_IV, + ELDORA_I, + ELDORA_II, + ELDORA_III, + ELDORA_IV, + CRIMSON_I, + CRIMSON_II, + CRIMSON_III, + CRIMSON_IV, + IMPERIAL_I, + IMPERIAL_II, + IMPERIAL_III, + IMPERIAL_IV, +} diff --git a/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.test.ts b/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.test.ts index 22c178e93..e852d5013 100644 --- a/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.test.ts +++ b/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.test.ts @@ -1,11 +1,20 @@ import t from "tap"; -import { CalculateGitadoraColour, GitadoraSkillToColour } from "./builtin-class-handlers"; +import { GitadoraColours, SDVXVFClasses } from "../../../constants/classes"; +import CreateLogCtx from "../../../logger/logger"; +import { + CalculateGitadoraColour, + GitadoraSkillToColour, + CalculateSDVXClass, + SDVXVF5ToClass, +} from "./builtin-class-handlers"; + +const logger = CreateLogCtx(__filename); t.test("#CalculateGitadoraColour", (t) => { t.strictSame( - CalculateGitadoraColour("gitadora", "Gita", 1, { skill: 1500 }), + CalculateGitadoraColour("gitadora", "Gita", 1, { skill: 1500 }, logger), { - skillColour: "orangegradient", + colour: GitadoraColours.ORANGE_GRADIENT, }, "Should wrap the skill colour." ); @@ -14,24 +23,85 @@ t.test("#CalculateGitadoraColour", (t) => { }); t.test("#GitadoraSkillToColour", (t) => { - t.equal(GitadoraSkillToColour(0), "white"); - t.equal(GitadoraSkillToColour(999), "white"); - t.equal(GitadoraSkillToColour(1000), "orange"); - t.equal(GitadoraSkillToColour(1500), "orangegradient"); - t.equal(GitadoraSkillToColour(2000), "yellow"); - t.equal(GitadoraSkillToColour(2500), "yellowgradient"); - t.equal(GitadoraSkillToColour(3000), "green"); - t.equal(GitadoraSkillToColour(3500), "greengradient"); - t.equal(GitadoraSkillToColour(4000), "blue"); - t.equal(GitadoraSkillToColour(4500), "bluegradient"); - t.equal(GitadoraSkillToColour(5000), "purple"); - t.equal(GitadoraSkillToColour(5500), "purplegradient"); - t.equal(GitadoraSkillToColour(6000), "red"); - t.equal(GitadoraSkillToColour(6500), "redgradient"); - t.equal(GitadoraSkillToColour(7000), "bronze"); - t.equal(GitadoraSkillToColour(7500), "silver"); - t.equal(GitadoraSkillToColour(8000), "gold"); - t.equal(GitadoraSkillToColour(8500), "rainbow"); + t.equal(GitadoraSkillToColour(0), GitadoraColours.WHITE); + t.equal(GitadoraSkillToColour(999), GitadoraColours.WHITE); + t.equal(GitadoraSkillToColour(1000), GitadoraColours.ORANGE); + t.equal(GitadoraSkillToColour(1500), GitadoraColours.ORANGE_GRADIENT); + t.equal(GitadoraSkillToColour(2000), GitadoraColours.YELLOW); + t.equal(GitadoraSkillToColour(2500), GitadoraColours.YELLOW_GRADIENT); + t.equal(GitadoraSkillToColour(3000), GitadoraColours.GREEN); + t.equal(GitadoraSkillToColour(3500), GitadoraColours.GREEN_GRADIENT); + t.equal(GitadoraSkillToColour(4000), GitadoraColours.BLUE); + t.equal(GitadoraSkillToColour(4500), GitadoraColours.BLUE_GRADIENT); + t.equal(GitadoraSkillToColour(5000), GitadoraColours.PURPLE); + t.equal(GitadoraSkillToColour(5500), GitadoraColours.PURPLE_GRADIENT); + t.equal(GitadoraSkillToColour(6000), GitadoraColours.RED); + t.equal(GitadoraSkillToColour(6500), GitadoraColours.RED_GRADIENT); + t.equal(GitadoraSkillToColour(7000), GitadoraColours.BRONZE); + t.equal(GitadoraSkillToColour(7500), GitadoraColours.SILVER); + t.equal(GitadoraSkillToColour(8000), GitadoraColours.GOLD); + t.equal(GitadoraSkillToColour(8500), GitadoraColours.RAINBOW); + + t.end(); +}); + +t.test("#CalculateSDVXClass", (t) => { + t.strictSame(CalculateSDVXClass("sdvx", "Single", 1, { VF5: 10 }, logger), { + vfClass: SDVXVFClasses.COBALT_I, + }); + + t.end(); +}); + +t.test("#SDVXVF5ToClass", (t) => { + function f(vf: number) { + return SDVXVF5ToClass(vf, logger); + } + + t.equal(f(0), SDVXVFClasses.SIENNA_I); + t.equal(f(2.5), SDVXVFClasses.SIENNA_II); + t.equal(f(5), SDVXVFClasses.SIENNA_III); + t.equal(f(7.5), SDVXVFClasses.SIENNA_IV); + t.equal(f(10), SDVXVFClasses.COBALT_I); + t.equal(f(10.5), SDVXVFClasses.COBALT_II); + t.equal(f(11), SDVXVFClasses.COBALT_III); + t.equal(f(11.5), SDVXVFClasses.COBALT_IV); + t.equal(f(12), SDVXVFClasses.DANDELION_I); + t.equal(f(12.5), SDVXVFClasses.DANDELION_II); + t.equal(f(13), SDVXVFClasses.DANDELION_III); + t.equal(f(13.5), SDVXVFClasses.DANDELION_IV); + + t.equal(f(14), SDVXVFClasses.CYAN_I); + t.equal(f(14.25), SDVXVFClasses.CYAN_II); + t.equal(f(14.5), SDVXVFClasses.CYAN_III); + t.equal(f(14.75), SDVXVFClasses.CYAN_IV); + t.equal(f(15), SDVXVFClasses.SCARLET_I); + t.equal(f(15.25), SDVXVFClasses.SCARLET_II); + t.equal(f(15.5), SDVXVFClasses.SCARLET_III); + t.equal(f(15.75), SDVXVFClasses.SCARLET_IV); + t.equal(f(16), SDVXVFClasses.CORAL_I); + t.equal(f(16.25), SDVXVFClasses.CORAL_II); + t.equal(f(16.5), SDVXVFClasses.CORAL_III); + t.equal(f(16.75), SDVXVFClasses.CORAL_IV); + t.equal(f(17), SDVXVFClasses.ARGENTO_I); + t.equal(f(17.25), SDVXVFClasses.ARGENTO_II); + t.equal(f(17.5), SDVXVFClasses.ARGENTO_III); + t.equal(f(17.75), SDVXVFClasses.ARGENTO_IV); + t.equal(f(18), SDVXVFClasses.ELDORA_I); + t.equal(f(18.25), SDVXVFClasses.ELDORA_II); + t.equal(f(18.5), SDVXVFClasses.ELDORA_III); + t.equal(f(18.75), SDVXVFClasses.ELDORA_IV); + t.equal(f(19), SDVXVFClasses.CRIMSON_I); + t.equal(f(19.25), SDVXVFClasses.CRIMSON_II); + t.equal(f(19.5), SDVXVFClasses.CRIMSON_III); + t.equal(f(19.75), SDVXVFClasses.CRIMSON_IV); + + t.equal(f(20), SDVXVFClasses.IMPERIAL_I); + t.equal(f(21), SDVXVFClasses.IMPERIAL_II); + t.equal(f(22), SDVXVFClasses.IMPERIAL_III); + t.equal(f(23), SDVXVFClasses.IMPERIAL_IV); + t.equal(f(24), SDVXVFClasses.IMPERIAL_IV); + t.equal(f(1000), SDVXVFClasses.IMPERIAL_IV); t.end(); }); diff --git a/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts b/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts index 54d8796d7..363db75b8 100644 --- a/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts +++ b/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts @@ -1,54 +1,97 @@ -import { Game, Playtypes, integer } from "kamaitachi-common"; +import { Game, Playtypes, integer, GameSpecificCalcLookup } from "kamaitachi-common"; +import { GameClasses } from "kamaitachi-common/js/game-classes"; +import { GitadoraColours, SDVXVFClasses } from "../../../constants/classes"; +import { KtLogger } from "../../../logger/logger"; + +export function CalculateSDVXClass( + game: Game, + playtype: Playtypes[Game], + userID: integer, + customRatings: Partial>, + logger: KtLogger +): Partial> { + if (!customRatings.VF5) { + return {}; + } + + return { vfClass: SDVXVF5ToClass(customRatings.VF5, logger) }; +} + +/** + * Converts a user's profile VF5 to a class. There are almost 40 classes, and writing + * if statements for all of them is a painful endeavour. This function returns an index + * derived from the gaps between classes, to avoid writing out all those statements. + * @see https://bemaniwiki.com/index.php?SOUND%20VOLTEX%20VIVID%20WAVE/VOLFORCE + */ +export function SDVXVF5ToClass(vf: number, logger: KtLogger) { + // This is impossible, but a failsafe regardless + if (vf >= 24) { + logger.warn(`User has excessive VF5 of ${vf}. Defaulting to Imperial IV.`); + return SDVXVFClasses.IMPERIAL_IV; + } else if (vf >= 20) { + // imperial i -> iv has gaps of 1 + return SDVXVFClasses.IMPERIAL_I + Math.floor(vf - 20); + } else if (vf >= 14) { + // cyan i -> crimson iv has gaps of 0.25 + return SDVXVFClasses.CYAN_I + Math.floor(4 * (vf - 14)); + } else if (vf >= 10) { + // cobalt i -> dandelion iv have gaps of 0.5 + return SDVXVFClasses.COBALT_I + Math.floor(2 * (vf - 10)); + } + + return Math.floor(vf / 2.5); +} export function CalculateGitadoraColour( game: Game, playtype: Playtypes[Game], userID: integer, - customRatings: Record -): Record { - const skillColour = GitadoraSkillToColour(customRatings.skill); + customRatings: Record, + logger: KtLogger +): Partial> { + const colour = GitadoraSkillToColour(customRatings.skill); return { - skillColour, + colour, }; } export function GitadoraSkillToColour(sk: number) { if (sk >= 8500) { - return "rainbow"; + return GitadoraColours.RAINBOW; } else if (sk >= 8000) { - return "gold"; + return GitadoraColours.GOLD; } else if (sk >= 7500) { - return "silver"; + return GitadoraColours.SILVER; } else if (sk >= 7000) { - return "bronze"; + return GitadoraColours.BRONZE; } else if (sk >= 6500) { - return "redgradient"; + return GitadoraColours.RED_GRADIENT; } else if (sk >= 6000) { - return "red"; + return GitadoraColours.RED; } else if (sk >= 5500) { - return "purplegradient"; + return GitadoraColours.PURPLE_GRADIENT; } else if (sk >= 5000) { - return "purple"; + return GitadoraColours.PURPLE; } else if (sk >= 4500) { - return "bluegradient"; + return GitadoraColours.BLUE_GRADIENT; } else if (sk >= 4000) { - return "blue"; + return GitadoraColours.BLUE; } else if (sk >= 3500) { - return "greengradient"; + return GitadoraColours.GREEN_GRADIENT; } else if (sk >= 3000) { - return "green"; + return GitadoraColours.GREEN; } else if (sk >= 2500) { - return "yellowgradient"; + return GitadoraColours.YELLOW_GRADIENT; } else if (sk >= 2000) { - return "yellow"; + return GitadoraColours.YELLOW; } else if (sk >= 1500) { - return "orangegradient"; + return GitadoraColours.ORANGE_GRADIENT; } else if (sk >= 1000) { - return "orange"; + return GitadoraColours.ORANGE; } - return "white"; + return GitadoraColours.WHITE; } export function CalculateJubeatColour( @@ -56,7 +99,7 @@ export function CalculateJubeatColour( playtype: Playtypes[Game], userID: integer, customRatings: Record -): Promise> { +): GameClasses<"jubeat:Single"> { throw new Error("Not implemented."); } diff --git a/server/src/lib/score-import/framework/user-game-stats/classes.test.ts b/server/src/lib/score-import/framework/user-game-stats/classes.test.ts index 1d0bca715..ba7c3b2d8 100644 --- a/server/src/lib/score-import/framework/user-game-stats/classes.test.ts +++ b/server/src/lib/score-import/framework/user-game-stats/classes.test.ts @@ -4,6 +4,7 @@ import CreateLogCtx from "../../../logger/logger"; import ResetDBState from "../../../../test-utils/reset-db-state"; import { CalculateClassDeltas, UpdateUGSClasses } from "./classes"; import { CloseAllConnections } from "../../../../test-utils/close-connections"; +import { GitadoraColours } from "../../../constants/classes"; const logger = CreateLogCtx(__filename); @@ -36,7 +37,7 @@ t.test("#UpdateUGSClasses", (t) => { logger ); - t.strictSame(res, { skillColour: "rainbow" }); + t.strictSame(res, { colour: GitadoraColours.RAINBOW }); t.end(); }); diff --git a/server/src/lib/score-import/framework/user-game-stats/classes.ts b/server/src/lib/score-import/framework/user-game-stats/classes.ts index f1aa06564..28c21c14e 100644 --- a/server/src/lib/score-import/framework/user-game-stats/classes.ts +++ b/server/src/lib/score-import/framework/user-game-stats/classes.ts @@ -1,7 +1,11 @@ import { Game, Playtypes, integer, UserGameStats, ClassDelta, IDStrings } from "kamaitachi-common"; import deepmerge from "deepmerge"; import { KtLogger } from "../../../logger/logger"; -import { CalculateGitadoraColour, CalculateJubeatColour } from "./builtin-class-handlers"; +import { + CalculateGitadoraColour, + CalculateJubeatColour, + CalculateSDVXClass, +} from "./builtin-class-handlers"; import { ReturnClassIfGreater } from "../../../../utils/class"; import { GameClasses } from "kamaitachi-common/js/game-classes"; import { RedisPub } from "../../../../external/redis/redis-IPC"; @@ -50,7 +54,9 @@ const STATIC_CLASS_HANDLERS: ClassHandlerMap = { maimai: null, museca: null, popn: null, - sdvx: null, + sdvx: { + Single: CalculateSDVXClass, + }, usc: null, }; @@ -92,7 +98,8 @@ export async function UpdateUGSClasses( game, playtype, userID, - customRatings + customRatings, + logger ); } diff --git a/server/src/server/router/api/v1/import/router.test.ts b/server/src/server/router/api/v1/import/router.test.ts index e018e47be..c9adc4f13 100644 --- a/server/src/server/router/api/v1/import/router.test.ts +++ b/server/src/server/router/api/v1/import/router.test.ts @@ -324,9 +324,3 @@ t.test("POST /api/v1/import/file", async (t) => { }); t.teardown(CloseAllConnections); - -import winr from "why-is-node-running"; - -setTimeout(() => { - winr(); -}, 80_000);