API/Client/js/LocalServer.js
2026-01-24 16:47:19 -04:00

419 lines
14 KiB
JavaScript

/**
* Local Server for Singleplayer Mode
* A simplified server that runs within the Electron client for offline/singleplayer functionality
* NOTE: This version requires express, socket.io, and cors dependencies to be installed
*/
class LocalServer {
constructor() {
this.app = null;
this.server = null;
this.io = null;
this.port = null;
this.isRunning = false;
this.connectedClients = new Map();
console.log('[LOCAL SERVER] LocalServer initialized');
}
async initialize() {
try {
// Try to require dependencies
if (typeof require !== 'undefined') {
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const cors = require('cors');
this.setupExpress(express, cors);
this.createServer = createServer;
this.ServerClass = Server;
console.log('[LOCAL SERVER] Dependencies loaded successfully');
return true;
} else {
console.warn('[LOCAL SERVER] require() not available, running in browser context');
return false;
}
} catch (error) {
console.error('[LOCAL SERVER] Failed to load dependencies:', error.message);
console.log('[LOCAL SERVER] Please install dependencies: npm install express socket.io cors');
return false;
}
}
setupExpress(express, cors) {
// Initialize Express app
this.app = express();
// Middleware
this.app.use(cors({
origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"],
credentials: true
}));
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true }));
// Health check endpoint
this.app.get('/health', (req, res) => {
res.status(200).json({
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
mode: 'local'
});
});
// API version endpoint
this.app.get('/api/ssc/version', (req, res) => {
res.status(200).json({
version: '1.0.0',
service: 'galaxystrikeonline-local-server',
timestamp: new Date().toISOString(),
mode: 'local'
});
});
// Mock authentication endpoints for singleplayer
this.app.post('/api/auth/login', (req, res) => {
const { email, password } = req.body;
// Auto-authenticate for singleplayer mode
const mockUser = {
id: 'local-user',
email: email || 'local@player.com',
username: 'Local Player',
token: 'local-token-' + Date.now(),
createdAt: new Date().toISOString()
};
res.status(200).json({
success: true,
user: mockUser,
token: mockUser.token,
message: 'Logged in to local mode'
});
});
this.app.post('/api/auth/register', (req, res) => {
const { email, password, username } = req.body;
// Auto-register for singleplayer mode
const mockUser = {
id: 'local-user',
email: email || 'local@player.com',
username: username || 'Local Player',
token: 'local-token-' + Date.now(),
createdAt: new Date().toISOString()
};
res.status(201).json({
success: true,
user: mockUser,
token: mockUser.token,
message: 'Registered in local mode'
});
});
// Mock server browser endpoints
this.app.get('/api/servers', (req, res) => {
// Return a single local server
const localServer = {
id: 'local-server',
name: 'Local Singleplayer',
description: 'Your personal local server for singleplayer gaming',
type: 'private',
region: 'local',
maxPlayers: 1,
currentPlayers: 0,
owner: 'Local Player',
address: 'localhost',
port: this.port,
status: 'online',
createdAt: new Date().toISOString(),
ping: 0
};
res.status(200).json({
success: true,
servers: [localServer]
});
});
// Mock game data endpoints
this.app.get('/api/game/player/:id', (req, res) => {
// Return player data from local storage if available
const playerId = req.params.id;
let saveData;
try {
// In Electron context, access localStorage differently
if (typeof localStorage !== 'undefined') {
saveData = localStorage.getItem(`gso_save_slot_1`);
}
} catch (error) {
console.warn('[LOCAL SERVER] Could not access localStorage:', error);
}
if (saveData) {
try {
const parsedData = JSON.parse(saveData);
res.status(200).json({
success: true,
player: parsedData.player
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Failed to parse save data'
});
}
} else {
res.status(404).json({
success: false,
error: 'No save data found'
});
}
});
this.app.post('/api/game/player/:id/save', (req, res) => {
// Save player data to local storage
const playerId = req.params.id;
const playerData = req.body;
try {
let existingSaveData = '{}';
// In Electron context, access localStorage differently
if (typeof localStorage !== 'undefined') {
existingSaveData = localStorage.getItem(`gso_save_slot_1`) || '{}';
}
const parsedExisting = JSON.parse(existingSaveData);
// Merge player data
parsedExisting.player = playerData;
parsedExisting.lastSave = Date.now();
if (typeof localStorage !== 'undefined') {
localStorage.setItem(`gso_save_slot_1`, JSON.stringify(parsedExisting));
}
res.status(200).json({
success: true,
message: 'Player data saved locally'
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Failed to save player data'
});
}
});
}
async start(port = 3004) {
if (this.isRunning) {
console.log('[LOCAL SERVER] Server is already running on port', this.port);
return { success: false, error: 'Server already running' };
}
try {
// Initialize dependencies if not already done
if (!this.app) {
const initialized = await this.initialize();
if (!initialized) {
return { success: false, error: 'Failed to initialize server dependencies' };
}
}
this.port = port;
this.server = this.createServer(this.app);
this.io = new this.ServerClass(this.server, {
cors: {
origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"],
methods: ["GET", "POST"]
}
});
// Setup Socket.IO handlers
this.setupSocketHandlers();
// Start the server
await new Promise((resolve, reject) => {
this.server.listen(port, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
this.isRunning = true;
console.log(`[LOCAL SERVER] Local server started on port ${port}`);
return {
success: true,
port: port,
url: `http://localhost:${port}`
};
} catch (error) {
console.error('[LOCAL SERVER] Failed to start server:', error);
return { success: false, error: error.message };
}
}
setupSocketHandlers() {
this.io.on('connection', (socket) => {
console.log('[LOCAL SERVER] Client connected:', socket.id);
this.connectedClients.set(socket.id, {
connectedAt: Date.now(),
playerData: null
});
// Handle authentication
socket.on('authenticate', (data) => {
console.log('[LOCAL SERVER] Authenticating client:', socket.id, data);
// Auto-authenticate for local mode
socket.emit('authenticated', {
success: true,
user: {
id: 'local-user',
username: 'Local Player',
token: 'local-token-' + Date.now()
}
});
// Update client info
const clientInfo = this.connectedClients.get(socket.id);
if (clientInfo) {
clientInfo.playerData = {
id: 'local-user',
username: 'Local Player'
};
}
});
// Handle game data sync
socket.on('saveGameData', (data) => {
console.log('[LOCAL SERVER] Saving game data for:', socket.id);
// Save to localStorage (this will be handled by the client-side save system)
socket.emit('gameDataSaved', {
success: true,
timestamp: Date.now()
});
});
socket.on('loadGameData', (data) => {
console.log('[LOCAL SERVER] Loading game data for:', socket.id);
// Load from localStorage (this will be handled by the client-side load system)
let saveData;
try {
if (typeof localStorage !== 'undefined') {
saveData = localStorage.getItem(`gso_save_slot_1`);
}
} catch (error) {
console.warn('[LOCAL SERVER] Could not access localStorage:', error);
}
if (saveData) {
try {
const parsedData = JSON.parse(saveData);
socket.emit('gameDataLoaded', {
success: true,
data: parsedData
});
} catch (error) {
socket.emit('gameDataLoaded', {
success: false,
error: 'Failed to parse save data'
});
}
} else {
socket.emit('gameDataLoaded', {
success: false,
error: 'No save data found'
});
}
});
// Handle disconnection
socket.on('disconnect', () => {
console.log('[LOCAL SERVER] Client disconnected:', socket.id);
this.connectedClients.delete(socket.id);
});
// Send welcome message
socket.emit('welcome', {
message: 'Connected to local server',
serverInfo: {
mode: 'local',
port: this.port,
timestamp: new Date().toISOString()
}
});
});
}
async stop() {
if (!this.isRunning) {
console.log('[LOCAL SERVER] Server is not running');
return { success: false, error: 'Server is not running' };
}
try {
// Disconnect all clients
if (this.io) {
this.io.disconnectSockets();
}
// Close the server
if (this.server) {
await new Promise((resolve) => {
this.server.close(resolve);
});
}
this.isRunning = false;
this.port = null;
this.connectedClients.clear();
console.log('[LOCAL SERVER] Local server stopped');
return { success: true };
} catch (error) {
console.error('[LOCAL SERVER] Failed to stop server:', error);
return { success: false, error: error.message };
}
}
getStatus() {
return {
isRunning: this.isRunning,
port: this.port,
connectedClients: this.connectedClients.size,
uptime: this.isRunning ? process.uptime() : 0
};
}
getUrl() {
return this.isRunning ? `http://localhost:${this.port}` : null;
}
}
// Export for use in Node.js environment
if (typeof module !== 'undefined' && module.exports) {
module.exports = LocalServer;
}
// Export for use in browser environment
if (typeof window !== 'undefined') {
window.LocalServer = LocalServer;
}