refactor: 拆分WasmCore大文件为模块化结构 - 将23KB的WasmCore.ts拆分为types/loader/fallback/core/instance五个模块

This commit is contained in:
YHH
2025-06-09 13:23:46 +08:00
parent 2e7f764d6c
commit 6e2e7a4af5
8 changed files with 575 additions and 707 deletions

View File

@@ -0,0 +1,291 @@
/**
* WASM ECS核心模块
*/
import { EntityId, ComponentMask, QueryResult, PerformanceStats, WasmEcsCoreInstance, WasmModule } from './types';
import { WasmLoader } from './loader';
import { JavaScriptFallback } from './fallback';
export class WasmEcsCore {
private wasmLoader: WasmLoader;
private jsFallback: JavaScriptFallback;
private initialized = false;
private usingWasm = false;
constructor() {
this.wasmLoader = new WasmLoader();
this.jsFallback = new JavaScriptFallback();
}
public setSilent(silent: boolean): void {
this.wasmLoader.setSilent(silent);
}
async initialize(): Promise<boolean> {
if (this.initialized) return true;
console.log('🔄 初始化ECS核心...');
this.usingWasm = await this.wasmLoader.loadWasmModule();
this.initialized = true;
return true;
}
createEntity(): EntityId {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
return wasmCore ? wasmCore.create_entity() : this.jsFallback.createEntity();
}
return this.jsFallback.createEntity();
}
destroyEntity(entityId: EntityId): boolean {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
return wasmCore ? wasmCore.destroy_entity(entityId) : this.jsFallback.destroyEntity(entityId);
}
return this.jsFallback.destroyEntity(entityId);
}
updateEntityMask(entityId: EntityId, mask: ComponentMask): void {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
wasmCore.update_entity_mask(entityId, mask);
} else {
this.jsFallback.updateEntityMask(entityId, mask);
}
} else {
this.jsFallback.updateEntityMask(entityId, mask);
}
}
batchUpdateMasks(entityIds: EntityId[], masks: ComponentMask[]): void {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
const entityIdArray = new Uint32Array(entityIds);
const maskArray = new BigUint64Array(masks);
wasmCore.batch_update_masks(entityIdArray, maskArray);
} else {
this.jsFallback.batchUpdateMasks(entityIds, masks);
}
} else {
this.jsFallback.batchUpdateMasks(entityIds, masks);
}
}
queryEntities(mask: ComponentMask, maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
const resultPtr = wasmCore.query_entities(mask, maxResults);
const count = wasmCore.get_query_result_count();
const wasmModule = this.wasmLoader.getWasmModule();
if (wasmModule && wasmModule.memory) {
const memory = new Uint32Array(wasmModule.memory.buffer);
const entities = new Uint32Array(count);
for (let i = 0; i < count; i++) {
entities[i] = memory[resultPtr / 4 + i];
}
return { entities, count };
}
}
}
return this.jsFallback.queryEntities(mask, maxResults);
}
queryCached(mask: ComponentMask): QueryResult {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
const resultPtr = wasmCore.query_cached(mask);
const count = wasmCore.get_cached_query_count(mask);
const wasmModule = this.wasmLoader.getWasmModule();
if (wasmModule && wasmModule.memory) {
const memory = new Uint32Array(wasmModule.memory.buffer);
const entities = new Uint32Array(count);
for (let i = 0; i < count; i++) {
entities[i] = memory[resultPtr / 4 + i];
}
return { entities, count };
}
}
}
return this.jsFallback.queryCached(mask);
}
queryMultipleComponents(masks: ComponentMask[], maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
const maskArray = new BigUint64Array(masks);
const resultPtr = wasmCore.query_multiple_components(maskArray, maxResults);
const count = wasmCore.get_query_result_count();
const wasmModule = this.wasmLoader.getWasmModule();
if (wasmModule && wasmModule.memory) {
const memory = new Uint32Array(wasmModule.memory.buffer);
const entities = new Uint32Array(count);
for (let i = 0; i < count; i++) {
entities[i] = memory[resultPtr / 4 + i];
}
return { entities, count };
}
}
}
return this.jsFallback.queryMultipleComponents(masks, maxResults);
}
queryWithExclusion(includeMask: ComponentMask, excludeMask: ComponentMask, maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
const resultPtr = wasmCore.query_with_exclusion(includeMask, excludeMask, maxResults);
const count = wasmCore.get_query_result_count();
const wasmModule = this.wasmLoader.getWasmModule();
if (wasmModule && wasmModule.memory) {
const memory = new Uint32Array(wasmModule.memory.buffer);
const entities = new Uint32Array(count);
for (let i = 0; i < count; i++) {
entities[i] = memory[resultPtr / 4 + i];
}
return { entities, count };
}
}
}
return this.jsFallback.queryWithExclusion(includeMask, excludeMask, maxResults);
}
getEntityMask(entityId: EntityId): ComponentMask | null {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
return wasmCore.get_entity_mask(entityId);
}
}
return this.jsFallback.getEntityMask(entityId);
}
entityExists(entityId: EntityId): boolean {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
return wasmCore.entity_exists(entityId);
}
}
return this.jsFallback.entityExists(entityId);
}
createComponentMask(componentIds: number[]): ComponentMask {
this.ensureInitialized();
if (this.usingWasm) {
const wasmModule = this.wasmLoader.getWasmModule();
if (wasmModule) {
const componentIdArray = new Uint32Array(componentIds);
return wasmModule.create_component_mask(componentIdArray);
}
}
return this.jsFallback.createComponentMask(componentIds);
}
maskContainsComponent(mask: ComponentMask, componentId: number): boolean {
this.ensureInitialized();
if (this.usingWasm) {
const wasmModule = this.wasmLoader.getWasmModule();
if (wasmModule) {
return wasmModule.mask_contains_component(mask, componentId);
}
}
return this.jsFallback.maskContainsComponent(mask, componentId);
}
getPerformanceStats(): PerformanceStats {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
const stats = wasmCore.get_performance_stats();
return {
entityCount: stats[0] || 0,
indexCount: stats[1] || 0,
queryCount: stats[2] || 0,
updateCount: stats[3] || 0,
wasmEnabled: true
};
}
}
return this.jsFallback.getPerformanceStats();
}
clear(): void {
this.ensureInitialized();
if (this.usingWasm) {
const wasmCore = this.wasmLoader.getWasmCore();
if (wasmCore) {
wasmCore.clear();
}
}
this.jsFallback.clear();
}
isUsingWasm(): boolean {
return this.usingWasm;
}
isInitialized(): boolean {
return this.initialized;
}
private ensureInitialized(): void {
if (!this.initialized) {
throw new Error('ECS核心未初始化请先调用 initialize() 方法');
}
}
cleanup(): void {
this.wasmLoader.cleanup();
this.jsFallback.clear();
this.initialized = false;
this.usingWasm = false;
}
}

