From 7eaa8bc06ba607ddeffd3e95682aaa2c6f48172e Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Thu, 27 Oct 2022 19:38:36 +0100 Subject: [PATCH] fix(lr2hook): reject BP if user failed LR2Hook sends invalid BP if the user ends up exiting the song early. We need to handle this, and not accept it. --- server/src/lib/migration/migrations.ts | 3 +- .../migrations/null-lr2hook-failed-bps.ts | 31 ++++++++++++ .../import-types/ir/lr2hook/converter.test.ts | 47 +++++++++++++++++++ .../import-types/ir/lr2hook/converter.ts | 17 +++++-- 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 server/src/lib/migration/migrations/null-lr2hook-failed-bps.ts diff --git a/server/src/lib/migration/migrations.ts b/server/src/lib/migration/migrations.ts index 16eeaa821..3cd8a88ca 100644 --- a/server/src/lib/migration/migrations.ts +++ b/server/src/lib/migration/migrations.ts @@ -1,5 +1,6 @@ import UGPTAddPreferredRanking from "./migrations/add-preferredRanking-to-ugpt"; import UGPTRivalsMigration from "./migrations/add-rivals-to-ugpt"; +import NullLR2HookFailedBPs from "./migrations/null-lr2hook-failed-bps"; import RecalcBrokenIIDXNotecounts from "./migrations/recalc-broken-iidx-notecounts"; import RemoveIIDXBeginners from "./migrations/remove-iidx-beginners"; import RemoveMultifolderStats from "./migrations/remove-multifolder-stats"; @@ -44,7 +45,7 @@ if (Environment.nodeEnv !== "test") { // bokutachi specific migrations if (TachiConfig.TYPE !== "ktchi") { - // none, currently + REGISTERED_MIGRATIONS.push(NullLR2HookFailedBPs); } } diff --git a/server/src/lib/migration/migrations/null-lr2hook-failed-bps.ts b/server/src/lib/migration/migrations/null-lr2hook-failed-bps.ts new file mode 100644 index 000000000..93b1e3ce5 --- /dev/null +++ b/server/src/lib/migration/migrations/null-lr2hook-failed-bps.ts @@ -0,0 +1,31 @@ +import db from "external/mongo/db"; +import { UpdateAllPBs } from "utils/calculations/recalc-scores"; +import type { Migration } from "utils/types"; + +const migration: Migration = { + id: "null-lr2hook-failed-bps", + up: async () => { + await db.scores.update( + { + importType: "ir/lr2hook", + "scoreMeta.gauge": "HARD", + lamp: { $in: ["FAILED", "NO PLAY"] }, + }, + { + $set: { + "scoreData.hitMeta.bp": null, + }, + }, + { + multi: true, + } + ); + + await UpdateAllPBs(undefined, { game: "bms" }); + }, + down: () => { + throw new Error(`Unable to revert transaction, some scores may have been nulled.`); + }, +}; + +export default migration; diff --git a/server/src/lib/score-import/import-types/ir/lr2hook/converter.test.ts b/server/src/lib/score-import/import-types/ir/lr2hook/converter.test.ts index 6936460f4..f655c1c57 100644 --- a/server/src/lib/score-import/import-types/ir/lr2hook/converter.test.ts +++ b/server/src/lib/score-import/import-types/ir/lr2hook/converter.test.ts @@ -1,6 +1,7 @@ import { ConverterLR2Hook } from "./converter"; import CreateLogCtx from "lib/logger/logger"; import t from "tap"; +import { dmf } from "test-utils/misc"; import ResetDBState from "test-utils/resets"; import { TestingLR2HookScore } from "test-utils/test-data"; @@ -30,6 +31,52 @@ t.test("#ConverterLR2Hook", (t) => { dryScore: { scoreData: { score: TestingLR2HookScore.scoreData.exScore, + hitMeta: { + bp: 75, + }, + }, + game: "bms", + importType: "ir/lr2hook", + scoreMeta: { + client: "LR2", + }, + }, + }); + + t.end(); + }); + + t.test("Should null BP if the score was a fail with hard-gauge.", async (t) => { + const res = await ConverterLR2Hook( + dmf(TestingLR2HookScore, { + playerData: { + gauge: "HARD", + }, + scoreData: { + lamp: "FAIL", + }, + } as any), + { timeReceived: 10 }, + "ir/lr2hook", + logger + ); + + t.hasStrict(res, { + song: { + id: 27339, + }, + chart: { + chartID: "88eb6cc5683e2740cbd07f588a5f3db1db8d467b", + data: { + hashMD5: TestingLR2HookScore.md5, + }, + }, + dryScore: { + scoreData: { + score: TestingLR2HookScore.scoreData.exScore, + hitMeta: { + bp: null, + }, }, game: "bms", importType: "ir/lr2hook", diff --git a/server/src/lib/score-import/import-types/ir/lr2hook/converter.ts b/server/src/lib/score-import/import-types/ir/lr2hook/converter.ts index 0aefb0748..f96b6a993 100644 --- a/server/src/lib/score-import/import-types/ir/lr2hook/converter.ts +++ b/server/src/lib/score-import/import-types/ir/lr2hook/converter.ts @@ -36,6 +36,17 @@ export const ConverterLR2Hook: ConverterFunction = const { percent, grade } = GenericGetGradeAndPercent("bms", data.scoreData.exScore, chart); + const gauge = ConvertGauge(data.playerData.gauge); + const lamp = ConvertLamp(data.scoreData.lamp); + + let bp: number | null = data.scoreData.bad + data.scoreData.poor; + + // lr2hook doesn't send "max" BP so to speak. If you fail really early + // or quit out, you can have an early fail with like, 5 BP. + if ((lamp === "FAILED" || lamp === "NO PLAY") && gauge === "HARD") { + bp = null; + } + const dryScore: DryScore<"bms:7K" | "bms:14K"> = { game: "bms", service: "LR2Hook", @@ -46,7 +57,7 @@ export const ConverterLR2Hook: ConverterFunction = score: data.scoreData.exScore, percent, grade, - lamp: ConvertLamp(data.scoreData.lamp), + lamp, judgements: { pgreat: data.scoreData.pgreat, great: data.scoreData.great, @@ -55,14 +66,14 @@ export const ConverterLR2Hook: ConverterFunction = poor: data.scoreData.poor, }, hitMeta: { - bp: data.scoreData.bad + data.scoreData.poor, + bp, maxCombo: data.scoreData.maxCombo, gauge: data.scoreData.hpGraph[999] ?? 0, gaugeHistory: data.scoreData.hpGraph, }, }, scoreMeta: { - gauge: ConvertGauge(data.playerData.gauge), + gauge, random: chart.playtype === "7K" ? ConvertRandom(data.playerData.random) : null, client: "LR2", },