impl friends system
All checks were successful
Deploy Backend / deploy (push) Successful in 2m43s
Deploy Website / deploy (push) Successful in 4m55s

This commit is contained in:
Lee
2024-10-16 11:23:28 +01:00
parent 2e2c03241e
commit 8ab81b1b27
9 changed files with 375 additions and 12 deletions

View File

@ -0,0 +1,129 @@
type CacheOptions = {
/**
* The time the cached object will be valid for
*/
ttl?: number;
/**
* How often to check for expired objects
*/
checkInterval?: number;
/**
* Enable debug messages
*/
debug?: boolean;
};
type CachedObject = {
/**
* The cached object
*/
value: any;
/**
* The timestamp the object was cached
*/
timestamp: number;
};
export class SSRCache {
/**
* The time the cached object will be valid for
* @private
*/
private readonly ttl: number | undefined;
/**
* How often to check for expired objects
* @private
*/
private readonly checkInterval: number | undefined;
/**
* Enable debug messages
* @private
*/
private readonly debug: boolean;
/**
* The objects that have been cached
* @private
*/
private cache = new Map<string, CachedObject>();
constructor({ ttl, checkInterval, debug }: CacheOptions) {
this.ttl = ttl;
this.checkInterval = checkInterval || this.ttl ? 1000 * 60 : undefined; // 1 minute
this.debug = debug || false;
if (this.ttl !== undefined && this.checkInterval !== undefined) {
setInterval(() => {
for (const [key, value] of this.cache.entries()) {
if (value.timestamp + this.ttl! > Date.now()) {
continue;
}
this.remove(key);
}
}, this.checkInterval);
}
}
/**
* Gets an object from the cache
*
* @param key the cache key for the object
*/
public get<T>(key: string): T | undefined {
const cachedObject = this.cache.get(key);
if (cachedObject === undefined) {
if (this.debug) {
console.log(`Cache miss for key: ${key}`);
}
return undefined;
}
if (this.debug) {
console.log(`Retrieved ${key} from cache, value: ${JSON.stringify(cachedObject)}`);
}
return cachedObject.value as T;
}
/**
* Sets an object in the cache
*
* @param key the cache key
* @param value the object
*/
public set<T>(key: string, value: T): void {
this.cache.set(key, {
value,
timestamp: Date.now(),
});
if (this.debug) {
console.log(`Inserted ${key} into cache, value: ${JSON.stringify(value)}`);
}
}
/**
* Checks if an object is in the cache
*
* @param key the cache key
*/
public has(key: string): boolean {
return this.cache.has(key);
}
/**
* Removes an object from the cache
*
* @param key the cache key
*/
public remove(key: string): void {
this.cache.delete(key);
if (this.debug) {
console.log(`Removed ${key} from cache`);
}
}
}

View File

@ -8,6 +8,7 @@ import ScoreSaberLeaderboardToken from "../../types/token/scoresaber/score-saber
import ScoreSaberLeaderboardScoresPageToken from "../../types/token/scoresaber/score-saber-leaderboard-scores-page-token";
import { clamp, lerp } from "../../utils/math-utils";
import { CurvePoint } from "../../utils/curve-point";
import { SSRCache } from "../../cache";
const API_BASE = "https://scoresaber.com/api";
@ -28,6 +29,10 @@ const LOOKUP_LEADERBOARD_SCORES_ENDPOINT = `${API_BASE}/leaderboard/by-id/:id/sc
const STAR_MULTIPLIER = 42.117208413;
const playerCache = new SSRCache({
ttl: 60 * 30, // 30 minutes
});
class ScoreSaberService extends Service {
private curvePoints = [
new CurvePoint(0, 0),
@ -98,9 +103,13 @@ class ScoreSaberService extends Service {
* Looks up a player by their ID.
*
* @param playerId the ID of the player to look up
* @param cache whether to use the local cache
* @returns the player that matches the ID, or undefined
*/
public async lookupPlayer(playerId: string): Promise<ScoreSaberPlayerToken | undefined> {
public async lookupPlayer(playerId: string, cache: boolean = false): Promise<ScoreSaberPlayerToken | undefined> {
if (cache && playerCache.has(playerId)) {
return playerCache.get(playerId);
}
const before = performance.now();
this.log(`Looking up player "${playerId}"...`);
const token = await this.fetch<ScoreSaberPlayerToken>(LOOKUP_PLAYER_ENDPOINT.replace(":id", playerId));
@ -108,6 +117,9 @@ class ScoreSaberService extends Service {
return undefined;
}
this.log(`Found player "${playerId}" in ${(performance.now() - before).toFixed(0)}ms`);
if (cache) {
playerCache.set(playerId, token);
}
return token;
}