View File

@@ -0,0 +1,134 @@
/**
* JavaScript回退实现
*/
import { EntityId, ComponentMask, QueryResult, PerformanceStats } from './types';
export class JavaScriptFallback {
private jsEntityMasks = new Map<EntityId, ComponentMask>();
private jsNextEntityId = 1;
private jsQueryCount = 0;
private jsUpdateCount = 0;
createEntity(): EntityId {
const entityId = this.jsNextEntityId++;
this.jsEntityMasks.set(entityId, 0n);
return entityId;
}
destroyEntity(entityId: EntityId): boolean {
return this.jsEntityMasks.delete(entityId);
}
updateEntityMask(entityId: EntityId, mask: ComponentMask): void {
this.jsEntityMasks.set(entityId, mask);
this.jsUpdateCount++;
}
batchUpdateMasks(entityIds: EntityId[], masks: ComponentMask[]): void {
for (let i = 0; i < entityIds.length && i < masks.length; i++) {
this.jsEntityMasks.set(entityIds[i], masks[i]);
}
this.jsUpdateCount += Math.min(entityIds.length, masks.length);
}
queryEntities(mask: ComponentMask, maxResults: number = 10000): QueryResult {
const results: number[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
if ((entityMask & mask) === mask) {
results.push(entityId);
if (results.length >= maxResults) break;
}
}
this.jsQueryCount++;
return {
entities: new Uint32Array(results),
count: results.length
};
}
queryCached(mask: ComponentMask): QueryResult {
return this.queryEntities(mask);
}
queryMultipleComponents(masks: ComponentMask[], maxResults: number = 10000): QueryResult {
const results: number[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
let matches = false;
for (const mask of masks) {
if ((entityMask & mask) === mask) {
matches = true;
break;
}
}
if (matches) {
results.push(entityId);
if (results.length >= maxResults) break;
}
}
this.jsQueryCount++;
return {
entities: new Uint32Array(results),
count: results.length
};
}
queryWithExclusion(includeMask: ComponentMask, excludeMask: ComponentMask, maxResults: number = 10000): QueryResult {
const results: number[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
if ((entityMask & includeMask) === includeMask && (entityMask & excludeMask) === 0n) {
results.push(entityId);
if (results.length >= maxResults) break;
}
}
this.jsQueryCount++;
return {
entities: new Uint32Array(results),
count: results.length
};
}
getEntityMask(entityId: EntityId): ComponentMask | null {
return this.jsEntityMasks.get(entityId) || null;
}
entityExists(entityId: EntityId): boolean {
return this.jsEntityMasks.has(entityId);
}
createComponentMask(componentIds: number[]): ComponentMask {
let mask = 0n;
for (const id of componentIds) {
mask |= (1n << BigInt(id));
}
return mask;
}
maskContainsComponent(mask: ComponentMask, componentId: number): boolean {
return (mask & (1n << BigInt(componentId))) !== 0n;
}
getPerformanceStats(): PerformanceStats {
return {
entityCount: this.jsEntityMasks.size,
indexCount: 0,
queryCount: this.jsQueryCount,
updateCount: this.jsUpdateCount,
wasmEnabled: false
};
}
clear(): void {
this.jsEntityMasks.clear();
this.jsNextEntityId = 1;
this.jsQueryCount = 0;
this.jsUpdateCount = 0;
}
getEntityCount(): number {
return this.jsEntityMasks.size;
}
}

