Skip to content

PablisMB/guessmyalgorithm-server

Repository files navigation

GuessMyAlgorithm — Server

Backend for GuessMyAlgorithm, a real-time multiplayer social game where friends connect their online accounts and guess which player a piece of activity belongs to.

Each round, the game surfaces a real activity from one player's connected accounts — a Spotify track, a YouTube video, a Steam session, an AniList watch — and everyone else has to guess whose it is. Points go to whoever guesses correctly, and to the player who stumped everyone.

Stack: Node.js · Express · Socket.IO · Supabase (PostgreSQL) · Redis


Architecture

Client (React Native)
  │
  ├── HTTP (Axios + Supabase JWT)   →  REST API routes
  └── WebSocket (Socket.IO)         →  Real-time game engine
                                          │
                                    ┌─────▼──────────────────┐
                                    │  gameLoop.js            │
                                    │  context → guess →      │
                                    │  lock → reveal →        │
                                    │  reaction → score       │
                                    └─────┬──────────────────┘
                                          │
                               Supabase (PostgreSQL)
                               Redis (optional, pub/sub)

The server owns all game state. Clients only send votes and reactions — they never drive phase transitions. This keeps all players synchronized regardless of connection quality and makes cheating structurally impossible.


Key Modules

src/sockets/gameLoop.js

Phase-driven round engine. Each round goes through:

context (show activity) → guess (players vote) → lock (timer end)
  → reveal (show answer) → reaction (emoji overlay) → score → next round

Flash rounds interrupt normal play at the game midpoint:

flash_input (25s) → flash_coin (8s) → flash_reveal (10s)

Transitions are setTimeout-based on the server. Per-socket rate limiters prevent guess spam (5 guesses/15 s) and reaction spam (5 reactions/10 s).

src/services/roundGenerator.js

Picks which player's activity to use each round (balances exposure across players), calls the appropriate fetcher, and builds the round payload sent to all clients.

Supported platforms: Spotify · YouTube · YouTube Music · Deezer · AniList · TikTok · Twitch · Discord · Steam · Valorant (Riot) · Lichess · Chess.com · Twitter/X

src/services/dataFetcher.js

Per-platform fetch functions that normalize activity data into a common shape: { type, label, sublabel, icon, metadata }. Encrypted OAuth tokens are decrypted on the fly using utils/encryption.js.

src/sockets/roomManager.js

Room lifecycle: join, leave, play-again. Enforces max players, broadcasts player list updates, handles graceful disconnect and reconnect (players auto-rejoin their room on reconnect within the session).

src/config/gameState.js

In-memory store for active room state. Written on every phase transition; Supabase is only written at round end for stats and feed events. Redis pub/sub is used when scaling horizontally.


Tech Stack

Layer Technology
HTTP framework Express 4
Real-time Socket.IO 4
Auth Supabase JWT + custom middleware
Database PostgreSQL via Supabase
Caching / pub-sub Redis (Upstash, optional)
OAuth 13 platform integrations
Security Helmet · CORS · express-rate-limit · AES encryption
Testing Jest · Supertest
Process management Nodemon (dev) · Node (prod)

Getting Started

Prerequisites

  • Node.js 18+
  • A Supabase project (free tier works)
  • At least one OAuth app configured (Spotify or AniList are easiest)

Redis is optional — the server falls back to in-memory state if REDIS_URL is not set.

Install

npm install

Environment

cp .env.example .env

Minimum required variables:

Variable Where to get it
SUPABASE_URL Supabase → Settings → API
SUPABASE_ANON_KEY Supabase → Settings → API
DATABASE_URL Supabase → Settings → Database → Connection string (Transaction Pooler)
JWT_SECRET Any random 32+ character string
ENCRYPTION_KEY Any random 32 character string
One OAuth pair e.g. SPOTIFY_CLIENT_ID + SPOTIFY_CLIENT_SECRET from Spotify Developer Dashboard

All other OAuth integrations are optional — the game works with any single connected service per player.

Database

npm run db:migrate

Run

npm run dev        # development (nodemon)
npm start          # production

Server starts on http://localhost:3000. Health check: GET /api/health.


API Overview

POST   /api/auth/register
POST   /api/auth/login

GET    /api/oauth/:service          →  redirect to provider
GET    /api/oauth/:service/callback →  exchange code, store token

GET    /api/rooms                   →  list open rooms
POST   /api/rooms                   →  create room
GET    /api/rooms/:id               →  room details

GET    /api/services                →  connected services for current user
GET    /api/friends                 →  friend list + online status
GET    /api/stats                   →  player statistics
GET    /api/shop                    →  cosmetics catalogue
POST   /api/shop/buy                →  purchase item
GET    /api/feed                    →  social activity feed

Socket Events

Client → Server
  join-room        { roomId, userId }
  start-game       { roomId }
  vote             { roomId, targetId }
  reaction         { roomId, emoji }
  flash-input      { roomId, text }
  flash-vote       { roomId, choice }

Server → Client
  game-started     { round, players, mode }
  phase-change     { phase, timeLeft }
  player-voted     { count, total }
  round-reveal     { correct, scores }
  game-over        { finalScores, rewards }
  friend-online    { userId }

Project Structure

src/
├── index.js              Express app, middleware, route mounting
├── config/
│   ├── database.js       PostgreSQL pool (Supabase)
│   ├── gameState.js      In-memory room & session state
│   ├── redis.js          Redis client (optional)
│   └── onlineUsers.js    Presence tracking
├── middleware/
│   └── auth.js           Supabase JWT verification
├── routes/               REST route handlers
│   └── oauth/            Per-platform OAuth callbacks
├── services/
│   ├── dataFetcher.js    Fetch & normalize platform activity data
│   ├── roundGenerator.js Select player + activity, build round payload
│   └── eventGenerator.js Social feed event descriptions (i18n keys)
├── sockets/
│   ├── index.js          Socket auth, connection lifecycle
│   ├── gameLoop.js       Phase engine
│   ├── impostorLoop.js   Impostor mode variant
│   ├── roomManager.js    Room join/leave/play-again
│   └── presenceUtils.js  Friend online/offline notifications
├── jobs/
│   ├── tiktokPoller.js   Periodic TikTok data sync
│   ├── roomCleanup.js    Idle room garbage collection
│   └── startupRecovery.js Restore in-progress rooms on restart
└── utils/
    ├── encryption.js     AES encrypt/decrypt for stored OAuth tokens
    └── socketRateLimit.js Per-socket rate limiter factory

License

MIT

About

Real-time multiplayer social game backend — Node.js, Express, Socket.IO

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors