feat: implement complete Phase 2 frontend foundation with React 18
Major milestone: Frontend implementation complete for Shattered Void MMO FRONTEND IMPLEMENTATION: - React 18 + TypeScript + Vite development environment - Tailwind CSS with custom dark theme for sci-fi aesthetic - Zustand state management with authentication persistence - Socket.io WebSocket client with auto-reconnection - Protected routing with authentication guards - Responsive design with mobile-first approach AUTHENTICATION SYSTEM: - Login/register forms with comprehensive validation - JWT token management with localStorage persistence - Password strength validation and user feedback - Protected routes and authentication guards CORE GAME INTERFACE: - Colony management dashboard with real-time updates - Resource display with live production tracking - WebSocket integration for real-time game events - Navigation with connection status indicator - Toast notifications for user feedback BACKEND ENHANCEMENTS: - Complete Research System with technology tree (23 technologies) - Fleet Management System with ship designs and movement - Enhanced Authentication with email verification and password reset - Complete game tick integration for all systems - Advanced WebSocket events for real-time updates ARCHITECTURE FEATURES: - Type-safe TypeScript throughout - Component-based architecture with reusable UI elements - API client with request/response interceptors - Error handling and loading states - Performance optimized builds with code splitting Phase 2 Status: Frontend foundation complete (Week 1-2 objectives met) Ready for: Colony management, fleet operations, research interface Next: Enhanced gameplay features and admin interface 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8d9ef427be
commit
d41d1e8125
130 changed files with 33588 additions and 14817 deletions
|
|
@ -10,563 +10,563 @@ const logger = require('../../utils/logger');
|
|||
const { ValidationError, ConflictError, NotFoundError } = require('../../middleware/error.middleware');
|
||||
|
||||
class CombatController {
|
||||
constructor() {
|
||||
this.combatPluginManager = null;
|
||||
this.gameEventService = null;
|
||||
this.combatService = null;
|
||||
}
|
||||
constructor() {
|
||||
this.combatPluginManager = null;
|
||||
this.gameEventService = null;
|
||||
this.combatService = null;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Initialize controller with dependencies
|
||||
* @param {Object} dependencies - Service dependencies
|
||||
*/
|
||||
async initialize(dependencies = {}) {
|
||||
this.gameEventService = dependencies.gameEventService || new GameEventService();
|
||||
this.combatPluginManager = dependencies.combatPluginManager || new CombatPluginManager();
|
||||
this.combatService = dependencies.combatService || new CombatService(this.gameEventService, this.combatPluginManager);
|
||||
async initialize(dependencies = {}) {
|
||||
this.gameEventService = dependencies.gameEventService || new GameEventService();
|
||||
this.combatPluginManager = dependencies.combatPluginManager || new CombatPluginManager();
|
||||
this.combatService = dependencies.combatService || new CombatService(this.gameEventService, this.combatPluginManager);
|
||||
|
||||
// Initialize plugin manager
|
||||
await this.combatPluginManager.initialize('controller-init');
|
||||
}
|
||||
// Initialize plugin manager
|
||||
await this.combatPluginManager.initialize('controller-init');
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Initiate combat between fleets or fleet vs colony
|
||||
* POST /api/combat/initiate
|
||||
*/
|
||||
async initiateCombat(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
const combatData = req.body;
|
||||
async initiateCombat(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
const combatData = req.body;
|
||||
|
||||
logger.info('Combat initiation request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
combatData
|
||||
});
|
||||
logger.info('Combat initiation request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
combatData,
|
||||
});
|
||||
|
||||
// Validate required fields
|
||||
if (!combatData.attacker_fleet_id) {
|
||||
return res.status(400).json({
|
||||
error: 'Attacker fleet ID is required',
|
||||
code: 'MISSING_ATTACKER_FLEET'
|
||||
});
|
||||
}
|
||||
// Validate required fields
|
||||
if (!combatData.attacker_fleet_id) {
|
||||
return res.status(400).json({
|
||||
error: 'Attacker fleet ID is required',
|
||||
code: 'MISSING_ATTACKER_FLEET',
|
||||
});
|
||||
}
|
||||
|
||||
if (!combatData.location) {
|
||||
return res.status(400).json({
|
||||
error: 'Combat location is required',
|
||||
code: 'MISSING_LOCATION'
|
||||
});
|
||||
}
|
||||
if (!combatData.location) {
|
||||
return res.status(400).json({
|
||||
error: 'Combat location is required',
|
||||
code: 'MISSING_LOCATION',
|
||||
});
|
||||
}
|
||||
|
||||
if (!combatData.defender_fleet_id && !combatData.defender_colony_id) {
|
||||
return res.status(400).json({
|
||||
error: 'Either defender fleet or colony must be specified',
|
||||
code: 'MISSING_DEFENDER'
|
||||
});
|
||||
}
|
||||
if (!combatData.defender_fleet_id && !combatData.defender_colony_id) {
|
||||
return res.status(400).json({
|
||||
error: 'Either defender fleet or colony must be specified',
|
||||
code: 'MISSING_DEFENDER',
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize services if not already done
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
// Initialize services if not already done
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
// Initiate combat
|
||||
const result = await this.combatService.initiateCombat(combatData, playerId, correlationId);
|
||||
// Initiate combat
|
||||
const result = await this.combatService.initiateCombat(combatData, playerId, correlationId);
|
||||
|
||||
logger.info('Combat initiated successfully', {
|
||||
correlationId,
|
||||
playerId,
|
||||
battleId: result.battleId
|
||||
});
|
||||
logger.info('Combat initiated successfully', {
|
||||
correlationId,
|
||||
playerId,
|
||||
battleId: result.battleId,
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Combat initiated successfully'
|
||||
});
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Combat initiated successfully',
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Combat initiation failed', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Combat initiation failed', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return res.status(400).json({
|
||||
error: error.message,
|
||||
code: 'VALIDATION_ERROR'
|
||||
});
|
||||
}
|
||||
if (error instanceof ValidationError) {
|
||||
return res.status(400).json({
|
||||
error: error.message,
|
||||
code: 'VALIDATION_ERROR',
|
||||
});
|
||||
}
|
||||
|
||||
if (error instanceof ConflictError) {
|
||||
return res.status(409).json({
|
||||
error: error.message,
|
||||
code: 'CONFLICT_ERROR'
|
||||
});
|
||||
}
|
||||
if (error instanceof ConflictError) {
|
||||
return res.status(409).json({
|
||||
error: error.message,
|
||||
code: 'CONFLICT_ERROR',
|
||||
});
|
||||
}
|
||||
|
||||
if (error instanceof NotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: error.message,
|
||||
code: 'NOT_FOUND'
|
||||
});
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: error.message,
|
||||
code: 'NOT_FOUND',
|
||||
});
|
||||
}
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get active combats for the current player
|
||||
* GET /api/combat/active
|
||||
*/
|
||||
async getActiveCombats(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
async getActiveCombats(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
|
||||
logger.info('Active combats request', {
|
||||
correlationId,
|
||||
playerId
|
||||
});
|
||||
logger.info('Active combats request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
});
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const activeCombats = await this.combatService.getActiveCombats(playerId, correlationId);
|
||||
const activeCombats = await this.combatService.getActiveCombats(playerId, correlationId);
|
||||
|
||||
logger.info('Active combats retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
count: activeCombats.length
|
||||
});
|
||||
logger.info('Active combats retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
count: activeCombats.length,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
combats: activeCombats,
|
||||
count: activeCombats.length
|
||||
}
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
combats: activeCombats,
|
||||
count: activeCombats.length,
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to get active combats', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get active combats', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get combat history for the current player
|
||||
* GET /api/combat/history
|
||||
*/
|
||||
async getCombatHistory(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
|
||||
// Parse query parameters
|
||||
const options = {
|
||||
limit: parseInt(req.query.limit) || 20,
|
||||
offset: parseInt(req.query.offset) || 0,
|
||||
outcome: req.query.outcome || null
|
||||
};
|
||||
async getCombatHistory(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
|
||||
// Validate parameters
|
||||
if (options.limit > 100) {
|
||||
return res.status(400).json({
|
||||
error: 'Limit cannot exceed 100',
|
||||
code: 'INVALID_LIMIT'
|
||||
});
|
||||
}
|
||||
// Parse query parameters
|
||||
const options = {
|
||||
limit: parseInt(req.query.limit) || 20,
|
||||
offset: parseInt(req.query.offset) || 0,
|
||||
outcome: req.query.outcome || null,
|
||||
};
|
||||
|
||||
if (options.outcome && !['attacker_victory', 'defender_victory', 'draw'].includes(options.outcome)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid outcome filter',
|
||||
code: 'INVALID_OUTCOME'
|
||||
});
|
||||
}
|
||||
// Validate parameters
|
||||
if (options.limit > 100) {
|
||||
return res.status(400).json({
|
||||
error: 'Limit cannot exceed 100',
|
||||
code: 'INVALID_LIMIT',
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Combat history request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
options
|
||||
});
|
||||
if (options.outcome && !['attacker_victory', 'defender_victory', 'draw'].includes(options.outcome)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid outcome filter',
|
||||
code: 'INVALID_OUTCOME',
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
logger.info('Combat history request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
options,
|
||||
});
|
||||
|
||||
const history = await this.combatService.getCombatHistory(playerId, options, correlationId);
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
logger.info('Combat history retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
count: history.combats.length,
|
||||
total: history.pagination.total
|
||||
});
|
||||
const history = await this.combatService.getCombatHistory(playerId, options, correlationId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: history
|
||||
});
|
||||
logger.info('Combat history retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
count: history.combats.length,
|
||||
total: history.pagination.total,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat history', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: history,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat history', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get detailed combat encounter information
|
||||
* GET /api/combat/encounter/:encounterId
|
||||
*/
|
||||
async getCombatEncounter(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
const encounterId = parseInt(req.params.encounterId);
|
||||
async getCombatEncounter(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
const encounterId = parseInt(req.params.encounterId);
|
||||
|
||||
if (!encounterId || isNaN(encounterId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid encounter ID is required',
|
||||
code: 'INVALID_ENCOUNTER_ID'
|
||||
});
|
||||
}
|
||||
if (!encounterId || isNaN(encounterId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid encounter ID is required',
|
||||
code: 'INVALID_ENCOUNTER_ID',
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Combat encounter request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
encounterId
|
||||
});
|
||||
logger.info('Combat encounter request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
encounterId,
|
||||
});
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const encounter = await this.combatService.getCombatEncounter(encounterId, playerId, correlationId);
|
||||
const encounter = await this.combatService.getCombatEncounter(encounterId, playerId, correlationId);
|
||||
|
||||
if (!encounter) {
|
||||
return res.status(404).json({
|
||||
error: 'Combat encounter not found or access denied',
|
||||
code: 'ENCOUNTER_NOT_FOUND'
|
||||
});
|
||||
}
|
||||
if (!encounter) {
|
||||
return res.status(404).json({
|
||||
error: 'Combat encounter not found or access denied',
|
||||
code: 'ENCOUNTER_NOT_FOUND',
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Combat encounter retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
encounterId
|
||||
});
|
||||
logger.info('Combat encounter retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
encounterId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: encounter
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: encounter,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat encounter', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
encounterId: req.params.encounterId,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat encounter', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
encounterId: req.params.encounterId,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get combat statistics for the current player
|
||||
* GET /api/combat/statistics
|
||||
*/
|
||||
async getCombatStatistics(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
async getCombatStatistics(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
|
||||
logger.info('Combat statistics request', {
|
||||
correlationId,
|
||||
playerId
|
||||
});
|
||||
logger.info('Combat statistics request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
});
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const statistics = await this.combatService.getCombatStatistics(playerId, correlationId);
|
||||
const statistics = await this.combatService.getCombatStatistics(playerId, correlationId);
|
||||
|
||||
logger.info('Combat statistics retrieved', {
|
||||
correlationId,
|
||||
playerId
|
||||
});
|
||||
logger.info('Combat statistics retrieved', {
|
||||
correlationId,
|
||||
playerId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: statistics
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: statistics,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat statistics', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat statistics', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Update fleet positioning for tactical combat
|
||||
* PUT /api/combat/position/:fleetId
|
||||
*/
|
||||
async updateFleetPosition(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
const fleetId = parseInt(req.params.fleetId);
|
||||
const positionData = req.body;
|
||||
async updateFleetPosition(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const playerId = req.user.id;
|
||||
const fleetId = parseInt(req.params.fleetId);
|
||||
const positionData = req.body;
|
||||
|
||||
if (!fleetId || isNaN(fleetId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid fleet ID is required',
|
||||
code: 'INVALID_FLEET_ID'
|
||||
});
|
||||
}
|
||||
if (!fleetId || isNaN(fleetId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid fleet ID is required',
|
||||
code: 'INVALID_FLEET_ID',
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Fleet position update request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
fleetId,
|
||||
positionData
|
||||
});
|
||||
logger.info('Fleet position update request', {
|
||||
correlationId,
|
||||
playerId,
|
||||
fleetId,
|
||||
positionData,
|
||||
});
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const result = await this.combatService.updateFleetPosition(fleetId, positionData, playerId, correlationId);
|
||||
const result = await this.combatService.updateFleetPosition(fleetId, positionData, playerId, correlationId);
|
||||
|
||||
logger.info('Fleet position updated', {
|
||||
correlationId,
|
||||
playerId,
|
||||
fleetId
|
||||
});
|
||||
logger.info('Fleet position updated', {
|
||||
correlationId,
|
||||
playerId,
|
||||
fleetId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Fleet position updated successfully'
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Fleet position updated successfully',
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to update fleet position', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
fleetId: req.params.fleetId,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to update fleet position', {
|
||||
correlationId: req.correlationId,
|
||||
playerId: req.user?.id,
|
||||
fleetId: req.params.fleetId,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
if (error instanceof ValidationError) {
|
||||
return res.status(400).json({
|
||||
error: error.message,
|
||||
code: 'VALIDATION_ERROR'
|
||||
});
|
||||
}
|
||||
if (error instanceof ValidationError) {
|
||||
return res.status(400).json({
|
||||
error: error.message,
|
||||
code: 'VALIDATION_ERROR',
|
||||
});
|
||||
}
|
||||
|
||||
if (error instanceof NotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: error.message,
|
||||
code: 'NOT_FOUND'
|
||||
});
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: error.message,
|
||||
code: 'NOT_FOUND',
|
||||
});
|
||||
}
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get available combat types and configurations
|
||||
* GET /api/combat/types
|
||||
*/
|
||||
async getCombatTypes(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
async getCombatTypes(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
|
||||
logger.info('Combat types request', { correlationId });
|
||||
logger.info('Combat types request', { correlationId });
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const combatTypes = await this.combatService.getAvailableCombatTypes(correlationId);
|
||||
const combatTypes = await this.combatService.getAvailableCombatTypes(correlationId);
|
||||
|
||||
logger.info('Combat types retrieved', {
|
||||
correlationId,
|
||||
count: combatTypes.length
|
||||
});
|
||||
logger.info('Combat types retrieved', {
|
||||
correlationId,
|
||||
count: combatTypes.length,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: combatTypes
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: combatTypes,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat types', {
|
||||
correlationId: req.correlationId,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat types', {
|
||||
correlationId: req.correlationId,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Force resolve a combat (admin only)
|
||||
* POST /api/combat/resolve/:battleId
|
||||
*/
|
||||
async forceResolveCombat(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const battleId = parseInt(req.params.battleId);
|
||||
async forceResolveCombat(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const battleId = parseInt(req.params.battleId);
|
||||
|
||||
if (!battleId || isNaN(battleId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid battle ID is required',
|
||||
code: 'INVALID_BATTLE_ID'
|
||||
});
|
||||
}
|
||||
if (!battleId || isNaN(battleId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid battle ID is required',
|
||||
code: 'INVALID_BATTLE_ID',
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('Force resolve combat request', {
|
||||
correlationId,
|
||||
battleId,
|
||||
adminUser: req.user?.id
|
||||
});
|
||||
logger.info('Force resolve combat request', {
|
||||
correlationId,
|
||||
battleId,
|
||||
adminUser: req.user?.id,
|
||||
});
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const result = await this.combatService.processCombat(battleId, correlationId);
|
||||
const result = await this.combatService.processCombat(battleId, correlationId);
|
||||
|
||||
logger.info('Combat force resolved', {
|
||||
correlationId,
|
||||
battleId,
|
||||
outcome: result.outcome
|
||||
});
|
||||
logger.info('Combat force resolved', {
|
||||
correlationId,
|
||||
battleId,
|
||||
outcome: result.outcome,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Combat resolved successfully'
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: 'Combat resolved successfully',
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to force resolve combat', {
|
||||
correlationId: req.correlationId,
|
||||
battleId: req.params.battleId,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to force resolve combat', {
|
||||
correlationId: req.correlationId,
|
||||
battleId: req.params.battleId,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
if (error instanceof NotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: error.message,
|
||||
code: 'NOT_FOUND'
|
||||
});
|
||||
}
|
||||
if (error instanceof NotFoundError) {
|
||||
return res.status(404).json({
|
||||
error: error.message,
|
||||
code: 'NOT_FOUND',
|
||||
});
|
||||
}
|
||||
|
||||
if (error instanceof ConflictError) {
|
||||
return res.status(409).json({
|
||||
error: error.message,
|
||||
code: 'CONFLICT_ERROR'
|
||||
});
|
||||
}
|
||||
if (error instanceof ConflictError) {
|
||||
return res.status(409).json({
|
||||
error: error.message,
|
||||
code: 'CONFLICT_ERROR',
|
||||
});
|
||||
}
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get combat queue status (admin only)
|
||||
* GET /api/combat/queue
|
||||
*/
|
||||
async getCombatQueue(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const status = req.query.status || null;
|
||||
const limit = parseInt(req.query.limit) || 50;
|
||||
async getCombatQueue(req, res, next) {
|
||||
try {
|
||||
const correlationId = req.correlationId;
|
||||
const status = req.query.status || null;
|
||||
const limit = parseInt(req.query.limit) || 50;
|
||||
|
||||
logger.info('Combat queue request', {
|
||||
correlationId,
|
||||
status,
|
||||
limit,
|
||||
adminUser: req.user?.id
|
||||
});
|
||||
logger.info('Combat queue request', {
|
||||
correlationId,
|
||||
status,
|
||||
limit,
|
||||
adminUser: req.user?.id,
|
||||
});
|
||||
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
if (!this.combatService) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const queue = await this.combatService.getCombatQueue({ status, limit }, correlationId);
|
||||
const queue = await this.combatService.getCombatQueue({ status, limit }, correlationId);
|
||||
|
||||
logger.info('Combat queue retrieved', {
|
||||
correlationId,
|
||||
count: queue.length
|
||||
});
|
||||
logger.info('Combat queue retrieved', {
|
||||
correlationId,
|
||||
count: queue.length,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: queue
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: queue,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat queue', {
|
||||
correlationId: req.correlationId,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get combat queue', {
|
||||
correlationId: req.correlationId,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
next(error);
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
const combatController = new CombatController();
|
||||
|
||||
module.exports = {
|
||||
CombatController,
|
||||
|
||||
// Export bound methods for route usage
|
||||
initiateCombat: combatController.initiateCombat.bind(combatController),
|
||||
getActiveCombats: combatController.getActiveCombats.bind(combatController),
|
||||
getCombatHistory: combatController.getCombatHistory.bind(combatController),
|
||||
getCombatEncounter: combatController.getCombatEncounter.bind(combatController),
|
||||
getCombatStatistics: combatController.getCombatStatistics.bind(combatController),
|
||||
updateFleetPosition: combatController.updateFleetPosition.bind(combatController),
|
||||
getCombatTypes: combatController.getCombatTypes.bind(combatController),
|
||||
forceResolveCombat: combatController.forceResolveCombat.bind(combatController),
|
||||
getCombatQueue: combatController.getCombatQueue.bind(combatController)
|
||||
};
|
||||
CombatController,
|
||||
|
||||
// Export bound methods for route usage
|
||||
initiateCombat: combatController.initiateCombat.bind(combatController),
|
||||
getActiveCombats: combatController.getActiveCombats.bind(combatController),
|
||||
getCombatHistory: combatController.getCombatHistory.bind(combatController),
|
||||
getCombatEncounter: combatController.getCombatEncounter.bind(combatController),
|
||||
getCombatStatistics: combatController.getCombatStatistics.bind(combatController),
|
||||
updateFleetPosition: combatController.updateFleetPosition.bind(combatController),
|
||||
getCombatTypes: combatController.getCombatTypes.bind(combatController),
|
||||
forceResolveCombat: combatController.forceResolveCombat.bind(combatController),
|
||||
getCombatQueue: combatController.getCombatQueue.bind(combatController),
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue