From d9a279addec1c5f895370767daa6934015999a40 Mon Sep 17 00:00:00 2001 From: esengine <359807859@qq.com> Date: Wed, 4 Feb 2026 20:09:45 -0800 Subject: [PATCH 1/3] feat: Add platform adapter and npm publish workflow - Add platform adapter layer for Web and WeChat separation - Add WeChat-specific polyfills (fetch, wasm, fs) - Multi-entry build: index.js (Web), index.wechat.js (WeChat) - Add GitHub Actions workflow for npm publishing --- .github/workflows/publish-sdk.yml | 44 +++++++ sdk/package.json | 1 + sdk/rollup.config.js | 75 ++++++++--- sdk/src/core.ts | 209 ++++++++++++++++++++++++++++++ sdk/src/index.ts | 189 +-------------------------- sdk/src/index.wechat.ts | 23 ++++ sdk/src/platform/base.ts | 92 +++++++++++++ sdk/src/platform/index.ts | 34 +++++ sdk/src/platform/types.ts | 99 ++++++++++++++ sdk/src/platform/web.ts | 93 +++++++++++++ sdk/src/platform/wechat/fetch.ts | 109 ++++++++++++++++ sdk/src/platform/wechat/fs.ts | 122 +++++++++++++++++ sdk/src/platform/wechat/index.ts | 81 ++++++++++++ sdk/src/platform/wechat/wasm.ts | 89 +++++++++++++ sdk/src/preview/PreviewPlugin.ts | 5 +- 15 files changed, 1064 insertions(+), 201 deletions(-) create mode 100644 .github/workflows/publish-sdk.yml create mode 100644 sdk/src/core.ts create mode 100644 sdk/src/index.wechat.ts create mode 100644 sdk/src/platform/base.ts create mode 100644 sdk/src/platform/index.ts create mode 100644 sdk/src/platform/types.ts create mode 100644 sdk/src/platform/web.ts create mode 100644 sdk/src/platform/wechat/fetch.ts create mode 100644 sdk/src/platform/wechat/fs.ts create mode 100644 sdk/src/platform/wechat/index.ts create mode 100644 sdk/src/platform/wechat/wasm.ts diff --git a/.github/workflows/publish-sdk.yml b/.github/workflows/publish-sdk.yml new file mode 100644 index 000000000..04ec038e5 --- /dev/null +++ b/.github/workflows/publish-sdk.yml @@ -0,0 +1,44 @@ +name: Publish SDK + +on: + push: + tags: + - 'sdk-v*' + workflow_dispatch: + inputs: + version: + description: 'Version to publish (e.g., 0.2.0)' + required: false + +jobs: + publish: + name: Publish to npm + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Install dependencies + working-directory: sdk + run: pnpm install + + - name: Build SDK + working-directory: sdk + run: pnpm run build + + - name: Publish to npm + working-directory: sdk + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/sdk/package.json b/sdk/package.json index a60e4f672..27912eafd 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -45,6 +45,7 @@ "license": "MIT", "devDependencies": { "@rollup/plugin-typescript": "^11.1.5", + "minigame-api-typings": "^3.8.16", "rimraf": "^5.0.5", "rollup": "^4.9.0", "rollup-plugin-dts": "^6.1.0", diff --git a/sdk/rollup.config.js b/sdk/rollup.config.js index bc434b2b7..7a7213503 100644 --- a/sdk/rollup.config.js +++ b/sdk/rollup.config.js @@ -1,12 +1,20 @@ import typescript from '@rollup/plugin-typescript'; import dts from 'rollup-plugin-dts'; +// ============================================================================= +// Module Entries +// ============================================================================= + const modules = { - 'index': 'src/index.ts', + 'index': 'src/index.ts', // Web entry + 'index.wechat': 'src/index.wechat.ts', // WeChat entry 'wasm': 'src/wasm.ts', }; -// Local dist builds (ESM) +// ============================================================================= +// Local Dist Builds (ESM) +// ============================================================================= + const esmBuilds = Object.entries(modules).map(([name, input]) => ({ input, output: { @@ -25,21 +33,35 @@ const esmBuilds = Object.entries(modules).map(([name, input]) => ({ }, })); -// Type declarations -const dtsBuilds = Object.entries(modules).map(([name, input]) => ({ - input, - output: { - file: `dist/${name}.d.ts`, - format: 'esm', +// ============================================================================= +// Type Declarations +// ============================================================================= + +// Only generate .d.ts for main entries (Web and WeChat share the same types) +const dtsBuilds = [ + { + input: 'src/index.ts', + output: { + file: 'dist/index.d.ts', + format: 'esm', + }, + plugins: [dts()], }, - plugins: [dts()], -})); + { + input: 'src/wasm.ts', + output: { + file: 'dist/wasm.d.ts', + format: 'esm', + }, + plugins: [dts()], + }, +]; // ============================================================================= -// Desktop Public Builds (organized by format) +// Desktop Public Builds // ============================================================================= -// ESM build for editor preview +// ESM build for editor preview (Web) const esmPublicBuild = { input: 'src/index.ts', output: { @@ -58,8 +80,28 @@ const esmPublicBuild = { }, }; -// CJS build for WeChat Mini Game -const cjsPublicBuild = { +// CJS build for WeChat Mini Game (includes WeChat adapter) +const cjsWechatBuild = { + input: 'src/index.wechat.ts', + output: { + file: '../desktop/public/sdk/cjs/esengine.wechat.js', + format: 'cjs', + sourcemap: false, + exports: 'named', + }, + plugins: [ + typescript({ + tsconfig: './tsconfig.json', + declaration: false, + }), + ], + treeshake: { + moduleSideEffects: false, + }, +}; + +// CJS build for Web (no WeChat code) +const cjsWebBuild = { input: 'src/index.ts', output: { file: '../desktop/public/sdk/cjs/esengine.js', @@ -78,7 +120,7 @@ const cjsPublicBuild = { }, }; -// IIFE build for playable ads +// IIFE build for playable ads (Web, no WeChat code) const iifePublicBuild = { input: 'src/index.ts', output: { @@ -102,6 +144,7 @@ export default [ ...esmBuilds, ...dtsBuilds, esmPublicBuild, - cjsPublicBuild, + cjsWebBuild, + cjsWechatBuild, iifePublicBuild, ]; diff --git a/sdk/src/core.ts b/sdk/src/core.ts new file mode 100644 index 000000000..49887a6b9 --- /dev/null +++ b/sdk/src/core.ts @@ -0,0 +1,209 @@ +/** + * @file core.ts + * @brief ESEngine SDK - Core exports (no platform initialization) + */ + +// ============================================================================= +// Types +// ============================================================================= + +export { + type Entity, + INVALID_ENTITY, + type TextureHandle, + INVALID_TEXTURE, + type Vec2, + type Vec3, + type Vec4, + type Color, + type Quat, + vec2, + vec3, + vec4, + color, + quat, +} from './types'; + +// ============================================================================= +// Components +// ============================================================================= + +export { + defineComponent, + defineTag, + isBuiltinComponent, + type ComponentDef, + type BuiltinComponentDef, + type AnyComponentDef, + type ComponentData, + LocalTransform, + WorldTransform, + Sprite, + Camera, + Canvas, + Velocity, + Parent, + Children, + type LocalTransformData, + type WorldTransformData, + type SpriteData, + type CameraData, + type CanvasData, + type VelocityData, + type ParentData, + type ChildrenData, +} from './component'; + +// ============================================================================= +// Resources +// ============================================================================= + +export { + defineResource, + Res, + ResMut, + Time, + Input, + type ResourceDef, + type ResDescriptor, + type ResMutDescriptor, + type ResMutInstance, + type TimeData, + type InputState, +} from './resource'; + +// ============================================================================= +// Query +// ============================================================================= + +export { + Query, + Mut, + QueryInstance, + type QueryDescriptor, + type QueryResult, + type MutWrapper, +} from './query'; + +// ============================================================================= +// Commands +// ============================================================================= + +export { + Commands, + CommandsInstance, + EntityCommands, + type CommandsDescriptor, +} from './commands'; + +// ============================================================================= +// System +// ============================================================================= + +export { + Schedule, + defineSystem, + SystemRunner, + type SystemDef, + type SystemParam, + type InferParam, + type InferParams, +} from './system'; + +// ============================================================================= +// World +// ============================================================================= + +export { World } from './world'; + +// ============================================================================= +// App +// ============================================================================= + +export { + App, + createWebApp, + type Plugin, + type WebAppOptions, +} from './app'; + +// ============================================================================= +// WASM Types +// ============================================================================= + +export type { + ESEngineModule, + CppRegistry, + CppResourceManager, +} from './wasm'; + +// ============================================================================= +// UI +// ============================================================================= + +export { + Text, + TextAlign, + TextVerticalAlign, + TextOverflow, + UIRect, + TextRenderer, + TextPlugin, + textPlugin, + type TextData, + type UIRectData, + type TextRenderResult, +} from './ui'; + +// ============================================================================= +// Asset +// ============================================================================= + +export { + AssetServer, + Assets, + AssetPlugin, + assetPlugin, + type TextureInfo, + type AssetsData, +} from './asset'; + +// ============================================================================= +// Scene +// ============================================================================= + +export { + loadSceneData, + loadSceneWithAssets, + type SceneData, + type SceneEntityData, + type SceneComponentData, + type SceneLoadOptions, +} from './scene'; + +// ============================================================================= +// Preview +// ============================================================================= + +export { PreviewPlugin } from './preview'; + +// ============================================================================= +// Platform (base functions only) +// ============================================================================= + +export { + getPlatform, + getPlatformType, + isPlatformInitialized, + isWeChat, + isWeb, + platformFetch, + platformReadFile, + platformReadTextFile, + platformFileExists, + platformInstantiateWasm, + type PlatformAdapter, + type PlatformType, + type PlatformRequestOptions, + type PlatformResponse, +} from './platform'; diff --git a/sdk/src/index.ts b/sdk/src/index.ts index cbf3741d7..14fc44cf6 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -1,188 +1,11 @@ /** * @file index.ts - * @brief ESEngine SDK - Main entry point + * @brief ESEngine SDK - Web entry point (auto-initializes Web platform) */ -// ============================================================================= -// Types -// ============================================================================= +// Initialize Web platform +import { setPlatform, webAdapter } from './platform'; +setPlatform(webAdapter); -export { - type Entity, - INVALID_ENTITY, - type TextureHandle, - INVALID_TEXTURE, - type Vec2, - type Vec3, - type Vec4, - type Color, - type Quat, - vec2, - vec3, - vec4, - color, - quat, -} from './types'; - -// ============================================================================= -// Components -// ============================================================================= - -export { - defineComponent, - defineTag, - isBuiltinComponent, - type ComponentDef, - type BuiltinComponentDef, - type AnyComponentDef, - type ComponentData, - LocalTransform, - WorldTransform, - Sprite, - Camera, - Canvas, - Velocity, - Parent, - Children, - type LocalTransformData, - type WorldTransformData, - type SpriteData, - type CameraData, - type CanvasData, - type VelocityData, - type ParentData, - type ChildrenData, -} from './component'; - -// ============================================================================= -// Resources -// ============================================================================= - -export { - defineResource, - Res, - ResMut, - Time, - Input, - type ResourceDef, - type ResDescriptor, - type ResMutDescriptor, - type ResMutInstance, - type TimeData, - type InputState, -} from './resource'; - -// ============================================================================= -// Query -// ============================================================================= - -export { - Query, - Mut, - QueryInstance, - type QueryDescriptor, - type QueryResult, - type MutWrapper, -} from './query'; - -// ============================================================================= -// Commands -// ============================================================================= - -export { - Commands, - CommandsInstance, - EntityCommands, - type CommandsDescriptor, -} from './commands'; - -// ============================================================================= -// System -// ============================================================================= - -export { - Schedule, - defineSystem, - SystemRunner, - type SystemDef, - type SystemParam, - type InferParam, - type InferParams, -} from './system'; - -// ============================================================================= -// World -// ============================================================================= - -export { World } from './world'; - -// ============================================================================= -// App -// ============================================================================= - -export { - App, - createWebApp, - type Plugin, - type WebAppOptions, -} from './app'; - -// ============================================================================= -// WASM Types -// ============================================================================= - -export type { - ESEngineModule, - CppRegistry, - CppResourceManager, -} from './wasm'; - -// ============================================================================= -// UI -// ============================================================================= - -export { - Text, - TextAlign, - TextVerticalAlign, - TextOverflow, - UIRect, - TextRenderer, - TextPlugin, - textPlugin, - type TextData, - type UIRectData, - type TextRenderResult, -} from './ui'; - -// ============================================================================= -// Asset -// ============================================================================= - -export { - AssetServer, - Assets, - AssetPlugin, - assetPlugin, - type TextureInfo, - type AssetsData, -} from './asset'; - -// ============================================================================= -// Scene -// ============================================================================= - -export { - loadSceneData, - loadSceneWithAssets, - type SceneData, - type SceneEntityData, - type SceneComponentData, - type SceneLoadOptions, -} from './scene'; - -// ============================================================================= -// Preview -// ============================================================================= - -export { PreviewPlugin } from './preview'; +// Re-export everything from core +export * from './core'; diff --git a/sdk/src/index.wechat.ts b/sdk/src/index.wechat.ts new file mode 100644 index 000000000..a06a4c0ce --- /dev/null +++ b/sdk/src/index.wechat.ts @@ -0,0 +1,23 @@ +/** + * @file index.wechat.ts + * @brief ESEngine SDK - WeChat MiniGame entry point + */ + +// Initialize WeChat platform +import { setPlatform } from './platform'; +import { wechatAdapter, initWeChatPlatform } from './platform/wechat'; + +initWeChatPlatform(); +setPlatform(wechatAdapter); + +// Re-export everything from core +export * from './core'; + +// Export WeChat-specific utilities +export { + wxReadFile, + wxReadTextFile, + wxFileExists, + wxFileExistsSync, + wxWriteFile, +} from './platform/wechat'; diff --git a/sdk/src/platform/base.ts b/sdk/src/platform/base.ts new file mode 100644 index 000000000..527303ea4 --- /dev/null +++ b/sdk/src/platform/base.ts @@ -0,0 +1,92 @@ +/** + * @file base.ts + * @brief Base platform adapter - no platform-specific code + */ + +import type { PlatformAdapter } from './types'; + +// ============================================================================= +// Platform Instance (set by entry point) +// ============================================================================= + +let currentPlatform: PlatformAdapter | null = null; + +/** + * Set the platform adapter (called by entry point) + */ +export function setPlatform(adapter: PlatformAdapter): void { + currentPlatform = adapter; + console.log(`[ESEngine] Platform: ${adapter.name}`); +} + +/** + * Get the current platform adapter + * @throws Error if platform not initialized + */ +export function getPlatform(): PlatformAdapter { + if (!currentPlatform) { + throw new Error( + '[ESEngine] Platform not initialized. ' + + 'Import from "esengine" (web) or "esengine/wechat" (WeChat) instead of direct imports.' + ); + } + return currentPlatform; +} + +/** + * Check if platform is initialized + */ +export function isPlatformInitialized(): boolean { + return currentPlatform !== null; +} + +/** + * Get platform type + */ +export function getPlatformType(): 'web' | 'wechat' | null { + return currentPlatform?.name ?? null; +} + +/** + * Check if running on WeChat + */ +export function isWeChat(): boolean { + return currentPlatform?.name === 'wechat'; +} + +/** + * Check if running on Web + */ +export function isWeb(): boolean { + return currentPlatform?.name === 'web'; +} + +// ============================================================================= +// Convenience Functions +// ============================================================================= + +export async function platformFetch( + url: string, + options?: import('./types').PlatformRequestOptions +): Promise { + return getPlatform().fetch(url, options); +} + +export async function platformReadFile(path: string): Promise { + return getPlatform().readFile(path); +} + +export async function platformReadTextFile(path: string): Promise { + return getPlatform().readTextFile(path); +} + +export async function platformFileExists(path: string): Promise { + return getPlatform().fileExists(path); +} + +export async function platformInstantiateWasm( + pathOrBuffer: string | ArrayBuffer, + imports: WebAssembly.Imports +): Promise { + return getPlatform().instantiateWasm(pathOrBuffer, imports); +} diff --git a/sdk/src/platform/index.ts b/sdk/src/platform/index.ts new file mode 100644 index 000000000..59da5838d --- /dev/null +++ b/sdk/src/platform/index.ts @@ -0,0 +1,34 @@ +/** + * @file index.ts + * @brief Platform adapter exports + */ + +// Re-export types +export type { + PlatformAdapter, + PlatformType, + PlatformRequestOptions, + PlatformResponse, + WasmInstantiateResult, +} from './types'; + +// Re-export base functions +export { + setPlatform, + getPlatform, + getPlatformType, + isPlatformInitialized, + isWeChat, + isWeb, + platformFetch, + platformReadFile, + platformReadTextFile, + platformFileExists, + platformInstantiateWasm, +} from './base'; + +// Note: webAdapter is exported here for initialization +// wechatAdapter should be imported directly from './wechat' to avoid bundling in web builds + +// Re-export web adapter +export { webAdapter } from './web'; diff --git a/sdk/src/platform/types.ts b/sdk/src/platform/types.ts new file mode 100644 index 000000000..af7403045 --- /dev/null +++ b/sdk/src/platform/types.ts @@ -0,0 +1,99 @@ +/** + * @file types.ts + * @brief Platform adapter interface definitions + */ + +// ============================================================================= +// Response Types +// ============================================================================= + +export interface PlatformResponse { + ok: boolean; + status: number; + statusText: string; + headers: Record; + json(): Promise; + text(): Promise; + arrayBuffer(): Promise; +} + +// ============================================================================= +// Request Types +// ============================================================================= + +export interface PlatformRequestOptions { + method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS'; + headers?: Record; + body?: string | ArrayBuffer; + responseType?: 'text' | 'arraybuffer' | 'json'; + timeout?: number; +} + +// ============================================================================= +// WASM Types +// ============================================================================= + +export interface WasmInstantiateResult { + instance: WebAssembly.Instance; + module: WebAssembly.Module; +} + +// ============================================================================= +// Platform Adapter Interface +// ============================================================================= + +export interface PlatformAdapter { + /** + * Platform name identifier + */ + readonly name: 'web' | 'wechat'; + + /** + * Fetch resource from URL + */ + fetch(url: string, options?: PlatformRequestOptions): Promise; + + /** + * Read local file as ArrayBuffer + * @param path - File path (relative to game root) + */ + readFile(path: string): Promise; + + /** + * Read local file as text + * @param path - File path (relative to game root) + */ + readTextFile(path: string): Promise; + + /** + * Check if file exists + * @param path - File path + */ + fileExists(path: string): Promise; + + /** + * Instantiate WebAssembly module + * @param pathOrBuffer - WASM file path (WeChat) or ArrayBuffer (Web) + * @param imports - Import object + */ + instantiateWasm( + pathOrBuffer: string | ArrayBuffer, + imports: WebAssembly.Imports + ): Promise; +} + +// ============================================================================= +// Platform Detection +// ============================================================================= + +export type PlatformType = 'web' | 'wechat'; + +export function detectPlatform(): PlatformType { + // Check for WeChat MiniGame environment + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const g = globalThis as any; + if (typeof g.wx !== 'undefined' && typeof g.wx.getSystemInfoSync === 'function') { + return 'wechat'; + } + return 'web'; +} diff --git a/sdk/src/platform/web.ts b/sdk/src/platform/web.ts new file mode 100644 index 000000000..3c5b60e1c --- /dev/null +++ b/sdk/src/platform/web.ts @@ -0,0 +1,93 @@ +/** + * @file web.ts + * @brief Web platform adapter implementation + */ + +import type { + PlatformAdapter, + PlatformRequestOptions, + PlatformResponse, + WasmInstantiateResult, +} from './types'; + +// ============================================================================= +// Web Platform Adapter +// ============================================================================= + +class WebPlatformAdapter implements PlatformAdapter { + readonly name = 'web' as const; + + async fetch(url: string, options?: PlatformRequestOptions): Promise { + const response = await globalThis.fetch(url, { + method: options?.method ?? 'GET', + headers: options?.headers, + body: options?.body, + }); + + const headers: Record = {}; + response.headers.forEach((value, key) => { + headers[key] = value; + }); + + return { + ok: response.ok, + status: response.status, + statusText: response.statusText, + headers, + json: () => response.json() as Promise, + text: () => response.text(), + arrayBuffer: () => response.arrayBuffer(), + }; + } + + async readFile(path: string): Promise { + const response = await this.fetch(path); + if (!response.ok) { + throw new Error(`Failed to read file: ${path} (${response.status})`); + } + return response.arrayBuffer(); + } + + async readTextFile(path: string): Promise { + const response = await this.fetch(path); + if (!response.ok) { + throw new Error(`Failed to read file: ${path} (${response.status})`); + } + return response.text(); + } + + async fileExists(path: string): Promise { + try { + const response = await globalThis.fetch(path, { method: 'HEAD' }); + return response.ok; + } catch { + return false; + } + } + + async instantiateWasm( + pathOrBuffer: string | ArrayBuffer, + imports: WebAssembly.Imports + ): Promise { + let buffer: ArrayBuffer; + + if (typeof pathOrBuffer === 'string') { + buffer = await this.readFile(pathOrBuffer); + } else { + buffer = pathOrBuffer; + } + + const result = await WebAssembly.instantiate(buffer, imports); + + return { + instance: result.instance, + module: result.module, + }; + } +} + +// ============================================================================= +// Export Singleton +// ============================================================================= + +export const webAdapter = new WebPlatformAdapter(); diff --git a/sdk/src/platform/wechat/fetch.ts b/sdk/src/platform/wechat/fetch.ts new file mode 100644 index 000000000..d2f7d85c2 --- /dev/null +++ b/sdk/src/platform/wechat/fetch.ts @@ -0,0 +1,109 @@ +/** + * @file fetch.ts + * @brief Fetch polyfill for WeChat MiniGame using wx.request + */ + +/// + +import type { PlatformRequestOptions, PlatformResponse } from '../types'; + +// ============================================================================= +// WeChat Fetch Implementation +// ============================================================================= + +export function wxFetch(url: string, options?: PlatformRequestOptions): Promise { + return new Promise((resolve, reject) => { + const responseType = options?.responseType ?? 'text'; + + wx.request({ + url, + method: (options?.method ?? 'GET') as WechatMinigame.RequestOption['method'], + data: options?.body, + header: options?.headers, + responseType: responseType === 'arraybuffer' ? 'arraybuffer' : 'text', + timeout: options?.timeout, + success: (res) => { + const ok = res.statusCode >= 200 && res.statusCode < 300; + const headers: Record = {}; + + if (res.header) { + for (const key of Object.keys(res.header)) { + headers[key.toLowerCase()] = res.header[key]; + } + } + + resolve({ + ok, + status: res.statusCode, + statusText: ok ? 'OK' : 'Error', + headers, + json: () => { + if (typeof res.data === 'string') { + return Promise.resolve(JSON.parse(res.data) as T); + } + return Promise.resolve(res.data as T); + }, + text: () => { + if (typeof res.data === 'string') { + return Promise.resolve(res.data); + } + return Promise.resolve(JSON.stringify(res.data)); + }, + arrayBuffer: () => { + if (res.data instanceof ArrayBuffer) { + return Promise.resolve(res.data); + } + // Convert string to ArrayBuffer + const encoder = new TextEncoder(); + const data = typeof res.data === 'string' ? res.data : JSON.stringify(res.data); + return Promise.resolve(encoder.encode(data).buffer); + }, + }); + }, + fail: (err) => { + reject(new Error(`wx.request failed: ${err.errMsg}`)); + }, + }); + }); +} + +// ============================================================================= +// Global Fetch Polyfill +// ============================================================================= + +export function polyfillFetch(): void { + if (typeof globalThis.fetch === 'undefined') { + (globalThis as any).fetch = async ( + input: string | URL, + init?: RequestInit + ): Promise => { + const url = typeof input === 'string' ? input : input.toString(); + const options: PlatformRequestOptions = { + method: (init?.method as PlatformRequestOptions['method']) ?? 'GET', + headers: init?.headers as Record, + body: init?.body as string | ArrayBuffer, + }; + + const response = await wxFetch(url, options); + + // Return a Response-like object + return { + ok: response.ok, + status: response.status, + statusText: response.statusText, + headers: new Headers(response.headers), + json: () => response.json(), + text: () => response.text(), + arrayBuffer: () => response.arrayBuffer(), + blob: () => Promise.reject(new Error('Blob not supported in WeChat')), + formData: () => Promise.reject(new Error('FormData not supported in WeChat')), + clone: () => { throw new Error('clone not supported'); }, + body: null, + bodyUsed: false, + redirected: false, + type: 'basic', + url, + } as unknown as Response; + }; + } +} diff --git a/sdk/src/platform/wechat/fs.ts b/sdk/src/platform/wechat/fs.ts new file mode 100644 index 000000000..34a29d466 --- /dev/null +++ b/sdk/src/platform/wechat/fs.ts @@ -0,0 +1,122 @@ +/** + * @file fs.ts + * @brief File system adapter for WeChat MiniGame + */ + +/// + +// ============================================================================= +// Types +// ============================================================================= + +type WxFileSystemManager = WechatMinigame.FileSystemManager; + +// ============================================================================= +// WeChat File System +// ============================================================================= + +let fsManager: WxFileSystemManager | null = null; + +function getFileSystemManager(): WxFileSystemManager { + if (!fsManager) { + fsManager = wx.getFileSystemManager(); + } + return fsManager; +} + +/** + * Read file as ArrayBuffer (synchronous) + */ +export function wxReadFileSync(path: string): ArrayBuffer { + const fs = getFileSystemManager(); + return fs.readFileSync(path) as ArrayBuffer; +} + +/** + * Read file as string (synchronous) + */ +export function wxReadTextFileSync(path: string, encoding: 'utf8' | 'utf-8' = 'utf-8'): string { + const fs = getFileSystemManager(); + return fs.readFileSync(path, encoding) as string; +} + +/** + * Read file as ArrayBuffer (async) + */ +export function wxReadFile(path: string): Promise { + return new Promise((resolve, reject) => { + const fs = getFileSystemManager(); + fs.readFile({ + filePath: path, + success: (res) => { + resolve(res.data as ArrayBuffer); + }, + fail: (err) => { + reject(new Error(`Failed to read file "${path}": ${err.errMsg}`)); + }, + }); + }); +} + +/** + * Read file as string (async) + */ +export function wxReadTextFile(path: string, encoding: 'utf8' | 'utf-8' = 'utf-8'): Promise { + return new Promise((resolve, reject) => { + const fs = getFileSystemManager(); + fs.readFile({ + filePath: path, + encoding, + success: (res) => { + resolve(res.data as string); + }, + fail: (err) => { + reject(new Error(`Failed to read file "${path}": ${err.errMsg}`)); + }, + }); + }); +} + +/** + * Check if file exists + */ +export function wxFileExists(path: string): Promise { + return new Promise((resolve) => { + const fs = getFileSystemManager(); + fs.access({ + path, + success: () => resolve(true), + fail: () => resolve(false), + }); + }); +} + +/** + * Check if file exists (sync) + */ +export function wxFileExistsSync(path: string): boolean { + const fs = getFileSystemManager(); + try { + fs.accessSync(path); + return true; + } catch { + return false; + } +} + +/** + * Write file + */ +export function wxWriteFile(path: string, data: string | ArrayBuffer): Promise { + return new Promise((resolve, reject) => { + const fs = getFileSystemManager(); + fs.writeFile({ + filePath: path, + data, + success: () => resolve(), + fail: (err) => { + reject(new Error(`Failed to write file "${path}": ${err.errMsg}`)); + }, + }); + }); +} diff --git a/sdk/src/platform/wechat/index.ts b/sdk/src/platform/wechat/index.ts new file mode 100644 index 000000000..e3123b6ba --- /dev/null +++ b/sdk/src/platform/wechat/index.ts @@ -0,0 +1,81 @@ +/** + * @file index.ts + * @brief WeChat MiniGame platform adapter + */ + +/// + +import type { + PlatformAdapter, + PlatformRequestOptions, + PlatformResponse, + WasmInstantiateResult, +} from '../types'; +import { wxFetch, polyfillFetch } from './fetch'; +import { wxInstantiateWasm, polyfillWebAssembly } from './wasm'; +import { wxReadFile, wxReadTextFile, wxFileExists } from './fs'; + +// ============================================================================= +// WeChat Platform Adapter +// ============================================================================= + +class WeChatPlatformAdapter implements PlatformAdapter { + readonly name = 'wechat' as const; + + async fetch(url: string, options?: PlatformRequestOptions): Promise { + return wxFetch(url, options); + } + + async readFile(path: string): Promise { + return wxReadFile(path); + } + + async readTextFile(path: string): Promise { + return wxReadTextFile(path); + } + + async fileExists(path: string): Promise { + return wxFileExists(path); + } + + async instantiateWasm( + pathOrBuffer: string | ArrayBuffer, + imports: WebAssembly.Imports + ): Promise { + if (typeof pathOrBuffer !== 'string') { + throw new Error( + 'WeChat WXWebAssembly requires a file path string, not ArrayBuffer' + ); + } + return wxInstantiateWasm(pathOrBuffer, imports); + } +} + +// ============================================================================= +// Initialization +// ============================================================================= + +let initialized = false; + +/** + * Initialize WeChat platform polyfills + * Call this at the entry point of your game + */ +export function initWeChatPlatform(): void { + if (initialized) return; + initialized = true; + + polyfillFetch(); + polyfillWebAssembly(); + + console.log('[ESEngine] WeChat platform initialized'); +} + +// ============================================================================= +// Export +// ============================================================================= + +export const wechatAdapter = new WeChatPlatformAdapter(); + +export { polyfillFetch, polyfillWebAssembly }; +export { wxReadFile, wxReadTextFile, wxFileExists, wxFileExistsSync, wxWriteFile } from './fs'; diff --git a/sdk/src/platform/wechat/wasm.ts b/sdk/src/platform/wechat/wasm.ts new file mode 100644 index 000000000..4b446e595 --- /dev/null +++ b/sdk/src/platform/wechat/wasm.ts @@ -0,0 +1,89 @@ +/** + * @file wasm.ts + * @brief WebAssembly adapter for WeChat MiniGame using WXWebAssembly + */ + +/// + +import type { WasmInstantiateResult } from '../types'; + +// ============================================================================= +// WeChat WASM Implementation +// ============================================================================= + +/** + * Instantiate WASM using WXWebAssembly + * Note: WeChat requires path to be a string (package-relative path), not ArrayBuffer + */ +export async function wxInstantiateWasm( + path: string, + imports: WebAssembly.Imports +): Promise { + if (typeof WXWebAssembly === 'undefined') { + throw new Error('WXWebAssembly is not available. Requires WeChat base library >= 2.13.0'); + } + + // WeChat requires the path to be a package-relative path string + // e.g., "esengine.wasm" not "/esengine.wasm" or ArrayBuffer + const normalizedPath = path.startsWith('/') ? path.slice(1) : path; + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await WXWebAssembly.instantiate(normalizedPath, imports as any); + return { + // WXWebAssembly.instantiate returns { instance, module } but types are slightly different + instance: (result as any).instance ?? result, + module: (result as any).module, + }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + throw new Error(`WXWebAssembly.instantiate failed for "${normalizedPath}": ${message}`); + } +} + +// ============================================================================= +// Global WebAssembly Polyfill +// ============================================================================= + +/** + * Polyfill global WebAssembly with WXWebAssembly + * This allows existing code using WebAssembly.instantiate to work on WeChat + */ +export function polyfillWebAssembly(): void { + if (typeof WXWebAssembly !== 'undefined' && typeof WebAssembly === 'undefined') { + (globalThis as any).WebAssembly = { + instantiate: async ( + bufferOrPath: BufferSource | string, + imports?: WebAssembly.Imports + ) => { + if (typeof bufferOrPath !== 'string') { + throw new Error( + 'WeChat WXWebAssembly requires a file path string, not ArrayBuffer. ' + + 'Use platform adapter instead of direct WebAssembly.instantiate.' + ); + } + return wxInstantiateWasm(bufferOrPath, imports ?? {}); + }, + compile: () => { + throw new Error('WebAssembly.compile is not supported in WeChat MiniGame'); + }, + compileStreaming: () => { + throw new Error('WebAssembly.compileStreaming is not supported in WeChat MiniGame'); + }, + instantiateStreaming: () => { + throw new Error('WebAssembly.instantiateStreaming is not supported in WeChat MiniGame'); + }, + validate: () => { + throw new Error('WebAssembly.validate is not supported in WeChat MiniGame'); + }, + Module: class {} as any, + Instance: class {} as any, + Memory: class {} as any, + Table: class {} as any, + Global: class {} as any, + CompileError: Error, + LinkError: Error, + RuntimeError: Error, + }; + } +} diff --git a/sdk/src/preview/PreviewPlugin.ts b/sdk/src/preview/PreviewPlugin.ts index 85be70303..bb521b6f8 100644 --- a/sdk/src/preview/PreviewPlugin.ts +++ b/sdk/src/preview/PreviewPlugin.ts @@ -7,6 +7,7 @@ import type { App, Plugin } from '../app'; import { Assets, assetPlugin } from '../asset'; import { loadSceneWithAssets, type SceneData } from '../scene'; import { LocalTransform, Camera, type LocalTransformData, type CameraData } from '../component'; +import { platformFetch } from '../platform'; // ============================================================================= // PreviewPlugin @@ -44,11 +45,11 @@ export class PreviewPlugin implements Plugin { if (!this.app_) return; try { - const response = await fetch(this.sceneUrl_); + const response = await platformFetch(this.sceneUrl_); if (!response.ok) { throw new Error(`Failed to fetch scene: ${response.status}`); } - const sceneData = await response.json() as SceneData; + const sceneData = await response.json(); const assets = this.app_.getResource(Assets); await loadSceneWithAssets(this.app_.world, sceneData, { From d45b7d89d5ca708c7199aeb55ff24d2babffb7c4 Mon Sep 17 00:00:00 2001 From: esengine <359807859@qq.com> Date: Wed, 4 Feb 2026 23:39:08 -0800 Subject: [PATCH 2/3] feat: Add WeChat MiniGame build support - Add esengine_wxgame CMake target with WeChat-specific Emscripten flags (-sEXPORT_ES6=0, -sDYNAMIC_EXECUTION=0 for CommonJS compatibility) - Add scripts/build-wxgame.sh for automated WeChat engine builds - Add initRendererWithContext() to accept externally created WebGL context (avoids document.querySelector which doesn't exist in WeChat) - Update SDK createWebApp() to support glContextHandle option - Add Tauri commands for wxgame engine file access - Update WeChatBuilder to create WebGL context in JS and pass to WASM --- CMakeLists.txt | 12 + cmake/Emscripten.cmake | 39 +- desktop/public/sdk/cjs/esengine.wechat.js | 1998 +++++++++++++++++++++ desktop/public/wasm/esengine.wxgame.js | 2 + desktop/src-tauri/src/lib.rs | 111 +- desktop/src/native-fs.ts | 71 +- editor/src/builder/BuildSettingsDialog.ts | 9 + editor/src/builder/WeChatBuilder.ts | 129 +- editor/src/types/BuildTypes.ts | 13 + scripts/build-wxgame.sh | 34 + sdk/src/app.ts | 7 +- sdk/src/wasm.ts | 9 + src/esengine/bindings/WebSDKEntry.cpp | 36 + src/esengine/platform/web/WebPlatform.cpp | 89 +- 14 files changed, 2482 insertions(+), 77 deletions(-) create mode 100644 desktop/public/sdk/cjs/esengine.wechat.js create mode 100644 desktop/public/wasm/esengine.wxgame.js create mode 100755 scripts/build-wxgame.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 4649f3bb7..e58606040 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,6 +273,18 @@ html,body{width:100%;height:100%;overflow:hidden;background:#1a1a1a} ]=]) endif() + + # WeChat MiniGame SDK (CommonJS compatible) + if(ES_BUILD_WXGAME) + add_executable(esengine_wxgame src/esengine/bindings/WebSDKEntry.cpp) + target_link_libraries(esengine_wxgame PRIVATE -Wl,--whole-archive esengine -Wl,--no-whole-archive) + es_apply_wxgame_sdk_settings(esengine_wxgame) + + set_target_properties(esengine_wxgame PROPERTIES + OUTPUT_NAME "esengine.wxgame" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/sdk" + ) + endif() endif() if(ES_BUILD_TESTS) diff --git a/cmake/Emscripten.cmake b/cmake/Emscripten.cmake index 428e16300..442d77be1 100644 --- a/cmake/Emscripten.cmake +++ b/cmake/Emscripten.cmake @@ -93,14 +93,25 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release") ) endif() -# WeChat MiniGame specific settings -if(ES_BUILD_WXGAME) - list(APPEND ES_EMSCRIPTEN_LINK_FLAGS - -sENVIRONMENT=web - -sMODULARIZE=1 - "-sEXPORT_NAME='ESEngineModule'" - ) -endif() +# WeChat MiniGame SDK link flags (CommonJS compatible, no ES6) +set(ES_EMSCRIPTEN_WXGAME_SDK_FLAGS + --bind + -sWASM=1 + -sUSE_WEBGL2=1 + -sFULL_ES3=1 + -sALLOW_MEMORY_GROWTH=1 + -sNO_EXIT_RUNTIME=1 + -sENVIRONMENT=web,node + -sEXPORT_ES6=0 + -sMODULARIZE=1 + -sNO_FILESYSTEM=1 + -sDYNAMIC_EXECUTION=0 + "-sEXPORT_NAME='ESEngineModule'" + "-sEXPORTED_FUNCTIONS=['_malloc','_free']" + "-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','HEAPF32','HEAPU8','HEAPU32','GL']" + -O3 + --closure=0 +) # Helper function to apply Emscripten settings to a target (monolithic build) function(es_apply_emscripten_settings TARGET_NAME) @@ -209,6 +220,18 @@ function(es_apply_single_file_settings TARGET_NAME) endif() endfunction() +function(es_apply_wxgame_sdk_settings TARGET_NAME) + if(ES_BUILD_WXGAME) + target_compile_options(${TARGET_NAME} PRIVATE ${ES_EMSCRIPTEN_COMPILE_FLAGS}) + + string(REPLACE ";" " " LINK_FLAGS_STR "${ES_EMSCRIPTEN_WXGAME_SDK_FLAGS}") + set_target_properties(${TARGET_NAME} PROPERTIES + SUFFIX ".js" + LINK_FLAGS "${LINK_FLAGS_STR}" + ) + endif() +endfunction() + message(STATUS "Emscripten configuration loaded") if(ES_BUILD_MODULAR) message(STATUS "Modular build enabled: MAIN_MODULE + SIDE_MODULE architecture") diff --git a/desktop/public/sdk/cjs/esengine.wechat.js b/desktop/public/sdk/cjs/esengine.wechat.js new file mode 100644 index 000000000..55e17c573 --- /dev/null +++ b/desktop/public/sdk/cjs/esengine.wechat.js @@ -0,0 +1,1998 @@ +'use strict'; + +/** + * @file base.ts + * @brief Base platform adapter - no platform-specific code + */ +// ============================================================================= +// Platform Instance (set by entry point) +// ============================================================================= +let currentPlatform = null; +/** + * Set the platform adapter (called by entry point) + */ +function setPlatform(adapter) { + currentPlatform = adapter; + console.log(`[ESEngine] Platform: ${adapter.name}`); +} +/** + * Get the current platform adapter + * @throws Error if platform not initialized + */ +function getPlatform() { + if (!currentPlatform) { + throw new Error('[ESEngine] Platform not initialized. ' + + 'Import from "esengine" (web) or "esengine/wechat" (WeChat) instead of direct imports.'); + } + return currentPlatform; +} +/** + * Check if platform is initialized + */ +function isPlatformInitialized() { + return currentPlatform !== null; +} +/** + * Get platform type + */ +function getPlatformType() { + return currentPlatform?.name ?? null; +} +/** + * Check if running on WeChat + */ +function isWeChat() { + return currentPlatform?.name === 'wechat'; +} +/** + * Check if running on Web + */ +function isWeb() { + return currentPlatform?.name === 'web'; +} +// ============================================================================= +// Convenience Functions +// ============================================================================= +async function platformFetch(url, options) { + return getPlatform().fetch(url, options); +} +async function platformReadFile(path) { + return getPlatform().readFile(path); +} +async function platformReadTextFile(path) { + return getPlatform().readTextFile(path); +} +async function platformFileExists(path) { + return getPlatform().fileExists(path); +} +async function platformInstantiateWasm(pathOrBuffer, imports) { + return getPlatform().instantiateWasm(pathOrBuffer, imports); +} + +/** + * @file fetch.ts + * @brief Fetch polyfill for WeChat MiniGame using wx.request + */ +// ============================================================================= +// WeChat Fetch Implementation +// ============================================================================= +function wxFetch(url, options) { + return new Promise((resolve, reject) => { + const responseType = options?.responseType ?? 'text'; + wx.request({ + url, + method: (options?.method ?? 'GET'), + data: options?.body, + header: options?.headers, + responseType: responseType === 'arraybuffer' ? 'arraybuffer' : 'text', + timeout: options?.timeout, + success: (res) => { + const ok = res.statusCode >= 200 && res.statusCode < 300; + const headers = {}; + if (res.header) { + for (const key of Object.keys(res.header)) { + headers[key.toLowerCase()] = res.header[key]; + } + } + resolve({ + ok, + status: res.statusCode, + statusText: ok ? 'OK' : 'Error', + headers, + json: () => { + if (typeof res.data === 'string') { + return Promise.resolve(JSON.parse(res.data)); + } + return Promise.resolve(res.data); + }, + text: () => { + if (typeof res.data === 'string') { + return Promise.resolve(res.data); + } + return Promise.resolve(JSON.stringify(res.data)); + }, + arrayBuffer: () => { + if (res.data instanceof ArrayBuffer) { + return Promise.resolve(res.data); + } + // Convert string to ArrayBuffer + const encoder = new TextEncoder(); + const data = typeof res.data === 'string' ? res.data : JSON.stringify(res.data); + return Promise.resolve(encoder.encode(data).buffer); + }, + }); + }, + fail: (err) => { + reject(new Error(`wx.request failed: ${err.errMsg}`)); + }, + }); + }); +} +// ============================================================================= +// Global Fetch Polyfill +// ============================================================================= +function polyfillFetch() { + if (typeof globalThis.fetch === 'undefined') { + globalThis.fetch = async (input, init) => { + const url = typeof input === 'string' ? input : input.toString(); + const options = { + method: init?.method ?? 'GET', + headers: init?.headers, + body: init?.body, + }; + const response = await wxFetch(url, options); + // Return a Response-like object + return { + ok: response.ok, + status: response.status, + statusText: response.statusText, + headers: new Headers(response.headers), + json: () => response.json(), + text: () => response.text(), + arrayBuffer: () => response.arrayBuffer(), + blob: () => Promise.reject(new Error('Blob not supported in WeChat')), + formData: () => Promise.reject(new Error('FormData not supported in WeChat')), + clone: () => { throw new Error('clone not supported'); }, + body: null, + bodyUsed: false, + redirected: false, + type: 'basic', + url, + }; + }; + } +} + +/** + * @file wasm.ts + * @brief WebAssembly adapter for WeChat MiniGame using WXWebAssembly + */ +// ============================================================================= +// WeChat WASM Implementation +// ============================================================================= +/** + * Instantiate WASM using WXWebAssembly + * Note: WeChat requires path to be a string (package-relative path), not ArrayBuffer + */ +async function wxInstantiateWasm(path, imports) { + if (typeof WXWebAssembly === 'undefined') { + throw new Error('WXWebAssembly is not available. Requires WeChat base library >= 2.13.0'); + } + // WeChat requires the path to be a package-relative path string + // e.g., "esengine.wasm" not "/esengine.wasm" or ArrayBuffer + const normalizedPath = path.startsWith('/') ? path.slice(1) : path; + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await WXWebAssembly.instantiate(normalizedPath, imports); + return { + // WXWebAssembly.instantiate returns { instance, module } but types are slightly different + instance: result.instance ?? result, + module: result.module, + }; + } + catch (err) { + const message = err instanceof Error ? err.message : String(err); + throw new Error(`WXWebAssembly.instantiate failed for "${normalizedPath}": ${message}`); + } +} +// ============================================================================= +// Global WebAssembly Polyfill +// ============================================================================= +/** + * Polyfill global WebAssembly with WXWebAssembly + * This allows existing code using WebAssembly.instantiate to work on WeChat + */ +function polyfillWebAssembly() { + if (typeof WXWebAssembly !== 'undefined' && typeof WebAssembly === 'undefined') { + globalThis.WebAssembly = { + instantiate: async (bufferOrPath, imports) => { + if (typeof bufferOrPath !== 'string') { + throw new Error('WeChat WXWebAssembly requires a file path string, not ArrayBuffer. ' + + 'Use platform adapter instead of direct WebAssembly.instantiate.'); + } + return wxInstantiateWasm(bufferOrPath, imports ?? {}); + }, + compile: () => { + throw new Error('WebAssembly.compile is not supported in WeChat MiniGame'); + }, + compileStreaming: () => { + throw new Error('WebAssembly.compileStreaming is not supported in WeChat MiniGame'); + }, + instantiateStreaming: () => { + throw new Error('WebAssembly.instantiateStreaming is not supported in WeChat MiniGame'); + }, + validate: () => { + throw new Error('WebAssembly.validate is not supported in WeChat MiniGame'); + }, + Module: class { + }, + Instance: class { + }, + Memory: class { + }, + Table: class { + }, + Global: class { + }, + CompileError: Error, + LinkError: Error, + RuntimeError: Error, + }; + } +} + +/** + * @file fs.ts + * @brief File system adapter for WeChat MiniGame + */ +/// +// ============================================================================= +// WeChat File System +// ============================================================================= +let fsManager = null; +function getFileSystemManager() { + if (!fsManager) { + fsManager = wx.getFileSystemManager(); + } + return fsManager; +} +/** + * Read file as ArrayBuffer (async) + */ +function wxReadFile(path) { + return new Promise((resolve, reject) => { + const fs = getFileSystemManager(); + fs.readFile({ + filePath: path, + success: (res) => { + resolve(res.data); + }, + fail: (err) => { + reject(new Error(`Failed to read file "${path}": ${err.errMsg}`)); + }, + }); + }); +} +/** + * Read file as string (async) + */ +function wxReadTextFile(path, encoding = 'utf-8') { + return new Promise((resolve, reject) => { + const fs = getFileSystemManager(); + fs.readFile({ + filePath: path, + encoding, + success: (res) => { + resolve(res.data); + }, + fail: (err) => { + reject(new Error(`Failed to read file "${path}": ${err.errMsg}`)); + }, + }); + }); +} +/** + * Check if file exists + */ +function wxFileExists(path) { + return new Promise((resolve) => { + const fs = getFileSystemManager(); + fs.access({ + path, + success: () => resolve(true), + fail: () => resolve(false), + }); + }); +} +/** + * Check if file exists (sync) + */ +function wxFileExistsSync(path) { + const fs = getFileSystemManager(); + try { + fs.accessSync(path); + return true; + } + catch { + return false; + } +} +/** + * Write file + */ +function wxWriteFile(path, data) { + return new Promise((resolve, reject) => { + const fs = getFileSystemManager(); + fs.writeFile({ + filePath: path, + data, + success: () => resolve(), + fail: (err) => { + reject(new Error(`Failed to write file "${path}": ${err.errMsg}`)); + }, + }); + }); +} + +/** + * @file index.ts + * @brief WeChat MiniGame platform adapter + */ +// ============================================================================= +// WeChat Platform Adapter +// ============================================================================= +class WeChatPlatformAdapter { + constructor() { + this.name = 'wechat'; + } + async fetch(url, options) { + return wxFetch(url, options); + } + async readFile(path) { + return wxReadFile(path); + } + async readTextFile(path) { + return wxReadTextFile(path); + } + async fileExists(path) { + return wxFileExists(path); + } + async instantiateWasm(pathOrBuffer, imports) { + if (typeof pathOrBuffer !== 'string') { + throw new Error('WeChat WXWebAssembly requires a file path string, not ArrayBuffer'); + } + return wxInstantiateWasm(pathOrBuffer, imports); + } +} +// ============================================================================= +// Initialization +// ============================================================================= +let initialized = false; +/** + * Initialize WeChat platform polyfills + * Call this at the entry point of your game + */ +function initWeChatPlatform() { + if (initialized) + return; + initialized = true; + polyfillFetch(); + polyfillWebAssembly(); + console.log('[ESEngine] WeChat platform initialized'); +} +// ============================================================================= +// Export +// ============================================================================= +const wechatAdapter = new WeChatPlatformAdapter(); + +/** + * @file types.ts + * @brief Core type definitions for ESEngine SDK + */ +const INVALID_ENTITY = 0; +const INVALID_TEXTURE = 0xFFFFFFFF; +// ============================================================================= +// Factory Functions +// ============================================================================= +const vec2 = (x = 0, y = 0) => ({ x, y }); +const vec3 = (x = 0, y = 0, z = 0) => ({ x, y, z }); +const vec4 = (x = 0, y = 0, z = 0, w = 1) => ({ x, y, z, w }); +const color = (r = 1, g = 1, b = 1, a = 1) => ({ x: r, y: g, z: b, w: a }); +const quat = (w = 1, x = 0, y = 0, z = 0) => ({ w, x, y, z }); + +/** + * @file component.ts + * @brief Component definition and builtin components + */ +let componentCounter = 0; +function createComponentDef(name, defaults) { + const id = ++componentCounter; + return { + _id: Symbol(`Component_${id}_${name}`), + _name: name, + _default: defaults, + _builtin: false, + create(data) { + return { ...defaults, ...data }; + } + }; +} +function defineComponent(name, defaults) { + const def = createComponentDef(name, defaults); + registerToEditor(name, defaults, false); + return def; +} +function defineTag(name) { + const def = createComponentDef(name, {}); + registerToEditor(name, {}, true); + return def; +} +function registerToEditor(name, defaults, isTag) { + if (typeof window !== 'undefined' && window.__esengine_registerComponent) { + window.__esengine_registerComponent(name, defaults, isTag); + } +} +function defineBuiltin(name, defaults) { + return { + _id: Symbol(`Builtin_${name}`), + _name: name, + _cppName: name, + _builtin: true, + _default: defaults + }; +} +function isBuiltinComponent(comp) { + return comp._builtin === true; +} +// ============================================================================= +// Builtin Component Instances +// ============================================================================= +const LocalTransform = defineBuiltin('LocalTransform', { + position: { x: 0, y: 0, z: 0 }, + rotation: { w: 1, x: 0, y: 0, z: 0 }, + scale: { x: 1, y: 1, z: 1 } +}); +const WorldTransform = defineBuiltin('WorldTransform', { + position: { x: 0, y: 0, z: 0 }, + rotation: { w: 1, x: 0, y: 0, z: 0 }, + scale: { x: 1, y: 1, z: 1 } +}); +const Sprite = defineBuiltin('Sprite', { + texture: 0, + color: { x: 1, y: 1, z: 1, w: 1 }, + size: { x: 32, y: 32 }, + uvOffset: { x: 0, y: 0 }, + uvScale: { x: 1, y: 1 }, + layer: 0, + flipX: false, + flipY: false +}); +const Camera = defineBuiltin('Camera', { + projectionType: 0, + fov: 60, + orthoSize: 5, + nearPlane: 0.1, + farPlane: 1000, + aspectRatio: 1.77, + isActive: true, + priority: 0 +}); +const Canvas = defineBuiltin('Canvas', { + designResolution: { x: 1920, y: 1080 }, + pixelsPerUnit: 100, + scaleMode: 0, + matchWidthOrHeight: 0.5, + backgroundColor: { x: 0, y: 0, z: 0, w: 1 } +}); +const Velocity = defineBuiltin('Velocity', { + linear: { x: 0, y: 0, z: 0 }, + angular: { x: 0, y: 0, z: 0 } +}); +const Parent = defineBuiltin('Parent', { + entity: 0 +}); +const Children = defineBuiltin('Children', { + entities: [] +}); + +/** + * @file resource.ts + * @brief Resource system for global singleton data + */ +let resourceCounter = 0; +function defineResource(defaultValue, name) { + const id = ++resourceCounter; + return { + _id: Symbol(`Resource_${id}_${name ?? ''}`), + _name: name ?? `Resource_${id}`, + _default: defaultValue + }; +} +function Res(resource) { + return { _type: 'res', _resource: resource }; +} +function ResMut(resource) { + return { _type: 'res_mut', _resource: resource }; +} +// ============================================================================= +// Resource Instances (Runtime) +// ============================================================================= +class ResMutInstance { + constructor(value, setter) { + this.value_ = value; + this.setter_ = setter; + } + get() { + return this.value_; + } + set(value) { + this.value_ = value; + this.setter_(value); + } + modify(fn) { + fn(this.value_); + this.setter_(this.value_); + } +} +// ============================================================================= +// Resource Storage +// ============================================================================= +class ResourceStorage { + constructor() { + this.resources_ = new Map(); + } + insert(resource, value) { + this.resources_.set(resource._id, value); + } + get(resource) { + if (!this.resources_.has(resource._id)) { + this.resources_.set(resource._id, resource._default); + } + return this.resources_.get(resource._id); + } + set(resource, value) { + this.resources_.set(resource._id, value); + } + has(resource) { + return this.resources_.has(resource._id); + } + remove(resource) { + this.resources_.delete(resource._id); + } + getResMut(resource) { + return new ResMutInstance(this.get(resource), (v) => this.set(resource, v)); + } +} +const Time = defineResource({ + delta: 0, + elapsed: 0, + frameCount: 0 +}, 'Time'); +const Input = defineResource({ + keysDown: new Set(), + keysPressed: new Set(), + keysReleased: new Set(), + mouseX: 0, + mouseY: 0, + mouseButtons: new Set() +}, 'Input'); + +/** + * @file query.ts + * @brief Component query system with mutable component support + */ +function Mut(component) { + return { _type: 'mut', _component: component }; +} +function isMutWrapper(value) { + return typeof value === 'object' && value !== null && value._type === 'mut'; +} +function createQueryDescriptor(components, withFilters = [], withoutFilters = []) { + const mutIndices = []; + components.forEach((comp, i) => { + if (isMutWrapper(comp)) { + mutIndices.push(i); + } + }); + return { + _type: 'query', + _components: components, + _mutIndices: mutIndices, + _with: withFilters, + _without: withoutFilters + }; +} +// ============================================================================= +// Query Factory +// ============================================================================= +function Query(...components) { + return createQueryDescriptor(components); +} +class QueryInstance { + constructor(world, descriptor) { + this.pendingMutations_ = []; + this.world_ = world; + this.descriptor_ = descriptor; + } + getActualComponent(arg) { + return isMutWrapper(arg) ? arg._component : arg; + } + commitPending() { + for (const mutation of this.pendingMutations_) { + this.world_.insert(mutation.entity, mutation.component, mutation.data); + } + this.pendingMutations_ = []; + } + *[Symbol.iterator]() { + const { _components, _mutIndices, _with, _without } = this.descriptor_; + const actualComponents = _components.map(c => this.getActualComponent(c)); + const allRequired = [...actualComponents, ..._with]; + const entities = this.world_.getEntitiesWithComponents(allRequired); + let prevEntity = null; + let prevMutData = []; + try { + for (const entity of entities) { + // Commit previous entity's mutations before moving to next + if (prevEntity !== null) { + for (const mut of prevMutData) { + this.world_.insert(prevEntity, mut.component, mut.data); + } + } + prevMutData = []; + let excluded = false; + for (const comp of _without) { + if (this.world_.has(entity, comp)) { + excluded = true; + break; + } + } + if (excluded) + continue; + const components = actualComponents.map(comp => this.world_.get(entity, comp)); + // Track mutable components for write-back + for (const idx of _mutIndices) { + prevMutData.push({ + component: actualComponents[idx], + data: components[idx] + }); + } + prevEntity = entity; + yield [entity, ...components]; + } + } + finally { + // Commit last entity's mutations + if (prevEntity !== null) { + for (const mut of prevMutData) { + this.world_.insert(prevEntity, mut.component, mut.data); + } + } + } + } + forEach(callback) { + for (const [entity, ...components] of this) { + callback(entity, ...components); + } + } + single() { + for (const result of this) { + return result; + } + return null; + } + isEmpty() { + return this.single() === null; + } + count() { + let n = 0; + for (const _ of this) { + n++; + } + return n; + } + toArray() { + return [...this]; + } +} + +/** + * @file commands.ts + * @brief Deferred entity/component operations + */ +function Commands() { + return { _type: 'commands' }; +} +// ============================================================================= +// Entity Commands (Builder for spawning) +// ============================================================================= +class EntityCommands { + constructor(commands, entity) { + this.components_ = []; + this.commands_ = commands; + if (entity === null) { + this.entityRef_ = { entity: 0 }; + this.isNew_ = true; + } + else { + this.entityRef_ = { entity }; + this.isNew_ = false; + } + } + insert(component, data) { + let instance; + if (isBuiltinComponent(component)) { + instance = { ...component._default, ...data }; + } + else { + instance = component.create(data); + } + if (this.isNew_) { + this.components_.push({ component, data: instance }); + } + else { + this.commands_.queueInsert(this.entityRef_.entity, component, instance); + } + return this; + } + remove(component) { + if (!this.isNew_) { + this.commands_.queueRemove(this.entityRef_.entity, component); + } + return this; + } + id() { + if (this.isNew_ && this.entityRef_.entity === 0) { + this.finalize(); + } + return this.entityRef_.entity; + } + finalize() { + if (this.isNew_) { + this.commands_.spawnImmediate(this.components_, this.entityRef_); + this.isNew_ = false; + } + } +} +// ============================================================================= +// Commands Instance (Runtime) +// ============================================================================= +class CommandsInstance { + constructor(world, resources) { + this.pending_ = []; + this.world_ = world; + this.resources_ = resources; + } + spawn() { + return new EntityCommands(this, null); + } + entity(entity) { + return new EntityCommands(this, entity); + } + despawn(entity) { + this.pending_.push({ type: 'despawn', entity }); + return this; + } + insertResource(resource, value) { + this.pending_.push({ + type: 'insert_resource', + resource: resource, + value + }); + return this; + } + queueInsert(entity, component, data) { + this.pending_.push({ type: 'insert', entity, component, data }); + } + queueRemove(entity, component) { + this.pending_.push({ type: 'remove', entity, component }); + } + spawnImmediate(components, entityRef) { + const entity = this.world_.spawn(); + entityRef.entity = entity; + for (const entry of components) { + this.world_.insert(entity, entry.component, entry.data); + } + } + flush() { + for (const cmd of this.pending_) { + this.executeCommand(cmd); + } + this.pending_ = []; + } + executeCommand(cmd) { + switch (cmd.type) { + case 'spawn': { + const entity = this.world_.spawn(); + cmd.entityRef.entity = entity; + for (const entry of cmd.components) { + this.world_.insert(entity, entry.component, entry.data); + } + break; + } + case 'despawn': + this.world_.despawn(cmd.entity); + break; + case 'insert': + this.world_.insert(cmd.entity, cmd.component, cmd.data); + break; + case 'remove': + this.world_.remove(cmd.entity, cmd.component); + break; + case 'insert_resource': + this.resources_.insert(cmd.resource, cmd.value); + break; + } + } +} + +/** + * @file system.ts + * @brief System definition and scheduling + */ +// ============================================================================= +// Schedule Phases +// ============================================================================= +exports.Schedule = void 0; +(function (Schedule) { + Schedule[Schedule["Startup"] = 0] = "Startup"; + Schedule[Schedule["First"] = 1] = "First"; + Schedule[Schedule["PreUpdate"] = 2] = "PreUpdate"; + Schedule[Schedule["Update"] = 3] = "Update"; + Schedule[Schedule["PostUpdate"] = 4] = "PostUpdate"; + Schedule[Schedule["Last"] = 5] = "Last"; + Schedule[Schedule["FixedPreUpdate"] = 10] = "FixedPreUpdate"; + Schedule[Schedule["FixedUpdate"] = 11] = "FixedUpdate"; + Schedule[Schedule["FixedPostUpdate"] = 12] = "FixedPostUpdate"; +})(exports.Schedule || (exports.Schedule = {})); +let systemCounter = 0; +function defineSystem(params, fn, options) { + const id = ++systemCounter; + return { + _id: Symbol(`System_${id}_${options?.name ?? ''}`), + _params: params, + _fn: fn, + _name: options?.name ?? `System_${id}` + }; +} +// ============================================================================= +// System Runner +// ============================================================================= +class SystemRunner { + constructor(world, resources) { + this.world_ = world; + this.resources_ = resources; + } + run(system) { + const args = system._params.map(param => this.resolveParam(param)); + system._fn(...args); + for (const arg of args) { + if (arg instanceof CommandsInstance) { + arg.flush(); + } + } + } + resolveParam(param) { + switch (param._type) { + case 'query': + return new QueryInstance(this.world_, param); + case 'res': + return this.resources_.get(param._resource); + case 'res_mut': + return this.resources_.getResMut(param._resource); + case 'commands': + return new CommandsInstance(this.world_, this.resources_); + default: + throw new Error('Unknown system parameter type'); + } + } +} + +/** + * @file world.ts + * @brief ECS World with C++ Registry integration + */ +// ============================================================================= +// World +// ============================================================================= +class World { + constructor() { + this.cppRegistry_ = null; + this.entities_ = new Set(); + this.tsStorage_ = new Map(); + } + connectCpp(cppRegistry) { + this.cppRegistry_ = cppRegistry; + } + get hasCpp() { + return this.cppRegistry_ !== null; + } + // ========================================================================= + // Entity Management + // ========================================================================= + spawn() { + let entity; + if (this.cppRegistry_) { + entity = this.cppRegistry_.create(); + } + else { + entity = (this.entities_.size + 1); + } + this.entities_.add(entity); + return entity; + } + despawn(entity) { + if (this.cppRegistry_) { + this.cppRegistry_.destroy(entity); + } + this.entities_.delete(entity); + for (const storage of this.tsStorage_.values()) { + storage.delete(entity); + } + } + valid(entity) { + if (this.cppRegistry_) { + return this.cppRegistry_.valid(entity); + } + return this.entities_.has(entity); + } + entityCount() { + return this.entities_.size; + } + getAllEntities() { + return Array.from(this.entities_); + } + setParent(child, parent) { + if (this.cppRegistry_) { + this.cppRegistry_.setParent(child, parent); + } + } + // ========================================================================= + // Component Management + // ========================================================================= + insert(entity, component, data) { + if (isBuiltinComponent(component)) { + return this.insertBuiltin(entity, component, data); + } + return this.insertScript(entity, component, data); + } + get(entity, component) { + if (isBuiltinComponent(component)) { + return this.getBuiltin(entity, component); + } + return this.getScript(entity, component); + } + has(entity, component) { + if (isBuiltinComponent(component)) { + return this.hasBuiltin(entity, component); + } + return this.hasScript(entity, component); + } + remove(entity, component) { + if (isBuiltinComponent(component)) { + this.removeBuiltin(entity, component); + } + else { + this.removeScript(entity, component); + } + } + // ========================================================================= + // Builtin Component Operations (C++ Registry) + // ========================================================================= + insertBuiltin(entity, component, data) { + const merged = { ...component._default, ...data }; + if (this.cppRegistry_) { + const adder = `add${component._cppName}`; + const fn = this.cppRegistry_[adder]; + fn.call(this.cppRegistry_, entity, merged); + } + return merged; + } + getBuiltin(entity, component) { + if (!this.cppRegistry_) { + throw new Error('C++ Registry not connected'); + } + const getter = `get${component._cppName}`; + const fn = this.cppRegistry_[getter]; + return fn.call(this.cppRegistry_, entity); + } + hasBuiltin(entity, component) { + if (!this.cppRegistry_) { + return false; + } + const checker = `has${component._cppName}`; + const fn = this.cppRegistry_[checker]; + return fn.call(this.cppRegistry_, entity); + } + removeBuiltin(entity, component) { + if (!this.cppRegistry_) { + return; + } + const remover = `remove${component._cppName}`; + const fn = this.cppRegistry_[remover]; + fn.call(this.cppRegistry_, entity); + } + // ========================================================================= + // Script Component Operations (TypeScript storage) + // ========================================================================= + insertScript(entity, component, data) { + const value = component.create(data); + this.getStorage(component).set(entity, value); + return value; + } + getScript(entity, component) { + const storage = this.tsStorage_.get(component._id); + if (!storage) { + throw new Error(`Component not found: ${component._name}`); + } + return storage.get(entity); + } + hasScript(entity, component) { + const storage = this.tsStorage_.get(component._id); + return storage?.has(entity) ?? false; + } + removeScript(entity, component) { + const storage = this.tsStorage_.get(component._id); + storage?.delete(entity); + } + getStorage(component) { + let storage = this.tsStorage_.get(component._id); + if (!storage) { + storage = new Map(); + this.tsStorage_.set(component._id, storage); + } + return storage; + } + // ========================================================================= + // Query Support + // ========================================================================= + getEntitiesWithComponents(components) { + if (components.length === 0) { + return this.getAllEntities(); + } + const entities = []; + for (const entity of this.entities_) { + let hasAll = true; + for (const comp of components) { + if (!this.has(entity, comp)) { + hasAll = false; + break; + } + } + if (hasAll) { + entities.push(entity); + } + } + return entities; + } +} + +/** + * @file text.ts + * @brief Text component definition for UI text rendering + */ +// ============================================================================= +// Text Alignment Enums +// ============================================================================= +exports.TextAlign = void 0; +(function (TextAlign) { + TextAlign[TextAlign["Left"] = 0] = "Left"; + TextAlign[TextAlign["Center"] = 1] = "Center"; + TextAlign[TextAlign["Right"] = 2] = "Right"; +})(exports.TextAlign || (exports.TextAlign = {})); +exports.TextVerticalAlign = void 0; +(function (TextVerticalAlign) { + TextVerticalAlign[TextVerticalAlign["Top"] = 0] = "Top"; + TextVerticalAlign[TextVerticalAlign["Middle"] = 1] = "Middle"; + TextVerticalAlign[TextVerticalAlign["Bottom"] = 2] = "Bottom"; +})(exports.TextVerticalAlign || (exports.TextVerticalAlign = {})); +exports.TextOverflow = void 0; +(function (TextOverflow) { + TextOverflow[TextOverflow["Visible"] = 0] = "Visible"; + TextOverflow[TextOverflow["Clip"] = 1] = "Clip"; + TextOverflow[TextOverflow["Ellipsis"] = 2] = "Ellipsis"; +})(exports.TextOverflow || (exports.TextOverflow = {})); +// ============================================================================= +// Text Component Definition +// ============================================================================= +const Text = defineComponent('Text', { + content: '', + fontFamily: 'Arial', + fontSize: 24, + color: { x: 1, y: 1, z: 1, w: 1 }, + align: exports.TextAlign.Left, + verticalAlign: exports.TextVerticalAlign.Top, + wordWrap: true, + overflow: exports.TextOverflow.Visible, + lineHeight: 1.2, + dirty: true, +}); + +/** + * @file TextRenderer.ts + * @brief Renders text to GPU textures using Canvas 2D API + */ +// ============================================================================= +// Text Renderer +// ============================================================================= +class TextRenderer { + constructor(module) { + this.cache = new Map(); + this.module = module; + if (typeof OffscreenCanvas !== 'undefined') { + this.canvas = new OffscreenCanvas(512, 512); + this.ctx = this.canvas.getContext('2d', { willReadFrequently: true }); + } + else { + this.canvas = document.createElement('canvas'); + this.canvas.width = 512; + this.canvas.height = 512; + this.ctx = this.canvas.getContext('2d', { willReadFrequently: true }); + } + } + /** + * Renders text to a texture and returns the handle + */ + renderText(text, uiRect) { + const ctx = this.ctx; + const canvas = this.canvas; + ctx.font = `${text.fontSize}px ${text.fontFamily}`; + const hasContainer = uiRect && uiRect.size.x > 0 && uiRect.size.y > 0; + const containerWidth = hasContainer ? uiRect.size.x : 0; + const containerHeight = hasContainer ? uiRect.size.y : 0; + const shouldWrap = text.wordWrap && hasContainer; + let lines = this.wrapText(text.content, shouldWrap ? containerWidth : 0); + const lineHeightPx = Math.ceil(text.fontSize * text.lineHeight); + const padding = Math.ceil(text.fontSize * 0.2); + const measuredWidth = Math.ceil(this.measureWidth(lines)); + const measuredHeight = Math.ceil(lines.length * lineHeightPx); + const width = hasContainer ? Math.ceil(containerWidth) : measuredWidth + padding * 2; + const height = hasContainer ? Math.ceil(containerHeight) : measuredHeight + padding * 2; + // Handle overflow ellipsis + if (hasContainer && text.overflow === exports.TextOverflow.Ellipsis && measuredHeight > containerHeight) { + const maxLines = Math.floor(containerHeight / lineHeightPx); + if (maxLines > 0 && lines.length > maxLines) { + lines = lines.slice(0, maxLines); + const lastLine = lines[maxLines - 1]; + lines[maxLines - 1] = this.truncateWithEllipsis(lastLine, containerWidth); + } + } + if (canvas.width < width || canvas.height < height) { + const newWidth = Math.max(canvas.width, this.nextPowerOf2(width)); + const newHeight = Math.max(canvas.height, this.nextPowerOf2(height)); + canvas.width = newWidth; + canvas.height = newHeight; + } + const r = Math.round(text.color.x * 255); + const g = Math.round(text.color.y * 255); + const b = Math.round(text.color.z * 255); + const a = text.color.w; + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.font = `${text.fontSize}px ${text.fontFamily}`; + ctx.textAlign = this.mapAlign(text.align); + ctx.textBaseline = 'top'; + ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a})`; + // Handle overflow clip + if (hasContainer && text.overflow === exports.TextOverflow.Clip) { + ctx.save(); + ctx.beginPath(); + ctx.rect(0, 0, width, height); + ctx.clip(); + } + const textBlockHeight = lines.length * lineHeightPx; + let startY; + if (hasContainer) { + switch (text.verticalAlign) { + case exports.TextVerticalAlign.Top: + startY = padding; + break; + case exports.TextVerticalAlign.Middle: + startY = (height - textBlockHeight) / 2; + break; + case exports.TextVerticalAlign.Bottom: + startY = height - textBlockHeight - padding; + break; + default: startY = padding; + } + } + else { + startY = padding; + } + let y = startY; + for (const line of lines) { + let x; + switch (text.align) { + case exports.TextAlign.Left: + x = padding; + break; + case exports.TextAlign.Center: + x = width / 2; + break; + case exports.TextAlign.Right: + x = width - padding; + break; + default: + x = padding; + } + ctx.fillText(line, x, y); + y += lineHeightPx; + } + if (hasContainer && text.overflow === exports.TextOverflow.Clip) { + ctx.restore(); + } + const imageData = ctx.getImageData(0, 0, width, height); + const pixels = new Uint8Array(imageData.data.buffer); + const flipped = this.flipVertically(pixels, width, height); + const rm = this.module.getResourceManager(); + const ptr = this.module._malloc(flipped.length); + this.module.HEAPU8.set(flipped, ptr); + const textureHandle = rm.createTexture(width, height, ptr, flipped.length, 1); + this.module._free(ptr); + return { textureHandle, width, height }; + } + truncateWithEllipsis(text, maxWidth) { + const ellipsis = '...'; + if (this.ctx.measureText(text).width <= maxWidth) { + return text; + } + let truncated = text; + while (truncated.length > 0 && this.ctx.measureText(truncated + ellipsis).width > maxWidth) { + truncated = truncated.slice(0, -1); + } + return truncated + ellipsis; + } + /** + * Renders text for an entity and caches the result + */ + renderForEntity(entity, text, uiRect) { + const existing = this.cache.get(entity); + if (existing) { + const rm = this.module.getResourceManager(); + rm.releaseTexture(existing.textureHandle); + } + const result = this.renderText(text, uiRect); + this.cache.set(entity, result); + return result; + } + /** + * Gets cached render result for entity + */ + getCached(entity) { + return this.cache.get(entity); + } + /** + * Releases texture for entity + */ + release(entity) { + const cached = this.cache.get(entity); + if (cached) { + const rm = this.module.getResourceManager(); + rm.releaseTexture(cached.textureHandle); + this.cache.delete(entity); + } + } + /** + * Releases all cached textures + */ + releaseAll() { + const rm = this.module.getResourceManager(); + for (const result of this.cache.values()) { + rm.releaseTexture(result.textureHandle); + } + this.cache.clear(); + } + // ========================================================================= + // Private Methods + // ========================================================================= + wrapText(text, maxWidth) { + if (!text) + return ['']; + if (maxWidth <= 0) + return text.split('\n'); + const paragraphs = text.split('\n'); + const lines = []; + for (const paragraph of paragraphs) { + if (!paragraph) { + lines.push(''); + continue; + } + let currentLine = ''; + for (const char of paragraph) { + const testLine = currentLine + char; + const metrics = this.ctx.measureText(testLine); + if (metrics.width > maxWidth && currentLine) { + lines.push(currentLine); + currentLine = char; + } + else { + currentLine = testLine; + } + } + if (currentLine) { + lines.push(currentLine); + } + } + return lines.length > 0 ? lines : ['']; + } + measureWidth(lines) { + let maxWidth = 0; + for (const line of lines) { + const metrics = this.ctx.measureText(line); + const width = metrics.actualBoundingBoxLeft !== undefined + ? metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight + : metrics.width; + maxWidth = Math.max(maxWidth, width); + } + return maxWidth; + } + mapAlign(align) { + switch (align) { + case exports.TextAlign.Left: + return 'left'; + case exports.TextAlign.Center: + return 'center'; + case exports.TextAlign.Right: + return 'right'; + } + } + nextPowerOf2(n) { + let p = 1; + while (p < n) + p *= 2; + return p; + } + flipVertically(pixels, width, height) { + const rowSize = width * 4; + const flipped = new Uint8Array(pixels.length); + for (let y = 0; y < height; y++) { + const srcOffset = y * rowSize; + const dstOffset = (height - 1 - y) * rowSize; + flipped.set(pixels.subarray(srcOffset, srcOffset + rowSize), dstOffset); + } + return flipped; + } +} + +/** + * @file TextPlugin.ts + * @brief Plugin that automatically syncs Text components to Sprite textures + */ +// ============================================================================= +// Text Plugin +// ============================================================================= +class TextPlugin { + build(app) { + const module = app.wasmModule; + if (!module) { + console.warn('TextPlugin: No WASM module available'); + return; + } + const renderer = new TextRenderer(module); + const world = app.world; + app.addSystemToSchedule(exports.Schedule.PreUpdate, defineSystem([], () => { + const entities = world.getEntitiesWithComponents([Text]); + for (const entity of entities) { + const text = world.get(entity, Text); + if (!text.dirty) + continue; + if (!world.has(entity, Sprite)) { + world.insert(entity, Sprite, { + texture: 0, + size: { x: 0, y: 0 }, + color: { x: 1, y: 1, z: 1, w: 1 }, + anchor: { x: 0.5, y: 0.5 }, + flip: { x: false, y: false } + }); + } + const result = renderer.renderForEntity(entity, text); + const sprite = world.get(entity, Sprite); + sprite.texture = result.textureHandle; + sprite.size = { x: result.width, y: result.height }; + sprite.uvOffset = { x: 0, y: 0 }; + sprite.uvScale = { x: 1, y: 1 }; + world.insert(entity, Sprite, sprite); + text.dirty = false; + } + })); + } +} +const textPlugin = new TextPlugin(); + +/** + * @file app.ts + * @brief Application builder and web platform integration + */ +// ============================================================================= +// App +// ============================================================================= +class App { + constructor() { + this.systems_ = new Map(); + this.runner_ = null; + this.running_ = false; + this.lastTime_ = 0; + this.module_ = null; + this.mainLoop = () => { + if (!this.running_) { + return; + } + const now = performance.now(); + const deltaMs = now - this.lastTime_; + this.lastTime_ = now; + const delta = deltaMs / 1000; + this.updateTime(delta); + this.runSchedule(exports.Schedule.First); + this.runSchedule(exports.Schedule.PreUpdate); + this.runSchedule(exports.Schedule.Update); + this.runSchedule(exports.Schedule.PostUpdate); + this.runSchedule(exports.Schedule.Last); + requestAnimationFrame(this.mainLoop); + }; + this.world_ = new World(); + this.resources_ = new ResourceStorage(); + for (const s of Object.values(exports.Schedule)) { + if (typeof s === 'number') { + this.systems_.set(s, []); + } + } + } + static new() { + return new App(); + } + // ========================================================================= + // Plugins + // ========================================================================= + addPlugin(plugin) { + plugin.build(this); + return this; + } + // ========================================================================= + // Systems + // ========================================================================= + addSystemToSchedule(schedule, system) { + this.systems_.get(schedule).push({ system }); + return this; + } + addSystem(system) { + return this.addSystemToSchedule(exports.Schedule.Update, system); + } + addStartupSystem(system) { + return this.addSystemToSchedule(exports.Schedule.Startup, system); + } + // ========================================================================= + // C++ Integration + // ========================================================================= + connectCpp(cppRegistry, module) { + this.world_.connectCpp(cppRegistry); + if (module) { + this.module_ = module; + } + return this; + } + get wasmModule() { + return this.module_; + } + // ========================================================================= + // World Access + // ========================================================================= + get world() { + return this.world_; + } + // ========================================================================= + // Resource Access + // ========================================================================= + insertResource(resource, value) { + this.resources_.insert(resource, value); + return this; + } + getResource(resource) { + return this.resources_.get(resource); + } + hasResource(resource) { + return this.resources_.has(resource); + } + // ========================================================================= + // Run + // ========================================================================= + run() { + if (this.running_) { + return; + } + this.running_ = true; + this.runner_ = new SystemRunner(this.world_, this.resources_); + this.resources_.insert(Time, { delta: 0, elapsed: 0, frameCount: 0 }); + this.runSchedule(exports.Schedule.Startup); + this.lastTime_ = performance.now(); + this.mainLoop(); + } + quit() { + this.running_ = false; + } + // ========================================================================= + // Internal + // ========================================================================= + runSchedule(schedule) { + const systems = this.systems_.get(schedule); + if (!systems || !this.runner_) { + return; + } + for (const entry of systems) { + this.runner_.run(entry.system); + } + } + updateTime(delta) { + const time = this.resources_.get(Time); + const newTime = { + delta, + elapsed: time.elapsed + delta, + frameCount: time.frameCount + 1 + }; + this.resources_.set(Time, newTime); + } +} +function createWebApp(module, options) { + const app = App.new(); + const cppRegistry = new module.Registry(); + app.connectCpp(cppRegistry, module); + if (options?.glContextHandle) { + module.initRendererWithContext(options.glContextHandle); + } + else { + module.initRenderer(); + } + const getViewportSize = options?.getViewportSize ?? (() => ({ + width: window.innerWidth * (window.devicePixelRatio || 1), + height: window.innerHeight * (window.devicePixelRatio || 1) + })); + const renderSystem = { + _id: Symbol('RenderSystem'), + _name: 'RenderSystem', + _params: [], + _fn: () => { + const { width, height } = getViewportSize(); + module.renderFrame(cppRegistry, width, height); + } + }; + app.addSystemToSchedule(exports.Schedule.Last, renderSystem); + app.addPlugin(textPlugin); + return app; +} + +/** + * @file UIRect.ts + * @brief UIRect component for UI layout with anchor and pivot + */ +// ============================================================================= +// UIRect Component Definition +// ============================================================================= +const UIRect = defineComponent('UIRect', { + size: { x: 100, y: 100 }, + anchor: { x: 0.5, y: 0.5 }, + pivot: { x: 0.5, y: 0.5 }, +}); + +/** + * @file AssetServer.ts + * @brief Asset loading and caching system + */ +// ============================================================================= +// AssetServer +// ============================================================================= +class AssetServer { + constructor(module) { + this.cache_ = new Map(); + this.pending_ = new Map(); + this.module_ = module; + if (typeof OffscreenCanvas !== 'undefined') { + this.canvas_ = new OffscreenCanvas(512, 512); + this.ctx_ = this.canvas_.getContext('2d', { willReadFrequently: true }); + } + else { + this.canvas_ = document.createElement('canvas'); + this.canvas_.width = 512; + this.canvas_.height = 512; + this.ctx_ = this.canvas_.getContext('2d', { willReadFrequently: true }); + } + } + // ========================================================================= + // Public API + // ========================================================================= + async loadTexture(source) { + const cached = this.cache_.get(source); + if (cached) { + return cached; + } + const pending = this.pending_.get(source); + if (pending) { + return pending; + } + const promise = this.loadTextureInternal(source); + this.pending_.set(source, promise); + try { + const result = await promise; + this.cache_.set(source, result); + return result; + } + finally { + this.pending_.delete(source); + } + } + getTexture(source) { + return this.cache_.get(source); + } + hasTexture(source) { + return this.cache_.has(source); + } + releaseTexture(source) { + const info = this.cache_.get(source); + if (info) { + const rm = this.module_.getResourceManager(); + rm.releaseTexture(info.handle); + this.cache_.delete(source); + } + } + releaseAll() { + const rm = this.module_.getResourceManager(); + for (const info of this.cache_.values()) { + rm.releaseTexture(info.handle); + } + this.cache_.clear(); + } + setTextureMetadata(handle, border) { + const rm = this.module_.getResourceManager(); + rm.setTextureMetadata(handle, border.left, border.right, border.top, border.bottom); + } + setTextureMetadataByPath(source, border) { + const info = this.cache_.get(source); + if (info) { + this.setTextureMetadata(info.handle, border); + return true; + } + return false; + } + // ========================================================================= + // Private Methods + // ========================================================================= + async loadTextureInternal(source) { + const img = await this.loadImage(source); + return this.createTextureFromImage(img); + } + async loadImage(source) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = async () => { + if (typeof createImageBitmap !== 'undefined') { + try { + const bitmap = await createImageBitmap(img, { + premultiplyAlpha: 'none', + colorSpaceConversion: 'none' + }); + resolve(bitmap); + return; + } + catch { + // Fall back to Image element + } + } + resolve(img); + }; + img.onerror = () => reject(new Error(`Failed to load image: ${source}`)); + img.src = source; + }); + } + createTextureFromImage(img) { + const { width, height } = img; + if (this.canvas_.width < width || this.canvas_.height < height) { + this.canvas_.width = Math.max(this.canvas_.width, this.nextPowerOf2(width)); + this.canvas_.height = Math.max(this.canvas_.height, this.nextPowerOf2(height)); + } + this.ctx_.clearRect(0, 0, this.canvas_.width, this.canvas_.height); + this.ctx_.globalCompositeOperation = 'copy'; + this.ctx_.drawImage(img, 0, 0); + this.ctx_.globalCompositeOperation = 'source-over'; + const imageData = this.ctx_.getImageData(0, 0, width, height); + const pixels = new Uint8Array(imageData.data.buffer); + this.unpremultiplyAlpha(pixels); + const flipped = this.flipVertically(pixels, width, height); + const rm = this.module_.getResourceManager(); + const ptr = this.module_._malloc(flipped.length); + this.module_.HEAPU8.set(flipped, ptr); + const handle = rm.createTexture(width, height, ptr, flipped.length, 1); + this.module_._free(ptr); + return { handle, width, height }; + } + unpremultiplyAlpha(pixels) { + for (let i = 0; i < pixels.length; i += 4) { + const a = pixels[i + 3]; + if (a > 0 && a < 255) { + const scale = 255 / a; + pixels[i] = Math.min(255, Math.round(pixels[i] * scale)); + pixels[i + 1] = Math.min(255, Math.round(pixels[i + 1] * scale)); + pixels[i + 2] = Math.min(255, Math.round(pixels[i + 2] * scale)); + } + } + } + flipVertically(pixels, width, height) { + const rowSize = width * 4; + const flipped = new Uint8Array(pixels.length); + for (let y = 0; y < height; y++) { + const srcOffset = y * rowSize; + const dstOffset = (height - 1 - y) * rowSize; + flipped.set(pixels.subarray(srcOffset, srcOffset + rowSize), dstOffset); + } + return flipped; + } + nextPowerOf2(n) { + let p = 1; + while (p < n) + p *= 2; + return p; + } +} + +/** + * @file AssetPlugin.ts + * @brief Plugin that provides asset loading capabilities + */ +const Assets = defineResource({ server: null }, 'Assets'); +// ============================================================================= +// Asset Plugin +// ============================================================================= +class AssetPlugin { + build(app) { + const module = app.wasmModule; + if (!module) { + console.warn('AssetPlugin: No WASM module available'); + return; + } + const server = new AssetServer(module); + app.insertResource(Assets, { server }); + } +} +const assetPlugin = new AssetPlugin(); + +/** + * @file scene.ts + * @brief Scene loading utilities + */ +// ============================================================================= +// Scene Loader +// ============================================================================= +function loadSceneData(world, sceneData) { + const entityMap = new Map(); + for (const entityData of sceneData.entities) { + const entity = world.spawn(); + entityMap.set(entityData.id, entity); + for (const compData of entityData.components) { + loadComponent(world, entity, compData); + } + } + return entityMap; +} +async function loadSceneWithAssets(world, sceneData, options) { + const entityMap = new Map(); + const assetServer = options?.assetServer; + const texturePathToUrl = new Map(); + for (const entityData of sceneData.entities) { + const entity = world.spawn(); + entityMap.set(entityData.id, entity); + for (const compData of entityData.components) { + if (compData.type === 'Sprite' && assetServer) { + const data = compData.data; + if (typeof data.texture === 'string' && data.texture) { + const texturePath = data.texture; + const isDataUrl = texturePath.startsWith('data:'); + const url = isDataUrl + ? texturePath + : options?.assetBaseUrl + ? `${options.assetBaseUrl}/${texturePath}` + : `/${texturePath}`; + try { + const info = await assetServer.loadTexture(url); + data.texture = info.handle; + texturePathToUrl.set(texturePath, url); + } + catch (err) { + console.warn(`Failed to load texture: ${url}`, err); + data.texture = 0; + } + } + } + loadComponent(world, entity, compData); + } + } + for (const entityData of sceneData.entities) { + if (entityData.parent !== null) { + const entity = entityMap.get(entityData.id); + const parentEntity = entityMap.get(entityData.parent); + if (entity !== undefined && parentEntity !== undefined) { + world.setParent(entity, parentEntity); + } + } + } + if (assetServer && sceneData.textureMetadata) { + for (const [texturePath, metadata] of Object.entries(sceneData.textureMetadata)) { + const url = texturePathToUrl.get(texturePath); + if (url && metadata.sliceBorder) { + assetServer.setTextureMetadataByPath(url, metadata.sliceBorder); + } + } + } + return entityMap; +} +function loadComponent(world, entity, compData) { + const data = compData.data; + switch (compData.type) { + case 'LocalTransform': + world.insert(entity, LocalTransform, data); + break; + case 'Sprite': + world.insert(entity, Sprite, data); + break; + case 'Camera': + world.insert(entity, Camera, data); + break; + case 'Canvas': + world.insert(entity, Canvas, data); + break; + case 'Text': + world.insert(entity, Text, data); + break; + default: + console.warn(`Unknown component type: ${compData.type}`); + } +} + +/** + * @file PreviewPlugin.ts + * @brief Plugin for editor preview functionality + */ +// ============================================================================= +// PreviewPlugin +// ============================================================================= +class PreviewPlugin { + constructor(sceneUrl) { + this.app_ = null; + this.loadPromise_ = null; + this.sceneUrl_ = sceneUrl; + } + build(app) { + this.app_ = app; + if (!app.hasResource(Assets)) { + app.addPlugin(assetPlugin); + } + this.loadPromise_ = this.loadScene(); + } + /** + * @brief Wait for scene loading to complete + */ + async waitForReady() { + if (this.loadPromise_) { + await this.loadPromise_; + } + } + async loadScene() { + if (!this.app_) + return; + try { + const response = await platformFetch(this.sceneUrl_); + if (!response.ok) { + throw new Error(`Failed to fetch scene: ${response.status}`); + } + const sceneData = await response.json(); + const assets = this.app_.getResource(Assets); + await loadSceneWithAssets(this.app_.world, sceneData, { + assetServer: assets.server + }); + this.ensureCamera(); + } + catch (err) { + console.error('[PreviewPlugin] Failed to load scene:', err); + } + } + ensureCamera() { + if (!this.app_) + return; + const world = this.app_.world; + let hasActiveCamera = false; + const cameraEntities = world.getEntitiesWithComponents([Camera]); + for (const entity of cameraEntities) { + const camera = world.get(entity, Camera); + if (camera.isActive) { + hasActiveCamera = true; + break; + } + } + if (!hasActiveCamera) { + console.warn('[PreviewPlugin] No active camera found, creating default camera'); + const cameraEntity = world.spawn(); + const transformData = { + position: { x: 0, y: 0, z: 10 }, + rotation: { x: 0, y: 0, z: 0, w: 1 }, + scale: { x: 1, y: 1, z: 1 }, + }; + world.insert(cameraEntity, LocalTransform, transformData); + const cameraData = { + isActive: true, + projectionType: 0, + fov: 60, + orthoSize: 540, + nearPlane: 0.1, + farPlane: 1000, + aspectRatio: 1.77, + priority: 0, + }; + world.insert(cameraEntity, Camera, cameraData); + } + } +} + +/** + * @file index.wechat.ts + * @brief ESEngine SDK - WeChat MiniGame entry point + */ +// Initialize WeChat platform +initWeChatPlatform(); +setPlatform(wechatAdapter); + +exports.App = App; +exports.AssetPlugin = AssetPlugin; +exports.AssetServer = AssetServer; +exports.Assets = Assets; +exports.Camera = Camera; +exports.Canvas = Canvas; +exports.Children = Children; +exports.Commands = Commands; +exports.CommandsInstance = CommandsInstance; +exports.EntityCommands = EntityCommands; +exports.INVALID_ENTITY = INVALID_ENTITY; +exports.INVALID_TEXTURE = INVALID_TEXTURE; +exports.Input = Input; +exports.LocalTransform = LocalTransform; +exports.Mut = Mut; +exports.Parent = Parent; +exports.PreviewPlugin = PreviewPlugin; +exports.Query = Query; +exports.QueryInstance = QueryInstance; +exports.Res = Res; +exports.ResMut = ResMut; +exports.Sprite = Sprite; +exports.SystemRunner = SystemRunner; +exports.Text = Text; +exports.TextPlugin = TextPlugin; +exports.TextRenderer = TextRenderer; +exports.Time = Time; +exports.UIRect = UIRect; +exports.Velocity = Velocity; +exports.World = World; +exports.WorldTransform = WorldTransform; +exports.assetPlugin = assetPlugin; +exports.color = color; +exports.createWebApp = createWebApp; +exports.defineComponent = defineComponent; +exports.defineResource = defineResource; +exports.defineSystem = defineSystem; +exports.defineTag = defineTag; +exports.getPlatform = getPlatform; +exports.getPlatformType = getPlatformType; +exports.isBuiltinComponent = isBuiltinComponent; +exports.isPlatformInitialized = isPlatformInitialized; +exports.isWeChat = isWeChat; +exports.isWeb = isWeb; +exports.loadSceneData = loadSceneData; +exports.loadSceneWithAssets = loadSceneWithAssets; +exports.platformFetch = platformFetch; +exports.platformFileExists = platformFileExists; +exports.platformInstantiateWasm = platformInstantiateWasm; +exports.platformReadFile = platformReadFile; +exports.platformReadTextFile = platformReadTextFile; +exports.quat = quat; +exports.textPlugin = textPlugin; +exports.vec2 = vec2; +exports.vec3 = vec3; +exports.vec4 = vec4; +exports.wxFileExists = wxFileExists; +exports.wxFileExistsSync = wxFileExistsSync; +exports.wxReadFile = wxReadFile; +exports.wxReadTextFile = wxReadTextFile; +exports.wxWriteFile = wxWriteFile; diff --git a/desktop/public/wasm/esengine.wxgame.js b/desktop/public/wasm/esengine.wxgame.js new file mode 100644 index 000000000..03d951440 --- /dev/null +++ b/desktop/public/wasm/esengine.wxgame.js @@ -0,0 +1,2 @@ +var ESEngineModule=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else{}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["za"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("esengine.wxgame.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>abort("");var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}}if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};for(var[i,field]of fieldRecords.entries()){const getterReturnType=fieldTypes[i];const getter=field.getter;const getterContext=field.getterContext;const setterArgumentType=fieldTypes[i+fieldRecords.length];const setter=field.setter;const setterContext=field.setterContext;fields[field.fieldName]={read:ptr=>getterReturnType.fromWireType(getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,o));runDestructors(destructors)},optional:getterReturnType.optional}}return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)&&!fields[fieldName].optional){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var finalizationRegistry=false;var detachFinalizer=handle=>{};var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var init_ClassHandle=()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose){proto[symbolDispose]=proto["delete"]}};function ClassHandle(){}var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this.toWireType=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError(`Use 'new' to construct ${name}`)}if(undefined===registeredClass.constructor_body){throw new BindingError(`${name} has no accessible constructor`)}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i>2])}return array};function usesDestructorStack(argTypes){for(var i=1;i{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync,isNonnullReturn)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=AsciiToString(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker,isAsync);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this.fromWireType(HEAP8[pointer])}:function(pointer){return this.fromWireType(HEAPU8[pointer])};case 2:return signed?function(pointer){return this.fromWireType(HEAP16[pointer>>1])}:function(pointer){return this.fromWireType(HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this.fromWireType(HEAP32[pointer>>2])}:function(pointer){return this.fromWireType(HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};function getEnumValueType(rawValueType){return rawValueType===0?"object":rawValueType===1?"number":"string"}var __embind_register_enum=(rawType,name,size,isSigned,rawValueType)=>{name=AsciiToString(name);const valueType=getEnumValueType(rawValueType);switch(valueType){case"object":{function ctor(){}ctor.values={};registerType(rawType,{name,constructor:ctor,valueType,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor);break}case"number":{var keysMap={};registerType(rawType,{name,keysMap,valueType,fromWireType:c=>c,toWireType:(destructors,c)=>c,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}case"string":{var valuesMap={};var reverseMap={};var keysMap={};registerType(rawType,{name,valuesMap,reverseMap,keysMap,valueType,fromWireType:function(c){return this.reverseMap[c]},toWireType:function(destructors,c){return this.valuesMap[c]},readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}}};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=AsciiToString(name);switch(enumType.valueType){case"object":{var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value;break}case"number":{enumType.keysMap[name]=enumValue;break}case"string":{enumType.valuesMap[name]=enumValue;enumType.reverseMap[enumValue]=name;enumType.keysMap[name]=name;break}}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var __embind_register_std_string=(rawType,name)=>{name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}}else{HEAPU8.set(value,ptr)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:AsciiToString(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:AsciiToString(fieldName),getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext,setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext})};var __embind_register_void=(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetperformance.now();var _emscripten_date_now=()=>Date.now();var nowIsMonotonic=1;var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function _clock_time_get(clk_id,ignored_precision,ptime){ignored_precision=bigintToI53Checked(ignored_precision);if(!checkWasiClock(clk_id)){return 28}var now;if(clk_id===0){now=_emscripten_date_now()}else if(nowIsMonotonic){now=_emscripten_get_now()}else{return 52}var nsec=Math.round(now*1e3*1e3);HEAP64[ptime>>3]=BigInt(nsec);return 0}var readEmAsmArgsArray=[];var readEmAsmArgs=(sigPtr,buf)=>{readEmAsmArgsArray.length=0;var ch;while(ch=HEAPU8[sigPtr++]){var wide=ch!=105;wide&=ch!=112;buf+=wide&&buf%8?4:0;readEmAsmArgsArray.push(ch==112?HEAPU32[buf>>2]:ch==106?HEAP64[buf>>3]:ch==105?HEAP32[buf>>2]:HEAPF64[buf>>3]);buf+=wide?8:4}return readEmAsmArgsArray};var runEmAsmFunction=(code,sigPtr,argbuf)=>{var args=readEmAsmArgs(sigPtr,argbuf);return ASM_CONSTS[code](...args)};var _emscripten_asm_const_double=(code,sigPtr,argbuf)=>runEmAsmFunction(code,sigPtr,argbuf);var _emscripten_asm_const_int=(code,sigPtr,argbuf)=>runEmAsmFunction(code,sigPtr,argbuf);var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var GLctx;var webgl_enable_ANGLE_instanced_arrays=ctx=>{var ext=ctx.getExtension("ANGLE_instanced_arrays");if(ext){ctx["vertexAttribDivisor"]=(index,divisor)=>ext["vertexAttribDivisorANGLE"](index,divisor);ctx["drawArraysInstanced"]=(mode,first,count,primcount)=>ext["drawArraysInstancedANGLE"](mode,first,count,primcount);ctx["drawElementsInstanced"]=(mode,count,type,indices,primcount)=>ext["drawElementsInstancedANGLE"](mode,count,type,indices,primcount);return 1}};var webgl_enable_OES_vertex_array_object=ctx=>{var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=()=>ext["createVertexArrayOES"]();ctx["deleteVertexArray"]=vao=>ext["deleteVertexArrayOES"](vao);ctx["bindVertexArray"]=vao=>ext["bindVertexArrayOES"](vao);ctx["isVertexArray"]=vao=>ext["isVertexArrayOES"](vao);return 1}};var webgl_enable_WEBGL_draw_buffers=ctx=>{var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=(n,bufs)=>ext["drawBuffersWEBGL"](n,bufs);return 1}};var webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"));var webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"));var webgl_enable_EXT_polygon_offset_clamp=ctx=>!!(ctx.extPolygonOffsetClamp=ctx.getExtension("EXT_polygon_offset_clamp"));var webgl_enable_EXT_clip_control=ctx=>!!(ctx.extClipControl=ctx.getExtension("EXT_clip_control"));var webgl_enable_WEBGL_polygon_mode=ctx=>!!(ctx.webglPolygonMode=ctx.getExtension("WEBGL_polygon_mode"));var webgl_enable_WEBGL_multi_draw=ctx=>!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"));var getEmscriptenSupportedExtensions=ctx=>{var supportedExtensions=["ANGLE_instanced_arrays","EXT_blend_minmax","EXT_disjoint_timer_query","EXT_frag_depth","EXT_shader_texture_lod","EXT_sRGB","OES_element_index_uint","OES_fbo_render_mipmap","OES_standard_derivatives","OES_texture_float","OES_texture_half_float","OES_texture_half_float_linear","OES_vertex_array_object","WEBGL_color_buffer_float","WEBGL_depth_texture","WEBGL_draw_buffers","EXT_color_buffer_float","EXT_conservative_depth","EXT_disjoint_timer_query_webgl2","EXT_texture_norm16","NV_shader_noperspective_interpolation","WEBGL_clip_cull_distance","EXT_clip_control","EXT_color_buffer_half_float","EXT_depth_clamp","EXT_float_blend","EXT_polygon_offset_clamp","EXT_texture_compression_bptc","EXT_texture_compression_rgtc","EXT_texture_filter_anisotropic","KHR_parallel_shader_compile","OES_texture_float_linear","WEBGL_blend_func_extended","WEBGL_compressed_texture_astc","WEBGL_compressed_texture_etc","WEBGL_compressed_texture_etc1","WEBGL_compressed_texture_s3tc","WEBGL_compressed_texture_s3tc_srgb","WEBGL_debug_renderer_info","WEBGL_debug_shaders","WEBGL_lose_context","WEBGL_multi_draw","WEBGL_polygon_mode"];return(ctx.getSupportedExtensions()||[]).filter(ext=>supportedExtensions.includes(ext))};var registerPreMainLoop=f=>{typeof MainLoop!="undefined"&&MainLoop.preMainLoop.push(f)};var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],stringCache:{},stringiCache:{},unpackAlignment:4,unpackRowLength:0,recordError:errorCode=>{if(!GL.lastError){GL.lastError=errorCode}},getNewId:table=>{var ret=GL.counter++;for(var i=table.length;i{for(var i=0;i>2]=id}},MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:i=>32-Math.clz32(i===0?0:i-1),generateTempBuffers:(quads,context)=>{var largestIndex=GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE);context.tempVertexBufferCounters1=[];context.tempVertexBufferCounters2=[];context.tempVertexBufferCounters1.length=context.tempVertexBufferCounters2.length=largestIndex+1;context.tempVertexBuffers1=[];context.tempVertexBuffers2=[];context.tempVertexBuffers1.length=context.tempVertexBuffers2.length=largestIndex+1;context.tempIndexBuffers=[];context.tempIndexBuffers.length=largestIndex+1;for(var i=0;i<=largestIndex;++i){context.tempIndexBuffers[i]=null;context.tempVertexBufferCounters1[i]=context.tempVertexBufferCounters2[i]=0;var ringbufferLength=GL.numTempVertexBuffersPerSize;context.tempVertexBuffers1[i]=[];context.tempVertexBuffers2[i]=[];var ringbuffer1=context.tempVertexBuffers1[i];var ringbuffer2=context.tempVertexBuffers2[i];ringbuffer1.length=ringbuffer2.length=ringbufferLength;for(var j=0;j>1;var quadIndexes=new Uint16Array(numIndexes);var i=0,v=0;while(1){quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+1;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v+3;if(i>=numIndexes)break;v+=4}context.GLctx.bufferData(34963,quadIndexes,35044);context.GLctx.bindBuffer(34963,null)}},getTempVertexBuffer:sizeBytes=>{var idx=GL.log2ceilLookup(sizeBytes);var ringbuffer=GL.currentContext.tempVertexBuffers1[idx];var nextFreeBufferIndex=GL.currentContext.tempVertexBufferCounters1[idx];GL.currentContext.tempVertexBufferCounters1[idx]=GL.currentContext.tempVertexBufferCounters1[idx]+1&GL.numTempVertexBuffersPerSize-1;var vbo=ringbuffer[nextFreeBufferIndex];if(vbo){return vbo}var prevVBO=GLctx.getParameter(34964);ringbuffer[nextFreeBufferIndex]=GLctx.createBuffer();GLctx.bindBuffer(34962,ringbuffer[nextFreeBufferIndex]);GLctx.bufferData(34962,1<{var idx=GL.log2ceilLookup(sizeBytes);var ibo=GL.currentContext.tempIndexBuffers[idx];if(ibo){return ibo}var prevIBO=GLctx.getParameter(34965);GL.currentContext.tempIndexBuffers[idx]=GLctx.createBuffer();GLctx.bindBuffer(34963,GL.currentContext.tempIndexBuffers[idx]);GLctx.bufferData(34963,1<{if(!GL.currentContext){return}var vb=GL.currentContext.tempVertexBuffers1;GL.currentContext.tempVertexBuffers1=GL.currentContext.tempVertexBuffers2;GL.currentContext.tempVertexBuffers2=vb;vb=GL.currentContext.tempVertexBufferCounters1;GL.currentContext.tempVertexBufferCounters1=GL.currentContext.tempVertexBufferCounters2;GL.currentContext.tempVertexBufferCounters2=vb;var largestIndex=GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE);for(var i=0;i<=largestIndex;++i){GL.currentContext.tempVertexBufferCounters1[i]=0}},getSource:(shader,count,string,length)=>{var source="";for(var i=0;i>2]:undefined;source+=UTF8ToString(HEAPU32[string+i*4>>2],len)}return source},calcBufLength:(size,type,stride,count)=>{if(stride>0){return count*stride}var typeSize=GL.byteSizeByType[type-GL.byteSizeByTypeRoot];return size*typeSize*count},usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:count=>{GL.resetBufferBinding=false;for(var i=0;i{if(GL.resetBufferBinding){GLctx.bindBuffer(34962,GL.buffers[GLctx.currentArrayBufferBinding])}},createContext:(canvas,webGLContextAttributes)=>{if(!canvas.getContextSafariWebGL2Fixed){canvas.getContextSafariWebGL2Fixed=canvas.getContext;function fixedGetContext(ver,attrs){var gl=canvas.getContextSafariWebGL2Fixed(ver,attrs);return ver=="webgl"==gl instanceof WebGLRenderingContext?gl:null}canvas.getContext=fixedGetContext}var ctx=webGLContextAttributes.majorVersion>1?canvas.getContext("webgl2",webGLContextAttributes):canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},registerContext:(ctx,webGLContextAttributes)=>{var handle=GL.getNewId(GL.contexts);var context={handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault=="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}context.maxVertexAttribs=context.GLctx.getParameter(34921);context.clientBuffers=[];for(var i=0;i{GL.currentContext=GL.contexts[contextHandle];Module["ctx"]=GLctx=GL.currentContext?.GLctx;return!(contextHandle&&!GLctx)},getContext:contextHandle=>GL.contexts[contextHandle],deleteContext:contextHandle=>{if(GL.currentContext===GL.contexts[contextHandle]){GL.currentContext=null}if(typeof JSEvents=="object"){JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas)}if(GL.contexts[contextHandle]?.GLctx.canvas){GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined}GL.contexts[contextHandle]=null},initExtensions:context=>{context||=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;webgl_enable_WEBGL_multi_draw(GLctx);webgl_enable_EXT_polygon_offset_clamp(GLctx);webgl_enable_EXT_clip_control(GLctx);webgl_enable_WEBGL_polygon_mode(GLctx);webgl_enable_ANGLE_instanced_arrays(GLctx);webgl_enable_OES_vertex_array_object(GLctx);webgl_enable_WEBGL_draw_buffers(GLctx);webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx);webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx);if(context.version>=2){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query_webgl2")}if(context.version<2||!GLctx.disjointTimerQueryExt){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}for(var ext of getEmscriptenSupportedExtensions(GLctx)){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}}}};var webglPowerPreferences=["default","low-power","high-performance"];var maybeCStringToJsString=cString=>cString>2?UTF8ToString(cString):cString;var specialHTMLTargets=[0,globalThis.document??0,globalThis.window??0];var findEventTarget=target=>{target=maybeCStringToJsString(target);var domElement=specialHTMLTargets[target]||globalThis.document?.querySelector(target);return domElement};var findCanvasEventTarget=findEventTarget;var _emscripten_webgl_do_create_context=(target,attributes)=>{var attr32=attributes>>2;var powerPreference=HEAP32[attr32+(8>>2)];var contextAttributes={alpha:!!HEAP8[attributes+0],depth:!!HEAP8[attributes+1],stencil:!!HEAP8[attributes+2],antialias:!!HEAP8[attributes+3],premultipliedAlpha:!!HEAP8[attributes+4],preserveDrawingBuffer:!!HEAP8[attributes+5],powerPreference:webglPowerPreferences[powerPreference],failIfMajorPerformanceCaveat:!!HEAP8[attributes+12],majorVersion:HEAP32[attr32+(16>>2)],minorVersion:HEAP32[attr32+(20>>2)],enableExtensionsByDefault:HEAP8[attributes+24],explicitSwapControl:HEAP8[attributes+25],proxyContextToMainThread:HEAP32[attr32+(28>>2)],renderViaOffscreenBackBuffer:HEAP8[attributes+32]};var canvas=findCanvasEventTarget(target);if(!canvas){return 0}if(contextAttributes.explicitSwapControl){return 0}var contextHandle=GL.createContext(canvas,contextAttributes);return contextHandle};var _emscripten_webgl_create_context=_emscripten_webgl_do_create_context;var _emscripten_webgl_destroy_context=contextHandle=>{if(GL.currentContext==contextHandle)GL.currentContext=0;GL.deleteContext(contextHandle)};var _emscripten_webgl_make_context_current=contextHandle=>{var success=GL.makeContextCurrent(contextHandle);return success?0:-5};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var _fd_close=fd=>52;var _fd_read=(fd,iov,iovcnt,pnum)=>52;function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0};var _emscripten_glActiveTexture=x0=>GLctx.activeTexture(x0);var _glActiveTexture=_emscripten_glActiveTexture;var _emscripten_glAttachShader=(program,shader)=>{GLctx.attachShader(GL.programs[program],GL.shaders[shader])};var _glAttachShader=_emscripten_glAttachShader;var _emscripten_glBindBuffer=(target,buffer)=>{if(buffer&&!GL.buffers[buffer]){var b=GLctx.createBuffer();b.name=buffer;GL.buffers[buffer]=b}if(target==34962){GLctx.currentArrayBufferBinding=buffer}else if(target==34963){GLctx.currentElementArrayBufferBinding=buffer}if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])};var _glBindBuffer=_emscripten_glBindBuffer;var _emscripten_glBindTexture=(target,texture)=>{GLctx.bindTexture(target,GL.textures[texture])};var _glBindTexture=_emscripten_glBindTexture;var _emscripten_glBindVertexArray=vao=>{GLctx.bindVertexArray(GL.vaos[vao]);var ibo=GLctx.getParameter(34965);GLctx.currentElementArrayBufferBinding=ibo?ibo.name|0:0};var _glBindVertexArray=_emscripten_glBindVertexArray;var _emscripten_glBlendFunc=(x0,x1)=>GLctx.blendFunc(x0,x1);var _glBlendFunc=_emscripten_glBlendFunc;var _emscripten_glBufferData=(target,size,data,usage)=>{if(GL.currentContext.version>=2){if(data&&size){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}return}GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)};var _glBufferData=_emscripten_glBufferData;var _emscripten_glBufferSubData=(target,offset,size,data)=>{if(GL.currentContext.version>=2){size&&GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))};var _glBufferSubData=_emscripten_glBufferSubData;var _emscripten_glClear=x0=>GLctx.clear(x0);var _glClear=_emscripten_glClear;var _emscripten_glClearColor=(x0,x1,x2,x3)=>GLctx.clearColor(x0,x1,x2,x3);var _glClearColor=_emscripten_glClearColor;var _emscripten_glCompileShader=shader=>{GLctx.compileShader(GL.shaders[shader])};var _glCompileShader=_emscripten_glCompileShader;var _emscripten_glCreateProgram=()=>{var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;program.maxUniformLength=program.maxAttributeLength=program.maxUniformBlockNameLength=0;program.uniformIdCounter=1;GL.programs[id]=program;return id};var _glCreateProgram=_emscripten_glCreateProgram;var _emscripten_glCreateShader=shaderType=>{var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id};var _glCreateShader=_emscripten_glCreateShader;var _emscripten_glDeleteBuffers=(n,buffers)=>{for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}};var _glDeleteBuffers=_emscripten_glDeleteBuffers;var _emscripten_glDeleteProgram=id=>{if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null};var _glDeleteProgram=_emscripten_glDeleteProgram;var _emscripten_glDeleteShader=id=>{if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null};var _glDeleteShader=_emscripten_glDeleteShader;var _emscripten_glDeleteTextures=(n,textures)=>{for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}};var _glDeleteTextures=_emscripten_glDeleteTextures;var _emscripten_glDeleteVertexArrays=(n,vaos)=>{for(var i=0;i>2];GLctx.deleteVertexArray(GL.vaos[id]);GL.vaos[id]=null}};var _glDeleteVertexArrays=_emscripten_glDeleteVertexArrays;var _emscripten_glDisable=x0=>GLctx.disable(x0);var _glDisable=_emscripten_glDisable;var _emscripten_glDrawElements=(mode,count,type,indices)=>{var buf;var vertexes=0;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));if(count>0){for(var i=0;iMath.max(max,current))+1;break}}}indices=0}GL.preDrawHandleClientVertexAttribBindings(vertexes);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}};var _glDrawElements=_emscripten_glDrawElements;var _emscripten_glEnable=x0=>GLctx.enable(x0);var _glEnable=_emscripten_glEnable;var _emscripten_glEnableVertexAttribArray=index=>{var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)};var _glEnableVertexAttribArray=_emscripten_glEnableVertexAttribArray;var _emscripten_glGenBuffers=(n,buffers)=>{GL.genObject(n,buffers,"createBuffer",GL.buffers)};var _glGenBuffers=_emscripten_glGenBuffers;var _emscripten_glGenTextures=(n,textures)=>{GL.genObject(n,textures,"createTexture",GL.textures)};var _glGenTextures=_emscripten_glGenTextures;var _emscripten_glGenVertexArrays=(n,arrays)=>{GL.genObject(n,arrays,"createVertexArray",GL.vaos)};var _glGenVertexArrays=_emscripten_glGenVertexArrays;var _emscripten_glGenerateMipmap=x0=>GLctx.generateMipmap(x0);var _glGenerateMipmap=_emscripten_glGenerateMipmap;var _emscripten_glGetProgramInfoLog=(program,maxLength,length,infoLog)=>{var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _glGetProgramInfoLog=_emscripten_glGetProgramInfoLog;var _emscripten_glGetProgramiv=(program,pname,p)=>{if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}program=GL.programs[program];if(pname==35716){var log=GLctx.getProgramInfoLog(program);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){if(!program.maxUniformLength){var numActiveUniforms=GLctx.getProgramParameter(program,35718);for(var i=0;i>2]=program.maxUniformLength}else if(pname==35722){if(!program.maxAttributeLength){var numActiveAttributes=GLctx.getProgramParameter(program,35721);for(var i=0;i>2]=program.maxAttributeLength}else if(pname==35381){if(!program.maxUniformBlockNameLength){var numActiveUniformBlocks=GLctx.getProgramParameter(program,35382);for(var i=0;i>2]=program.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(program,pname)}};var _glGetProgramiv=_emscripten_glGetProgramiv;var _emscripten_glGetShaderInfoLog=(shader,maxLength,length,infoLog)=>{var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _glGetShaderInfoLog=_emscripten_glGetShaderInfoLog;var _emscripten_glGetShaderiv=(shader,pname,p)=>{if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}};var _glGetShaderiv=_emscripten_glGetShaderiv;var jstoi_q=str=>parseInt(str);var webglGetLeftBracePos=name=>name.slice(-1)=="]"&&name.lastIndexOf("[");var webglPrepareUniformLocationsBeforeFirstUse=program=>{var uniformLocsById=program.uniformLocsById,uniformSizeAndIdsByName=program.uniformSizeAndIdsByName,i,j;if(!uniformLocsById){program.uniformLocsById=uniformLocsById={};program.uniformArrayNamesById={};var numActiveUniforms=GLctx.getProgramParameter(program,35718);for(i=0;i0?nm.slice(0,lb):nm;var id=program.uniformIdCounter;program.uniformIdCounter+=sz;uniformSizeAndIdsByName[arrayName]=[sz,id];for(j=0;j{name=UTF8ToString(name);if(program=GL.programs[program]){webglPrepareUniformLocationsBeforeFirstUse(program);var uniformLocsById=program.uniformLocsById;var arrayIndex=0;var uniformBaseName=name;var leftBrace=webglGetLeftBracePos(name);if(leftBrace>0){arrayIndex=jstoi_q(name.slice(leftBrace+1))>>>0;uniformBaseName=name.slice(0,leftBrace)}var sizeAndId=program.uniformSizeAndIdsByName[uniformBaseName];if(sizeAndId&&arrayIndex{program=GL.programs[program];GLctx.linkProgram(program);program.uniformLocsById=0;program.uniformSizeAndIdsByName={}};var _glLinkProgram=_emscripten_glLinkProgram;var _emscripten_glShaderSource=(shader,count,string,length)=>{var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)};var _glShaderSource=_emscripten_glShaderSource;var computeUnpackAlignedImageSize=(width,height,sizePerPixel)=>{function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=(GL.unpackRowLength||width)*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,GL.unpackAlignment);return height*alignedRowSize};var colorChannelsInGlTextureFormat=format=>{var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1};var heapObjectForWebGLType=type=>{type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16};var toTypedArrayIndex=(pointer,heap)=>pointer>>>31-Math.clz32(heap.BYTES_PER_ELEMENT);var emscriptenWebGLGetTexPixelData=(type,format,width,height,pixels,internalFormat)=>{var heap=heapObjectForWebGLType(type);var sizePerPixel=colorChannelsInGlTextureFormat(format)*heap.BYTES_PER_ELEMENT;var bytes=computeUnpackAlignedImageSize(width,height,sizePerPixel);return heap.subarray(toTypedArrayIndex(pixels,heap),toTypedArrayIndex(pixels+bytes,heap))};var _emscripten_glTexImage2D=(target,level,internalFormat,width,height,border,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);var index=toTypedArrayIndex(pixels,heap);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,index);return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null;GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixelData)};var _glTexImage2D=_emscripten_glTexImage2D;var _emscripten_glTexParameteri=(x0,x1,x2)=>GLctx.texParameteri(x0,x1,x2);var _glTexParameteri=_emscripten_glTexParameteri;var _emscripten_glTexSubImage2D=(target,level,xoffset,yoffset,width,height,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,toTypedArrayIndex(pixels,heap));return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0):null;GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)};var _glTexSubImage2D=_emscripten_glTexSubImage2D;var webglGetUniformLocation=location=>{var p=GLctx.currentProgram;if(p){var webglLoc=p.uniformLocsById[location];if(typeof webglLoc=="number"){p.uniformLocsById[location]=webglLoc=GLctx.getUniformLocation(p,p.uniformArrayNamesById[location]+(webglLoc>0?`[${webglLoc}]`:""))}return webglLoc}else{GL.recordError(1282)}};var _emscripten_glUniform1i=(location,v0)=>{GLctx.uniform1i(webglGetUniformLocation(location),v0)};var _glUniform1i=_emscripten_glUniform1i;var miniTempWebGLIntBuffers=[];var _emscripten_glUniform1iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform1iv(webglGetUniformLocation(location),HEAP32,value>>2,count);return}if(count<=288){var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2]}}else{var view=HEAP32.subarray(value>>2,value+count*4>>2)}GLctx.uniform1iv(webglGetUniformLocation(location),view)};var _glUniform1iv=_emscripten_glUniform1iv;var _emscripten_glUniform4f=(location,v0,v1,v2,v3)=>{GLctx.uniform4f(webglGetUniformLocation(location),v0,v1,v2,v3)};var _glUniform4f=_emscripten_glUniform4f;var miniTempWebGLFloatBuffers=[];var _emscripten_glUniformMatrix4fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*16);return}if(count<=18){var view=miniTempWebGLFloatBuffers[16*count];var heap=HEAPF32;value=value>>2;count*=16;for(var i=0;i>2,value+count*64>>2)}GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,view)};var _glUniformMatrix4fv=_emscripten_glUniformMatrix4fv;var _emscripten_glUseProgram=program=>{program=GL.programs[program];GLctx.useProgram(program);GLctx.currentProgram=program};var _glUseProgram=_emscripten_glUseProgram;var _emscripten_glVertexAttribPointer=(index,size,type,normalized,stride,ptr)=>{var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)};var _glVertexAttribPointer=_emscripten_glVertexAttribPointer;var _emscripten_glViewport=(x0,x1,x2,x3)=>GLctx.viewport(x0,x1,x2,x3);var _glViewport=_emscripten_glViewport;var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};init_ClassHandle();init_RegisteredPointer();registerPreMainLoop(()=>GL.newRenderingFrameStarted());var miniTempWebGLIntBuffersStorage=new Int32Array(288);for(var i=0;i<=288;++i){miniTempWebGLIntBuffers[i]=miniTempWebGLIntBuffersStorage.subarray(0,i)}var miniTempWebGLFloatBuffersStorage=new Float32Array(288);for(var i=0;i<=288;++i){miniTempWebGLFloatBuffers[i]=miniTempWebGLFloatBuffersStorage.subarray(0,i)}{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["GL"]=GL;var ASM_CONSTS={29508:()=>{var canvas=Module.canvas;if(!canvas){console.error("[ESEngine] Module.canvas not set");return 0}var ctx=canvas.getContext("webgl2",{alpha:false,depth:true,stencil:false,antialias:true,premultipliedAlpha:true,preserveDrawingBuffer:false});if(!ctx){ctx=canvas.getContext("webgl",{alpha:false,depth:true,stencil:false,antialias:true,premultipliedAlpha:true,preserveDrawingBuffer:false})}if(!ctx){console.error("[ESEngine] Failed to create WebGL context");return 0}var handle=GL.registerContext(ctx,{majorVersion:ctx.getParameter(7938).indexOf("WebGL 2")===0?2:1,minorVersion:0,enableExtensionsByDefault:true});return handle},30209:()=>Module.canvas?window.devicePixelRatio||1:1};var ___getTypeName,_es_init,_es_update,_es_render,_es_shutdown,_es_on_touch,_es_on_key,_es_renderer_clear,_es_renderer_begin_scene,_es_renderer_end_scene,_es_renderer_draw_quad,_es_renderer_draw_quad_textured,_malloc,_free,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){___getTypeName=wasmExports["Aa"];_es_init=Module["_es_init"]=wasmExports["Ca"];_es_update=Module["_es_update"]=wasmExports["Da"];_es_render=Module["_es_render"]=wasmExports["Ea"];_es_shutdown=Module["_es_shutdown"]=wasmExports["Fa"];_es_on_touch=Module["_es_on_touch"]=wasmExports["Ga"];_es_on_key=Module["_es_on_key"]=wasmExports["Ha"];_es_renderer_clear=Module["_es_renderer_clear"]=wasmExports["Ia"];_es_renderer_begin_scene=Module["_es_renderer_begin_scene"]=wasmExports["Ja"];_es_renderer_end_scene=Module["_es_renderer_end_scene"]=wasmExports["Ka"];_es_renderer_draw_quad=Module["_es_renderer_draw_quad"]=wasmExports["La"];_es_renderer_draw_quad_textured=Module["_es_renderer_draw_quad_textured"]=wasmExports["Ma"];_malloc=Module["_malloc"]=wasmExports["Na"];_free=Module["_free"]=wasmExports["Oa"];__emscripten_stack_restore=wasmExports["Pa"];__emscripten_stack_alloc=wasmExports["Qa"];_emscripten_stack_get_current=wasmExports["Ra"];memory=wasmMemory=wasmExports["ya"];__indirect_function_table=wasmTable=wasmExports["Ba"]}var wasmImports={o:___cxa_throw,Z:__abort_js,d:__embind_finalize_value_object,S:__embind_register_bigint,va:__embind_register_bool,A:__embind_register_class,aa:__embind_register_class_constructor,b:__embind_register_class_function,ta:__embind_register_emval,C:__embind_register_enum,h:__embind_register_enum_value,R:__embind_register_float,g:__embind_register_function,f:__embind_register_integer,c:__embind_register_memory_view,ua:__embind_register_std_string,y:__embind_register_std_wstring,e:__embind_register_value_object,a:__embind_register_value_object_field,wa:__embind_register_void,T:__tzset_js,_:_clock_time_get,ca:_emscripten_asm_const_double,da:_emscripten_asm_const_int,ba:_emscripten_get_now,$:_emscripten_resize_heap,xa:_emscripten_webgl_create_context,F:_emscripten_webgl_destroy_context,z:_emscripten_webgl_make_context_current,U:_environ_get,V:_environ_sizes_get,W:_fd_close,X:_fd_read,Y:_fd_seek,B:_fd_write,M:_glActiveTexture,G:_glAttachShader,j:_glBindBuffer,m:_glBindTexture,s:_glBindVertexArray,x:_glBlendFunc,t:_glBufferData,ka:_glBufferSubData,k:_glClear,n:_glClearColor,I:_glCompileShader,ra:_glCreateProgram,K:_glCreateShader,v:_glDeleteBuffers,L:_glDeleteProgram,i:_glDeleteShader,Q:_glDeleteTextures,ia:_glDeleteVertexArrays,N:_glDisable,w:_glDrawElements,r:_glEnable,ha:_glEnableVertexAttribArray,u:_glGenBuffers,P:_glGenTextures,ja:_glGenVertexArrays,fa:_glGenerateMipmap,pa:_glGetProgramInfoLog,E:_glGetProgramiv,H:_glGetShaderInfoLog,p:_glGetShaderiv,q:_glGetUniformLocation,qa:_glLinkProgram,J:_glShaderSource,O:_glTexImage2D,l:_glTexParameteri,ea:_glTexSubImage2D,na:_glUniform1i,sa:_glUniform1iv,ma:_glUniform4f,la:_glUniformMatrix4fv,oa:_glUseProgram,ga:_glVertexAttribPointer,D:_glViewport};function run(){preRun();function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +;return moduleRtn}})();if(typeof exports==="object"&&typeof module==="object"){module.exports=ESEngineModule;module.exports.default=ESEngineModule}else if(typeof define==="function"&&define["amd"])define([],()=>ESEngineModule); diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index efd645f5d..bb0ce2965 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -4,8 +4,18 @@ mod preview_server; use preview_server::PreviewServer; use std::path::PathBuf; +use std::process::Stdio; use std::sync::Mutex; -use tauri::State; +use tauri::{AppHandle, Emitter, State}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Command; + +const ENGINE_JS: &[u8] = include_bytes!("../../public/wasm/esengine.js"); +const ENGINE_WASM: &[u8] = include_bytes!("../../public/wasm/esengine.wasm"); +const ENGINE_SINGLE_JS: &[u8] = include_bytes!("../../public/wasm/esengine.single.js"); +const ENGINE_WXGAME_JS: &[u8] = include_bytes!("../../public/wasm/esengine.wxgame.js"); +const ENGINE_WXGAME_WASM: &[u8] = include_bytes!("../../public/wasm/esengine.wxgame.wasm"); +const SDK_WECHAT_JS: &[u8] = include_bytes!("../../public/sdk/cjs/esengine.wechat.js"); // ============================================================================= // State @@ -59,6 +69,98 @@ fn open_folder(path: String) -> Result<(), String> { open::that(&path).map_err(|e| e.to_string()) } +#[tauri::command] +fn get_engine_js() -> Vec { + ENGINE_JS.to_vec() +} + +#[tauri::command] +fn get_engine_wasm() -> Vec { + ENGINE_WASM.to_vec() +} + +#[tauri::command] +fn get_engine_single_js() -> Vec { + ENGINE_SINGLE_JS.to_vec() +} + +#[tauri::command] +fn get_engine_wxgame_js() -> Vec { + ENGINE_WXGAME_JS.to_vec() +} + +#[tauri::command] +fn get_engine_wxgame_wasm() -> Vec { + ENGINE_WXGAME_WASM.to_vec() +} + +#[tauri::command] +fn get_sdk_wechat_js() -> Vec { + SDK_WECHAT_JS.to_vec() +} + +#[derive(Clone, serde::Serialize)] +struct CommandOutput { + stream: String, + data: String, +} + +#[derive(serde::Serialize)] +struct CommandResult { + code: i32, +} + +#[tauri::command] +async fn execute_command( + app: AppHandle, + cmd: String, + args: Vec, + cwd: String, +) -> Result { + let mut child = Command::new(&cmd) + .args(&args) + .current_dir(&cwd) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| e.to_string())?; + + let stdout = child.stdout.take().ok_or("Failed to capture stdout")?; + let stderr = child.stderr.take().ok_or("Failed to capture stderr")?; + + let app_stdout = app.clone(); + let stdout_handle = tokio::spawn(async move { + let reader = BufReader::new(stdout); + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + let _ = app_stdout.emit("command-output", CommandOutput { + stream: "stdout".to_string(), + data: line, + }); + } + }); + + let app_stderr = app.clone(); + let stderr_handle = tokio::spawn(async move { + let reader = BufReader::new(stderr); + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + let _ = app_stderr.emit("command-output", CommandOutput { + stream: "stderr".to_string(), + data: line, + }); + } + }); + + let _ = tokio::join!(stdout_handle, stderr_handle); + + let status = child.wait().await.map_err(|e| e.to_string())?; + + Ok(CommandResult { + code: status.code().unwrap_or(-1), + }) +} + // ============================================================================= // Entry Point // ============================================================================= @@ -77,6 +179,13 @@ pub fn run() { stop_preview_server, open_preview_in_browser, open_folder, + execute_command, + get_engine_js, + get_engine_wasm, + get_engine_single_js, + get_engine_wxgame_js, + get_engine_wxgame_wasm, + get_sdk_wechat_js, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/desktop/src/native-fs.ts b/desktop/src/native-fs.ts index 56866c1bf..10c962771 100644 --- a/desktop/src/native-fs.ts +++ b/desktop/src/native-fs.ts @@ -16,6 +16,7 @@ import { copyFile, } from '@tauri-apps/plugin-fs'; import { invoke } from '@tauri-apps/api/core'; +import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { resourceDir } from '@tauri-apps/api/path'; export interface DirectoryEntry { @@ -59,6 +60,12 @@ export interface NativeFS { ): Promise; openFolder(path: string): Promise; getResourcePath(): Promise; + getEngineJs(): Promise; + getEngineWasm(): Promise; + getEngineSingleJs(): Promise; + getEngineWxgameJs(): Promise; + getEngineWxgameWasm(): Promise; + getSdkWechatJs(): Promise; } export function injectNativeFS(): void { @@ -251,11 +258,73 @@ export function injectNativeFS(): void { try { return await resourceDir(); } catch { - // Fallback for development mode return ''; } }, + + async getEngineJs() { + const data = await invoke('get_engine_js'); + return new TextDecoder().decode(new Uint8Array(data)); + }, + + async getEngineWasm() { + const data = await invoke('get_engine_wasm'); + return new Uint8Array(data); + }, + + async getEngineSingleJs() { + const data = await invoke('get_engine_single_js'); + return new TextDecoder().decode(new Uint8Array(data)); + }, + + async getEngineWxgameJs() { + const data = await invoke('get_engine_wxgame_js'); + return new TextDecoder().decode(new Uint8Array(data)); + }, + + async getEngineWxgameWasm() { + const data = await invoke('get_engine_wxgame_wasm'); + return new Uint8Array(data); + }, + + async getSdkWechatJs() { + const data = await invoke('get_sdk_wechat_js'); + return new TextDecoder().decode(new Uint8Array(data)); + }, }; (window as any).__esengine_fs = nativeFS; + + const nativeShell = { + async execute( + cmd: string, + args: string[], + cwd: string, + onOutput?: (stream: 'stdout' | 'stderr', data: string) => void + ): Promise<{ code: number }> { + let unlisten: UnlistenFn | null = null; + + try { + if (onOutput) { + unlisten = await listen<{ stream: string; data: string }>( + 'command-output', + (event) => { + onOutput(event.payload.stream as 'stdout' | 'stderr', event.payload.data); + } + ); + } + + const result = await invoke<{ code: number }>('execute_command', { cmd, args, cwd }); + return result; + } catch (err) { + return { code: 1 }; + } finally { + if (unlisten) { + unlisten(); + } + } + }, + }; + + (window as any).__esengine_shell = nativeShell; } diff --git a/editor/src/builder/BuildSettingsDialog.ts b/editor/src/builder/BuildSettingsDialog.ts index 8585b4b30..c6edf7a9e 100644 --- a/editor/src/builder/BuildSettingsDialog.ts +++ b/editor/src/builder/BuildSettingsDialog.ts @@ -296,6 +296,13 @@ export class BuildSettingsDialog { +
+ + +