View File

@@ -0,0 +1,9 @@
/**
* WASM模块导出
*/
export * from './types';
export { WasmLoader } from './loader';
export { JavaScriptFallback } from './fallback';
export { WasmEcsCore } from './core';
export { ecsCore, initializeEcs } from './instance';

View File

@@ -0,0 +1,12 @@
/**
* WASM ECS核心全局实例
*/
import { WasmEcsCore } from './core';
export const ecsCore = new WasmEcsCore();
export async function initializeEcs(silent: boolean = false): Promise<boolean> {
ecsCore.setSilent(silent);
return await ecsCore.initialize();
}

View File

@@ -0,0 +1,73 @@
/**
* WASM模块加载器
*/
import { WasmModule, WasmEcsCoreInstance } from './types';
export class WasmLoader {
private wasmModule: WasmModule | null = null;
private wasmCore: WasmEcsCoreInstance | null = null;
private silent = false;
public setSilent(silent: boolean): void {
this.silent = silent;
}
public async loadWasmModule(): Promise<boolean> {
try {
const wasmPath = '../../bin/wasm/ecs_wasm_core';
this.wasmModule = await import(wasmPath);
if (this.wasmModule) {
await this.initializeWasmModule();
this.wasmCore = new this.wasmModule.EcsCore();
}
return true;
} catch (error) {
if (!this.silent) {
console.warn('WASM加载失败使用JavaScript实现');
}
return false;
}
}
private async initializeWasmModule(): Promise<void> {
if (!this.wasmModule) return;
if (typeof require !== 'undefined') {
const fs = require('fs');
const path = require('path');
const currentDir = path.dirname(__filename);
const wasmPath = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
if (fs.existsSync(wasmPath)) {
const wasmBytes = fs.readFileSync(wasmPath);
if (this.wasmModule.initSync) {
this.wasmModule.initSync(wasmBytes);
} else {
await this.wasmModule.default({ module_or_path: wasmBytes });
}
} else {
throw new Error(`WASM文件不存在: ${wasmPath}`);
}
} else {
await this.wasmModule.default();
}
}
public getWasmCore(): WasmEcsCoreInstance | null {
return this.wasmCore;
}
public getWasmModule(): WasmModule | null {
return this.wasmModule;
}
public cleanup(): void {
if (this.wasmCore && this.wasmCore.free) {
this.wasmCore.free();
}
this.wasmCore = null;
this.wasmModule = null;
}
}

