UNPKG

advanced-games-library

Version:

Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes

456 lines (389 loc) 11.2 kB
/** * Performance monitoring utilities for the games library */ export interface PerformanceMetrics { gameLoadTime: number; averageFPS: number; memoryUsage: number; renderTime: number; inputLatency: number; errorCount: number; crashCount: number; } export interface PerformanceBenchmark { timestamp: Date; gameId: string; metrics: PerformanceMetrics; deviceInfo?: DeviceInfo; } export interface DeviceInfo { platform: string; version: string; model?: string; memory?: number; cpuArch?: string; } /** * Performance Monitor class for tracking game performance */ export class PerformanceMonitor { private static instance: PerformanceMonitor; private isEnabled: boolean = true; private metrics: Map<string, PerformanceBenchmark[]> = new Map(); private currentSession: Map<string, Partial<PerformanceMetrics>> = new Map(); private fpsCounter: FPSCounter = new FPSCounter(); private constructor() {} static getInstance(): PerformanceMonitor { if (!PerformanceMonitor.instance) { PerformanceMonitor.instance = new PerformanceMonitor(); } return PerformanceMonitor.instance; } /** * Enable or disable performance monitoring */ setEnabled(enabled: boolean): void { this.isEnabled = enabled; if (enabled) { this.fpsCounter.start(); } else { this.fpsCounter.stop(); } } /** * Start monitoring a game session */ startGameSession(gameId: string): void { if (!this.isEnabled) return; this.currentSession.set(gameId, { gameLoadTime: 0, averageFPS: 0, memoryUsage: 0, renderTime: 0, inputLatency: 0, errorCount: 0, crashCount: 0 }); this.fpsCounter.reset(); } /** * End monitoring a game session */ endGameSession(gameId: string): PerformanceBenchmark | null { if (!this.isEnabled) return null; const sessionMetrics = this.currentSession.get(gameId); if (!sessionMetrics) return null; const benchmark: PerformanceBenchmark = { timestamp: new Date(), gameId, metrics: { gameLoadTime: sessionMetrics.gameLoadTime || 0, averageFPS: this.fpsCounter.getAverageFPS(), memoryUsage: this.getCurrentMemoryUsage(), renderTime: sessionMetrics.renderTime || 0, inputLatency: sessionMetrics.inputLatency || 0, errorCount: sessionMetrics.errorCount || 0, crashCount: sessionMetrics.crashCount || 0 }, deviceInfo: this.getDeviceInfo() }; // Store benchmark if (!this.metrics.has(gameId)) { this.metrics.set(gameId, []); } this.metrics.get(gameId)!.push(benchmark); // Clean up session this.currentSession.delete(gameId); return benchmark; } /** * Record game load time */ recordGameLoadTime(gameId: string, loadTime: number): void { if (!this.isEnabled) return; const session = this.currentSession.get(gameId); if (session) { session.gameLoadTime = loadTime; } } /** * Record render time */ recordRenderTime(gameId: string, renderTime: number): void { if (!this.isEnabled) return; const session = this.currentSession.get(gameId); if (session) { session.renderTime = renderTime; } } /** * Record input latency */ recordInputLatency(gameId: string, latency: number): void { if (!this.isEnabled) return; const session = this.currentSession.get(gameId); if (session) { session.inputLatency = latency; } } /** * Record error */ recordError(gameId: string): void { if (!this.isEnabled) return; const session = this.currentSession.get(gameId); if (session) { session.errorCount = (session.errorCount || 0) + 1; } } /** * Record crash */ recordCrash(gameId: string): void { if (!this.isEnabled) return; const session = this.currentSession.get(gameId); if (session) { session.crashCount = (session.crashCount || 0) + 1; } } /** * Get performance metrics for a specific game */ getGameMetrics(gameId: string): PerformanceBenchmark[] { return this.metrics.get(gameId) || []; } /** * Get aggregated performance statistics */ getPerformanceStats(gameId?: string): PerformanceStats { let allBenchmarks: PerformanceBenchmark[] = []; if (gameId) { allBenchmarks = this.metrics.get(gameId) || []; } else { this.metrics.forEach(benchmarks => { allBenchmarks.push(...benchmarks); }); } if (allBenchmarks.length === 0) { return { totalSessions: 0, averageLoadTime: 0, averageFPS: 0, averageMemoryUsage: 0, averageRenderTime: 0, averageInputLatency: 0, totalErrors: 0, totalCrashes: 0, performanceScore: 0 }; } const stats = allBenchmarks.reduce((acc, benchmark) => { acc.totalSessions++; acc.totalLoadTime += benchmark.metrics.gameLoadTime; acc.totalFPS += benchmark.metrics.averageFPS; acc.totalMemoryUsage += benchmark.metrics.memoryUsage; acc.totalRenderTime += benchmark.metrics.renderTime; acc.totalInputLatency += benchmark.metrics.inputLatency; acc.totalErrors += benchmark.metrics.errorCount; acc.totalCrashes += benchmark.metrics.crashCount; return acc; }, { totalSessions: 0, totalLoadTime: 0, totalFPS: 0, totalMemoryUsage: 0, totalRenderTime: 0, totalInputLatency: 0, totalErrors: 0, totalCrashes: 0 }); const count = allBenchmarks.length; return { totalSessions: count, averageLoadTime: stats.totalLoadTime / count, averageFPS: stats.totalFPS / count, averageMemoryUsage: stats.totalMemoryUsage / count, averageRenderTime: stats.totalRenderTime / count, averageInputLatency: stats.totalInputLatency / count, totalErrors: stats.totalErrors, totalCrashes: stats.totalCrashes, performanceScore: this.calculatePerformanceScore(stats, count) }; } /** * Get current memory usage */ private getCurrentMemoryUsage(): number { try { if (typeof process !== 'undefined' && process.memoryUsage) { return process.memoryUsage().heapUsed / 1024 / 1024; // MB } // Fallback for React Native return 0; } catch { return 0; } } /** * Get device information */ private getDeviceInfo(): DeviceInfo { try { // This would be replaced with actual device info in React Native return { platform: typeof process !== 'undefined' ? 'node' : 'web', version: '1.0.0', model: 'unknown', memory: 0, cpuArch: 'unknown' }; } catch { return { platform: 'unknown', version: 'unknown' }; } } /** * Calculate performance score (0-100) */ private calculatePerformanceScore(stats: any, count: number): number { const avgLoadTime = stats.totalLoadTime / count; const avgFPS = stats.totalFPS / count; const avgMemoryUsage = stats.totalMemoryUsage / count; const errorRate = stats.totalErrors / count; // Performance scoring algorithm let score = 100; // Penalize slow load times if (avgLoadTime > 3000) score -= 20; else if (avgLoadTime > 1000) score -= 10; // Penalize low FPS if (avgFPS < 30) score -= 25; else if (avgFPS < 45) score -= 15; else if (avgFPS < 55) score -= 5; // Penalize high memory usage if (avgMemoryUsage > 200) score -= 20; else if (avgMemoryUsage > 100) score -= 10; // Penalize errors score -= errorRate * 10; // Penalize crashes heavily score -= (stats.totalCrashes / count) * 30; return Math.max(0, Math.min(100, score)); } /** * Clear all performance data */ clear(): void { this.metrics.clear(); this.currentSession.clear(); this.fpsCounter.reset(); } /** * Export performance data */ exportData(): { [gameId: string]: PerformanceBenchmark[] } { const data: { [gameId: string]: PerformanceBenchmark[] } = {}; this.metrics.forEach((benchmarks, gameId) => { data[gameId] = [...benchmarks]; }); return data; } } /** * FPS Counter class */ class FPSCounter { private frames: number = 0; private startTime: number = 0; private lastFrameTime: number = 0; private isRunning: boolean = false; private frameTimestamps: number[] = []; start(): void { this.isRunning = true; this.startTime = performance.now(); this.lastFrameTime = this.startTime; this.scheduleFrame(); } stop(): void { this.isRunning = false; } reset(): void { this.frames = 0; this.frameTimestamps = []; this.startTime = performance.now(); this.lastFrameTime = this.startTime; } private scheduleFrame(): void { if (!this.isRunning) return; requestAnimationFrame(() => { const now = performance.now(); this.frameTimestamps.push(now); this.frames++; // Keep only last 60 frames for accurate FPS calculation if (this.frameTimestamps.length > 60) { this.frameTimestamps.shift(); } this.lastFrameTime = now; this.scheduleFrame(); }); } getCurrentFPS(): number { if (this.frameTimestamps.length < 2) return 0; const timeDiff = this.frameTimestamps[this.frameTimestamps.length - 1] - this.frameTimestamps[0]; return (this.frameTimestamps.length - 1) / (timeDiff / 1000); } getAverageFPS(): number { if (this.frames === 0) return 0; const totalTime = (this.lastFrameTime - this.startTime) / 1000; return this.frames / totalTime; } } /** * Performance statistics interface */ export interface PerformanceStats { totalSessions: number; averageLoadTime: number; averageFPS: number; averageMemoryUsage: number; averageRenderTime: number; averageInputLatency: number; totalErrors: number; totalCrashes: number; performanceScore: number; } /** * Performance decorator for measuring function execution time */ export function measurePerformance(target: any, propertyName: string, descriptor: PropertyDescriptor) { const method = descriptor.value; descriptor.value = function (...args: any[]) { const start = performance.now(); const result = method.apply(this, args); const end = performance.now(); const monitor = PerformanceMonitor.getInstance(); monitor.recordRenderTime(this.gameId || 'unknown', end - start); return result; }; return descriptor; } /** * Memory usage decorator */ export function trackMemoryUsage(target: any, propertyName: string, descriptor: PropertyDescriptor) { const method = descriptor.value; descriptor.value = function (...args: any[]) { const monitor = PerformanceMonitor.getInstance(); try { const result = method.apply(this, args); return result; } catch (error) { monitor.recordError(this.gameId || 'unknown'); throw error; } }; return descriptor; } // Export performance monitor instance export const performanceMonitor = PerformanceMonitor.getInstance();