Files
esengine/packages/engine/platform-wechat/src/WeChatAdapter.ts

290 lines
8.5 KiB
TypeScript
Raw Normal View History

/**
*
*/
import type {
IPlatformAdapter,
PlatformWorker,
WorkerCreationOptions,
PlatformConfig
} from '@esengine/ecs-framework';
import type { SystemInfo } from '@esengine/platform-common';
import { WeChatCanvasSubsystem } from './subsystems/WeChatCanvasSubsystem';
import { WeChatAudioSubsystem } from './subsystems/WeChatAudioSubsystem';
import { WeChatStorageSubsystem } from './subsystems/WeChatStorageSubsystem';
import { WeChatNetworkSubsystem } from './subsystems/WeChatNetworkSubsystem';
import { WeChatInputSubsystem } from './subsystems/WeChatInputSubsystem';
import { WeChatFileSubsystem } from './subsystems/WeChatFileSubsystem';
import { WeChatWASMSubsystem } from './subsystems/WeChatWASMSubsystem';
import { getWx, isWeChatMiniGame } from './utils';
/**
* Worker
*/
class WeChatWorker implements PlatformWorker {
private _worker: WechatMinigame.Worker;
private _state: 'running' | 'terminated' = 'running';
constructor(worker: WechatMinigame.Worker) {
this._worker = worker;
}
get state(): 'running' | 'terminated' {
return this._state;
}
postMessage(message: any, _transfer?: Transferable[]): void {
this._worker.postMessage(message);
}
onMessage(handler: (event: { data: any }) => void): void {
this._worker.onMessage((res) => {
handler({ data: res });
});
}
onError(handler: (error: ErrorEvent) => void): void {
this._worker.onError((error) => {
handler(error as unknown as ErrorEvent);
});
}
terminate(): void {
this._worker.terminate();
this._state = 'terminated';
}
}
/**
*
*/
export class WeChatAdapter implements IPlatformAdapter {
readonly name = 'wechat-minigame';
readonly version: string;
// 子系统实例
private _canvas: WeChatCanvasSubsystem | null = null;
private _audio: WeChatAudioSubsystem | null = null;
private _storage: WeChatStorageSubsystem | null = null;
private _network: WeChatNetworkSubsystem | null = null;
private _input: WeChatInputSubsystem | null = null;
private _file: WeChatFileSubsystem | null = null;
private _wasm: WeChatWASMSubsystem | null = null;
private _deviceInfo: WechatMinigame.DeviceInfo | null = null;
private _windowInfo: WechatMinigame.WindowInfo | null = null;
private _appBaseInfo: WechatMinigame.AppBaseInfo | null = null;
constructor() {
if (!isWeChatMiniGame()) {
throw new Error('当前环境不是微信小游戏环境');
}
// 使用新的分离 API 获取系统信息
const wxApi = getWx();
this._deviceInfo = wxApi.getDeviceInfo();
this._windowInfo = wxApi.getWindowInfo();
this._appBaseInfo = wxApi.getAppBaseInfo();
this.version = this._appBaseInfo.SDKVersion;
}
// ========================================================================
// IPlatformAdapter 基础实现
// ========================================================================
isWorkerSupported(): boolean {
// 微信小游戏支持 Worker但有限制
return typeof getWx().createWorker === 'function';
}
isSharedArrayBufferSupported(): boolean {
// 微信小游戏不支持 SharedArrayBuffer
return false;
}
getHardwareConcurrency(): number {
// 微信小游戏无法获取真实核心数,返回保守值
return 2;
}
createWorker(script: string, options?: WorkerCreationOptions): PlatformWorker {
// 微信小游戏 Worker 需要指定文件路径,不支持内联脚本
// script 参数应该是 worker 文件的路径
const worker = getWx().createWorker(script, {
useExperimentalWorker: true
});
return new WeChatWorker(worker);
}
createSharedArrayBuffer(_length: number): SharedArrayBuffer | null {
// 微信小游戏不支持 SharedArrayBuffer
return null;
}
getHighResTimestamp(): number {
return Date.now();
}
getPlatformConfig(): PlatformConfig {
return {
maxWorkerCount: 1,
supportsModuleWorker: false,
supportsTransferableObjects: true,
maxSharedArrayBufferSize: 0,
workerScriptPrefix: '',
limitations: {
noEval: true,
requiresWorkerInit: true,
memoryLimit: this._deviceInfo?.memorySize
? parseInt(String(this._deviceInfo.memorySize)) * 1024 * 1024
: 256 * 1024 * 1024,
workerNotSupported: false,
workerLimitations: [
'Worker 必须使用独立文件,不支持内联脚本',
'仅支持 1 个 Worker 实例',
'不支持 SharedArrayBuffer',
'Worker 文件需要在 game.json 中配置'
]
},
extensions: {
platform: 'wechat-minigame',
sdkVersion: this._appBaseInfo?.SDKVersion
}
};
}
async getPlatformConfigAsync(): Promise<PlatformConfig> {
return this.getPlatformConfig();
}
// ========================================================================
// 子系统访问器
// ========================================================================
/**
* Canvas
*/
get canvas(): WeChatCanvasSubsystem {
if (!this._canvas) {
this._canvas = new WeChatCanvasSubsystem();
}
return this._canvas;
}
/**
*
*/
get audio(): WeChatAudioSubsystem {
if (!this._audio) {
this._audio = new WeChatAudioSubsystem();
}
return this._audio;
}
/**
*
*/
get storage(): WeChatStorageSubsystem {
if (!this._storage) {
this._storage = new WeChatStorageSubsystem();
}
return this._storage;
}
/**
*
*/
get network(): WeChatNetworkSubsystem {
if (!this._network) {
this._network = new WeChatNetworkSubsystem();
}
return this._network;
}
/**
*
*/
get input(): WeChatInputSubsystem {
if (!this._input) {
this._input = new WeChatInputSubsystem();
}
return this._input;
}
/**
*
*/
get file(): WeChatFileSubsystem {
if (!this._file) {
this._file = new WeChatFileSubsystem();
}
return this._file;
}
/**
* WASM
*/
get wasm(): WeChatWASMSubsystem {
if (!this._wasm) {
this._wasm = new WeChatWASMSubsystem();
}
return this._wasm;
}
// ========================================================================
// 系统信息
// ========================================================================
/**
*
*/
getSystemInfo(): SystemInfo {
const device = this._deviceInfo!;
const window = this._windowInfo!;
const app = this._appBaseInfo!;
return {
brand: device.brand,
model: device.model,
pixelRatio: window.pixelRatio,
screenWidth: window.screenWidth,
screenHeight: window.screenHeight,
windowWidth: window.windowWidth,
windowHeight: window.windowHeight,
statusBarHeight: window.statusBarHeight || 0,
system: device.system,
platform: device.platform as SystemInfo['platform'],
SDKVersion: app.SDKVersion,
benchmarkLevel: device.benchmarkLevel || 0,
memorySize: device.memorySize ? parseInt(String(device.memorySize)) : undefined
};
}
/**
*
*/
compareVersion(v1: string, v2: string): number {
const a1 = v1.split('.').map(Number);
const a2 = v2.split('.').map(Number);
const len = Math.max(a1.length, a2.length);
for (let i = 0; i < len; i++) {
const n1 = a1[i] || 0;
const n2 = a2[i] || 0;
if (n1 > n2) return 1;
if (n1 < n2) return -1;
}
return 0;
}
/**
* API
*/
canIUse(schema: string): boolean {
return getWx().canIUse(schema);
}
}