抽象worker接口,避免污染项目
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^20.19.0",
|
||||
"@types/node": "^20.19.17",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"rimraf": "^5.0.0",
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,7 @@ export interface PlatformDetectionResult {
|
||||
/**
|
||||
* 平台类型
|
||||
*/
|
||||
platform: 'browser' | 'wechat-minigame' | 'bytedance-minigame' | 'alipay-minigame' | 'baidu-minigame' | 'unknown';
|
||||
platform: 'browser' | 'wechat-minigame' | 'bytedance-minigame' | 'alipay-minigame' | 'baidu-minigame' | 'nodejs' | 'unknown';
|
||||
|
||||
/**
|
||||
* 是否确定检测结果
|
||||
@@ -197,93 +197,3 @@ export interface PlatformDetectionResult {
|
||||
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;
|
||||
}
|
||||
@@ -27,8 +27,15 @@ export class PlatformDetector {
|
||||
features.push('self');
|
||||
}
|
||||
|
||||
// 检测Node.js环境(优先级最高,因为Node.js可能包含全局对象模拟)
|
||||
if (this.isNodeJS()) {
|
||||
platform = 'nodejs';
|
||||
confident = true;
|
||||
adapterClass = 'NodeAdapter';
|
||||
features.push('nodejs', 'process', 'require');
|
||||
}
|
||||
// 检测微信小游戏环境
|
||||
if (this.isWeChatMiniGame()) {
|
||||
else if (this.isWeChatMiniGame()) {
|
||||
platform = 'wechat-minigame';
|
||||
confident = true;
|
||||
adapterClass = 'WeChatMiniGameAdapter';
|
||||
@@ -122,6 +129,28 @@ export class PlatformDetector {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为Node.js环境
|
||||
*/
|
||||
private static isNodeJS(): boolean {
|
||||
try {
|
||||
// 检查Node.js特有的全局对象和模块
|
||||
return !!(
|
||||
typeof process !== 'undefined' &&
|
||||
process.versions &&
|
||||
process.versions.node &&
|
||||
typeof require !== 'undefined' &&
|
||||
typeof module !== 'undefined' &&
|
||||
typeof exports !== 'undefined' &&
|
||||
// 确保不是在浏览器环境中的Node.js模拟
|
||||
typeof window === 'undefined' &&
|
||||
typeof document === 'undefined'
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为支付宝小游戏环境
|
||||
*/
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,106 +29,42 @@ export class PlatformManager {
|
||||
*/
|
||||
public getAdapter(): IPlatformAdapter {
|
||||
if (!this.adapter) {
|
||||
throw new Error('平台适配器未初始化,请检查平台环境');
|
||||
throw new Error('平台适配器未注册,请调用 registerAdapter() 注册适配器');
|
||||
}
|
||||
return this.adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台检测结果
|
||||
* 注册平台适配器
|
||||
*/
|
||||
public getDetectionResult() {
|
||||
return this.detectionResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置适配器(用于测试或特殊情况)
|
||||
*/
|
||||
public setAdapter(adapter: IPlatformAdapter): void {
|
||||
public registerAdapter(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()
|
||||
}
|
||||
this.logger.info(`平台适配器已注册: ${adapter.name}`, {
|
||||
name: adapter.name,
|
||||
version: adapter.version,
|
||||
supportsWorker: adapter.isWorkerSupported(),
|
||||
supportsSharedArrayBuffer: adapter.isSharedArrayBufferSupported(),
|
||||
hardwareConcurrency: 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 hasAdapter(): boolean {
|
||||
return this.adapter !== null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取平台适配器信息(用于调试)
|
||||
*/
|
||||
public getAdapterInfo(): any {
|
||||
return this.adapter ? {
|
||||
name: this.adapter.name,
|
||||
version: this.adapter.version,
|
||||
config: this.adapter.getPlatformConfig()
|
||||
} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +123,7 @@ export class PlatformManager {
|
||||
*/
|
||||
public async getFullPlatformConfig(): Promise<any> {
|
||||
if (!this.adapter) {
|
||||
return null;
|
||||
throw new Error('平台适配器未注册');
|
||||
}
|
||||
|
||||
// 如果适配器支持异步获取配置,使用异步方法
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
/// <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} 存在。`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,7 @@ export type {
|
||||
PlatformWorker,
|
||||
WorkerCreationOptions,
|
||||
PlatformConfig,
|
||||
PlatformDetectionResult,
|
||||
WeChatDeviceInfo,
|
||||
BrowserDeviceInfo
|
||||
PlatformDetectionResult
|
||||
} from './IPlatformAdapter';
|
||||
|
||||
// 平台检测器
|
||||
@@ -19,16 +17,13 @@ export { PlatformDetector } from './PlatformDetector';
|
||||
// 平台管理器
|
||||
export { PlatformManager } from './PlatformManager';
|
||||
|
||||
// 平台适配器实现
|
||||
export { BrowserAdapter } from './BrowserAdapter';
|
||||
export { WeChatMiniGameAdapter } from './WeChatMiniGameAdapter';
|
||||
|
||||
// 内部导入用于便利函数
|
||||
import { PlatformManager } from './PlatformManager';
|
||||
import type { IPlatformAdapter } from './IPlatformAdapter';
|
||||
|
||||
// 便利函数
|
||||
export function getCurrentPlatform() {
|
||||
return PlatformManager.getInstance().getDetectionResult();
|
||||
export function registerPlatformAdapter(adapter: IPlatformAdapter) {
|
||||
return PlatformManager.getInstance().registerAdapter(adapter);
|
||||
}
|
||||
|
||||
export function getCurrentAdapter() {
|
||||
@@ -45,4 +40,8 @@ export function getFullPlatformConfig() {
|
||||
|
||||
export function supportsFeature(feature: 'worker' | 'shared-array-buffer' | 'transferable-objects' | 'module-worker') {
|
||||
return PlatformManager.getInstance().supportsFeature(feature);
|
||||
}
|
||||
|
||||
export function hasAdapter() {
|
||||
return PlatformManager.getInstance().hasAdapter();
|
||||
}
|
||||
Reference in New Issue
Block a user