import { Server } from "socket.io"; import axios from "axios"; import { handleConnectionEvents } from "./events/connection.js"; import { addUser, removeUser } from "./state/connection.js"; import gameEvents from "./events/game.js"; import matchEvents from "./events/match.js"; export const server = { io: null, }; const API_URL = "http://localhost:8000/api/v1"; const disconnectTimers = new Map(); const RECONNECT_GRACE_PERIOD = 1 * 60 * 1000; const userId = (socket) => socket.user && socket.user.id; export const serverStart = (port) => { server.io = new Server(port, { cors: { origin: "*", }, }); server.io.use(async (socket, next) => { let token = socket.handshake.auth.token; if (!token) { return next(new Error("Authentication error: No token provided")); } if (!token.startsWith("Bearer ")) { token = "Bearer " + token; } try { const response = await axios.get(`${API_URL}/users/me`, { headers: { Authorization: token, Accept: "application/json" }, }); const user = response.data.data || response.data; if (!user || !user.id) { return next(new Error("Authentication error: User data not found")); } socket.user = user; socket.token = token; socket.handshake.auth.token = token; if (disconnectTimers.has(user.id)) { console.log(`[Connection] User ${user.id} reconnected! Cancelling surrender timer.`); clearTimeout(disconnectTimers.get(user.id)); disconnectTimers.delete(user.id); } addUser(socket.id, user); next(); } catch (error) { if (error.response) { console.error( "❌ Erro Laravel:", error.response.status, error.response.data ); } else { console.error("❌ Erro Rede:", error.message); } next(new Error("Authentication error: Invalid token")); } }); server.io.on("connection", (socket) => { gameEvents(server.io, socket); matchEvents(server.io, socket); handleConnectionEvents(server.io, socket); socket.on("notify_disconnect", () => { socket.isVoluntaryDisconnect = true; }); socket.on("disconnect", () => { removeUser(socket.id); if (socket.isVoluntaryDisconnect) { console.log(`[Disconnect] User ${userId} disconnected voluntarily. No surrender needed.`); return; } if (userId) { console.log(`[Disconnect] User ${userId} disconnected. Waiting ${RECONNECT_GRACE_PERIOD / 1000}s...`); const timer = setTimeout(async () => { console.log(`[Timeout] User ${userId} did not return. Forcing surrender.`); const gameId = socket.activeGameId; if (gameId) { try { await axios.post(`${API_URL}/games/${gameId}/resign`, {}, { headers: { Authorization: socket.handshake.auth.token, Accept: "application/json" } }); server.io.to(gameId).emit("game_over", { winner: "opponent", reason: "disconnect" }); console.log(`[Timeout] Successfully resigned game ${gameId} for user ${userId}`); } catch (err) { console.error(`[Timeout] Failed to resign game for user ${userId}:`, err.message); } } disconnectTimers.delete(userId); }, RECONNECT_GRACE_PERIOD); disconnectTimers.set(userId, timer); } }); }); };