diff --git a/server/src/server/router/ir/beatoraja/charts/convert-scores.test.ts b/server/src/server/router/ir/beatoraja/charts/convert-scores.test.ts index e0d0e6775..218bb9770 100644 --- a/server/src/server/router/ir/beatoraja/charts/convert-scores.test.ts +++ b/server/src/server/router/ir/beatoraja/charts/convert-scores.test.ts @@ -1,11 +1,8 @@ import t from "tap"; -import db from "external/mongo/db"; import ResetDBState from "test-utils/resets"; import { GetKTDataJSON } from "test-utils/test-data"; -import { TachiPBScoreToBeatorajaFormat } from "./convert-scores"; -import { ScoreDocument, PBScoreDocument } from "tachi-common"; -import { Random20Hex } from "utils/misc"; -import deepmerge from "deepmerge"; +import { PBScoreDocument } from "tachi-common"; +import { TachiScoreDataToBeatorajaFormat } from "./convert-scores"; const gazerChart = GetKTDataJSON("./tachi/bms-gazer-chart.json"); @@ -24,43 +21,30 @@ const pbScore = { userID: 1, } as unknown as PBScoreDocument<"bms:7K" | "bms:14K">; -t.test("#TachiPBScoreToBeatorajaFormat", (t) => { +t.test("#TachiScoreDataToBeatorajaFormat", (t) => { t.beforeEach(ResetDBState); - t.beforeEach(async () => { - await db.scores.insert( - [1, 2, 3, 4, 5].map(() => ({ - userID: 1, - chartID: gazerChart.chartID, - scoreID: Random20Hex(), - })) as ScoreDocument[] + + t.test("Should convert score.", (t) => { + const res = TachiScoreDataToBeatorajaFormat( + pbScore, + gazerChart.data.hashSHA256, + "", + gazerChart.data.notecount, + 0 ); - await db.scores.insert({ - scoreID: "mock_lampPB", - scoreData: { - hitMeta: {}, - }, - scoreMeta: { - inputDevice: "KEYBOARD", - }, - } as ScoreDocument); - }); - - t.test("Should convert score.", async (t) => { - const res = await TachiPBScoreToBeatorajaFormat(pbScore, gazerChart, 1); - t.strictSame( res, { sha256: "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d", player: "", - playcount: 5, + playcount: 0, clear: 5, date: 0, maxcombo: 0, - deviceType: "KEYBOARD", + deviceType: null, gauge: 0, - random: 0, + random: null, passnotes: 0, minbp: 0, notes: 2256, @@ -71,85 +55,13 @@ t.test("#TachiPBScoreToBeatorajaFormat", (t) => { t.end(); }); - t.test("Should emplace username if requestingUserID is not the pbscore owner", async (t) => { - const res = await TachiPBScoreToBeatorajaFormat(pbScore, gazerChart, 2); - - t.strictSame( - res, - { - sha256: "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d", - player: "test_zkldi", - playcount: 5, - clear: 5, - date: 0, - maxcombo: 0, - deviceType: "KEYBOARD", - gauge: 0, - random: 0, - passnotes: 0, - minbp: 0, - notes: 2256, - }, - "Should return the beatoraja score format." - ); - - t.end(); - }); - - t.test("Should return random if one is present", async (t) => { - await db.scores.remove({ scoreID: "mock_lampPB" }); - await db.scores.insert({ - scoreID: "mock_lampPB", - scoreData: { - hitMeta: {}, - }, - scoreMeta: { - inputDevice: "KEYBOARD", - random: "RANDOM", - }, - } as ScoreDocument); - - const res = await TachiPBScoreToBeatorajaFormat(pbScore, gazerChart, 2); - - t.strictSame( - res, - { - sha256: "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d", - player: "test_zkldi", - playcount: 5, - clear: 5, - date: 0, - maxcombo: 0, - deviceType: "KEYBOARD", - gauge: 0, - random: 2, - passnotes: 0, - minbp: 0, - notes: 2256, - }, - "Should return the beatoraja score format." - ); - - t.end(); - }); - - t.test("Should skip random if score is 14K", async (t) => { - await db.scores.remove({ scoreID: "mock_lampPB" }); - await db.scores.insert({ - scoreID: "mock_lampPB", - scoreData: { - hitMeta: {}, - }, - scoreMeta: { - inputDevice: "KEYBOARD", - random: "RANDOM", - }, - } as ScoreDocument); - - const res = await TachiPBScoreToBeatorajaFormat( - deepmerge(pbScore, { playtype: "14K" }), - gazerChart, - 2 + t.test("Should emplace username if requestingUserID is not the pbscore owner", (t) => { + const res = TachiScoreDataToBeatorajaFormat( + pbScore, + gazerChart.data.hashSHA256, + "test_zkldi", + gazerChart.data.notecount, + 0 ); t.strictSame( @@ -157,13 +69,13 @@ t.test("#TachiPBScoreToBeatorajaFormat", (t) => { { sha256: "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d", player: "test_zkldi", - playcount: 5, + playcount: 0, clear: 5, date: 0, maxcombo: 0, - deviceType: "KEYBOARD", + deviceType: null, gauge: 0, - random: 0, + random: null, passnotes: 0, minbp: 0, notes: 2256, @@ -174,15 +86,5 @@ t.test("#TachiPBScoreToBeatorajaFormat", (t) => { t.end(); }); - t.test("Should throw severe if no lampPB exists.", async (t) => { - await db.scores.remove({ scoreID: "mock_lampPB" }); - - t.rejects(() => TachiPBScoreToBeatorajaFormat(pbScore, gazerChart, 2), { - message: /User 1's PB on.*has no lampPB/u, - }); - - t.end(); - }); - t.end(); }); diff --git a/server/src/server/router/ir/beatoraja/charts/convert-scores.ts b/server/src/server/router/ir/beatoraja/charts/convert-scores.ts index 70798cc64..92cc25ed4 100644 --- a/server/src/server/router/ir/beatoraja/charts/convert-scores.ts +++ b/server/src/server/router/ir/beatoraja/charts/convert-scores.ts @@ -36,48 +36,6 @@ type BeatorajaPartialScoreFormat = { export type BeatorajaIRScoreFormat = BeatorajaPartialScoreFormat & BeatorajaScoreJudgements; -const logger = CreateLogCtx(__filename); - -/** - * Converts a Tachi Score PB into the beatoraja IR format. - * @param pbScore - The PB score to convert. - * @param chart - The chart document the PB score belongs to. - * @param requestingUserID - The user who requested this conversion. This is - * because beatoraja uses the empty string to dictate that the score was from - * the requesting user. - */ -export async function TachiPBScoreToBeatorajaFormat( - pbScore: PBScoreDocument<"bms:7K" | "bms:14K">, - chart: ChartDocument<"bms:7K" | "bms:14K">, - requestingUserID: integer -) { - const playcount = await db.scores.count({ userID: pbScore.userID, chartID: chart.chartID }); - const username = - pbScore.userID === requestingUserID ? "" : await GetUsernameFromUserID(pbScore.userID); - const lampPB = (await db.scores.findOne({ - scoreID: pbScore.composedFrom.lampPB, - })) as ScoreDocument<"bms:7K" | "bms:14K"> | null; - - if (!lampPB) { - logger.severe( - `User ${pbScore.userID}'s PB on ${chart.chartID} has no lampPB, but references ${pbScore.composedFrom.lampPB}.` - ); - throw new Error( - `User ${pbScore.userID}'s PB on ${chart.chartID} has no lampPB, but references ${pbScore.composedFrom.lampPB}.` - ); - } - - return TachiScoreDataToBeatorajaFormat( - pbScore, - chart.data.hashSHA256, - username, - chart.data.notecount, - playcount, - lampPB.scoreMeta.inputDevice, - lampPB.scoreMeta.random - ); -} - /** * Converts various data from Tachi to the beatoraja format. * @param pbScore - The users PB Score document for this chart. @@ -89,28 +47,15 @@ export async function TachiPBScoreToBeatorajaFormat( * @param random - What random modifier was used. * @returns A Beatoraja Score Document. */ -function TachiScoreDataToBeatorajaFormat( +export function TachiScoreDataToBeatorajaFormat( pbScore: PBScoreDocument<"bms:7K" | "bms:14K">, sha256: string, username: string, notecount: integer, - playcount: integer, - inputDevice: ScoreDocument<"bms:7K" | "bms:14K">["scoreMeta"]["inputDevice"], - random: ScoreDocument<"bms:7K" | "bms:14K">["scoreMeta"]["random"] + playcount: integer ) { const scoreData = pbScore.scoreData; - let rajaRandom = 0 as const; - - // Beatoraja does not support DP randoms - it only stores them in replay files, - // and only sends the left hand random here. - if (pbScore.playtype === "7K") { - if (random) { - // @ts-expect-error Invalid indexing because playtype removes the random tuple. - rajaRandom = RAN_INDEXES[random]; - } - } - const beatorajaScore: BeatorajaPartialScoreFormat = { sha256, player: username, @@ -118,9 +63,9 @@ function TachiScoreDataToBeatorajaFormat( clear: LAMP_TO_BEATORAJA[scoreData.lampIndex] ?? 0, date: pbScore.timeAchieved ?? 0, maxcombo: scoreData.hitMeta.maxCombo ?? 0, - deviceType: inputDevice ?? null, gauge: scoreData.hitMeta.gauge ?? 0, - random: rajaRandom, + deviceType: null, // These two are now unsupported due to performance concerns. + random: null, minbp: scoreData.hitMeta.bp ?? 0, passnotes: 0, notes: notecount, diff --git a/server/src/server/router/ir/beatoraja/charts/router.ts b/server/src/server/router/ir/beatoraja/charts/router.ts index 4b5f18370..78edacc63 100644 --- a/server/src/server/router/ir/beatoraja/charts/router.ts +++ b/server/src/server/router/ir/beatoraja/charts/router.ts @@ -1,8 +1,8 @@ import { ChartDocument, PBScoreDocument } from "tachi-common"; import { Router, RequestHandler } from "express"; import db from "external/mongo/db"; -import { SYMBOL_TachiAPIAuth, SYMBOL_TachiData } from "lib/constants/tachi"; -import { TachiPBScoreToBeatorajaFormat } from "./convert-scores"; +import { SYMBOL_TachiData } from "lib/constants/tachi"; +import { TachiScoreDataToBeatorajaFormat } from "./convert-scores"; import { AssignToReqTachiData } from "utils/req-tachi-data"; const router: Router = Router({ mergeParams: true }); @@ -37,14 +37,37 @@ router.get("/scores", async (req, res) => { chartID: chart.chartID, })) as PBScoreDocument<"bms:7K" | "bms:14K">[]; - // @todo #139 Optimise GET /ir/beatoraja/chart/:chartSHA256/scores - // @optimisable - This should be solved with a couple queries and a hashmap. - const beatorajaScores = await Promise.all( - scores.map((e) => - TachiPBScoreToBeatorajaFormat(e, chart, req[SYMBOL_TachiAPIAuth]!.userID!) - ) + const userDocs = await db.users.find( + { + id: { $in: scores.map((e) => e.userID) }, + }, + { + projection: { + id: 1, + username: 1, + }, + } ); + const userMap = new Map(); + for (const user of userDocs) { + userMap.set(user.id, user); + } + + const beatorajaScores = []; + + for (const score of scores) { + beatorajaScores.push( + TachiScoreDataToBeatorajaFormat( + score, + chart.data.hashSHA256, + userMap.get(score.userID).username, + chart.data.notecount, + 0 // Playcount is always 0 at the moment due to performance concerns. + ) + ); + } + return res.status(200).json({ success: true, description: `Successfully returned ${beatorajaScores.length}`,