重构项目结构:整理gitignore,移动source目录到根目录,统一依赖管理
This commit is contained in:
86
.gitignore
vendored
86
.gitignore
vendored
@@ -1,11 +1,75 @@
|
|||||||
/source/node_modules
|
# 依赖目录
|
||||||
/source/bin
|
node_modules/
|
||||||
/demo/bin-debug
|
npm-debug.log*
|
||||||
/demo/bin-release
|
yarn-debug.log*
|
||||||
/.idea
|
yarn-error.log*
|
||||||
/.vscode
|
|
||||||
/demo_wxgame
|
# 构建输出
|
||||||
/demo/.wing
|
bin/
|
||||||
/demo/.idea
|
dist/
|
||||||
/demo/.vscode
|
wasm/
|
||||||
/source/docs
|
*.tgz
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# 临时文件
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# IDE 配置
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# 操作系统文件
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# 日志文件
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# 环境配置
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# 测试覆盖率
|
||||||
|
coverage/
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# Rust 构建文件
|
||||||
|
src/wasm/*/target/
|
||||||
|
src/wasm/*/pkg/
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# 包管理器锁文件(保留npm的,忽略其他的)
|
||||||
|
yarn.lock
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
# 文档生成
|
||||||
|
docs/api/
|
||||||
|
docs/build/
|
||||||
|
|
||||||
|
# 备份文件
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
# 演示项目构建产物
|
||||||
|
/demo/bin-debug/
|
||||||
|
/demo/bin-release/
|
||||||
|
/demo/.wing/
|
||||||
|
/demo/.idea/
|
||||||
|
/demo/.vscode/
|
||||||
|
/demo_wxgame/
|
||||||
|
|||||||
1441
source/package-lock.json → package-lock.json
generated
1441
source/package-lock.json → package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esengine/ecs-framework",
|
"name": "@esengine/ecs-framework",
|
||||||
"version": "2.1.7",
|
"version": "2.1.9",
|
||||||
"description": "用于Laya、Cocos等游戏引擎的高性能ECS框架",
|
"description": "用于Laya、Cocos等游戏引擎的高性能ECS框架",
|
||||||
"main": "bin/index.js",
|
"main": "bin/index.js",
|
||||||
"types": "bin/index.d.ts",
|
"types": "bin/index.d.ts",
|
||||||
@@ -27,10 +27,7 @@
|
|||||||
"build": "npm run build:wasm && npm run build:ts",
|
"build": "npm run build:wasm && npm run build:ts",
|
||||||
"build:watch": "tsc --watch",
|
"build:watch": "tsc --watch",
|
||||||
"rebuild": "npm run clean && npm run clean:wasm && npm run build",
|
"rebuild": "npm run clean && npm run clean:wasm && npm run build",
|
||||||
"bundle": "npm run build && node scripts/bundle.js",
|
"build:npm": "npm run build && node scripts/build-rollup.js",
|
||||||
"compress": "npm run build && node scripts/compress.js",
|
|
||||||
"package": "npm run bundle && npm run compress",
|
|
||||||
"build:npm": "npm run build && node scripts/build-single.js",
|
|
||||||
"test:benchmark": "npm run build && node bin/Testing/Performance/benchmark.js",
|
"test:benchmark": "npm run build && node bin/Testing/Performance/benchmark.js",
|
||||||
"test:unit": "npm run build && node bin/Testing/test-runner.js",
|
"test:unit": "npm run build && node bin/Testing/test-runner.js",
|
||||||
"benchmark": "node scripts/benchmark.js",
|
"benchmark": "node scripts/benchmark.js",
|
||||||
@@ -43,11 +40,13 @@
|
|||||||
"author": "yhh",
|
"author": "yhh",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^28.0.3",
|
||||||
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||||
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@types/node": "^20.19.0",
|
"@types/node": "^20.19.0",
|
||||||
"archiver": "^7.0.1",
|
|
||||||
"esbuild": "^0.25.5",
|
|
||||||
"rimraf": "^5.0.0",
|
"rimraf": "^5.0.0",
|
||||||
"terser": "^5.41.0",
|
"rollup": "^4.42.0",
|
||||||
|
"rollup-plugin-dts": "^6.2.1",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
99
rollup.config.js
Normal file
99
rollup.config.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
const resolve = require('@rollup/plugin-node-resolve');
|
||||||
|
const commonjs = require('@rollup/plugin-commonjs');
|
||||||
|
const terser = require('@rollup/plugin-terser');
|
||||||
|
const dts = require('rollup-plugin-dts').default;
|
||||||
|
const { readFileSync } = require('fs');
|
||||||
|
|
||||||
|
const pkg = JSON.parse(readFileSync('./package.json', 'utf8'));
|
||||||
|
|
||||||
|
const banner = `/**
|
||||||
|
* @esengine/ecs-framework v${pkg.version}
|
||||||
|
* 高性能ECS框架 - 适用于Cocos Creator和Laya引擎
|
||||||
|
*
|
||||||
|
* @author ${pkg.author}
|
||||||
|
* @license ${pkg.license}
|
||||||
|
*/`;
|
||||||
|
|
||||||
|
const external = [];
|
||||||
|
|
||||||
|
const commonPlugins = [
|
||||||
|
resolve({
|
||||||
|
browser: true,
|
||||||
|
preferBuiltins: false
|
||||||
|
}),
|
||||||
|
commonjs({
|
||||||
|
include: /node_modules/
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
// ES模块构建
|
||||||
|
{
|
||||||
|
input: 'bin/index.js',
|
||||||
|
output: {
|
||||||
|
file: 'dist/index.js',
|
||||||
|
format: 'es',
|
||||||
|
banner,
|
||||||
|
sourcemap: true,
|
||||||
|
exports: 'named'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...commonPlugins,
|
||||||
|
terser({
|
||||||
|
format: {
|
||||||
|
comments: /^!/
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
external,
|
||||||
|
treeshake: {
|
||||||
|
moduleSideEffects: false,
|
||||||
|
propertyReadSideEffects: false,
|
||||||
|
unknownGlobalSideEffects: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// UMD构建(可选)
|
||||||
|
{
|
||||||
|
input: 'bin/index.js',
|
||||||
|
output: {
|
||||||
|
file: 'dist/index.umd.js',
|
||||||
|
format: 'umd',
|
||||||
|
name: 'ECSFramework',
|
||||||
|
banner,
|
||||||
|
sourcemap: true,
|
||||||
|
exports: 'named'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
...commonPlugins,
|
||||||
|
terser({
|
||||||
|
format: {
|
||||||
|
comments: /^!/
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
external,
|
||||||
|
treeshake: {
|
||||||
|
moduleSideEffects: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 类型定义构建
|
||||||
|
{
|
||||||
|
input: 'bin/index.d.ts',
|
||||||
|
output: {
|
||||||
|
file: 'dist/index.d.ts',
|
||||||
|
format: 'es',
|
||||||
|
banner: `/**
|
||||||
|
* @esengine/ecs-framework v${pkg.version}
|
||||||
|
* TypeScript definitions
|
||||||
|
*/`
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
dts({
|
||||||
|
respectExternal: true
|
||||||
|
})
|
||||||
|
],
|
||||||
|
external: []
|
||||||
|
}
|
||||||
|
];
|
||||||
142
scripts/build-rollup.js
Normal file
142
scripts/build-rollup.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
console.log('🚀 使用 Rollup 构建npm包...');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
// 清理旧的dist目录
|
||||||
|
if (fs.existsSync('./dist')) {
|
||||||
|
console.log('🧹 清理旧的构建文件...');
|
||||||
|
execSync('rimraf ./dist', { stdio: 'inherit' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行Rollup构建
|
||||||
|
console.log('📦 执行 Rollup 构建...');
|
||||||
|
execSync('rollup -c', { stdio: 'inherit' });
|
||||||
|
|
||||||
|
// 生成package.json
|
||||||
|
console.log('📋 生成 package.json...');
|
||||||
|
generatePackageJson();
|
||||||
|
|
||||||
|
// 复制其他文件
|
||||||
|
console.log('📁 复制必要文件...');
|
||||||
|
copyFiles();
|
||||||
|
|
||||||
|
// 输出构建结果
|
||||||
|
showBuildResults();
|
||||||
|
|
||||||
|
console.log('✅ 构建完成!');
|
||||||
|
console.log('\n🚀 发布命令:');
|
||||||
|
console.log('cd dist && npm publish');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 构建失败:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePackageJson() {
|
||||||
|
const sourcePackage = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||||
|
|
||||||
|
const distPackage = {
|
||||||
|
name: sourcePackage.name,
|
||||||
|
version: sourcePackage.version,
|
||||||
|
description: sourcePackage.description,
|
||||||
|
main: 'index.js',
|
||||||
|
module: 'index.js',
|
||||||
|
types: 'index.d.ts',
|
||||||
|
type: 'module',
|
||||||
|
exports: {
|
||||||
|
'.': {
|
||||||
|
import: './index.js',
|
||||||
|
types: './index.d.ts'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: [
|
||||||
|
'index.js',
|
||||||
|
'index.js.map',
|
||||||
|
'index.umd.js',
|
||||||
|
'index.umd.js.map',
|
||||||
|
'index.d.ts',
|
||||||
|
'wasm',
|
||||||
|
'README.md',
|
||||||
|
'LICENSE'
|
||||||
|
],
|
||||||
|
keywords: [
|
||||||
|
'ecs',
|
||||||
|
'entity-component-system',
|
||||||
|
'game-engine',
|
||||||
|
'typescript',
|
||||||
|
'cocos-creator',
|
||||||
|
'laya',
|
||||||
|
'rollup'
|
||||||
|
],
|
||||||
|
author: sourcePackage.author,
|
||||||
|
license: sourcePackage.license,
|
||||||
|
repository: sourcePackage.repository,
|
||||||
|
bugs: sourcePackage.bugs,
|
||||||
|
homepage: sourcePackage.homepage,
|
||||||
|
engines: {
|
||||||
|
node: '>=16.0.0'
|
||||||
|
},
|
||||||
|
sideEffects: false
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync('./dist/package.json', JSON.stringify(distPackage, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyFiles() {
|
||||||
|
const filesToCopy = [
|
||||||
|
{ src: '../README.md', dest: './dist/README.md' },
|
||||||
|
{ src: '../LICENSE', dest: './dist/LICENSE' }
|
||||||
|
];
|
||||||
|
|
||||||
|
filesToCopy.forEach(({ src, dest }) => {
|
||||||
|
if (fs.existsSync(src)) {
|
||||||
|
fs.copyFileSync(src, dest);
|
||||||
|
console.log(` ✓ 复制: ${path.basename(dest)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 复制WASM文件(过滤.gitignore)
|
||||||
|
const wasmDir = './bin/wasm';
|
||||||
|
if (fs.existsSync(wasmDir)) {
|
||||||
|
const distWasmDir = './dist/wasm';
|
||||||
|
if (!fs.existsSync(distWasmDir)) {
|
||||||
|
fs.mkdirSync(distWasmDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
let copiedCount = 0;
|
||||||
|
fs.readdirSync(wasmDir).forEach(file => {
|
||||||
|
// 过滤掉.gitignore文件
|
||||||
|
if (file !== '.gitignore') {
|
||||||
|
fs.copyFileSync(
|
||||||
|
path.join(wasmDir, file),
|
||||||
|
path.join(distWasmDir, file)
|
||||||
|
);
|
||||||
|
copiedCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (copiedCount > 0) {
|
||||||
|
console.log(` ✓ 复制: ${copiedCount}个WASM文件`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBuildResults() {
|
||||||
|
const distDir = './dist';
|
||||||
|
const files = ['index.js', 'index.umd.js', 'index.d.ts'];
|
||||||
|
|
||||||
|
console.log('\n📊 构建结果:');
|
||||||
|
files.forEach(file => {
|
||||||
|
const filePath = path.join(distDir, file);
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
const size = fs.statSync(filePath).size;
|
||||||
|
console.log(` ${file}: ${(size / 1024).toFixed(1)}KB`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
[
|
|
||||||
"@babel/preset-env",
|
|
||||||
{
|
|
||||||
"targets": {
|
|
||||||
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
|
||||||
},
|
|
||||||
"modules": "auto"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
92
source/.gitignore
vendored
92
source/.gitignore
vendored
@@ -1,92 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# Build output
|
|
||||||
bin/
|
|
||||||
dev-bin/
|
|
||||||
dist/
|
|
||||||
|
|
||||||
# WASM build artifacts
|
|
||||||
src/wasm/rust-ecs-core/target/
|
|
||||||
src/wasm/rust-ecs-core/pkg/
|
|
||||||
*.wasm
|
|
||||||
wasm-pack.log
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Environment files
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
201
source/LICENSE
201
source/LICENSE
@@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"targets": {
|
|
||||||
"debug": {
|
|
||||||
"outFile": "bin/ecs-core.wasm",
|
|
||||||
"textFile": "bin/ecs-core.wat",
|
|
||||||
"sourceMap": true,
|
|
||||||
"debug": true
|
|
||||||
},
|
|
||||||
"release": {
|
|
||||||
"outFile": "bin/ecs-core.wasm",
|
|
||||||
"optimizeLevel": 3,
|
|
||||||
"shrinkLevel": 2,
|
|
||||||
"converge": false,
|
|
||||||
"noAssert": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"bindings": "esm",
|
|
||||||
"exportRuntime": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
/**
|
|
||||||
* ECS框架构建配置
|
|
||||||
* 针对Laya、Cocos等游戏引擎优化
|
|
||||||
*/
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
// 输入配置
|
|
||||||
input: {
|
|
||||||
entry: './src/index.ts',
|
|
||||||
tsconfig: './tsconfig.json'
|
|
||||||
},
|
|
||||||
|
|
||||||
// 输出配置
|
|
||||||
output: {
|
|
||||||
dir: './bin',
|
|
||||||
formats: ['es', 'cjs', 'umd'], // ES模块、CommonJS、UMD
|
|
||||||
filename: {
|
|
||||||
es: 'ecs-framework.esm.js',
|
|
||||||
cjs: 'ecs-framework.cjs.js',
|
|
||||||
umd: 'ecs-framework.umd.js'
|
|
||||||
},
|
|
||||||
minify: true,
|
|
||||||
sourcemap: true
|
|
||||||
},
|
|
||||||
|
|
||||||
// WebAssembly支持配置
|
|
||||||
wasm: {
|
|
||||||
enabled: false, // 暂时禁用,未来启用
|
|
||||||
modules: {
|
|
||||||
// 计划迁移到WebAssembly的模块
|
|
||||||
core: {
|
|
||||||
entry: './src/wasm/core.ts',
|
|
||||||
output: 'ecs-core.wasm',
|
|
||||||
features: ['query-system', 'math-utils']
|
|
||||||
},
|
|
||||||
physics: {
|
|
||||||
entry: './src/wasm/physics.ts',
|
|
||||||
output: 'ecs-physics.wasm',
|
|
||||||
features: ['collision-detection', 'spatial-hash']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// AssemblyScript配置
|
|
||||||
assemblyscript: {
|
|
||||||
target: 'wasm32',
|
|
||||||
optimize: true,
|
|
||||||
runtime: 'minimal'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 游戏引擎特定配置
|
|
||||||
engines: {
|
|
||||||
laya: {
|
|
||||||
// Laya引擎特定优化
|
|
||||||
target: 'es5',
|
|
||||||
polyfills: ['Promise', 'Object.assign'],
|
|
||||||
globals: ['Laya'],
|
|
||||||
wasm: {
|
|
||||||
// Laya环境下的WebAssembly配置
|
|
||||||
loader: 'laya-wasm-loader',
|
|
||||||
fallback: true // 支持降级到JavaScript
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cocos: {
|
|
||||||
// Cocos引擎特定优化
|
|
||||||
target: 'es6',
|
|
||||||
polyfills: [],
|
|
||||||
globals: ['cc'],
|
|
||||||
wasm: {
|
|
||||||
// Cocos环境下的WebAssembly配置
|
|
||||||
loader: 'cocos-wasm-loader',
|
|
||||||
fallback: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 小游戏平台优化
|
|
||||||
platforms: {
|
|
||||||
wechat: {
|
|
||||||
// 微信小游戏优化
|
|
||||||
maxSize: '4MB',
|
|
||||||
treeshaking: true,
|
|
||||||
compression: 'gzip',
|
|
||||||
wasm: {
|
|
||||||
// 微信小游戏WebAssembly支持
|
|
||||||
enabled: true,
|
|
||||||
maxWasmSize: '2MB', // WebAssembly模块大小限制
|
|
||||||
preload: ['ecs-core.wasm'] // 预加载核心模块
|
|
||||||
}
|
|
||||||
},
|
|
||||||
alipay: {
|
|
||||||
// 支付宝小游戏优化
|
|
||||||
maxSize: '4MB',
|
|
||||||
treeshaking: true,
|
|
||||||
compression: 'gzip',
|
|
||||||
wasm: {
|
|
||||||
enabled: true,
|
|
||||||
maxWasmSize: '2MB'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bytedance: {
|
|
||||||
// 字节跳动小游戏优化
|
|
||||||
maxSize: '4MB',
|
|
||||||
treeshaking: true,
|
|
||||||
compression: 'gzip',
|
|
||||||
wasm: {
|
|
||||||
enabled: true,
|
|
||||||
maxWasmSize: '2MB'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 性能优化配置
|
|
||||||
optimization: {
|
|
||||||
// 启用Tree Shaking
|
|
||||||
treeshaking: true,
|
|
||||||
// 代码分割
|
|
||||||
codeSplitting: false, // 小游戏通常不需要代码分割
|
|
||||||
// 压缩配置
|
|
||||||
minify: {
|
|
||||||
removeComments: true,
|
|
||||||
removeConsole: false, // 保留console用于调试
|
|
||||||
removeDebugger: true
|
|
||||||
},
|
|
||||||
// 内联小文件
|
|
||||||
inlineThreshold: 1024,
|
|
||||||
// WebAssembly优化
|
|
||||||
wasm: {
|
|
||||||
// 启用WebAssembly优化
|
|
||||||
optimize: true,
|
|
||||||
// 内存配置
|
|
||||||
memory: {
|
|
||||||
initial: 1, // 初始内存页数 (64KB per page)
|
|
||||||
maximum: 16, // 最大内存页数
|
|
||||||
shared: false // 是否共享内存
|
|
||||||
},
|
|
||||||
// 导出配置
|
|
||||||
exports: {
|
|
||||||
memory: true,
|
|
||||||
table: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 开发配置
|
|
||||||
development: {
|
|
||||||
sourcemap: true,
|
|
||||||
hotReload: false, // 小游戏不支持热重载
|
|
||||||
debugMode: true
|
|
||||||
},
|
|
||||||
|
|
||||||
// 生产配置
|
|
||||||
production: {
|
|
||||||
sourcemap: false,
|
|
||||||
minify: true,
|
|
||||||
optimization: true,
|
|
||||||
bundleAnalyzer: true
|
|
||||||
},
|
|
||||||
|
|
||||||
// 实验性功能
|
|
||||||
experimental: {
|
|
||||||
// 混合架构支持
|
|
||||||
hybrid: {
|
|
||||||
enabled: true,
|
|
||||||
// 自动检测WebAssembly支持
|
|
||||||
autoDetect: true,
|
|
||||||
// 性能基准测试
|
|
||||||
benchmark: true,
|
|
||||||
// 降级策略
|
|
||||||
fallback: {
|
|
||||||
strategy: 'graceful', // 优雅降级
|
|
||||||
modules: ['core', 'physics'] // 支持降级的模块
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const gulp = require('gulp');
|
|
||||||
const { series, parallel } = require('gulp');
|
|
||||||
const terser = require('gulp-terser');
|
|
||||||
const inject = require('gulp-inject-string');
|
|
||||||
const ts = require('gulp-typescript');
|
|
||||||
const merge = require('merge2');
|
|
||||||
const typescript = require('gulp-typescript');
|
|
||||||
const concat = require('gulp-concat');
|
|
||||||
const replace = require('gulp-string-replace');
|
|
||||||
|
|
||||||
// TypeScript项目配置
|
|
||||||
const tsProject = ts.createProject('tsconfig.json', {
|
|
||||||
module: 'es2020',
|
|
||||||
target: 'es2018'
|
|
||||||
});
|
|
||||||
|
|
||||||
function buildJs() {
|
|
||||||
return tsProject.src()
|
|
||||||
.pipe(tsProject())
|
|
||||||
.js.pipe(inject.replace('var es;', ''))
|
|
||||||
.pipe(inject.prepend('window.es = {};\n'))
|
|
||||||
.pipe(inject.replace('var __extends =', 'window.__extends ='))
|
|
||||||
.pipe(terser())
|
|
||||||
.pipe(gulp.dest('./bin'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDts() {
|
|
||||||
return tsProject.src()
|
|
||||||
.pipe(tsProject())
|
|
||||||
// .dts.pipe(inject.append('import e = framework;'))
|
|
||||||
.pipe(gulp.dest('./bin'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建ES模块版本
|
|
||||||
gulp.task('build-esm', function() {
|
|
||||||
return gulp.src(['src/**/*.ts'])
|
|
||||||
.pipe(tsProject())
|
|
||||||
.pipe(concat('ecs-framework.esm.js'))
|
|
||||||
.pipe(inject.prepend('// ECS Framework - ES Module Version\n'))
|
|
||||||
.pipe(gulp.dest('bin/'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 构建UMD版本(兼容各种游戏引擎)
|
|
||||||
gulp.task('build-umd', function() {
|
|
||||||
const umdProject = ts.createProject('tsconfig.json', {
|
|
||||||
module: 'umd',
|
|
||||||
target: 'es5'
|
|
||||||
});
|
|
||||||
|
|
||||||
return gulp.src(['src/**/*.ts'])
|
|
||||||
.pipe(umdProject())
|
|
||||||
.pipe(concat('ecs-framework.umd.js'))
|
|
||||||
.pipe(inject.prepend(`// ECS Framework - UMD Version
|
|
||||||
(function (global, factory) {
|
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
||||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
||||||
(global = global || self, factory(global.ECS = {}));
|
|
||||||
}(this, (function (exports) { 'use strict';
|
|
||||||
`))
|
|
||||||
.pipe(inject.append('\n})));'))
|
|
||||||
.pipe(gulp.dest('bin/'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 构建Laya引擎专用版本
|
|
||||||
gulp.task('build-laya', function() {
|
|
||||||
const layaProject = ts.createProject('tsconfig.json', {
|
|
||||||
module: 'none',
|
|
||||||
target: 'es5'
|
|
||||||
});
|
|
||||||
|
|
||||||
return gulp.src(['src/**/*.ts'])
|
|
||||||
.pipe(layaProject())
|
|
||||||
.pipe(concat('ecs-framework.laya.js'))
|
|
||||||
.pipe(replace(/export\s+/g, ''))
|
|
||||||
.pipe(inject.prepend(`// ECS Framework - Laya Engine Version
|
|
||||||
var ECS = ECS || {};
|
|
||||||
(function(ECS) {
|
|
||||||
`))
|
|
||||||
.pipe(inject.append('\n})(ECS);'))
|
|
||||||
.pipe(gulp.dest('bin/'));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 构建Cocos引擎专用版本
|
|
||||||
gulp.task('build-cocos', function() {
|
|
||||||
const cocosProject = ts.createProject('tsconfig.json', {
|
|
||||||
module: 'es2020',
|
|
||||||
target: 'es2018'
|
|
||||||
});
|
|
||||||
|
|
||||||
return gulp.src(['src/**/*.ts'])
|
|
||||||
.pipe(cocosProject())
|
|
||||||
.pipe(concat('ecs-framework.cocos.js'))
|
|
||||||
.pipe(inject.prepend('// ECS Framework - Cocos Engine Version\n'))
|
|
||||||
.pipe(gulp.dest('bin/'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 压缩所有构建文件
|
|
||||||
gulp.task('minify', function() {
|
|
||||||
return gulp.src(['bin/*.js', '!bin/*.min.js'])
|
|
||||||
.pipe(terser({
|
|
||||||
compress: {
|
|
||||||
drop_console: false, // 保留console用于调试
|
|
||||||
drop_debugger: true,
|
|
||||||
pure_funcs: ['console.log'] // 可选择性移除console.log
|
|
||||||
},
|
|
||||||
mangle: {
|
|
||||||
keep_fnames: true // 保留函数名用于调试
|
|
||||||
},
|
|
||||||
format: {
|
|
||||||
comments: false
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.pipe(concat(function(file) {
|
|
||||||
return file.basename.replace('.js', '.min.js');
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest('bin/'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 生成类型定义文件
|
|
||||||
gulp.task('build-types', function() {
|
|
||||||
const dtsProject = ts.createProject('tsconfig.json', {
|
|
||||||
declaration: true,
|
|
||||||
emitDeclarationOnly: true
|
|
||||||
});
|
|
||||||
|
|
||||||
return gulp.src(['src/**/*.ts'])
|
|
||||||
.pipe(dtsProject())
|
|
||||||
.pipe(concat('ecs-framework.d.ts'))
|
|
||||||
.pipe(gulp.dest('bin/'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 清理构建目录
|
|
||||||
gulp.task('clean', function() {
|
|
||||||
const del = require('del');
|
|
||||||
return del(['bin/**/*']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 开发构建(快速,包含源码映射)
|
|
||||||
gulp.task('build-dev', gulp.series('clean', 'build-esm', 'build-types'));
|
|
||||||
|
|
||||||
// 生产构建(完整,包含所有版本和压缩)
|
|
||||||
gulp.task('build', gulp.series(
|
|
||||||
'clean',
|
|
||||||
gulp.parallel('build-esm', 'build-umd', 'build-laya', 'build-cocos'),
|
|
||||||
'build-types',
|
|
||||||
'minify'
|
|
||||||
));
|
|
||||||
|
|
||||||
// 监听文件变化
|
|
||||||
gulp.task('watch', function() {
|
|
||||||
gulp.watch('src/**/*.ts', gulp.series('build-dev'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 默认任务
|
|
||||||
gulp.task('default', gulp.series('build'));
|
|
||||||
|
|
||||||
exports.buildJs = buildJs;
|
|
||||||
exports.buildDts = buildDts;
|
|
||||||
exports.build = series(buildJs, buildDts);
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const esbuild = require('esbuild');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ECS Framework 单文件构建脚本
|
|
||||||
* 专门用于npm包发布的单文件版本
|
|
||||||
*/
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
// 输入配置
|
|
||||||
entryPoint: './bin/index.js',
|
|
||||||
|
|
||||||
// 输出配置
|
|
||||||
outputDir: './dist',
|
|
||||||
outputFile: 'index.js',
|
|
||||||
|
|
||||||
// 压缩配置
|
|
||||||
minify: true,
|
|
||||||
sourcemap: true,
|
|
||||||
|
|
||||||
// 目标环境 - 支持BigInt等ES2020特性
|
|
||||||
target: ['es2020'],
|
|
||||||
format: 'esm',
|
|
||||||
|
|
||||||
// npm包配置
|
|
||||||
generatePackageJson: true,
|
|
||||||
generateTypes: true
|
|
||||||
};
|
|
||||||
|
|
||||||
async function buildSingleFile() {
|
|
||||||
console.log('🚀 构建单文件npm包...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 确保输出目录存在
|
|
||||||
if (!fs.existsSync(config.outputDir)) {
|
|
||||||
fs.mkdirSync(config.outputDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一步:使用esbuild打包
|
|
||||||
console.log('📦 使用 esbuild 打包...');
|
|
||||||
|
|
||||||
const result = await esbuild.build({
|
|
||||||
entryPoints: [config.entryPoint],
|
|
||||||
bundle: true,
|
|
||||||
minify: config.minify,
|
|
||||||
sourcemap: config.sourcemap,
|
|
||||||
target: config.target,
|
|
||||||
format: config.format,
|
|
||||||
outfile: path.join(config.outputDir, config.outputFile),
|
|
||||||
platform: 'browser', // 浏览器环境
|
|
||||||
|
|
||||||
// 外部依赖
|
|
||||||
external: [],
|
|
||||||
|
|
||||||
// 定义Node.js模块的浏览器替代
|
|
||||||
inject: [],
|
|
||||||
|
|
||||||
// 定义全局变量
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"production"',
|
|
||||||
'require': 'undefined',
|
|
||||||
'__filename': '""',
|
|
||||||
'__dirname': '""'
|
|
||||||
},
|
|
||||||
|
|
||||||
// 元信息
|
|
||||||
metafile: true,
|
|
||||||
|
|
||||||
// 日志级别
|
|
||||||
logLevel: 'info',
|
|
||||||
|
|
||||||
// 保持类名(便于调试)
|
|
||||||
keepNames: true,
|
|
||||||
|
|
||||||
// 生成更兼容的代码
|
|
||||||
legalComments: 'none'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 显示打包结果
|
|
||||||
if (result.metafile) {
|
|
||||||
const analysis = await esbuild.analyzeMetafile(result.metafile);
|
|
||||||
console.log('📊 打包分析:');
|
|
||||||
console.log(analysis);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二步:生成类型定义文件
|
|
||||||
if (config.generateTypes) {
|
|
||||||
console.log('📝 生成类型定义文件...');
|
|
||||||
await generateTypeDefinitions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第三步:生成package.json
|
|
||||||
if (config.generatePackageJson) {
|
|
||||||
console.log('📋 生成package.json...');
|
|
||||||
await generatePackageJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第四步:复制必要文件
|
|
||||||
await copyEssentialFiles();
|
|
||||||
|
|
||||||
console.log('✅ 单文件构建完成!');
|
|
||||||
console.log(`📄 主文件: ${path.join(config.outputDir, config.outputFile)}`);
|
|
||||||
|
|
||||||
// 显示文件大小
|
|
||||||
const stats = fs.statSync(path.join(config.outputDir, config.outputFile));
|
|
||||||
console.log(`📏 文件大小: ${(stats.size / 1024).toFixed(2)} KB`);
|
|
||||||
|
|
||||||
console.log('\n🚀 发布到npm:');
|
|
||||||
console.log('cd dist && npm publish');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ 构建失败:', error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成类型定义文件
|
|
||||||
*/
|
|
||||||
async function generateTypeDefinitions() {
|
|
||||||
const sourceTypesFile = './bin/index.d.ts';
|
|
||||||
const targetTypesFile = path.join(config.outputDir, 'index.d.ts');
|
|
||||||
|
|
||||||
if (fs.existsSync(sourceTypesFile)) {
|
|
||||||
// 读取原始类型定义
|
|
||||||
let typesContent = fs.readFileSync(sourceTypesFile, 'utf8');
|
|
||||||
|
|
||||||
// 处理相对路径导入,将其转换为绝对导入
|
|
||||||
typesContent = typesContent.replace(/from ['"]\.\//g, "from './");
|
|
||||||
typesContent = typesContent.replace(/from ['"]\.\.\//g, "from './");
|
|
||||||
|
|
||||||
// 添加版本信息注释
|
|
||||||
const header = `/**
|
|
||||||
* @esengine/ecs-framework
|
|
||||||
* 高性能ECS框架 - 适用于Cocos Creator和Laya引擎
|
|
||||||
* 版本: ${require('../package.json').version}
|
|
||||||
* 构建时间: ${new Date().toISOString()}
|
|
||||||
*/
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
fs.writeFileSync(targetTypesFile, header + typesContent);
|
|
||||||
console.log(` ✓ 生成: ${targetTypesFile}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成npm包的package.json
|
|
||||||
*/
|
|
||||||
async function generatePackageJson() {
|
|
||||||
const sourcePackage = require('../package.json');
|
|
||||||
|
|
||||||
// 创建完全干净的package.json,只包含发布必需的字段
|
|
||||||
const distPackage = {};
|
|
||||||
|
|
||||||
// 按顺序添加字段,确保没有任何开发相关字段
|
|
||||||
distPackage.name = sourcePackage.name;
|
|
||||||
distPackage.version = sourcePackage.version;
|
|
||||||
distPackage.description = sourcePackage.description;
|
|
||||||
distPackage.main = 'index.js';
|
|
||||||
distPackage.types = 'index.d.ts';
|
|
||||||
distPackage.module = 'index.js';
|
|
||||||
distPackage.type = 'module';
|
|
||||||
|
|
||||||
// 导出配置
|
|
||||||
distPackage.exports = {
|
|
||||||
".": {
|
|
||||||
"import": "./index.js",
|
|
||||||
"types": "./index.d.ts"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 文件配置
|
|
||||||
distPackage.files = [
|
|
||||||
'index.js',
|
|
||||||
'index.js.map',
|
|
||||||
'index.d.ts',
|
|
||||||
'wasm/**/*',
|
|
||||||
'README.md',
|
|
||||||
'LICENSE'
|
|
||||||
];
|
|
||||||
|
|
||||||
// 关键词
|
|
||||||
distPackage.keywords = [
|
|
||||||
...sourcePackage.keywords,
|
|
||||||
'single-file',
|
|
||||||
'bundled',
|
|
||||||
'minified'
|
|
||||||
];
|
|
||||||
|
|
||||||
// 元信息
|
|
||||||
distPackage.author = sourcePackage.author;
|
|
||||||
distPackage.license = sourcePackage.license;
|
|
||||||
|
|
||||||
// Repository信息
|
|
||||||
distPackage.repository = {
|
|
||||||
type: 'git',
|
|
||||||
url: 'git+https://github.com/esengine/ecs-framework.git'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发布配置
|
|
||||||
distPackage.publishConfig = {
|
|
||||||
access: 'public'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 引擎兼容性
|
|
||||||
distPackage.engines = {
|
|
||||||
node: '>=16.0.0'
|
|
||||||
};
|
|
||||||
|
|
||||||
const packagePath = path.join(config.outputDir, 'package.json');
|
|
||||||
fs.writeFileSync(packagePath, JSON.stringify(distPackage, null, 2));
|
|
||||||
console.log(` ✓ 生成: ${packagePath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 复制必要文件
|
|
||||||
*/
|
|
||||||
async function copyEssentialFiles() {
|
|
||||||
console.log('📁 复制必要文件...');
|
|
||||||
|
|
||||||
const filesToCopy = [
|
|
||||||
{ src: '../README.md', dest: 'README.md' },
|
|
||||||
{ src: '../LICENSE', dest: 'LICENSE', optional: true }
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const file of filesToCopy) {
|
|
||||||
const srcPath = path.resolve(file.src);
|
|
||||||
const destPath = path.join(config.outputDir, file.dest);
|
|
||||||
|
|
||||||
if (fs.existsSync(srcPath)) {
|
|
||||||
fs.copyFileSync(srcPath, destPath);
|
|
||||||
console.log(` ✓ 复制: ${file.dest}`);
|
|
||||||
} else if (!file.optional) {
|
|
||||||
console.warn(` ⚠️ 文件不存在: ${srcPath}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复制WASM文件
|
|
||||||
await copyWasmFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 复制WASM文件
|
|
||||||
*/
|
|
||||||
async function copyWasmFiles() {
|
|
||||||
const wasmSrcDir = './bin/wasm';
|
|
||||||
const wasmDestDir = path.join(config.outputDir, 'wasm');
|
|
||||||
|
|
||||||
if (fs.existsSync(wasmSrcDir)) {
|
|
||||||
console.log('📦 复制WASM文件...');
|
|
||||||
|
|
||||||
// 创建目标目录
|
|
||||||
if (!fs.existsSync(wasmDestDir)) {
|
|
||||||
fs.mkdirSync(wasmDestDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复制所有WASM相关文件
|
|
||||||
const wasmFiles = fs.readdirSync(wasmSrcDir);
|
|
||||||
for (const file of wasmFiles) {
|
|
||||||
// 排除.gitignore文件
|
|
||||||
if (file === '.gitignore') continue;
|
|
||||||
|
|
||||||
const srcPath = path.join(wasmSrcDir, file);
|
|
||||||
const destPath = path.join(wasmDestDir, file);
|
|
||||||
|
|
||||||
if (fs.statSync(srcPath).isFile()) {
|
|
||||||
fs.copyFileSync(srcPath, destPath);
|
|
||||||
console.log(` ✓ 复制WASM: ${file}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn(' ⚠️ WASM目录不存在: ' + wasmSrcDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行构建
|
|
||||||
if (require.main === module) {
|
|
||||||
buildSingleFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { buildSingleFile, config };
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const esbuild = require('esbuild');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const { minify } = require('terser');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ECS Framework 打包脚本
|
|
||||||
* 将bin目录中的所有文件合并成一个压缩文件
|
|
||||||
*/
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
// 输入配置
|
|
||||||
entryPoint: './bin/index.js',
|
|
||||||
|
|
||||||
// 输出配置
|
|
||||||
outputDir: './dist',
|
|
||||||
outputFile: 'ecs-framework.min.js',
|
|
||||||
|
|
||||||
// 压缩配置
|
|
||||||
minify: true,
|
|
||||||
sourcemap: true,
|
|
||||||
|
|
||||||
// 包含WASM文件
|
|
||||||
includeWasm: true,
|
|
||||||
|
|
||||||
// 目标环境
|
|
||||||
target: ['es2017'],
|
|
||||||
format: 'esm'
|
|
||||||
};
|
|
||||||
|
|
||||||
async function createBundle() {
|
|
||||||
console.log('🚀 开始打包 ECS Framework...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 确保输出目录存在
|
|
||||||
if (!fs.existsSync(config.outputDir)) {
|
|
||||||
fs.mkdirSync(config.outputDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一步:使用esbuild打包
|
|
||||||
console.log('📦 使用 esbuild 打包...');
|
|
||||||
|
|
||||||
const result = await esbuild.build({
|
|
||||||
entryPoints: [config.entryPoint],
|
|
||||||
bundle: true,
|
|
||||||
minify: config.minify,
|
|
||||||
sourcemap: config.sourcemap,
|
|
||||||
target: config.target,
|
|
||||||
format: config.format,
|
|
||||||
outfile: path.join(config.outputDir, config.outputFile),
|
|
||||||
platform: 'browser',
|
|
||||||
|
|
||||||
// 外部依赖(如果有的话)
|
|
||||||
external: [],
|
|
||||||
|
|
||||||
// 定义全局变量
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"production"'
|
|
||||||
},
|
|
||||||
|
|
||||||
// 处理WASM文件
|
|
||||||
loader: {
|
|
||||||
'.wasm': 'binary'
|
|
||||||
},
|
|
||||||
|
|
||||||
// 插件配置
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
name: 'wasm-loader',
|
|
||||||
setup(build) {
|
|
||||||
// 处理WASM文件导入
|
|
||||||
build.onLoad({ filter: /\.wasm$/ }, async (args) => {
|
|
||||||
const wasmBuffer = await fs.promises.readFile(args.path);
|
|
||||||
const base64 = wasmBuffer.toString('base64');
|
|
||||||
|
|
||||||
return {
|
|
||||||
contents: `
|
|
||||||
const wasmBase64 = "${base64}";
|
|
||||||
const wasmBinary = Uint8Array.from(atob(wasmBase64), c => c.charCodeAt(0));
|
|
||||||
export default wasmBinary;
|
|
||||||
`,
|
|
||||||
loader: 'js'
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// 元信息
|
|
||||||
metafile: true,
|
|
||||||
|
|
||||||
// 日志级别
|
|
||||||
logLevel: 'info'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 显示打包结果
|
|
||||||
if (result.metafile) {
|
|
||||||
const analysis = await esbuild.analyzeMetafile(result.metafile);
|
|
||||||
console.log('📊 打包分析:');
|
|
||||||
console.log(analysis);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二步:复制WASM文件到dist目录
|
|
||||||
if (config.includeWasm) {
|
|
||||||
console.log('📁 复制 WASM 文件...');
|
|
||||||
await copyWasmFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第三步:生成压缩包信息
|
|
||||||
await generateBundleInfo();
|
|
||||||
|
|
||||||
console.log('✅ 打包完成!');
|
|
||||||
console.log(`📄 输出文件: ${path.join(config.outputDir, config.outputFile)}`);
|
|
||||||
|
|
||||||
// 显示文件大小
|
|
||||||
const stats = fs.statSync(path.join(config.outputDir, config.outputFile));
|
|
||||||
console.log(`📏 文件大小: ${(stats.size / 1024).toFixed(2)} KB`);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ 打包失败:', error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 复制WASM文件到dist目录
|
|
||||||
*/
|
|
||||||
async function copyWasmFiles() {
|
|
||||||
const wasmDir = './bin/wasm';
|
|
||||||
const distWasmDir = path.join(config.outputDir, 'wasm');
|
|
||||||
|
|
||||||
if (fs.existsSync(wasmDir)) {
|
|
||||||
if (!fs.existsSync(distWasmDir)) {
|
|
||||||
fs.mkdirSync(distWasmDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const wasmFiles = fs.readdirSync(wasmDir);
|
|
||||||
for (const file of wasmFiles) {
|
|
||||||
// 排除 .gitignore 文件
|
|
||||||
if (file === '.gitignore') {
|
|
||||||
console.log(` ⏭️ 跳过: ${file}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const srcPath = path.join(wasmDir, file);
|
|
||||||
const destPath = path.join(distWasmDir, file);
|
|
||||||
|
|
||||||
if (fs.statSync(srcPath).isFile()) {
|
|
||||||
fs.copyFileSync(srcPath, destPath);
|
|
||||||
console.log(` ✓ 复制: ${file}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成打包信息文件
|
|
||||||
*/
|
|
||||||
async function generateBundleInfo() {
|
|
||||||
const bundleInfo = {
|
|
||||||
name: '@esengine/ecs-framework',
|
|
||||||
version: require('../package.json').version,
|
|
||||||
buildTime: new Date().toISOString(),
|
|
||||||
files: {
|
|
||||||
main: config.outputFile,
|
|
||||||
sourcemap: config.outputFile + '.map',
|
|
||||||
wasm: 'wasm/'
|
|
||||||
},
|
|
||||||
target: config.target,
|
|
||||||
format: config.format,
|
|
||||||
minified: config.minify,
|
|
||||||
size: {
|
|
||||||
main: fs.statSync(path.join(config.outputDir, config.outputFile)).size,
|
|
||||||
wasm: getWasmSize()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const infoPath = path.join(config.outputDir, 'bundle-info.json');
|
|
||||||
fs.writeFileSync(infoPath, JSON.stringify(bundleInfo, null, 2));
|
|
||||||
console.log(`📋 生成打包信息: ${infoPath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取WASM文件总大小
|
|
||||||
*/
|
|
||||||
function getWasmSize() {
|
|
||||||
const wasmDir = path.join(config.outputDir, 'wasm');
|
|
||||||
let totalSize = 0;
|
|
||||||
|
|
||||||
if (fs.existsSync(wasmDir)) {
|
|
||||||
const files = fs.readdirSync(wasmDir);
|
|
||||||
for (const file of files) {
|
|
||||||
const filePath = path.join(wasmDir, file);
|
|
||||||
if (fs.statSync(filePath).isFile()) {
|
|
||||||
totalSize += fs.statSync(filePath).size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行打包
|
|
||||||
if (require.main === module) {
|
|
||||||
createBundle();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { createBundle, config };
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const archiver = require('archiver');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ECS Framework 压缩脚本
|
|
||||||
* 将bin目录压缩成ZIP文件
|
|
||||||
*/
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
sourceDir: './bin',
|
|
||||||
outputDir: './dist',
|
|
||||||
outputFile: 'ecs-framework-bin.zip',
|
|
||||||
compressionLevel: 9, // 最高压缩级别
|
|
||||||
includeSourceMaps: false // 是否包含source map文件
|
|
||||||
};
|
|
||||||
|
|
||||||
async function createCompressedArchive() {
|
|
||||||
console.log('🗜️ 开始压缩 bin 目录...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 确保输出目录存在
|
|
||||||
if (!fs.existsSync(config.outputDir)) {
|
|
||||||
fs.mkdirSync(config.outputDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const outputPath = path.join(config.outputDir, config.outputFile);
|
|
||||||
const output = fs.createWriteStream(outputPath);
|
|
||||||
const archive = archiver('zip', {
|
|
||||||
zlib: { level: config.compressionLevel }
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听事件
|
|
||||||
output.on('close', () => {
|
|
||||||
const sizeKB = (archive.pointer() / 1024).toFixed(2);
|
|
||||||
console.log('✅ 压缩完成!');
|
|
||||||
console.log(`📄 输出文件: ${outputPath}`);
|
|
||||||
console.log(`📏 压缩后大小: ${sizeKB} KB`);
|
|
||||||
console.log(`📊 压缩了 ${archive.pointer()} 字节`);
|
|
||||||
|
|
||||||
// 生成压缩信息
|
|
||||||
generateCompressionInfo(outputPath, archive.pointer());
|
|
||||||
});
|
|
||||||
|
|
||||||
output.on('end', () => {
|
|
||||||
console.log('数据已全部写入');
|
|
||||||
});
|
|
||||||
|
|
||||||
archive.on('warning', (err) => {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
console.warn('⚠️ 警告:', err);
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
archive.on('error', (err) => {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 连接输出流
|
|
||||||
archive.pipe(output);
|
|
||||||
|
|
||||||
// 添加bin目录中的所有文件
|
|
||||||
console.log('📁 添加文件到压缩包...');
|
|
||||||
|
|
||||||
// 递归添加目录
|
|
||||||
archive.directory(config.sourceDir, false, (entry) => {
|
|
||||||
// 过滤文件
|
|
||||||
if (!config.includeSourceMaps && entry.name.endsWith('.map')) {
|
|
||||||
console.log(` ⏭️ 跳过: ${entry.name}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排除 .gitignore 文件
|
|
||||||
if (entry.name === '.gitignore' || entry.name.endsWith('/.gitignore')) {
|
|
||||||
console.log(` ⏭️ 跳过: ${entry.name}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(` ✓ 添加: ${entry.name}`);
|
|
||||||
return entry;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 完成压缩
|
|
||||||
await archive.finalize();
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ 压缩失败:', error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成压缩信息文件
|
|
||||||
*/
|
|
||||||
function generateCompressionInfo(outputPath, compressedSize) {
|
|
||||||
const originalSize = getDirectorySize(config.sourceDir);
|
|
||||||
const compressionRatio = ((originalSize - compressedSize) / originalSize * 100).toFixed(2);
|
|
||||||
|
|
||||||
const compressionInfo = {
|
|
||||||
name: '@esengine/ecs-framework',
|
|
||||||
version: require('../package.json').version,
|
|
||||||
compressionTime: new Date().toISOString(),
|
|
||||||
files: {
|
|
||||||
archive: config.outputFile
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
original: originalSize,
|
|
||||||
compressed: compressedSize,
|
|
||||||
ratio: `${compressionRatio}%`
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
compressionLevel: config.compressionLevel,
|
|
||||||
includeSourceMaps: config.includeSourceMaps
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const infoPath = path.join(config.outputDir, 'compression-info.json');
|
|
||||||
fs.writeFileSync(infoPath, JSON.stringify(compressionInfo, null, 2));
|
|
||||||
console.log(`📋 生成压缩信息: ${infoPath}`);
|
|
||||||
console.log(`📈 压缩率: ${compressionRatio}%`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取目录总大小
|
|
||||||
*/
|
|
||||||
function getDirectorySize(dirPath) {
|
|
||||||
let totalSize = 0;
|
|
||||||
|
|
||||||
function calculateSize(currentPath) {
|
|
||||||
const stats = fs.statSync(currentPath);
|
|
||||||
|
|
||||||
if (stats.isDirectory()) {
|
|
||||||
const files = fs.readdirSync(currentPath);
|
|
||||||
for (const file of files) {
|
|
||||||
calculateSize(path.join(currentPath, file));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 过滤source map文件
|
|
||||||
if (!config.includeSourceMaps && currentPath.endsWith('.map')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排除 .gitignore 文件
|
|
||||||
if (currentPath.endsWith('.gitignore')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalSize += stats.size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateSize(dirPath);
|
|
||||||
return totalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行压缩
|
|
||||||
if (require.main === module) {
|
|
||||||
createCompressedArchive();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { createCompressedArchive, config };
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* WASM模块导出
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './types';
|
|
||||||
export { WasmLoader } from './loader';
|
|
||||||
export { JavaScriptFallback } from './fallback';
|
|
||||||
export { WasmEcsCore } from './core';
|
|
||||||
export { ecsCore, initializeEcs } from './instance';
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
# Rust WebAssembly 编译指南
|
|
||||||
|
|
||||||
本指南将帮助您从零开始安装Rust环境并编译WASM模块。
|
|
||||||
|
|
||||||
## 📋 前置要求
|
|
||||||
|
|
||||||
- Windows 10/11 或 macOS/Linux
|
|
||||||
- 稳定的网络连接
|
|
||||||
- 管理员权限(用于安装软件)
|
|
||||||
|
|
||||||
## 🚀 第一步:安装 Rust
|
|
||||||
|
|
||||||
### Windows 用户
|
|
||||||
|
|
||||||
1. **下载 Rust 安装器**
|
|
||||||
- 访问 https://rustup.rs/
|
|
||||||
- 点击 "DOWNLOAD RUSTUP-INIT.EXE (64-BIT)"
|
|
||||||
- 或者直接下载:https://win.rustup.rs/x86_64
|
|
||||||
|
|
||||||
2. **运行安装器**
|
|
||||||
```cmd
|
|
||||||
# 下载后运行
|
|
||||||
rustup-init.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **选择安装选项**
|
|
||||||
- 出现提示时,选择 "1) Proceed with installation (default)"
|
|
||||||
- 等待安装完成
|
|
||||||
|
|
||||||
4. **重启命令行**
|
|
||||||
- 关闭当前命令行窗口
|
|
||||||
- 重新打开 cmd 或 PowerShell
|
|
||||||
|
|
||||||
### macOS/Linux 用户
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 使用官方安装脚本
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
||||||
|
|
||||||
# 重新加载环境变量
|
|
||||||
source ~/.cargo/env
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 第二步:安装 wasm-pack
|
|
||||||
|
|
||||||
wasm-pack 是编译 Rust 到 WebAssembly 的官方工具。
|
|
||||||
|
|
||||||
### Windows 用户
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
# 方法1:使用 cargo 安装(推荐)
|
|
||||||
cargo install wasm-pack
|
|
||||||
|
|
||||||
# 方法2:下载预编译版本
|
|
||||||
# 访问 https://github.com/rustwasm/wasm-pack/releases
|
|
||||||
# 下载最新的 Windows 版本
|
|
||||||
```
|
|
||||||
|
|
||||||
### macOS/Linux 用户
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 方法1:使用官方安装脚本
|
|
||||||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
|
||||||
|
|
||||||
# 方法2:使用 cargo 安装
|
|
||||||
cargo install wasm-pack
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✅ 第三步:验证安装
|
|
||||||
|
|
||||||
打开新的命令行窗口,运行以下命令验证安装:
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
# 检查 Rust 版本
|
|
||||||
rustc --version
|
|
||||||
|
|
||||||
# 检查 Cargo 版本
|
|
||||||
cargo --version
|
|
||||||
|
|
||||||
# 检查 wasm-pack 版本
|
|
||||||
wasm-pack --version
|
|
||||||
```
|
|
||||||
|
|
||||||
如果所有命令都能正常显示版本号,说明安装成功!
|
|
||||||
|
|
||||||
## 🏗️ 第四步:编译 WASM 模块
|
|
||||||
|
|
||||||
现在可以编译我们的 Rust WASM 模块了:
|
|
||||||
|
|
||||||
### 使用批处理文件(Windows 推荐)
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
# 进入项目目录
|
|
||||||
cd D:\project\ecs-framework\source\src\wasm\rust-ecs-core
|
|
||||||
|
|
||||||
# 运行批处理文件
|
|
||||||
build.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
### 使用命令行(跨平台)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 进入项目目录
|
|
||||||
cd source/src/wasm/rust-ecs-core
|
|
||||||
|
|
||||||
# 编译 WASM 模块
|
|
||||||
wasm-pack build --target web --out-dir pkg --release
|
|
||||||
```
|
|
||||||
|
|
||||||
### 编译选项说明
|
|
||||||
|
|
||||||
- `--target web`: 生成适用于浏览器的模块
|
|
||||||
- `--out-dir pkg`: 输出到 pkg 目录
|
|
||||||
- `--release`: 发布模式,启用优化
|
|
||||||
|
|
||||||
## 📦 第五步:验证编译结果
|
|
||||||
|
|
||||||
编译成功后,`pkg` 目录应该包含以下文件:
|
|
||||||
|
|
||||||
```
|
|
||||||
pkg/
|
|
||||||
├── ecs_wasm_core.js # JavaScript 绑定
|
|
||||||
├── ecs_wasm_core_bg.wasm # WebAssembly 二进制文件
|
|
||||||
├── ecs_wasm_core.d.ts # TypeScript 类型定义
|
|
||||||
├── package.json # NPM 包配置
|
|
||||||
└── README.md # 包说明
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🧪 第六步:测试 WASM 模块
|
|
||||||
|
|
||||||
创建一个简单的测试文件来验证模块是否正常工作:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>WASM ECS 测试</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Rust WASM ECS 测试</h1>
|
|
||||||
<div id="output"></div>
|
|
||||||
|
|
||||||
<script type="module">
|
|
||||||
import init, { EcsCore } from './pkg/ecs_wasm_core.js';
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
try {
|
|
||||||
// 初始化 WASM 模块
|
|
||||||
await init();
|
|
||||||
|
|
||||||
// 创建 ECS 核心实例
|
|
||||||
const ecs = new EcsCore();
|
|
||||||
|
|
||||||
// 创建实体
|
|
||||||
const entity = ecs.create_entity();
|
|
||||||
console.log('创建实体:', entity);
|
|
||||||
|
|
||||||
// 显示结果
|
|
||||||
document.getElementById('output').innerHTML =
|
|
||||||
`✅ WASM 模块加载成功!<br>创建的实体ID: ${entity}`;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('错误:', error);
|
|
||||||
document.getElementById('output').innerHTML =
|
|
||||||
`❌ 错误: ${error.message}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 故障排除
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
1. **"rustc 不是内部或外部命令"**
|
|
||||||
- 重启命令行窗口
|
|
||||||
- 检查环境变量是否正确设置
|
|
||||||
- 重新安装 Rust
|
|
||||||
|
|
||||||
2. **"wasm-pack 不是内部或外部命令"**
|
|
||||||
- 确保 wasm-pack 安装成功
|
|
||||||
- 重启命令行窗口
|
|
||||||
- 尝试使用 `cargo install wasm-pack` 重新安装
|
|
||||||
|
|
||||||
3. **编译错误**
|
|
||||||
- 检查 Rust 版本是否为最新稳定版
|
|
||||||
- 运行 `rustup update` 更新 Rust
|
|
||||||
- 检查网络连接,确保能下载依赖
|
|
||||||
|
|
||||||
4. **WASM 模块加载失败**
|
|
||||||
- 确保使用 HTTP 服务器而不是直接打开文件
|
|
||||||
- 检查浏览器是否支持 WebAssembly
|
|
||||||
- 查看浏览器控制台的错误信息
|
|
||||||
|
|
||||||
### 更新工具
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 更新 Rust
|
|
||||||
rustup update
|
|
||||||
|
|
||||||
# 更新 wasm-pack
|
|
||||||
cargo install wasm-pack --force
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 下一步
|
|
||||||
|
|
||||||
编译成功后,您可以:
|
|
||||||
|
|
||||||
1. 在项目中使用 `WasmLoader` 加载模块
|
|
||||||
2. 运行性能基准测试
|
|
||||||
3. 集成到您的游戏或应用中
|
|
||||||
|
|
||||||
## 📞 获取帮助
|
|
||||||
|
|
||||||
如果遇到问题,可以:
|
|
||||||
|
|
||||||
1. 查看 Rust 官方文档:https://doc.rust-lang.org/
|
|
||||||
2. 查看 wasm-pack 文档:https://rustwasm.github.io/wasm-pack/
|
|
||||||
3. 检查项目的 GitHub Issues
|
|
||||||
4. 在 Rust 社区寻求帮助:https://users.rust-lang.org/
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[target.wasm32-unknown-unknown]
|
|
||||||
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']
|
|
||||||
286
source/src/wasm/rust-ecs-core/Cargo.lock
generated
286
source/src/wasm/rust-ecs-core/Cargo.lock
generated
@@ -1,286 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.8.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"getrandom",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.18.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ecs-wasm-core"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"getrandom",
|
|
||||||
"js-sys",
|
|
||||||
"serde",
|
|
||||||
"serde-wasm-bindgen",
|
|
||||||
"smallvec",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"web-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"js-sys",
|
|
||||||
"libc",
|
|
||||||
"r-efi",
|
|
||||||
"wasi",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "js-sys"
|
|
||||||
version = "0.3.77"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.172"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.95"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.40"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde-wasm-bindgen"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
|
||||||
dependencies = [
|
|
||||||
"js-sys",
|
|
||||||
"serde",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.101"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.14.2+wasi-0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen-rt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"rustversion",
|
|
||||||
"wasm-bindgen-macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-backend"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"log",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"wasm-bindgen-macro-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-macro-support"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"wasm-bindgen-backend",
|
|
||||||
"wasm-bindgen-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-shared"
|
|
||||||
version = "0.2.100"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "web-sys"
|
|
||||||
version = "0.3.77"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
|
||||||
dependencies = [
|
|
||||||
"js-sys",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wit-bindgen-rt"
|
|
||||||
version = "0.39.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.25"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.25"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ecs-wasm-core"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
wasm-bindgen = "*"
|
|
||||||
js-sys = "*"
|
|
||||||
serde = { version = "*", features = ["derive"] }
|
|
||||||
serde-wasm-bindgen = "*"
|
|
||||||
|
|
||||||
# 高性能哈希表,避免getrandom版本冲突
|
|
||||||
ahash = "*"
|
|
||||||
# 使用标准库的HashMap,避免getrandom版本冲突
|
|
||||||
smallvec = "*" # 栈分配的小向量
|
|
||||||
# 为 WASM 环境配置 getrandom
|
|
||||||
getrandom = { version = "*", features = ["wasm_js"] }
|
|
||||||
|
|
||||||
[dependencies.web-sys]
|
|
||||||
version = "0.3"
|
|
||||||
features = [
|
|
||||||
"console",
|
|
||||||
"Performance",
|
|
||||||
"Window",
|
|
||||||
]
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
# 优化WASM二进制大小和性能
|
|
||||||
opt-level = 3
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release.package."*"]
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
# 配置 wasm-pack 行为
|
|
||||||
[package.metadata.wasm-pack.profile.release]
|
|
||||||
# 如果下载失败,可以暂时禁用 wasm-opt 优化
|
|
||||||
wasm-opt = false
|
|
||||||
# 或者指定本地 wasm-opt 路径(如果已安装)
|
|
||||||
# wasm-opt = ["-O4", "--enable-simd"]
|
|
||||||
|
|
||||||
# 配置网络超时和重试
|
|
||||||
[package.metadata.wasm-pack]
|
|
||||||
# 增加下载超时时间
|
|
||||||
timeout = 300
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo 正在构建 WASM 模块...
|
|
||||||
|
|
||||||
REM 方法1:尝试正常构建
|
|
||||||
echo 尝试正常构建...
|
|
||||||
wasm-pack build --target web --out-dir pkg --release
|
|
||||||
if %ERRORLEVEL% == 0 (
|
|
||||||
echo ✅ 构建成功!
|
|
||||||
goto :success
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ❌ 正常构建失败,尝试其他方法...
|
|
||||||
|
|
||||||
REM 方法2:设置代理(如果有的话)
|
|
||||||
REM set HTTPS_PROXY=http://127.0.0.1:7890
|
|
||||||
REM set HTTP_PROXY=http://127.0.0.1:7890
|
|
||||||
|
|
||||||
REM 方法3:禁用 wasm-opt 优化
|
|
||||||
echo 尝试禁用 wasm-opt 优化...
|
|
||||||
wasm-pack build --target web --out-dir pkg --release -- --no-default-features
|
|
||||||
if %ERRORLEVEL% == 0 (
|
|
||||||
echo ✅ 构建成功(已禁用优化)!
|
|
||||||
goto :success
|
|
||||||
)
|
|
||||||
|
|
||||||
REM 方法4:手动下载 binaryen
|
|
||||||
echo 尝试手动处理 binaryen...
|
|
||||||
if not exist "tools\binaryen" (
|
|
||||||
echo 请手动下载 binaryen 到 tools 目录
|
|
||||||
echo 下载地址: https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-windows.tar.gz
|
|
||||||
echo 或者使用国内镜像源
|
|
||||||
)
|
|
||||||
|
|
||||||
REM 方法5:使用环境变量跳过下载
|
|
||||||
echo 尝试跳过 binaryen 下载...
|
|
||||||
set WASM_PACK_CACHE_DISABLE=1
|
|
||||||
wasm-pack build --target web --out-dir pkg --release --mode no-install
|
|
||||||
if %ERRORLEVEL% == 0 (
|
|
||||||
echo ✅ 构建成功(跳过下载)!
|
|
||||||
goto :success
|
|
||||||
)
|
|
||||||
|
|
||||||
echo ❌ 所有方法都失败了
|
|
||||||
echo 建议:
|
|
||||||
echo 1. 检查网络连接
|
|
||||||
echo 2. 使用 VPN 或代理
|
|
||||||
echo 3. 手动下载 binaryen 工具
|
|
||||||
echo 4. 临时禁用 wasm-opt 优化
|
|
||||||
goto :end
|
|
||||||
|
|
||||||
:success
|
|
||||||
echo 🎉 WASM 模块构建完成!
|
|
||||||
echo 输出目录: pkg/
|
|
||||||
|
|
||||||
:end
|
|
||||||
pause
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
REM Rust WASM构建脚本 (Windows版本)
|
|
||||||
echo 开始构建Rust ECS WASM模块...
|
|
||||||
|
|
||||||
REM 检查是否安装了必要的工具
|
|
||||||
where wasm-pack >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo 错误:未找到wasm-pack,请先安装:
|
|
||||||
echo curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf ^| sh
|
|
||||||
echo 或者访问: https://rustwasm.github.io/wasm-pack/installer/
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM 检查是否安装了Rust
|
|
||||||
where rustc >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo 错误:未找到Rust,请先安装:
|
|
||||||
echo 访问: https://rustup.rs/
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM 清理之前的构建缓存
|
|
||||||
echo 清理之前的构建缓存...
|
|
||||||
if exist Cargo.lock del Cargo.lock
|
|
||||||
if exist target rmdir /s /q target
|
|
||||||
if exist pkg rmdir /s /q pkg
|
|
||||||
cargo clean
|
|
||||||
|
|
||||||
echo 更新依赖...
|
|
||||||
cargo update
|
|
||||||
|
|
||||||
REM 设置环境变量解决getrandom问题
|
|
||||||
set RUSTFLAGS=--cfg getrandom_backend="wasm_js"
|
|
||||||
|
|
||||||
REM 构建WASM模块
|
|
||||||
echo 正在编译WASM模块...
|
|
||||||
wasm-pack build --target web --out-dir pkg --release
|
|
||||||
|
|
||||||
REM 检查构建是否成功
|
|
||||||
if %errorlevel% equ 0 (
|
|
||||||
echo ✅ WASM模块构建成功!
|
|
||||||
echo 生成的文件位于 pkg/ 目录:
|
|
||||||
dir pkg
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo 📦 生成的文件说明:
|
|
||||||
echo - ecs_wasm_core.js: JavaScript绑定
|
|
||||||
echo - ecs_wasm_core_bg.wasm: WebAssembly二进制文件
|
|
||||||
echo - ecs_wasm_core.d.ts: TypeScript类型定义
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo 🚀 使用方法:
|
|
||||||
echo import init, { EcsCore } from './pkg/ecs_wasm_core.js';
|
|
||||||
echo await init^(^);
|
|
||||||
echo const ecs = new EcsCore^(^);
|
|
||||||
) else (
|
|
||||||
echo ❌ 构建失败!
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
pause
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Rust WASM构建脚本
|
|
||||||
echo "开始构建Rust ECS WASM模块..."
|
|
||||||
|
|
||||||
# 检查是否安装了必要的工具
|
|
||||||
if ! command -v wasm-pack &> /dev/null; then
|
|
||||||
echo "错误:未找到wasm-pack,请先安装:"
|
|
||||||
echo "curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 构建WASM模块
|
|
||||||
echo "正在编译WASM模块..."
|
|
||||||
wasm-pack build --target web --out-dir pkg --release
|
|
||||||
|
|
||||||
# 检查构建是否成功
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "✅ WASM模块构建成功!"
|
|
||||||
echo "生成的文件位于 pkg/ 目录:"
|
|
||||||
ls -la pkg/
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "📦 生成的文件说明:"
|
|
||||||
echo " - ecs_wasm_core.js: JavaScript绑定"
|
|
||||||
echo " - ecs_wasm_core_bg.wasm: WebAssembly二进制文件"
|
|
||||||
echo " - ecs_wasm_core.d.ts: TypeScript类型定义"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "🚀 使用方法:"
|
|
||||||
echo "import init, { EcsCore } from './pkg/ecs_wasm_core.js';"
|
|
||||||
echo "await init();"
|
|
||||||
echo "const ecs = new EcsCore();"
|
|
||||||
else
|
|
||||||
echo "❌ 构建失败!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use js_sys::Array;
|
|
||||||
|
|
||||||
mod query;
|
|
||||||
use query::QueryEngine;
|
|
||||||
|
|
||||||
// 当wasm-bindgen功能启用时,提供console.log绑定
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
fn log(s: &str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义一个宏来简化日志记录
|
|
||||||
macro_rules! console_log {
|
|
||||||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 实体ID类型
|
|
||||||
pub type EntityId = u32;
|
|
||||||
|
|
||||||
/// 组件掩码类型
|
|
||||||
pub type ComponentMask = u64;
|
|
||||||
|
|
||||||
/// 高性能ECS核心,专注于实体查询和掩码管理
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct EcsCore {
|
|
||||||
/// 查询引擎
|
|
||||||
query_engine: QueryEngine,
|
|
||||||
/// 下一个可用的实体ID
|
|
||||||
next_entity_id: EntityId,
|
|
||||||
/// 更新计数
|
|
||||||
update_count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl EcsCore {
|
|
||||||
/// 创建新的ECS核心
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
pub fn new() -> EcsCore {
|
|
||||||
EcsCore {
|
|
||||||
query_engine: QueryEngine::new(),
|
|
||||||
next_entity_id: 1,
|
|
||||||
update_count: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 创建新实体
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn create_entity(&mut self) -> EntityId {
|
|
||||||
let entity_id = self.next_entity_id;
|
|
||||||
self.next_entity_id += 1;
|
|
||||||
self.query_engine.add_entity(entity_id, 0);
|
|
||||||
entity_id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 删除实体
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn destroy_entity(&mut self, entity_id: EntityId) -> bool {
|
|
||||||
self.query_engine.remove_entity(entity_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 更新实体的组件掩码
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn update_entity_mask(&mut self, entity_id: EntityId, mask: ComponentMask) {
|
|
||||||
self.query_engine.update_entity_mask(entity_id, mask);
|
|
||||||
self.update_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 批量更新实体掩码
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn batch_update_masks(&mut self, entity_ids: &[u32], masks: &[u64]) {
|
|
||||||
self.query_engine.batch_update_masks(entity_ids, masks);
|
|
||||||
self.update_count += entity_ids.len() as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 查询实体
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn query_entities(&mut self, mask: ComponentMask, max_results: u32) -> *const u32 {
|
|
||||||
let results = self.query_engine.query_entities(mask, max_results as usize);
|
|
||||||
results.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取查询结果数量
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn get_query_result_count(&self) -> usize {
|
|
||||||
self.query_engine.get_last_query_result_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 缓存查询实体
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn query_cached(&mut self, mask: ComponentMask) -> *const u32 {
|
|
||||||
let results = self.query_engine.query_cached(mask);
|
|
||||||
results.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取缓存查询结果数量
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn get_cached_query_count(&mut self, mask: ComponentMask) -> usize {
|
|
||||||
self.query_engine.query_cached(mask).len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 多组件查询
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn query_multiple_components(&mut self, masks: &[u64], max_results: u32) -> *const u32 {
|
|
||||||
let results = self.query_engine.query_multiple_components(masks, max_results as usize);
|
|
||||||
results.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 排除查询
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn query_with_exclusion(&mut self, include_mask: ComponentMask, exclude_mask: ComponentMask, max_results: u32) -> *const u32 {
|
|
||||||
let results = self.query_engine.query_with_exclusion(include_mask, exclude_mask, max_results as usize);
|
|
||||||
results.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取实体的组件掩码
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn get_entity_mask(&self, entity_id: EntityId) -> ComponentMask {
|
|
||||||
self.query_engine.get_entity_mask(entity_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 检查实体是否存在
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn entity_exists(&self, entity_id: EntityId) -> bool {
|
|
||||||
self.query_engine.entity_exists(entity_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取实体数量
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn get_entity_count(&self) -> u32 {
|
|
||||||
self.query_engine.get_entity_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取性能统计信息
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn get_performance_stats(&self) -> Array {
|
|
||||||
let stats = Array::new();
|
|
||||||
stats.push(&JsValue::from(self.query_engine.get_entity_count())); // 实体数量
|
|
||||||
stats.push(&JsValue::from(self.query_engine.get_query_count())); // 查询次数
|
|
||||||
stats.push(&JsValue::from(self.update_count)); // 更新次数
|
|
||||||
stats
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 清理所有数据
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.query_engine.clear();
|
|
||||||
self.next_entity_id = 1;
|
|
||||||
self.update_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 重建查询缓存
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn rebuild_query_cache(&mut self) {
|
|
||||||
self.query_engine.force_rebuild_cache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 创建组件掩码的辅助函数
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn create_component_mask(component_ids: &[u32]) -> ComponentMask {
|
|
||||||
let mut mask = 0u64;
|
|
||||||
for &id in component_ids {
|
|
||||||
if id < 64 {
|
|
||||||
mask |= 1u64 << id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mask
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 检查掩码是否包含指定组件
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn mask_contains_component(mask: ComponentMask, component_id: u32) -> bool {
|
|
||||||
if component_id >= 64 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
(mask & (1u64 << component_id)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 初始化函数
|
|
||||||
#[wasm_bindgen(start)]
|
|
||||||
pub fn main() {
|
|
||||||
console_log!("Rust ECS WASM模块已加载");
|
|
||||||
}
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
use crate::{EntityId, ComponentMask};
|
|
||||||
use ahash::AHashMap;
|
|
||||||
|
|
||||||
/// 查询引擎,负责高性能的实体查询
|
|
||||||
pub struct QueryEngine {
|
|
||||||
/// 实体到组件掩码的映射
|
|
||||||
entity_masks: AHashMap<EntityId, ComponentMask>,
|
|
||||||
|
|
||||||
/// 常用查询掩码的缓存
|
|
||||||
cached_queries: AHashMap<ComponentMask, Vec<EntityId>>,
|
|
||||||
|
|
||||||
/// 查询结果缓冲区
|
|
||||||
query_buffer: Vec<EntityId>,
|
|
||||||
|
|
||||||
/// 缓存有效性标志
|
|
||||||
cache_dirty: bool,
|
|
||||||
|
|
||||||
/// 性能统计
|
|
||||||
query_count: u32,
|
|
||||||
|
|
||||||
/// 最后查询结果数量
|
|
||||||
last_query_result_count: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QueryEngine {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
QueryEngine {
|
|
||||||
entity_masks: AHashMap::with_capacity(50000),
|
|
||||||
cached_queries: AHashMap::with_capacity(32),
|
|
||||||
query_buffer: Vec::with_capacity(50000),
|
|
||||||
cache_dirty: true,
|
|
||||||
query_count: 0,
|
|
||||||
last_query_result_count: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 添加实体掩码
|
|
||||||
pub fn add_entity(&mut self, entity_id: EntityId, mask: ComponentMask) {
|
|
||||||
self.entity_masks.insert(entity_id, mask);
|
|
||||||
self.cache_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 移除实体
|
|
||||||
pub fn remove_entity(&mut self, entity_id: EntityId) -> bool {
|
|
||||||
if self.entity_masks.remove(&entity_id).is_some() {
|
|
||||||
self.cache_dirty = true;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 更新实体掩码
|
|
||||||
pub fn update_entity_mask(&mut self, entity_id: EntityId, mask: ComponentMask) {
|
|
||||||
if self.entity_masks.contains_key(&entity_id) {
|
|
||||||
self.entity_masks.insert(entity_id, mask);
|
|
||||||
self.cache_dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 批量更新实体掩码
|
|
||||||
pub fn batch_update_masks(&mut self, entity_ids: &[EntityId], masks: &[ComponentMask]) {
|
|
||||||
if entity_ids.len() != masks.len() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, &entity_id) in entity_ids.iter().enumerate() {
|
|
||||||
if self.entity_masks.contains_key(&entity_id) {
|
|
||||||
self.entity_masks.insert(entity_id, masks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cache_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 重建查询缓存
|
|
||||||
pub fn rebuild_cache(&mut self) {
|
|
||||||
if !self.cache_dirty {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空所有缓存
|
|
||||||
for cached_entities in self.cached_queries.values_mut() {
|
|
||||||
cached_entities.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重建所有缓存的查询
|
|
||||||
for (&query_mask, cached_entities) in &mut self.cached_queries {
|
|
||||||
for (&entity_id, &entity_mask) in &self.entity_masks {
|
|
||||||
if (entity_mask & query_mask) == query_mask {
|
|
||||||
cached_entities.push(entity_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cache_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 通用查询方法
|
|
||||||
pub fn query_entities(&mut self, mask: ComponentMask, max_results: usize) -> &[EntityId] {
|
|
||||||
self.query_buffer.clear();
|
|
||||||
self.query_count += 1;
|
|
||||||
|
|
||||||
for (&entity_id, &entity_mask) in &self.entity_masks {
|
|
||||||
if (entity_mask & mask) == mask {
|
|
||||||
self.query_buffer.push(entity_id);
|
|
||||||
if self.query_buffer.len() >= max_results {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.last_query_result_count = self.query_buffer.len();
|
|
||||||
&self.query_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 查询指定掩码的实体(带缓存)
|
|
||||||
pub fn query_cached(&mut self, mask: ComponentMask) -> &[EntityId] {
|
|
||||||
// 如果缓存中没有这个查询,添加它
|
|
||||||
if !self.cached_queries.contains_key(&mask) {
|
|
||||||
self.cached_queries.insert(mask, Vec::new());
|
|
||||||
self.cache_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.rebuild_cache();
|
|
||||||
self.query_count += 1;
|
|
||||||
|
|
||||||
self.cached_queries.get(&mask).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 多组件查询
|
|
||||||
pub fn query_multiple_components(&mut self, masks: &[ComponentMask], max_results: usize) -> &[EntityId] {
|
|
||||||
self.query_buffer.clear();
|
|
||||||
self.query_count += 1;
|
|
||||||
|
|
||||||
if masks.is_empty() {
|
|
||||||
return &self.query_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (&entity_id, &entity_mask) in &self.entity_masks {
|
|
||||||
let mut matches_all = true;
|
|
||||||
for &mask in masks {
|
|
||||||
if (entity_mask & mask) != mask {
|
|
||||||
matches_all = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches_all {
|
|
||||||
self.query_buffer.push(entity_id);
|
|
||||||
if self.query_buffer.len() >= max_results {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&self.query_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 带排除条件的查询
|
|
||||||
pub fn query_with_exclusion(&mut self, include_mask: ComponentMask, exclude_mask: ComponentMask, max_results: usize) -> &[EntityId] {
|
|
||||||
self.query_buffer.clear();
|
|
||||||
self.query_count += 1;
|
|
||||||
|
|
||||||
for (&entity_id, &entity_mask) in &self.entity_masks {
|
|
||||||
if (entity_mask & include_mask) == include_mask && (entity_mask & exclude_mask) == 0 {
|
|
||||||
self.query_buffer.push(entity_id);
|
|
||||||
if self.query_buffer.len() >= max_results {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&self.query_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取实体的组件掩码
|
|
||||||
pub fn get_entity_mask(&self, entity_id: EntityId) -> ComponentMask {
|
|
||||||
self.entity_masks.get(&entity_id).copied().unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 检查实体是否存在
|
|
||||||
pub fn entity_exists(&self, entity_id: EntityId) -> bool {
|
|
||||||
self.entity_masks.contains_key(&entity_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取实体数量
|
|
||||||
pub fn get_entity_count(&self) -> u32 {
|
|
||||||
self.entity_masks.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取查询统计
|
|
||||||
pub fn get_query_count(&self) -> u32 {
|
|
||||||
self.query_count
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取最后查询结果数量
|
|
||||||
pub fn get_last_query_result_count(&self) -> usize {
|
|
||||||
self.last_query_result_count
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 清理所有数据
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.entity_masks.clear();
|
|
||||||
self.cached_queries.clear();
|
|
||||||
self.query_buffer.clear();
|
|
||||||
|
|
||||||
self.cache_dirty = true;
|
|
||||||
self.query_count = 0;
|
|
||||||
self.last_query_result_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 强制重建查询缓存
|
|
||||||
pub fn force_rebuild_cache(&mut self) {
|
|
||||||
self.cache_dirty = true;
|
|
||||||
self.rebuild_cache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user