Initial commit

This commit is contained in:
2026-03-13 16:01:39 -04:00
commit 27e8c9a6fe
22 changed files with 8681 additions and 0 deletions

181
server/index.js Normal file
View File

@@ -0,0 +1,181 @@
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`);
});