From 90153b98fef84364ef9a7b1c7f23f596c9b357b5 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Tue, 30 Sep 2025 09:51:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide/platform-adapter/browser.md | 137 ++---------- examples/worker-system-demo/package-lock.json | 2 +- examples/worker-system-demo/src/main.ts | 7 +- .../src/platform/BrowserAdapter.ts | 204 ++++++++++++++++++ 4 files changed, 227 insertions(+), 123 deletions(-) create mode 100644 examples/worker-system-demo/src/platform/BrowserAdapter.ts diff --git a/docs/guide/platform-adapter/browser.md b/docs/guide/platform-adapter/browser.md index cb84deb4..71e79138 100644 --- a/docs/guide/platform-adapter/browser.md +++ b/docs/guide/platform-adapter/browser.md @@ -10,7 +10,7 @@ - ✅ **SharedArrayBuffer**: 支持(需要COOP/COEP头部) - ✅ **Transferable Objects**: 完全支持 - ✅ **高精度时间**: 使用 `performance.now()` -- ✅ **设备信息**: 完整的浏览器和设备信息 +- ✅ **基础信息**: 浏览器版本和基本配置 ## 完整实现 @@ -19,8 +19,7 @@ import type { IPlatformAdapter, PlatformWorker, WorkerCreationOptions, - PlatformConfig, - BrowserDeviceInfo + PlatformConfig } from '@esengine/ecs-framework'; /** @@ -100,105 +99,33 @@ export class BrowserAdapter implements IPlatformAdapter { public getPlatformConfig(): PlatformConfig { return { maxWorkerCount: this.getHardwareConcurrency(), - supportsModuleWorker: this.checkModuleWorkerSupport(), + supportsModuleWorker: false, supportsTransferableObjects: true, - maxSharedArrayBufferSize: this.getMaxSharedArrayBufferSize(), + maxSharedArrayBufferSize: 1024 * 1024 * 1024, // 1GB workerScriptPrefix: '', limitations: { noEval: false, requiresWorkerInit: false - }, - extensions: { - userAgent: navigator.userAgent, - vendor: navigator.vendor, - language: navigator.language, - cookieEnabled: navigator.cookieEnabled, - onLine: navigator.onLine } }; } - /** - * 获取浏览器设备信息 - */ - public getDeviceInfo(): BrowserDeviceInfo { - const deviceInfo: BrowserDeviceInfo = { - // 浏览器信息 - userAgent: navigator.userAgent, - vendor: navigator.vendor, - language: navigator.language, - languages: navigator.languages, - cookieEnabled: navigator.cookieEnabled, - onLine: navigator.onLine, - - // 硬件信息 - hardwareConcurrency: navigator.hardwareConcurrency, - deviceMemory: (navigator as any).deviceMemory, - maxTouchPoints: navigator.maxTouchPoints, - - // 屏幕信息 - screenWidth: screen.width, - screenHeight: screen.height, - availWidth: screen.availWidth, - availHeight: screen.availHeight, - colorDepth: screen.colorDepth, - pixelDepth: screen.pixelDepth, - - // 窗口信息 - innerWidth: window.innerWidth, - innerHeight: window.innerHeight, - outerWidth: window.outerWidth, - outerHeight: window.outerHeight, - devicePixelRatio: window.devicePixelRatio, - - // 平台信息 - platform: navigator.platform, - appVersion: navigator.appVersion, - appName: navigator.appName - }; - - // 连接信息(如果支持) - const connection = (navigator as any).connection; - if (connection) { - deviceInfo.connection = { - effectiveType: connection.effectiveType, - downlink: connection.downlink, - rtt: connection.rtt, - saveData: connection.saveData - }; - } - - return deviceInfo; - } - /** * 获取浏览器信息 */ private getBrowserInfo(): string { const userAgent = navigator.userAgent; - let browserName = 'Unknown'; - let version = 'Unknown'; - - // 简单的浏览器检测 if (userAgent.includes('Chrome')) { - browserName = 'Chrome'; const match = userAgent.match(/Chrome\/([0-9.]+)/); - if (match) version = match[1]; + return match ? `Chrome ${match[1]}` : 'Chrome'; } else if (userAgent.includes('Firefox')) { - browserName = 'Firefox'; const match = userAgent.match(/Firefox\/([0-9.]+)/); - if (match) version = match[1]; + if (match) return `Firefox ${match[1]}`; } else if (userAgent.includes('Safari')) { - browserName = 'Safari'; const match = userAgent.match(/Version\/([0-9.]+)/); - if (match) version = match[1]; - } else if (userAgent.includes('Edge')) { - browserName = 'Edge'; - const match = userAgent.match(/Edge\/([0-9.]+)/); - if (match) version = match[1]; + if (match) return `Safari ${match[1]}`; } - - return `${browserName} ${version}`; + return 'Unknown Browser'; } /** @@ -206,40 +133,12 @@ export class BrowserAdapter implements IPlatformAdapter { */ private checkSharedArrayBufferEnabled(): boolean { try { - // 尝试创建一个小的SharedArrayBuffer来测试 new SharedArrayBuffer(8); return true; } catch { return false; } } - - /** - * 检查是否支持模块Worker - */ - private checkModuleWorkerSupport(): boolean { - try { - // 简单检测:Chrome 80+、Firefox 114+等支持 - return 'type' in Worker.prototype || false; - } catch { - return false; - } - } - - /** - * 获取SharedArrayBuffer最大大小限制 - */ - private getMaxSharedArrayBufferSize(): number { - // 浏览器通常限制为1GB或更少 - const deviceMemory = (navigator as any).deviceMemory; - if (deviceMemory) { - // 限制为设备内存的25% - return Math.min(deviceMemory * 0.25 * 1024 * 1024 * 1024, 1024 * 1024 * 1024); - } - - // 默认1GB - return 1024 * 1024 * 1024; - } } /** @@ -411,19 +310,16 @@ interface PhysicsData { } ``` -### 4. 检查适配器状态 +### 4. 验证适配器工作状态 ```typescript -const manager = PlatformManager.getInstance(); - -// 检查是否已注册适配器 -if (manager.hasAdapter()) { - console.log('适配器信息:', manager.getAdapterInfo()); - - // 检查功能支持 - console.log('Worker支持:', manager.supportsFeature('worker')); - console.log('SharedArrayBuffer支持:', manager.supportsFeature('shared-array-buffer')); -} +// 验证适配器是否正常工作 +const adapter = new BrowserAdapter(); +console.log('适配器名称:', adapter.name); +console.log('浏览器版本:', adapter.version); +console.log('Worker支持:', adapter.isWorkerSupported()); +console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported()); +console.log('CPU核心数:', adapter.getHardwareConcurrency()); ``` ## 重要注意事项 @@ -471,5 +367,4 @@ console.log('Worker支持:', adapter.isWorkerSupported()); console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported()); console.log('硬件并发数:', adapter.getHardwareConcurrency()); console.log('平台配置:', adapter.getPlatformConfig()); -console.log('设备信息:', adapter.getDeviceInfo()); ``` \ No newline at end of file diff --git a/examples/worker-system-demo/package-lock.json b/examples/worker-system-demo/package-lock.json index f86f0445..5d0d3d01 100644 --- a/examples/worker-system-demo/package-lock.json +++ b/examples/worker-system-demo/package-lock.json @@ -29,7 +29,7 @@ "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", "@types/jest": "^29.5.14", - "@types/node": "^20.19.0", + "@types/node": "^20.19.17", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "rimraf": "^5.0.0", diff --git a/examples/worker-system-demo/src/main.ts b/examples/worker-system-demo/src/main.ts index 454f244e..60dcff14 100644 --- a/examples/worker-system-demo/src/main.ts +++ b/examples/worker-system-demo/src/main.ts @@ -1,5 +1,6 @@ -import { Core } from '@esengine/ecs-framework'; +import { Core, PlatformManager } from '@esengine/ecs-framework'; import { GameScene } from './GameScene'; +import { BrowserAdapter } from './platform/BrowserAdapter'; // 性能监控 interface PerformanceStats { @@ -24,6 +25,10 @@ class WorkerDemo { private elements: { [key: string]: HTMLElement } = {}; constructor() { + // 注册浏览器适配器 + const browserAdapter = new BrowserAdapter(); + PlatformManager.getInstance().registerAdapter(browserAdapter); + // 获取canvas this.canvas = document.getElementById('gameCanvas') as HTMLCanvasElement; if (!this.canvas) { diff --git a/examples/worker-system-demo/src/platform/BrowserAdapter.ts b/examples/worker-system-demo/src/platform/BrowserAdapter.ts new file mode 100644 index 00000000..2c88067e --- /dev/null +++ b/examples/worker-system-demo/src/platform/BrowserAdapter.ts @@ -0,0 +1,204 @@ +import type { + IPlatformAdapter, + PlatformWorker, + WorkerCreationOptions, + PlatformConfig +} from '@esengine/ecs-framework'; + +/** + * 浏览器平台适配器 + */ +export class BrowserAdapter implements IPlatformAdapter { + public readonly name = 'browser'; + public readonly version: string; + + constructor() { + this.version = this.getBrowserInfo(); + } + + /** + * 检查是否支持Worker + */ + public isWorkerSupported(): boolean { + return typeof Worker !== 'undefined'; + } + + /** + * 检查是否支持SharedArrayBuffer + */ + public isSharedArrayBufferSupported(): boolean { + return typeof SharedArrayBuffer !== 'undefined' && this.checkSharedArrayBufferEnabled(); + } + + /** + * 获取硬件并发数(CPU核心数) + */ + public getHardwareConcurrency(): number { + return navigator.hardwareConcurrency || 4; + } + + /** + * 创建Worker + */ + public createWorker(script: string, options: WorkerCreationOptions = {}): PlatformWorker { + if (!this.isWorkerSupported()) { + throw new Error('浏览器不支持Worker'); + } + + try { + return new BrowserWorker(script, options); + } catch (error) { + throw new Error(`创建浏览器Worker失败: ${(error as Error).message}`); + } + } + + /** + * 创建SharedArrayBuffer + */ + public createSharedArrayBuffer(length: number): SharedArrayBuffer | null { + if (!this.isSharedArrayBufferSupported()) { + return null; + } + + try { + return new SharedArrayBuffer(length); + } catch (error) { + console.warn('SharedArrayBuffer创建失败:', error); + return null; + } + } + + /** + * 获取高精度时间戳 + */ + public getHighResTimestamp(): number { + return performance.now(); + } + + /** + * 获取平台配置 + */ + public getPlatformConfig(): PlatformConfig { + return { + maxWorkerCount: this.getHardwareConcurrency(), + supportsModuleWorker: false, + supportsTransferableObjects: true, + maxSharedArrayBufferSize: 1024 * 1024 * 1024, // 1GB + workerScriptPrefix: '', + limitations: { + noEval: false, + requiresWorkerInit: false + } + }; + } + + /** + * 获取浏览器信息 + */ + private getBrowserInfo(): string { + const userAgent = navigator.userAgent; + if (userAgent.includes('Chrome')) { + const match = userAgent.match(/Chrome\/([0-9.]+)/); + return match ? `Chrome ${match[1]}` : 'Chrome'; + } else if (userAgent.includes('Firefox')) { + const match = userAgent.match(/Firefox\/([0-9.]+)/); + return match ? `Firefox ${match[1]}` : 'Firefox'; + } else if (userAgent.includes('Safari')) { + const match = userAgent.match(/Version\/([0-9.]+)/); + return match ? `Safari ${match[1]}` : 'Safari'; + } + return 'Unknown Browser'; + } + + /** + * 检查SharedArrayBuffer是否真正可用 + */ + private checkSharedArrayBufferEnabled(): boolean { + try { + new SharedArrayBuffer(8); + return true; + } catch { + return false; + } + } +} + +/** + * 浏览器Worker封装 + */ +class BrowserWorker implements PlatformWorker { + private _state: 'running' | 'terminated' = 'running'; + private worker: Worker; + + constructor(script: string, options: WorkerCreationOptions = {}) { + this.worker = this.createBrowserWorker(script, options); + } + + public get state(): 'running' | 'terminated' { + return this._state; + } + + public postMessage(message: any, transfer?: Transferable[]): void { + if (this._state === 'terminated') { + throw new Error('Worker已被终止'); + } + + try { + if (transfer && transfer.length > 0) { + this.worker.postMessage(message, transfer); + } else { + this.worker.postMessage(message); + } + } catch (error) { + throw new Error(`发送消息到Worker失败: ${(error as Error).message}`); + } + } + + public onMessage(handler: (event: { data: any }) => void): void { + this.worker.onmessage = (event: MessageEvent) => { + handler({ data: event.data }); + }; + } + + public onError(handler: (error: ErrorEvent) => void): void { + this.worker.onerror = handler; + } + + public terminate(): void { + if (this._state === 'running') { + try { + this.worker.terminate(); + this._state = 'terminated'; + } catch (error) { + console.error('终止Worker失败:', error); + } + } + } + + /** + * 创建浏览器Worker + */ + private createBrowserWorker(script: string, options: WorkerCreationOptions): Worker { + try { + // 创建Blob URL + const blob = new Blob([script], { type: 'application/javascript' }); + const url = URL.createObjectURL(blob); + + // 创建Worker + const worker = new Worker(url, { + type: options.type || 'classic', + credentials: options.credentials, + name: options.name + }); + + // 清理Blob URL(延迟清理,确保Worker已加载) + setTimeout(() => { + URL.revokeObjectURL(url); + }, 1000); + + return worker; + } catch (error) { + throw new Error(`无法创建浏览器Worker: ${(error as Error).message}`); + } + } +} \ No newline at end of file