274 lines
7.2 KiB
JavaScript
274 lines
7.2 KiB
JavaScript
/**
|
|
* 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'
|
|
});
|
|
}
|
|
});
|
|
|
|
// 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
|
|
});
|
|
|
|
// 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);
|
|
});
|
|
|
|
// Welcome message (similar to LocalServer)
|
|
socket.emit('welcome', {
|
|
message: 'Connected to game server',
|
|
serverInfo: {
|
|
mode: 'multiplayer',
|
|
activeInstances: gameInstances.size,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
const PORT = process.env.PORT || 3001;
|
|
|
|
async function startServer() {
|
|
try {
|
|
// Connect to database
|
|
await connectDB();
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`[GAME SERVER] Game server running on port ${PORT}`);
|
|
console.log(`[GAME SERVER] Socket.IO ready for multiplayer connections`);
|
|
});
|
|
} catch (error) {
|
|
console.error('[GAME SERVER] Failed to start server:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
startServer();
|
|
|
|
module.exports = { app, server, io, gameInstances, connectedClients };
|