331 lines
9.2 KiB
JavaScript
331 lines
9.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'
|
|
});
|
|
}
|
|
});
|
|
|
|
// 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
|
|
});
|
|
|
|
// 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 || 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();
|
|
});
|
|
} 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 };
|