View File

@@ -0,0 +1,48 @@
/**
* WASM ECS核心类型定义
*/
export type EntityId = number;
export type ComponentMask = bigint;
export interface QueryResult {
entities: Uint32Array;
count: number;
}
export interface PerformanceStats {
entityCount: number;
indexCount: number;
queryCount: number;
updateCount: number;
wasmEnabled: boolean;
}
export interface WasmEcsCoreInstance {
create_entity(): number;
destroy_entity(entity_id: number): boolean;
update_entity_mask(entity_id: number, mask: bigint): void;
batch_update_masks(entity_ids: Uint32Array, masks: BigUint64Array): void;
query_entities(mask: bigint, max_results: number): number;
get_query_result_count(): number;
query_cached(mask: bigint): number;
get_cached_query_count(mask: bigint): number;
query_multiple_components(masks: BigUint64Array, max_results: number): number;
query_with_exclusion(include_mask: bigint, exclude_mask: bigint, max_results: number): number;
get_entity_mask(entity_id: number): bigint;
entity_exists(entity_id: number): boolean;
get_entity_count(): number;
get_performance_stats(): Array<any>;
clear(): void;
rebuild_query_cache(): void;
free?(): void;
}
export interface WasmModule {
EcsCore: new () => WasmEcsCoreInstance;
create_component_mask: (componentIds: Uint32Array) => ComponentMask;
mask_contains_component: (mask: ComponentMask, componentId: number) => boolean;
default: (input?: any) => Promise<any>;
initSync?: (input: any) => any;
memory?: WebAssembly.Memory;
}

View File

