/** * Game Server - Based on Client LocalServer Infrastructure * Handles real-time multiplayer game instances and gameplay */ const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const cors = require('cors'); const dotenv = require('dotenv'); require('dotenv').config(); const logger = require('./utils/logger'); const connectDB = require('./config/database'); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"], methods: ["GET", "POST"], credentials: true } }); // Game state const gameInstances = new Map(); const connectedClients = new Map(); // Middleware app.use(cors({ origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"], credentials: true })); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime(), mode: 'game-server', activeInstances: gameInstances.size, connectedClients: connectedClients.size }); }); // API version endpoint app.get('/api/ssc/version', (req, res) => { res.status(200).json({ version: '1.0.0', service: 'galaxystrikeonline-game-server', timestamp: new Date().toISOString(), mode: 'multiplayer' }); }); // Game data endpoints (similar to LocalServer) app.post('/api/game/player/:id/save', (req, res) => { const playerId = req.params.id; const playerData = req.body; // Store player data in game instance if (connectedClients.has(playerId)) { connectedClients.get(playerId).playerData = playerData; // Broadcast to other players in same instance const instanceId = connectedClients.get(playerId).instanceId; if (gameInstances.has(instanceId)) { io.to(instanceId).emit('playerDataUpdated', { playerId, timestamp: Date.now() }); } } res.status(200).json({ success: true, message: 'Player data saved to game server' }); }); app.get('/api/game/player/:id', (req, res) => { const playerId = req.params.id; if (connectedClients.has(playerId)) { const playerData = connectedClients.get(playerId).playerData; res.status(200).json({ success: true, player: playerData }); } else { res.status(404).json({ success: false, error: 'Player not connected to game server' }); } }); // Game system routes app.use('/api/crafting', require('./routes/crafting')); app.use('/api/quests', require('./routes/quests')); app.use('/api/skills', require('./routes/skills')); app.use('/api/ships', require('./routes/ships')); app.use('/api/idle', require('./routes/idle')); app.use('/api/dungeons', require('./routes/dungeons')); app.use('/api/base', require('./routes/base')); // Socket.IO handlers (based on LocalServer) io.on('connection', (socket) => { console.log('[GAME SERVER] Client connected:', socket.id); connectedClients.set(socket.id, { connectedAt: Date.now(), playerData: null, instanceId: null }); // Update player count on API server when new player connects updatePlayerCountOnAPI(); // Authentication (similar to LocalServer) socket.on('authenticate', (data) => { console.log('[GAME SERVER] Authenticating client:', socket.id, data); // In production, validate with API server socket.emit('authenticated', { success: true, user: { id: data.userId || socket.id, username: data.username || 'Game Player', token: 'game-token-' + Date.now() } }); }); // Game data events (similar to LocalServer) socket.on('saveGameData', (data) => { console.log('[GAME SERVER] Saving game data for:', socket.id); if (connectedClients.has(socket.id)) { connectedClients.get(socket.id).playerData = data; } socket.emit('gameDataSaved', { success: true, timestamp: Date.now() }); }); socket.on('loadGameData', (data) => { console.log('[GAME SERVER] Loading game data for:', socket.id); const playerData = connectedClients.get(socket.id)?.playerData || {}; socket.emit('gameDataLoaded', { success: true, data: playerData }); }); // Game-specific events socket.on('joinGameInstance', (data) => { const { instanceId } = data; if (!gameInstances.has(instanceId)) { gameInstances.set(instanceId, { id: instanceId, players: new Set(), createdAt: Date.now() }); } const instance = gameInstances.get(instanceId); instance.players.add(socket.id); connectedClients.get(socket.id).instanceId = instanceId; socket.join(instanceId); socket.emit('joinedGameInstance', { instanceId, playerCount: instance.players.size }); // Notify other players socket.to(instanceId).emit('playerJoinedInstance', { playerId: socket.id, playerCount: instance.players.size }); }); socket.on('leaveGameInstance', (data) => { const clientData = connectedClients.get(socket.id); if (clientData && clientData.instanceId) { const instance = gameInstances.get(clientData.instanceId); if (instance) { instance.players.delete(socket.id); socket.leave(clientData.instanceId); // Clean up empty instances if (instance.players.size === 0) { gameInstances.delete(clientData.instanceId); } // Notify other players socket.to(clientData.instanceId).emit('playerLeftInstance', { playerId: socket.id, playerCount: instance.players.size }); } clientData.instanceId = null; } }); socket.on('gameAction', (data) => { const clientData = connectedClients.get(socket.id); if (clientData && clientData.instanceId) { // Broadcast game action to other players in same instance socket.to(clientData.instanceId).emit('gameAction', { playerId: socket.id, action: data, timestamp: Date.now() }); } }); socket.on('disconnect', () => { console.log('[GAME SERVER] Client disconnected:', socket.id); const clientData = connectedClients.get(socket.id); if (clientData && clientData.instanceId) { const instance = gameInstances.get(clientData.instanceId); if (instance) { instance.players.delete(socket.id); // Clean up empty instances if (instance.players.size === 0) { gameInstances.delete(clientData.instanceId); } // Notify other players socket.to(clientData.instanceId).emit('playerLeftInstance', { playerId: socket.id, playerCount: instance.players.size }); } } connectedClients.delete(socket.id); // Update player count on API server updatePlayerCountOnAPI(); }); }); // Update player count on API server async function updatePlayerCountOnAPI() { try { const apiServerUrl = 'http://localhost:3001'; const currentPlayers = connectedClients.size; console.log(`[GAME SERVER] Updating player count: ${currentPlayers} players`); const response = await fetch(`${apiServerUrl}/api/servers/update-status/devgame-server-3002`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ currentPlayers: currentPlayers, status: currentPlayers > 0 ? 'active' : 'waiting' }) }); if (response.ok) { const result = await response.json(); console.log(`[GAME SERVER] Player count updated:`, result); } else { console.error(`[GAME SERVER] Failed to update player count: ${response.status}`); } } catch (error) { console.error('[GAME SERVER] Error updating player count:', error); } } // Start server const PORT = process.env.PORT || 3002; async function startServer() { try { // Connect to database await connectDB(); server.listen(PORT, async () => { console.log(`[GAME SERVER] Game server running on port ${PORT}`); console.log(`[GAME SERVER] Socket.IO ready for multiplayer connections`); // Register with API server await registerWithAPIServer(); // Start periodic player count updates setInterval(updatePlayerCountOnAPI, 30000); // Update every 30 seconds }); } catch (error) { console.error('[GAME SERVER] Failed to start server:', error); process.exit(1); } } // Register this GameServer with the API server async function registerWithAPIServer() { try { // Force use of local API server for development const apiServerUrl = 'http://localhost:3001'; const gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; const serverData = { serverId: `devgame-server-${PORT}`, name: 'Dev Game Server', type: 'public', // Must be 'public' or 'private' according to model region: 'Europe', maxPlayers: 100, // Hardcoded to allow 100 players currentPlayers: 0, gameServerUrl: gameServerUrl, owner: { userId: 'developer', username: 'Developer' } }; console.log(`[GAME SERVER] Registering with API server at ${apiServerUrl}`); console.log(`[GAME SERVER] Server data:`, serverData); const response = await fetch(`${apiServerUrl}/api/servers/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(serverData) }); if (response.ok) { const result = await response.json(); console.log(`[GAME SERVER] Successfully registered with API server:`, result); } else { const errorText = await response.text(); console.error(`[GAME SERVER] Failed to register with API server: ${response.status}`); console.error(`[GAME SERVER] Error response:`, errorText); } } catch (error) { console.error('[GAME SERVER] Error registering with API server:', error); } } startServer(); module.exports = { app, server, io, gameInstances, connectedClients };