支持wx/browser的worker(由于wx限制默认不开启worker)
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"@types/multer": "^1.4.13",
|
||||
"@types/ws": "^8.18.1",
|
||||
"coi-serviceworker": "^0.1.7",
|
||||
"minigame-api-typings": "^3.8.12",
|
||||
"protobufjs": "^7.5.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"ws": "^8.18.2"
|
||||
@@ -11220,6 +11221,12 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minigame-api-typings": {
|
||||
"version": "3.8.12",
|
||||
"resolved": "https://registry.npmjs.org/minigame-api-typings/-/minigame-api-typings-3.8.12.tgz",
|
||||
"integrity": "sha512-72KNUlj0oo7pK0yYIBaaagJ7gdN2oX8z/UIWyunPRHeIT4UTH70Z3HAovOZ53E5ENOQVPD5+EcnlaOdUFbk2PQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
"@types/multer": "^1.4.13",
|
||||
"@types/ws": "^8.18.1",
|
||||
"coi-serviceworker": "^0.1.7",
|
||||
"minigame-api-typings": "^3.8.12",
|
||||
"protobufjs": "^7.5.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"ws": "^8.18.2"
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Matcher } from '../Utils/Matcher';
|
||||
import { Time } from '../../Utils/Time';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
import type { IComponent } from '../../Types';
|
||||
import { PlatformManager } from '../../Platform/PlatformManager';
|
||||
import type { IPlatformAdapter, PlatformWorker } from '../../Platform/IPlatformAdapter';
|
||||
|
||||
/**
|
||||
* Worker处理函数类型
|
||||
@@ -191,14 +193,18 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
systemConfig?: any;
|
||||
entitiesPerWorker?: number;
|
||||
};
|
||||
private workerPool: WebWorkerPool | null = null;
|
||||
private workerPool: PlatformWorkerPool | null = null;
|
||||
private isProcessing = false;
|
||||
protected sharedBuffer: SharedArrayBuffer | null = null;
|
||||
protected sharedFloatArray: Float32Array | null = null;
|
||||
private platformAdapter: IPlatformAdapter;
|
||||
|
||||
constructor(matcher?: Matcher, config: WorkerSystemConfig = {}) {
|
||||
super(matcher);
|
||||
|
||||
// 获取平台适配器
|
||||
this.platformAdapter = PlatformManager.getInstance().getAdapter();
|
||||
|
||||
// 验证和调整 worker 数量,确保不超过系统最大值
|
||||
const requestedWorkerCount = config.workerCount ?? this.getMaxSystemWorkerCount();
|
||||
const maxSystemWorkerCount = this.getMaxSystemWorkerCount();
|
||||
@@ -233,25 +239,22 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
* 检查是否支持Worker
|
||||
*/
|
||||
private isWorkerSupported(): boolean {
|
||||
return typeof Worker !== 'undefined' && typeof Blob !== 'undefined';
|
||||
return this.platformAdapter.isWorkerSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持SharedArrayBuffer
|
||||
*/
|
||||
private isSharedArrayBufferSupported(): boolean {
|
||||
return typeof SharedArrayBuffer !== 'undefined' && self.crossOriginIsolated;
|
||||
return this.platformAdapter.isSharedArrayBufferSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统支持的最大Worker数量
|
||||
*/
|
||||
private getMaxSystemWorkerCount(): number {
|
||||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {
|
||||
// 使用全部CPU核心数作为最大限制
|
||||
return navigator.hardwareConcurrency;
|
||||
}
|
||||
return 4; // 降级默认值
|
||||
const platformConfig = this.platformAdapter.getPlatformConfig();
|
||||
return platformConfig.maxWorkerCount;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +270,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
try {
|
||||
// 检查是否支持SharedArrayBuffer
|
||||
if (!this.isSharedArrayBufferSupported()) {
|
||||
this.logger.warn(`${this.systemName}: 不支持SharedArrayBuffer,降级到单Worker模式以保证数据处理完整性`);
|
||||
this.logger.warn(`${this.systemName}: 平台不支持SharedArrayBuffer,降级到单Worker模式以保证数据处理完整性`);
|
||||
this.config.useSharedArrayBuffer = false;
|
||||
// 降级到单Worker模式:确保所有实体在同一个Worker中处理,维持实体间交互的完整性
|
||||
this.config.workerCount = 1;
|
||||
@@ -277,8 +280,10 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
// 使用配置的实体数据大小和最大实体数量
|
||||
// 预分配缓冲区:maxEntities * entityDataSize * 4字节
|
||||
const bufferSize = this.config.maxEntities * this.config.entityDataSize * 4;
|
||||
this.sharedBuffer = new SharedArrayBuffer(bufferSize);
|
||||
this.sharedFloatArray = new Float32Array(this.sharedBuffer);
|
||||
this.sharedBuffer = this.platformAdapter.createSharedArrayBuffer(bufferSize);
|
||||
if (this.sharedBuffer) {
|
||||
this.sharedFloatArray = new Float32Array(this.sharedBuffer);
|
||||
}
|
||||
|
||||
this.logger.info(`${this.systemName}: SharedArrayBuffer初始化成功 (${bufferSize} 字节)`);
|
||||
} catch (error) {
|
||||
@@ -296,9 +301,26 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
private initializeWorkerPool(): void {
|
||||
try {
|
||||
const script = this.createWorkerScript();
|
||||
this.workerPool = new WebWorkerPool(
|
||||
this.config.workerCount,
|
||||
script,
|
||||
|
||||
// 在WorkerEntitySystem中处理平台相关逻辑
|
||||
const workers: PlatformWorker[] = [];
|
||||
const platformConfig = this.platformAdapter.getPlatformConfig();
|
||||
const fullScript = (platformConfig.workerScriptPrefix || '') + script;
|
||||
|
||||
for (let i = 0; i < this.config.workerCount; i++) {
|
||||
try {
|
||||
const worker = this.platformAdapter.createWorker(fullScript, {
|
||||
name: `WorkerEntitySystem-${i}`
|
||||
});
|
||||
workers.push(worker);
|
||||
} catch (error) {
|
||||
this.logger.error(`创建Worker ${i} 失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
this.workerPool = new PlatformWorkerPool(
|
||||
workers,
|
||||
this.sharedBuffer // 传递SharedArrayBuffer给Worker池
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -832,10 +854,10 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Web Worker池管理器
|
||||
* 平台适配的Worker池管理器
|
||||
*/
|
||||
class WebWorkerPool {
|
||||
private workers: Worker[] = [];
|
||||
class PlatformWorkerPool {
|
||||
private workers: PlatformWorker[] = [];
|
||||
private taskQueue: Array<{
|
||||
id: string;
|
||||
data: any;
|
||||
@@ -845,18 +867,22 @@ class WebWorkerPool {
|
||||
private busyWorkers = new Set<number>();
|
||||
private taskCounter = 0;
|
||||
private sharedBuffer: SharedArrayBuffer | null = null;
|
||||
private readonly logger = createLogger('PlatformWorkerPool');
|
||||
|
||||
constructor(workerCount: number, script: string, sharedBuffer?: SharedArrayBuffer | null) {
|
||||
this.sharedBuffer = sharedBuffer || null;
|
||||
constructor(
|
||||
workers: PlatformWorker[],
|
||||
sharedBuffer: SharedArrayBuffer | null = null
|
||||
) {
|
||||
this.sharedBuffer = sharedBuffer;
|
||||
this.workers = workers;
|
||||
|
||||
const blob = new Blob([script], { type: 'application/javascript' });
|
||||
const scriptURL = URL.createObjectURL(blob);
|
||||
// 为每个Worker设置消息处理器
|
||||
for (let i = 0; i < workers.length; i++) {
|
||||
const worker = workers[i];
|
||||
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
const worker = new Worker(scriptURL);
|
||||
|
||||
worker.onmessage = (e) => this.handleWorkerMessage(i, e.data);
|
||||
worker.onerror = (error) => this.handleWorkerError(i, error);
|
||||
// 设置消息处理器
|
||||
worker.onMessage((event) => this.handleWorkerMessage(i, event.data));
|
||||
worker.onError((error) => this.handleWorkerError(i, error));
|
||||
|
||||
// 如果有SharedArrayBuffer,发送给Worker
|
||||
if (sharedBuffer) {
|
||||
@@ -865,11 +891,7 @@ class WebWorkerPool {
|
||||
sharedBuffer: sharedBuffer
|
||||
});
|
||||
}
|
||||
|
||||
this.workers.push(worker);
|
||||
}
|
||||
|
||||
URL.revokeObjectURL(scriptURL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
278
packages/core/src/Platform/BrowserAdapter.ts
Normal file
278
packages/core/src/Platform/BrowserAdapter.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
import type {
|
||||
IPlatformAdapter,
|
||||
PlatformWorker,
|
||||
WorkerCreationOptions,
|
||||
PlatformConfig,
|
||||
BrowserDeviceInfo
|
||||
} from './IPlatformAdapter';
|
||||
import { createLogger, type ILogger } from '../Utils/Logger';
|
||||
|
||||
/**
|
||||
* 浏览器平台适配器
|
||||
* 支持标准Web API的浏览器环境
|
||||
*/
|
||||
export class BrowserAdapter implements IPlatformAdapter {
|
||||
public readonly name = 'browser';
|
||||
public readonly version = this.getBrowserVersion();
|
||||
private readonly logger: ILogger;
|
||||
|
||||
constructor() {
|
||||
this.logger = createLogger('BrowserAdapter');
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持Worker
|
||||
*/
|
||||
public isWorkerSupported(): boolean {
|
||||
return typeof Worker !== 'undefined' && typeof Blob !== 'undefined' && typeof URL !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持SharedArrayBuffer
|
||||
*/
|
||||
public isSharedArrayBufferSupported(): boolean {
|
||||
return typeof SharedArrayBuffer !== 'undefined' &&
|
||||
typeof self !== 'undefined' &&
|
||||
self.crossOriginIsolated === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取硬件并发数
|
||||
*/
|
||||
public getHardwareConcurrency(): number {
|
||||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {
|
||||
return navigator.hardwareConcurrency;
|
||||
}
|
||||
return 4; // 默认值
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Worker
|
||||
*/
|
||||
public createWorker(script: string, options: WorkerCreationOptions = {}): PlatformWorker {
|
||||
const blob = new Blob([script], { type: 'application/javascript' });
|
||||
const scriptURL = URL.createObjectURL(blob);
|
||||
|
||||
const worker = new Worker(scriptURL, {
|
||||
type: options.type || 'classic',
|
||||
credentials: options.credentials || 'same-origin',
|
||||
name: options.name
|
||||
});
|
||||
|
||||
return new BrowserWorker(worker, scriptURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SharedArrayBuffer
|
||||
*/
|
||||
public createSharedArrayBuffer(length: number): SharedArrayBuffer | null {
|
||||
if (!this.isSharedArrayBufferSupported()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new SharedArrayBuffer(length);
|
||||
} catch (error) {
|
||||
this.logger.warn('Failed to create SharedArrayBuffer:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取高精度时间戳
|
||||
*/
|
||||
public getHighResTimestamp(): number {
|
||||
if (typeof performance !== 'undefined' && performance.now) {
|
||||
return performance.now();
|
||||
}
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台配置
|
||||
*/
|
||||
public getPlatformConfig(): PlatformConfig {
|
||||
return {
|
||||
maxWorkerCount: this.getHardwareConcurrency(),
|
||||
supportsModuleWorker: this.supportsModuleWorker(),
|
||||
supportsTransferableObjects: this.supportsTransferableObjects(),
|
||||
maxSharedArrayBufferSize: this.getMaxSharedArrayBufferSize(),
|
||||
workerScriptPrefix: '',
|
||||
limitations: {
|
||||
noEval: false,
|
||||
requiresWorkerInit: false
|
||||
},
|
||||
extensions: {
|
||||
supportsServiceWorker: typeof navigator !== 'undefined' && 'serviceWorker' in navigator,
|
||||
supportsWebAssembly: typeof WebAssembly !== 'undefined'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取浏览器设备信息
|
||||
*/
|
||||
public getDeviceInfo(): BrowserDeviceInfo {
|
||||
try {
|
||||
const deviceInfo: BrowserDeviceInfo = {};
|
||||
|
||||
// 浏览器基础信息
|
||||
if (typeof navigator !== 'undefined') {
|
||||
deviceInfo.userAgent = navigator.userAgent;
|
||||
deviceInfo.vendor = navigator.vendor;
|
||||
deviceInfo.language = navigator.language;
|
||||
deviceInfo.languages = navigator.languages as string[];
|
||||
deviceInfo.cookieEnabled = navigator.cookieEnabled;
|
||||
deviceInfo.onLine = navigator.onLine;
|
||||
deviceInfo.doNotTrack = navigator.doNotTrack;
|
||||
deviceInfo.platform = navigator.platform;
|
||||
deviceInfo.appVersion = navigator.appVersion;
|
||||
deviceInfo.appName = navigator.appName;
|
||||
|
||||
// 硬件信息
|
||||
deviceInfo.hardwareConcurrency = navigator.hardwareConcurrency;
|
||||
deviceInfo.maxTouchPoints = navigator.maxTouchPoints;
|
||||
|
||||
// 设备内存(实验性API)
|
||||
if ('deviceMemory' in navigator) {
|
||||
deviceInfo.deviceMemory = (navigator as any).deviceMemory;
|
||||
}
|
||||
|
||||
// 网络连接信息(实验性API)
|
||||
if ('connection' in navigator) {
|
||||
const connection = (navigator as any).connection;
|
||||
deviceInfo.connection = {
|
||||
effectiveType: connection.effectiveType,
|
||||
downlink: connection.downlink,
|
||||
rtt: connection.rtt,
|
||||
saveData: connection.saveData
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 屏幕信息
|
||||
if (typeof screen !== 'undefined') {
|
||||
deviceInfo.screenWidth = screen.width;
|
||||
deviceInfo.screenHeight = screen.height;
|
||||
deviceInfo.availWidth = screen.availWidth;
|
||||
deviceInfo.availHeight = screen.availHeight;
|
||||
deviceInfo.colorDepth = screen.colorDepth;
|
||||
deviceInfo.pixelDepth = screen.pixelDepth;
|
||||
}
|
||||
|
||||
// 窗口信息
|
||||
if (typeof window !== 'undefined') {
|
||||
deviceInfo.innerWidth = window.innerWidth;
|
||||
deviceInfo.innerHeight = window.innerHeight;
|
||||
deviceInfo.outerWidth = window.outerWidth;
|
||||
deviceInfo.outerHeight = window.outerHeight;
|
||||
deviceInfo.devicePixelRatio = window.devicePixelRatio;
|
||||
}
|
||||
|
||||
return deviceInfo;
|
||||
} catch (error) {
|
||||
this.logger.warn('获取浏览器设备信息失败', error);
|
||||
return {} as BrowserDeviceInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器版本信息
|
||||
*/
|
||||
private getBrowserVersion(): string {
|
||||
if (typeof navigator !== 'undefined') {
|
||||
return navigator.userAgent;
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持模块Worker
|
||||
*/
|
||||
private supportsModuleWorker(): boolean {
|
||||
try {
|
||||
// 尝试创建一个简单的模块Worker来测试支持
|
||||
const blob = new Blob(['export {};'], { type: 'application/javascript' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const worker = new Worker(url, { type: 'module' });
|
||||
worker.terminate();
|
||||
URL.revokeObjectURL(url);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持Transferable Objects
|
||||
*/
|
||||
private supportsTransferableObjects(): boolean {
|
||||
try {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const blob = new Blob(['self.postMessage(null);'], { type: 'application/javascript' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const worker = new Worker(url);
|
||||
worker.postMessage(buffer, [buffer]);
|
||||
worker.terminate();
|
||||
URL.revokeObjectURL(url);
|
||||
return buffer.byteLength === 0; // Buffer应该被transfer走
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SharedArrayBuffer最大大小限制
|
||||
*/
|
||||
private getMaxSharedArrayBufferSize(): number {
|
||||
// 浏览器环境返回保守的默认值
|
||||
// 大多数现代浏览器都支持较大的SharedArrayBuffer
|
||||
return 256 * 1024 * 1024; // 256MB,适合大多数ECS应用场景
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览器Worker封装
|
||||
*/
|
||||
class BrowserWorker implements PlatformWorker {
|
||||
private _state: 'running' | 'terminated' = 'running';
|
||||
|
||||
constructor(
|
||||
private worker: Worker,
|
||||
private scriptURL: string
|
||||
) {}
|
||||
|
||||
public get state(): 'running' | 'terminated' {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public postMessage(message: any, transfer?: Transferable[]): void {
|
||||
if (this._state === 'terminated') {
|
||||
throw new Error('Worker has been terminated');
|
||||
}
|
||||
if (transfer && transfer.length > 0) {
|
||||
this.worker.postMessage(message, transfer);
|
||||
} else {
|
||||
this.worker.postMessage(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') {
|
||||
this.worker.terminate();
|
||||
URL.revokeObjectURL(this.scriptURL);
|
||||
this._state = 'terminated';
|
||||
}
|
||||
}
|
||||
}
|
||||
289
packages/core/src/Platform/IPlatformAdapter.ts
Normal file
289
packages/core/src/Platform/IPlatformAdapter.ts
Normal file
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* 平台适配器接口
|
||||
* 用于适配不同的运行环境(浏览器、微信小游戏、字节跳动小游戏等)
|
||||
*/
|
||||
export interface IPlatformAdapter {
|
||||
/**
|
||||
* 平台名称
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* 平台版本信息
|
||||
*/
|
||||
readonly version?: string;
|
||||
|
||||
/**
|
||||
* 检查是否支持Worker
|
||||
*/
|
||||
isWorkerSupported(): boolean;
|
||||
|
||||
/**
|
||||
* 检查是否支持SharedArrayBuffer
|
||||
*/
|
||||
isSharedArrayBufferSupported(): boolean;
|
||||
|
||||
/**
|
||||
* 获取硬件并发数(CPU核心数)
|
||||
*/
|
||||
getHardwareConcurrency(): number;
|
||||
|
||||
/**
|
||||
* 创建Worker
|
||||
* @param script Worker脚本内容
|
||||
* @param options Worker选项
|
||||
*/
|
||||
createWorker(script: string, options?: WorkerCreationOptions): PlatformWorker;
|
||||
|
||||
/**
|
||||
* 创建SharedArrayBuffer
|
||||
* @param length 缓冲区大小(字节)
|
||||
*/
|
||||
createSharedArrayBuffer(length: number): SharedArrayBuffer | null;
|
||||
|
||||
/**
|
||||
* 获取高精度时间戳
|
||||
*/
|
||||
getHighResTimestamp(): number;
|
||||
|
||||
/**
|
||||
* 获取平台特定的配置
|
||||
*/
|
||||
getPlatformConfig(): PlatformConfig;
|
||||
|
||||
/**
|
||||
* 异步获取平台配置(包含需要异步获取的信息)
|
||||
*/
|
||||
getPlatformConfigAsync?(): Promise<PlatformConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Worker创建选项
|
||||
*/
|
||||
export interface WorkerCreationOptions {
|
||||
/**
|
||||
* Worker类型
|
||||
*/
|
||||
type?: 'classic' | 'module';
|
||||
|
||||
/**
|
||||
* 凭据模式
|
||||
*/
|
||||
credentials?: 'omit' | 'same-origin' | 'include';
|
||||
|
||||
/**
|
||||
* Worker名称(用于调试)
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台Worker接口
|
||||
*/
|
||||
export interface PlatformWorker {
|
||||
/**
|
||||
* 发送消息到Worker
|
||||
*/
|
||||
postMessage(message: any, transfer?: Transferable[]): void;
|
||||
|
||||
/**
|
||||
* 监听Worker消息
|
||||
*/
|
||||
onMessage(handler: (event: { data: any }) => void): void;
|
||||
|
||||
/**
|
||||
* 监听Worker错误
|
||||
*/
|
||||
onError(handler: (error: ErrorEvent) => void): void;
|
||||
|
||||
/**
|
||||
* 终止Worker
|
||||
*/
|
||||
terminate(): void;
|
||||
|
||||
/**
|
||||
* Worker状态
|
||||
*/
|
||||
readonly state: 'running' | 'terminated';
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台配置
|
||||
*/
|
||||
export interface PlatformConfig {
|
||||
/**
|
||||
* 最大Worker数量限制
|
||||
*/
|
||||
maxWorkerCount: number;
|
||||
|
||||
/**
|
||||
* 是否支持模块Worker
|
||||
*/
|
||||
supportsModuleWorker: boolean;
|
||||
|
||||
/**
|
||||
* 是否支持Transferable Objects
|
||||
*/
|
||||
supportsTransferableObjects: boolean;
|
||||
|
||||
/**
|
||||
* SharedArrayBuffer的最大大小限制(字节)
|
||||
*/
|
||||
maxSharedArrayBufferSize?: number;
|
||||
|
||||
/**
|
||||
* 平台特定的Worker脚本前缀(如果需要)
|
||||
*/
|
||||
workerScriptPrefix?: string;
|
||||
|
||||
/**
|
||||
* 平台特定的限制和特性
|
||||
*/
|
||||
limitations?: {
|
||||
/**
|
||||
* 是否禁用eval(影响动态脚本创建)
|
||||
*/
|
||||
noEval?: boolean;
|
||||
|
||||
/**
|
||||
* 是否需要特殊的Worker初始化
|
||||
*/
|
||||
requiresWorkerInit?: boolean;
|
||||
|
||||
/**
|
||||
* 内存限制(字节)
|
||||
*/
|
||||
memoryLimit?: number;
|
||||
|
||||
/**
|
||||
* Worker是否不受支持(用于明确标记不支持Worker的平台)
|
||||
*/
|
||||
workerNotSupported?: boolean;
|
||||
|
||||
/**
|
||||
* Worker限制说明列表
|
||||
*/
|
||||
workerLimitations?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 平台特定的扩展属性
|
||||
*/
|
||||
extensions?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台检测结果
|
||||
*/
|
||||
export interface PlatformDetectionResult {
|
||||
/**
|
||||
* 平台类型
|
||||
*/
|
||||
platform: 'browser' | 'wechat-minigame' | 'bytedance-minigame' | 'alipay-minigame' | 'baidu-minigame' | 'unknown';
|
||||
|
||||
/**
|
||||
* 是否确定检测结果
|
||||
*/
|
||||
confident: boolean;
|
||||
|
||||
/**
|
||||
* 检测到的特征
|
||||
*/
|
||||
features: string[];
|
||||
|
||||
/**
|
||||
* 建议使用的适配器类名
|
||||
*/
|
||||
adapterClass?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小游戏设备信息接口
|
||||
*/
|
||||
export interface WeChatDeviceInfo {
|
||||
// 设备基础信息
|
||||
brand?: string;
|
||||
model?: string;
|
||||
platform?: string;
|
||||
system?: string;
|
||||
benchmarkLevel?: number;
|
||||
cpuType?: string;
|
||||
memorySize?: string;
|
||||
deviceAbi?: string;
|
||||
abi?: string;
|
||||
|
||||
// 窗口信息
|
||||
screenWidth?: number;
|
||||
screenHeight?: number;
|
||||
screenTop?: number;
|
||||
windowWidth?: number;
|
||||
windowHeight?: number;
|
||||
pixelRatio?: number;
|
||||
statusBarHeight?: number;
|
||||
safeArea?: {
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
// 应用信息
|
||||
version?: string;
|
||||
language?: string;
|
||||
theme?: string;
|
||||
SDKVersion?: string;
|
||||
enableDebug?: boolean;
|
||||
fontSizeSetting?: number;
|
||||
host?: {
|
||||
appId: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览器设备信息接口
|
||||
*/
|
||||
export interface BrowserDeviceInfo {
|
||||
// 浏览器信息
|
||||
userAgent?: string;
|
||||
vendor?: string;
|
||||
language?: string;
|
||||
languages?: string[];
|
||||
cookieEnabled?: boolean;
|
||||
onLine?: boolean;
|
||||
doNotTrack?: string | null;
|
||||
|
||||
// 硬件信息
|
||||
hardwareConcurrency?: number;
|
||||
deviceMemory?: number;
|
||||
maxTouchPoints?: number;
|
||||
|
||||
// 屏幕信息
|
||||
screenWidth?: number;
|
||||
screenHeight?: number;
|
||||
availWidth?: number;
|
||||
availHeight?: number;
|
||||
colorDepth?: number;
|
||||
pixelDepth?: number;
|
||||
|
||||
// 窗口信息
|
||||
innerWidth?: number;
|
||||
innerHeight?: number;
|
||||
outerWidth?: number;
|
||||
outerHeight?: number;
|
||||
devicePixelRatio?: number;
|
||||
|
||||
// 连接信息(如果支持)
|
||||
connection?: {
|
||||
effectiveType?: string;
|
||||
downlink?: number;
|
||||
rtt?: number;
|
||||
saveData?: boolean;
|
||||
};
|
||||
|
||||
// 平台信息
|
||||
platform?: string;
|
||||
appVersion?: string;
|
||||
appName?: string;
|
||||
}
|
||||
209
packages/core/src/Platform/PlatformDetector.ts
Normal file
209
packages/core/src/Platform/PlatformDetector.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import type { PlatformDetectionResult } from './IPlatformAdapter';
|
||||
|
||||
/**
|
||||
* 平台检测器
|
||||
* 自动检测当前运行环境并返回对应的平台信息
|
||||
*/
|
||||
export class PlatformDetector {
|
||||
/**
|
||||
* 检测当前平台
|
||||
*/
|
||||
public static detect(): PlatformDetectionResult {
|
||||
const features: string[] = [];
|
||||
let platform: PlatformDetectionResult['platform'] = 'unknown';
|
||||
let confident = false;
|
||||
let adapterClass: string | undefined;
|
||||
|
||||
// 检查全局对象和API
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
features.push('globalThis');
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
features.push('window');
|
||||
}
|
||||
|
||||
if (typeof self !== 'undefined') {
|
||||
features.push('self');
|
||||
}
|
||||
|
||||
// 检测微信小游戏环境
|
||||
if (this.isWeChatMiniGame()) {
|
||||
platform = 'wechat-minigame';
|
||||
confident = true;
|
||||
adapterClass = 'WeChatMiniGameAdapter';
|
||||
features.push('wx', 'wechat-minigame');
|
||||
}
|
||||
// 检测字节跳动小游戏环境
|
||||
else if (this.isByteDanceMiniGame()) {
|
||||
platform = 'bytedance-minigame';
|
||||
confident = true;
|
||||
adapterClass = 'ByteDanceMiniGameAdapter';
|
||||
features.push('tt', 'bytedance-minigame');
|
||||
}
|
||||
// 检测支付宝小游戏环境
|
||||
else if (this.isAlipayMiniGame()) {
|
||||
platform = 'alipay-minigame';
|
||||
confident = true;
|
||||
adapterClass = 'AlipayMiniGameAdapter';
|
||||
features.push('my', 'alipay-minigame');
|
||||
}
|
||||
// 检测百度小游戏环境
|
||||
else if (this.isBaiduMiniGame()) {
|
||||
platform = 'baidu-minigame';
|
||||
confident = true;
|
||||
adapterClass = 'BaiduMiniGameAdapter';
|
||||
features.push('swan', 'baidu-minigame');
|
||||
}
|
||||
// 检测浏览器环境
|
||||
else if (this.isBrowser()) {
|
||||
platform = 'browser';
|
||||
confident = true;
|
||||
adapterClass = 'BrowserAdapter';
|
||||
features.push('browser');
|
||||
}
|
||||
|
||||
// 添加功能检测特征
|
||||
if (typeof Worker !== 'undefined') {
|
||||
features.push('Worker');
|
||||
}
|
||||
|
||||
if (typeof SharedArrayBuffer !== 'undefined') {
|
||||
features.push('SharedArrayBuffer');
|
||||
}
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {
|
||||
features.push('hardwareConcurrency');
|
||||
}
|
||||
|
||||
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
||||
features.push('performance.now');
|
||||
}
|
||||
|
||||
if (typeof Blob !== 'undefined') {
|
||||
features.push('Blob');
|
||||
}
|
||||
|
||||
if (typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function') {
|
||||
features.push('URL.createObjectURL');
|
||||
}
|
||||
|
||||
return {
|
||||
platform,
|
||||
confident,
|
||||
features,
|
||||
adapterClass
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为微信小游戏环境
|
||||
*/
|
||||
private static isWeChatMiniGame(): boolean {
|
||||
// 检查wx全局对象
|
||||
if (typeof (globalThis as any).wx !== 'undefined') {
|
||||
const wx = (globalThis as any).wx;
|
||||
// 检查微信小游戏特有的API
|
||||
return !!(wx.getSystemInfo && wx.createCanvas && wx.createImage);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为字节跳动小游戏环境
|
||||
*/
|
||||
private static isByteDanceMiniGame(): boolean {
|
||||
// 检查tt全局对象
|
||||
if (typeof (globalThis as any).tt !== 'undefined') {
|
||||
const tt = (globalThis as any).tt;
|
||||
// 检查字节跳动小游戏特有的API
|
||||
return !!(tt.getSystemInfo && tt.createCanvas && tt.createImage);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为支付宝小游戏环境
|
||||
*/
|
||||
private static isAlipayMiniGame(): boolean {
|
||||
// 检查my全局对象
|
||||
if (typeof (globalThis as any).my !== 'undefined') {
|
||||
const my = (globalThis as any).my;
|
||||
// 检查支付宝小游戏特有的API
|
||||
return !!(my.getSystemInfo && my.createCanvas);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为百度小游戏环境
|
||||
*/
|
||||
private static isBaiduMiniGame(): boolean {
|
||||
// 检查swan全局对象
|
||||
if (typeof (globalThis as any).swan !== 'undefined') {
|
||||
const swan = (globalThis as any).swan;
|
||||
// 检查百度小游戏特有的API
|
||||
return !!(swan.getSystemInfo && swan.createCanvas);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为浏览器环境
|
||||
*/
|
||||
private static isBrowser(): boolean {
|
||||
// 检查浏览器特有的对象和API
|
||||
return typeof window !== 'undefined' &&
|
||||
typeof document !== 'undefined' &&
|
||||
typeof navigator !== 'undefined' &&
|
||||
window.location !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详细的环境信息(用于调试)
|
||||
*/
|
||||
public static getDetailedInfo(): Record<string, any> {
|
||||
const info: Record<string, any> = {};
|
||||
|
||||
// 基础检测
|
||||
info.userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown';
|
||||
info.platform = typeof navigator !== 'undefined' ? navigator.platform : 'unknown';
|
||||
|
||||
// 全局对象检测
|
||||
info.globalObjects = {
|
||||
window: typeof window !== 'undefined',
|
||||
document: typeof document !== 'undefined',
|
||||
navigator: typeof navigator !== 'undefined',
|
||||
wx: typeof (globalThis as any).wx !== 'undefined',
|
||||
tt: typeof (globalThis as any).tt !== 'undefined',
|
||||
my: typeof (globalThis as any).my !== 'undefined',
|
||||
swan: typeof (globalThis as any).swan !== 'undefined'
|
||||
};
|
||||
|
||||
// Worker相关检测
|
||||
info.workerSupport = {
|
||||
Worker: typeof Worker !== 'undefined',
|
||||
SharedWorker: typeof SharedWorker !== 'undefined',
|
||||
ServiceWorker: typeof navigator !== 'undefined' && 'serviceWorker' in navigator,
|
||||
SharedArrayBuffer: typeof SharedArrayBuffer !== 'undefined',
|
||||
crossOriginIsolated: typeof self !== 'undefined' ? self.crossOriginIsolated : false
|
||||
};
|
||||
|
||||
// 性能相关检测
|
||||
info.performance = {
|
||||
performanceNow: typeof performance !== 'undefined' && typeof performance.now === 'function',
|
||||
hardwareConcurrency: typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : undefined
|
||||
};
|
||||
|
||||
// 其他API检测
|
||||
info.apiSupport = {
|
||||
Blob: typeof Blob !== 'undefined',
|
||||
URL: typeof URL !== 'undefined',
|
||||
createObjectURL: typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function',
|
||||
ArrayBuffer: typeof ArrayBuffer !== 'undefined',
|
||||
TypedArrays: typeof Float32Array !== 'undefined'
|
||||
};
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
206
packages/core/src/Platform/PlatformManager.ts
Normal file
206
packages/core/src/Platform/PlatformManager.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import type { IPlatformAdapter } from './IPlatformAdapter';
|
||||
import { PlatformDetector } from './PlatformDetector';
|
||||
import { BrowserAdapter } from './BrowserAdapter';
|
||||
import { WeChatMiniGameAdapter } from './WeChatMiniGameAdapter';
|
||||
import { createLogger, type ILogger } from '../Utils/Logger';
|
||||
|
||||
/**
|
||||
* 平台管理器
|
||||
* 负责自动检测平台并提供对应的适配器
|
||||
*/
|
||||
export class PlatformManager {
|
||||
private static instance: PlatformManager;
|
||||
private adapter: IPlatformAdapter | null = null;
|
||||
private detectionResult: any = null;
|
||||
private readonly logger: ILogger;
|
||||
|
||||
private constructor() {
|
||||
this.logger = createLogger('PlatformManager');
|
||||
this.detectAndInitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static getInstance(): PlatformManager {
|
||||
if (!PlatformManager.instance) {
|
||||
PlatformManager.instance = new PlatformManager();
|
||||
}
|
||||
return PlatformManager.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前平台适配器
|
||||
*/
|
||||
public getAdapter(): IPlatformAdapter {
|
||||
if (!this.adapter) {
|
||||
throw new Error('平台适配器未初始化,请检查平台环境');
|
||||
}
|
||||
return this.adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台检测结果
|
||||
*/
|
||||
public getDetectionResult() {
|
||||
return this.detectionResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置适配器(用于测试或特殊情况)
|
||||
*/
|
||||
public setAdapter(adapter: IPlatformAdapter): void {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测平台并初始化对应的适配器
|
||||
*/
|
||||
private detectAndInitialize(): void {
|
||||
this.detectionResult = PlatformDetector.detect();
|
||||
|
||||
try {
|
||||
switch (this.detectionResult.platform) {
|
||||
case 'wechat-minigame':
|
||||
this.adapter = new WeChatMiniGameAdapter();
|
||||
break;
|
||||
|
||||
case 'bytedance-minigame':
|
||||
// TODO: 实现字节跳动小游戏适配器
|
||||
this.logger.warn('字节跳动小游戏适配器尚未实现,降级到浏览器适配器');
|
||||
this.adapter = new BrowserAdapter();
|
||||
break;
|
||||
|
||||
case 'alipay-minigame':
|
||||
// TODO: 实现支付宝小游戏适配器
|
||||
this.logger.warn('支付宝小游戏适配器尚未实现,降级到浏览器适配器');
|
||||
this.adapter = new BrowserAdapter();
|
||||
break;
|
||||
|
||||
case 'baidu-minigame':
|
||||
// TODO: 实现百度小游戏适配器
|
||||
this.logger.warn('百度小游戏适配器尚未实现,降级到浏览器适配器');
|
||||
this.adapter = new BrowserAdapter();
|
||||
break;
|
||||
|
||||
case 'browser':
|
||||
default:
|
||||
this.adapter = new BrowserAdapter();
|
||||
break;
|
||||
}
|
||||
|
||||
// 输出初始化信息
|
||||
this.logInitializationInfo();
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('平台适配器初始化失败:', error);
|
||||
// 降级到浏览器适配器
|
||||
this.adapter = new BrowserAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出初始化信息
|
||||
*/
|
||||
private logInitializationInfo(): void {
|
||||
if (!this.adapter) return;
|
||||
|
||||
const config = this.adapter.getPlatformConfig();
|
||||
|
||||
this.logger.info(`平台适配器初始化完成:`, {
|
||||
platform: this.detectionResult.platform,
|
||||
adapterName: this.adapter.name,
|
||||
version: this.adapter.version,
|
||||
features: this.detectionResult.features,
|
||||
config: {
|
||||
maxWorkerCount: config.maxWorkerCount,
|
||||
supportsSharedArrayBuffer: this.adapter.isSharedArrayBufferSupported(),
|
||||
supportsWorker: this.adapter.isWorkerSupported(),
|
||||
hardwareConcurrency: this.adapter.getHardwareConcurrency()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详细的平台信息(用于调试)
|
||||
*/
|
||||
public getDetailedInfo(): any {
|
||||
return {
|
||||
detectionResult: this.detectionResult,
|
||||
detailedInfo: PlatformDetector.getDetailedInfo(),
|
||||
adapterInfo: this.adapter ? {
|
||||
name: this.adapter.name,
|
||||
version: this.adapter.version,
|
||||
config: this.adapter.getPlatformConfig()
|
||||
} : null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前平台是否支持特定功能
|
||||
*/
|
||||
public supportsFeature(feature: 'worker' | 'shared-array-buffer' | 'transferable-objects' | 'module-worker'): boolean {
|
||||
if (!this.adapter) return false;
|
||||
|
||||
const config = this.adapter.getPlatformConfig();
|
||||
|
||||
switch (feature) {
|
||||
case 'worker':
|
||||
return this.adapter.isWorkerSupported();
|
||||
case 'shared-array-buffer':
|
||||
return this.adapter.isSharedArrayBufferSupported();
|
||||
case 'transferable-objects':
|
||||
return config.supportsTransferableObjects;
|
||||
case 'module-worker':
|
||||
return config.supportsModuleWorker;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础的Worker配置信息(不做自动决策)
|
||||
* 用户应该根据自己的业务需求来配置Worker参数
|
||||
*/
|
||||
public getBasicWorkerConfig(): {
|
||||
platformSupportsWorker: boolean;
|
||||
platformSupportsSharedArrayBuffer: boolean;
|
||||
platformMaxWorkerCount: number;
|
||||
platformLimitations: any;
|
||||
} {
|
||||
if (!this.adapter) {
|
||||
return {
|
||||
platformSupportsWorker: false,
|
||||
platformSupportsSharedArrayBuffer: false,
|
||||
platformMaxWorkerCount: 1,
|
||||
platformLimitations: {}
|
||||
};
|
||||
}
|
||||
|
||||
const config = this.adapter.getPlatformConfig();
|
||||
|
||||
return {
|
||||
platformSupportsWorker: this.adapter.isWorkerSupported(),
|
||||
platformSupportsSharedArrayBuffer: this.adapter.isSharedArrayBufferSupported(),
|
||||
platformMaxWorkerCount: config.maxWorkerCount,
|
||||
platformLimitations: config.limitations || {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步获取完整的平台配置信息(包含性能信息)
|
||||
*/
|
||||
public async getFullPlatformConfig(): Promise<any> {
|
||||
if (!this.adapter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果适配器支持异步获取配置,使用异步方法
|
||||
if (typeof this.adapter.getPlatformConfigAsync === 'function') {
|
||||
return await this.adapter.getPlatformConfigAsync();
|
||||
}
|
||||
|
||||
// 否则返回同步配置
|
||||
return this.adapter.getPlatformConfig();
|
||||
}
|
||||
}
|
||||
451
packages/core/src/Platform/WeChatMiniGameAdapter.ts
Normal file
451
packages/core/src/Platform/WeChatMiniGameAdapter.ts
Normal file
@@ -0,0 +1,451 @@
|
||||
/// <reference types="minigame-api-typings" />
|
||||
|
||||
import type {
|
||||
IPlatformAdapter,
|
||||
PlatformWorker,
|
||||
WorkerCreationOptions,
|
||||
PlatformConfig,
|
||||
WeChatDeviceInfo
|
||||
} from './IPlatformAdapter';
|
||||
import { createLogger, type ILogger } from '../Utils/Logger';
|
||||
|
||||
/**
|
||||
* 微信小游戏平台适配器
|
||||
* 适配微信小游戏环境的特殊限制和API
|
||||
*/
|
||||
export class WeChatMiniGameAdapter implements IPlatformAdapter {
|
||||
public readonly name = 'wechat-minigame';
|
||||
public readonly version = this.getWeChatVersion();
|
||||
|
||||
private readonly wx: WechatMinigame.Wx;
|
||||
private readonly logger: ILogger;
|
||||
|
||||
constructor() {
|
||||
this.logger = createLogger('WeChatMiniGameAdapter');
|
||||
this.wx = (globalThis as any).wx;
|
||||
if (!this.wx) {
|
||||
throw new Error('微信小游戏环境未检测到wx对象');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持Worker
|
||||
* 微信小游戏虽然有Worker API,但限制太严格,对于动态ECS系统不实用
|
||||
*/
|
||||
public isWorkerSupported(): boolean {
|
||||
// 虽然微信小游戏有Worker API,但由于以下限制,实际上不适用于动态ECS系统:
|
||||
// 1. 不能动态创建Worker脚本(必须预配置文件)
|
||||
// 2. 禁止eval(不能动态执行用户的处理函数)
|
||||
// 3. 需要用户手动配置game.json
|
||||
// 因此对于WorkerEntitySystem来说,认为不支持Worker
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持SharedArrayBuffer
|
||||
* 微信小游戏不支持SharedArrayBuffer,因为:
|
||||
* 1. 不满足crossOriginIsolated要求
|
||||
* 2. 小游戏运行在受限沙箱环境中
|
||||
* 3. 出于安全考虑,微信限制了此API
|
||||
*/
|
||||
public isSharedArrayBufferSupported(): boolean {
|
||||
// 微信小游戏明确不支持SharedArrayBuffer
|
||||
// 即使有API存在,也无法在沙箱环境中正常使用
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取硬件并发数
|
||||
* 微信小游戏没有直接的CPU核心数API,返回保守的默认值
|
||||
* 用户应该根据自己的业务需求和测试结果来设置Worker数量
|
||||
*/
|
||||
public getHardwareConcurrency(): number {
|
||||
// 微信小游戏平台返回保守的默认值
|
||||
// 用户可以通过getDevicePerformanceInfo()获取详细信息来自行判断
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备性能信息(供用户参考)
|
||||
* 用户可以根据这些信息自行决定Worker配置
|
||||
*/
|
||||
public getDevicePerformanceInfo(): Promise<{
|
||||
benchmarkLevel?: number;
|
||||
modelLevel?: number;
|
||||
memorySize?: string;
|
||||
cpuType?: string;
|
||||
platform?: string;
|
||||
system?: string;
|
||||
}> {
|
||||
return new Promise((resolve) => {
|
||||
const result: any = {};
|
||||
|
||||
// 获取设备基础信息
|
||||
if (typeof this.wx.getDeviceInfo === 'function') {
|
||||
try {
|
||||
const deviceInfo = this.wx.getDeviceInfo();
|
||||
Object.assign(result, {
|
||||
memorySize: deviceInfo.memorySize,
|
||||
cpuType: deviceInfo.cpuType,
|
||||
platform: deviceInfo.platform,
|
||||
system: deviceInfo.system,
|
||||
benchmarkLevel: deviceInfo.benchmarkLevel
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.warn('获取设备信息失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取性能基准信息
|
||||
if (typeof this.wx.getDeviceBenchmarkInfo === 'function') {
|
||||
this.wx.getDeviceBenchmarkInfo({
|
||||
success: (res) => {
|
||||
result.benchmarkLevel = res.benchmarkLevel;
|
||||
result.modelLevel = res.modelLevel;
|
||||
resolve(result);
|
||||
},
|
||||
fail: () => {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Worker
|
||||
* 微信小游戏不支持WorkerEntitySystem所需的动态Worker创建
|
||||
*/
|
||||
public createWorker(_script: string, _options: WorkerCreationOptions = {}): PlatformWorker {
|
||||
throw new Error(
|
||||
'微信小游戏不支持WorkerEntitySystem。' +
|
||||
'原因:1) 不能动态创建Worker脚本 2) 禁止eval动态执行代码。' +
|
||||
'WorkerEntitySystem将自动降级到同步模式运行。'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从预配置的文件路径创建Worker(仅供特殊用途)
|
||||
* 注意:这不适用于WorkerEntitySystem,仅供其他特殊Worker需求使用
|
||||
*/
|
||||
public createWorkerFromPath(scriptPath: string, options: {
|
||||
useExperimentalWorker?: boolean;
|
||||
name?: string;
|
||||
} = {}): PlatformWorker {
|
||||
// 即使支持原生Worker API,也明确说明不适用于ECS
|
||||
this.logger.warn('微信小游戏Worker不适用于WorkerEntitySystem,建议使用同步模式');
|
||||
return new WeChatWorker(scriptPath, options, this.wx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SharedArrayBuffer
|
||||
* 微信小游戏通常不支持,返回null
|
||||
*/
|
||||
public createSharedArrayBuffer(length: number): SharedArrayBuffer | null {
|
||||
if (!this.isSharedArrayBufferSupported()) {
|
||||
this.logger.info('微信小游戏环境不支持SharedArrayBuffer,将使用Worker模式');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new SharedArrayBuffer(length);
|
||||
} catch (error) {
|
||||
this.logger.warn('SharedArrayBuffer创建失败', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取高精度时间戳
|
||||
*/
|
||||
public getHighResTimestamp(): number {
|
||||
// 微信小游戏支持performance.now()
|
||||
if (typeof performance !== 'undefined' && performance.now) {
|
||||
return performance.now();
|
||||
}
|
||||
|
||||
// 备选方案:使用Date.now()
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台配置(同步版本,不包含异步获取的性能信息)
|
||||
*/
|
||||
public getPlatformConfig(): PlatformConfig {
|
||||
return {
|
||||
maxWorkerCount: 0, // 不支持WorkerEntitySystem
|
||||
supportsModuleWorker: false,
|
||||
supportsTransferableObjects: false, // 对WorkerEntitySystem无意义
|
||||
maxSharedArrayBufferSize: 0, // 不支持SharedArrayBuffer
|
||||
workerScriptPrefix: '',
|
||||
limitations: {
|
||||
noEval: true, // 微信小游戏禁止eval
|
||||
requiresWorkerInit: true,
|
||||
// 明确说明Worker限制
|
||||
workerNotSupported: true,
|
||||
workerLimitations: [
|
||||
'不能动态创建Worker脚本',
|
||||
'禁止eval动态执行代码',
|
||||
'必须预配置Worker文件路径',
|
||||
'不适用于动态ECS系统'
|
||||
]
|
||||
},
|
||||
extensions: {
|
||||
wechatVersion: this.version,
|
||||
supportedAPIs: this.getSupportedAPIs(),
|
||||
deviceInfo: this.getDeviceInfo()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步获取平台配置(包含性能信息)
|
||||
*/
|
||||
public async getPlatformConfigAsync(): Promise<PlatformConfig> {
|
||||
const baseConfig = this.getPlatformConfig();
|
||||
|
||||
try {
|
||||
// 异步获取性能信息
|
||||
const performanceInfo = await this.getDevicePerformanceInfo();
|
||||
|
||||
return {
|
||||
...baseConfig,
|
||||
extensions: {
|
||||
...baseConfig.extensions,
|
||||
performanceInfo
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.warn('获取性能信息失败,返回基础配置', error);
|
||||
return baseConfig;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取微信版本信息
|
||||
*/
|
||||
private getWeChatVersion(): string {
|
||||
try {
|
||||
const systemInfo = this.wx.getSystemInfoSync();
|
||||
return `WeChat ${systemInfo.version} (${systemInfo.platform})`;
|
||||
} catch {
|
||||
return 'WeChat Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取支持的API列表
|
||||
*/
|
||||
private getSupportedAPIs(): string[] {
|
||||
const apis: string[] = [];
|
||||
|
||||
// 检查常用的微信小游戏API
|
||||
const commonAPIs = [
|
||||
'getSystemInfo', 'createCanvas', 'createImage', 'createAudio',
|
||||
'createWorker', 'downloadFile', 'request', 'connectSocket',
|
||||
'setStorage', 'getStorage', 'showToast', 'showModal'
|
||||
];
|
||||
|
||||
for (const api of commonAPIs) {
|
||||
if (typeof (this.wx as any)[api] === 'function') {
|
||||
apis.push(api);
|
||||
}
|
||||
}
|
||||
|
||||
return apis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备信息
|
||||
*/
|
||||
public getDeviceInfo(): WeChatDeviceInfo {
|
||||
try {
|
||||
const deviceInfo: any = {};
|
||||
|
||||
// 优先使用最新的设备信息API
|
||||
if (typeof this.wx.getDeviceInfo === 'function') {
|
||||
const info = this.wx.getDeviceInfo();
|
||||
Object.assign(deviceInfo, {
|
||||
brand: info.brand,
|
||||
model: info.model,
|
||||
platform: info.platform,
|
||||
system: info.system,
|
||||
benchmarkLevel: info.benchmarkLevel,
|
||||
cpuType: info.cpuType,
|
||||
memorySize: info.memorySize,
|
||||
deviceAbi: info.deviceAbi,
|
||||
abi: info.abi
|
||||
});
|
||||
}
|
||||
|
||||
// 获取设备性能基准信息
|
||||
if (typeof this.wx.getDeviceBenchmarkInfo === 'function') {
|
||||
this.wx.getDeviceBenchmarkInfo({
|
||||
success: (res) => {
|
||||
deviceInfo.benchmarkLevel = res.benchmarkLevel;
|
||||
deviceInfo.modelLevel = res.modelLevel;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取窗口信息
|
||||
if (typeof this.wx.getWindowInfo === 'function') {
|
||||
const windowInfo = this.wx.getWindowInfo();
|
||||
Object.assign(deviceInfo, {
|
||||
screenWidth: windowInfo.screenWidth,
|
||||
screenHeight: windowInfo.screenHeight,
|
||||
screenTop: windowInfo.screenTop,
|
||||
windowWidth: windowInfo.windowWidth,
|
||||
windowHeight: windowInfo.windowHeight,
|
||||
pixelRatio: windowInfo.pixelRatio,
|
||||
statusBarHeight: windowInfo.statusBarHeight,
|
||||
safeArea: windowInfo.safeArea
|
||||
});
|
||||
}
|
||||
|
||||
// 获取应用基础信息
|
||||
if (typeof this.wx.getAppBaseInfo === 'function') {
|
||||
try {
|
||||
const appInfo = this.wx.getAppBaseInfo();
|
||||
Object.assign(deviceInfo, {
|
||||
version: appInfo.version,
|
||||
language: appInfo.language,
|
||||
theme: appInfo.theme,
|
||||
SDKVersion: appInfo.SDKVersion,
|
||||
enableDebug: appInfo.enableDebug,
|
||||
fontSizeSetting: appInfo.fontSizeSetting,
|
||||
// host是一个对象,不是hostName
|
||||
host: appInfo.host
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.warn('获取应用基础信息失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果新API不完整,才使用废弃API作为兜底
|
||||
if (Object.keys(deviceInfo).length === 0 && typeof this.wx.getSystemInfoSync === 'function') {
|
||||
this.logger.warn('新API不可用,使用废弃的getSystemInfoSync作为兜底');
|
||||
try {
|
||||
const systemInfo = this.wx.getSystemInfoSync();
|
||||
return {
|
||||
brand: systemInfo.brand,
|
||||
model: systemInfo.model,
|
||||
platform: systemInfo.platform,
|
||||
system: systemInfo.system,
|
||||
version: systemInfo.version,
|
||||
benchmarkLevel: systemInfo.benchmarkLevel,
|
||||
screenWidth: systemInfo.screenWidth,
|
||||
screenHeight: systemInfo.screenHeight,
|
||||
pixelRatio: systemInfo.pixelRatio
|
||||
} as WeChatDeviceInfo;
|
||||
} catch (error) {
|
||||
this.logger.error('所有获取设备信息的方法都失败了', error);
|
||||
return {} as WeChatDeviceInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return deviceInfo as WeChatDeviceInfo;
|
||||
} catch (error) {
|
||||
this.logger.warn('获取设备信息失败', error);
|
||||
return {} as WeChatDeviceInfo;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小游戏Worker封装
|
||||
*/
|
||||
class WeChatWorker implements PlatformWorker {
|
||||
private _state: 'running' | 'terminated' = 'running';
|
||||
private worker!: WechatMinigame.Worker;
|
||||
private readonly logger: ILogger;
|
||||
|
||||
constructor(
|
||||
private scriptPath: string,
|
||||
private options: { useExperimentalWorker?: boolean; name?: string },
|
||||
private wx: WechatMinigame.Wx
|
||||
) {
|
||||
this.logger = createLogger('WeChatWorker');
|
||||
this.createWeChatWorker();
|
||||
}
|
||||
|
||||
public get state(): 'running' | 'terminated' {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public postMessage(message: any, _transfer?: Transferable[]): void {
|
||||
if (this._state === 'terminated') {
|
||||
throw new Error('Worker已被终止');
|
||||
}
|
||||
|
||||
if (this.worker) {
|
||||
this.worker.postMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public onMessage(handler: (event: { data: any }) => void): void {
|
||||
if (this.worker) {
|
||||
this.worker.onMessage((data: any) => {
|
||||
handler({ data });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onError(handler: (error: ErrorEvent) => void): void {
|
||||
if (this.worker) {
|
||||
this.worker.onError((error: any) => {
|
||||
// 转换微信错误格式为标准ErrorEvent格式
|
||||
const errorEvent = new ErrorEvent('error', {
|
||||
message: error.message || '微信Worker错误',
|
||||
filename: error.filename || '',
|
||||
lineno: error.lineno || 0,
|
||||
colno: error.colno || 0,
|
||||
error: error
|
||||
});
|
||||
handler(errorEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
if (this._state === 'running' && this.worker) {
|
||||
this.worker.terminate();
|
||||
this._state = 'terminated';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信Worker
|
||||
* 使用预配置的Worker脚本文件路径
|
||||
*/
|
||||
private createWeChatWorker(): void {
|
||||
try {
|
||||
// 使用微信小游戏的createWorker API,传入预配置的脚本路径
|
||||
this.worker = this.wx.createWorker(this.scriptPath, {
|
||||
useExperimentalWorker: this.options.useExperimentalWorker || false
|
||||
});
|
||||
|
||||
// 监听Worker被系统回收事件(实验性Worker特有)
|
||||
if (this.options.useExperimentalWorker && this.worker.onProcessKilled) {
|
||||
this.worker.onProcessKilled(() => {
|
||||
this.logger.warn(`微信Worker ${this.scriptPath} 被系统回收`);
|
||||
this._state = 'terminated';
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('创建微信Worker失败:', error);
|
||||
throw new Error(
|
||||
`无法创建微信Worker: ${error}。` +
|
||||
`请确保已在game.json中配置workers字段,并且Worker文件 ${this.scriptPath} 存在。`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
packages/core/src/Platform/index.ts
Normal file
48
packages/core/src/Platform/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 平台适配模块导出
|
||||
*/
|
||||
|
||||
// 接口和类型
|
||||
export type {
|
||||
IPlatformAdapter,
|
||||
PlatformWorker,
|
||||
WorkerCreationOptions,
|
||||
PlatformConfig,
|
||||
PlatformDetectionResult,
|
||||
WeChatDeviceInfo,
|
||||
BrowserDeviceInfo
|
||||
} from './IPlatformAdapter';
|
||||
|
||||
// 平台检测器
|
||||
export { PlatformDetector } from './PlatformDetector';
|
||||
|
||||
// 平台管理器
|
||||
export { PlatformManager } from './PlatformManager';
|
||||
|
||||
// 平台适配器实现
|
||||
export { BrowserAdapter } from './BrowserAdapter';
|
||||
export { WeChatMiniGameAdapter } from './WeChatMiniGameAdapter';
|
||||
|
||||
// 内部导入用于便利函数
|
||||
import { PlatformManager } from './PlatformManager';
|
||||
|
||||
// 便利函数
|
||||
export function getCurrentPlatform() {
|
||||
return PlatformManager.getInstance().getDetectionResult();
|
||||
}
|
||||
|
||||
export function getCurrentAdapter() {
|
||||
return PlatformManager.getInstance().getAdapter();
|
||||
}
|
||||
|
||||
export function getBasicWorkerConfig() {
|
||||
return PlatformManager.getInstance().getBasicWorkerConfig();
|
||||
}
|
||||
|
||||
export function getFullPlatformConfig() {
|
||||
return PlatformManager.getInstance().getFullPlatformConfig();
|
||||
}
|
||||
|
||||
export function supportsFeature(feature: 'worker' | 'shared-array-buffer' | 'transferable-objects' | 'module-worker') {
|
||||
return PlatformManager.getInstance().supportsFeature(feature);
|
||||
}
|
||||
@@ -33,3 +33,6 @@ export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './
|
||||
// 工具类和类型定义
|
||||
export * from './Utils';
|
||||
export * from './Types';
|
||||
|
||||
// 平台适配
|
||||
export * from './Platform';
|
||||
Reference in New Issue
Block a user