diff --git a/client/src/components/user/UGPTStatContainer.tsx b/client/src/components/user/UGPTStatContainer.tsx index 1dcd5ec7b..b5db1f06a 100644 --- a/client/src/components/user/UGPTStatContainer.tsx +++ b/client/src/components/user/UGPTStatContainer.tsx @@ -17,7 +17,7 @@ export default function UGPTStatContainer({ const searchParams = new URLSearchParams(); searchParams.set("mode", stat.mode); - searchParams.set("property", stat.property); + searchParams.set("property", stat.metric); if (stat.mode === "chart") { searchParams.set("chartID", stat.chartID); diff --git a/client/src/components/user/UGPTStatCreator.tsx b/client/src/components/user/UGPTStatCreator.tsx index 2af350a6b..b86095f62 100644 --- a/client/src/components/user/UGPTStatCreator.tsx +++ b/client/src/components/user/UGPTStatCreator.tsx @@ -48,13 +48,13 @@ export default function UGPTStatCreator({ if (values.mode === "chart") { stat = { mode: "chart", - property: values.property as ShowcaseStatDetails["property"], + metric: values.property as ShowcaseStatDetails["metric"], chartID: values.chartID ?? "", }; } else if (values.mode === "folder") { stat = { mode: "folder", - property: values.property as "lamp" | "score" | "percent" | "grade", + metric: values.property as "lamp" | "score" | "percent" | "grade", folderID: values.folderID ?? "", gte, }; diff --git a/client/src/components/user/UGPTStatShowcase.tsx b/client/src/components/user/UGPTStatShowcase.tsx index e6316dbc0..68aee2c29 100644 --- a/client/src/components/user/UGPTStatShowcase.tsx +++ b/client/src/components/user/UGPTStatShowcase.tsx @@ -266,7 +266,7 @@ function StatDelta({ v1: number; v2?: number; mode: "folder" | "chart"; - property: UGPTPreferenceStatsReturn["stat"]["property"]; + property: UGPTPreferenceStatsReturn["stat"]["metric"]; game: Game; playtype: Playtype; }) { @@ -397,15 +397,15 @@ export function StatDisplay({

{FormatChart(game, song, chart)}

- {UppercaseFirst(stat.property)}:{" "} - {FormatPropertyGTE(game, playtype, stat.property, result.value)} + {UppercaseFirst(stat.metric)}:{" "} + {FormatPropertyGTE(game, playtype, stat.metric, result.value)}

{user && user.id !== reqUser.id && ( @@ -431,8 +431,8 @@ export function StatDisplay({

{headerStr}

- {UppercaseFirst(stat.property)} >={" "} - {FormatPropertyGTE(game, playtype, stat.property, stat.gte)} + {UppercaseFirst(stat.metric)} >={" "} + {FormatPropertyGTE(game, playtype, stat.metric, stat.gte)}

{result.value} @@ -447,7 +447,7 @@ export function StatDisplay({ v1={statData.result.value} v2={compareData?.result.value} mode={stat.mode} - property={stat.property} + property={stat.metric} game={game} playtype={playtype} /> diff --git a/common/src/config/config.ts b/common/src/config/config.ts index d299f2cde..5400337ef 100644 --- a/common/src/config/config.ts +++ b/common/src/config/config.ts @@ -20,7 +20,6 @@ import type { GamePTConfig, Playtype, Playtypes, - ProvidedMetrics, } from "../types/game-config"; import type { INTERNAL_GAME_CONFIG, INTERNAL_GAME_PT_CONFIG } from "../types/internals"; import type { ConfScoreMetric } from "../types/metrics"; diff --git a/common/src/lib/schemas.ts b/common/src/lib/schemas.ts index 773a952f2..da7802e31 100644 --- a/common/src/lib/schemas.ts +++ b/common/src/lib/schemas.ts @@ -39,7 +39,11 @@ function prSchemaFnWrap(schema: PrudenceSchema) { }; } -const PR_GAME_STATS = (game: Game, playtype: Playtypes[Game], gptConfig: INTERNAL_GAME_PT_CONFIG) => ({ +const PR_GAME_STATS = ( + game: Game, + playtype: Playtypes[Game], + gptConfig: INTERNAL_GAME_PT_CONFIG +) => ({ userID: p.isPositiveNonZeroInteger, game: p.is(game), playtype: p.is(playtype), @@ -77,7 +81,9 @@ const extractGPTIDString = (self: unknown) => { // Playtype might be undefined in the case where the string contains no colon. if (playtype === undefined || !IsValidPlaytype(game, playtype)) { - throw new Error(`Expected valid playtype -- got ${playtype} from gptString ${s.gptString}.`); + throw new Error( + `Expected valid playtype -- got ${playtype} from gptString ${s.gptString}.` + ); } return { game, playtype }; @@ -652,7 +658,6 @@ const PRE_SCHEMAS = { status: p.nullable(p.isBoundedString(3, 140)), customPfpLocation: "?string", customBannerLocation: "?string", - clan: p.nullable(p.isBoundedString(2, 4)), lastSeen: p.isPositiveInteger, badges: [p.isIn("beta", "alpha", "devTeam")], authLevel: p.isBoundedInteger(UserAuthLevels.BANNED, UserAuthLevels.ADMIN), diff --git a/common/src/types/documents.ts b/common/src/types/documents.ts index 0ad0362f0..02818c406 100644 --- a/common/src/types/documents.ts +++ b/common/src/types/documents.ts @@ -319,7 +319,6 @@ export interface UserDocument { status: string | null; customPfpLocation: string | null; customBannerLocation: string | null; - clan: string | null; // todo badges: Array; authLevel: UserAuthLevels; } @@ -609,14 +608,20 @@ export type ShowcaseStatDetails = ShowcaseStatChart | ShowcaseStatFolder; export interface ShowcaseStatFolder { mode: "folder"; folderID: string; - property: "grade" | "lamp" | "percent" | "score"; + + // should be a valid metric for the showcase this game is for + // this is not checked by the typesystem though. sorry! + metric: string; gte: number; } export interface ShowcaseStatChart { mode: "chart"; chartID: string; - property: "grade" | "lamp" | "percent" | "playcount" | "score"; + + // should be a valid metric for the showcase this game is for + // this is not checked by the typesystem though. sorry! + metric: string; } export interface UGPTSettingsDocument { diff --git a/common/src/types/metrics.ts b/common/src/types/metrics.ts index b2b4edb71..ecc59ae6e 100644 --- a/common/src/types/metrics.ts +++ b/common/src/types/metrics.ts @@ -24,12 +24,18 @@ interface ConfDecimalScoreMetricNormal { type: "DECIMAL"; validate: (v: number) => string | true; + + // This exists to allow DecimalScoreMetric.chartDependentMax. + chartDependentMax?: never; } interface ConfIntegerScoreMetricNormal { type: "INTEGER"; validate: (v: number) => string | true; + + // see above + chartDependentMax?: never; } interface ConfDecimalScoreMetricChartDependent { diff --git a/server/src/external/mongo/indexes.ts b/server/src/external/mongo/indexes.ts index 0361dfa55..6f0396544 100644 --- a/server/src/external/mongo/indexes.ts +++ b/server/src/external/mongo/indexes.ts @@ -2,7 +2,8 @@ import CreateLogCtx from "lib/logger/logger"; import { TachiConfig } from "lib/setup/config"; import monk from "monk"; -import { Random20Hex } from "utils/misc"; +import { GAME_PT_CONFIGS } from "tachi-common"; +import { DedupeArr, Random20Hex } from "utils/misc"; import type { Databases } from "./db"; import type { IndexOptions } from "mongodb"; import type { IMonkManager } from "monk"; @@ -20,6 +21,8 @@ function index(fields: Record, options?: IndexOptions) { const UNIQUE = { unique: true }; +const allDefaultMetrics = DedupeArr(Object.values(GAME_PT_CONFIGS).map((e) => e.defaultMetric)); + // NOTE: INDEXES REMOVED FROM HERE WILL **NOT** AUTOMATICALLY UPDATE. // YOU NEED TO RUN src/scripts/set-indexes.ts --reset TO REMOVE INDEXES. // (indexes added will automatically apply on tachi-server boot, though). @@ -34,7 +37,10 @@ const staticIndexes: Partial>> = { ], "personal-bests": [ index({ chartID: 1, userID: 1 }, UNIQUE), - index({ chartID: 1, [`scoreData.${gptConfig.defaultMetric}`]: 1 }), + + // one index for every default metric, why not. + ...allDefaultMetrics.map((e) => index({ chartID: 1, [`scoreData.${e}`]: 1 })), + index({ userID: 1, game: 1, playtype: 1 }), // activity diff --git a/server/src/lib/score-import/framework/pb/process-pbs.ts b/server/src/lib/score-import/framework/pb/process-pbs.ts index 22634f341..b9166315c 100644 --- a/server/src/lib/score-import/framework/pb/process-pbs.ts +++ b/server/src/lib/score-import/framework/pb/process-pbs.ts @@ -1,5 +1,7 @@ import { CreatePBDoc, UpdateChartRanking } from "./create-pb-doc"; import db from "external/mongo/db"; +import { GetGPTString } from "tachi-common"; +import { GetChartForIDGuaranteed } from "utils/db"; import type { PBScoreDocumentNoRank } from "./create-pb-doc"; import type { KtLogger } from "lib/logger/logger"; import type { Game, integer, Playtype } from "tachi-common"; @@ -18,10 +20,16 @@ export async function ProcessPBs( return; } + const gpt = GetGPTString(game, playtype); + const promises = []; for (const chartID of chartIDs) { - promises.push(CreatePBDoc(userID, chartID, logger)); + promises.push( + GetChartForIDGuaranteed(game, chartID).then((chart) => + CreatePBDoc(gpt, userID, chart, logger) + ) + ); } const pbDocsReturn = await Promise.all(promises); @@ -49,7 +57,18 @@ export async function ProcessPBs( pbDocs.map((e) => ({ updateOne: { filter: { chartID: e.chartID, userID: e.userID }, - update: { $set: e }, + update: { + $set: { + ...e, + + // stub out ranking data with some invalid nonsense. + rankingData: { + outOf: 0, + rank: 0, + rivalRank: null, + }, + }, + }, upsert: true, }, })), diff --git a/server/src/lib/score-import/framework/score-importing/derivers.ts b/server/src/lib/score-import/framework/score-importing/derivers.ts index 1174e9525..ff295c6c5 100644 --- a/server/src/lib/score-import/framework/score-importing/derivers.ts +++ b/server/src/lib/score-import/framework/score-importing/derivers.ts @@ -2,7 +2,7 @@ import { InternalFailure } from "../common/converter-failures"; import { GPT_SERVER_IMPLEMENTATIONS } from "game-implementations/game-implementations"; import { GetGPTConfig } from "tachi-common"; import type { DryScore, DryScoreData } from "../common/types"; -import type { GPTDerivers } from "./types"; +import type { GPTDerivers } from "game-implementations/types"; import type { KtLogger } from "lib/logger/logger"; import type { ChartDocument, diff --git a/server/src/lib/score-import/framework/score-importing/hydrate-score.ts b/server/src/lib/score-import/framework/score-importing/hydrate-score.ts index 8a7ba94a1..d8b6be53f 100644 --- a/server/src/lib/score-import/framework/score-importing/hydrate-score.ts +++ b/server/src/lib/score-import/framework/score-importing/hydrate-score.ts @@ -1,5 +1,5 @@ -import { CreateScoreCalcData } from "../calculated-data/score"; import { CreateFullScoreData } from "./derivers"; +import { CreateScoreCalcData } from "../calculated-data/score"; import { GetGPTString } from "tachi-common"; import type { DryScore } from "../common/types"; import type { KtLogger } from "lib/logger/logger"; @@ -22,7 +22,7 @@ export function HydrateScore( const scoreData = CreateFullScoreData(gpt, dryScore.scoreData, chart, logger); - const calculatedData = CreateScoreCalcData(dryScore, chart); + const calculatedData = CreateScoreCalcData(dryScore.game, dryScore.scoreData, chart); const score: ScoreDocument = { ...dryScore, diff --git a/server/src/lib/score-import/import-types/file/solid-state-squad/converter.ts b/server/src/lib/score-import/import-types/file/solid-state-squad/converter.ts index a330c6de6..03c337b8c 100644 --- a/server/src/lib/score-import/import-types/file/solid-state-squad/converter.ts +++ b/server/src/lib/score-import/import-types/file/solid-state-squad/converter.ts @@ -10,6 +10,7 @@ import type { DryScore } from "../../../framework/common/types"; import type { ConverterFunction } from "../../common/types"; import type { S3Score } from "./types"; import type { Difficulties, Playtypes, Versions } from "tachi-common"; +import type { GetEnumValue } from "tachi-common/types/metrics"; import type { EmptyObject } from "utils/types"; export function ParseDifficulty(diff: S3Score["diff"]): { diff --git a/server/src/lib/score-mutation/update-score.ts b/server/src/lib/score-mutation/update-score.ts index c99b61170..c1805c40c 100644 --- a/server/src/lib/score-mutation/update-score.ts +++ b/server/src/lib/score-mutation/update-score.ts @@ -76,7 +76,7 @@ export default async function UpdateScore( logger.verbose("Received Update Score request."); // eslint-disable-next-line require-atomic-updates - newScore.calculatedData = CreateScoreCalcData(newScore, chart); + newScore.calculatedData = CreateScoreCalcData(newScore.game, newScore.scoreData, chart); try { // Having _id defined will cause this to throw, causing it to not apply diff --git a/server/src/lib/showcase/evaluator.ts b/server/src/lib/showcase/evaluator.ts index ccc27d066..75f12cebb 100644 --- a/server/src/lib/showcase/evaluator.ts +++ b/server/src/lib/showcase/evaluator.ts @@ -1,13 +1,16 @@ import db from "external/mongo/db"; +import { GetGPTConfig } from "tachi-common"; import { GetFolderChartIDs } from "utils/folder"; import type { - integer, - ShowcaseStatDetails, + GPTString, ShowcaseStatChart, + ShowcaseStatDetails, ShowcaseStatFolder, + integer, } from "tachi-common"; export function EvaluateShowcaseStat( + gpt: GPTString, details: ShowcaseStatDetails, userID: integer ): Promise<{ @@ -16,9 +19,9 @@ export function EvaluateShowcaseStat( }> { switch (details.mode) { case "chart": - return EvaluateShowcaseChartStat(details, userID); + return EvaluateShowcaseChartStat(gpt, details, userID); case "folder": - return EvaluateShowcaseFolderStat(details, userID); + return EvaluateShowcaseFolderStat(gpt, details, userID); default: // @ts-expect-error This should never happen anyway -- this ignore ignores a 'never' result. @@ -26,13 +29,17 @@ export function EvaluateShowcaseStat( } } -async function EvaluateShowcaseChartStat(details: ShowcaseStatChart, userID: integer) { +async function EvaluateShowcaseChartStat( + gpt: GPTString, + details: ShowcaseStatChart, + userID: integer +) { // requires special handling - if (details.property === "playcount") { + if (details.metric === "playcount") { return { value: await db.scores.count({ chartID: details.chartID, userID }) }; } - const mongoProp = PropToMongoProp(details.property); + const mongoProp = PropToMongoProp(gpt, details.metric); const pb = await db["personal-bests"].findOne( { chartID: details.chartID, userID }, @@ -43,12 +50,30 @@ async function EvaluateShowcaseChartStat(details: ShowcaseStatChart, userID: int return { value: null }; } - const scProp = PropToScoreDataProp(details.property); + const metric = details.metric; - return { value: pb.scoreData[scProp] }; + const gptConfig = GetGPTConfig(gpt); + + const scoreMetricConfig = gptConfig.providedMetrics[metric] ?? gptConfig.derivedMetrics[metric]; + + if (!scoreMetricConfig) { + throw new Error(`Invalid metric of ${metric} passed for game ${gpt}.`); + } + + if (scoreMetricConfig.type === "ENUM") { + // @ts-expect-error guaranteed to be correct + return { value: pb.scoreData.enumIndexes[metric] }; + } + + // @ts-expect-error guaranteed to be correct + return { value: pb.scoreData[metric] }; } -async function EvaluateShowcaseFolderStat(details: ShowcaseStatFolder, userID: integer) { +async function EvaluateShowcaseFolderStat( + gpt: GPTString, + details: ShowcaseStatFolder, + userID: integer +) { let chartIDs; if (Array.isArray(details.folderID)) { @@ -57,7 +82,7 @@ async function EvaluateShowcaseFolderStat(details: ShowcaseStatFolder, userID: i chartIDs = await GetFolderChartIDs(details.folderID); } - const mongoProp = PropToMongoProp(details.property); + const mongoProp = PropToMongoProp(gpt, details.metric); const value = await db["personal-bests"].count({ userID, @@ -70,28 +95,18 @@ async function EvaluateShowcaseFolderStat(details: ShowcaseStatFolder, userID: i return { value, outOf: chartIDs.length }; } -function PropToMongoProp(prop: "grade" | "lamp" | "percent" | "score") { - switch (prop) { - case "score": - return "scoreData.score"; - case "lamp": - return "scoreData.lampIndex"; - case "grade": - return "scoreData.gradeIndex"; - case "percent": - return [`scoreData.${gptConfig.defaultMetric}`]; - } -} +function PropToMongoProp(gpt: GPTString, metric: string) { + const gptConfig = GetGPTConfig(gpt); -function PropToScoreDataProp(prop: "grade" | "lamp" | "percent" | "score") { - switch (prop) { - case "score": - return "score"; - case "lamp": - return "lampIndex"; - case "grade": - return "gradeIndex"; - case "percent": - return "percent"; + const scoreMetricConfig = gptConfig.providedMetrics[metric] ?? gptConfig.derivedMetrics[metric]; + + if (!scoreMetricConfig) { + throw new Error(`Invalid metric of ${metric} passed for game ${gpt}.`); } + + if (scoreMetricConfig.type === "ENUM") { + return `scoreData.enumIndexes.${metric}`; + } + + return `scoreData.${metric}`; } diff --git a/server/src/lib/showcase/get-stats.test.ts b/server/src/lib/showcase/get-stats.test.ts index 34f05bc45..56ec15e84 100644 --- a/server/src/lib/showcase/get-stats.test.ts +++ b/server/src/lib/showcase/get-stats.test.ts @@ -31,13 +31,13 @@ t.test("#EvalulateUsersStatsShowcase", (t) => { { folderID: TestingIIDXFolderSP10.folderID, mode: "folder", - property: "lamp", + metric: "lamp", gte: IIDX_LAMPS.HARD_CLEAR, }, { chartID: Testing511SPA.chartID, mode: "chart", - property: "score", + metric: "score", }, ], gameSpecific: { diff --git a/server/src/lib/showcase/get-stats.ts b/server/src/lib/showcase/get-stats.ts index c8369e33d..d98fab2c3 100644 --- a/server/src/lib/showcase/get-stats.ts +++ b/server/src/lib/showcase/get-stats.ts @@ -2,7 +2,8 @@ import { EvaluateShowcaseStat } from "./evaluator"; import { GetRelatedStatDocuments } from "./get-related"; import db from "external/mongo/db"; import CreateLogCtx from "lib/logger/logger"; -import type { integer, Game, Playtype, ShowcaseStatDetails } from "tachi-common"; +import { GetGPTString } from "tachi-common"; +import type { integer, Game, Playtype, ShowcaseStatDetails, GPTString } from "tachi-common"; const logger = CreateLogCtx(__filename); @@ -34,16 +35,23 @@ export async function EvaluateUsersStatsShowcase( ); } + const gpt = GetGPTString(game, playtype); + const results = await Promise.all( - settings.preferences.stats.map((details) => EvaluateStats(details, userID, game)) + settings.preferences.stats.map((details) => EvaluateStats(gpt, details, userID, game)) ); return results; } -async function EvaluateStats(details: ShowcaseStatDetails, userID: integer, game: Game) { +async function EvaluateStats( + gpt: GPTString, + details: ShowcaseStatDetails, + userID: integer, + game: Game +) { const [result, related] = await Promise.all([ - EvaluateShowcaseStat(details, userID), + EvaluateShowcaseStat(gpt, details, userID), GetRelatedStatDocuments(details, game), ]); diff --git a/server/src/lib/showcase/showcase.test.ts b/server/src/lib/showcase/showcase.test.ts index ea30c891f..e35534127 100644 --- a/server/src/lib/showcase/showcase.test.ts +++ b/server/src/lib/showcase/showcase.test.ts @@ -18,19 +18,19 @@ t.test("#RemoveStaleFolderShowcaseStats", (t) => { { mode: "chart", chartID: "foo", - property: "grade", + metric: "grade", }, { mode: "folder", folderID: "REMOVED_FOLDER", gte: 1, - property: "lamp", + metric: "lamp", }, { mode: "folder", folderID: "NORMAL_FOLDER", gte: 1, - property: "lamp", + metric: "lamp", }, ], }, @@ -42,13 +42,13 @@ t.test("#RemoveStaleFolderShowcaseStats", (t) => { { mode: "chart", chartID: "foo", - property: "grade", + metric: "grade", }, { mode: "folder", folderID: "REMOVED_FOLDER", gte: 1, - property: "lamp", + metric: "lamp", }, ], }, diff --git a/server/src/lib/targets/goal-utils.ts b/server/src/lib/targets/goal-utils.ts index 1f81c7193..55a904315 100644 --- a/server/src/lib/targets/goal-utils.ts +++ b/server/src/lib/targets/goal-utils.ts @@ -1,5 +1,5 @@ import db from "external/mongo/db"; -import { FormatGame, GetGamePTConfig, GetSpecificGPTConfig } from "tachi-common"; +import { FormatGame, GetGPTString, GetGamePTConfig, GetSpecificGPTConfig } from "tachi-common"; import { GetFolderForIDGuaranteed, HumaniseChartID } from "utils/db"; import { GetFolderChartIDs } from "utils/folder"; import { FormatMaxDP, HumanisedJoinArray } from "utils/misc"; @@ -11,7 +11,9 @@ export async function CreateGoalTitle( game: Game, playtype: Playtype ) { - const formattedCriteria = FormatCriteria(criteria, game, playtype); + const gptString = GetGPTString(game, playtype); + + const formattedCriteria = FormatCriteria(criteria, gptString); const datasetName = await FormatCharts(charts, criteria, game); diff --git a/server/src/lib/targets/goals.ts b/server/src/lib/targets/goals.ts index 3d37e7d9d..124eb37ce 100644 --- a/server/src/lib/targets/goals.ts +++ b/server/src/lib/targets/goals.ts @@ -3,7 +3,7 @@ import db from "external/mongo/db"; import fjsh from "fast-json-stable-hash"; import { SubscribeFailReasons } from "lib/constants/err-codes"; import CreateLogCtx from "lib/logger/logger"; -import { FormatGame, GenericFormatGradeDelta, GetGamePTConfig } from "tachi-common"; +import { FormatGame, GetGamePTConfig } from "tachi-common"; import { GetFolderChartIDs } from "utils/folder"; import { IsNullish } from "utils/misc"; import type { KtLogger } from "lib/logger/logger"; diff --git a/server/src/server/router/api/v1/auth/auth.ts b/server/src/server/router/api/v1/auth/auth.ts index 835b3b724..960a3e796 100644 --- a/server/src/server/router/api/v1/auth/auth.ts +++ b/server/src/server/router/api/v1/auth/auth.ts @@ -1,14 +1,14 @@ import bcrypt from "bcryptjs"; import db from "external/mongo/db"; import CreateLogCtx from "lib/logger/logger"; -import { Environment, ServerConfig } from "lib/setup/config"; +import { ServerConfig } from "lib/setup/config"; import { p } from "prudence"; import { UserAuthLevels } from "tachi-common"; import nodeFetch from "utils/fetch"; import { Random20Hex } from "utils/misc"; import { CreateURLWithParams } from "utils/url"; import { FormatUserDoc } from "utils/user"; -import type { integer, UserDocument, UserSettings } from "tachi-common"; +import type { integer, UserDocument, UserSettingsDocument } from "tachi-common"; import type { PrivateUserInfoDocument } from "utils/types"; const logger = CreateLogCtx(__filename); @@ -60,7 +60,7 @@ export async function AddNewInvite(user: UserDocument) { return result; } -export const DEFAULT_USER_SETTINGS: UserSettings["preferences"] = { +export const DEFAULT_USER_SETTINGS: UserSettingsDocument["preferences"] = { developerMode: false, advancedMode: false, invisible: false, @@ -87,7 +87,6 @@ export async function AddNewUser( username, usernameLowercase: username.toLowerCase(), about: "I'm a fairly nondescript person.", - clan: null, socialMedia: {}, status: null, customBannerLocation: null, @@ -119,13 +118,13 @@ export function InsertPrivateUserInfo(userID: integer, hashedPassword: string, e export function InsertDefaultUserSettings(userID: integer) { logger.verbose(`Inserting default settings for ${userID}.`); - const userSettings: UserSettings = { + const UserSettingsDocument: UserSettingsDocument = { userID, following: [], preferences: DEFAULT_USER_SETTINGS, }; - return db["user-settings"].insert(userSettings); + return db["user-settings"].insert(UserSettingsDocument); } export async function ValidateCaptcha( @@ -168,7 +167,11 @@ export async function ValidateCaptcha( return gcr.success; } -export function MountAuthCookie(req: Express.Request, user: UserDocument, settings: UserSettings) { +export function MountAuthCookie( + req: Express.Request, + user: UserDocument, + settings: UserSettingsDocument +) { req.session.tachi = { user, settings, diff --git a/server/src/server/router/api/v1/auth/router.ts b/server/src/server/router/api/v1/auth/router.ts index 3e65b21cc..72cb788af 100644 --- a/server/src/server/router/api/v1/auth/router.ts +++ b/server/src/server/router/api/v1/auth/router.ts @@ -13,7 +13,7 @@ import db from "external/mongo/db"; import { SendEmail } from "lib/email/client"; import { EmailFormatResetPassword, EmailFormatVerifyEmail } from "lib/email/formats"; import CreateLogCtx from "lib/logger/logger"; -import { Environment, ServerConfig, TachiConfig } from "lib/setup/config"; +import { Environment, ServerConfig } from "lib/setup/config"; import { p } from "prudence"; import prValidate from "server/middleware/prudence-validate"; import { diff --git a/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts b/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts index ab43337dc..bea5f4557 100644 --- a/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts +++ b/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts @@ -1,5 +1,5 @@ import { Router } from "express"; -import { GetPBsForJubility } from "lib/score-import/framework/calculated-data/profile"; +import { GetPBsForJubility } from "game-implementations/games/jubeat"; import { GetRelevantSongsAndCharts } from "utils/db"; import { GetUser } from "utils/req-tachi-data"; diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts index c32a79f0b..4f003109f 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/settings/router.ts @@ -4,8 +4,7 @@ import db from "external/mongo/db"; import CreateLogCtx from "lib/logger/logger"; import { p } from "prudence"; import { RequirePermissions } from "server/middleware/auth"; -import { GetGamePTConfig } from "tachi-common"; -import { PR_GAMESPECIFIC_SETTINGS } from "tachi-common/lib/schemas"; +import { GetGamePTConfig, PrudenceZodShim } from "tachi-common"; import { FormatPrError, optNull } from "utils/prudence"; import { GetUGPT } from "utils/req-tachi-data"; import { FormatUserDoc } from "utils/user"; @@ -31,7 +30,7 @@ router.patch( const gptConfig = GetGamePTConfig(game, playtype); - const gameSpecificSchema = PR_GAMESPECIFIC_SETTINGS(game); + const gameSpecificSchema = PrudenceZodShim(gptConfig.preferences); const err = p(req.safeBody, { preferredScoreAlg: p.optional(p.nullable(p.isIn(gptConfig.scoreRatingAlgs))), diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts index e18a0bd7b..75c7cb600 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.test.ts @@ -30,13 +30,13 @@ const SetFolders = async () => { { folderID: TestingIIDXFolderSP10.folderID, mode: "folder", - property: "lamp", + metric: "lamp", gte: IIDX_LAMPS.HARD_CLEAR, }, { chartID: Testing511SPA.chartID, mode: "chart", - property: "score", + metric: "score", }, ], gameSpecific: { @@ -96,7 +96,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/showcase", (t) => { { mode: "folder", folderID: TestingIIDXFolderSP10.folderID, - property: "score", + metric: "score", gte: 1480, }, ], diff --git a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts index 25249686d..82593ea33 100644 --- a/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts +++ b/server/src/server/router/api/v1/users/_userID/games/_game/_playtype/showcase/router.ts @@ -6,7 +6,7 @@ import { EvaluateUsersStatsShowcase } from "lib/showcase/get-stats"; import { p } from "prudence"; import { RequirePermissions } from "server/middleware/auth"; import { RequireAuthedAsUser } from "server/router/api/v1/users/_userID/middleware"; -import { FormatGame, GetGamePTConfig } from "tachi-common"; +import { FormatGame, GetGPTString, GetGamePTConfig } from "tachi-common"; import { IsRecord } from "utils/misc"; import { FormatPrError } from "utils/prudence"; import { GetUGPT } from "utils/req-tachi-data"; @@ -103,7 +103,7 @@ router.get("/custom", async (req, res) => { stat = { mode: "folder", - property: req.query.property as "grade" | "lamp" | "percent" | "score", + metric: req.query.property as "grade" | "lamp" | "percent" | "score", folderID, gte: Number(req.query.gte), }; @@ -137,7 +137,7 @@ router.get("/custom", async (req, res) => { stat = { mode: "chart", - property: req.query.property as "grade" | "lamp" | "percent" | "playcount" | "score", + metric: req.query.property as "grade" | "lamp" | "percent" | "playcount" | "score", chartID: req.query.chartID as string, }; } else { @@ -147,7 +147,9 @@ router.get("/custom", async (req, res) => { }); } - const result = await EvaluateShowcaseStat(stat, user.id); + const gpt = GetGPTString(game, playtype); + + const result = await EvaluateShowcaseStat(gpt, stat, user.id); const related = await GetRelatedStatDocuments(stat, game); diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts index 8aa1717a2..780f92547 100644 --- a/server/src/test-utils/test-data.ts +++ b/server/src/test-utils/test-data.ts @@ -830,7 +830,6 @@ export const FakeOtherUser: UserDocument = { about: "", authLevel: 0, badges: [], - clan: null, customBannerLocation: null, customPfpLocation: null, joinDate: 0, diff --git a/server/src/utils/calculations/recalc-scores.ts b/server/src/utils/calculations/recalc-scores.ts index 4198a2956..16f22c2a7 100644 --- a/server/src/utils/calculations/recalc-scores.ts +++ b/server/src/utils/calculations/recalc-scores.ts @@ -40,7 +40,7 @@ export async function RecalcAllScores(filter = {}) { modifiedUsers.add(`${c.game}-${c.playtype}-${c.userID}`); modifiedUserIDs.add(c.userID); - const calculatedData = CreateScoreCalcData(c, chart); + const calculatedData = CreateScoreCalcData(c.game, c.scoreData, chart); return { scoreID: c.scoreID, calculatedData }; }, diff --git a/server/src/utils/scores.ts b/server/src/utils/scores.ts index 55bdb065a..23e580cfc 100644 --- a/server/src/utils/scores.ts +++ b/server/src/utils/scores.ts @@ -43,11 +43,7 @@ export function FilterChartsAndSongs( } export function GetScoreIDsFromComposed(pb: PBScoreDocument) { - const arr = [pb.composedFrom.lampPB, pb.composedFrom.scorePB]; - - if (pb.composedFrom.other) { - arr.push(...pb.composedFrom.other.map((e) => e.scoreID)); - } + const arr = pb.composedFrom.map((e) => e.scoreID); return DedupeArr(arr); }