diff --git a/src/lib/server/pakubiiti/AlbumState.svelte.ts b/src/lib/server/pakubiiti/AlbumState.svelte.ts deleted file mode 100644 index 5fb7c85..0000000 --- a/src/lib/server/pakubiiti/AlbumState.svelte.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { AlbumSolveState } from '$lib/types'; - -class AlbumState { - private albums: AlbumSolveState[] | undefined = undefined; - - setAlbums(data: AlbumSolveState[]) { - if (!data) { - return; - } - - this.albums = data; - } - - checkSolve(data: AlbumSolveState[]) { - if (!data || !this.albums) { - return false; - } - - for (const solve of data) { - const search = this.albums.filter((album) => album.name === solve.name); - - if (!search) { - return false; - } - - const matching = search.at(0); - - if (!matching) { - return false; - } - - if (matching.image !== solve.image) { - return false; - } - - if (matching.artists !== solve.artists) { - return false; - } - } - - return true; - } -} - -export const albumState = new AlbumState(); diff --git a/src/lib/server/pakubiiti/PlayerState.svelte.ts b/src/lib/server/pakubiiti/PlayerState.svelte.ts index 2d3448d..3b6d8f4 100644 --- a/src/lib/server/pakubiiti/PlayerState.svelte.ts +++ b/src/lib/server/pakubiiti/PlayerState.svelte.ts @@ -1,90 +1,139 @@ -import type { Player } from '$lib/types'; +import type { AlbumSolveState, Player } from '$lib/types'; export class PlayerState { players = $state([]); + private lastAccessed: Map = new Map(); - newPlayer(id: string) { - this.players.push({ id: id, stage: 0, highscore: 0, playing: true }); + constructor() { + // Clean up expired sessions every hour + setInterval(() => this.cleanup(), 1000 * 60 * 60); } - getStage(id: string) { - const player = this.players.find((player) => player.id === id); + private updateLastAccessed(id: string) { + this.lastAccessed.set(id, Date.now()); + } - if (!player) { - return undefined; + private findPlayer(id: string): Player | undefined { + this.updateLastAccessed(id); + return this.players.find((player) => player.id === id); + } + + private checkSolution(solution: AlbumSolveState[], submission: AlbumSolveState[]) { + if (!solution || !submission) { + return false; } - return player.stage; + for (const solve of solution) { + const search = submission.filter((album) => album.name === solve.name); + + if (!search) { + return false; + } + + const matching = search.at(0); + + if (!matching) { + return false; + } + + if (matching.image !== solve.image) { + return false; + } + + if (matching.artists !== solve.artists) { + return false; + } + } + + return true; + } + + newPlayer(id: string) { + this.updateLastAccessed(id); + this.players.push({ id: id, stage: 0, highscore: 0, playing: true, albums: [] }); } nextStage(id: string) { - const player = this.players.find((player) => player.id === id); - - if (!player) { - return; + const player = this.findPlayer(id); + if (player) { + player.stage += 1; } - - player.stage += 1; } restart(id: string) { - const player = this.players.find((player) => player.id === id); - - if (!player) { - return; + const player = this.findPlayer(id); + if (player) { + player.stage = 0; + player.playing = true; } - - player.stage = 0; - player.playing = true; } - score(id: string, won: boolean) { - const player = this.players.find((player) => player.id === id); + score(id: string, submission: AlbumSolveState[]) { + if (!submission) return false; - if (!player) { - return; - } + const player = this.findPlayer(id); + if (!player) return false; - if (won) { + if (this.checkSolution(player.albums, submission)) { player.stage += 1; - return; + return true; } player.playing = false; - if (player.stage > player.highscore) { player.highscore = player.stage; } + + return true; } getHighscore(id: string) { - const player = this.players.find((player) => player.id === id); + const player = this.findPlayer(id); + return player?.highscore; + } - if (!player) { - return undefined; - } - - return player.highscore; + getStage(id: string) { + const player = this.findPlayer(id); + return player?.stage; } getPlaying(id: string) { - const player = this.players.find((player) => player.id === id); - - if (!player) { - return undefined; - } - - return player.playing; + const player = this.findPlayer(id); + return player?.playing; } setPlaying(id: string, playing: boolean) { - const player = this.players.find((player) => player.id === id); - - if (!player) { - return; + const player = this.findPlayer(id); + if (player) { + player.playing = playing; } + } - player.playing = playing; + getAlbums(id: string) { + const player = this.findPlayer(id); + return player?.albums; + } + + setAlbums(id: string, albums: AlbumSolveState[]) { + const player = this.findPlayer(id); + if (player) { + player.albums = albums; + } + } + + // Clean up players that haven't been accessed in 24 hours + private cleanup(): void { + const now = Date.now(); + const expiryTime = 24 * 60 * 60 * 1000; // 24 hours + + this.players = this.players.filter((player) => { + const lastAccess = this.lastAccessed.get(player.id); + if (!lastAccess || now - lastAccess > expiryTime) { + this.lastAccessed.delete(player.id); + return false; + } + return true; + }); } } diff --git a/src/lib/types.ts b/src/lib/types.ts index 29e7df1..a3b8db0 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -41,6 +41,7 @@ export type Player = { stage: number; highscore: number; playing: boolean; + albums: AlbumSolveState[]; }; export interface TimeRemaining { diff --git a/src/routes/api/pakubiiti/getAlbums/[count]/+server.ts b/src/routes/api/pakubiiti/getAlbums/[count]/+server.ts index 3e37e80..6d23b9e 100644 --- a/src/routes/api/pakubiiti/getAlbums/[count]/+server.ts +++ b/src/routes/api/pakubiiti/getAlbums/[count]/+server.ts @@ -1,7 +1,6 @@ import type { AlbumSolveState } from '$lib/types'; import { error, json } from '@sveltejs/kit'; -import { albumState } from '$lib/server/pakubiiti/AlbumState.svelte'; import { spotifyAPI } from '$lib/server/pakubiiti/Spotify.svelte'; const maxTries = 10; @@ -30,12 +29,8 @@ export async function GET({ params }) { } if (albums.length !== count) { - albumState.setAlbums([]); - return error(500, "Couldn't get albums from Spotify."); } - albumState.setAlbums(albums); - return json({ albums: albums }); } diff --git a/src/routes/vinge/pakubiiti/+page.server.ts b/src/routes/vinge/pakubiiti/+page.server.ts index ba8f98f..bcb38ea 100644 --- a/src/routes/vinge/pakubiiti/+page.server.ts +++ b/src/routes/vinge/pakubiiti/+page.server.ts @@ -4,7 +4,6 @@ import type { AlbumSolveState } from '$lib/types'; import { nanoid } from 'nanoid'; import { shuffleArray } from '$lib/utils'; -import { albumState } from '$lib/server/pakubiiti/AlbumState.svelte'; import { playerState } from '$lib/server/pakubiiti/PlayerState.svelte'; import { ratelimit } from '$lib/server/redis'; @@ -92,6 +91,8 @@ export const load: PageServerLoad = async (event) => { value: album.artists })); + playerState.setAlbums(user, data.albums); + return { names: shuffleArray(albumNames), images: shuffleArray(albumImages), @@ -133,9 +134,7 @@ export const actions = { state.push({ name: name, image: image, artists: artists }); } - const solved = albumState.checkSolve(state); - - playerState.score(user, solved); + const solved = playerState.score(user, state); return { solved: solved }; },