2025-02-12 00:01:51 +02:00
|
|
|
import type { AlbumSolveState, Player } from '$lib/types';
|
2025-01-19 05:24:56 +02:00
|
|
|
|
|
|
|
export class PlayerState {
|
2025-01-21 11:42:52 +02:00
|
|
|
players = $state<Player[]>([]);
|
2025-02-12 00:01:51 +02:00
|
|
|
private lastAccessed: Map<string, number> = new Map();
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
constructor() {
|
|
|
|
// Clean up expired sessions every hour
|
|
|
|
setInterval(() => this.cleanup(), 1000 * 60 * 60);
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
private updateLastAccessed(id: string) {
|
|
|
|
this.lastAccessed.set(id, Date.now());
|
|
|
|
}
|
|
|
|
|
|
|
|
private findPlayer(id: string): Player | undefined {
|
|
|
|
this.updateLastAccessed(id);
|
|
|
|
return this.players.find((player) => player.id === id);
|
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
private checkSolution(solution: AlbumSolveState[], submission: AlbumSolveState[]) {
|
|
|
|
if (!solution || !submission) {
|
|
|
|
return false;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
for (const solve of solution) {
|
|
|
|
const search = submission.filter((album) => album.name === solve.name);
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
if (!search) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const matching = search.at(0);
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
if (!matching) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matching.image !== solve.image) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matching.artists !== solve.artists) {
|
|
|
|
return false;
|
|
|
|
}
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
return true;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
newPlayer(id: string) {
|
|
|
|
this.updateLastAccessed(id);
|
|
|
|
this.players.push({ id: id, stage: 0, highscore: 0, playing: true, albums: [] });
|
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
nextStage(id: string) {
|
|
|
|
const player = this.findPlayer(id);
|
|
|
|
if (player) {
|
|
|
|
player.stage += 1;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-02-12 00:01:51 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
restart(id: string) {
|
|
|
|
const player = this.findPlayer(id);
|
|
|
|
if (player) {
|
|
|
|
player.stage = 0;
|
|
|
|
player.playing = true;
|
|
|
|
}
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
score(id: string, submission: AlbumSolveState[]) {
|
|
|
|
if (!submission) return false;
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
const player = this.findPlayer(id);
|
|
|
|
if (!player) return false;
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
if (this.checkSolution(player.albums, submission)) {
|
2025-01-21 11:42:52 +02:00
|
|
|
player.stage += 1;
|
2025-02-12 00:01:51 +02:00
|
|
|
return true;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
|
2025-01-21 11:42:52 +02:00
|
|
|
player.playing = false;
|
|
|
|
if (player.stage > player.highscore) {
|
|
|
|
player.highscore = player.stage;
|
|
|
|
}
|
2025-02-12 00:01:51 +02:00
|
|
|
|
|
|
|
return true;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getHighscore(id: string) {
|
2025-02-12 00:01:51 +02:00
|
|
|
const player = this.findPlayer(id);
|
|
|
|
return player?.highscore;
|
|
|
|
}
|
2025-01-21 11:42:52 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
getStage(id: string) {
|
|
|
|
const player = this.findPlayer(id);
|
|
|
|
return player?.stage;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getPlaying(id: string) {
|
2025-02-12 00:01:51 +02:00
|
|
|
const player = this.findPlayer(id);
|
|
|
|
return player?.playing;
|
|
|
|
}
|
2025-01-21 11:42:52 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
setPlaying(id: string, playing: boolean) {
|
|
|
|
const player = this.findPlayer(id);
|
|
|
|
if (player) {
|
|
|
|
player.playing = playing;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
getAlbums(id: string) {
|
|
|
|
const player = this.findPlayer(id);
|
|
|
|
return player?.albums;
|
|
|
|
}
|
2025-01-21 11:42:52 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
setAlbums(id: string, albums: AlbumSolveState[]) {
|
|
|
|
const player = this.findPlayer(id);
|
|
|
|
if (player) {
|
|
|
|
player.albums = albums;
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-02-12 00:01:51 +02:00
|
|
|
}
|
2025-01-21 11:42:52 +02:00
|
|
|
|
2025-02-12 00:01:51 +02:00
|
|
|
// 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;
|
|
|
|
});
|
2025-01-21 11:42:52 +02:00
|
|
|
}
|
2025-01-19 05:24:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const playerState = new PlayerState();
|