mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-03 04:18:16 +03:00
Merge pull request #743 from TNG-dev/zkldi/issue-711
This commit is contained in:
@@ -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 }];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user