Merge pull request #743 from TNG-dev/zkldi/issue-711

This commit is contained in:
zkldi
2022-11-04 20:46:34 +00:00
committed by GitHub
3 changed files with 255 additions and 44 deletions
@@ -6,7 +6,9 @@ import { GetGamePTConfig } from "tachi-common";
import t from "tap";
import ResetDBState from "test-utils/resets";
import { Testing511SPA, TestingBMS7KScore, TestingIIDXSPScore } from "test-utils/test-data";
import type { PBScoreDocumentNoRank } from "./create-pb-doc";
import type { KtLogger } from "lib/logger/logger";
import type { ScoreDocument } from "tachi-common";
const IIDXScore = TestingIIDXSPScore;
@@ -193,5 +195,161 @@ t.test("#CreatePBDoc", (t) => {
t.end();
});
t.test("(BMS) Should inherit BP from the best BP score.", async (t) => {
await db.scores.remove({});
await db.scores.insert([
TestingBMS7KScore,
deepmerge(TestingBMS7KScore, {
scoreData: {
lamp: "FULL COMBO",
lampIndex: lamps.indexOf("FULL COMBO"),
score: 0,
percent: 0,
hitMeta: {
bp: 15,
},
},
calculatedData: {
sieglinde: 500,
},
scoreID: "LAMP_PB_ID",
}),
deepmerge(TestingBMS7KScore, {
scoreData: {
hitMeta: {
bp: 1,
},
},
scoreID: "BP_PB_ID",
}),
]);
const res = (await CreatePBDoc(1, TestingBMS7KScore.chartID, logger)) as
| PBScoreDocumentNoRank<"bms:7K" | "bms:14K">
| undefined;
t.not(res, undefined, "Should actually return something.");
t.equal(
res?.scoreData.hitMeta.bp,
1,
"Should select the best BP's BP and not the score PBs."
);
t.strictSame(res?.composedFrom, {
lampPB: "LAMP_PB_ID",
scorePB: TestingBMS7KScore.scoreID,
other: [
{
name: "Best BP",
scoreID: "BP_PB_ID",
},
],
});
t.end();
});
t.test("(PMS) Should inherit BP from the best BP score.", async (t) => {
const pmsScore = deepmerge(TestingBMS7KScore, {
game: "pms",
playtype: "Controller",
}) as unknown as ScoreDocument<"pms:Controller">;
await db.scores.remove({});
await db.scores.insert([
pmsScore,
deepmerge(pmsScore, {
scoreData: {
lamp: "FULL COMBO",
lampIndex: lamps.indexOf("FULL COMBO"),
score: 0,
percent: 0,
hitMeta: {
bp: 15,
},
},
calculatedData: {
sieglinde: 500,
},
scoreID: "LAMP_PB_ID",
}),
deepmerge(pmsScore, {
scoreData: {
hitMeta: {
bp: 1,
},
},
scoreID: "BP_PB_ID",
}),
]);
const res = (await CreatePBDoc(1, TestingBMS7KScore.chartID, logger)) as
| PBScoreDocumentNoRank<"bms:7K" | "bms:14K">
| undefined;
t.not(res, undefined, "Should actually return something.");
t.equal(
res?.scoreData.hitMeta.bp,
1,
"Should select the best BP's BP and not the score PBs."
);
t.strictSame(res?.composedFrom, {
lampPB: "LAMP_PB_ID",
scorePB: TestingBMS7KScore.scoreID,
other: [
{
name: "Best BP",
scoreID: "BP_PB_ID",
},
],
});
t.end();
});
t.test("(BMS) Should inherit graph data from the best lamp.", async (t) => {
await db.scores.remove({});
await db.scores.insert([
TestingBMS7KScore,
deepmerge(TestingBMS7KScore, {
scoreData: {
lamp: "FULL COMBO",
lampIndex: lamps.indexOf("FULL COMBO"),
score: 0,
percent: 0,
hitMeta: {
bp: 15,
gauge: 12,
gaugeHistory: [20, 20, 21, 12],
},
},
calculatedData: {
sieglinde: 500,
},
scoreID: "LAMP_PB_ID",
}),
]);
const res = (await CreatePBDoc(1, TestingBMS7KScore.chartID, logger)) as
| PBScoreDocumentNoRank<"bms:7K" | "bms:14K">
| undefined;
t.not(res, undefined, "Should actually return something.");
t.hasStrict(
res?.scoreData.hitMeta,
{
gauge: 12,
gaugeHistory: [20, 20, 21, 12],
},
"Should select the lampPBs gauge data and not the score PBs."
);
t.end();
});
t.end();
});
@@ -1,17 +1,28 @@
import {
BMSMergeFn,
IIDXMergeFn,
PMSMergeFn,
PopnMergeFn,
SDVXMergeFn,
USCMergeFn,
} from "./game-specific-merge";
import db from "external/mongo/db";
import { GetEveryonesRivalIDs, GetRivalIDs, GetRivalUsers } from "lib/rivals/rivals";
import { GetEveryonesRivalIDs } from "lib/rivals/rivals";
import type { KtLogger } from "lib/logger/logger";
import type { BulkWriteUpdateOneOperation } from "mongodb";
import type { Game, integer, PBScoreDocument, Playtype, ScoreDocument } from "tachi-common";
import type {
Game,
IDStrings,
integer,
PBScoreDocument,
Playtype,
ScoreDocument,
} from "tachi-common";
export type PBScoreDocumentNoRank = Omit<PBScoreDocument, "rankingData">;
export type PBScoreDocumentNoRank<I extends IDStrings = IDStrings> = Omit<
PBScoreDocument<I>,
"rankingData"
>;
export async function CreatePBDoc(userID: integer, chartID: string, logger: KtLogger) {
const scorePB = await db.scores.findOne(
@@ -200,8 +211,9 @@ function GetGameSpecificMergeFn(game: Game) {
case "popn":
return PopnMergeFn;
case "bms":
case "pms":
return BMSMergeFn;
case "pms":
return PMSMergeFn;
default:
return null;
}
@@ -25,44 +25,7 @@ export async function IIDXMergeFn(
DeleteUndefinedProps(pbDoc.scoreData.hitMeta);
// bad+poor PB document. This is a weird, third indepdenent metric that IIDX players sometimes care about.
const bpPB = (await db.scores.findOne(
{
userID: scorePB.userID,
chartID: scorePB.chartID,
"scoreData.hitMeta.bp": { $exists: true },
},
{
sort: {
// bp 0 is the best BP, bp 1 is worse, so on
"scoreData.hitMeta.bp": 1,
},
}
)) as ScoreDocument<"iidx:DP" | "iidx:SP"> | null;
if (!bpPB) {
logger.verbose(
`Could not find BP PB for ${scorePB.userID} ${scorePB.chartID} in PB joining. User likely has no scores with BP defined.`,
{ pbDoc }
);
// this isn't actually an error! we just don't have to do anything.
return true;
}
// by default scorePB is chosen for hitMeta fields, so, we can skip any assignments here by returning here.
if (bpPB.scoreID === scorePB.scoreID) {
logger.debug(`Skipped merging BP PB as scorePB was also BP PB.`);
return true;
} else if (bpPB.scoreID === lampPB.scoreID) {
pbDoc.scoreData.hitMeta.bp = lampPB.scoreData.hitMeta.bp;
logger.debug(`Skipped adding BP PB as composedFrom because lampPB was also BP PB.`);
return true;
}
pbDoc.scoreData.hitMeta.bp = bpPB.scoreData.hitMeta.bp!;
pbDoc.composedFrom.other = [{ name: "Best BP", scoreID: bpPB.scoreID }];
await MergeBPPB(pbDoc, scorePB, lampPB, logger);
return true;
}
@@ -78,14 +41,32 @@ export function PopnMergeFn(
return true;
}
export function BMSMergeFn(
export async function BMSMergeFn(
pbDoc: PBScoreDocument<"bms:7K" | "bms:14K">,
scorePB: ScoreDocument<"bms:7K" | "bms:14K">,
lampPB: ScoreDocument<"bms:7K" | "bms:14K">,
_logger: KtLogger
logger: KtLogger
) {
pbDoc.calculatedData.sieglinde = lampPB.calculatedData.sieglinde;
pbDoc.scoreData.hitMeta.gaugeHistory = lampPB.scoreData.hitMeta.gaugeHistory;
pbDoc.scoreData.hitMeta.gauge = lampPB.scoreData.hitMeta.gauge;
await MergeBPPB(pbDoc, scorePB, lampPB, logger);
return true;
}
export async function PMSMergeFn(
pbDoc: PBScoreDocument<"pms:Controller" | "pms:Keyboard">,
scorePB: ScoreDocument<"pms:Controller" | "pms:Keyboard">,
lampPB: ScoreDocument<"pms:Controller" | "pms:Keyboard">,
logger: KtLogger
) {
pbDoc.calculatedData.sieglinde = lampPB.calculatedData.sieglinde;
await MergeBPPB(pbDoc, scorePB, lampPB, logger);
return true;
}
@@ -168,3 +149,63 @@ export async function SDVXMergeFn(
return true;
}
type IDStringsWithBP =
| "bms:7K"
| "bms:14K"
| "iidx:DP"
| "iidx:SP"
| "pms:Controller"
| "pms:Keyboard";
/**
* Given typical PB-Merge information, fetch the best `bp` for this user's scores
* on this chart and merge it with the `pbDoc` if it's large enough.
*
* @returns NOTHING, mutates original input.
*/
async function MergeBPPB(
pbDoc: PBScoreDocument<IDStringsWithBP>,
scorePB: ScoreDocument<IDStringsWithBP>,
lampPB: ScoreDocument<IDStringsWithBP>,
logger: KtLogger
) {
// bad+poor PB document. This is a weird, third indepdenent metric that IIDX players sometimes care about.
const bpPB = (await db.scores.findOne(
{
userID: scorePB.userID,
chartID: scorePB.chartID,
"scoreData.hitMeta.bp": { $exists: true },
},
{
sort: {
// bp 0 is the best BP, bp 1 is worse, so on
"scoreData.hitMeta.bp": 1,
},
}
)) as ScoreDocument<"iidx:DP" | "iidx:SP"> | null;
if (!bpPB) {
logger.verbose(
`Could not find BP PB for ${scorePB.userID} ${scorePB.chartID} in PB joining. User likely has no scores with BP defined.`,
{ pbDoc }
);
// this isn't actually an error! we just don't have to do anything.
return;
}
// by default scorePB is chosen for hitMeta fields, so, we can skip any assignments here by returning here.
if (bpPB.scoreID === scorePB.scoreID) {
logger.debug(`Skipped merging BP PB as scorePB was also BP PB.`);
return true;
} else if (bpPB.scoreID === lampPB.scoreID) {
pbDoc.scoreData.hitMeta.bp = lampPB.scoreData.hitMeta.bp;
logger.debug(`Skipped adding BP PB as composedFrom because lampPB was also BP PB.`);
return;
}
pbDoc.scoreData.hitMeta.bp = bpPB.scoreData.hitMeta.bp;
pbDoc.composedFrom.other = [{ name: "Best BP", scoreID: bpPB.scoreID }];
}