const express = require('express'); const http = require('http'); const { Server } = require('socket.io'); const cors = require('cors'); const { GameManager } = require('./game/GameManager'); const app = express(); app.use(cors()); app.use(express.json()); const server = http.createServer(app); const io = new Server(server, { cors: { origin: '*', methods: ['GET', 'POST'] }, }); const gm = new GameManager(); app.get('/api/cards', (req, res) => { res.json(gm.getCardList()); }); app.get('/api/starter-decks', (req, res) => { res.json(gm.getStarterDecks()); }); app.get('/api/rooms', (req, res) => { res.json(gm.getRooms()); }); io.on('connection', (socket) => { let playerName = 'Player'; let playerId = socket.id; console.log(`Player connected: ${playerId}`); socket.on('setName', (name) => { playerName = name || 'Player'; socket.emit('nameSet', { id: playerId, name: playerName }); }); socket.on('createRoom', (data) => { const room = gm.createRoom(playerId, playerName, data?.roomName); socket.join(room.id); socket.emit('roomCreated', room); io.emit('roomsUpdated', gm.getRooms()); }); socket.on('joinRoom', (data) => { const result = gm.joinRoom(data.roomId, playerId, playerName); if (result.error) { socket.emit('error', { message: result.error }); return; } socket.join(data.roomId); socket.emit('roomJoined', result.room); io.to(data.roomId).emit('roomUpdated', result.room); io.emit('roomsUpdated', gm.getRooms()); }); socket.on('leaveRoom', () => { const currentRoomId = gm.playerRooms.get(playerId); const result = gm.leaveRoom(playerId); if (result.error) return; if (currentRoomId) { socket.leave(currentRoomId); if (!result.roomDeleted) { io.to(currentRoomId).emit('roomUpdated', result.room); } } io.emit('roomsUpdated', gm.getRooms()); }); socket.on('selectDeck', (data) => { const result = gm.selectDeck(playerId, data.deckColor); if (result.error) { socket.emit('error', { message: result.error }); return; } const roomId = gm.playerRooms.get(playerId); io.to(roomId).emit('roomUpdated', result.room); }); socket.on('selectCustomDeck', (data) => { const result = gm.selectCustomDeck(playerId, data.cardIds); if (result.error) { socket.emit('error', { message: result.error }); return; } const roomId = gm.playerRooms.get(playerId); io.to(roomId).emit('roomUpdated', result.room); }); socket.on('startGame', () => { const roomId = gm.playerRooms.get(playerId); if (!roomId) { socket.emit('error', { message: 'Not in a room' }); return; } const room = gm.getRoomDetails(roomId); if (!room || room.hostId !== playerId) { socket.emit('error', { message: 'Only the host can start the game' }); return; } const result = gm.startGame(roomId); if (result.error) { socket.emit('error', { message: result.error }); return; } room.players.forEach((p) => { const playerState = gm.getGameState(result.gameId, p.id); io.to(p.id).emit('gameStarted', { gameId: result.gameId, state: playerState }); }); }); socket.on('gameAction', (data) => { const roomId = gm.playerRooms.get(playerId); if (!roomId) return; const room = gm.getRoomDetails(roomId); if (!room || !room.gameId) return; const result = gm.handleGameAction(room.gameId, playerId, data.action); if (result.error) { socket.emit('actionError', { message: result.error }); return; } room.players.forEach((p) => { const playerState = gm.getGameState(room.gameId, p.id); io.to(p.id).emit('gameStateUpdated', playerState); }); if (result.needsResponse) { socket.emit('needsResponse', { type: result.needsResponse, options: result.options, }); } }); socket.on('getRooms', () => { socket.emit('roomsUpdated', gm.getRooms()); }); socket.on('disconnect', () => { console.log(`Player disconnected: ${playerId}`); const roomId = gm.playerRooms.get(playerId); if (roomId) { const result = gm.leaveRoom(playerId); if (result && !result.error) { if (!result.roomDeleted) { io.to(roomId).emit('roomUpdated', result.room); io.to(roomId).emit('playerDisconnected', { playerId, playerName }); } io.emit('roomsUpdated', gm.getRooms()); } } }); }); const PORT = process.env.PORT || 3001; server.listen(PORT, '0.0.0.0', () => { const os = require('os'); const interfaces = os.networkInterfaces(); let lanIp = 'localhost'; for (const iface of Object.values(interfaces)) { for (const alias of iface) { if (alias.family === 'IPv4' && !alias.internal) { lanIp = alias.address; break; } } } console.log(`\n Arcane Duels Server running on:`); console.log(` Local: http://localhost:${PORT}`); console.log(` Network: http://${lanIp}:${PORT}\n`); });