advanced-games-library
Version:
Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes
456 lines (389 loc) • 11.2 kB
text/typescript
/**
* 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();