@@ -1,702 +1,8 @@
/**
* 统一的WASM ECS核心模块
*
* 为小游戏优化的高性能ECS引擎提供简洁的API和自动回退机制
* 适用于NPM包发布和多种部署环境
* WASM ECS核心模块
*
* 提供高性能的ECS操作支持WASM和JavaScript双重实现
*/
/** 实体ID类型 */
export type EntityId = number;
/** 组件掩码类型 */
export type ComponentMask = bigint;
/** 查询结果接口 */
export interface QueryResult {
/** 查询到的实体ID数组 */
entities: Uint32Array;
/** 实体数量 */
count: number;
}
/** 性能统计接口 */
export interface PerformanceStats {
/** 实体总数 */
entityCount: number;
/** 索引数量 */
indexCount: number;
/** 查询次数 */
queryCount: number;
/** 更新次数 */
updateCount: number;
/** 是否使用WASM */
wasmEnabled: boolean;
}
/** WASM模块类型定义 */
interface WasmEcsCoreInstance {
create_entity(): number;
destroy_entity(entity_id: number): boolean;
update_entity_mask(entity_id: number, mask: bigint): void;
batch_update_masks(entity_ids: Uint32Array, masks: BigUint64Array): void;
query_entities(mask: bigint, max_results: number): number;
get_query_result_count(): number;
query_cached(mask: bigint): number;
get_cached_query_count(mask: bigint): number;
query_multiple_components(masks: BigUint64Array, max_results: number): number;
query_with_exclusion(include_mask: bigint, exclude_mask: bigint, max_results: number): number;
get_entity_mask(entity_id: number): bigint;
entity_exists(entity_id: number): boolean;
get_entity_count(): number;
get_performance_stats(): Array<any>;
clear(): void;
rebuild_query_cache(): void;
free?(): void;
}
interface WasmModule {
EcsCore: new () => WasmEcsCoreInstance;
create_component_mask: (componentIds: Uint32Array) => ComponentMask;
mask_contains_component: (mask: ComponentMask, componentId: number) => boolean;
default: (input?: any) => Promise<any>;
initSync?: (input: any) => any;
memory?: WebAssembly.Memory;
}
/**
* 统一的WASM ECS核心类
*
* 提供高性能的ECS操作自动选择WASM或JavaScript实现
* 针对小游戏场景优化,易于使用且性能卓越
* 支持NPM包发布和多种部署环境
*/
export class WasmEcsCore {
/** WASM核心实例 */
private wasmCore: WasmEcsCoreInstance | null = null;
/** WASM模块 */
private wasmModule: WasmModule | null = null;
/** 是否已初始化 */
private initialized = false;
/** 是否使用WASM */
private usingWasm = false;
private silent = false;
// JavaScript回退实现
private jsEntityMasks = new Map<EntityId, ComponentMask>();
private jsNextEntityId = 1;
private jsQueryCount = 0;
private jsUpdateCount = 0;
/**
* 设置静默模式
*/
public setSilent(silent: boolean): void {
this.silent = silent;
}
/**
* 初始化ECS核心
*
* 尝试加载WASM模块失败时自动回退到JavaScript实现
*
* @returns 初始化是否成功
*/
async initialize(): Promise<boolean> {
if (this.initialized) return true;
if (!this.silent) {
console.log('🔄 初始化ECS核心...');
}
try {
// 尝试从bin目录加载WASM模块
const wasmPath = '../../bin/wasm/ecs_wasm_core';
if (!this.silent) {
console.log(`🔍 尝试加载WASM模块: ${wasmPath}`);
console.log(`📁 当前文件位置: ${typeof __filename !== 'undefined' ? __filename : 'unknown'}`);
console.log(`📂 工作目录: ${typeof process !== 'undefined' ? process.cwd() : 'unknown'}`);
// 计算绝对路径
if (typeof __filename !== 'undefined' && typeof require !== 'undefined') {
const path = require('path');
const fs = require('fs');
const currentDir = path.dirname(__filename);
const absoluteWasmPath = path.resolve(currentDir, wasmPath);
console.log(`📍 计算的绝对路径: ${absoluteWasmPath}`);
// 检查文件是否存在
const jsFile = absoluteWasmPath + '.js';
const wasmFile = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
console.log(`📄 检查JS文件: ${jsFile} - ${fs.existsSync(jsFile) ? '存在' : '不存在'}`);
console.log(`📄 检查WASM文件: ${wasmFile} - ${fs.existsSync(wasmFile) ? '存在' : '不存在'}`);
}
}
this.wasmModule = await import(wasmPath);
if (!this.silent) {
console.log('✅ WASM模块导入成功正在初始化...');
}
if (this.wasmModule) {
// 在初始化前,先检查.wasm文件的加载路径
if (!this.silent) {
console.log('🔍 WASM模块将尝试加载 .wasm 文件...');
// 模拟WASM模块内部的路径计算
if (typeof __filename !== 'undefined' && typeof require !== 'undefined') {
const path = require('path');
const { pathToFileURL } = require('url');
const currentDir = path.dirname(__filename);
const wasmJsFile = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core.js');
const wasmBgFile = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
const wasmJsUrl = pathToFileURL(wasmJsFile).href;
const expectedWasmUrl = new URL('ecs_wasm_core_bg.wasm', wasmJsUrl).href;
console.log(`📍 WASM JS文件URL: ${wasmJsUrl}`);
console.log(`📍 预期的.wasm文件URL: ${expectedWasmUrl}`);
console.log(`📍 实际.wasm文件路径: ${wasmBgFile}`);
const fs = require('fs');
console.log(`📄 .wasm文件是否存在: ${fs.existsSync(wasmBgFile) ? '存在' : '不存在'}`);
}
}
// 在Node.js环境中需要手动读取WASM文件
if (typeof require !== 'undefined') {
const fs = require('fs');
const path = require('path');
const currentDir = path.dirname(__filename);
const wasmPath = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
if (!this.silent) {
console.log(`🔧 在Node.js环境中手动加载WASM文件: ${wasmPath}`);
}
if (fs.existsSync(wasmPath)) {
const wasmBytes = fs.readFileSync(wasmPath);
// 使用initSync同步初始化WASM模块
if (this.wasmModule.initSync) {
this.wasmModule.initSync(wasmBytes);
} else {
await this.wasmModule.default({ module_or_path: wasmBytes });
}
} else {
throw new Error(`WASM文件不存在: ${wasmPath}`);
}
} else {
await this.wasmModule.default();
}
this.wasmCore = new this.wasmModule.EcsCore();
}
this.usingWasm = true;
if (!this.silent) {
console.log('✅ WASM模块加载成功');
}
} catch (error) {
if (!this.silent) {
console.warn('⚠️ WASM加载失败使用JavaScript实现');
console.warn(`❌ 错误详情: ${error}`);
}
this.usingWasm = false;
}
this.initialized = true;
if (!this.silent) {
console.log(`🎮 ECS核心初始化完成 (${this.usingWasm ? 'WASM' : 'JavaScript'})`);
}
return true;
}
/**
* 创建新实体
*
* @returns 新实体的ID
*/
createEntity(): EntityId {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.create_entity();
} else {
const entityId = this.jsNextEntityId++;
this.jsEntityMasks.set(entityId, BigInt(0));
return entityId;
}
}
/**
* 删除实体
*
* @param entityId 实体ID
* @returns 是否删除成功
*/
destroyEntity(entityId: EntityId): boolean {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.destroy_entity(entityId);
} else {
return this.jsEntityMasks.delete(entityId);
}
}
/**
* 更新实体的组件掩码
*
* @param entityId 实体ID
* @param mask 组件掩码
*/
updateEntityMask(entityId: EntityId, mask: ComponentMask): void {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
this.wasmCore.update_entity_mask(entityId, mask);
} else {
this.jsEntityMasks.set(entityId, mask);
this.jsUpdateCount++;
}
}
/**
* 批量更新实体掩码(高性能)
*
* @param entityIds 实体ID数组
* @param masks 组件掩码数组
*/
batchUpdateMasks(entityIds: EntityId[], masks: ComponentMask[]): void {
this.ensureInitialized();
if (entityIds.length !== masks.length) {
throw new Error('实体ID和掩码数组长度必须相同');
}
if (this.usingWasm && this.wasmCore) {
const entityIdsArray = new Uint32Array(entityIds);
const masksArray = new BigUint64Array(masks);
this.wasmCore.batch_update_masks(entityIdsArray, masksArray);
} else {
for (let i = 0; i < entityIds.length; i++) {
this.jsEntityMasks.set(entityIds[i], masks[i]);
}
this.jsUpdateCount += entityIds.length;
}
}
/**
* 查询包含指定组件的实体
*
* @param mask 组件掩码
* @param maxResults 最大结果数
* @returns 查询结果
*/
queryEntities(mask: ComponentMask, maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const ptr = this.wasmCore.query_entities(mask, maxResults);
const count = this.wasmCore.get_query_result_count();
if (ptr && count > 0 && this.wasmModule?.memory) {
const entities = new Uint32Array(this.wasmModule.memory.buffer, ptr, count);
return {
entities: new Uint32Array(entities), // 创建副本以确保数据安全
count
};
} else {
return { entities: new Uint32Array(0), count: 0 };
}
} catch (error) {
if (!this.silent) {
console.warn('WASM查询失败回退到JavaScript实现:', error);
}
// 回退到JavaScript实现
}
}
// JavaScript实现
this.jsQueryCount++;
const entities: EntityId[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
if ((entityMask & mask) === mask) {
entities.push(entityId);
if (entities.length >= maxResults) break;
}
}
return {
entities: new Uint32Array(entities),
count: entities.length
};
}
/**
* 查询指定掩码的实体(带缓存优化)
*
* @param mask 组件掩码
* @returns 查询结果
*/
queryCached(mask: ComponentMask): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const ptr = this.wasmCore.query_cached(mask);
const count = this.wasmCore.get_cached_query_count(mask);
if (ptr && count > 0 && this.wasmModule?.memory) {
const entities = new Uint32Array(this.wasmModule.memory.buffer, ptr, count);
return {
entities: new Uint32Array(entities), // 复制数据
count
};
}
return { entities: new Uint32Array(0), count: 0 };
} catch (error) {
if (!this.silent) {
console.warn('WASM缓存查询失败回退到通用查询:', error);
}
// 回退到通用查询
return this.queryEntities(mask);
}
}
// JavaScript实现 - 直接使用通用查询
return this.queryEntities(mask);
}
/**
* 查询包含多个组件的实体
*
* @param masks 组件掩码数组
* @param maxResults 最大结果数
* @returns 查询结果
*/
queryMultipleComponents(masks: ComponentMask[], maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const masksArray = new BigUint64Array(masks);
const ptr = this.wasmCore.query_multiple_components(masksArray, maxResults);
if (ptr && this.wasmModule?.memory) {
// 暂时返回空结果,需要实现内存访问
return { entities: new Uint32Array(0), count: 0 };
}
return { entities: new Uint32Array(0), count: 0 };
} catch (error) {
if (!this.silent) {
console.warn('WASM多组件查询失败回退到JavaScript实现:', error);
}
// 回退到JavaScript实现
}
}
// JavaScript实现
this.jsQueryCount++;
const entities: EntityId[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
let hasAll = true;
for (const mask of masks) {
if ((entityMask & mask) !== mask) {
hasAll = false;
break;
}
}
if (hasAll) {
entities.push(entityId);
if (entities.length >= maxResults) break;
}
}
return {
entities: new Uint32Array(entities),
count: entities.length
};
}
/**
* 排除查询:包含某些组件但不包含其他组件
*
* @param includeMask 必须包含的组件掩码
* @param excludeMask 必须排除的组件掩码
* @param maxResults 最大结果数
* @returns 查询结果
*/
queryWithExclusion(includeMask: ComponentMask, excludeMask: ComponentMask, maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const ptr = this.wasmCore.query_with_exclusion(includeMask, excludeMask, maxResults);
if (ptr && this.wasmModule?.memory) {
// 暂时返回空结果,需要实现内存访问
return { entities: new Uint32Array(0), count: 0 };
}
return { entities: new Uint32Array(0), count: 0 };
} catch (error) {
if (!this.silent) {
console.warn('WASM排除查询失败回退到JavaScript实现:', error);
}
// 回退到JavaScript实现
}
}
// JavaScript实现
this.jsQueryCount++;
const entities: EntityId[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
if ((entityMask & includeMask) === includeMask && (entityMask & excludeMask) === BigInt(0)) {
entities.push(entityId);
if (entities.length >= maxResults) break;
}
}
return {
entities: new Uint32Array(entities),
count: entities.length
};
}
/**
* 获取实体的组件掩码
*
* @param entityId 实体ID
* @returns 组件掩码如果实体不存在则返回null
*/
getEntityMask(entityId: EntityId): ComponentMask | null {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.get_entity_mask(entityId) || null;
} else {
return this.jsEntityMasks.get(entityId) || null;
}
}
/**
* 检查实体是否存在
*
* @param entityId 实体ID
* @returns 是否存在
*/
entityExists(entityId: EntityId): boolean {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.entity_exists(entityId);
} else {
return this.jsEntityMasks.has(entityId);
}
}
/**
* 创建组件掩码
*
* @param componentIds 组件ID数组
* @returns 组件掩码
*/
createComponentMask(componentIds: number[]): ComponentMask {
if (this.usingWasm && this.wasmModule) {
return this.wasmModule.create_component_mask(new Uint32Array(componentIds));
} else {
let mask = BigInt(0);
for (const id of componentIds) {
if (id < 64) {
mask |= BigInt(1) << BigInt(id);
}
}
return mask;
}
}
/**
* 检查掩码是否包含组件
*
* @param mask 组件掩码
* @param componentId 组件ID
* @returns 是否包含
*/
maskContainsComponent(mask: ComponentMask, componentId: number): boolean {
if (this.usingWasm && this.wasmModule) {
return this.wasmModule.mask_contains_component(mask, componentId);
} else {
if (componentId >= 64) return false;
return (mask & (BigInt(1) << BigInt(componentId))) !== BigInt(0);
}
}
/**
* 获取性能统计信息
*
* @returns 性能统计
*/
getPerformanceStats(): PerformanceStats {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
const stats = Array.from(this.wasmCore.get_performance_stats());
return {
entityCount: stats[0] as number,
indexCount: stats[1] as number,
queryCount: stats[2] as number,
updateCount: stats[3] as number,
wasmEnabled: true
};
} else {
return {
entityCount: this.jsEntityMasks.size,
indexCount: 0,
queryCount: this.jsQueryCount,
updateCount: this.jsUpdateCount,
wasmEnabled: false
};
}
}
/**
* 清空所有数据
*/
clear(): void {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
this.wasmCore.clear();
} else {
this.jsEntityMasks.clear();
this.jsNextEntityId = 1;
this.jsQueryCount = 0;
this.jsUpdateCount = 0;
}
}
/**
* 是否使用WASM实现
*
* @returns 是否使用WASM
*/
isUsingWasm(): boolean {
return this.usingWasm;
}
/**
* 是否已初始化
*
* @returns 是否已初始化
*/
isInitialized(): boolean {
return this.initialized;
}
/**
* 确保已初始化
*/
private ensureInitialized(): void {
if (!this.initialized) {
throw new Error('ECS核心未初始化请先调用 initialize()');
}
}
/**
* 清理资源
*/
cleanup(): void {
if (this.usingWasm && this.wasmCore) {
try {
this.wasmCore.free?.();
} catch (error) {
if (!this.silent) {
console.warn('⚠️ 清理WASM资源时出错:', error);
}
}
}
this.wasmCore = null;
this.wasmModule = null;
this.jsEntityMasks.clear();
this.initialized = false;
this.usingWasm = false;
}
}
/**
* 全局ECS核心实例
*
* 提供单例模式的ECS核心确保整个应用使用同一个实例
*/
export const ecsCore = new WasmEcsCore();
/**
* 初始化ECS引擎
*
* 便捷的初始化函数,推荐在应用启动时调用
*
* @param silent 是否静默模式
* @returns 初始化是否成功
*
* @example
* ```typescript
* import { initializeEcs } from 'ecs-framework';
*
* async function main() {
* // 使用默认配置JavaScript实现
* await initializeEcs();
*
* // 或者自定义配置
* await initializeEcs({
* enabled: false, // 禁用WASM
* silent: true // 静默模式
* });
* }
* ```
*/
export async function initializeEcs(silent: boolean = false): Promise<boolean> {
ecsCore.setSilent(silent);
return ecsCore.initialize();
}
/**
* 快速查询工具函数
*
* 为常见查询操作提供便捷的API
*/
export const Query = {
/**
* 查询拥有指定组件的所有实体
*/
withComponent: (componentId: number, maxResults?: number): QueryResult => {
const mask = ecsCore.createComponentMask([componentId]);
return ecsCore.queryEntities(mask, maxResults);
},
/**
* 查询拥有多个组件的实体
*/
withComponents: (componentIds: number[], maxResults?: number): QueryResult => {
const masks = componentIds.map(id => ecsCore.createComponentMask([id]));
return ecsCore.queryMultipleComponents(masks, maxResults);
},
/**
* 查询拥有某些组件但不拥有其他组件的实体
*/
withExclusion: (includeIds: number[], excludeIds: number[], maxResults?: number): QueryResult => {
const includeMask = ecsCore.createComponentMask(includeIds);
const excludeMask = ecsCore.createComponentMask(excludeIds);
return ecsCore.queryWithExclusion(includeMask, excludeMask, maxResults);
}
};
export * from './Wasm';
export type Query = import('./Wasm').QueryResult;

View File

@@ -1,22 +1,17 @@
/**
* 工具模块导出
*/
export * from './Extensions';
export * from './Pool';
export * from './Emitter';
export * from './GlobalManager';
export * from './PerformanceMonitor';
export { Time } from './Time';
// WebAssembly核心模块
export {
WasmEcsCore,
ecsCore,
initializeEcs,
Query,
EntityId,
ComponentMask,
QueryResult,
PerformanceStats as WasmPerformanceStats
} from './WasmCore';
PerformanceStats as WasmPerformanceStats,
WasmLoader,
JavaScriptFallback
} from './Wasm';