mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-12 09:18:25 +03:00
checkpoint: 8
This commit is contained in:
@@ -70,7 +70,7 @@ export function ParseGPT(str: string) {
|
||||
// this is an interesting way of game checking noah
|
||||
const gameConfig = GetGameConfig(game) as GameConfig | undefined;
|
||||
|
||||
if (!gameConfig || !gameConfig.validPlaytypes.includes(playtype)) {
|
||||
if (!gameConfig || !gameConfig.playtypes.includes(playtype)) {
|
||||
throw new Error(`Invalid GPT Combination '${str}'.`);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const GPTChoices: Array<[string, string]> = [];
|
||||
for (const game of ServerConfig.games) {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
for (const playtype of gameConfig.validPlaytypes) {
|
||||
for (const playtype of gameConfig.playtypes) {
|
||||
GPTChoices.push([FormatGame(game, playtype), `${game}:${playtype}`]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function HandleGoalAchievedV1(
|
||||
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
const shouldShowPlaytype = gameConfig.validPlaytypes.length !== 1;
|
||||
const shouldShowPlaytype = gameConfig.playtypes.length !== 1;
|
||||
|
||||
const embed = CreateEmbed(userDoc.id)
|
||||
.setTitle(
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function HandleQuestAchievedV1(
|
||||
const quest = await GetQuestWithID(event.questID, game, playtype);
|
||||
|
||||
const gameConfig = GetGameConfig(game);
|
||||
const shouldShowPlaytype = gameConfig.validPlaytypes.length > 1 ? ` (${playtype})` : "";
|
||||
const shouldShowPlaytype = gameConfig.playtypes.length > 1 ? ` (${playtype})` : "";
|
||||
|
||||
const embed = CreateEmbed(userDoc.id).setTitle(
|
||||
`${userDoc.username} just completed the ${quest.name}${shouldShowPlaytype} quest!`
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function PlaytypeSelect({
|
||||
</div>
|
||||
<div className="card-footer">
|
||||
<div className="d-flex justify-content-center btn-group">
|
||||
{gameConfig.validPlaytypes.map((pt) => (
|
||||
{gameConfig.playtypes.map((pt) => (
|
||||
<Link
|
||||
key={pt}
|
||||
className="btn btn-outline-primary float-right"
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function BatchManualPage() {
|
||||
|
||||
if (!gptConfig) {
|
||||
throw new Error(
|
||||
`Invalid Playtype ${data.meta.playtype}. Expected any of ${gameConfig.validPlaytypes}.`
|
||||
`Invalid Playtype ${data.meta.playtype}. Expected any of ${gameConfig.playtypes}.`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ const PR_LOCAL_QUESTS_SCHEMA: PrudenceSchema = {
|
||||
playtype: (self, parent) => {
|
||||
const gameConfig = GetGameConfig(parent.game as any);
|
||||
|
||||
if (!gameConfig.validPlaytypes.includes(self)) {
|
||||
if (!gameConfig.playtypes.includes(self)) {
|
||||
return `Invalid playtype '${self}' for ${parent.game}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ export default function GameRoutes() {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/games/:game">
|
||||
{gameConfig.validPlaytypes.length === 1 ? (
|
||||
<Redirect to={`/games/${game}/${gameConfig.validPlaytypes[0]}`} />
|
||||
{gameConfig.playtypes.length === 1 ? (
|
||||
<Redirect to={`/games/${game}/${gameConfig.playtypes[0]}`} />
|
||||
) : (
|
||||
<PlaytypeSelect
|
||||
subheaderCrumbs={["Games", gameConfig.name]}
|
||||
|
||||
@@ -154,9 +154,9 @@ function UserGameRoutes({ reqUser }: { reqUser: UserDocument }) {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/u/:userID/games/:game">
|
||||
{gameConfig.validPlaytypes.length === 1 ? (
|
||||
{gameConfig.playtypes.length === 1 ? (
|
||||
<Redirect
|
||||
to={`/u/${reqUser.username}/games/${game}/${gameConfig.validPlaytypes[0]}`}
|
||||
to={`/u/${reqUser.username}/games/${game}/${gameConfig.playtypes[0]}`}
|
||||
/>
|
||||
) : (
|
||||
<PlaytypeSelect
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function AllGames() {
|
||||
for (const game of TachiConfig.games) {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
for (const playtype of gameConfig.validPlaytypes) {
|
||||
for (const playtype of gameConfig.playtypes) {
|
||||
links.push(
|
||||
<MenuLink
|
||||
key={`${game}:${playtype}`}
|
||||
|
||||
@@ -74,7 +74,7 @@ export function HeaderMenu({
|
||||
}
|
||||
|
||||
for (const game of TachiConfig.games) {
|
||||
for (const playtype of GetGameConfig(game).validPlaytypes) {
|
||||
for (const playtype of GetGameConfig(game).playtypes) {
|
||||
const e = ugsMap.get(`${game}:${playtype}`);
|
||||
|
||||
if (!e) {
|
||||
|
||||
@@ -137,9 +137,7 @@ function SearchResults({ results }: { results: SearchReturns }) {
|
||||
<SearchResult
|
||||
key={s.id + s.game}
|
||||
link={`/games/${s.game}/${
|
||||
GetGameConfig(s.game).validPlaytypes.includes(
|
||||
playtype as Playtype
|
||||
)
|
||||
GetGameConfig(s.game).playtypes.includes(playtype as Playtype)
|
||||
? playtype
|
||||
: GetGameConfig(s.game).defaultPlaytype
|
||||
}/songs/${s.id}`}
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function AddNewQuestModal({
|
||||
{TachiConfig.games.flatMap((game) => {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
return gameConfig.validPlaytypes.map((playtype) => (
|
||||
return gameConfig.playtypes.map((playtype) => (
|
||||
<option
|
||||
key={`${game}:${playtype}`}
|
||||
value={`${game}:${playtype}`}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function IsSupportedPlaytype<G extends Game = Game>(
|
||||
): str is Playtypes[G] {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
return gameConfig.validPlaytypes.includes(str as unknown as Playtypes[G]);
|
||||
return gameConfig.playtypes.includes(str as unknown as Playtypes[G]);
|
||||
}
|
||||
|
||||
export function IsScore<I extends IDStrings>(
|
||||
|
||||
@@ -5,7 +5,7 @@ export function GetSortedGPTs() {
|
||||
const arr = [];
|
||||
for (const game of TachiConfig.games) {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
for (const playtype of gameConfig.validPlaytypes) {
|
||||
for (const playtype of gameConfig.playtypes) {
|
||||
arr.push({ game, playtype });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import { GetGameConfig, GetGamePTConfig } from "./old-config";
|
||||
import { allSupportedGames } from "../constants/import-types";
|
||||
import { GetGameConfig, GetGamePTConfig, allSupportedGames } from "./config";
|
||||
import t from "tap";
|
||||
|
||||
t.test("#GetGameConfig", (t) => {
|
||||
t.plan(allSupportedGames.length);
|
||||
|
||||
for (const game of allSupportedGames) {
|
||||
t.equal(GetGameConfig(game).internalName, game);
|
||||
// i don't feel *that* strongly about this restriction, but game IDs *definitely*
|
||||
// can't have things like `:` in them.
|
||||
t.match(game, /^[a-z]+$/u, "Game IDs must be a-z only.");
|
||||
|
||||
const conf = GetGameConfig(game);
|
||||
|
||||
t.not(conf, undefined, `'${game}' should have a config defined.`);
|
||||
t.ok(
|
||||
conf.playtypes.includes(conf.defaultPlaytype),
|
||||
"Default playtype should be in the array of playtypes."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,20 +23,34 @@ t.test("#GetGamePTConfig", (t) => {
|
||||
for (const game of allSupportedGames) {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
for (const playtype of gameConfig.validPlaytypes) {
|
||||
for (const playtype of gameConfig.playtypes) {
|
||||
const conf = GetGamePTConfig(game, playtype);
|
||||
|
||||
t.equal(
|
||||
conf.idString,
|
||||
`${game}:${playtype}`,
|
||||
`Should return the right GamePTConfig (${game} ${playtype})`
|
||||
t.not(conf, undefined, `'${game}:${playtype}' should have a config defined.`);
|
||||
|
||||
t.ok(
|
||||
conf.scoreRatingAlgs[conf.defaultScoreRatingAlg],
|
||||
"The default score rating alg should have an implementation."
|
||||
);
|
||||
|
||||
t.equal(
|
||||
conf.gradeBoundaries.length,
|
||||
conf.grades.length,
|
||||
`Should have the same amount of grades as grade boundaries. (${game} ${playtype})`
|
||||
t.ok(
|
||||
conf.sessionRatingAlgs[conf.defaultSessionRatingAlg],
|
||||
"The default session rating alg should have an implementation."
|
||||
);
|
||||
|
||||
t.ok(
|
||||
conf.profileRatingAlgs[conf.defaultProfileRatingAlg],
|
||||
"The default profile rating alg should have an implementation."
|
||||
);
|
||||
|
||||
if (conf.difficultyConfig.type === "FIXED") {
|
||||
t.ok(
|
||||
conf.difficultyConfig.difficultyOrder.includes(
|
||||
conf.difficultyConfig.defaultDifficulty
|
||||
),
|
||||
"The default difficulty should be part of difficultyOrder."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const BMS_CONF = {
|
||||
defaultPlaytype: "7K",
|
||||
name: "BMS",
|
||||
validPlaytypes: ["7K", "14K"],
|
||||
playtypes: ["7K", "14K"],
|
||||
songData: z.strictObject({
|
||||
genre: z.nullable(z.string()),
|
||||
subtitle: z.nullable(z.string()),
|
||||
@@ -178,7 +178,7 @@ export const BMS_7K_CONF = {
|
||||
egr: { type: "INTEGER" },
|
||||
egd: { type: "INTEGER" },
|
||||
ebd: { type: "INTEGER" },
|
||||
epr: { type: "INTEGER" }, //
|
||||
epr: { type: "INTEGER" },
|
||||
lpg: { type: "INTEGER" },
|
||||
lgr: { type: "INTEGER" },
|
||||
lgd: { type: "INTEGER" },
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const CHUNITHM_CONF = {
|
||||
defaultPlaytype: "Single",
|
||||
name: "CHUNITHM",
|
||||
validPlaytypes: ["Single"],
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
genre: z.string(),
|
||||
displayVersion: z.string(),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const GITADORA_CONF = {
|
||||
defaultPlaytype: "Dora",
|
||||
name: "GITADORA",
|
||||
validPlaytypes: ["Gita", "Dora"],
|
||||
playtypes: ["Gita", "Dora"],
|
||||
songData: z.strictObject({
|
||||
isHot: z.boolean(),
|
||||
displayVersion: z.string(),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const IIDX_CONF = {
|
||||
defaultPlaytype: "SP",
|
||||
name: "beatmania IIDX",
|
||||
validPlaytypes: ["SP", "DP"],
|
||||
playtypes: ["SP", "DP"],
|
||||
songData: z.strictObject({
|
||||
genre: z.string(),
|
||||
displayVersion: z.nullable(z.string()),
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const ITG_CONF = {
|
||||
defaultPlaytype: "Stamina",
|
||||
name: "ITG",
|
||||
validPlaytypes: ["Stamina"],
|
||||
playtypes: ["Stamina"],
|
||||
songData: z.strictObject({
|
||||
subtitle: z.string(),
|
||||
originalPack: z.string(),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const JUBEAT_CONF = {
|
||||
defaultPlaytype: "Single",
|
||||
name: "jubeat",
|
||||
validPlaytypes: ["Single"],
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
displayVersion: z.string(),
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const MAIMAI_DX_CONF = {
|
||||
defaultPlaytype: "Single",
|
||||
name: "maimai DX",
|
||||
validPlaytypes: ["Single"],
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
displayVersion: z.string(),
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const MUSECA_CONF = {
|
||||
defaultPlaytype: "Single",
|
||||
name: "MÚSECA",
|
||||
validPlaytypes: ["Single"],
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
titleJP: z.string(),
|
||||
artistJP: z.string(),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const PMS_CONF = {
|
||||
defaultPlaytype: "Controller",
|
||||
name: "PMS",
|
||||
validPlaytypes: ["Controller", "Keyboard"],
|
||||
playtypes: ["Controller", "Keyboard"],
|
||||
songData: z.strictObject({
|
||||
genre: z.nullable(z.string()),
|
||||
subtitle: z.nullable(z.string()),
|
||||
@@ -78,7 +78,19 @@ export const PMS_CONTROLLER_CONF = {
|
||||
|
||||
additionalMetrics: {
|
||||
...FAST_SLOW_MAXCOMBO,
|
||||
// TODO
|
||||
bp: { type: "INTEGER" },
|
||||
gauge: { type: "DECIMAL" },
|
||||
gaugeHistory: { type: "GRAPH" },
|
||||
epg: { type: "INTEGER" },
|
||||
egr: { type: "INTEGER" },
|
||||
egd: { type: "INTEGER" },
|
||||
ebd: { type: "INTEGER" },
|
||||
epr: { type: "INTEGER" },
|
||||
lpg: { type: "INTEGER" },
|
||||
lgr: { type: "INTEGER" },
|
||||
lgd: { type: "INTEGER" },
|
||||
lbd: { type: "INTEGER" },
|
||||
lpr: { type: "INTEGER" },
|
||||
},
|
||||
|
||||
scoreRatingAlgs: {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const POPN_CONF = {
|
||||
defaultPlaytype: "9B",
|
||||
name: "pop'n music",
|
||||
validPlaytypes: ["9B"],
|
||||
playtypes: ["9B"],
|
||||
songData: z.strictObject({
|
||||
displayVersion: z.nullable(z.string()),
|
||||
genre: z.string(),
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const SDVX_CONF = {
|
||||
defaultPlaytype: "Single",
|
||||
name: "SOUND VOLTEX",
|
||||
validPlaytypes: ["Single"],
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
displayVersion: z.string(),
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const USC_CONF = {
|
||||
defaultPlaytype: "Controller",
|
||||
name: "USC",
|
||||
validPlaytypes: ["Controller", "Keyboard"],
|
||||
playtypes: ["Controller", "Keyboard"],
|
||||
songData: z.strictObject({}),
|
||||
} as const satisfies INTERNAL_GAME_CONFIG;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { INTERNAL_GAME_CONFIG, GamePTConfig } from "../../types/internals";
|
||||
export const WACCA_CONF = {
|
||||
defaultPlaytype: "Single",
|
||||
name: "WACCA",
|
||||
validPlaytypes: ["Single"],
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
titleJP: z.string(),
|
||||
artistJP: z.string(),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ import t from "tap";
|
||||
t.test("BMS Tables should be unique.", (t) => {
|
||||
const allKeys = BMS_TABLES.map((e) => `${e.playtype}-${e.prefix}`);
|
||||
|
||||
t.strictSame(allKeys, [...new Set(allKeys)], "There should be no duplicate tables prefixes.");
|
||||
t.strictSame(allKeys, [...new Set(allKeys)], "There should be no duplicate table prefixes.");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ import t from "tap";
|
||||
t.test("Property Checks", (t) => {
|
||||
t.type(tachiCommon.GetGameConfig, "function");
|
||||
t.type(tachiCommon.GetGamePTConfig, "function");
|
||||
t.type(tachiCommon.gameClasses, "object");
|
||||
t.type(tachiCommon.COLOUR_SET, "object");
|
||||
|
||||
t.end();
|
||||
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
export * from "./types";
|
||||
|
||||
export * from "./config/old-config";
|
||||
export * as gameClasses from "./config/game-classes";
|
||||
export * as ESDCore from "./lib/esd";
|
||||
export * from "./config/config";
|
||||
export * as StaticConfig from "./constants/import-types";
|
||||
export * from "./utils/util";
|
||||
export * from "./webhooks";
|
||||
|
||||
+10
-10
@@ -2,15 +2,15 @@
|
||||
// their name in the database to the object they schemaify.
|
||||
// The schemas themselves are wrapped in functions that throw on error.
|
||||
|
||||
import { GetGameConfig, GetGamePTConfig } from "../config/old-config";
|
||||
import { allIDStrings, allImportTypes, allSupportedGames } from "../constants/import-types";
|
||||
import { GetGameConfig, allSupportedGames, GetGamePTConfig, allGPTStrings } from "../config/config";
|
||||
import { allImportTypes } from "../constants/import-types";
|
||||
import { ALL_PERMISSIONS } from "../constants/permissions";
|
||||
import { UserAuthLevels } from "../types";
|
||||
import deepmerge from "deepmerge";
|
||||
import { p } from "prudence";
|
||||
import type { GamePTConfig } from "../config/old-config";
|
||||
import type { AllClassSets } from "../config/game-classes";
|
||||
import type { Game, IDStrings, NotificationBody, Playtype, Playtypes } from "../types";
|
||||
import type { Game, Playtypes, Playtype } from "../types/game-config";
|
||||
import type { GamePTConfig } from "../types/internals";
|
||||
import type { NotificationBody } from "../types/notifications";
|
||||
import type {
|
||||
PrudenceSchema,
|
||||
ValidationFunctionParentOptionsKeychain,
|
||||
@@ -219,7 +219,7 @@ const GetScoreMeta = (game: Game, playtype: Playtypes[Game]): PrudenceSchema =>
|
||||
};
|
||||
|
||||
function IsValidPlaytype(game: Game, str: string): str is Playtypes[Game] {
|
||||
return GetGameConfig(game).validPlaytypes.includes(str as Playtypes[Game]);
|
||||
return GetGameConfig(game).playtypes.includes(str as Playtypes[Game]);
|
||||
}
|
||||
|
||||
function IsValidGame(str: string): str is Game {
|
||||
@@ -238,7 +238,7 @@ const getPlaytype = (game: Game, self: unknown): Playtypes[Game] => {
|
||||
const playtype = s.playtype as string;
|
||||
|
||||
if (!IsValidPlaytype(game, playtype)) {
|
||||
throw new Error(`Expected any of ${gameConfig.validPlaytypes.join(", ")}`);
|
||||
throw new Error(`Expected any of ${gameConfig.playtypes.join(", ")}`);
|
||||
}
|
||||
|
||||
return playtype;
|
||||
@@ -811,13 +811,13 @@ const PRE_SCHEMAS = {
|
||||
userID: p.isPositiveNonZeroInteger,
|
||||
timeStarted: p.isPositive,
|
||||
timeFinished: p.isPositive,
|
||||
idStrings: [p.isIn(allIDStrings)],
|
||||
idStrings: [p.isIn(allGPTStrings)],
|
||||
importID: "string",
|
||||
scoreIDs: ["string"],
|
||||
game: p.isIn(games),
|
||||
|
||||
// @ts-expect-error We've asserted this is definitely a game.
|
||||
playtypes: [(self) => p.isIn(GetGameConfig(self.game).validPlaytypes)(self)],
|
||||
playtypes: [(self) => p.isIn(GetGameConfig(self.game).playtypes)(self)],
|
||||
errors: [
|
||||
{
|
||||
type: "string",
|
||||
@@ -1024,7 +1024,7 @@ const PRE_SCHEMAS = {
|
||||
const { game } = extractGPTIDString(self);
|
||||
|
||||
return prSchemaFnWrap({
|
||||
idString: p.isIn(allIDStrings),
|
||||
idString: p.isIn(allGPTStrings),
|
||||
chartDoc: PR_CHART_DOCUMENT(game),
|
||||
songDoc: PR_SONG_DOCUMENT(game),
|
||||
userIDs: [p.isPositiveNonZeroInteger],
|
||||
|
||||
+12
-29
@@ -1,7 +1,8 @@
|
||||
import { allSupportedGames } from "../config/config";
|
||||
import type {
|
||||
Game,
|
||||
ChartDocument,
|
||||
GameToIDStrings,
|
||||
GameToGPTStrings,
|
||||
SongDocument,
|
||||
BMSCourseDocument,
|
||||
FolderDocument,
|
||||
@@ -15,7 +16,7 @@ import type {
|
||||
// note that TS won't let you do this multiple times within an object
|
||||
// so, we have to join them ourselves. Ah well, not that bad.
|
||||
type ChartDBSeeds = {
|
||||
[G in Game as `charts-${G}.json`]: Array<ChartDocument<GameToIDStrings[G]>>;
|
||||
[G in Game as `charts-${G}.json`]: Array<ChartDocument<GameToGPTStrings[G]>>;
|
||||
};
|
||||
|
||||
type SongDBSeeds = {
|
||||
@@ -34,41 +35,23 @@ interface OtherDBSeeds {
|
||||
export type AllDatabaseSeeds = ChartDBSeeds & OtherDBSeeds & SongDBSeeds;
|
||||
|
||||
// Nifty trick to enforce that we always specify all database seeds :)
|
||||
const CURRENT_DATABASE_SEEDS: Record<keyof AllDatabaseSeeds, true> = {
|
||||
const CURRENT_DATABASE_SEEDS: Record<keyof OtherDBSeeds, true> = {
|
||||
"bms-course-lookup.json": true,
|
||||
"charts-bms.json": true,
|
||||
"charts-chunithm.json": true,
|
||||
"charts-gitadora.json": true,
|
||||
"charts-iidx.json": true,
|
||||
"charts-itg.json": true,
|
||||
"charts-jubeat.json": true,
|
||||
"charts-maimaidx.json": true,
|
||||
"charts-museca.json": true,
|
||||
"charts-pms.json": true,
|
||||
"charts-popn.json": true,
|
||||
"charts-sdvx.json": true,
|
||||
"charts-usc.json": true,
|
||||
"charts-wacca.json": true,
|
||||
"folders.json": true,
|
||||
"goals.json": true,
|
||||
"questlines.json": true,
|
||||
"quests.json": true,
|
||||
"songs-bms.json": true,
|
||||
"songs-chunithm.json": true,
|
||||
"songs-gitadora.json": true,
|
||||
"songs-iidx.json": true,
|
||||
"songs-itg.json": true,
|
||||
"songs-jubeat.json": true,
|
||||
"songs-maimaidx.json": true,
|
||||
"songs-museca.json": true,
|
||||
"songs-pms.json": true,
|
||||
"songs-popn.json": true,
|
||||
"songs-sdvx.json": true,
|
||||
"songs-usc.json": true,
|
||||
"songs-wacca.json": true,
|
||||
"tables.json": true,
|
||||
};
|
||||
|
||||
for (const game of allSupportedGames) {
|
||||
// @ts-expect-error define all games' seeds on the object.
|
||||
CURRENT_DATABASE_SEEDS[`songs-${game}`] = true;
|
||||
|
||||
// @ts-expect-error define all games' seeds on the object.
|
||||
CURRENT_DATABASE_SEEDS[`charts-${game}`] = true;
|
||||
}
|
||||
|
||||
export const DatabaseSeedNames = Object.keys(CURRENT_DATABASE_SEEDS) as Array<
|
||||
keyof AllDatabaseSeeds
|
||||
>;
|
||||
|
||||
+6
-6
@@ -12,9 +12,9 @@ export enum UserAuthLevels {
|
||||
ADMIN = 3,
|
||||
}
|
||||
|
||||
// export * from "./types/api";
|
||||
// export * from "./types/batch-manual";
|
||||
// export * from "./types/documents";
|
||||
// export * from "./types/game-implementations";
|
||||
// export * from "./types/import-types";
|
||||
// export * from "./types/notifications";
|
||||
export * from "./types/api";
|
||||
export * from "./types/batch-manual";
|
||||
export * from "./types/documents";
|
||||
export * from "./types/game-config";
|
||||
export * from "./types/import-types";
|
||||
export * from "./types/notifications";
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
import type { UserAuthLevels, integer } from "../types";
|
||||
import type {
|
||||
ChartDocumentData,
|
||||
ChartSets,
|
||||
Classes,
|
||||
Difficulties,
|
||||
ExtractedAdditionalMetrics,
|
||||
ExtractedScoreMetrics,
|
||||
GPTString,
|
||||
GPTStringToGame,
|
||||
GPTStringToPlaytype,
|
||||
Game,
|
||||
Judgements,
|
||||
Playtype,
|
||||
Playtypes,
|
||||
Preferences,
|
||||
ProfileRatingAlgorithms,
|
||||
ScoreMeta,
|
||||
ScoreRatingAlgorithms,
|
||||
SessionRatingAlgorithms,
|
||||
SongDocumentData,
|
||||
UserAuthLevels,
|
||||
integer,
|
||||
} from "../types";
|
||||
import type { APIPermissions } from "./api";
|
||||
import type { ImportTypes } from "./import-types";
|
||||
import type { BaseNotification, NotificationBody } from "./notifications";
|
||||
@@ -112,7 +134,7 @@ interface SessionScoreNewInfo {
|
||||
|
||||
export type SessionScoreInfo = SessionScoreNewInfo | SessionScorePBInfo;
|
||||
|
||||
export interface SessionDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface SessionDocument<GPT extends GPTString = GPTString> {
|
||||
userID: integer;
|
||||
sessionID: string;
|
||||
scoreIDs: Array<string>;
|
||||
@@ -140,8 +162,8 @@ export interface ImportDocument {
|
||||
timeStarted: number;
|
||||
timeFinished: number;
|
||||
|
||||
// Contains an array of GPTStrings, which dictates what (game:playtype)s were involved in this import.
|
||||
gptStrings: Array<GPTStrings>;
|
||||
// Contains an array of GPTString, which dictates what (game:playtype)s were involved in this import.
|
||||
GPTString: Array<GPTString>;
|
||||
importID: string;
|
||||
scoreIDs: Array<string>;
|
||||
game: Game;
|
||||
@@ -290,12 +312,12 @@ export interface UserDocument {
|
||||
authLevel: UserAuthLevels;
|
||||
}
|
||||
|
||||
export interface UserGameStats<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface UserGameStats<GPT extends GPTString = GPTString> {
|
||||
userID: integer;
|
||||
game: GPTStringToGame[GPT];
|
||||
playtype: GPTStringToPlaytype[GPT];
|
||||
ratings: Partial<Record<ProfileRatingAlgorithms[GPT], number | null>>;
|
||||
classes: Partial<GameClasses<GPT>>;
|
||||
classes: Partial<Classes[GPT]>;
|
||||
}
|
||||
|
||||
export interface ChartTierlistInfo {
|
||||
@@ -304,7 +326,7 @@ export interface ChartTierlistInfo {
|
||||
individualDifference?: boolean;
|
||||
}
|
||||
|
||||
export interface ChartDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface ChartDocument<GPT extends GPTString = GPTString> {
|
||||
chartID: string;
|
||||
songID: integer;
|
||||
level: string;
|
||||
@@ -312,8 +334,8 @@ export interface ChartDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
isPrimary: boolean;
|
||||
difficulty: Difficulties[GPT];
|
||||
playtype: GPTStringToPlaytype[GPT];
|
||||
data: ChartData[GPT];
|
||||
chartSets: Array<SupportedVersions[GPT]>;
|
||||
data: ChartDocumentData[GPT];
|
||||
chartSets: Array<ChartSets[GPT]>;
|
||||
}
|
||||
|
||||
export interface SongDocument<G extends Game = Game> {
|
||||
@@ -322,7 +344,7 @@ export interface SongDocument<G extends Game = Game> {
|
||||
artist: string;
|
||||
searchTerms: Array<string>;
|
||||
altTitles: Array<string>;
|
||||
data: SongData[G];
|
||||
data: SongDocumentData[G];
|
||||
}
|
||||
|
||||
export interface TableDocument {
|
||||
@@ -391,22 +413,17 @@ export type QuestSubscriptionDocument = {
|
||||
}
|
||||
);
|
||||
|
||||
export interface ScoreDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export type ScoreData<GPT extends GPTString = GPTString> = ExtractedScoreMetrics[GPT] & {
|
||||
judgements: Partial<Record<Judgements[GPT], integer | null>>;
|
||||
additionalMetrics: Partial<ExtractedAdditionalMetrics[GPT]>;
|
||||
};
|
||||
|
||||
export interface ScoreDocument<GPT extends GPTString = GPTString> {
|
||||
service: string;
|
||||
game: GPTStringToGame[GPT];
|
||||
playtype: GPTStringToPlaytype[GPT];
|
||||
userID: integer;
|
||||
scoreData: {
|
||||
score: number;
|
||||
lamp: Lamps[GPT];
|
||||
percent: number;
|
||||
grade: Grades[GPT];
|
||||
lampIndex: integer;
|
||||
gradeIndex: integer;
|
||||
esd: number | null;
|
||||
judgements: Partial<Record<Judgements[GPT], integer | null>>;
|
||||
additionalMetrics: Partial<AdditionalMetrics[GPT]>;
|
||||
};
|
||||
scoreData: ScoreData<GPT>;
|
||||
scoreMeta: Partial<ScoreMeta[GPT]>;
|
||||
calculatedData: Partial<Record<ScoreRatingAlgorithms[GPT], number | null>>;
|
||||
timeAchieved: integer | null;
|
||||
@@ -425,7 +442,7 @@ export interface PBScoreComposedReference {
|
||||
scoreID: string;
|
||||
}
|
||||
|
||||
export interface PBScoreDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface PBScoreDocument<GPT extends GPTString = GPTString> {
|
||||
composedFrom: {
|
||||
scorePB: string;
|
||||
lampPB: string;
|
||||
@@ -449,17 +466,7 @@ export interface PBScoreDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
highlight: boolean;
|
||||
isPrimary: boolean;
|
||||
timeAchieved: number | null;
|
||||
scoreData: {
|
||||
score: number;
|
||||
lamp: Lamps[GPT];
|
||||
percent: number;
|
||||
grade: Grades[GPT];
|
||||
lampIndex: integer;
|
||||
gradeIndex: integer;
|
||||
esd: number | null;
|
||||
judgements: Partial<Record<Judgements[GPT], integer | null>>;
|
||||
additionalMetrics: Partial<AdditionalMetrics[GPT]>;
|
||||
};
|
||||
scoreData: ScoreData<GPT>;
|
||||
calculatedData: Partial<Record<ScoreRatingAlgorithms[GPT], number | null>>;
|
||||
}
|
||||
|
||||
@@ -479,7 +486,7 @@ export interface ImportProcessInfoInvalidDatapoint {
|
||||
content: Record<string, never>;
|
||||
}
|
||||
|
||||
export interface ImportProcessInfoScoreImported<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface ImportProcessInfoScoreImported<GPT extends GPTString = GPTString> {
|
||||
success: true;
|
||||
type: "ScoreImported";
|
||||
message: string;
|
||||
@@ -500,13 +507,14 @@ export interface ImportProcessInfoSongOrChartNotFound {
|
||||
type: "SongOrChartNotFound";
|
||||
message: string;
|
||||
content: {
|
||||
// these are too complex to type. Not bothering.
|
||||
data: unknown;
|
||||
context: unknown; // @TODO Type these properly.
|
||||
context: unknown;
|
||||
orphanID: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type ImportProcessingInfo<GPT extends GPTStrings = GPTStrings> =
|
||||
export type ImportProcessingInfo<GPT extends GPTString = GPTString> =
|
||||
| ImportProcessInfoInternalError
|
||||
| ImportProcessInfoInvalidDatapoint
|
||||
| ImportProcessInfoOrphanExists
|
||||
@@ -582,7 +590,7 @@ export interface ShowcaseStatChart {
|
||||
property: "grade" | "lamp" | "percent" | "playcount" | "score";
|
||||
}
|
||||
|
||||
export interface UGPTSettingsDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface UGPTSettingsDocument<GPT extends GPTString = GPTString> {
|
||||
userID: integer;
|
||||
game: GPTStringToGame[GPT];
|
||||
playtype: GPTStringToPlaytype[GPT];
|
||||
@@ -594,12 +602,12 @@ export interface UGPTSettingsDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
scoreBucket: "grade" | "lamp" | null;
|
||||
defaultTable: string | null;
|
||||
preferredRanking: "global" | "rival" | null;
|
||||
gameSpecific: UGPTSpecificPreferences[GPT];
|
||||
gameSpecific: Preferences[GPT];
|
||||
};
|
||||
rivals: Array<integer>;
|
||||
}
|
||||
|
||||
export interface UserGameStatsSnapshotDocument<GPT extends GPTStrings = GPTStrings>
|
||||
export interface UserGameStatsSnapshotDocument<GPT extends GPTString = GPTString>
|
||||
extends UserGameStats<GPT> {
|
||||
rankings: Record<ProfileRatingAlgorithms[GPT], { ranking: integer | null; outOf: integer }>;
|
||||
playcount: integer;
|
||||
@@ -641,7 +649,7 @@ export interface KsHookSettingsDocument {
|
||||
forceStaticImport: boolean;
|
||||
}
|
||||
|
||||
export interface OrphanChartDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface OrphanChartDocument<GPT extends GPTString = GPTString> {
|
||||
gptString: GPT;
|
||||
chartDoc: ChartDocument<GPT>;
|
||||
songDoc: SongDocument<GPTStringToGame[GPT]>;
|
||||
@@ -650,16 +658,16 @@ export interface OrphanChartDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
|
||||
export interface ClassDelta {
|
||||
game: Game;
|
||||
set: AllClassSets;
|
||||
set: Classes[GPTString];
|
||||
playtype: Playtype;
|
||||
old: integer | null;
|
||||
new: integer;
|
||||
}
|
||||
|
||||
export interface ClassAchievementDocument<GPT extends GPTStrings = GPTStrings> {
|
||||
export interface ClassAchievementDocument<GPT extends GPTString = GPTString> {
|
||||
game: GPTStringToGame[GPT];
|
||||
playtype: GPTStringToPlaytype[GPT];
|
||||
classSet: AllClassSets;
|
||||
classSet: Classes[GPT];
|
||||
classOldValue: integer | null;
|
||||
classValue: integer;
|
||||
timeAchieved: number;
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
} from "./game-config-utils";
|
||||
import type { ExtractEnumMetricNames, ExtractMetrics } from "./metrics";
|
||||
import type { ExtractArrayElementType } from "./utils";
|
||||
import type { z } from "zod";
|
||||
import type { AnyZodObject, z } from "zod";
|
||||
|
||||
/**
|
||||
* All the games Tachi supports.
|
||||
@@ -30,7 +30,7 @@ export type Game = keyof typeof GAME_CONFIGS;
|
||||
* is the go-to.
|
||||
*/
|
||||
export type Playtypes = {
|
||||
[G in Game]: typeof GAME_CONFIGS[G]["validPlaytypes"][number];
|
||||
[G in Game]: typeof GAME_CONFIGS[G]["playtypes"][number];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -49,13 +49,13 @@ export type SongDocumentData = {
|
||||
export interface GameConfig<G extends Game = Game> {
|
||||
readonly name: string;
|
||||
readonly defaultPlaytype: Playtypes[G];
|
||||
readonly validPlaytypes: ReadonlyArray<Playtypes[G]>;
|
||||
readonly playtypes: ReadonlyArray<Playtypes[G]>;
|
||||
|
||||
/**
|
||||
* Song documents get their own game-specific record that they use for whatever
|
||||
* they want. IIDX song documents store genres, etc.
|
||||
*/
|
||||
readonly songData: SongDocumentData[G];
|
||||
readonly songData: AnyZodObject;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ export type GPTString = keyof {
|
||||
[G in Game as `${G}:${Playtypes[G]}`]: never;
|
||||
};
|
||||
|
||||
export type GameToGPTStrings = {
|
||||
export type GPTStrings = {
|
||||
[G in Game]: `${G}:${Playtypes[G]}`;
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ export type ExtractedClasses = {
|
||||
};
|
||||
|
||||
export type Classes = {
|
||||
[G in GPTString]: typeof GAME_PT_CONFIGS[G]["classes"];
|
||||
[G in GPTString]: keyof typeof GAME_PT_CONFIGS[G]["classes"];
|
||||
};
|
||||
|
||||
export type ChartDocumentData = {
|
||||
@@ -357,13 +357,16 @@ export interface SpecificGamePTConfig<GPT extends GPTString> {
|
||||
* Chart documents get their own GPT-specific record that they use for whatever
|
||||
* they want. IIDX documents store BPI information like kaiden averages, BMS
|
||||
* charts store sha256/md5 hashes, etc.
|
||||
*
|
||||
* This is a zod schema that can be used to validate that input.
|
||||
*/
|
||||
chartData: ChartDocumentData[GPT];
|
||||
chartData: AnyZodObject;
|
||||
|
||||
/**
|
||||
* What game-specific user-settings exist for this GPT?
|
||||
* This is a zod schema that can be used to validate provided GPT-specific
|
||||
* preferences.
|
||||
*/
|
||||
preferences: Preferences[GPT];
|
||||
preferences: AnyZodObject;
|
||||
|
||||
/**
|
||||
* What game-specific metadata should be stored on scores for this GPT?
|
||||
@@ -371,7 +374,7 @@ export interface SpecificGamePTConfig<GPT extends GPTString> {
|
||||
* These are for things like what options were used (RANDOM, MIRROR etc.)
|
||||
* and don't exist on PBs.
|
||||
*/
|
||||
scoreMeta: ScoreMeta[GPT];
|
||||
scoreMeta: AnyZodObject;
|
||||
|
||||
/**
|
||||
* What "matchTypes" should this game support for batch-manual imports? This
|
||||
|
||||
@@ -56,7 +56,7 @@ export type GamePTConfig = Readonly<{
|
||||
*/
|
||||
export type INTERNAL_GAME_CONFIG<PT extends string = string> = Readonly<{
|
||||
name: string;
|
||||
validPlaytypes: ReadonlyArray<PT>;
|
||||
playtypes: ReadonlyArray<PT>;
|
||||
defaultPlaytype: PT;
|
||||
songData: AnyZodObject;
|
||||
}>;
|
||||
|
||||
+57
-25
@@ -1,6 +1,16 @@
|
||||
import { GetGameConfig, GetGamePTConfig } from "../config/old-config";
|
||||
import type { ChartDocument, Game, IDStrings, Playtypes, SongDocument } from "..";
|
||||
import type { Grades, integer, PBScoreDocument } from "../types";
|
||||
import { GetGameConfig, GetGamePTConfig, GetSpecificGPTConfig } from "../config/config";
|
||||
import type {
|
||||
integer,
|
||||
PBScoreDocument,
|
||||
ChartDocument,
|
||||
Game,
|
||||
Playtypes,
|
||||
SongDocument,
|
||||
GameToGPTStrings,
|
||||
SpecificGamePTConfig,
|
||||
GPTString,
|
||||
GPTStrings,
|
||||
} from "../types";
|
||||
import type { PrudenceError } from "prudence";
|
||||
|
||||
export function FormatInt(v: number): string {
|
||||
@@ -23,9 +33,21 @@ export function FormatDifficulty(chart: ChartDocument, game: Game): string {
|
||||
}
|
||||
|
||||
if (game === "gitadora") {
|
||||
const gptConfig = GetGamePTConfig(game, chart.playtype);
|
||||
const ch = chart as ChartDocument<GPTStrings["gitadora"]>;
|
||||
|
||||
const shortDiff = gptConfig.shortDifficulties[chart.difficulty] ?? chart.difficulty;
|
||||
const gptConfig = GetSpecificGPTConfig<GPTStrings["gitadora"]>(
|
||||
game,
|
||||
chart.playtype as Playtypes["gitadora"]
|
||||
);
|
||||
|
||||
// @ts-expect-error maybe this new config format was a mistake.
|
||||
// it's complaining that since the dora config doesn't have shorthand for
|
||||
// "BASS BASIC", this assignment may fail.
|
||||
// it's technically correct, but in the worst way, since this isn't
|
||||
// actually possible.
|
||||
// todo: come up with something better.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const shortDiff = gptConfig.difficultyConfig.difficultyShorthand[ch.difficulty];
|
||||
|
||||
// gitadora should always use short diffs. they just look better.
|
||||
return `${shortDiff} ${chart.level}`;
|
||||
@@ -33,7 +55,7 @@ export function FormatDifficulty(chart: ChartDocument, game: Game): string {
|
||||
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
if (gameConfig.validPlaytypes.length > 1) {
|
||||
if (gameConfig.playtypes.length > 1) {
|
||||
return `${chart.playtype} ${chart.difficulty} ${chart.level}`;
|
||||
}
|
||||
|
||||
@@ -54,9 +76,15 @@ export function FormatDifficultyShort(chart: ChartDocument, game: Game): string
|
||||
return `S${itgChart.data.difficultyTag} ${chart.level}`;
|
||||
}
|
||||
|
||||
const shortDiff = gptConfig.shortDifficulties[chart.difficulty] ?? chart.difficulty;
|
||||
if (gptConfig.difficultyConfig.type === "DYNAMIC") {
|
||||
// TODO cap string length
|
||||
return `${chart.difficulty} ${chart.level}`;
|
||||
}
|
||||
|
||||
if (gameConfig.validPlaytypes.length === 1 || game === "gitadora") {
|
||||
const shortDiff =
|
||||
gptConfig.difficultyConfig.difficultyShorthand[chart.difficulty] ?? chart.difficulty;
|
||||
|
||||
if (gameConfig.playtypes.length === 1 || game === "gitadora") {
|
||||
return `${shortDiff} ${chart.level}`;
|
||||
}
|
||||
|
||||
@@ -70,7 +98,7 @@ export function FormatDifficultyShort(chart: ChartDocument, game: Game): string
|
||||
export function FormatGame(game: Game, playtype: Playtypes[Game]): string {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
if (gameConfig.validPlaytypes.length === 1) {
|
||||
if (gameConfig.playtypes.length === 1) {
|
||||
return gameConfig.name;
|
||||
}
|
||||
|
||||
@@ -84,7 +112,7 @@ export function FormatChart(
|
||||
short = false
|
||||
): string {
|
||||
if (game === "bms") {
|
||||
const tables = (chart as ChartDocument<"bms:7K" | "bms:14K">).data.tableFolders;
|
||||
const tables = (chart as ChartDocument<GPTStrings["bms"]>).data.tableFolders;
|
||||
|
||||
const bmsSong = song as SongDocument<"bms">;
|
||||
|
||||
@@ -104,7 +132,7 @@ export function FormatChart(
|
||||
|
||||
return `${realTitle} (${tables.map((e) => `${e.table}${e.level}`).join(", ")})`;
|
||||
} else if (game === "usc") {
|
||||
const uscChart = chart as ChartDocument<"usc:Controller" | "usc:Keyboard">;
|
||||
const uscChart = chart as ChartDocument<GPTStrings["usc"]>;
|
||||
|
||||
// If this chart isn't an official, render it differently
|
||||
if (!uscChart.data.isOfficial) {
|
||||
@@ -136,15 +164,19 @@ export function FormatChart(
|
||||
|
||||
let playtypeStr = `${chart.playtype}`;
|
||||
|
||||
if (gameConfig.validPlaytypes.length === 1) {
|
||||
if (gameConfig.playtypes.length === 1) {
|
||||
playtypeStr = "";
|
||||
}
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, chart.playtype);
|
||||
|
||||
const diff = short
|
||||
? gptConfig.shortDifficulties[chart.difficulty] ?? chart.difficulty
|
||||
: chart.difficulty;
|
||||
let diff: string;
|
||||
|
||||
if (gptConfig.difficultyConfig.type === "DYNAMIC") {
|
||||
diff = chart.difficulty;
|
||||
} else {
|
||||
diff = gptConfig.difficultyConfig.difficultyShorthand[chart.difficulty] ?? chart.difficulty;
|
||||
}
|
||||
|
||||
// iidx formats things like SPA instead of SP A.
|
||||
// this is a hack, this should be part of the gptConfig, tbh.
|
||||
@@ -156,16 +188,16 @@ export function FormatChart(
|
||||
space = " ";
|
||||
}
|
||||
|
||||
// return the most recent version this chart appeared in if it
|
||||
// return the most recent chartSet this chart appeared in if it
|
||||
// is not primary.
|
||||
if (!chart.isPrimary) {
|
||||
return `${song.title} (${playtypeStr}${space}${diff} ${chart.level} ${chart.versions[0]})`;
|
||||
return `${song.title} (${playtypeStr}${space}${diff} ${chart.level} ${chart.chartSets[0]})`;
|
||||
}
|
||||
|
||||
return `${song.title} (${playtypeStr}${space}${diff} ${chart.level})`;
|
||||
}
|
||||
|
||||
export function AbsoluteGradeDelta<I extends IDStrings = IDStrings>(
|
||||
export function AbsoluteGradeDelta<I extends GPTString = GPTString>(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
score: number,
|
||||
@@ -190,12 +222,12 @@ export function AbsoluteGradeDelta<I extends IDStrings = IDStrings>(
|
||||
return score - gradeScore;
|
||||
}
|
||||
|
||||
export function RelativeGradeDelta<I extends IDStrings = IDStrings>(
|
||||
export function RelativeGradeDelta<GPT extends GPTString = GPTString>(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
score: number,
|
||||
percent: number,
|
||||
grade: Grades[I],
|
||||
grade: Grades[GPT],
|
||||
relativeIndex: integer
|
||||
): { grade: string; delta: number } | null {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
@@ -240,7 +272,7 @@ type FormatGradeDeltaReturns =
|
||||
closer: "lower";
|
||||
};
|
||||
|
||||
export function GenericFormatGradeDelta<I extends IDStrings = IDStrings>(
|
||||
export function GenericFormatGradeDelta<I extends GPTString = GPTString>(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
score: number,
|
||||
@@ -278,7 +310,7 @@ export function GenericFormatGradeDelta<I extends IDStrings = IDStrings>(
|
||||
};
|
||||
}
|
||||
|
||||
export function GetCloserGradeDelta<I extends IDStrings = IDStrings>(
|
||||
export function GetCloserGradeDelta<I extends GPTString = GPTString>(
|
||||
game: Game,
|
||||
playtype: Playtypes[Game],
|
||||
score: number,
|
||||
@@ -315,7 +347,7 @@ export function CreateSongMap<G extends Game = Game>(songs: Array<SongDocument<G
|
||||
return songMap;
|
||||
}
|
||||
|
||||
export function CreateChartMap<I extends IDStrings = IDStrings>(charts: Array<ChartDocument<I>>) {
|
||||
export function CreateChartMap<I extends GPTString = GPTString>(charts: Array<ChartDocument<I>>) {
|
||||
const chartMap = new Map<string, ChartDocument<I>>();
|
||||
|
||||
for (const chart of charts) {
|
||||
@@ -395,8 +427,8 @@ export function GradeGoalFormatter(pb: PBScoreDocument) {
|
||||
|
||||
// For games with 'BP', show that next to the clear.
|
||||
export function IIDXBMSLampGoalFormatter(pb: PBScoreDocument<IIDXBMSGPTs>) {
|
||||
if (typeof pb.scoreData.hitMeta.bp === "number") {
|
||||
return `${pb.scoreData.lamp} (BP: ${pb.scoreData.hitMeta.bp})`;
|
||||
if (typeof pb.scoreData.additionalMetrics.bp === "number") {
|
||||
return `${pb.scoreData.lamp.string} (BP: ${pb.scoreData.additionalMetrics.bp})`;
|
||||
}
|
||||
|
||||
return pb.scoreData.lamp;
|
||||
|
||||
@@ -392,7 +392,7 @@ async function UpdateUsersGameStats(
|
||||
// Instead of using the provided playtypes, run the classHandler on all
|
||||
// playtypes. This should only happen if a classHandler is provided, and is
|
||||
// a hack fix for things like #480.
|
||||
const allPlaytypes = GetGameConfig(game).validPlaytypes;
|
||||
const allPlaytypes = GetGameConfig(game).playtypes;
|
||||
|
||||
const playtypes = classHandler ? allPlaytypes : modifiedPlaytypes;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export function ParseBatchManualFromObject(
|
||||
if (!(typeof maybePlaytype === "string" && IsValidPlaytype(game, maybePlaytype))) {
|
||||
throw new ScoreImportFatalError(
|
||||
400,
|
||||
`Invalid playtype '${maybePlaytype}' - expected any of ${gameConfig.validPlaytypes.join(
|
||||
`Invalid playtype '${maybePlaytype}' - expected any of ${gameConfig.playtypes.join(
|
||||
", "
|
||||
)}.`
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ router.get("/", async (req, res) => {
|
||||
const gpts = [];
|
||||
|
||||
for (const game of TachiConfig.GAMES) {
|
||||
const playtypes = GetGameConfig(game).validPlaytypes;
|
||||
const playtypes = GetGameConfig(game).playtypes;
|
||||
|
||||
for (const playtype of playtypes) {
|
||||
gpts.push({ game, playtype, query: {} });
|
||||
|
||||
@@ -8,7 +8,7 @@ export const ValidatePlaytypeFromParam: RequestHandler = (req, res, next) => {
|
||||
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
if (!gameConfig.validPlaytypes.includes(req.params.playtype as Playtype)) {
|
||||
if (!gameConfig.playtypes.includes(req.params.playtype as Playtype)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `The playtype ${req.params.playtype} is not supported.`,
|
||||
@@ -25,7 +25,7 @@ export const ValidatePlaytypeFromParamFor =
|
||||
(req, res, next) => {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
if (!gameConfig.validPlaytypes.includes(req.params.playtype as Playtype)) {
|
||||
if (!gameConfig.playtypes.includes(req.params.playtype as Playtype)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `The playtype ${req.params.playtype} is not supported.`,
|
||||
|
||||
@@ -100,7 +100,7 @@ export async function UpdateAllPBs(userIDs?: Array<integer>, filter = {}) {
|
||||
for (const game of TachiConfig.GAMES) {
|
||||
const gameConfig = GetGameConfig(game);
|
||||
|
||||
for (const playtype of gameConfig.validPlaytypes) {
|
||||
for (const playtype of gameConfig.playtypes) {
|
||||
const scores = await db.scores.find(
|
||||
deepmerge({ userID: user.id, game, playtype }, filter),
|
||||
{
|
||||
|
||||
@@ -79,7 +79,7 @@ export function IsValidGame(str: string): str is Game {
|
||||
}
|
||||
|
||||
export function IsValidPlaytype(game: Game, str: string): str is Playtype {
|
||||
return GetGameConfig(game).validPlaytypes.includes(str as Playtype);
|
||||
return GetGameConfig(game).playtypes.includes(str as Playtype);
|
||||
}
|
||||
|
||||
export function IsValidScoreAlg(
|
||||
|
||||
Reference in New Issue
Block a user