This commit is contained in:
YHH
2025-09-30 09:51:02 +08:00
parent 8c4e8d523e
commit 90153b98fe
4 changed files with 227 additions and 123 deletions

View File

@@ -10,7 +10,7 @@
-**SharedArrayBuffer**: 支持需要COOP/COEP头部 -**SharedArrayBuffer**: 支持需要COOP/COEP头部
-**Transferable Objects**: 完全支持 -**Transferable Objects**: 完全支持
-**高精度时间**: 使用 `performance.now()` -**高精度时间**: 使用 `performance.now()`
-**设备信息**: 完整的浏览器和设备信息 -**基础信息**: 浏览器版本和基本配置
## 完整实现 ## 完整实现
@@ -19,8 +19,7 @@ import type {
IPlatformAdapter, IPlatformAdapter,
PlatformWorker, PlatformWorker,
WorkerCreationOptions, WorkerCreationOptions,
PlatformConfig, PlatformConfig
BrowserDeviceInfo
} from '@esengine/ecs-framework'; } from '@esengine/ecs-framework';
/** /**
@@ -100,105 +99,33 @@ export class BrowserAdapter implements IPlatformAdapter {
public getPlatformConfig(): PlatformConfig { public getPlatformConfig(): PlatformConfig {
return { return {
maxWorkerCount: this.getHardwareConcurrency(), maxWorkerCount: this.getHardwareConcurrency(),
supportsModuleWorker: this.checkModuleWorkerSupport(), supportsModuleWorker: false,
supportsTransferableObjects: true, supportsTransferableObjects: true,
maxSharedArrayBufferSize: this.getMaxSharedArrayBufferSize(), maxSharedArrayBufferSize: 1024 * 1024 * 1024, // 1GB
workerScriptPrefix: '', workerScriptPrefix: '',
limitations: { limitations: {
noEval: false, noEval: false,
requiresWorkerInit: 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 { private getBrowserInfo(): string {
const userAgent = navigator.userAgent; const userAgent = navigator.userAgent;
let browserName = 'Unknown';
let version = 'Unknown';
// 简单的浏览器检测
if (userAgent.includes('Chrome')) { if (userAgent.includes('Chrome')) {
browserName = 'Chrome';
const match = userAgent.match(/Chrome\/([0-9.]+)/); const match = userAgent.match(/Chrome\/([0-9.]+)/);
if (match) version = match[1]; return match ? `Chrome ${match[1]}` : 'Chrome';
} else if (userAgent.includes('Firefox')) { } else if (userAgent.includes('Firefox')) {
browserName = 'Firefox';
const match = userAgent.match(/Firefox\/([0-9.]+)/); const match = userAgent.match(/Firefox\/([0-9.]+)/);
if (match) version = match[1]; if (match) return `Firefox ${match[1]}`;
} else if (userAgent.includes('Safari')) { } else if (userAgent.includes('Safari')) {
browserName = 'Safari';
const match = userAgent.match(/Version\/([0-9.]+)/); const match = userAgent.match(/Version\/([0-9.]+)/);
if (match) version = match[1]; if (match) return `Safari ${match[1]}`;
} else if (userAgent.includes('Edge')) {
browserName = 'Edge';
const match = userAgent.match(/Edge\/([0-9.]+)/);
if (match) version = match[1];
} }
return 'Unknown Browser';
return `${browserName} ${version}`;
} }
/** /**
@@ -206,40 +133,12 @@ export class BrowserAdapter implements IPlatformAdapter {
*/ */
private checkSharedArrayBufferEnabled(): boolean { private checkSharedArrayBufferEnabled(): boolean {
try { try {
// 尝试创建一个小的SharedArrayBuffer来测试
new SharedArrayBuffer(8); new SharedArrayBuffer(8);
return true; return true;
} catch { } catch {
return false; 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 ```typescript
const manager = PlatformManager.getInstance(); // 验证适配器是否正常工作
const adapter = new BrowserAdapter();
// 检查是否已注册适配器 console.log('适配器名称:', adapter.name);
if (manager.hasAdapter()) { console.log('浏览器版本:', adapter.version);
console.log('适配器信息:', manager.getAdapterInfo()); console.log('Worker支持:', adapter.isWorkerSupported());
console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported());
// 检查功能支持 console.log('CPU核心数:', adapter.getHardwareConcurrency());
console.log('Worker支持:', manager.supportsFeature('worker'));
console.log('SharedArrayBuffer支持:', manager.supportsFeature('shared-array-buffer'));
}
``` ```
## 重要注意事项 ## 重要注意事项
@@ -471,5 +367,4 @@ console.log('Worker支持:', adapter.isWorkerSupported());
console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported()); console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported());
console.log('硬件并发数:', adapter.getHardwareConcurrency()); console.log('硬件并发数:', adapter.getHardwareConcurrency());
console.log('平台配置:', adapter.getPlatformConfig()); console.log('平台配置:', adapter.getPlatformConfig());
console.log('设备信息:', adapter.getDeviceInfo());
``` ```

View File

@@ -29,7 +29,7 @@
"@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/node": "^20.19.0", "@types/node": "^20.19.17",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"rimraf": "^5.0.0", "rimraf": "^5.0.0",

View File

@@ -1,5 +1,6 @@
import { Core } from '@esengine/ecs-framework'; import { Core, PlatformManager } from '@esengine/ecs-framework';
import { GameScene } from './GameScene'; import { GameScene } from './GameScene';
import { BrowserAdapter } from './platform/BrowserAdapter';
// 性能监控 // 性能监控
interface PerformanceStats { interface PerformanceStats {
@@ -24,6 +25,10 @@ class WorkerDemo {
private elements: { [key: string]: HTMLElement } = {}; private elements: { [key: string]: HTMLElement } = {};
constructor() { constructor() {
// 注册浏览器适配器
const browserAdapter = new BrowserAdapter();
PlatformManager.getInstance().registerAdapter(browserAdapter);
// 获取canvas // 获取canvas
this.canvas = document.getElementById('gameCanvas') as HTMLCanvasElement; this.canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
if (!this.canvas) { if (!this.canvas) {

View File

@@ -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}`);
}
}
}