import { Server } from "@rahoot/common/types/game/socket" import { inviteCodeValidator } from "@rahoot/common/validators/auth" import env from "@rahoot/socket/env" import Config from "@rahoot/socket/services/config" import Game from "@rahoot/socket/services/game" import Registry from "@rahoot/socket/services/registry" import { withGame } from "@rahoot/socket/utils/game" import { Server as ServerIO } from "socket.io" const io: Server = new ServerIO({ cors: { origin: [env.WEB_ORIGIN], }, }) Config.init() const registry = Registry.getInstance() const port = 3001 console.log(`Socket server running on port ${port}`) io.listen(Number(port)) io.on("connection", (socket) => { console.log( `A user connected: socketId: ${socket.id}, clientId: ${socket.handshake.auth.clientId}` ) socket.on("player:reconnect", ({ gameId }) => { const game = registry.getPlayerGame(gameId, socket.handshake.auth.clientId) if (game) { game.reconnect(socket) return } socket.emit("game:reset", "Game not found") }) socket.on("manager:reconnect", ({ gameId }) => { const game = registry.getManagerGame(gameId, socket.handshake.auth.clientId) if (game) { game.reconnect(socket) return } socket.emit("game:reset", "Game expired") }) socket.on("manager:auth", (password) => { try { const config = Config.game() if (password !== config.managerPassword) { socket.emit("manager:errorMessage", "Invalid password") return } socket.emit("manager:quizzList", Config.quizz()) } catch (error) { console.error("Failed to read game config:", error) socket.emit("manager:errorMessage", "Failed to read game config") } }) socket.on("game:create", (quizzId) => { const quizzList = Config.quizz() const quizz = quizzList.find((q) => q.id === quizzId) if (!quizz) { socket.emit("game:errorMessage", "Quizz not found") return } const game = new Game(io, socket, quizz) registry.addGame(game) }) socket.on("player:join", (inviteCode) => { const result = inviteCodeValidator.safeParse(inviteCode) if (result.error) { socket.emit("game:errorMessage", result.error.issues[0].message) return } const game = registry.getGameByInviteCode(inviteCode) if (!game) { socket.emit("game:errorMessage", "Game not found") return } socket.emit("game:successRoom", game.gameId) }) socket.on("player:login", ({ gameId, data }) => withGame(gameId, socket, (game) => game.join(socket, data.username)) ) socket.on("manager:kickPlayer", ({ gameId, data }) => withGame(gameId, socket, (game) => game.kickPlayer(socket, data.playerId)) ) socket.on("manager:startGame", ({ gameId }) => withGame(gameId, socket, (game) => game.start(socket)) ) socket.on("player:selectedAnswer", ({ gameId, data }) => withGame(gameId, socket, (game) => game.selectAnswer(socket, data.answerKey) ) ) socket.on("manager:abortQuiz", ({ gameId }) => withGame(gameId, socket, (game) => game.abortRound(socket)) ) socket.on("manager:nextQuestion", ({ gameId }) => withGame(gameId, socket, (game) => game.nextRound(socket)) ) socket.on("manager:showLeaderboard", ({ gameId }) => withGame(gameId, socket, (game) => game.showLeaderboard()) ) socket.on("disconnect", () => { console.log(`A user disconnected : ${socket.id}`) const managerGame = registry.getGameByManagerSocketId(socket.id) if (managerGame) { managerGame.manager.connected = false registry.markGameAsEmpty(managerGame) if (!managerGame.started) { console.log("Reset game (manager disconnected)") managerGame.abortCooldown() io.to(managerGame.gameId).emit("game:reset", "Manager disconnected") registry.removeGame(managerGame.gameId) return } } const game = registry.getGameByPlayerSocketId(socket.id) if (!game) { return } const player = game.players.find((p) => p.id === socket.id) if (!player) { return } if (!game.started) { game.players = game.players.filter((p) => p.id !== socket.id) io.to(game.manager.id).emit("manager:removePlayer", player.id) io.to(game.gameId).emit("game:totalPlayers", game.players.length) console.log(`Removed player ${player.username} from game ${game.gameId}`) return } player.connected = false io.to(game.gameId).emit("game:totalPlayers", game.players.length) }) }) process.on("SIGINT", () => { Registry.getInstance().cleanup() process.exit(0) }) process.on("SIGTERM", () => { Registry.getInstance().cleanup() process.exit(0) })