From 62bc6b547e424a370668899423ae877f188dc160 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Mon, 29 Sep 2025 13:21:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81wx/browser=E7=9A=84worker?= =?UTF-8?q?=EF=BC=88=E7=94=B1=E4=BA=8Ewx=E9=99=90=E5=88=B6=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=B8=8D=E5=BC=80=E5=90=AFworker)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 7 + package.json | 1 + .../src/ECS/Systems/WorkerEntitySystem.ts | 82 ++-- packages/core/src/Platform/BrowserAdapter.ts | 278 +++++++++++ .../core/src/Platform/IPlatformAdapter.ts | 289 +++++++++++ .../core/src/Platform/PlatformDetector.ts | 209 ++++++++ packages/core/src/Platform/PlatformManager.ts | 206 ++++++++ .../src/Platform/WeChatMiniGameAdapter.ts | 451 ++++++++++++++++++ packages/core/src/Platform/index.ts | 48 ++ packages/core/src/index.ts | 5 +- 10 files changed, 1545 insertions(+), 31 deletions(-) create mode 100644 packages/core/src/Platform/BrowserAdapter.ts create mode 100644 packages/core/src/Platform/IPlatformAdapter.ts create mode 100644 packages/core/src/Platform/PlatformDetector.ts create mode 100644 packages/core/src/Platform/PlatformManager.ts create mode 100644 packages/core/src/Platform/WeChatMiniGameAdapter.ts create mode 100644 packages/core/src/Platform/index.ts diff --git a/package-lock.json b/package-lock.json index 5f0b62e1..b1664610 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 9921f158..9bff7f85 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/packages/core/src/ECS/Systems/WorkerEntitySystem.ts b/packages/core/src/ECS/Systems/WorkerEntitySystem.ts index addb749d..c0da46ad 100644 --- a/packages/core/src/ECS/Systems/WorkerEntitySystem.ts +++ b/packages/core/src/ECS/Systems/WorkerEntitySystem.ts @@ -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 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 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 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 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 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 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(); 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); } /** diff --git a/packages/core/src/Platform/BrowserAdapter.ts b/packages/core/src/Platform/BrowserAdapter.ts new file mode 100644 index 00000000..df321ce2 --- /dev/null +++ b/packages/core/src/Platform/BrowserAdapter.ts @@ -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'; + } + } +} \ No newline at end of file diff --git a/packages/core/src/Platform/IPlatformAdapter.ts b/packages/core/src/Platform/IPlatformAdapter.ts new file mode 100644 index 00000000..53f58a4b --- /dev/null +++ b/packages/core/src/Platform/IPlatformAdapter.ts @@ -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; +} + +/** + * 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; +} + +/** + * 平台检测结果 + */ +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; +} \ No newline at end of file diff --git a/packages/core/src/Platform/PlatformDetector.ts b/packages/core/src/Platform/PlatformDetector.ts new file mode 100644 index 00000000..9ba02882 --- /dev/null +++ b/packages/core/src/Platform/PlatformDetector.ts @@ -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 { + const info: Record = {}; + + // 基础检测 + 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; + } +} \ No newline at end of file diff --git a/packages/core/src/Platform/PlatformManager.ts b/packages/core/src/Platform/PlatformManager.ts new file mode 100644 index 00000000..b6400ae7 --- /dev/null +++ b/packages/core/src/Platform/PlatformManager.ts @@ -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 { + if (!this.adapter) { + return null; + } + + // 如果适配器支持异步获取配置,使用异步方法 + if (typeof this.adapter.getPlatformConfigAsync === 'function') { + return await this.adapter.getPlatformConfigAsync(); + } + + // 否则返回同步配置 + return this.adapter.getPlatformConfig(); + } +} \ No newline at end of file diff --git a/packages/core/src/Platform/WeChatMiniGameAdapter.ts b/packages/core/src/Platform/WeChatMiniGameAdapter.ts new file mode 100644 index 00000000..b3a6e678 --- /dev/null +++ b/packages/core/src/Platform/WeChatMiniGameAdapter.ts @@ -0,0 +1,451 @@ +/// + +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 { + 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} 存在。` + ); + } + } +} \ No newline at end of file diff --git a/packages/core/src/Platform/index.ts b/packages/core/src/Platform/index.ts new file mode 100644 index 00000000..b45a91ae --- /dev/null +++ b/packages/core/src/Platform/index.ts @@ -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); +} \ No newline at end of file diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f51fc328..95c93742 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -32,4 +32,7 @@ export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './ // 工具类和类型定义 export * from './Utils'; -export * from './Types'; \ No newline at end of file +export * from './Types'; + +// 平台适配 +export * from './Platform'; \ No newline at end of file