From 9626931b918a3bfa7f5f3a6517f947501bd5812f Mon Sep 17 00:00:00 2001
From: Liam
Date: Sat, 26 Oct 2024 18:41:51 +0100
Subject: [PATCH] migrate some values to ssr data tracking so we don't need to
rely on BL as much
---
.../src/controller/scores.controller.ts | 2 +-
projects/backend/src/index.ts | 45 ++++++------
projects/backend/src/service/score.service.ts | 73 +++++++++++++++++--
.../src/model/score/impl/scoresaber-score.ts | 28 +++++++
.../common/src/model/score/previous-score.ts | 38 ++++++++++
.../score/badges/score-accuracy.tsx | 13 ++--
.../src/components/score/badges/score-pp.tsx | 56 ++++++--------
.../components/score/badges/score-score.tsx | 9 ++-
8 files changed, 196 insertions(+), 68 deletions(-)
create mode 100644 projects/common/src/model/score/previous-score.ts
diff --git a/projects/backend/src/controller/scores.controller.ts b/projects/backend/src/controller/scores.controller.ts
index 8c59e7e..4458f19 100644
--- a/projects/backend/src/controller/scores.controller.ts
+++ b/projects/backend/src/controller/scores.controller.ts
@@ -71,6 +71,6 @@ export default class ScoresController {
};
query: { search?: string };
}): Promise {
- return (await ScoreService.getPreviousScores(playerId, leaderboardId, page)).toJSON();
+ return (await ScoreService.getScoreHistory(playerId, leaderboardId, page)).toJSON();
}
}
diff --git a/projects/backend/src/index.ts b/projects/backend/src/index.ts
index 6e53aeb..6aa92f0 100644
--- a/projects/backend/src/index.ts
+++ b/projects/backend/src/index.ts
@@ -61,27 +61,30 @@ connectBeatLeaderWebsocket({
});
export const app = new Elysia();
-app.use(
- cron({
- name: "player-statistics-tracker-cron",
- pattern: "1 0 * * *", // Every day at 00:01
- timezone: "Europe/London", // UTC time
- run: async () => {
- await PlayerService.updatePlayerStatistics();
- },
- })
-);
-app.use(
- cron({
- name: "player-scores-tracker-cron",
- pattern: "0 4 * * *", // Every day at 04:00
- timezone: "Europe/London", // UTC time
- protect: true,
- run: async () => {
- await PlayerService.refreshPlayerScores();
- },
- })
-);
+if (isProduction()) {
+ app.use(
+ cron({
+ name: "player-statistics-tracker-cron",
+ pattern: "1 0 * * *", // Every day at 00:01
+ timezone: "Europe/London", // UTC time
+ protect: true,
+ run: async () => {
+ await PlayerService.updatePlayerStatistics();
+ },
+ })
+ );
+ app.use(
+ cron({
+ name: "player-scores-tracker-cron",
+ pattern: "0 4 * * *", // Every day at 04:00
+ timezone: "Europe/London", // UTC time
+ protect: true,
+ run: async () => {
+ await PlayerService.refreshPlayerScores();
+ },
+ })
+ );
+}
/**
* Custom error handler
diff --git a/projects/backend/src/service/score.service.ts b/projects/backend/src/service/score.service.ts
index 5377496..166527d 100644
--- a/projects/backend/src/service/score.service.ts
+++ b/projects/backend/src/service/score.service.ts
@@ -7,7 +7,6 @@ import BeatSaverService from "./beatsaver.service";
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
import { ScoreSort } from "@ssr/common/score/score-sort";
import { Leaderboards } from "@ssr/common/leaderboard";
-import Leaderboard from "@ssr/common/leaderboard/leaderboard";
import LeaderboardService from "./leaderboard.service";
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
import { PlayerScore } from "@ssr/common/score/player-score";
@@ -27,13 +26,18 @@ import {
import { BeatLeaderScoreImprovementToken } from "@ssr/common/types/token/beatleader/score/score-improvement";
import { ScoreType } from "@ssr/common/model/score/score";
import { getScoreSaberLeaderboardFromToken, getScoreSaberScoreFromToken } from "@ssr/common/token-creators";
-import { ScoreSaberScore, ScoreSaberScoreModel } from "@ssr/common/model/score/impl/scoresaber-score";
-import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
+import {
+ ScoreSaberPreviousScore,
+ ScoreSaberScore,
+ ScoreSaberScoreModel,
+} from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberScoreToken from "@ssr/common/types/token/scoresaber/score-saber-score-token";
import ScoreSaberLeaderboardToken from "@ssr/common/types/token/scoresaber/score-saber-leaderboard-token";
import { MapDifficulty } from "@ssr/common/score/map-difficulty";
import { MapCharacteristic } from "@ssr/common/types/map-characteristic";
import { Page, Pagination } from "@ssr/common/pagination";
+import ScoreSaberLeaderboard from "@ssr/common/model/leaderboard/impl/scoresaber-leaderboard";
+import Leaderboard from "@ssr/common/model/leaderboard/leaderboard";
const playerScoresCache = new SSRCache({
ttl: 1000 * 60, // 1 minute
@@ -394,6 +398,10 @@ export class ScoreService {
if (additionalData !== undefined) {
score.additionalData = additionalData;
}
+ const previousScore = await this.getPreviousScore(playerId, leaderboard.id + "", score.timestamp);
+ if (previousScore !== undefined) {
+ score.previousScore = previousScore;
+ }
scores.push({
score: score,
@@ -491,13 +499,13 @@ export class ScoreService {
}
/**
- * Gets the previous scores for a player.
+ * Gets the player's score history for a map.
*
* @param playerId the player's id to get the previous scores for
* @param leaderboardId the leaderboard to get the previous scores on
* @param page the page to get
*/
- public static async getPreviousScores(
+ public static async getScoreHistory(
playerId: string,
leaderboardId: string,
page: number
@@ -533,6 +541,10 @@ export class ScoreService {
if (additionalData !== undefined) {
score.additionalData = additionalData;
}
+ const previousScore = await this.getPreviousScore(playerId, leaderboardId, score.timestamp);
+ if (previousScore !== undefined) {
+ score.previousScore = previousScore;
+ }
toReturn.push({
score: score as unknown as ScoreSaberScore,
@@ -544,4 +556,55 @@ export class ScoreService {
return toReturn;
});
}
+
+ /**
+ * Gets the player's previous score for a map.
+ *
+ * @param playerId the player's id to get the previous score for
+ * @param leaderboardId the leaderboard to get the previous score on
+ * @param timestamp the score's timestamp to get the previous score for
+ * @returns the score, or undefined if none
+ */
+ public static async getPreviousScore(
+ playerId: string,
+ leaderboardId: string,
+ timestamp: Date
+ ): Promise {
+ const scores = await ScoreSaberScoreModel.find({ playerId: playerId, leaderboardId: leaderboardId });
+ if (scores == null || scores.length == 0) {
+ return undefined;
+ }
+
+ const scoreIndex = scores.findIndex(score => score.timestamp.getTime() == timestamp.getTime());
+ const score = scores.find(score => score.timestamp.getTime() == timestamp.getTime());
+ if (scoreIndex == -1 || score == undefined) {
+ return undefined;
+ }
+ const previousScore = scores[scoreIndex - 1];
+ if (previousScore == undefined) {
+ return undefined;
+ }
+ return {
+ score: previousScore.score,
+ accuracy: previousScore.accuracy,
+ modifiers: previousScore.modifiers,
+ misses: previousScore.misses,
+ missedNotes: previousScore.missedNotes,
+ badCuts: previousScore.badCuts,
+ fullCombo: previousScore.fullCombo,
+ pp: previousScore.pp,
+ weight: previousScore.weight,
+ maxCombo: previousScore.maxCombo,
+ change: {
+ score: score.score - previousScore.score,
+ accuracy: score.accuracy - previousScore.accuracy,
+ misses: score.misses - previousScore.misses,
+ missedNotes: score.missedNotes - previousScore.missedNotes,
+ badCuts: score.badCuts - previousScore.badCuts,
+ pp: score.pp - previousScore.pp,
+ weight: score.weight && previousScore.weight && score.weight - previousScore.weight,
+ maxCombo: score.maxCombo - previousScore.maxCombo,
+ },
+ } as ScoreSaberPreviousScore;
+ }
}
diff --git a/projects/common/src/model/score/impl/scoresaber-score.ts b/projects/common/src/model/score/impl/scoresaber-score.ts
index 9b3418a..4e08a0c 100644
--- a/projects/common/src/model/score/impl/scoresaber-score.ts
+++ b/projects/common/src/model/score/impl/scoresaber-score.ts
@@ -3,6 +3,7 @@ import Score from "../score";
import { type ScoreSaberLeaderboardPlayerInfoToken } from "../../../types/token/scoresaber/score-saber-leaderboard-player-info-token";
import { Document } from "mongoose";
import { AutoIncrementID } from "@typegoose/auto-increment";
+import { PreviousScore } from "../previous-score";
@modelOptions({
options: { allowMixed: Severity.ALLOW },
@@ -58,6 +59,11 @@ export class ScoreSaberScoreInternal extends Score {
*/
@Prop({ required: true })
public readonly maxCombo!: number;
+
+ /**
+ * The previous score, if any.
+ */
+ public previousScore?: ScoreSaberPreviousScore;
}
class ScoreSaberScorePublic extends ScoreSaberScoreInternal {
@@ -67,6 +73,28 @@ class ScoreSaberScorePublic extends ScoreSaberScoreInternal {
public playerInfo!: ScoreSaberLeaderboardPlayerInfoToken;
}
+export type ScoreSaberPreviousScore = PreviousScore & {
+ /**
+ * The pp of the previous score.
+ */
+ pp: number;
+
+ /**
+ * The weight of the previous score.
+ */
+ weight: number;
+
+ /**
+ * The max combo of the previous score.
+ */
+ maxCombo: number;
+
+ /**
+ * The change between the previous score and the current score.
+ */
+ change?: ScoreSaberPreviousScore;
+};
+
export type ScoreSaberScore = InstanceType;
export type ScoreSaberScoreDocument = ScoreSaberScore & Document;
export const ScoreSaberScoreModel: ReturnModelType =
diff --git a/projects/common/src/model/score/previous-score.ts b/projects/common/src/model/score/previous-score.ts
new file mode 100644
index 0000000..491ed45
--- /dev/null
+++ b/projects/common/src/model/score/previous-score.ts
@@ -0,0 +1,38 @@
+import { Modifier } from "../../score/modifier";
+
+export type PreviousScore = {
+ /**
+ * The score of the previous score.
+ */
+ score: number;
+
+ /**
+ * The accuracy of the previous score.
+ */
+ accuracy: number;
+
+ /**
+ * The modifiers of the previous score.
+ */
+ modifiers?: Modifier[];
+
+ /**
+ * The misses of the previous score.
+ */
+ misses: number;
+
+ /**
+ * The missed notes of the previous score.
+ */
+ missedNotes: number;
+
+ /**
+ * The bad cuts of the previous score.
+ */
+ badCuts: number;
+
+ /**
+ * The full combo of the previous score.
+ */
+ fullCombo?: boolean;
+};
diff --git a/projects/website/src/components/score/badges/score-accuracy.tsx b/projects/website/src/components/score/badges/score-accuracy.tsx
index 48c9ed7..c975304 100644
--- a/projects/website/src/components/score/badges/score-accuracy.tsx
+++ b/projects/website/src/components/score/badges/score-accuracy.tsx
@@ -14,8 +14,7 @@ type ScoreAccuracyProps = ScoreBadgeProps & {
};
export function ScoreAccuracyBadge({ score, leaderboard }: ScoreAccuracyProps) {
- const scoreImprovement = score.additionalData?.scoreImprovement;
- const previousAccuracy = scoreImprovement ? score.accuracy - scoreImprovement.accuracy : undefined;
+ const previousScore = score.previousScore;
const fcAccuracy = score.additionalData?.fcAccuracy;
const scoreBadge = getScoreBadgeFromAccuracy(score.accuracy);
@@ -57,9 +56,13 @@ export function ScoreAccuracyBadge({ score, leaderboard }: ScoreAccuracyProps) {
{modCount > 0 && }
- {scoreImprovement && previousAccuracy && (
-
- `${num.toFixed(2)}%`} />
+ {previousScore && previousScore.change && (
+
+ `${num.toFixed(2)}%`}
+ />
)}
diff --git a/projects/website/src/components/score/badges/score-pp.tsx b/projects/website/src/components/score/badges/score-pp.tsx
index 674414b..7f02076 100644
--- a/projects/website/src/components/score/badges/score-pp.tsx
+++ b/projects/website/src/components/score/badges/score-pp.tsx
@@ -4,7 +4,6 @@ import Tooltip from "@/components/tooltip";
import { ensurePositiveNumber, formatPp } from "@ssr/common/utils/number-utils";
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
import { Change } from "@/common/change";
-import { Warning } from "@/components/warning";
type ScorePpProps = ScoreBadgeProps & {
/**
@@ -14,8 +13,7 @@ type ScorePpProps = ScoreBadgeProps & {
};
export function ScorePpBadge({ score, leaderboard }: ScorePpProps) {
- const scoreImprovement = score.additionalData?.scoreImprovement;
- const previousAccuracy = scoreImprovement ? score.accuracy - scoreImprovement?.accuracy : undefined;
+ const previousScore = score.previousScore;
const fcAccuracy = score.additionalData?.fcAccuracy;
const pp = score.pp;
const weight = score.weight;
@@ -28,39 +26,29 @@ export function ScorePpBadge({ score, leaderboard }: ScorePpProps) {
return (
<>
-
-
-
Performance Points
-
Raw: {formatPp(pp)}pp
-
- Weighted: {formatPp(weightedPp)}pp ({(100 * weight).toFixed(2)}%)
-
- {fcPp &&
Full Combo: {fcPp}pp
}
-
-
- {previousAccuracy && (
-
-
- The previous pp may not be 100% accurate due to ScoreSaber API limitations.
+
+
+
+
Performance Points
+
Raw: {formatPp(pp)}pp
+
+ Weighted: {formatPp(weightedPp)}pp ({(100 * weight).toFixed(2)}%)
-
- )}
-
- }
- >
-
+ {fcPp &&
Full Combo: {fcPp}pp
}
+
+
+ }
+ >
{formatPp(pp)}pp
- {previousAccuracy && (
-
- )}
-
-
+
+ {previousScore && previousScore.change && (
+ Previous PP: {formatPp(previousScore.pp)}pp}>
+
+
+ )}
+
>
);
}
diff --git a/projects/website/src/components/score/badges/score-score.tsx b/projects/website/src/components/score/badges/score-score.tsx
index 977f73e..488036a 100644
--- a/projects/website/src/components/score/badges/score-score.tsx
+++ b/projects/website/src/components/score/badges/score-score.tsx
@@ -1,14 +1,19 @@
import { ScoreBadgeProps } from "@/components/score/badges/badge-props";
import { formatNumberWithCommas } from "@ssr/common/utils/number-utils";
import { Change } from "@/common/change";
+import Tooltip from "@/components/tooltip";
export function ScoreScoreBadge({ score }: ScoreBadgeProps) {
- const scoreImprovement = score.additionalData?.scoreImprovement;
+ const previousScore = score.previousScore;
return (
{formatNumberWithCommas(Number(score.score.toFixed(0)))}
- {scoreImprovement &&
}
+ {previousScore && previousScore.change && (
+
Previous Score: {previousScore.score}}>
+
+
+ )}
);
}