更新
This commit is contained in:
@@ -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());
|
|
||||||
```
|
```
|
||||||
2
examples/worker-system-demo/package-lock.json
generated
2
examples/worker-system-demo/package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
204
examples/worker-system-demo/src/platform/BrowserAdapter.ts
Normal file
204
examples/worker-system-demo/src/platform/BrowserAdapter.ts
Normal 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user