From daf81cf923dfd0365441708d2a4f6129f88ef0ac Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Thu, 23 Dec 2021 01:57:40 +0000 Subject: [PATCH] Rough baseline for pop'n music support. --- server/package.json | 2 +- server/pnpm-lock.yaml | 8 +- server/src/external/mongo/db.ts | 2 - server/src/external/mongo/schemas.ts | 13 +- server/src/lib/constants/classes.ts | 11 ++ .../calculated-data/calculated-data.ts | 151 ++++++++---------- .../framework/sessions/calculated-data.ts | 8 +- .../user-game-stats/builtin-class-handlers.ts | 32 ++++ .../framework/user-game-stats/classes.ts | 5 +- .../framework/user-game-stats/rating.ts | 8 +- 10 files changed, 140 insertions(+), 100 deletions(-) diff --git a/server/package.json b/server/package.json index 166d3e94d..f5ab4fe3d 100644 --- a/server/package.json +++ b/server/package.json @@ -82,7 +82,7 @@ "rimraf": "3.0.2", "safe-json-stringify": "1.2.0", "seq-logging": "^1.1.1", - "tachi-common": "0.4.4", + "tachi-common": "0.4.7", "ts-node": "10.0.0", "tsconfig-paths": "3.10.1", "typescript": "4.3.4", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index 68a260151..bbba342d8 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -63,7 +63,7 @@ specifiers: safe-json-stringify: 1.2.0 seq-logging: ^1.1.1 supertest: 6.1.3 - tachi-common: 0.4.4 + tachi-common: 0.4.7 tap: 15.0.9 ts-node: 10.0.0 tsconfig-paths: 3.10.1 @@ -107,7 +107,7 @@ dependencies: rimraf: 3.0.2 safe-json-stringify: 1.2.0 seq-logging: 1.1.1 - tachi-common: 0.4.4 + tachi-common: 0.4.7 ts-node: 10.0.0_83f53b0a0c5616d3fa00ed4e30b9ce1b tsconfig-paths: 3.10.1 typescript: 4.3.4 @@ -5386,8 +5386,8 @@ packages: strip-ansi: 6.0.0 dev: true - /tachi-common/0.4.4: - resolution: {integrity: sha512-vXLMkMUCEE5ja57HYSLqM7Mhe+oj416JPIKGMdz5ctrwjf7BX4eU8rEAYoBbibvl57SOFLylT6Bm1icyJuQC0w==} + /tachi-common/0.4.7: + resolution: {integrity: sha512-tlntA6tOJSG7l2GNx7byktIma/2upSr+s1tTS1c/t+6IUCED9scsCTwDcqaRSkjzz+fvx3SMg79jyyKIu0Wt9Q==} dependencies: monk: 7.3.4 transitivePeerDependencies: diff --git a/server/src/external/mongo/db.ts b/server/src/external/mongo/db.ts index 0a4d61f1c..da34edd22 100644 --- a/server/src/external/mongo/db.ts +++ b/server/src/external/mongo/db.ts @@ -15,7 +15,6 @@ import { FolderDocument, Game, GoalDocument, - IIDXBPIData, ImportDocument, ImportTimingsDocument, integer, @@ -157,7 +156,6 @@ const db = { imports: monkDB.get("imports"), "import-timings": monkDB.get("import-timings"), sessions: monkDB.get("sessions"), - "iidx-bpi-data": monkDB.get("iidx-bpi-data"), invites: monkDB.get("invites"), counters: monkDB.get("counters"), "game-stats": monkDB.get("game-stats"), diff --git a/server/src/external/mongo/schemas.ts b/server/src/external/mongo/schemas.ts index 796c9c3fe..0ef654ee2 100644 --- a/server/src/external/mongo/schemas.ts +++ b/server/src/external/mongo/schemas.ts @@ -773,6 +773,12 @@ export const DatabaseSchemas: Record = { displayVersion: "?string", }) ), + "songs-popn": prSchemaify( + PR_SongDocument({ + genre: "string", + displayVersion: "?string", + }) + ), "charts-iidx": (self) => { const playtype = getPlaytype("iidx", self); @@ -857,7 +863,12 @@ export const DatabaseSchemas: Record = { }, "charts-wacca": prSchemaify( PR_ChartDocument("wacca", "Single", { - releaseVersion: p.isIn("wacca", "s", "lily", "lily-r", "reverse"), + isHot: "boolean", + }) + ), + "charts-popn": prSchemaify( + PR_ChartDocument("popn", "9B", { + hashSHA256: "?string", }) ), goals: prSchemaify({ diff --git a/server/src/lib/constants/classes.ts b/server/src/lib/constants/classes.ts index 7e787c6b8..c76034c76 100644 --- a/server/src/lib/constants/classes.ts +++ b/server/src/lib/constants/classes.ts @@ -184,3 +184,14 @@ export enum WACCA_COLOURS { GOLD, RAINBOW, } + +export enum POPN_CLASSES { + KITTY, + GRADE_SCHOOL, + DELINQUENT, + DETECTIVE, + IDOL, + GENERAL, + HERMIT, + GOD, +} 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 c6dc8b0ac..e2c22873b 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 @@ -45,57 +45,30 @@ export async function CreateCalculatedData( } type CalculatedDataFunctions = { - [G in Game]: { - [P in Playtypes[G]]: ( - dryScore: DryScore, - chart: ChartDocument, - logger: KtLogger - ) => Promise | ScoreDocument["calculatedData"]; - }; + [I in IDStrings]: ( + dryScore: DryScore, + chart: ChartDocument, + logger: KtLogger + ) => Promise["calculatedData"]> | ScoreDocument["calculatedData"]; }; const CalculatedDataFunctions: CalculatedDataFunctions = { - iidx: { - SP: CalculateDataIIDXSP, - DP: CalculateDataIIDXDP, - }, - sdvx: { - Single: CalculateDataSDVXorUSC, - }, - // popn: { - // "9B": () => ({}), - // }, - museca: { - Single: CalculateDataMuseca, - }, - chunithm: { - Single: CalculateDataCHUNITHM, - }, - maimai: { - Single: CalculateDataMaimai, - }, - gitadora: { - Gita: CalculateDataGitadora, - Dora: CalculateDataGitadora, - }, - bms: { - "7K": CalculateDataBMS7K, - "14K": CalculateDataBMS14K, - }, - ddr: { - SP: CalculateDataDDR, - DP: CalculateDataDDR, - }, - // jubeat: { - // Single: CalculateDataJubeat, - // }, - usc: { - Controller: CalculateDataSDVXorUSC, - Keyboard: CalculateDataSDVXorUSC, - }, - wacca: { - Single: CalculateDataWACCA, - }, + "iidx:SP": CalculateDataIIDXSP, + "iidx:DP": CalculateDataIIDXDP, + "sdvx:Single": CalculateDataSDVXorUSC, + "popn:9B": CalculateDataPopn, + "museca:Single": CalculateDataMuseca, + "chunithm:Single": CalculateDataCHUNITHM, + "maimai:Single": CalculateDataMaimai, + "gitadora:Gita": CalculateDataGitadora, + "gitadora:Dora": CalculateDataGitadora, + "bms:7K": CalculateDataBMS7K, + "bms:14K": CalculateDataBMS14K, + "ddr:SP": CalculateDataDDR, + "ddr:DP": CalculateDataDDR, + "usc:Controller": CalculateDataSDVXorUSC, + "usc:Keyboard": CalculateDataSDVXorUSC, + "wacca:Single": CalculateDataWACCA, }; // Creates Game-Specific calculatedData for the provided game & playtype. @@ -110,39 +83,26 @@ export async function CalculateDataForGamePT( esd: number | null, logger: KtLogger ): Promise { - const GameRatingFns = CalculatedDataFunctions[game]; - - if (!HasOwnProperty(GameRatingFns, playtype)) { - logger.error( - `Invalid playtype of ${playtype} given for game ${game} in CalculateDataForGamePT, returning an empty object.` - ); - return {}; - } - - // @ts-expect-error standard game->pt stuff. - return GameRatingFns[playtype](dryScore, chart, logger); + // @ts-expect-error too many minor complains here... + return CalculatedDataFunctions[`${game}:${playtype}` as IDStrings](dryScore, chart, logger); } type CalculatedData = Required["calculatedData"]>; -async function CalculateDataIIDXSP( +function CalculateDataIIDXSP( dryScore: DryScore, - chart: ChartDocument, + chart: ChartDocument<"iidx:SP">, logger: KtLogger -): Promise> { - const BPIData = await db["iidx-bpi-data"].findOne({ - chartID: chart.chartID, - }); - +): CalculatedData<"iidx:SP"> { let bpi; - if (BPIData) { + if (chart.data.kaidenAverage && chart.data.worldRecord) { bpi = CalculateBPI( - BPIData.kavg, - BPIData.wr, + chart.data.kaidenAverage, + chart.data.worldRecord, dryScore.scoreData.score, (chart as ChartDocument<"iidx:SP">).data.notecount * 2, - BPIData.coef + chart.data.bpiCoefficient ); // kesdc = esd === null ? null : CalculateKESDC(BPIData.kesd, esd); disabled @@ -156,24 +116,20 @@ async function CalculateDataIIDXSP( }; } -async function CalculateDataIIDXDP( +function CalculateDataIIDXDP( dryScore: DryScore, - chart: ChartDocument, + chart: ChartDocument<"iidx:SP" | "iidx:DP">, logger: KtLogger -): Promise> { - const BPIData = await db["iidx-bpi-data"].findOne({ - chartID: chart.chartID, - }); - +): CalculatedData<"iidx:DP"> { let bpi; - if (BPIData) { + if (chart.data.kaidenAverage && chart.data.worldRecord) { bpi = CalculateBPI( - BPIData.kavg, - BPIData.wr, + chart.data.kaidenAverage, + chart.data.worldRecord, dryScore.scoreData.score, (chart as ChartDocument<"iidx:DP">).data.notecount * 2, - BPIData.coef + chart.data.bpiCoefficient ); // kesdc = esd === null ? null : CalculateKESDC(BPIData.kesd, esd); disabled @@ -213,11 +169,11 @@ function CalculateDataSDVXorUSC( }; } -async function CalculateDataMuseca( +function CalculateDataMuseca( dryScore: DryScore, chart: ChartDocument, logger: KtLogger -): Promise> { +): CalculatedData<"museca:Single"> { return { ktRating: CalculateKTRating(dryScore, "museca", "Single", chart, logger), }; @@ -232,11 +188,11 @@ function CalculateDataCHUNITHM( }; } -async function CalculateDataMaimai( +function CalculateDataMaimai( dryScore: DryScore, chart: ChartDocument, logger: KtLogger -): Promise> { +): CalculatedData<"maimai:Single"> { // @todo #373 Add maimai rating algorithms. return { ktRating: 0, @@ -360,3 +316,28 @@ function CalculateDataWACCA( // How discrete. How boring! return { rate: scoreCoef * chart.levelNum }; } + +function CalculateDataPopn( + dryScore: DryScore, + chart: ChartDocument, + logger: KtLogger +): CalculatedData<"popn:9B"> { + const score = dryScore.scoreData.score; + const lamp = dryScore.scoreData.lamp; + + if (score <= 50000) { + return { classPoints: 0 }; + } + + let clearBonus = 0; + + if (lamp === "CLEAR" || lamp === "EASY CLEAR") { + clearBonus = 3000; + } else if (lamp === "FULL COMBO" || lamp === "PERFECT") { + clearBonus = 5000; + } + + return { + classPoints: (10_000 * chart.levelNum + score - 50_000 + clearBonus) / 5440, + }; +} diff --git a/server/src/lib/score-import/framework/sessions/calculated-data.ts b/server/src/lib/score-import/framework/sessions/calculated-data.ts index de5b680ef..d2ebaa940 100644 --- a/server/src/lib/score-import/framework/sessions/calculated-data.ts +++ b/server/src/lib/score-import/framework/sessions/calculated-data.ts @@ -62,9 +62,11 @@ const CalculatedDataFunctions: CalculatedDataFunctions = { }; }, }, - // popn: { - // "9B": () => ({}), - // }, + popn: { + "9B": (scd) => ({ + classPoints: AvgBest10Map(scd, "classPoints"), + }), + }, museca: { Single: (scd) => ({ ktRating: AvgBest10Map(scd, "ktRating"), 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 f87fe35dc..d872ca8b2 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,6 +1,7 @@ import { CHUNITHM_COLOURS, GitadoraColours, + POPN_CLASSES, SDVXVFClasses, WACCA_COLOURS, } from "lib/constants/classes"; @@ -132,6 +133,37 @@ function WACCARateToColour(rate: number) { return WACCA_COLOURS.ASH; } +export function CalculatePopnClass( + game: Game, + playtype: Playtypes[Game], + userID: integer, + ratings: Record +) { + const cls = PopnClassPointsToClass(ratings.naiveClassPoints); + + return { class: cls }; +} + +function PopnClassPointsToClass(points: number) { + if (points < 21) { + return POPN_CLASSES.KITTY; + } else if (points < 34) { + return POPN_CLASSES.GRADE_SCHOOL; + } else if (points < 46) { + return POPN_CLASSES.DELINQUENT; + } else if (points < 59) { + return POPN_CLASSES.DETECTIVE; + } else if (points < 68) { + return POPN_CLASSES.IDOL; + } else if (points < 79) { + return POPN_CLASSES.GENERAL; + } else if (points < 91) { + return POPN_CLASSES.HERMIT; + } + + return POPN_CLASSES.GOD; +} + export function CalculateChunithmColour( game: Game, playtype: Playtypes[Game], 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 d27fd9a10..1e172abe4 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 @@ -8,6 +8,7 @@ import { ReturnClassIfGreater } from "utils/class"; import { CalculateChunithmColour, CalculateGitadoraColour, + CalculatePopnClass, CalculateSDVXClass, CalculateWACCAColour, } from "./builtin-class-handlers"; @@ -46,7 +47,9 @@ const STATIC_CLASS_HANDLERS: ClassHandlerMap = { // }, maimai: null, museca: null, - // popn: null, + popn: { + "9B": CalculatePopnClass, + }, sdvx: { Single: CalculateSDVXClass, }, diff --git a/server/src/lib/score-import/framework/user-game-stats/rating.ts b/server/src/lib/score-import/framework/user-game-stats/rating.ts index cd0649217..f63d140c9 100644 --- a/server/src/lib/score-import/framework/user-game-stats/rating.ts +++ b/server/src/lib/score-import/framework/user-game-stats/rating.ts @@ -119,9 +119,11 @@ const RatingFunctions: RatingFunctions = { naiveRating: await LazyMeanN("rating", 20)(g, p, u), }), }, - // popn: { - // "9B": async () => ({}), - // }, + popn: { + "9B": async (g, p, u) => ({ + naiveClassPoints: await LazyMeanN("classPoints", 20)(g, p, u), + }), + }, museca: { Single: async (g, p, u) => ({ ktRating: await LazyMeanN("ktRating", 20)(g, p, u),