From 9158a2e56444dbcda4212633f961dee6e400e1bf Mon Sep 17 00:00:00 2001 From: Albert Shin Date: Thu, 8 Aug 2024 00:54:14 -0400 Subject: [PATCH 1/6] feat: add myt maimai dx import support --- bot/src/slashCommands/commands/sync.ts | 1 + .../app/pages/dashboard/import/ImportPage.tsx | 3 +- client/src/app/routes/ImportRoutes.tsx | 3 + .../components/imports/MYTIntegrationPage.tsx | 3 +- common/src/constants/import-types.ts | 1 + common/src/types/import-types.ts | 1 + server/example/conf.json5 | 2 + .../api/myt-chunithm/converter.test.ts | 2 +- .../api/myt-maimaidx/converter.test.ts | 207 +++++ .../api/myt-maimaidx/converter.ts | 143 +++ .../import-types/api/myt-maimaidx/parser.ts | 56 ++ .../import-types/api/myt-maimaidx/types.ts | 3 + .../score-import/import-types/common/types.ts | 3 + .../score-import/import-types/converters.ts | 2 + .../lib/score-import/import-types/parsers.ts | 2 + .../generated/maimai/common_grpc_pb.d.ts | 1 + .../proto/generated/maimai/common_grpc_pb.js | 1 + .../src/proto/generated/maimai/common_pb.d.ts | 67 ++ .../src/proto/generated/maimai/common_pb.js | 96 ++ .../generated/maimai/playlog_grpc_pb.d.ts | 1 + .../proto/generated/maimai/playlog_grpc_pb.js | 1 + .../proto/generated/maimai/playlog_pb.d.ts | 118 +++ .../src/proto/generated/maimai/playlog_pb.js | 870 ++++++++++++++++++ .../proto/generated/maimai/user_grpc_pb.d.ts | 23 + .../proto/generated/maimai/user_grpc_pb.js | 45 + .../src/proto/generated/maimai/user_pb.d.ts | 70 ++ server/src/proto/generated/maimai/user_pb.js | 528 +++++++++++ server/src/proto/myt/maimai/common.proto | 55 ++ server/src/proto/myt/maimai/playlog.proto | 31 + server/src/proto/myt/maimai/user.proto | 21 + server/src/test-utils/test-data.ts | 36 + 31 files changed, 2392 insertions(+), 4 deletions(-) create mode 100644 server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts create mode 100644 server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts create mode 100644 server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts create mode 100644 server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts create mode 100644 server/src/proto/generated/maimai/common_grpc_pb.d.ts create mode 100644 server/src/proto/generated/maimai/common_grpc_pb.js create mode 100644 server/src/proto/generated/maimai/common_pb.d.ts create mode 100644 server/src/proto/generated/maimai/common_pb.js create mode 100644 server/src/proto/generated/maimai/playlog_grpc_pb.d.ts create mode 100644 server/src/proto/generated/maimai/playlog_grpc_pb.js create mode 100644 server/src/proto/generated/maimai/playlog_pb.d.ts create mode 100644 server/src/proto/generated/maimai/playlog_pb.js create mode 100644 server/src/proto/generated/maimai/user_grpc_pb.d.ts create mode 100644 server/src/proto/generated/maimai/user_grpc_pb.js create mode 100644 server/src/proto/generated/maimai/user_pb.d.ts create mode 100644 server/src/proto/generated/maimai/user_pb.js create mode 100644 server/src/proto/myt/maimai/common.proto create mode 100644 server/src/proto/myt/maimai/playlog.proto create mode 100644 server/src/proto/myt/maimai/user.proto diff --git a/bot/src/slashCommands/commands/sync.ts b/bot/src/slashCommands/commands/sync.ts index a62df0c26..fad32efb7 100644 --- a/bot/src/slashCommands/commands/sync.ts +++ b/bot/src/slashCommands/commands/sync.ts @@ -18,6 +18,7 @@ const choices: Array<[string, string]> = ( ["CG MUSECA", "api/cg-prod-museca"], ["CG Pop'n", "api/cg-prod-popn"], ["MYT CHUNITHM", "api/myt-chunithm"], + ["MYT MAIMAI DX", "api/myt-maimaidx"], ["MYT ONGEKI", "api/myt-ongeki"], ["MYT WACCA", "api/myt-wacca"], ] as Array<[string, string]> diff --git a/client/src/app/pages/dashboard/import/ImportPage.tsx b/client/src/app/pages/dashboard/import/ImportPage.tsx index fb64cd2c6..25987d4a6 100644 --- a/client/src/app/pages/dashboard/import/ImportPage.tsx +++ b/client/src/app/pages/dashboard/import/ImportPage.tsx @@ -319,7 +319,8 @@ function ImportInfoDisplayer({ game }: { game: Game }) { desc="Use your data from maimai DX NET." moreInfo="If you are playing on an official maimai DX server, you can import play data from it here." key="maimai DX NET Importer" - /> + />, + , ); } else if (game === "museca") { Content.unshift( diff --git a/client/src/app/routes/ImportRoutes.tsx b/client/src/app/routes/ImportRoutes.tsx index 4def42477..e556f7550 100644 --- a/client/src/app/routes/ImportRoutes.tsx +++ b/client/src/app/routes/ImportRoutes.tsx @@ -223,6 +223,9 @@ export default function ImportRoutes() { + + + diff --git a/client/src/components/imports/MYTIntegrationPage.tsx b/client/src/components/imports/MYTIntegrationPage.tsx index 3d93f0b2a..39c3ae9a1 100644 --- a/client/src/components/imports/MYTIntegrationPage.tsx +++ b/client/src/components/imports/MYTIntegrationPage.tsx @@ -16,8 +16,7 @@ import Icon from "components/util/Icon"; import ImportStateRenderer from "./ImportStateRenderer"; interface Props { - // Other games will be added in the future. - game: "chunithm" | "ongeki" | "wacca"; + game: "chunithm" | "maimaidx" | "ongeki" | "wacca"; } export default function MytIntegrationPage({ game }: Props) { diff --git a/common/src/constants/import-types.ts b/common/src/constants/import-types.ts index 1286c7af2..f50a535a1 100644 --- a/common/src/constants/import-types.ts +++ b/common/src/constants/import-types.ts @@ -35,6 +35,7 @@ const API_IMPORT_TYPES: Record = { "api/flo-sdvx": true, "api/min-sdvx": true, "api/myt-chunithm": true, + "api/myt-maimaidx": true, "api/myt-ongeki": true, "api/myt-wacca": true, "api/cg-dev-museca": true, diff --git a/common/src/types/import-types.ts b/common/src/types/import-types.ts index 9cad282db..aab7ee546 100644 --- a/common/src/types/import-types.ts +++ b/common/src/types/import-types.ts @@ -14,6 +14,7 @@ export type APIImportTypes = | "api/flo-sdvx" | "api/min-sdvx" | "api/myt-chunithm" + | "api/myt-maimaidx" | "api/myt-ongeki" | "api/myt-wacca" diff --git a/server/example/conf.json5 b/server/example/conf.json5 index ae3d21abb..572d12e36 100644 --- a/server/example/conf.json5 +++ b/server/example/conf.json5 @@ -80,6 +80,8 @@ "api/cg-gan-sdvx", "api/cg-gan-popn", "api/cg-gan-museca", + "api/myt-chunithm", + "api/myt-maimaidx", "api/myt-ongeki", "api/myt-wacca", ], diff --git a/server/src/lib/score-import/import-types/api/myt-chunithm/converter.test.ts b/server/src/lib/score-import/import-types/api/myt-chunithm/converter.test.ts index b9d0e80ae..effe12955 100644 --- a/server/src/lib/score-import/import-types/api/myt-chunithm/converter.test.ts +++ b/server/src/lib/score-import/import-types/api/myt-chunithm/converter.test.ts @@ -40,7 +40,7 @@ const parsedScore: MytChunithmScore = { }, }; -t.test("#ConvertAPIMytOngeki", (t) => { +t.test("#ConvertAPIMytChunithm", (t) => { t.beforeEach(ResetDBState); function convert(modifier: any = {}) { diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts new file mode 100644 index 000000000..8dacead75 --- /dev/null +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts @@ -0,0 +1,207 @@ +import ConvertAPIMytMaimaiDx from "./converter"; +import CreateLogCtx from "lib/logger/logger"; +import { ParseDateFromString } from "lib/score-import/framework/common/score-utils"; +import { + MaimaiComboStatus, + MaimaiLevel, + MaimaiScoreRank, + MaimaiSyncStatus, +} from "proto/generated/maimai/common_pb"; +import t from "tap"; +import { dmf } from "test-utils/misc"; +import ResetDBState from "test-utils/resets"; +import { TestingMaimaiDXChartConverter, TestingMaimaiDXSongConverter } from "test-utils/test-data"; +import type { MytMaimaiDxScore } from "./types"; + +const logger = CreateLogCtx(__filename); + +const parsedScore: MytMaimaiDxScore = { + playlogApiId: "6071c489-6ab9-4674-a443-f88b603fa596", + info: { + musicId: 11294, + level: MaimaiLevel.MAIMAI_LEVEL_EXPERT, + achievement: 990562, + deluxscore: 1825, + scoreRank: MaimaiScoreRank.MAIMAI_SCORE_RANK_SS, + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, + syncStatus: MaimaiSyncStatus.MAIMAI_SYNC_STATUS_NONE, + isClear: true, + isAchieveNewRecord: true, + isDeluxscoreNewRecord: true, + track: 1, + userPlayDate: "2022-11-03T04:21:05.000+09:00" + }, + judge: { + judgeCriticalPerfect: 10, + judgePerfect: 656, + judgeGreat: 19, + judgeGood: 1, + judgeMiss: 8, + maxCombo: 279, + fastCount: 5, + lateCount: 8 + } +}; + +t.test("#ConvertAPIMytMaimaiDx", (t) => { + t.beforeEach(ResetDBState); + + function convert(modifier: any = {}) { + return ConvertAPIMytMaimaiDx(dmf(parsedScore, modifier), {}, "api/myt-maimaidx", logger); + } + + t.test("Should return a dryScore on valid input.", async (t) => { + const res = await convert(); + + t.strictSame(res, { + song: TestingMaimaiDXSongConverter, + chart: TestingMaimaiDXChartConverter, + dryScore: { + service: "MYT", + game: "maimaidx", + scoreMeta: {}, + timeAchieved: ParseDateFromString("2022-11-03T04:21:05.000Z+09:00"), + comment: null, + importType: "api/myt-maimaidx", + scoreData: { + score: 99.0562, + lamp: "CLEAR", + judgements: { + pcrit: 10, + perfect: 656, + great: 19, + good: 1, + miss: 8 + }, + optional: { + fast: 5, + slow: 8, + maxCombo: 279, + }, + }, + }, + }); + t.end(); + }); + + t.test("Should reject Utage charts", (t) => { + t.rejects( + () => + convert({ + info: { + musicId: 8032, + level: MaimaiLevel.MAIMAI_LEVEL_UTAGE, + }, + }), + { + message: /Utage charts are not supported/u, + } + ); + t.end(); + }); + + t.test("Should reject unspecified difficulty", (t) => { + t.rejects( + () => + convert({ + info: { + level: MaimaiLevel.MAIMAI_LEVEL_UNSPECIFIED + }, + }), + { + message: /Can't process a score with unspecified difficulty/u, + } + ); + t.end(); + }); + + t.test("lamp", async (t) => { + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS, + isClear: true, + }, + }), + { + dryScore: { scoreData: { lamp: "ALL PERFECT+" } }, + } + ); + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT, + isClear: true, + }, + }), + { + dryScore: { scoreData: { lamp: "ALL PERFECT" } }, + } + ); + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS, + isClear: true, + }, + }), + { + dryScore: { scoreData: { lamp: "FULL COMBO+" } }, + } + ); + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO, + isClear: true, + }, + }), + { + dryScore: { scoreData: { lamp: "FULL COMBO" } }, + } + ); + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, + isClear: true, + } + }), + { + dryScore: { scoreData: { lamp: "CLEAR" } }, + } + ); + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, + isClear: false, + }, + }), + { + dryScore: { scoreData: { lamp: "FAILED" } }, + } + ); + t.hasStrict( + await convert({ + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO, + isClear: false, + }, + }), + { + dryScore: { scoreData: { lamp: "FAILED" } }, + } + ); + t.end(); + }); + + t.test("Should throw on missing chart", (t) => { + t.rejects(() => convert({ info: { musicId: 999999 } }), { + message: /Can't find chart with id 999999 and difficulty Master/u, + }); + t.end(); + }); + + t.end(); +}); diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts new file mode 100644 index 000000000..a8949fcc2 --- /dev/null +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts @@ -0,0 +1,143 @@ +import { + InternalFailure, + InvalidScoreFailure, + SkipScoreFailure, + SongOrChartNotFoundFailure, +} from "lib/score-import/framework/common/converter-failures"; +import { ParseDateFromString } from "lib/score-import/framework/common/score-utils"; +import { + MaimaiComboStatus, + MaimaiLevel +} from "proto/generated/maimai/common_pb"; +import { FindChartOnInGameID } from "utils/queries/charts"; +import { FindSongOnID } from "utils/queries/songs"; +import type { ConverterFunction } from "../../common/types"; +import type { MytMaimaiDxScore } from "./types"; +import type { DryScore } from "lib/score-import/framework/common/types"; +import type { ScoreData } from "tachi-common"; +import type { EmptyObject } from "utils/types"; + +const DIFFICULTIES = { + [MaimaiLevel.MAIMAI_LEVEL_UNSPECIFIED]: undefined, + [MaimaiLevel.MAIMAI_LEVEL_BASIC]: "Basic", + [MaimaiLevel.MAIMAI_LEVEL_ADVANCED]: "Advanced", + [MaimaiLevel.MAIMAI_LEVEL_EXPERT]: "Expert", + [MaimaiLevel.MAIMAI_LEVEL_MASTER]: "Master", + [MaimaiLevel.MAIMAI_LEVEL_REMASTER]: "Re:Master", + [MaimaiLevel.MAIMAI_LEVEL_UTAGE]: "Utage", +}; + +function getLamp( + comboStatus: number, + isClear: boolean, +): ScoreData<"maimaidx:Single">["lamp"] | undefined { + if ( + comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_UNSPECIFIED + ) { + return undefined; + } + + if (isClear === false) { + return "FAILED"; + } + + if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE) { + return "CLEAR"; + } + + if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO) { + return "FULL COMBO"; + } + + if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS) { + return "FULL COMBO+"; + } + + if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT) { + return "ALL PERFECT"; + } + + if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS) { + return "ALL PERFECT+"; + } + + return undefined; +} + +const ConvertAPIMytMaimaiDx: ConverterFunction = async ( + data, + _context, + importType, + logger +) => { + if (data.info === undefined || data.judge === undefined) { + throw new InvalidScoreFailure("Failed to receive score data from MYT API"); + } + + const baseDifficulty = DIFFICULTIES[data.info.level]; + if (baseDifficulty === undefined) { + throw new InvalidScoreFailure( + `Can't process a score with unspecified difficulty (musicId ${data.info.musicId})` + ); + } + if (baseDifficulty === "Utage") { + throw new SkipScoreFailure("Utage charts are not supported"); + } + // Songs with an ID higher than 10000 are considered DX charts + const difficulty = data.info.musicId >= 10000 ? `DX ${baseDifficulty}` : baseDifficulty; + + const lamp = getLamp(data.info.comboStatus, data.info.isClear); + + if (lamp === undefined) { + throw new InvalidScoreFailure( + "Can't process a score with an invalid combo status and/or clear status" + ); + } + + const chart = await FindChartOnInGameID("maimaidx", data.info.musicId, "Single", difficulty); + + if (chart === null) { + throw new SongOrChartNotFoundFailure( + `Can't find chart with id ${data.info.musicId} and difficulty ${difficulty}`, + importType, + data, + {} + ); + } + + const song = await FindSongOnID("maimaidx", chart.songID); + + if (song === null) { + logger.severe(`Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, { chart }); + throw new InternalFailure(`Song/chart desync: ${chart.songID} for chart ${chart.chartID}`); + } + + const dryScore: DryScore<"maimaidx:Single"> = { + service: "MYT", + game: "maimaidx", + scoreMeta: {}, + timeAchieved: ParseDateFromString(data.info.userPlayDate), + comment: null, + importType, + scoreData: { + percent: data.info.achievement / 10000, + lamp, + judgements: { + pcrit: data.judge.judgeCriticalPerfect, + perfect: data.judge.judgePerfect, + great: data.judge.judgeGreat, + good: data.judge.judgeGood, + miss: data.judge.judgeMiss + }, + optional: { + fast: data.judge.fastCount, + slow: data.judge.lateCount, + maxCombo: data.judge.maxCombo + } + }, + }; + + return { chart, song, dryScore }; +}; + +export default ConvertAPIMytMaimaiDx; diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts new file mode 100644 index 000000000..3a388135b --- /dev/null +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts @@ -0,0 +1,56 @@ +import { + FetchMytTitleAPIID, + GetMytHostname, + StreamRPCAsAsync, +} from "../../common/api-myt/traverse-api"; +import { credentials } from "@grpc/grpc-js"; +import ScoreImportFatalError from "lib/score-import/framework/score-importing/score-import-error"; +import { MaimaiUserClient } from "proto/generated/maimai/user_grpc_pb"; +import { GetPlaylogRequest } from "proto/generated/maimai/user_pb"; +import type { ParserFunctionReturns } from "../../common/types"; +import type { MytMaimaiDxScore } from "./types"; +import type { KtLogger } from "lib/logger/logger"; +import type { GetPlaylogStreamItem } from "proto/generated/maimai/user_pb"; +import type { integer } from "tachi-common"; +import type { EmptyObject } from "utils/types"; + +async function* getObjectsFromGrpcIterable( + iterable: AsyncIterable +): AsyncIterable { + for await (const item of iterable) { + yield item.toObject(); + } +} + +export default async function ParseMytMaimaiDx( + userID: integer, + logger: KtLogger +): Promise> { + const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", logger); + const endpoint = GetMytHostname(); + const client = new MaimaiUserClient(endpoint, credentials.createSsl()); + const request = new GetPlaylogRequest(); + + request.setProfileApiId(profileApiId); + + let iterable; + + try { + const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, logger); + + iterable = getObjectsFromGrpcIterable(stream); + } catch (err) { + logger.error( + `Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}` + ); + + throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); + } + + return { + iterable, + context: {}, + classProvider: null, + game: "maimaidx", + }; +} diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts new file mode 100644 index 000000000..e2bca355c --- /dev/null +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/types.ts @@ -0,0 +1,3 @@ +import type { GetPlaylogStreamItem } from "proto/generated/maimai/user_pb"; + +export type MytMaimaiDxScore = GetPlaylogStreamItem.AsObject; diff --git a/server/src/lib/score-import/import-types/common/types.ts b/server/src/lib/score-import/import-types/common/types.ts index 7ccbf3a22..3097f5413 100644 --- a/server/src/lib/score-import/import-types/common/types.ts +++ b/server/src/lib/score-import/import-types/common/types.ts @@ -1,6 +1,7 @@ import type { ConverterFailure } from "../../framework/common/converter-failures"; import type { DryScore } from "../../framework/common/types"; import type { MytChunithmScore } from "../api/myt-chunithm/types"; +import { MytMaimaiDxScore } from "../api/myt-maimaidx/types"; import type { MytOngekiScore } from "../api/myt-ongeki/types"; import type { MytWaccaScore } from "../api/myt-wacca/types"; import type { SDVXEamusementCSVData } from "../file/eamusement-sdvx-csv/types"; @@ -59,6 +60,7 @@ export interface ImportTypeDataMap { "api/eag-sdvx": unknown; "api/myt-chunithm": MytChunithmScore; + "api/myt-maimaidx": MytMaimaiDxScore; "api/myt-ongeki": MytOngekiScore; "api/myt-wacca": MytWaccaScore; @@ -104,6 +106,7 @@ export interface ImportTypeContextMap { "api/eag-iidx": KaiContext; "api/eag-sdvx": KaiContext; "api/myt-chunithm": EmptyObject; + "api/myt-maimaidx": EmptyObject; "api/myt-ongeki": EmptyObject; "api/myt-wacca": EmptyObject; diff --git a/server/src/lib/score-import/import-types/converters.ts b/server/src/lib/score-import/import-types/converters.ts index e1fbdeacd..d1168e29c 100644 --- a/server/src/lib/score-import/import-types/converters.ts +++ b/server/src/lib/score-import/import-types/converters.ts @@ -1,4 +1,5 @@ import ConvertAPIMytChunithm from "./api/myt-chunithm/converter"; +import ConvertAPIMytMaimaiDx from "./api/myt-maimaidx/converter"; import ConvertAPIMytOngeki from "./api/myt-ongeki/converter"; import ConvertAPIMytWACCA from "./api/myt-wacca/converter"; import { ConverterAPICGMuseca } from "./common/api-cg/museca/converter"; @@ -46,6 +47,7 @@ export const Converters: ConverterMap = { "api/flo-sdvx": ConvertAPIKaiSDVX, "api/min-sdvx": ConvertAPIKaiSDVX, "api/myt-chunithm": ConvertAPIMytChunithm, + "api/myt-maimaidx": ConvertAPIMytMaimaiDx, "api/myt-ongeki": ConvertAPIMytOngeki, "api/myt-wacca": ConvertAPIMytWACCA, diff --git a/server/src/lib/score-import/import-types/parsers.ts b/server/src/lib/score-import/import-types/parsers.ts index c75a358c6..18a056930 100644 --- a/server/src/lib/score-import/import-types/parsers.ts +++ b/server/src/lib/score-import/import-types/parsers.ts @@ -4,6 +4,7 @@ import { ParseFloIIDX } from "./api/flo-iidx/parser"; import { ParseFloSDVX } from "./api/flo-sdvx/parser"; import { ParseMinSDVX } from "./api/min-sdvx/parser"; import ParseMytChunithm from "./api/myt-chunithm/parser"; +import ParseMytMaimaiDx from "./api/myt-maimaidx/parser"; import ParseMytOngeki from "./api/myt-ongeki/parser"; import ParseMytWACCA from "./api/myt-wacca/parser"; import { @@ -68,6 +69,7 @@ export const Parsers = { "api/cg-gan-museca": ParseCGGanMuseca, "api/myt-chunithm": ParseMytChunithm, + "api/myt-maimaidx": ParseMytMaimaiDx, "api/myt-ongeki": ParseMytOngeki, "api/myt-wacca": ParseMytWACCA, diff --git a/server/src/proto/generated/maimai/common_grpc_pb.d.ts b/server/src/proto/generated/maimai/common_grpc_pb.d.ts new file mode 100644 index 000000000..51b4d6959 --- /dev/null +++ b/server/src/proto/generated/maimai/common_grpc_pb.d.ts @@ -0,0 +1 @@ +// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/server/src/proto/generated/maimai/common_grpc_pb.js b/server/src/proto/generated/maimai/common_grpc_pb.js new file mode 100644 index 000000000..97b3a2461 --- /dev/null +++ b/server/src/proto/generated/maimai/common_grpc_pb.js @@ -0,0 +1 @@ +// GENERATED CODE -- NO SERVICES IN PROTO \ No newline at end of file diff --git a/server/src/proto/generated/maimai/common_pb.d.ts b/server/src/proto/generated/maimai/common_pb.d.ts new file mode 100644 index 000000000..88168cc88 --- /dev/null +++ b/server/src/proto/generated/maimai/common_pb.d.ts @@ -0,0 +1,67 @@ +// package: mythos.maimai.v0 +// file: maimai/common.proto + +import * as jspb from "google-protobuf"; + +export interface MaimaiRankingTypeMap { + MAIMAI_RANKING_TYPE_UNSPECIFIED: 0; + MAIMAI_RANKING_TYPE_ACHIEVEMENT: 1; + MAIMAI_RANKING_TYPE_DX_SCORE: 2; +} + +export const MaimaiRankingType: MaimaiRankingTypeMap; + +export interface MaimaiLevelMap { + MAIMAI_LEVEL_UNSPECIFIED: 0; + MAIMAI_LEVEL_BASIC: 1; + MAIMAI_LEVEL_ADVANCED: 2; + MAIMAI_LEVEL_EXPERT: 3; + MAIMAI_LEVEL_MASTER: 4; + MAIMAI_LEVEL_REMASTER: 5; + MAIMAI_LEVEL_UTAGE: 6; +} + +export const MaimaiLevel: MaimaiLevelMap; + +export interface MaimaiComboStatusMap { + MAIMAI_COMBO_STATUS_UNSPECIFIED: 0; + MAIMAI_COMBO_STATUS_NONE: 1; + MAIMAI_COMBO_STATUS_FULL_COMBO: 2; + MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS: 3; + MAIMAI_COMBO_STATUS_ALL_PERFECT: 4; + MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS: 5; +} + +export const MaimaiComboStatus: MaimaiComboStatusMap; + +export interface MaimaiSyncStatusMap { + MAIMAI_SYNC_STATUS_UNSPECIFIED: 0; + MAIMAI_SYNC_STATUS_NONE: 1; + MAIMAI_SYNC_STATUS_FULL_SYNC: 2; + MAIMAI_SYNC_STATUS_FULL_SYNC_PLUS: 3; + MAIMAI_SYNC_STATUS_FULL_SYNC_DX: 4; + MAIMAI_SYNC_STATUS_FULL_SYNC_DX_PLUS: 5; +} + +export const MaimaiSyncStatus: MaimaiSyncStatusMap; + +export interface MaimaiScoreRankMap { + MAIMAI_SCORE_RANK_UNSPECIFIED: 0; + MAIMAI_SCORE_RANK_D: 1; + MAIMAI_SCORE_RANK_C: 2; + MAIMAI_SCORE_RANK_B: 3; + MAIMAI_SCORE_RANK_BB: 4; + MAIMAI_SCORE_RANK_BBB: 5; + MAIMAI_SCORE_RANK_A: 6; + MAIMAI_SCORE_RANK_AA: 7; + MAIMAI_SCORE_RANK_AAA: 8; + MAIMAI_SCORE_RANK_S: 9; + MAIMAI_SCORE_RANK_S_PLUS: 10; + MAIMAI_SCORE_RANK_SS: 11; + MAIMAI_SCORE_RANK_SS_PLUS: 12; + MAIMAI_SCORE_RANK_SSS: 13; + MAIMAI_SCORE_RANK_SSS_PLUS: 14; +} + +export const MaimaiScoreRank: MaimaiScoreRankMap; + diff --git a/server/src/proto/generated/maimai/common_pb.js b/server/src/proto/generated/maimai/common_pb.js new file mode 100644 index 000000000..ebec3d669 --- /dev/null +++ b/server/src/proto/generated/maimai/common_pb.js @@ -0,0 +1,96 @@ +// source: maimai/common.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.mythos.maimai.v0.MaimaiComboStatus', null, global); +goog.exportSymbol('proto.mythos.maimai.v0.MaimaiLevel', null, global); +goog.exportSymbol('proto.mythos.maimai.v0.MaimaiRankingType', null, global); +goog.exportSymbol('proto.mythos.maimai.v0.MaimaiScoreRank', null, global); +goog.exportSymbol('proto.mythos.maimai.v0.MaimaiSyncStatus', null, global); +/** + * @enum {number} + */ +proto.mythos.maimai.v0.MaimaiRankingType = { + MAIMAI_RANKING_TYPE_UNSPECIFIED: 0, + MAIMAI_RANKING_TYPE_ACHIEVEMENT: 1, + MAIMAI_RANKING_TYPE_DX_SCORE: 2 +}; + +/** + * @enum {number} + */ +proto.mythos.maimai.v0.MaimaiLevel = { + MAIMAI_LEVEL_UNSPECIFIED: 0, + MAIMAI_LEVEL_BASIC: 1, + MAIMAI_LEVEL_ADVANCED: 2, + MAIMAI_LEVEL_EXPERT: 3, + MAIMAI_LEVEL_MASTER: 4, + MAIMAI_LEVEL_REMASTER: 5, + MAIMAI_LEVEL_UTAGE: 6 +}; + +/** + * @enum {number} + */ +proto.mythos.maimai.v0.MaimaiComboStatus = { + MAIMAI_COMBO_STATUS_UNSPECIFIED: 0, + MAIMAI_COMBO_STATUS_NONE: 1, + MAIMAI_COMBO_STATUS_FULL_COMBO: 2, + MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS: 3, + MAIMAI_COMBO_STATUS_ALL_PERFECT: 4, + MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS: 5 +}; + +/** + * @enum {number} + */ +proto.mythos.maimai.v0.MaimaiSyncStatus = { + MAIMAI_SYNC_STATUS_UNSPECIFIED: 0, + MAIMAI_SYNC_STATUS_NONE: 1, + MAIMAI_SYNC_STATUS_FULL_SYNC: 2, + MAIMAI_SYNC_STATUS_FULL_SYNC_PLUS: 3, + MAIMAI_SYNC_STATUS_FULL_SYNC_DX: 4, + MAIMAI_SYNC_STATUS_FULL_SYNC_DX_PLUS: 5 +}; + +/** + * @enum {number} + */ +proto.mythos.maimai.v0.MaimaiScoreRank = { + MAIMAI_SCORE_RANK_UNSPECIFIED: 0, + MAIMAI_SCORE_RANK_D: 1, + MAIMAI_SCORE_RANK_C: 2, + MAIMAI_SCORE_RANK_B: 3, + MAIMAI_SCORE_RANK_BB: 4, + MAIMAI_SCORE_RANK_BBB: 5, + MAIMAI_SCORE_RANK_A: 6, + MAIMAI_SCORE_RANK_AA: 7, + MAIMAI_SCORE_RANK_AAA: 8, + MAIMAI_SCORE_RANK_S: 9, + MAIMAI_SCORE_RANK_S_PLUS: 10, + MAIMAI_SCORE_RANK_SS: 11, + MAIMAI_SCORE_RANK_SS_PLUS: 12, + MAIMAI_SCORE_RANK_SSS: 13, + MAIMAI_SCORE_RANK_SSS_PLUS: 14 +}; + +goog.object.extend(exports, proto.mythos.maimai.v0); diff --git a/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts b/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts new file mode 100644 index 000000000..51b4d6959 --- /dev/null +++ b/server/src/proto/generated/maimai/playlog_grpc_pb.d.ts @@ -0,0 +1 @@ +// GENERATED CODE -- NO SERVICES IN PROTO diff --git a/server/src/proto/generated/maimai/playlog_grpc_pb.js b/server/src/proto/generated/maimai/playlog_grpc_pb.js new file mode 100644 index 000000000..97b3a2461 --- /dev/null +++ b/server/src/proto/generated/maimai/playlog_grpc_pb.js @@ -0,0 +1 @@ +// GENERATED CODE -- NO SERVICES IN PROTO \ No newline at end of file diff --git a/server/src/proto/generated/maimai/playlog_pb.d.ts b/server/src/proto/generated/maimai/playlog_pb.d.ts new file mode 100644 index 000000000..e5578e5f4 --- /dev/null +++ b/server/src/proto/generated/maimai/playlog_pb.d.ts @@ -0,0 +1,118 @@ +// package: mythos.maimai.v0 +// file: maimai/playlog.proto + +import * as jspb from "google-protobuf"; +import * as maimai_common_pb from "../maimai/common_pb"; + +export class PlaylogInfo extends jspb.Message { + getMusicId(): number; + setMusicId(value: number): void; + + getLevel(): maimai_common_pb.MaimaiLevelMap[keyof maimai_common_pb.MaimaiLevelMap]; + setLevel(value: maimai_common_pb.MaimaiLevelMap[keyof maimai_common_pb.MaimaiLevelMap]): void; + + getAchievement(): number; + setAchievement(value: number): void; + + getDeluxscore(): number; + setDeluxscore(value: number): void; + + getScoreRank(): maimai_common_pb.MaimaiScoreRankMap[keyof maimai_common_pb.MaimaiScoreRankMap]; + setScoreRank(value: maimai_common_pb.MaimaiScoreRankMap[keyof maimai_common_pb.MaimaiScoreRankMap]): void; + + getComboStatus(): maimai_common_pb.MaimaiComboStatusMap[keyof maimai_common_pb.MaimaiComboStatusMap]; + setComboStatus(value: maimai_common_pb.MaimaiComboStatusMap[keyof maimai_common_pb.MaimaiComboStatusMap]): void; + + getSyncStatus(): maimai_common_pb.MaimaiSyncStatusMap[keyof maimai_common_pb.MaimaiSyncStatusMap]; + setSyncStatus(value: maimai_common_pb.MaimaiSyncStatusMap[keyof maimai_common_pb.MaimaiSyncStatusMap]): void; + + getIsClear(): boolean; + setIsClear(value: boolean): void; + + getIsAchieveNewRecord(): boolean; + setIsAchieveNewRecord(value: boolean): void; + + getIsDeluxscoreNewRecord(): boolean; + setIsDeluxscoreNewRecord(value: boolean): void; + + getTrack(): number; + setTrack(value: number): void; + + getUserPlayDate(): string; + setUserPlayDate(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): PlaylogInfo.AsObject; + static toObject(includeInstance: boolean, msg: PlaylogInfo): PlaylogInfo.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: PlaylogInfo, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): PlaylogInfo; + static deserializeBinaryFromReader(message: PlaylogInfo, reader: jspb.BinaryReader): PlaylogInfo; +} + +export namespace PlaylogInfo { + export type AsObject = { + musicId: number, + level: maimai_common_pb.MaimaiLevelMap[keyof maimai_common_pb.MaimaiLevelMap], + achievement: number, + deluxscore: number, + scoreRank: maimai_common_pb.MaimaiScoreRankMap[keyof maimai_common_pb.MaimaiScoreRankMap], + comboStatus: maimai_common_pb.MaimaiComboStatusMap[keyof maimai_common_pb.MaimaiComboStatusMap], + syncStatus: maimai_common_pb.MaimaiSyncStatusMap[keyof maimai_common_pb.MaimaiSyncStatusMap], + isClear: boolean, + isAchieveNewRecord: boolean, + isDeluxscoreNewRecord: boolean, + track: number, + userPlayDate: string, + } +} + +export class PlaylogJudge extends jspb.Message { + getJudgeCriticalPerfect(): number; + setJudgeCriticalPerfect(value: number): void; + + getJudgePerfect(): number; + setJudgePerfect(value: number): void; + + getJudgeGreat(): number; + setJudgeGreat(value: number): void; + + getJudgeGood(): number; + setJudgeGood(value: number): void; + + getJudgeMiss(): number; + setJudgeMiss(value: number): void; + + getMaxCombo(): number; + setMaxCombo(value: number): void; + + getFastCount(): number; + setFastCount(value: number): void; + + getLateCount(): number; + setLateCount(value: number): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): PlaylogJudge.AsObject; + static toObject(includeInstance: boolean, msg: PlaylogJudge): PlaylogJudge.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: PlaylogJudge, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): PlaylogJudge; + static deserializeBinaryFromReader(message: PlaylogJudge, reader: jspb.BinaryReader): PlaylogJudge; +} + +export namespace PlaylogJudge { + export type AsObject = { + judgeCriticalPerfect: number, + judgePerfect: number, + judgeGreat: number, + judgeGood: number, + judgeMiss: number, + maxCombo: number, + fastCount: number, + lateCount: number, + } +} + diff --git a/server/src/proto/generated/maimai/playlog_pb.js b/server/src/proto/generated/maimai/playlog_pb.js new file mode 100644 index 000000000..1ffd3135e --- /dev/null +++ b/server/src/proto/generated/maimai/playlog_pb.js @@ -0,0 +1,870 @@ +// source: maimai/playlog.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +var maimai_common_pb = require('../maimai/common_pb.js'); +goog.object.extend(proto, maimai_common_pb); +goog.exportSymbol('proto.mythos.maimai.v0.PlaylogInfo', null, global); +goog.exportSymbol('proto.mythos.maimai.v0.PlaylogJudge', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.mythos.maimai.v0.PlaylogInfo = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.mythos.maimai.v0.PlaylogInfo, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.mythos.maimai.v0.PlaylogInfo.displayName = 'proto.mythos.maimai.v0.PlaylogInfo'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.mythos.maimai.v0.PlaylogJudge = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.mythos.maimai.v0.PlaylogJudge, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.mythos.maimai.v0.PlaylogJudge.displayName = 'proto.mythos.maimai.v0.PlaylogJudge'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.toObject = function(opt_includeInstance) { + return proto.mythos.maimai.v0.PlaylogInfo.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.mythos.maimai.v0.PlaylogInfo} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.PlaylogInfo.toObject = function(includeInstance, msg) { + var f, obj = { + musicId: jspb.Message.getFieldWithDefault(msg, 1, 0), + level: jspb.Message.getFieldWithDefault(msg, 2, 0), + achievement: jspb.Message.getFieldWithDefault(msg, 3, 0), + deluxscore: jspb.Message.getFieldWithDefault(msg, 4, 0), + scoreRank: jspb.Message.getFieldWithDefault(msg, 5, 0), + comboStatus: jspb.Message.getFieldWithDefault(msg, 6, 0), + syncStatus: jspb.Message.getFieldWithDefault(msg, 7, 0), + isClear: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), + isAchieveNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), + isDeluxscoreNewRecord: jspb.Message.getBooleanFieldWithDefault(msg, 10, false), + track: jspb.Message.getFieldWithDefault(msg, 11, 0), + userPlayDate: jspb.Message.getFieldWithDefault(msg, 12, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.mythos.maimai.v0.PlaylogInfo} + */ +proto.mythos.maimai.v0.PlaylogInfo.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.mythos.maimai.v0.PlaylogInfo; + return proto.mythos.maimai.v0.PlaylogInfo.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.mythos.maimai.v0.PlaylogInfo} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.mythos.maimai.v0.PlaylogInfo} + */ +proto.mythos.maimai.v0.PlaylogInfo.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt32()); + msg.setMusicId(value); + break; + case 2: + var value = /** @type {!proto.mythos.maimai.v0.MaimaiLevel} */ (reader.readEnum()); + msg.setLevel(value); + break; + case 3: + var value = /** @type {number} */ (reader.readInt32()); + msg.setAchievement(value); + break; + case 4: + var value = /** @type {number} */ (reader.readInt32()); + msg.setDeluxscore(value); + break; + case 5: + var value = /** @type {!proto.mythos.maimai.v0.MaimaiScoreRank} */ (reader.readEnum()); + msg.setScoreRank(value); + break; + case 6: + var value = /** @type {!proto.mythos.maimai.v0.MaimaiComboStatus} */ (reader.readEnum()); + msg.setComboStatus(value); + break; + case 7: + var value = /** @type {!proto.mythos.maimai.v0.MaimaiSyncStatus} */ (reader.readEnum()); + msg.setSyncStatus(value); + break; + case 8: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsClear(value); + break; + case 9: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsAchieveNewRecord(value); + break; + case 10: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsDeluxscoreNewRecord(value); + break; + case 11: + var value = /** @type {number} */ (reader.readInt32()); + msg.setTrack(value); + break; + case 12: + var value = /** @type {string} */ (reader.readString()); + msg.setUserPlayDate(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.mythos.maimai.v0.PlaylogInfo.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.mythos.maimai.v0.PlaylogInfo} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.PlaylogInfo.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMusicId(); + if (f !== 0) { + writer.writeInt32( + 1, + f + ); + } + f = message.getLevel(); + if (f !== 0.0) { + writer.writeEnum( + 2, + f + ); + } + f = message.getAchievement(); + if (f !== 0) { + writer.writeInt32( + 3, + f + ); + } + f = message.getDeluxscore(); + if (f !== 0) { + writer.writeInt32( + 4, + f + ); + } + f = message.getScoreRank(); + if (f !== 0.0) { + writer.writeEnum( + 5, + f + ); + } + f = message.getComboStatus(); + if (f !== 0.0) { + writer.writeEnum( + 6, + f + ); + } + f = message.getSyncStatus(); + if (f !== 0.0) { + writer.writeEnum( + 7, + f + ); + } + f = message.getIsClear(); + if (f) { + writer.writeBool( + 8, + f + ); + } + f = message.getIsAchieveNewRecord(); + if (f) { + writer.writeBool( + 9, + f + ); + } + f = message.getIsDeluxscoreNewRecord(); + if (f) { + writer.writeBool( + 10, + f + ); + } + f = message.getTrack(); + if (f !== 0) { + writer.writeInt32( + 11, + f + ); + } + f = message.getUserPlayDate(); + if (f.length > 0) { + writer.writeString( + 12, + f + ); + } +}; + + +/** + * optional int32 music_id = 1; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getMusicId = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setMusicId = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional MaimaiLevel level = 2; + * @return {!proto.mythos.maimai.v0.MaimaiLevel} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getLevel = function() { + return /** @type {!proto.mythos.maimai.v0.MaimaiLevel} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {!proto.mythos.maimai.v0.MaimaiLevel} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setLevel = function(value) { + return jspb.Message.setProto3EnumField(this, 2, value); +}; + + +/** + * optional int32 achievement = 3; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getAchievement = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setAchievement = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional int32 deluxscore = 4; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getDeluxscore = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setDeluxscore = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional MaimaiScoreRank score_rank = 5; + * @return {!proto.mythos.maimai.v0.MaimaiScoreRank} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getScoreRank = function() { + return /** @type {!proto.mythos.maimai.v0.MaimaiScoreRank} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {!proto.mythos.maimai.v0.MaimaiScoreRank} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setScoreRank = function(value) { + return jspb.Message.setProto3EnumField(this, 5, value); +}; + + +/** + * optional MaimaiComboStatus combo_status = 6; + * @return {!proto.mythos.maimai.v0.MaimaiComboStatus} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getComboStatus = function() { + return /** @type {!proto.mythos.maimai.v0.MaimaiComboStatus} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {!proto.mythos.maimai.v0.MaimaiComboStatus} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setComboStatus = function(value) { + return jspb.Message.setProto3EnumField(this, 6, value); +}; + + +/** + * optional MaimaiSyncStatus sync_status = 7; + * @return {!proto.mythos.maimai.v0.MaimaiSyncStatus} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getSyncStatus = function() { + return /** @type {!proto.mythos.maimai.v0.MaimaiSyncStatus} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {!proto.mythos.maimai.v0.MaimaiSyncStatus} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setSyncStatus = function(value) { + return jspb.Message.setProto3EnumField(this, 7, value); +}; + + +/** + * optional bool is_clear = 8; + * @return {boolean} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getIsClear = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setIsClear = function(value) { + return jspb.Message.setProto3BooleanField(this, 8, value); +}; + + +/** + * optional bool is_achieve_new_record = 9; + * @return {boolean} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getIsAchieveNewRecord = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setIsAchieveNewRecord = function(value) { + return jspb.Message.setProto3BooleanField(this, 9, value); +}; + + +/** + * optional bool is_deluxscore_new_record = 10; + * @return {boolean} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getIsDeluxscoreNewRecord = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 10, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setIsDeluxscoreNewRecord = function(value) { + return jspb.Message.setProto3BooleanField(this, 10, value); +}; + + +/** + * optional int32 track = 11; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getTrack = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setTrack = function(value) { + return jspb.Message.setProto3IntField(this, 11, value); +}; + + +/** + * optional string user_play_date = 12; + * @return {string} + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.getUserPlayDate = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 12, "")); +}; + + +/** + * @param {string} value + * @return {!proto.mythos.maimai.v0.PlaylogInfo} returns this + */ +proto.mythos.maimai.v0.PlaylogInfo.prototype.setUserPlayDate = function(value) { + return jspb.Message.setProto3StringField(this, 12, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.toObject = function(opt_includeInstance) { + return proto.mythos.maimai.v0.PlaylogJudge.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.mythos.maimai.v0.PlaylogJudge} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.PlaylogJudge.toObject = function(includeInstance, msg) { + var f, obj = { + judgeCriticalPerfect: jspb.Message.getFieldWithDefault(msg, 1, 0), + judgePerfect: jspb.Message.getFieldWithDefault(msg, 2, 0), + judgeGreat: jspb.Message.getFieldWithDefault(msg, 3, 0), + judgeGood: jspb.Message.getFieldWithDefault(msg, 4, 0), + judgeMiss: jspb.Message.getFieldWithDefault(msg, 5, 0), + maxCombo: jspb.Message.getFieldWithDefault(msg, 6, 0), + fastCount: jspb.Message.getFieldWithDefault(msg, 7, 0), + lateCount: jspb.Message.getFieldWithDefault(msg, 8, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.mythos.maimai.v0.PlaylogJudge} + */ +proto.mythos.maimai.v0.PlaylogJudge.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.mythos.maimai.v0.PlaylogJudge; + return proto.mythos.maimai.v0.PlaylogJudge.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.mythos.maimai.v0.PlaylogJudge} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.mythos.maimai.v0.PlaylogJudge} + */ +proto.mythos.maimai.v0.PlaylogJudge.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt32()); + msg.setJudgeCriticalPerfect(value); + break; + case 2: + var value = /** @type {number} */ (reader.readInt32()); + msg.setJudgePerfect(value); + break; + case 3: + var value = /** @type {number} */ (reader.readInt32()); + msg.setJudgeGreat(value); + break; + case 4: + var value = /** @type {number} */ (reader.readInt32()); + msg.setJudgeGood(value); + break; + case 5: + var value = /** @type {number} */ (reader.readInt32()); + msg.setJudgeMiss(value); + break; + case 6: + var value = /** @type {number} */ (reader.readInt32()); + msg.setMaxCombo(value); + break; + case 7: + var value = /** @type {number} */ (reader.readInt32()); + msg.setFastCount(value); + break; + case 8: + var value = /** @type {number} */ (reader.readInt32()); + msg.setLateCount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.mythos.maimai.v0.PlaylogJudge.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.mythos.maimai.v0.PlaylogJudge} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.PlaylogJudge.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getJudgeCriticalPerfect(); + if (f !== 0) { + writer.writeInt32( + 1, + f + ); + } + f = message.getJudgePerfect(); + if (f !== 0) { + writer.writeInt32( + 2, + f + ); + } + f = message.getJudgeGreat(); + if (f !== 0) { + writer.writeInt32( + 3, + f + ); + } + f = message.getJudgeGood(); + if (f !== 0) { + writer.writeInt32( + 4, + f + ); + } + f = message.getJudgeMiss(); + if (f !== 0) { + writer.writeInt32( + 5, + f + ); + } + f = message.getMaxCombo(); + if (f !== 0) { + writer.writeInt32( + 6, + f + ); + } + f = message.getFastCount(); + if (f !== 0) { + writer.writeInt32( + 7, + f + ); + } + f = message.getLateCount(); + if (f !== 0) { + writer.writeInt32( + 8, + f + ); + } +}; + + +/** + * optional int32 judge_critical_perfect = 1; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeCriticalPerfect = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeCriticalPerfect = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional int32 judge_perfect = 2; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgePerfect = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgePerfect = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional int32 judge_great = 3; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeGreat = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeGreat = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional int32 judge_good = 4; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeGood = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeGood = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +/** + * optional int32 judge_miss = 5; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getJudgeMiss = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setJudgeMiss = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional int32 max_combo = 6; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getMaxCombo = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setMaxCombo = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional int32 fast_count = 7; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getFastCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setFastCount = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + +/** + * optional int32 late_count = 8; + * @return {number} + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.getLateCount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.PlaylogJudge} returns this + */ +proto.mythos.maimai.v0.PlaylogJudge.prototype.setLateCount = function(value) { + return jspb.Message.setProto3IntField(this, 8, value); +}; + + +goog.object.extend(exports, proto.mythos.maimai.v0); diff --git a/server/src/proto/generated/maimai/user_grpc_pb.d.ts b/server/src/proto/generated/maimai/user_grpc_pb.d.ts new file mode 100644 index 000000000..55b3fa25d --- /dev/null +++ b/server/src/proto/generated/maimai/user_grpc_pb.d.ts @@ -0,0 +1,23 @@ +// GENERATED CODE -- DO NOT EDIT! + +// package: mythos.maimai.v0 +// file: maimai/user.proto + +import * as maimai_user_pb from "../maimai/user_pb"; +import * as grpc from "@grpc/grpc-js"; + +interface IMaimaiUserService extends grpc.ServiceDefinition { + getPlaylog: grpc.MethodDefinition; +} + +export const MaimaiUserService: IMaimaiUserService; + +export interface IMaimaiUserServer extends grpc.UntypedServiceImplementation { + getPlaylog: grpc.handleServerStreamingCall; +} + +export class MaimaiUserClient extends grpc.Client { + constructor(address: string, credentials: grpc.ChannelCredentials, options?: object); + getPlaylog(argument: maimai_user_pb.GetPlaylogRequest, metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null): grpc.ClientReadableStream; + getPlaylog(argument: maimai_user_pb.GetPlaylogRequest, metadata?: grpc.Metadata | null, options?: grpc.CallOptions | null): grpc.ClientReadableStream; +} diff --git a/server/src/proto/generated/maimai/user_grpc_pb.js b/server/src/proto/generated/maimai/user_grpc_pb.js new file mode 100644 index 000000000..724196445 --- /dev/null +++ b/server/src/proto/generated/maimai/user_grpc_pb.js @@ -0,0 +1,45 @@ +// GENERATED CODE -- DO NOT EDIT! + +'use strict'; +var grpc = require('@grpc/grpc-js'); +var maimai_user_pb = require('../maimai/user_pb.js'); +var maimai_playlog_pb = require('../maimai/playlog_pb.js'); + +function serialize_mythos_maimai_v0_GetPlaylogRequest(arg) { + if (!(arg instanceof maimai_user_pb.GetPlaylogRequest)) { + throw new Error('Expected argument of type mythos.maimai.v0.GetPlaylogRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_mythos_maimai_v0_GetPlaylogRequest(buffer_arg) { + return maimai_user_pb.GetPlaylogRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_mythos_maimai_v0_GetPlaylogStreamItem(arg) { + if (!(arg instanceof maimai_user_pb.GetPlaylogStreamItem)) { + throw new Error('Expected argument of type mythos.maimai.v0.GetPlaylogStreamItem'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_mythos_maimai_v0_GetPlaylogStreamItem(buffer_arg) { + return maimai_user_pb.GetPlaylogStreamItem.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +var MaimaiUserService = exports.MaimaiUserService = { + getPlaylog: { + path: '/mythos.maimai.v0.MaimaiUser/GetPlaylog', + requestStream: false, + responseStream: true, + requestType: maimai_user_pb.GetPlaylogRequest, + responseType: maimai_user_pb.GetPlaylogStreamItem, + requestSerialize: serialize_mythos_maimai_v0_GetPlaylogRequest, + requestDeserialize: deserialize_mythos_maimai_v0_GetPlaylogRequest, + responseSerialize: serialize_mythos_maimai_v0_GetPlaylogStreamItem, + responseDeserialize: deserialize_mythos_maimai_v0_GetPlaylogStreamItem, + }, +}; + +exports.MaimaiUserClient = grpc.makeGenericClientConstructor(MaimaiUserService); diff --git a/server/src/proto/generated/maimai/user_pb.d.ts b/server/src/proto/generated/maimai/user_pb.d.ts new file mode 100644 index 000000000..d7a971164 --- /dev/null +++ b/server/src/proto/generated/maimai/user_pb.d.ts @@ -0,0 +1,70 @@ +// package: mythos.maimai.v0 +// file: maimai/user.proto + +import * as jspb from "google-protobuf"; +import * as maimai_playlog_pb from "../maimai/playlog_pb"; + +export class GetPlaylogRequest extends jspb.Message { + getProfileApiId(): string; + setProfileApiId(value: string): void; + + hasLastUserPlayDate(): boolean; + clearLastUserPlayDate(): void; + getLastUserPlayDate(): string; + setLastUserPlayDate(value: string): void; + + hasLimit(): boolean; + clearLimit(): void; + getLimit(): number; + setLimit(value: number): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetPlaylogRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetPlaylogRequest): GetPlaylogRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetPlaylogRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetPlaylogRequest; + static deserializeBinaryFromReader(message: GetPlaylogRequest, reader: jspb.BinaryReader): GetPlaylogRequest; +} + +export namespace GetPlaylogRequest { + export type AsObject = { + profileApiId: string, + lastUserPlayDate: string, + limit: number, + } +} + +export class GetPlaylogStreamItem extends jspb.Message { + getPlaylogApiId(): string; + setPlaylogApiId(value: string): void; + + hasInfo(): boolean; + clearInfo(): void; + getInfo(): maimai_playlog_pb.PlaylogInfo | undefined; + setInfo(value?: maimai_playlog_pb.PlaylogInfo): void; + + hasJudge(): boolean; + clearJudge(): void; + getJudge(): maimai_playlog_pb.PlaylogJudge | undefined; + setJudge(value?: maimai_playlog_pb.PlaylogJudge): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetPlaylogStreamItem.AsObject; + static toObject(includeInstance: boolean, msg: GetPlaylogStreamItem): GetPlaylogStreamItem.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetPlaylogStreamItem, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetPlaylogStreamItem; + static deserializeBinaryFromReader(message: GetPlaylogStreamItem, reader: jspb.BinaryReader): GetPlaylogStreamItem; +} + +export namespace GetPlaylogStreamItem { + export type AsObject = { + playlogApiId: string, + info?: maimai_playlog_pb.PlaylogInfo.AsObject, + judge?: maimai_playlog_pb.PlaylogJudge.AsObject, + } +} + diff --git a/server/src/proto/generated/maimai/user_pb.js b/server/src/proto/generated/maimai/user_pb.js new file mode 100644 index 000000000..7a5c047d4 --- /dev/null +++ b/server/src/proto/generated/maimai/user_pb.js @@ -0,0 +1,528 @@ +// source: maimai/user.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +var maimai_playlog_pb = require('../maimai/playlog_pb.js'); +goog.object.extend(proto, maimai_playlog_pb); +goog.exportSymbol('proto.mythos.maimai.v0.GetPlaylogRequest', null, global); +goog.exportSymbol('proto.mythos.maimai.v0.GetPlaylogStreamItem', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.mythos.maimai.v0.GetPlaylogRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.mythos.maimai.v0.GetPlaylogRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.mythos.maimai.v0.GetPlaylogRequest.displayName = 'proto.mythos.maimai.v0.GetPlaylogRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.mythos.maimai.v0.GetPlaylogStreamItem, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.mythos.maimai.v0.GetPlaylogStreamItem.displayName = 'proto.mythos.maimai.v0.GetPlaylogStreamItem'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.toObject = function(opt_includeInstance) { + return proto.mythos.maimai.v0.GetPlaylogRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.mythos.maimai.v0.GetPlaylogRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.GetPlaylogRequest.toObject = function(includeInstance, msg) { + var f, obj = { + profileApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), + lastUserPlayDate: jspb.Message.getFieldWithDefault(msg, 2, ""), + limit: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.mythos.maimai.v0.GetPlaylogRequest; + return proto.mythos.maimai.v0.GetPlaylogRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.mythos.maimai.v0.GetPlaylogRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setProfileApiId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setLastUserPlayDate(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setLimit(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.mythos.maimai.v0.GetPlaylogRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.mythos.maimai.v0.GetPlaylogRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.GetPlaylogRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getProfileApiId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = /** @type {string} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeString( + 2, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeUint64( + 3, + f + ); + } +}; + + +/** + * optional string profile_api_id = 1; + * @return {string} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.getProfileApiId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.setProfileApiId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string last_user_play_date = 2; + * @return {string} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.getLastUserPlayDate = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.setLastUserPlayDate = function(value) { + return jspb.Message.setField(this, 2, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.clearLastUserPlayDate = function() { + return jspb.Message.setField(this, 2, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.hasLastUserPlayDate = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint64 limit = 3; + * @return {number} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.getLimit = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.setLimit = function(value) { + return jspb.Message.setField(this, 3, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.mythos.maimai.v0.GetPlaylogRequest} returns this + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.clearLimit = function() { + return jspb.Message.setField(this, 3, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.mythos.maimai.v0.GetPlaylogRequest.prototype.hasLimit = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.toObject = function(opt_includeInstance) { + return proto.mythos.maimai.v0.GetPlaylogStreamItem.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.mythos.maimai.v0.GetPlaylogStreamItem} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.toObject = function(includeInstance, msg) { + var f, obj = { + playlogApiId: jspb.Message.getFieldWithDefault(msg, 1, ""), + info: (f = msg.getInfo()) && maimai_playlog_pb.PlaylogInfo.toObject(includeInstance, f), + judge: (f = msg.getJudge()) && maimai_playlog_pb.PlaylogJudge.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.mythos.maimai.v0.GetPlaylogStreamItem; + return proto.mythos.maimai.v0.GetPlaylogStreamItem.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.mythos.maimai.v0.GetPlaylogStreamItem} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setPlaylogApiId(value); + break; + case 2: + var value = new maimai_playlog_pb.PlaylogInfo; + reader.readMessage(value,maimai_playlog_pb.PlaylogInfo.deserializeBinaryFromReader); + msg.setInfo(value); + break; + case 3: + var value = new maimai_playlog_pb.PlaylogJudge; + reader.readMessage(value,maimai_playlog_pb.PlaylogJudge.deserializeBinaryFromReader); + msg.setJudge(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.mythos.maimai.v0.GetPlaylogStreamItem.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.mythos.maimai.v0.GetPlaylogStreamItem} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getPlaylogApiId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getInfo(); + if (f != null) { + writer.writeMessage( + 2, + f, + maimai_playlog_pb.PlaylogInfo.serializeBinaryToWriter + ); + } + f = message.getJudge(); + if (f != null) { + writer.writeMessage( + 3, + f, + maimai_playlog_pb.PlaylogJudge.serializeBinaryToWriter + ); + } +}; + + +/** + * optional string playlog_api_id = 1; + * @return {string} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.getPlaylogApiId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.setPlaylogApiId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional PlaylogInfo info = 2; + * @return {?proto.mythos.maimai.v0.PlaylogInfo} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.getInfo = function() { + return /** @type{?proto.mythos.maimai.v0.PlaylogInfo} */ ( + jspb.Message.getWrapperField(this, maimai_playlog_pb.PlaylogInfo, 2)); +}; + + +/** + * @param {?proto.mythos.maimai.v0.PlaylogInfo|undefined} value + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this +*/ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.setInfo = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.clearInfo = function() { + return this.setInfo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.hasInfo = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional PlaylogJudge judge = 3; + * @return {?proto.mythos.maimai.v0.PlaylogJudge} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.getJudge = function() { + return /** @type{?proto.mythos.maimai.v0.PlaylogJudge} */ ( + jspb.Message.getWrapperField(this, maimai_playlog_pb.PlaylogJudge, 3)); +}; + + +/** + * @param {?proto.mythos.maimai.v0.PlaylogJudge|undefined} value + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this +*/ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.setJudge = function(value) { + return jspb.Message.setWrapperField(this, 3, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.mythos.maimai.v0.GetPlaylogStreamItem} returns this + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.clearJudge = function() { + return this.setJudge(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.mythos.maimai.v0.GetPlaylogStreamItem.prototype.hasJudge = function() { + return jspb.Message.getField(this, 3) != null; +}; + + +goog.object.extend(exports, proto.mythos.maimai.v0); diff --git a/server/src/proto/myt/maimai/common.proto b/server/src/proto/myt/maimai/common.proto new file mode 100644 index 000000000..63ce6e003 --- /dev/null +++ b/server/src/proto/myt/maimai/common.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package mythos.maimai.v0; + +enum MaimaiRankingType { + MAIMAI_RANKING_TYPE_UNSPECIFIED = 0; + MAIMAI_RANKING_TYPE_ACHIEVEMENT = 1; + MAIMAI_RANKING_TYPE_DX_SCORE = 2; +} + +enum MaimaiLevel { + MAIMAI_LEVEL_UNSPECIFIED = 0; + MAIMAI_LEVEL_BASIC = 1; + MAIMAI_LEVEL_ADVANCED = 2; + MAIMAI_LEVEL_EXPERT = 3; + MAIMAI_LEVEL_MASTER = 4; + MAIMAI_LEVEL_REMASTER = 5; + MAIMAI_LEVEL_UTAGE = 6; +} + +enum MaimaiComboStatus { + MAIMAI_COMBO_STATUS_UNSPECIFIED = 0; + MAIMAI_COMBO_STATUS_NONE = 1; + MAIMAI_COMBO_STATUS_FULL_COMBO = 2; + MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS = 3; + MAIMAI_COMBO_STATUS_ALL_PERFECT = 4; + MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS = 5; +} + +enum MaimaiSyncStatus { + MAIMAI_SYNC_STATUS_UNSPECIFIED = 0; + MAIMAI_SYNC_STATUS_NONE = 1; + MAIMAI_SYNC_STATUS_FULL_SYNC = 2; + MAIMAI_SYNC_STATUS_FULL_SYNC_PLUS = 3; + MAIMAI_SYNC_STATUS_FULL_SYNC_DX = 4; + MAIMAI_SYNC_STATUS_FULL_SYNC_DX_PLUS = 5; +} + +enum MaimaiScoreRank { + MAIMAI_SCORE_RANK_UNSPECIFIED = 0; + MAIMAI_SCORE_RANK_D = 1; + MAIMAI_SCORE_RANK_C = 2; + MAIMAI_SCORE_RANK_B = 3; + MAIMAI_SCORE_RANK_BB = 4; + MAIMAI_SCORE_RANK_BBB = 5; + MAIMAI_SCORE_RANK_A = 6; + MAIMAI_SCORE_RANK_AA = 7; + MAIMAI_SCORE_RANK_AAA = 8; + MAIMAI_SCORE_RANK_S = 9; + MAIMAI_SCORE_RANK_S_PLUS = 10; + MAIMAI_SCORE_RANK_SS = 11; + MAIMAI_SCORE_RANK_SS_PLUS = 12; + MAIMAI_SCORE_RANK_SSS = 13; + MAIMAI_SCORE_RANK_SSS_PLUS = 14; +} diff --git a/server/src/proto/myt/maimai/playlog.proto b/server/src/proto/myt/maimai/playlog.proto new file mode 100644 index 000000000..bd1897501 --- /dev/null +++ b/server/src/proto/myt/maimai/playlog.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package mythos.maimai.v0; + +import "maimai/common.proto"; + +message PlaylogInfo { + int32 music_id = 1; + MaimaiLevel level = 2; + int32 achievement = 3; + int32 deluxscore = 4; + MaimaiScoreRank score_rank = 5; + MaimaiComboStatus combo_status = 6; + MaimaiSyncStatus sync_status = 7; + bool is_clear = 8; + bool is_achieve_new_record = 9; + bool is_deluxscore_new_record = 10; + int32 track = 11; + string user_play_date = 12; +} + +message PlaylogJudge { + int32 judge_critical_perfect = 1; + int32 judge_perfect = 2; + int32 judge_great = 3; + int32 judge_good = 4; + int32 judge_miss = 5; + int32 max_combo = 6; + int32 fast_count = 7; + int32 late_count = 8; +} diff --git a/server/src/proto/myt/maimai/user.proto b/server/src/proto/myt/maimai/user.proto new file mode 100644 index 000000000..8b996b1a3 --- /dev/null +++ b/server/src/proto/myt/maimai/user.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package mythos.maimai.v0; + +import "maimai/playlog.proto"; + +service MaimaiUser { + rpc GetPlaylog(GetPlaylogRequest) returns (stream GetPlaylogStreamItem); +} + +message GetPlaylogRequest { + string profile_api_id = 1; + optional string last_user_play_date = 2; + optional uint64 limit = 3; +} + +message GetPlaylogStreamItem { + string playlog_api_id = 1; + PlaylogInfo info = 2; + PlaylogJudge judge = 3; +} diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts index 2d882da7a..0de1f0128 100644 --- a/server/src/test-utils/test-data.ts +++ b/server/src/test-utils/test-data.ts @@ -1335,3 +1335,39 @@ export const TestingChunithmSongConverter: SongDocument<"chunithm"> = { searchTerms: [], title: "killy killy JOKER", }; + +export const TestingMaimaiDXSongConverter: SongDocument<"maimaidx"> = { + altTitles: [], + artist: "suzu", + data: { + displayVersion: "UNiVERSE", + genre: "オンゲキ&CHUNITHM" + }, + id: 844, + searchTerms: [ + "Shukusei", + "Shukusei Shinpan", + "Syukusei Shinpan" + ], + title: "宿星審判" +} + +export const TestingMaimaiDXChartConverter: ChartDocument<"maimaidx:Single"> = { + chartID: "be94febb411c6894e202f4daefdb683124c40339", + data: { + inGameID: 11294 + }, + difficulty: "DX Advanced", + isPrimary: true, + level: "8", + levelNum: 8.2, + playtype: "Single", + songID: 844, + versions: [ + "universeplus", + "festival", + "festivalplus", + "buddies", + "buddiesplus" + ] +} From b2b4e9d093e1f2f06ca34545ca8457bf56bdc1ae Mon Sep 17 00:00:00 2001 From: albshin Date: Thu, 8 Aug 2024 01:59:15 -0400 Subject: [PATCH 2/6] chore: fix myt integration showing up, tests --- .../app/pages/dashboard/import/ImportPage.tsx | 10 +++++++++ .../api/myt-maimaidx/converter.test.ts | 8 +++---- .../test-utils/mock-db/charts-maimaidx.json | 21 +++++++++++++++++++ .../test-utils/mock-db/songs-maimaidx.json | 17 +++++++++++++++ server/src/test-utils/test-data.ts | 8 +++---- 5 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 server/src/test-utils/mock-db/charts-maimaidx.json create mode 100644 server/src/test-utils/mock-db/songs-maimaidx.json diff --git a/client/src/app/pages/dashboard/import/ImportPage.tsx b/client/src/app/pages/dashboard/import/ImportPage.tsx index 25987d4a6..cdc648309 100644 --- a/client/src/app/pages/dashboard/import/ImportPage.tsx +++ b/client/src/app/pages/dashboard/import/ImportPage.tsx @@ -622,6 +622,16 @@ function ImportTypeInfoCard({ key="myt-chunithm" /> ); + case "api/myt-maimaidx": + return ( + + ); case "api/myt-ongeki": return ( { service: "MYT", game: "maimaidx", scoreMeta: {}, - timeAchieved: ParseDateFromString("2022-11-03T04:21:05.000Z+09:00"), + timeAchieved: ParseDateFromString("2022-11-03T04:21:05.000+09:00"), comment: null, importType: "api/myt-maimaidx", scoreData: { - score: 99.0562, + percent: 99.0562, lamp: "CLEAR", judgements: { pcrit: 10, @@ -197,8 +197,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }); t.test("Should throw on missing chart", (t) => { - t.rejects(() => convert({ info: { musicId: 999999 } }), { - message: /Can't find chart with id 999999 and difficulty Master/u, + t.rejects(() => convert({ info: { musicId: 999999, level: MaimaiLevel.MAIMAI_LEVEL_MASTER } }), { + message: /Can't find chart with id 999999 and difficulty DX Master/u, }); t.end(); }); diff --git a/server/src/test-utils/mock-db/charts-maimaidx.json b/server/src/test-utils/mock-db/charts-maimaidx.json new file mode 100644 index 000000000..42238a092 --- /dev/null +++ b/server/src/test-utils/mock-db/charts-maimaidx.json @@ -0,0 +1,21 @@ +[ + { + "chartID": "fab3d632610b9b98ee1e4f68e9ecf0161f9cb8cd", + "data": { + "inGameID": 11294 + }, + "difficulty": "DX Expert", + "isPrimary": true, + "level": "12", + "levelNum": 12.2, + "playtype": "Single", + "songID": 844, + "versions": [ + "universeplus", + "festival", + "festivalplus", + "buddies", + "buddiesplus" + ] + } +] \ No newline at end of file diff --git a/server/src/test-utils/mock-db/songs-maimaidx.json b/server/src/test-utils/mock-db/songs-maimaidx.json new file mode 100644 index 000000000..6aa38fbd1 --- /dev/null +++ b/server/src/test-utils/mock-db/songs-maimaidx.json @@ -0,0 +1,17 @@ +[ + { + "altTitles": [], + "artist": "suzu", + "data": { + "displayVersion": "UNiVERSE", + "genre": "オンゲキ&CHUNITHM" + }, + "id": 844, + "searchTerms": [ + "Shukusei", + "Shukusei Shinpan", + "Syukusei Shinpan" + ], + "title": "宿星審判" + } +] \ No newline at end of file diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts index 0de1f0128..046d5dd6a 100644 --- a/server/src/test-utils/test-data.ts +++ b/server/src/test-utils/test-data.ts @@ -1353,14 +1353,14 @@ export const TestingMaimaiDXSongConverter: SongDocument<"maimaidx"> = { } export const TestingMaimaiDXChartConverter: ChartDocument<"maimaidx:Single"> = { - chartID: "be94febb411c6894e202f4daefdb683124c40339", + chartID: "fab3d632610b9b98ee1e4f68e9ecf0161f9cb8cd", data: { inGameID: 11294 }, - difficulty: "DX Advanced", + difficulty: "DX Expert", isPrimary: true, - level: "8", - levelNum: 8.2, + level: "12", + levelNum: 12.2, playtype: "Single", songID: 844, versions: [ From 340b9a45bbedb676b7453cdea89f922d9bdb79cb Mon Sep 17 00:00:00 2001 From: albshin Date: Thu, 8 Aug 2024 02:11:29 -0400 Subject: [PATCH 3/6] chore: formatting --- .../api/myt-maimaidx/converter.test.ts | 114 ++++++++++-------- .../api/myt-maimaidx/converter.ts | 79 ++++++------ .../import-types/api/myt-maimaidx/parser.ts | 12 +- 3 files changed, 115 insertions(+), 90 deletions(-) diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts index f883f2daf..47b33cb1d 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts @@ -2,52 +2,60 @@ import ConvertAPIMytMaimaiDx from "./converter"; import CreateLogCtx from "lib/logger/logger"; import { ParseDateFromString } from "lib/score-import/framework/common/score-utils"; import { - MaimaiComboStatus, + MaimaiComboStatus, MaimaiLevel, - MaimaiScoreRank, + MaimaiScoreRank, MaimaiSyncStatus, } from "proto/generated/maimai/common_pb"; import t from "tap"; import { dmf } from "test-utils/misc"; import ResetDBState from "test-utils/resets"; -import { TestingMaimaiDXChartConverter, TestingMaimaiDXSongConverter } from "test-utils/test-data"; +import { + TestingMaimaiDXChartConverter, + TestingMaimaiDXSongConverter, +} from "test-utils/test-data"; import type { MytMaimaiDxScore } from "./types"; const logger = CreateLogCtx(__filename); const parsedScore: MytMaimaiDxScore = { - playlogApiId: "6071c489-6ab9-4674-a443-f88b603fa596", - info: { - musicId: 11294, - level: MaimaiLevel.MAIMAI_LEVEL_EXPERT, - achievement: 990562, - deluxscore: 1825, - scoreRank: MaimaiScoreRank.MAIMAI_SCORE_RANK_SS, - comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, - syncStatus: MaimaiSyncStatus.MAIMAI_SYNC_STATUS_NONE, - isClear: true, - isAchieveNewRecord: true, - isDeluxscoreNewRecord: true, - track: 1, - userPlayDate: "2022-11-03T04:21:05.000+09:00" - }, - judge: { - judgeCriticalPerfect: 10, - judgePerfect: 656, - judgeGreat: 19, - judgeGood: 1, - judgeMiss: 8, - maxCombo: 279, - fastCount: 5, - lateCount: 8 - } + playlogApiId: "6071c489-6ab9-4674-a443-f88b603fa596", + info: { + musicId: 11294, + level: MaimaiLevel.MAIMAI_LEVEL_EXPERT, + achievement: 990562, + deluxscore: 1825, + scoreRank: MaimaiScoreRank.MAIMAI_SCORE_RANK_SS, + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, + syncStatus: MaimaiSyncStatus.MAIMAI_SYNC_STATUS_NONE, + isClear: true, + isAchieveNewRecord: true, + isDeluxscoreNewRecord: true, + track: 1, + userPlayDate: "2022-11-03T04:21:05.000+09:00", + }, + judge: { + judgeCriticalPerfect: 10, + judgePerfect: 656, + judgeGreat: 19, + judgeGood: 1, + judgeMiss: 8, + maxCombo: 279, + fastCount: 5, + lateCount: 8, + }, }; t.test("#ConvertAPIMytMaimaiDx", (t) => { t.beforeEach(ResetDBState); function convert(modifier: any = {}) { - return ConvertAPIMytMaimaiDx(dmf(parsedScore, modifier), {}, "api/myt-maimaidx", logger); + return ConvertAPIMytMaimaiDx( + dmf(parsedScore, modifier), + {}, + "api/myt-maimaidx", + logger, + ); } t.test("Should return a dryScore on valid input.", async (t) => { @@ -71,7 +79,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { perfect: 656, great: 19, good: 1, - miss: 8 + miss: 8, }, optional: { fast: 5, @@ -95,7 +103,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }), { message: /Utage charts are not supported/u, - } + }, ); t.end(); }); @@ -105,12 +113,12 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { () => convert({ info: { - level: MaimaiLevel.MAIMAI_LEVEL_UNSPECIFIED + level: MaimaiLevel.MAIMAI_LEVEL_UNSPECIFIED, }, }), { message: /Can't process a score with unspecified difficulty/u, - } + }, ); t.end(); }); @@ -125,7 +133,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }), { dryScore: { scoreData: { lamp: "ALL PERFECT+" } }, - } + }, ); t.hasStrict( await convert({ @@ -136,7 +144,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }), { dryScore: { scoreData: { lamp: "ALL PERFECT" } }, - } + }, ); t.hasStrict( await convert({ @@ -147,7 +155,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }), { dryScore: { scoreData: { lamp: "FULL COMBO+" } }, - } + }, ); t.hasStrict( await convert({ @@ -158,48 +166,54 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }), { dryScore: { scoreData: { lamp: "FULL COMBO" } }, - } + }, ); t.hasStrict( await convert({ - info: { - comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, - isClear: true, - } + info: { + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, + isClear: true, + }, }), { dryScore: { scoreData: { lamp: "CLEAR" } }, - } + }, ); t.hasStrict( await convert({ info: { - comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, - isClear: false, + comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE, + isClear: false, }, }), { dryScore: { scoreData: { lamp: "FAILED" } }, - } + }, ); t.hasStrict( await convert({ info: { comboStatus: MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO, - isClear: false, + isClear: false, }, }), { dryScore: { scoreData: { lamp: "FAILED" } }, - } + }, ); t.end(); }); t.test("Should throw on missing chart", (t) => { - t.rejects(() => convert({ info: { musicId: 999999, level: MaimaiLevel.MAIMAI_LEVEL_MASTER } }), { - message: /Can't find chart with id 999999 and difficulty DX Master/u, - }); + t.rejects( + () => + convert({ + info: { musicId: 999999, level: MaimaiLevel.MAIMAI_LEVEL_MASTER }, + }), + { + message: /Can't find chart with id 999999 and difficulty DX Master/u, + }, + ); t.end(); }); diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts index a8949fcc2..6241e4130 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts @@ -6,8 +6,8 @@ import { } from "lib/score-import/framework/common/converter-failures"; import { ParseDateFromString } from "lib/score-import/framework/common/score-utils"; import { - MaimaiComboStatus, - MaimaiLevel + MaimaiComboStatus, + MaimaiLevel, } from "proto/generated/maimai/common_pb"; import { FindChartOnInGameID } from "utils/queries/charts"; import { FindSongOnID } from "utils/queries/songs"; @@ -24,22 +24,20 @@ const DIFFICULTIES = { [MaimaiLevel.MAIMAI_LEVEL_EXPERT]: "Expert", [MaimaiLevel.MAIMAI_LEVEL_MASTER]: "Master", [MaimaiLevel.MAIMAI_LEVEL_REMASTER]: "Re:Master", - [MaimaiLevel.MAIMAI_LEVEL_UTAGE]: "Utage", + [MaimaiLevel.MAIMAI_LEVEL_UTAGE]: "Utage", }; function getLamp( comboStatus: number, isClear: boolean, ): ScoreData<"maimaidx:Single">["lamp"] | undefined { - if ( - comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_UNSPECIFIED - ) { + if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_UNSPECIFIED) { return undefined; } - if (isClear === false) { + if (isClear === false) { return "FAILED"; - } + } if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE) { return "CLEAR"; @@ -64,52 +62,61 @@ function getLamp( return undefined; } -const ConvertAPIMytMaimaiDx: ConverterFunction = async ( - data, - _context, - importType, - logger -) => { +const ConvertAPIMytMaimaiDx: ConverterFunction< + MytMaimaiDxScore, + EmptyObject +> = async (data, _context, importType, logger) => { if (data.info === undefined || data.judge === undefined) { throw new InvalidScoreFailure("Failed to receive score data from MYT API"); } - const baseDifficulty = DIFFICULTIES[data.info.level]; + const baseDifficulty = DIFFICULTIES[data.info.level]; if (baseDifficulty === undefined) { throw new InvalidScoreFailure( - `Can't process a score with unspecified difficulty (musicId ${data.info.musicId})` + `Can't process a score with unspecified difficulty (musicId ${data.info.musicId})`, ); - } - if (baseDifficulty === "Utage") { + } + if (baseDifficulty === "Utage") { throw new SkipScoreFailure("Utage charts are not supported"); - } - // Songs with an ID higher than 10000 are considered DX charts - const difficulty = data.info.musicId >= 10000 ? `DX ${baseDifficulty}` : baseDifficulty; + } + // Songs with an ID higher than 10000 are considered DX charts + const difficulty = + data.info.musicId >= 10000 ? `DX ${baseDifficulty}` : baseDifficulty; const lamp = getLamp(data.info.comboStatus, data.info.isClear); if (lamp === undefined) { throw new InvalidScoreFailure( - "Can't process a score with an invalid combo status and/or clear status" + "Can't process a score with an invalid combo status and/or clear status", ); } - const chart = await FindChartOnInGameID("maimaidx", data.info.musicId, "Single", difficulty); + const chart = await FindChartOnInGameID( + "maimaidx", + data.info.musicId, + "Single", + difficulty, + ); if (chart === null) { throw new SongOrChartNotFoundFailure( `Can't find chart with id ${data.info.musicId} and difficulty ${difficulty}`, importType, data, - {} + {}, ); } const song = await FindSongOnID("maimaidx", chart.songID); if (song === null) { - logger.severe(`Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, { chart }); - throw new InternalFailure(`Song/chart desync: ${chart.songID} for chart ${chart.chartID}`); + logger.severe( + `Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, + { chart }, + ); + throw new InternalFailure( + `Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, + ); } const dryScore: DryScore<"maimaidx:Single"> = { @@ -123,17 +130,17 @@ const ConvertAPIMytMaimaiDx: ConverterFunction = percent: data.info.achievement / 10000, lamp, judgements: { - pcrit: data.judge.judgeCriticalPerfect, - perfect: data.judge.judgePerfect, - great: data.judge.judgeGreat, - good: data.judge.judgeGood, - miss: data.judge.judgeMiss + pcrit: data.judge.judgeCriticalPerfect, + perfect: data.judge.judgePerfect, + great: data.judge.judgeGreat, + good: data.judge.judgeGood, + miss: data.judge.judgeMiss, + }, + optional: { + fast: data.judge.fastCount, + slow: data.judge.lateCount, + maxCombo: data.judge.maxCombo, }, - optional: { - fast: data.judge.fastCount, - slow: data.judge.lateCount, - maxCombo: data.judge.maxCombo - } }, }; diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts index 3a388135b..ac4a09030 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts @@ -15,7 +15,7 @@ import type { integer } from "tachi-common"; import type { EmptyObject } from "utils/types"; async function* getObjectsFromGrpcIterable( - iterable: AsyncIterable + iterable: AsyncIterable, ): AsyncIterable { for await (const item of iterable) { yield item.toObject(); @@ -24,7 +24,7 @@ async function* getObjectsFromGrpcIterable( export default async function ParseMytMaimaiDx( userID: integer, - logger: KtLogger + logger: KtLogger, ): Promise> { const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", logger); const endpoint = GetMytHostname(); @@ -36,12 +36,16 @@ export default async function ParseMytMaimaiDx( let iterable; try { - const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, logger); + const stream = StreamRPCAsAsync( + client.getPlaylog.bind(client), + request, + logger, + ); iterable = getObjectsFromGrpcIterable(stream); } catch (err) { logger.error( - `Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}` + `Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}`, ); throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); From 030b0a79e6e1d68ab8443099fa6db623df2b701a Mon Sep 17 00:00:00 2001 From: albshin Date: Thu, 8 Aug 2024 02:16:16 -0400 Subject: [PATCH 4/6] fix: linting --- client/src/app/pages/dashboard/import/ImportPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/pages/dashboard/import/ImportPage.tsx b/client/src/app/pages/dashboard/import/ImportPage.tsx index cdc648309..cbef105d5 100644 --- a/client/src/app/pages/dashboard/import/ImportPage.tsx +++ b/client/src/app/pages/dashboard/import/ImportPage.tsx @@ -320,7 +320,7 @@ function ImportInfoDisplayer({ game }: { game: Game }) { moreInfo="If you are playing on an official maimai DX server, you can import play data from it here." key="maimai DX NET Importer" />, - , + ); } else if (game === "museca") { Content.unshift( From 568e40e870512f1c23b0833c9e934148009d9885 Mon Sep 17 00:00:00 2001 From: albshin Date: Thu, 8 Aug 2024 02:22:01 -0400 Subject: [PATCH 5/6] chore: more formatting --- .../api/myt-maimaidx/converter.test.ts | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts index 47b33cb1d..49bcc81c9 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts @@ -10,10 +10,7 @@ import { import t from "tap"; import { dmf } from "test-utils/misc"; import ResetDBState from "test-utils/resets"; -import { - TestingMaimaiDXChartConverter, - TestingMaimaiDXSongConverter, -} from "test-utils/test-data"; +import { TestingMaimaiDXChartConverter, TestingMaimaiDXSongConverter } from "test-utils/test-data"; import type { MytMaimaiDxScore } from "./types"; const logger = CreateLogCtx(__filename); @@ -50,12 +47,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { t.beforeEach(ResetDBState); function convert(modifier: any = {}) { - return ConvertAPIMytMaimaiDx( - dmf(parsedScore, modifier), - {}, - "api/myt-maimaidx", - logger, - ); + return ConvertAPIMytMaimaiDx(dmf(parsedScore, modifier), {}, "api/myt-maimaidx", logger); } t.test("Should return a dryScore on valid input.", async (t) => { @@ -102,7 +94,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - message: /Utage charts are not supported/u, + message: /Utage charts are not supported/u }, ); t.end(); @@ -117,7 +109,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - message: /Can't process a score with unspecified difficulty/u, + message: /Can't process a score with unspecified difficulty/u }, ); t.end(); @@ -132,7 +124,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "ALL PERFECT+" } }, + dryScore: { scoreData: { lamp: "ALL PERFECT+" } } }, ); t.hasStrict( @@ -143,7 +135,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "ALL PERFECT" } }, + dryScore: { scoreData: { lamp: "ALL PERFECT" } } }, ); t.hasStrict( @@ -154,7 +146,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FULL COMBO+" } }, + dryScore: { scoreData: { lamp: "FULL COMBO+" } } }, ); t.hasStrict( @@ -165,7 +157,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FULL COMBO" } }, + dryScore: { scoreData: { lamp: "FULL COMBO" } } }, ); t.hasStrict( @@ -176,7 +168,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "CLEAR" } }, + dryScore: { scoreData: { lamp: "CLEAR" } } }, ); t.hasStrict( @@ -187,7 +179,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FAILED" } }, + dryScore: { scoreData: { lamp: "FAILED" } } }, ); t.hasStrict( @@ -198,7 +190,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FAILED" } }, + dryScore: { scoreData: { lamp: "FAILED" } } }, ); t.end(); From 259e573a2d0c7ef9f7630dd00956e1fa7e623ed8 Mon Sep 17 00:00:00 2001 From: albshin Date: Thu, 8 Aug 2024 02:28:29 -0400 Subject: [PATCH 6/6] chore: maybe running lint-fix will fix it --- .../api/myt-maimaidx/converter.test.ts | 38 +++++++-------- .../api/myt-maimaidx/converter.ts | 47 ++++++++----------- .../import-types/api/myt-maimaidx/parser.ts | 12 ++--- .../score-import/import-types/common/types.ts | 2 +- server/src/test-utils/test-data.ts | 24 +++------- 5 files changed, 50 insertions(+), 73 deletions(-) diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts index 49bcc81c9..a0b5157e0 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.test.ts @@ -94,8 +94,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - message: /Utage charts are not supported/u - }, + message: /Utage charts are not supported/u, + } ); t.end(); }); @@ -109,8 +109,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - message: /Can't process a score with unspecified difficulty/u - }, + message: /Can't process a score with unspecified difficulty/u, + } ); t.end(); }); @@ -124,8 +124,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "ALL PERFECT+" } } - }, + dryScore: { scoreData: { lamp: "ALL PERFECT+" } }, + } ); t.hasStrict( await convert({ @@ -135,8 +135,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "ALL PERFECT" } } - }, + dryScore: { scoreData: { lamp: "ALL PERFECT" } }, + } ); t.hasStrict( await convert({ @@ -146,8 +146,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FULL COMBO+" } } - }, + dryScore: { scoreData: { lamp: "FULL COMBO+" } }, + } ); t.hasStrict( await convert({ @@ -157,8 +157,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FULL COMBO" } } - }, + dryScore: { scoreData: { lamp: "FULL COMBO" } }, + } ); t.hasStrict( await convert({ @@ -168,8 +168,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "CLEAR" } } - }, + dryScore: { scoreData: { lamp: "CLEAR" } }, + } ); t.hasStrict( await convert({ @@ -179,8 +179,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FAILED" } } - }, + dryScore: { scoreData: { lamp: "FAILED" } }, + } ); t.hasStrict( await convert({ @@ -190,8 +190,8 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }, }), { - dryScore: { scoreData: { lamp: "FAILED" } } - }, + dryScore: { scoreData: { lamp: "FAILED" } }, + } ); t.end(); }); @@ -204,7 +204,7 @@ t.test("#ConvertAPIMytMaimaiDx", (t) => { }), { message: /Can't find chart with id 999999 and difficulty DX Master/u, - }, + } ); t.end(); }); diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts index 6241e4130..8568ddf1e 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/converter.ts @@ -5,10 +5,7 @@ import { SongOrChartNotFoundFailure, } from "lib/score-import/framework/common/converter-failures"; import { ParseDateFromString } from "lib/score-import/framework/common/score-utils"; -import { - MaimaiComboStatus, - MaimaiLevel, -} from "proto/generated/maimai/common_pb"; +import { MaimaiComboStatus, MaimaiLevel } from "proto/generated/maimai/common_pb"; import { FindChartOnInGameID } from "utils/queries/charts"; import { FindSongOnID } from "utils/queries/songs"; import type { ConverterFunction } from "../../common/types"; @@ -29,13 +26,13 @@ const DIFFICULTIES = { function getLamp( comboStatus: number, - isClear: boolean, + isClear: boolean ): ScoreData<"maimaidx:Single">["lamp"] | undefined { if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_UNSPECIFIED) { return undefined; } - if (isClear === false) { + if (!isClear) { return "FAILED"; } @@ -62,61 +59,55 @@ function getLamp( return undefined; } -const ConvertAPIMytMaimaiDx: ConverterFunction< - MytMaimaiDxScore, - EmptyObject -> = async (data, _context, importType, logger) => { +const ConvertAPIMytMaimaiDx: ConverterFunction = async ( + data, + _context, + importType, + logger +) => { if (data.info === undefined || data.judge === undefined) { throw new InvalidScoreFailure("Failed to receive score data from MYT API"); } const baseDifficulty = DIFFICULTIES[data.info.level]; + if (baseDifficulty === undefined) { throw new InvalidScoreFailure( - `Can't process a score with unspecified difficulty (musicId ${data.info.musicId})`, + `Can't process a score with unspecified difficulty (musicId ${data.info.musicId})` ); } + if (baseDifficulty === "Utage") { throw new SkipScoreFailure("Utage charts are not supported"); } + // Songs with an ID higher than 10000 are considered DX charts - const difficulty = - data.info.musicId >= 10000 ? `DX ${baseDifficulty}` : baseDifficulty; + const difficulty = data.info.musicId >= 10000 ? `DX ${baseDifficulty}` : baseDifficulty; const lamp = getLamp(data.info.comboStatus, data.info.isClear); if (lamp === undefined) { throw new InvalidScoreFailure( - "Can't process a score with an invalid combo status and/or clear status", + "Can't process a score with an invalid combo status and/or clear status" ); } - const chart = await FindChartOnInGameID( - "maimaidx", - data.info.musicId, - "Single", - difficulty, - ); + const chart = await FindChartOnInGameID("maimaidx", data.info.musicId, "Single", difficulty); if (chart === null) { throw new SongOrChartNotFoundFailure( `Can't find chart with id ${data.info.musicId} and difficulty ${difficulty}`, importType, data, - {}, + {} ); } const song = await FindSongOnID("maimaidx", chart.songID); if (song === null) { - logger.severe( - `Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, - { chart }, - ); - throw new InternalFailure( - `Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, - ); + logger.severe(`Song/chart desync: ${chart.songID} for chart ${chart.chartID}`, { chart }); + throw new InternalFailure(`Song/chart desync: ${chart.songID} for chart ${chart.chartID}`); } const dryScore: DryScore<"maimaidx:Single"> = { diff --git a/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts b/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts index ac4a09030..3a388135b 100644 --- a/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts +++ b/server/src/lib/score-import/import-types/api/myt-maimaidx/parser.ts @@ -15,7 +15,7 @@ import type { integer } from "tachi-common"; import type { EmptyObject } from "utils/types"; async function* getObjectsFromGrpcIterable( - iterable: AsyncIterable, + iterable: AsyncIterable ): AsyncIterable { for await (const item of iterable) { yield item.toObject(); @@ -24,7 +24,7 @@ async function* getObjectsFromGrpcIterable( export default async function ParseMytMaimaiDx( userID: integer, - logger: KtLogger, + logger: KtLogger ): Promise> { const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", logger); const endpoint = GetMytHostname(); @@ -36,16 +36,12 @@ export default async function ParseMytMaimaiDx( let iterable; try { - const stream = StreamRPCAsAsync( - client.getPlaylog.bind(client), - request, - logger, - ); + const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, logger); iterable = getObjectsFromGrpcIterable(stream); } catch (err) { logger.error( - `Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}`, + `Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}` ); throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`); diff --git a/server/src/lib/score-import/import-types/common/types.ts b/server/src/lib/score-import/import-types/common/types.ts index 3097f5413..cd8f466c1 100644 --- a/server/src/lib/score-import/import-types/common/types.ts +++ b/server/src/lib/score-import/import-types/common/types.ts @@ -1,7 +1,7 @@ import type { ConverterFailure } from "../../framework/common/converter-failures"; import type { DryScore } from "../../framework/common/types"; import type { MytChunithmScore } from "../api/myt-chunithm/types"; -import { MytMaimaiDxScore } from "../api/myt-maimaidx/types"; +import type { MytMaimaiDxScore } from "../api/myt-maimaidx/types"; import type { MytOngekiScore } from "../api/myt-ongeki/types"; import type { MytWaccaScore } from "../api/myt-wacca/types"; import type { SDVXEamusementCSVData } from "../file/eamusement-sdvx-csv/types"; diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts index 046d5dd6a..32f8c2826 100644 --- a/server/src/test-utils/test-data.ts +++ b/server/src/test-utils/test-data.ts @@ -1341,21 +1341,17 @@ export const TestingMaimaiDXSongConverter: SongDocument<"maimaidx"> = { artist: "suzu", data: { displayVersion: "UNiVERSE", - genre: "オンゲキ&CHUNITHM" + genre: "オンゲキ&CHUNITHM", }, id: 844, - searchTerms: [ - "Shukusei", - "Shukusei Shinpan", - "Syukusei Shinpan" - ], - title: "宿星審判" -} + searchTerms: ["Shukusei", "Shukusei Shinpan", "Syukusei Shinpan"], + title: "宿星審判", +}; export const TestingMaimaiDXChartConverter: ChartDocument<"maimaidx:Single"> = { chartID: "fab3d632610b9b98ee1e4f68e9ecf0161f9cb8cd", data: { - inGameID: 11294 + inGameID: 11294, }, difficulty: "DX Expert", isPrimary: true, @@ -1363,11 +1359,5 @@ export const TestingMaimaiDXChartConverter: ChartDocument<"maimaidx:Single"> = { levelNum: 12.2, playtype: "Single", songID: 844, - versions: [ - "universeplus", - "festival", - "festivalplus", - "buddies", - "buddiesplus" - ] -} + versions: ["universeplus", "festival", "festivalplus", "buddies", "buddiesplus"], +};