ir/usc converters and parsers

This commit is contained in:
zkldi
2021-05-21 12:23:13 +01:00
parent 1bf8441d07
commit fd1dc2bf8a
5 changed files with 181 additions and 2 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ export interface USCClientScore {
near: integer;
error: integer;
options: {
gaugeType: integer;
gaugeType: 0 | 1;
gaugeOpt: integer;
mirror: boolean;
random: boolean;
+47 -1
View File
@@ -6,6 +6,10 @@ import { ChartDocument, PBScoreDocument } from "kamaitachi-common";
import { AssertStrAsPositiveNonZeroInt } from "../../../score-import/framework/common/string-asserts";
import CreateLogCtx from "../../../common/logger";
import { KtchiScoreToServerScore } from "./common";
import { ExpressWrappedScoreImportMain } from "../../../score-import/framework/express-wrapper";
import { GetUserWithID } from "../../../common/user";
import { KtLogger } from "../../../types";
import { ParseIRUSC } from "../../../score-import/import-types/ir/usc/parser";
const logger = CreateLogCtx("usc.ts");
@@ -57,6 +61,8 @@ const ValidateUSCRequest: RequestHandler = async (req, res, next) => {
}
req[SYMBOL_KtchiData] = { uscAuthDoc };
return next();
};
router.use(ValidateUSCRequest);
@@ -225,6 +231,46 @@ router.get("/charts/:chartHash/leaderboard", RetrieveChart, async (req, res) =>
* https://uscir.readthedocs.io/en/latest/endpoints/score-submit.html
* @name POST /api/ir/usc/scores
*/
router.post("/scores", async (req, res) => {});
router.post("/scores", async (req, res) => {
if (typeof req.body.chart !== "object" || req.body.chart === null) {
return res.status(200).json({
statusCode: STATUS_CODES.BAD_REQ,
description: "Invalid chart provided.",
});
}
if (typeof req.body.chart.chartHash !== "string") {
return res.status(200).json({
statusCode: STATUS_CODES.BAD_REQ,
description: "Invalid chart provided.",
});
}
const chartDoc = (await FindChartOnSHA256(
"usc",
req.body.chart.chartHash
)) as ChartDocument<"usc:Single"> | null;
if (!chartDoc) {
return res.status(200).json({
statusCode: STATUS_CODES.CHART_REFUSE,
description: "This chart is not supported.",
});
}
const userDoc = await GetUserWithID(req[SYMBOL_KtchiData]!.uscAuthDoc!.userID);
if (!userDoc) {
logger.severe(`User ${req[SYMBOL_KtchiData]!.uscAuthDoc!.userID} as no parent userDoc?`);
return res.status(200).json({
statusCode: STATUS_CODES.SERVER_ERROR,
description: "An internal server error has occured.",
});
}
const importParser = (logger: KtLogger) => ParseIRUSC(req.body, chartDoc, logger);
const importRes = await ExpressWrappedScoreImportMain(userDoc, false, "ir/usc", importParser);
});
export default router;
@@ -0,0 +1,78 @@
import { USCClientScore } from "../../../../api/ir/usc/common";
import { FindSongOnID } from "../../../../common/database-lookup/song";
import { ConverterFunction, DryScore, KtLogger } from "../../../../types";
import { InternalFailure } from "../../../framework/common/converter-failures";
import { GenericGetGradeAndPercent } from "../../../framework/common/score-utils";
import { IRUSCContext } from "./types";
import { Lamps } from "kamaitachi-common";
function DeriveNoteMod(data: USCClientScore): "NORMAL" | "MIRROR" | "RANDOM" | "MIR-RAN" {
if (data.options.mirror && data.options.random) {
return "MIR-RAN";
} else if (data.options.mirror) {
return "MIRROR";
} else if (data.options.random) {
return "RANDOM";
}
return "NORMAL";
}
function DeriveLamp(scoreDoc: USCClientScore, logger: KtLogger): Lamps["usc:Single"] {
if (scoreDoc.score === 10_000_000) {
return "PERFECT ULTIMATE CHAIN";
} else if (scoreDoc.error === 0) {
return "ULTIMATE CHAIN";
} else if (scoreDoc.options.gaugeType === 0) {
return scoreDoc.gauge >= 70 ? "CLEAR" : "FAILED";
} else if (scoreDoc.options.gaugeType === 1) {
return scoreDoc.gauge > 0 ? "EXCESSIVE CLEAR" : "FAILED";
}
logger.error(`Could not derive Lamp from Score Document`, { scoreDoc });
throw new InternalFailure(`Could not derive Lamp from Score Document`);
}
export const ConverterIRUSC: ConverterFunction<USCClientScore, IRUSCContext> = async (
data,
context,
importType,
logger
) => {
const song = await FindSongOnID("usc", context.chart.songID);
if (!song) {
logger.severe(`Song-Chart desync on USCIR ${context.chart.songID}.`);
throw new InternalFailure(`Song-Chart desync on USCIR ${context.chart.songID}.`);
}
const { grade, percent } = GenericGetGradeAndPercent("usc", data.score, context.chart);
const dryScore: DryScore<"usc:Single"> = {
comment: null,
game: "usc",
importType,
timeAchieved: Date.now(),
service: "USC-IR",
scoreData: {
grade,
percent,
score: data.score,
lamp: DeriveLamp(data, logger),
hitData: {
critical: data.crit,
near: data.near,
miss: data.error,
},
hitMeta: {
gauge: data.gauge,
},
},
scoreMeta: {
gaugeMod: data.options.gaugeOpt === 0 ? "NORMAL" : "HARD",
noteMod: DeriveNoteMod(data),
},
};
return { chart: context.chart, song, dryScore };
};
@@ -0,0 +1,50 @@
import { KtLogger, ParserFunctionReturnsSync } from "../../../../types";
import { ChartDocument } from "kamaitachi-common";
import p, { PrudenceSchema } from "prudence";
import { InvalidScoreFailure } from "../../../framework/common/converter-failures";
import { FormatPrError } from "../../../../common/prudence";
import { USCClientScore } from "../../../../api/ir/usc/common";
import { IRUSCContext } from "./types";
import { ConverterIRUSC } from "./converter";
const PR_USCIRScore: PrudenceSchema = {
score: p.isBoundedInteger(0, 10_000_000),
gauge: p.isBetween(0, 100),
timestamp: p.isPositiveInteger,
crit: p.isPositiveInteger,
near: p.isPositiveInteger,
error: p.isPositiveInteger,
options: {
gaugeType: p.isIn(0, 1),
mirror: "boolean",
random: "boolean",
autoFlags: p.isInteger,
},
};
export function ParseIRUSC(
body: Record<string, unknown>,
chart: ChartDocument<"usc:Single">,
logger: KtLogger
): ParserFunctionReturnsSync<USCClientScore, IRUSCContext> {
const err = p(
body.score,
PR_USCIRScore,
{},
{ throwOnNonObject: false, allowExcessKeys: true }
);
if (err) {
throw new InvalidScoreFailure(FormatPrError(err, "Invalid USC Score."));
}
return {
context: {
chart,
},
game: "usc",
iterable: [body.score] as USCClientScore[],
classHandler: null,
ConverterFunction: ConverterIRUSC,
};
}
@@ -0,0 +1,5 @@
import { ChartDocument } from "kamaitachi-common";
export interface IRUSCContext {
chart: ChartDocument<"usc:Single">;
}