UNPKG

minecraft-core-master

Version:

Núcleo avanzado para launchers de Minecraft. Descarga, instala y ejecuta versiones de Minecraft, assets, librerías, Java y loaders de forma automática y eficiente.

391 lines (390 loc) 14.7 kB
import { EventEmitter } from 'node:events'; import { ArgumentsBuilder } from '../Minecraft/Arguments.js'; export class MinecraftLaunch extends EventEmitter { options; launchProcess = null; performanceMonitor = null; gameState = { isRunning: false, startTime: 0, phase: 'idle', performance: { fps: 0, memory: '0MB', chunks: 0 } }; constructor(options) { super(); this.options = { enableDetailedEvents: true, enableTechnicalEvents: false, enableGameEvents: true, monitorPerformance: true, ...options }; } async launch() { try { this.emit('status', '🚀 Iniciando Minecraft...'); this.gameState.phase = 'pre-launch'; this.launchProcess = await ArgumentsBuilder(this.options); this.setupRealTimeMonitoring(); this.emit('status', '✅ Minecraft lanzado exitosamente'); } catch (error) { this.emit('error', error); throw error; } } setupRealTimeMonitoring() { if (!this.launchProcess) return; const { emitter } = this.launchProcess; emitter.on('status', (message) => { this.emit('status', message); this.options.statusCallback?.(message); }); emitter.on('progress', (data) => { const percentage = this.extractPercentage(data.message); this.emit('progress', data.type, percentage, data.message); this.options.progressCallback?.(data.type, percentage); }); emitter.on('game-started', () => { this.gameState.isRunning = true; this.gameState.startTime = Date.now(); this.gameState.phase = 'running'; this.emit('game-start'); }); emitter.on('game-exit', (data) => { this.gameState.isRunning = false; this.emit('game-exit', data.code, data.signal); this.cleanup(); }); emitter.on('error', (error) => { this.emit('error', error); }); if (this.options.enableDetailedEvents) { this.setupDetailedEvents(emitter); } if (this.options.enableTechnicalEvents) { this.setupTechnicalEvents(emitter); } if (this.options.enableGameEvents) { this.setupGameEvents(emitter); } if (this.options.monitorPerformance) { this.setupPerformanceMonitoring(); } } setupDetailedEvents(emitter) { emitter.on('phase-start', (phase) => { this.gameState.phase = phase; this.emit('debug:phase', `start:${phase}`, 0, { phase }); }); emitter.on('phase-end', (phase, time) => { this.emit('debug:phase', `end:${phase}`, time, { phase, duration: time, timestamp: Date.now() }); }); emitter.on('speed', (data) => { this.emit('debug:performance', { totalTime: data.time, phaseTimes: { [data.phase]: data.time }, memoryUsage: process.memoryUsage() }); }); emitter.on('debug', (data) => { switch (data.type) { case 'jvm-args': this.emit('debug:arguments', 'jvm', data.args, { memory: data.memory, classpathCount: data.classpathCount, proxy: data.proxy }); break; case 'game-args': this.emit('debug:arguments', 'game', data.args, { window: data.window, features: data.features, assets: { root: data.assetsRoot, index: data.assetsIndexName } }); break; case 'classpath': this.emit('debug:libraries', { total: data.count, lwjgl: data.classpath.filter((p) => p.includes('lwjgl')), natives: [data.nativesDir], classpath: data.classpath }); this.analyzeClasspath(data.classpath); break; case 'final-command': this.emit('debug:performance', { totalTime: 0, phaseTimes: {}, memoryUsage: process.memoryUsage() }); break; } }); emitter.on('libraries-processed', (data) => { this.emit('debug:libraries', { total: data.libraryCount, lwjgl: [], natives: [], classpath: [] }); }); } setupTechnicalEvents(emitter) { emitter.on('stdout', (output) => { this.processTechnicalOutput(output); }); emitter.on('stderr', (output) => { this.processTechnicalOutput(output); }); } setupGameEvents(emitter) { let gameOutputBuffer = ''; emitter.on('stdout', (output) => { gameOutputBuffer += output; const lines = gameOutputBuffer.split('\n'); gameOutputBuffer = lines.pop() || ''; for (const line of lines) { this.processGameOutput(line.trim()); } }); emitter.on('stderr', (output) => { this.processGameOutput(output.trim()); }); } processGameOutput(line) { if (!line) return; if (line.includes('Loading') || line.includes('Preparing')) { const stage = this.extractLoadingStage(line); const progress = this.extractLoadingProgress(line); this.emit('game:loading', stage, progress); } else if (line.includes('Creating level') || line.includes('Loading level')) { this.emit('game:world', 'creating', { action: 'create' }); } else if (line.includes('Joined game') || line.includes('Connecting to')) { const serverInfo = this.extractServerInfo(line); this.emit('game:connection', 'server', serverInfo); } else if (line.includes('<') && line.includes('>')) { const chatData = this.extractChatMessage(line); this.emit('game:chat', chatData.message, 'player'); } else if (line.startsWith('[System]') || line.includes('issued server command')) { this.emit('game:chat', line, 'system'); } else if (line.includes('FPS:') || line.includes('Allocated:')) { const perfData = this.extractPerformanceInfo(line); if (perfData.fps > 0) { this.gameState.performance = perfData; this.emit('game:performance', perfData.fps, perfData.memory, perfData.chunks); } } else if (line.includes('OpenGL') || line.includes('GPU') || line.includes('Renderer')) { const renderInfo = this.extractRenderInfo(line); if (renderInfo.renderer) { this.emit('technical:render', renderInfo.renderer, renderInfo.gpu, renderInfo.opengl); } } } processTechnicalOutput(output) { if (output.includes('Allocated:') || output.includes('Heap:')) { const memoryInfo = this.extractMemoryInfo(output); if (memoryInfo.heapUsed > 0) { this.emit('technical:memory', memoryInfo, this.generateMemoryRecommendations(memoryInfo)); } } if (output.includes('Warning:') || output.includes('Can\'t keep up!')) { this.emit('game:performance', Math.max(0, this.gameState.performance.fps - 10), this.gameState.performance.memory, this.gameState.performance.chunks); } } setupPerformanceMonitoring() { this.performanceMonitor = setInterval(() => { if (this.gameState.isRunning) { const memoryUsage = process.memoryUsage(); this.emit('debug:performance', { totalTime: Date.now() - this.gameState.startTime, phaseTimes: this.launchProcess?.stats.phaseTimes || {}, memoryUsage }); // Monitoreo de memoria del sistema this.emit('technical:memory', this.calculateMemoryMetrics(memoryUsage), this.generateSystemRecommendations(memoryUsage)); } }, 5000); } analyzeClasspath(classpath) { const analysis = { totalJars: classpath.length, missing: [], duplicates: this.findDuplicateLibraries(classpath), versionConflicts: this.findVersionConflicts(classpath), loadOrder: classpath }; this.emit('technical:classpath', classpath, analysis); } extractPercentage(message) { const match = message.match(/(\d+)%/); return match ? parseInt(match[1]) : 0; } extractLoadingStage(line) { if (line.includes('Loading')) return 'loading'; if (line.includes('Preparing')) return 'preparing'; if (line.includes('Building')) return 'building'; return 'unknown'; } extractLoadingProgress(line) { const match = line.match(/(\d+)\/(\d+)/); if (match) { const current = parseInt(match[1]); const total = parseInt(match[2]); return total > 0 ? (current / total) * 100 : 0; } return 0; } extractServerInfo(line) { const match = line.match(/(\d+\.\d+\.\d+\.\d+:\d+)|([\w.-]+\.[a-z]{2,})/); return match ? match[0] : 'unknown'; } extractChatMessage(line) { const match = line.match(/<(\w+)> (.+)/); return { player: match ? match[1] : 'unknown', message: match ? match[2] : line }; } extractPerformanceInfo(line) { const fpsMatch = line.match(/FPS:? (\d+)/); const memoryMatch = line.match(/(\d+\.?\d*[KMG]?B)/); const chunksMatch = line.match(/(\d+)\s*chunks/); return { fps: fpsMatch ? parseInt(fpsMatch[1]) : 0, memory: memoryMatch ? memoryMatch[1] : '0MB', chunks: chunksMatch ? parseInt(chunksMatch[1]) : 0 }; } extractRenderInfo(line) { const gpuMatch = line.match(/GPU:? ([^,\n]+)/); const rendererMatch = line.match(/Renderer:? ([^,\n]+)/); const openglMatch = line.match(/OpenGL:? ([^,\n]+)/); return { renderer: rendererMatch ? rendererMatch[1] : '', gpu: gpuMatch ? gpuMatch[1] : '', opengl: openglMatch ? openglMatch[1] : '' }; } extractMemoryInfo(line) { const heapMatch = line.match(/Heap:? (\d+)[KMG]?\/?(\d+)?[KMG]?/); const nativeMatch = line.match(/Native:? (\d+)[KMG]?/); return { heapUsed: heapMatch ? parseInt(heapMatch[1]) * 1024 * 1024 : 0, heapMax: heapMatch && heapMatch[2] ? parseInt(heapMatch[2]) * 1024 * 1024 : 0, nativeUsed: nativeMatch ? parseInt(nativeMatch[1]) * 1024 * 1024 : 0, gcTime: 0, recommendation: 'OPTIMAL' }; } calculateMemoryMetrics(usage) { const heapUsage = usage.heapUsed / (1024 * 1024); const heapMax = usage.heapTotal / (1024 * 1024); let recommendation = 'OPTIMAL'; if (heapUsage > heapMax * 0.9) recommendation = 'CRITICAL'; else if (heapUsage > heapMax * 0.7) recommendation = 'WARNING'; return { heapUsed: heapUsage, heapMax: heapMax, nativeUsed: usage.rss / (1024 * 1024), gcTime: 0, recommendation }; } findDuplicateLibraries(classpath) { const seen = new Set(); const duplicates = []; classpath.forEach(lib => { const libName = lib.split('/').pop(); if (seen.has(libName)) { duplicates.push(lib); } seen.add(libName); }); return duplicates; } findVersionConflicts(classpath) { const libVersions = new Map(); const conflicts = []; classpath.forEach(lib => { const match = lib.match(/([\w.-]+)-(\d+\.\d+\.\d+)/); if (match) { const [, name, version] = match; if (libVersions.has(name) && libVersions.get(name) !== version) { conflicts.push(`${name} (${libVersions.get(name)} vs ${version})`); } libVersions.set(name, version); } }); return conflicts; } generateMemoryRecommendations(metrics) { const recommendations = []; if (metrics.recommendation === 'CRITICAL') { recommendations.push('Aumentar memoria máxima de Java (-Xmx)'); recommendations.push('Cerrar otras aplicaciones'); } else if (metrics.recommendation === 'WARNING') { recommendations.push('Monitorizar uso de memoria'); recommendations.push('Considerar optimizar configuración'); } return recommendations; } generateSystemRecommendations(usage) { const recommendations = []; const usedPercent = (usage.heapUsed / usage.heapTotal) * 100; if (usedPercent > 90) { recommendations.push('🚨 CRÍTICO: Memoria casi agotada'); } else if (usedPercent > 70) { recommendations.push('⚠️ Memoria en niveles altos'); } return recommendations; } cleanup() { if (this.performanceMonitor) { clearInterval(this.performanceMonitor); this.performanceMonitor = null; } this.gameState.isRunning = false; this.gameState.phase = 'exited'; } kill() { this.cleanup(); return this.launchProcess?.kill() || false; } getState() { return { ...this.gameState, runningTime: this.gameState.isRunning ? Date.now() - this.gameState.startTime : 0, pid: this.launchProcess?.pid }; } getStats() { return this.launchProcess?.stats || null; } }