@esengine/ecs-framework 2.0.6 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/Core.js +21 -17
- package/bin/Core.js.map +1 -1
- package/bin/ECS/Component.js +5 -1
- package/bin/ECS/Component.js.map +1 -1
- package/bin/ECS/Components/IUpdatable.js +7 -2
- package/bin/ECS/Components/IUpdatable.js.map +1 -1
- package/bin/ECS/Components/SceneComponent.js +5 -1
- package/bin/ECS/Components/SceneComponent.js.map +1 -1
- package/bin/ECS/Core/BitMaskOptimizer.d.ts +75 -0
- package/bin/ECS/Core/BitMaskOptimizer.d.ts.map +1 -0
- package/bin/ECS/Core/BitMaskOptimizer.js +165 -0
- package/bin/ECS/Core/BitMaskOptimizer.js.map +1 -0
- package/bin/ECS/Core/ComponentPool.d.ts +72 -0
- package/bin/ECS/Core/ComponentPool.d.ts.map +1 -0
- package/bin/ECS/Core/ComponentPool.js +128 -0
- package/bin/ECS/Core/ComponentPool.js.map +1 -0
- package/bin/ECS/Core/ComponentStorage.js +9 -3
- package/bin/ECS/Core/ComponentStorage.js.map +1 -1
- package/bin/ECS/Core/EventSystem.js +12 -6
- package/bin/ECS/Core/EventSystem.js.map +1 -1
- package/bin/ECS/Core/FluentAPI.js +24 -17
- package/bin/ECS/Core/FluentAPI.js.map +1 -1
- package/bin/ECS/Core/IndexUpdateBatcher.d.ts +100 -0
- package/bin/ECS/Core/IndexUpdateBatcher.d.ts.map +1 -0
- package/bin/ECS/Core/IndexUpdateBatcher.js +223 -0
- package/bin/ECS/Core/IndexUpdateBatcher.js.map +1 -0
- package/bin/ECS/Core/QuerySystem.d.ts +292 -444
- package/bin/ECS/Core/QuerySystem.d.ts.map +1 -1
- package/bin/ECS/Core/QuerySystem.js +661 -1075
- package/bin/ECS/Core/QuerySystem.js.map +1 -1
- package/bin/ECS/CoreEvents.js +5 -2
- package/bin/ECS/CoreEvents.js.map +1 -1
- package/bin/ECS/Entity.js +17 -12
- package/bin/ECS/Entity.js.map +1 -1
- package/bin/ECS/Scene.d.ts +18 -2
- package/bin/ECS/Scene.d.ts.map +1 -1
- package/bin/ECS/Scene.js +67 -19
- package/bin/ECS/Scene.js.map +1 -1
- package/bin/ECS/Systems/EntitySystem.js +9 -5
- package/bin/ECS/Systems/EntitySystem.js.map +1 -1
- package/bin/ECS/Systems/IntervalSystem.js +8 -4
- package/bin/ECS/Systems/IntervalSystem.js.map +1 -1
- package/bin/ECS/Systems/PassiveSystem.js +6 -2
- package/bin/ECS/Systems/PassiveSystem.js.map +1 -1
- package/bin/ECS/Systems/ProcessingSystem.js +6 -2
- package/bin/ECS/Systems/ProcessingSystem.js.map +1 -1
- package/bin/ECS/Systems/index.js +11 -4
- package/bin/ECS/Systems/index.js.map +1 -1
- package/bin/ECS/Utils/Bits.js +5 -1
- package/bin/ECS/Utils/Bits.js.map +1 -1
- package/bin/ECS/Utils/ComponentTypeManager.js +8 -4
- package/bin/ECS/Utils/ComponentTypeManager.js.map +1 -1
- package/bin/ECS/Utils/EntityList.js +5 -1
- package/bin/ECS/Utils/EntityList.js.map +1 -1
- package/bin/ECS/Utils/EntityProcessorList.js +5 -1
- package/bin/ECS/Utils/EntityProcessorList.js.map +1 -1
- package/bin/ECS/Utils/IdentifierPool.js +5 -1
- package/bin/ECS/Utils/IdentifierPool.js.map +1 -1
- package/bin/ECS/Utils/Matcher.js +8 -4
- package/bin/ECS/Utils/Matcher.js.map +1 -1
- package/bin/ECS/Utils/index.js +15 -6
- package/bin/ECS/Utils/index.js.map +1 -1
- package/bin/ECS/index.js +27 -6
- package/bin/ECS/index.js.map +1 -1
- package/bin/Testing/Performance/benchmark.d.ts +6 -0
- package/bin/Testing/Performance/benchmark.d.ts.map +1 -0
- package/bin/Testing/Performance/benchmark.js +639 -0
- package/bin/Testing/Performance/benchmark.js.map +1 -0
- package/bin/Testing/Unit/bitmask-optimizer.test.d.ts +2 -0
- package/bin/Testing/Unit/bitmask-optimizer.test.d.ts.map +1 -0
- package/bin/Testing/Unit/bitmask-optimizer.test.js +164 -0
- package/bin/Testing/Unit/bitmask-optimizer.test.js.map +1 -0
- package/bin/Testing/Unit/component-pool.test.d.ts +5 -0
- package/bin/Testing/Unit/component-pool.test.d.ts.map +1 -0
- package/bin/Testing/Unit/component-pool.test.js +149 -0
- package/bin/Testing/Unit/component-pool.test.js.map +1 -0
- package/bin/Testing/test-runner.d.ts +51 -0
- package/bin/Testing/test-runner.d.ts.map +1 -0
- package/bin/Testing/test-runner.js +159 -0
- package/bin/Testing/test-runner.js.map +1 -0
- package/bin/Types/index.js +7 -4
- package/bin/Types/index.js.map +1 -1
- package/bin/Utils/Emitter.js +7 -2
- package/bin/Utils/Emitter.js.map +1 -1
- package/bin/Utils/Extensions/NumberExtension.js +5 -1
- package/bin/Utils/Extensions/NumberExtension.js.map +1 -1
- package/bin/Utils/Extensions/TypeUtils.js +5 -1
- package/bin/Utils/Extensions/TypeUtils.js.map +1 -1
- package/bin/Utils/Extensions/index.js +7 -2
- package/bin/Utils/Extensions/index.js.map +1 -1
- package/bin/Utils/GlobalManager.js +5 -1
- package/bin/Utils/GlobalManager.js.map +1 -1
- package/bin/Utils/PerformanceMonitor.js +7 -3
- package/bin/Utils/PerformanceMonitor.js.map +1 -1
- package/bin/Utils/Pool.js +9 -3
- package/bin/Utils/Pool.js.map +1 -1
- package/bin/Utils/Time.js +5 -1
- package/bin/Utils/Time.js.map +1 -1
- package/bin/Utils/Timers/ITimer.js +2 -1
- package/bin/Utils/Timers/Timer.js +7 -3
- package/bin/Utils/Timers/Timer.js.map +1 -1
- package/bin/Utils/Timers/TimerManager.js +8 -4
- package/bin/Utils/Timers/TimerManager.js.map +1 -1
- package/bin/Utils/WasmCore.d.ts +234 -0
- package/bin/Utils/WasmCore.d.ts.map +1 -0
- package/bin/Utils/WasmCore.js +634 -0
- package/bin/Utils/WasmCore.js.map +1 -0
- package/bin/Utils/index.d.ts +10 -0
- package/bin/Utils/index.d.ts.map +1 -1
- package/bin/Utils/index.js +36 -5
- package/bin/Utils/index.js.map +1 -1
- package/bin/index.d.ts +1 -2
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +42 -14
- package/bin/index.js.map +1 -1
- package/package.json +54 -74
- package/bin/Utils/AccelerationProvider.d.ts +0 -192
- package/bin/Utils/AccelerationProvider.d.ts.map +0 -1
- package/bin/Utils/AccelerationProvider.js +0 -261
- package/bin/Utils/AccelerationProvider.js.map +0 -1
- package/bin/Utils/WasmBridge.d.ts +0 -152
- package/bin/Utils/WasmBridge.d.ts.map +0 -1
- package/bin/Utils/WasmBridge.js +0 -336
- package/bin/Utils/WasmBridge.js.map +0 -1
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueryBuilder = exports.QuerySystem = exports.QueryConditionType = void 0;
|
|
4
|
+
const ComponentStorage_1 = require("./ComponentStorage");
|
|
5
|
+
const WasmCore_1 = require("../../Utils/WasmCore");
|
|
6
|
+
const ComponentPool_1 = require("./ComponentPool");
|
|
7
|
+
const BitMaskOptimizer_1 = require("./BitMaskOptimizer");
|
|
8
|
+
const IndexUpdateBatcher_1 = require("./IndexUpdateBatcher");
|
|
2
9
|
/**
|
|
3
10
|
* 查询条件类型
|
|
4
11
|
*/
|
|
5
|
-
|
|
12
|
+
var QueryConditionType;
|
|
6
13
|
(function (QueryConditionType) {
|
|
7
14
|
/** 必须包含所有指定组件 */
|
|
8
15
|
QueryConditionType["ALL"] = "all";
|
|
@@ -10,179 +17,241 @@ export var QueryConditionType;
|
|
|
10
17
|
QueryConditionType["ANY"] = "any";
|
|
11
18
|
/** 不能包含任何指定组件 */
|
|
12
19
|
QueryConditionType["NONE"] = "none";
|
|
13
|
-
})(QueryConditionType || (QueryConditionType = {}));
|
|
20
|
+
})(QueryConditionType || (exports.QueryConditionType = QueryConditionType = {}));
|
|
14
21
|
/**
|
|
15
22
|
* 高性能实体查询系统
|
|
16
|
-
*
|
|
23
|
+
*
|
|
24
|
+
* 提供快速的实体查询功能,支持按组件类型、标签、名称等多种方式查询实体。
|
|
25
|
+
* 系统采用多级索引和智能缓存机制,确保在大量实体场景下的查询性能。
|
|
26
|
+
*
|
|
27
|
+
* 主要特性:
|
|
28
|
+
* - 支持单组件和多组件查询
|
|
29
|
+
* - 自动索引管理和缓存优化
|
|
30
|
+
* - WebAssembly计算加速(如果可用)
|
|
31
|
+
* - 详细的性能统计信息
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // 查询所有包含Position和Velocity组件的实体
|
|
36
|
+
* const movingEntities = querySystem.queryAll(PositionComponent, VelocityComponent);
|
|
37
|
+
*
|
|
38
|
+
* // 查询特定标签的实体
|
|
39
|
+
* const playerEntities = querySystem.queryByTag(PLAYER_TAG);
|
|
40
|
+
* ```
|
|
17
41
|
*/
|
|
18
|
-
|
|
42
|
+
class QuerySystem {
|
|
19
43
|
constructor() {
|
|
20
44
|
this.entities = [];
|
|
45
|
+
this.wasmAvailable = false;
|
|
46
|
+
this.indexDirty = true;
|
|
47
|
+
// 查询缓存系统
|
|
21
48
|
this.queryCache = new Map();
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
|
|
25
|
-
this.
|
|
26
|
-
|
|
49
|
+
this.cacheMaxSize = 1000;
|
|
50
|
+
this.cacheTimeout = 5000; // 5秒缓存过期
|
|
51
|
+
// 性能统计
|
|
52
|
+
this.queryStats = {
|
|
53
|
+
totalQueries: 0,
|
|
54
|
+
cacheHits: 0,
|
|
55
|
+
indexHits: 0,
|
|
56
|
+
linearScans: 0
|
|
57
|
+
};
|
|
27
58
|
this.entityIndex = {
|
|
28
59
|
byMask: new Map(),
|
|
29
60
|
byComponentType: new Map(),
|
|
30
61
|
byTag: new Map(),
|
|
31
62
|
byName: new Map()
|
|
32
63
|
};
|
|
33
|
-
//
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
// 初始化优化组件
|
|
65
|
+
this.componentPoolManager = ComponentPool_1.ComponentPoolManager.getInstance();
|
|
66
|
+
this.bitMaskOptimizer = BitMaskOptimizer_1.BitMaskOptimizer.getInstance();
|
|
67
|
+
this.indexUpdateBatcher = new IndexUpdateBatcher_1.IndexUpdateBatcher();
|
|
68
|
+
// 设置索引更新批处理器的回调
|
|
69
|
+
this.indexUpdateBatcher.onBatchAdd = (entities) => {
|
|
70
|
+
for (const entity of entities) {
|
|
71
|
+
this.addEntityToIndexes(entity);
|
|
72
|
+
}
|
|
40
73
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
setEntities(entities) {
|
|
55
|
-
this.entities = entities;
|
|
56
|
-
this.invalidateIndexes();
|
|
57
|
-
this.clearCache();
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* 添加实体
|
|
61
|
-
* @param entity 实体
|
|
62
|
-
*/
|
|
63
|
-
addEntity(entity) {
|
|
64
|
-
this.pendingEntityAdds.push(entity);
|
|
65
|
-
this.scheduleBatchUpdate();
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* 批量添加实体
|
|
69
|
-
* @param entities 实体数组
|
|
70
|
-
*/
|
|
71
|
-
addEntities(entities) {
|
|
72
|
-
this.pendingEntityAdds.push(...entities);
|
|
73
|
-
this.scheduleBatchUpdate();
|
|
74
|
+
this.indexUpdateBatcher.onBatchRemove = (entities) => {
|
|
75
|
+
for (const entity of entities) {
|
|
76
|
+
this.removeEntityFromIndexes(entity);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
this.indexUpdateBatcher.onBatchUpdate = (updates) => {
|
|
80
|
+
for (const update of updates) {
|
|
81
|
+
this.removeEntityFromIndexes(update.entity);
|
|
82
|
+
this.addEntityToIndexes(update.entity);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
this.initializeWasm();
|
|
74
86
|
}
|
|
75
87
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
88
|
+
* 初始化WebAssembly支持
|
|
89
|
+
*
|
|
90
|
+
* 自动检测运行环境并启用WebAssembly计算加速。
|
|
91
|
+
* 如果WebAssembly不可用,系统将自动回退到JavaScript实现。
|
|
78
92
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
93
|
+
async initializeWasm() {
|
|
94
|
+
try {
|
|
95
|
+
const wasmLoaded = await WasmCore_1.ecsCore.initialize();
|
|
96
|
+
this.wasmAvailable = wasmLoaded && WasmCore_1.ecsCore.isUsingWasm();
|
|
97
|
+
if (this.wasmAvailable) {
|
|
98
|
+
console.log('QuerySystem: WebAssembly计算加速已启用');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log('QuerySystem: 使用JavaScript实现');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.warn('QuerySystem: WebAssembly初始化失败,使用JavaScript实现:', error);
|
|
106
|
+
this.wasmAvailable = false;
|
|
107
|
+
}
|
|
82
108
|
}
|
|
83
109
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
110
|
+
* 设置实体列表并重建索引
|
|
111
|
+
*
|
|
112
|
+
* 当实体集合发生大规模变化时调用此方法。
|
|
113
|
+
* 系统将重新构建所有索引以确保查询性能。
|
|
114
|
+
*
|
|
115
|
+
* @param entities 新的实体列表
|
|
86
116
|
*/
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
this.
|
|
117
|
+
setEntities(entities) {
|
|
118
|
+
this.entities = entities;
|
|
119
|
+
this.clearQueryCache();
|
|
120
|
+
this.rebuildIndexes();
|
|
90
121
|
}
|
|
91
122
|
/**
|
|
92
|
-
*
|
|
123
|
+
* 添加单个实体到查询系统
|
|
124
|
+
*
|
|
125
|
+
* 将新实体添加到查询系统中,并自动更新相关索引。
|
|
126
|
+
* 为了提高批量添加性能,可以延迟缓存清理。
|
|
127
|
+
*
|
|
128
|
+
* @param entity 要添加的实体
|
|
129
|
+
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
|
|
93
130
|
*/
|
|
94
|
-
|
|
95
|
-
if (!this.
|
|
96
|
-
this.
|
|
97
|
-
|
|
98
|
-
|
|
131
|
+
addEntity(entity, deferCacheClear = false) {
|
|
132
|
+
if (!this.entities.includes(entity)) {
|
|
133
|
+
this.entities.push(entity);
|
|
134
|
+
this.addEntityToIndexes(entity);
|
|
135
|
+
// 只有在非延迟模式下才立即清理缓存
|
|
136
|
+
if (!deferCacheClear) {
|
|
137
|
+
this.clearQueryCache();
|
|
138
|
+
}
|
|
99
139
|
}
|
|
100
140
|
}
|
|
101
141
|
/**
|
|
102
|
-
*
|
|
142
|
+
* 批量添加实体
|
|
143
|
+
*
|
|
144
|
+
* 高效地批量添加多个实体,减少缓存清理次数。
|
|
145
|
+
* 使用Set来避免O(n)的重复检查。
|
|
146
|
+
*
|
|
147
|
+
* @param entities 要添加的实体列表
|
|
103
148
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
149
|
+
addEntities(entities) {
|
|
150
|
+
if (entities.length === 0)
|
|
151
|
+
return;
|
|
152
|
+
// 使用Set来快速检查重复
|
|
153
|
+
const existingIds = new Set(this.entities.map(e => e.id));
|
|
154
|
+
let addedCount = 0;
|
|
155
|
+
for (const entity of entities) {
|
|
156
|
+
if (!existingIds.has(entity.id)) {
|
|
157
|
+
this.entities.push(entity);
|
|
111
158
|
this.addEntityToIndexes(entity);
|
|
159
|
+
existingIds.add(entity.id);
|
|
160
|
+
addedCount++;
|
|
112
161
|
}
|
|
113
|
-
this.pendingEntityAdds.length = 0;
|
|
114
162
|
}
|
|
115
|
-
//
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
// 从实体列表中移除
|
|
119
|
-
this.entities = this.entities.filter(entity => !removeSet.has(entity));
|
|
120
|
-
// 从索引中移除
|
|
121
|
-
for (const entity of this.pendingEntityRemoves) {
|
|
122
|
-
this.removeEntityFromIndexes(entity);
|
|
123
|
-
}
|
|
124
|
-
this.pendingEntityRemoves.length = 0;
|
|
163
|
+
// 只在有实体被添加时才清理缓存
|
|
164
|
+
if (addedCount > 0) {
|
|
165
|
+
this.clearQueryCache();
|
|
125
166
|
}
|
|
126
|
-
// 清空缓存
|
|
127
|
-
this.clearCache();
|
|
128
167
|
}
|
|
129
168
|
/**
|
|
130
|
-
*
|
|
169
|
+
* 批量添加实体(无重复检查版本)
|
|
170
|
+
*
|
|
171
|
+
* 假设所有实体都是新的,跳过重复检查以获得最大性能。
|
|
172
|
+
* 仅在确保没有重复实体时使用。
|
|
173
|
+
*
|
|
174
|
+
* @param entities 要添加的实体列表
|
|
131
175
|
*/
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
176
|
+
addEntitiesUnchecked(entities) {
|
|
177
|
+
if (entities.length === 0)
|
|
178
|
+
return;
|
|
179
|
+
// 避免调用栈溢出,分批添加
|
|
180
|
+
for (const entity of entities) {
|
|
181
|
+
this.entities.push(entity);
|
|
182
|
+
}
|
|
183
|
+
// 批量更新索引
|
|
184
|
+
for (const entity of entities) {
|
|
185
|
+
this.addEntityToIndexes(entity);
|
|
135
186
|
}
|
|
187
|
+
// 清理缓存
|
|
188
|
+
this.clearQueryCache();
|
|
136
189
|
}
|
|
137
190
|
/**
|
|
138
|
-
*
|
|
191
|
+
* 从查询系统移除实体
|
|
192
|
+
*
|
|
193
|
+
* 从查询系统中移除指定实体,并清理相关索引。
|
|
194
|
+
*
|
|
195
|
+
* @param entity 要移除的实体
|
|
139
196
|
*/
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
197
|
+
removeEntity(entity) {
|
|
198
|
+
const index = this.entities.indexOf(entity);
|
|
199
|
+
if (index !== -1) {
|
|
200
|
+
this.entities.splice(index, 1);
|
|
201
|
+
this.removeEntityFromIndexes(entity);
|
|
202
|
+
this.clearQueryCache();
|
|
203
|
+
}
|
|
146
204
|
}
|
|
147
205
|
/**
|
|
148
|
-
*
|
|
149
|
-
* @param entity 实体
|
|
206
|
+
* 将实体添加到各种索引中(优化版本)
|
|
150
207
|
*/
|
|
151
208
|
addEntityToIndexes(entity) {
|
|
152
|
-
// 按位掩码索引
|
|
153
209
|
const mask = entity.componentMask;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
210
|
+
// 组件掩码索引 - 优化Map操作
|
|
211
|
+
let maskSet = this.entityIndex.byMask.get(mask);
|
|
212
|
+
if (!maskSet) {
|
|
213
|
+
maskSet = new Set();
|
|
214
|
+
this.entityIndex.byMask.set(mask, maskSet);
|
|
215
|
+
}
|
|
216
|
+
maskSet.add(entity);
|
|
217
|
+
// 组件类型索引 - 批量处理
|
|
218
|
+
const components = entity.components;
|
|
219
|
+
for (let i = 0; i < components.length; i++) {
|
|
220
|
+
const componentType = components[i].constructor;
|
|
221
|
+
let typeSet = this.entityIndex.byComponentType.get(componentType);
|
|
222
|
+
if (!typeSet) {
|
|
223
|
+
typeSet = new Set();
|
|
224
|
+
this.entityIndex.byComponentType.set(componentType, typeSet);
|
|
163
225
|
}
|
|
164
|
-
|
|
226
|
+
typeSet.add(entity);
|
|
165
227
|
}
|
|
166
|
-
//
|
|
228
|
+
// 标签索引 - 只在有标签时处理
|
|
167
229
|
const tag = entity.tag;
|
|
168
|
-
if (
|
|
169
|
-
this.entityIndex.byTag.
|
|
230
|
+
if (tag !== undefined) {
|
|
231
|
+
let tagSet = this.entityIndex.byTag.get(tag);
|
|
232
|
+
if (!tagSet) {
|
|
233
|
+
tagSet = new Set();
|
|
234
|
+
this.entityIndex.byTag.set(tag, tagSet);
|
|
235
|
+
}
|
|
236
|
+
tagSet.add(entity);
|
|
170
237
|
}
|
|
171
|
-
|
|
172
|
-
// 按名称索引
|
|
238
|
+
// 名称索引 - 只在有名称时处理
|
|
173
239
|
const name = entity.name;
|
|
174
|
-
if (
|
|
175
|
-
this.entityIndex.byName.
|
|
240
|
+
if (name) {
|
|
241
|
+
let nameSet = this.entityIndex.byName.get(name);
|
|
242
|
+
if (!nameSet) {
|
|
243
|
+
nameSet = new Set();
|
|
244
|
+
this.entityIndex.byName.set(name, nameSet);
|
|
245
|
+
}
|
|
246
|
+
nameSet.add(entity);
|
|
176
247
|
}
|
|
177
|
-
this.entityIndex.byName.get(name).add(entity);
|
|
178
248
|
}
|
|
179
249
|
/**
|
|
180
|
-
*
|
|
181
|
-
* @param entity 实体
|
|
250
|
+
* 从各种索引中移除实体
|
|
182
251
|
*/
|
|
183
252
|
removeEntityFromIndexes(entity) {
|
|
184
|
-
// 从位掩码索引移除
|
|
185
253
|
const mask = entity.componentMask;
|
|
254
|
+
// 从组件掩码索引移除
|
|
186
255
|
const maskSet = this.entityIndex.byMask.get(mask);
|
|
187
256
|
if (maskSet) {
|
|
188
257
|
maskSet.delete(entity);
|
|
@@ -202,1155 +271,672 @@ export class QuerySystem {
|
|
|
202
271
|
}
|
|
203
272
|
}
|
|
204
273
|
// 从标签索引移除
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
tagSet
|
|
208
|
-
|
|
209
|
-
|
|
274
|
+
if (entity.tag !== undefined) {
|
|
275
|
+
const tagSet = this.entityIndex.byTag.get(entity.tag);
|
|
276
|
+
if (tagSet) {
|
|
277
|
+
tagSet.delete(entity);
|
|
278
|
+
if (tagSet.size === 0) {
|
|
279
|
+
this.entityIndex.byTag.delete(entity.tag);
|
|
280
|
+
}
|
|
210
281
|
}
|
|
211
282
|
}
|
|
212
283
|
// 从名称索引移除
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
nameSet
|
|
216
|
-
|
|
217
|
-
|
|
284
|
+
if (entity.name) {
|
|
285
|
+
const nameSet = this.entityIndex.byName.get(entity.name);
|
|
286
|
+
if (nameSet) {
|
|
287
|
+
nameSet.delete(entity);
|
|
288
|
+
if (nameSet.size === 0) {
|
|
289
|
+
this.entityIndex.byName.delete(entity.name);
|
|
290
|
+
}
|
|
218
291
|
}
|
|
219
292
|
}
|
|
220
293
|
}
|
|
221
294
|
/**
|
|
222
295
|
* 重建所有索引
|
|
296
|
+
*
|
|
297
|
+
* 清空并重新构建所有查询索引。
|
|
298
|
+
* 通常在大量实体变更后调用以确保索引一致性。
|
|
223
299
|
*/
|
|
224
300
|
rebuildIndexes() {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.
|
|
301
|
+
this.entityIndex.byMask.clear();
|
|
302
|
+
this.entityIndex.byComponentType.clear();
|
|
303
|
+
this.entityIndex.byTag.clear();
|
|
304
|
+
this.entityIndex.byName.clear();
|
|
228
305
|
for (const entity of this.entities) {
|
|
229
306
|
this.addEntityToIndexes(entity);
|
|
230
307
|
}
|
|
231
308
|
this.indexDirty = false;
|
|
232
|
-
this.lastIndexUpdate = Date.now();
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* 确保索引是最新的
|
|
236
|
-
*/
|
|
237
|
-
ensureIndexesUpdated() {
|
|
238
|
-
const now = Date.now();
|
|
239
|
-
if (this.indexDirty || (now - this.lastIndexUpdate) > this.indexUpdateThreshold) {
|
|
240
|
-
this.rebuildIndexes();
|
|
241
|
-
}
|
|
242
309
|
}
|
|
243
310
|
/**
|
|
244
311
|
* 查询包含所有指定组件的实体
|
|
245
|
-
*
|
|
246
|
-
*
|
|
312
|
+
*
|
|
313
|
+
* 返回同时包含所有指定组件类型的实体列表。
|
|
314
|
+
* 系统会自动选择最高效的查询策略,包括索引查找和缓存机制。
|
|
315
|
+
*
|
|
316
|
+
* @param componentTypes 要查询的组件类型列表
|
|
317
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* // 查询同时具有位置和速度组件的实体
|
|
322
|
+
* const result = querySystem.queryAll(PositionComponent, VelocityComponent);
|
|
323
|
+
* console.log(`找到 ${result.count} 个移动实体`);
|
|
324
|
+
* ```
|
|
247
325
|
*/
|
|
248
326
|
queryAll(...componentTypes) {
|
|
249
|
-
return this.query({
|
|
250
|
-
type: QueryConditionType.ALL,
|
|
251
|
-
componentTypes,
|
|
252
|
-
mask: this.createMask(componentTypes)
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* 查询包含任意指定组件的实体
|
|
257
|
-
* @param componentTypes 组件类型数组
|
|
258
|
-
* @returns 查询结果
|
|
259
|
-
*/
|
|
260
|
-
queryAny(...componentTypes) {
|
|
261
|
-
return this.query({
|
|
262
|
-
type: QueryConditionType.ANY,
|
|
263
|
-
componentTypes,
|
|
264
|
-
mask: this.createMask(componentTypes)
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* 查询不包含任何指定组件的实体
|
|
269
|
-
* @param componentTypes 组件类型数组
|
|
270
|
-
* @returns 查询结果
|
|
271
|
-
*/
|
|
272
|
-
queryNone(...componentTypes) {
|
|
273
|
-
return this.query({
|
|
274
|
-
type: QueryConditionType.NONE,
|
|
275
|
-
componentTypes,
|
|
276
|
-
mask: this.createMask(componentTypes)
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* 复合查询:同时满足多个条件
|
|
281
|
-
* @param conditions 查询条件数组
|
|
282
|
-
* @returns 查询结果
|
|
283
|
-
*/
|
|
284
|
-
queryComplex(...conditions) {
|
|
285
327
|
const startTime = performance.now();
|
|
286
|
-
|
|
287
|
-
|
|
328
|
+
this.queryStats.totalQueries++;
|
|
329
|
+
// 生成缓存键
|
|
330
|
+
const cacheKey = `all:${componentTypes.map(t => t.name).sort().join(',')}`;
|
|
288
331
|
// 检查缓存
|
|
289
|
-
const cached = this.
|
|
332
|
+
const cached = this.getFromCache(cacheKey);
|
|
290
333
|
if (cached) {
|
|
334
|
+
this.queryStats.cacheHits++;
|
|
291
335
|
return {
|
|
292
|
-
entities: cached
|
|
293
|
-
count: cached.
|
|
336
|
+
entities: cached,
|
|
337
|
+
count: cached.length,
|
|
294
338
|
executionTime: performance.now() - startTime,
|
|
295
339
|
fromCache: true
|
|
296
340
|
};
|
|
297
341
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
342
|
+
let entities;
|
|
343
|
+
// 单组件查询:直接使用索引
|
|
344
|
+
if (componentTypes.length === 1) {
|
|
345
|
+
this.queryStats.indexHits++;
|
|
346
|
+
entities = Array.from(this.entityIndex.byComponentType.get(componentTypes[0]) || []);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
// 多组件查询:使用高效算法
|
|
350
|
+
entities = this.queryMultipleComponents(componentTypes);
|
|
302
351
|
}
|
|
303
|
-
const executionTime = performance.now() - startTime;
|
|
304
352
|
// 缓存结果
|
|
305
|
-
this.
|
|
353
|
+
this.addToCache(cacheKey, entities);
|
|
306
354
|
return {
|
|
307
|
-
entities
|
|
308
|
-
count:
|
|
309
|
-
executionTime,
|
|
355
|
+
entities,
|
|
356
|
+
count: entities.length,
|
|
357
|
+
executionTime: performance.now() - startTime,
|
|
310
358
|
fromCache: false
|
|
311
359
|
};
|
|
312
360
|
}
|
|
313
361
|
/**
|
|
314
|
-
*
|
|
315
|
-
*
|
|
362
|
+
* 多组件查询算法
|
|
363
|
+
*
|
|
364
|
+
* 针对多组件查询场景的高效算法实现。
|
|
365
|
+
* 通过选择最小的组件集合作为起点,减少需要检查的实体数量。
|
|
366
|
+
*
|
|
367
|
+
* @param componentTypes 组件类型列表
|
|
368
|
+
* @returns 匹配的实体列表
|
|
316
369
|
*/
|
|
317
|
-
|
|
318
|
-
|
|
370
|
+
queryMultipleComponents(componentTypes) {
|
|
371
|
+
// 找到最小的组件集合作为起点
|
|
372
|
+
let smallestSet = null;
|
|
373
|
+
let smallestSize = Infinity;
|
|
374
|
+
for (const componentType of componentTypes) {
|
|
375
|
+
const set = this.entityIndex.byComponentType.get(componentType);
|
|
376
|
+
if (!set || set.size === 0) {
|
|
377
|
+
return []; // 如果任何组件没有实体,直接返回空结果
|
|
378
|
+
}
|
|
379
|
+
if (set.size < smallestSize) {
|
|
380
|
+
smallestSize = set.size;
|
|
381
|
+
smallestSet = set;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (!smallestSet) {
|
|
385
|
+
this.queryStats.linearScans++;
|
|
386
|
+
return this.queryByLinearScan(componentTypes);
|
|
387
|
+
}
|
|
388
|
+
// 从最小集合开始,逐步过滤
|
|
389
|
+
const mask = this.createComponentMask(componentTypes);
|
|
390
|
+
const result = [];
|
|
391
|
+
for (const entity of smallestSet) {
|
|
392
|
+
if ((entity.componentMask & mask) === mask) {
|
|
393
|
+
result.push(entity);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return result;
|
|
319
397
|
}
|
|
320
398
|
/**
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
399
|
+
* 线性扫描查询
|
|
400
|
+
*
|
|
401
|
+
* 当索引不可用时的备用查询方法。
|
|
402
|
+
* 遍历所有实体进行组件匹配检查。
|
|
403
|
+
*
|
|
404
|
+
* @param componentTypes 组件类型列表
|
|
405
|
+
* @returns 匹配的实体列表
|
|
324
406
|
*/
|
|
325
|
-
|
|
407
|
+
queryByLinearScan(componentTypes) {
|
|
408
|
+
const mask = this.createComponentMask(componentTypes);
|
|
409
|
+
return this.entities.filter(entity => (entity.componentMask & mask) === mask);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 查询包含任意指定组件的实体
|
|
413
|
+
*
|
|
414
|
+
* 返回包含任意一个指定组件类型的实体列表。
|
|
415
|
+
* 使用集合合并算法确保高效的查询性能。
|
|
416
|
+
*
|
|
417
|
+
* @param componentTypes 要查询的组件类型列表
|
|
418
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
419
|
+
*
|
|
420
|
+
* @example
|
|
421
|
+
* ```typescript
|
|
422
|
+
* // 查询具有武器或护甲组件的实体
|
|
423
|
+
* const result = querySystem.queryAny(WeaponComponent, ArmorComponent);
|
|
424
|
+
* console.log(`找到 ${result.count} 个装备实体`);
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
queryAny(...componentTypes) {
|
|
326
428
|
const startTime = performance.now();
|
|
327
|
-
this.
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
this.
|
|
429
|
+
this.queryStats.totalQueries++;
|
|
430
|
+
const cacheKey = `any:${componentTypes.map(t => t.name).sort().join(',')}`;
|
|
431
|
+
// 检查缓存
|
|
432
|
+
const cached = this.getFromCache(cacheKey);
|
|
433
|
+
if (cached) {
|
|
434
|
+
this.queryStats.cacheHits++;
|
|
435
|
+
return {
|
|
436
|
+
entities: cached,
|
|
437
|
+
count: cached.length,
|
|
438
|
+
executionTime: performance.now() - startTime,
|
|
439
|
+
fromCache: true
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
// 使用集合合并
|
|
443
|
+
const entitySet = new Set();
|
|
444
|
+
for (const componentType of componentTypes) {
|
|
445
|
+
const typeEntities = this.entityIndex.byComponentType.get(componentType);
|
|
446
|
+
if (typeEntities) {
|
|
447
|
+
for (const entity of typeEntities) {
|
|
448
|
+
entitySet.add(entity);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
const entities = Array.from(entitySet);
|
|
453
|
+
this.addToCache(cacheKey, entities);
|
|
331
454
|
return {
|
|
332
455
|
entities,
|
|
333
456
|
count: entities.length,
|
|
334
|
-
executionTime,
|
|
457
|
+
executionTime: performance.now() - startTime,
|
|
335
458
|
fromCache: false
|
|
336
459
|
};
|
|
337
460
|
}
|
|
338
461
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
462
|
+
* 查询不包含任何指定组件的实体
|
|
463
|
+
*
|
|
464
|
+
* 返回不包含任何指定组件类型的实体列表。
|
|
465
|
+
* 适用于排除特定类型实体的查询场景。
|
|
466
|
+
*
|
|
467
|
+
* @param componentTypes 要排除的组件类型列表
|
|
468
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```typescript
|
|
472
|
+
* // 查询不具有AI和玩家控制组件的实体(如静态物体)
|
|
473
|
+
* const result = querySystem.queryNone(AIComponent, PlayerControlComponent);
|
|
474
|
+
* console.log(`找到 ${result.count} 个静态实体`);
|
|
475
|
+
* ```
|
|
342
476
|
*/
|
|
343
|
-
|
|
477
|
+
queryNone(...componentTypes) {
|
|
344
478
|
const startTime = performance.now();
|
|
345
|
-
this.
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
this.
|
|
479
|
+
this.queryStats.totalQueries++;
|
|
480
|
+
const cacheKey = `none:${componentTypes.map(t => t.name).sort().join(',')}`;
|
|
481
|
+
// 检查缓存
|
|
482
|
+
const cached = this.getFromCache(cacheKey);
|
|
483
|
+
if (cached) {
|
|
484
|
+
this.queryStats.cacheHits++;
|
|
485
|
+
return {
|
|
486
|
+
entities: cached,
|
|
487
|
+
count: cached.length,
|
|
488
|
+
executionTime: performance.now() - startTime,
|
|
489
|
+
fromCache: true
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
const mask = this.createComponentMask(componentTypes);
|
|
493
|
+
const entities = this.entities.filter(entity => (entity.componentMask & mask) === BigInt(0));
|
|
494
|
+
this.addToCache(cacheKey, entities);
|
|
349
495
|
return {
|
|
350
496
|
entities,
|
|
351
497
|
count: entities.length,
|
|
352
|
-
executionTime,
|
|
498
|
+
executionTime: performance.now() - startTime,
|
|
353
499
|
fromCache: false
|
|
354
500
|
};
|
|
355
501
|
}
|
|
356
502
|
/**
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
503
|
+
* 按标签查询实体
|
|
504
|
+
*
|
|
505
|
+
* 返回具有指定标签的所有实体。
|
|
506
|
+
* 标签查询使用专用索引,具有很高的查询性能。
|
|
507
|
+
*
|
|
508
|
+
* @param tag 要查询的标签值
|
|
509
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
510
|
+
*
|
|
511
|
+
* @example
|
|
512
|
+
* ```typescript
|
|
513
|
+
* // 查询所有玩家实体
|
|
514
|
+
* const players = querySystem.queryByTag(PLAYER_TAG);
|
|
515
|
+
* ```
|
|
360
516
|
*/
|
|
361
|
-
|
|
517
|
+
queryByTag(tag) {
|
|
362
518
|
const startTime = performance.now();
|
|
363
|
-
this.
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
this.
|
|
519
|
+
this.queryStats.totalQueries++;
|
|
520
|
+
const cacheKey = `tag:${tag}`;
|
|
521
|
+
// 检查缓存
|
|
522
|
+
const cached = this.getFromCache(cacheKey);
|
|
523
|
+
if (cached) {
|
|
524
|
+
this.queryStats.cacheHits++;
|
|
525
|
+
return {
|
|
526
|
+
entities: cached,
|
|
527
|
+
count: cached.length,
|
|
528
|
+
executionTime: performance.now() - startTime,
|
|
529
|
+
fromCache: true
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
// 使用索引查询
|
|
533
|
+
this.queryStats.indexHits++;
|
|
534
|
+
const entities = Array.from(this.entityIndex.byTag.get(tag) || []);
|
|
535
|
+
// 缓存结果
|
|
536
|
+
this.addToCache(cacheKey, entities);
|
|
367
537
|
return {
|
|
368
538
|
entities,
|
|
369
539
|
count: entities.length,
|
|
370
|
-
executionTime,
|
|
540
|
+
executionTime: performance.now() - startTime,
|
|
371
541
|
fromCache: false
|
|
372
542
|
};
|
|
373
543
|
}
|
|
374
544
|
/**
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
545
|
+
* 按名称查询实体
|
|
546
|
+
*
|
|
547
|
+
* 返回具有指定名称的所有实体。
|
|
548
|
+
* 名称查询使用专用索引,适用于查找特定的命名实体。
|
|
549
|
+
*
|
|
550
|
+
* @param name 要查询的实体名称
|
|
551
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
* ```typescript
|
|
555
|
+
* // 查找名为"Player"的实体
|
|
556
|
+
* const player = querySystem.queryByName("Player");
|
|
557
|
+
* ```
|
|
378
558
|
*/
|
|
379
|
-
|
|
559
|
+
queryByName(name) {
|
|
380
560
|
const startTime = performance.now();
|
|
381
|
-
this.
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
let hasAllComponents = true;
|
|
394
|
-
for (const type of componentTypes) {
|
|
395
|
-
const component = entity.getComponent(type);
|
|
396
|
-
if (component === null) {
|
|
397
|
-
hasAllComponents = false;
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
entityComponents.push(component);
|
|
401
|
-
}
|
|
402
|
-
if (hasAllComponents) {
|
|
403
|
-
components.push(entityComponents);
|
|
404
|
-
}
|
|
561
|
+
this.queryStats.totalQueries++;
|
|
562
|
+
const cacheKey = `name:${name}`;
|
|
563
|
+
// 检查缓存
|
|
564
|
+
const cached = this.getFromCache(cacheKey);
|
|
565
|
+
if (cached) {
|
|
566
|
+
this.queryStats.cacheHits++;
|
|
567
|
+
return {
|
|
568
|
+
entities: cached,
|
|
569
|
+
count: cached.length,
|
|
570
|
+
executionTime: performance.now() - startTime,
|
|
571
|
+
fromCache: true
|
|
572
|
+
};
|
|
405
573
|
}
|
|
406
|
-
|
|
407
|
-
this.
|
|
574
|
+
// 使用索引查询
|
|
575
|
+
this.queryStats.indexHits++;
|
|
576
|
+
const entities = Array.from(this.entityIndex.byName.get(name) || []);
|
|
577
|
+
// 缓存结果
|
|
578
|
+
this.addToCache(cacheKey, entities);
|
|
408
579
|
return {
|
|
409
580
|
entities,
|
|
410
|
-
components,
|
|
411
581
|
count: entities.length,
|
|
412
|
-
executionTime,
|
|
582
|
+
executionTime: performance.now() - startTime,
|
|
413
583
|
fromCache: false
|
|
414
584
|
};
|
|
415
585
|
}
|
|
416
586
|
/**
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
*
|
|
430
|
-
* @param componentTypes 组件类型数组
|
|
431
|
-
* @param callback 回调函数
|
|
587
|
+
* 按单个组件类型查询实体
|
|
588
|
+
*
|
|
589
|
+
* 返回包含指定组件类型的所有实体。
|
|
590
|
+
* 这是最基础的查询方法,具有最高的查询性能。
|
|
591
|
+
*
|
|
592
|
+
* @param componentType 要查询的组件类型
|
|
593
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* // 查询所有具有位置组件的实体
|
|
598
|
+
* const entitiesWithPosition = querySystem.queryByComponent(PositionComponent);
|
|
599
|
+
* ```
|
|
432
600
|
*/
|
|
433
|
-
|
|
434
|
-
const result = this.queryAllTyped(...componentTypes);
|
|
435
|
-
for (let i = 0; i < result.entities.length; i++) {
|
|
436
|
-
callback(result.entities[i], result.components[i]);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* 查询单个组件类型的实体,返回类型安全的结果
|
|
441
|
-
* @param componentType 组件类型
|
|
442
|
-
* @returns 实体和组件的配对数组
|
|
443
|
-
*/
|
|
444
|
-
queryComponentTyped(componentType) {
|
|
445
|
-
const startTime = performance.now();
|
|
446
|
-
this.ensureIndexesUpdated();
|
|
447
|
-
const entities = Array.from(this.entityIndex.byComponentType.get(componentType) || []);
|
|
448
|
-
const result = [];
|
|
449
|
-
for (const entity of entities) {
|
|
450
|
-
const component = entity.getComponent(componentType);
|
|
451
|
-
if (component) {
|
|
452
|
-
result.push({ entity, component });
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
const executionTime = performance.now() - startTime;
|
|
456
|
-
this.updatePerformanceStats(executionTime, true);
|
|
457
|
-
return result;
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* 查询两个组件类型的实体,返回类型安全的结果
|
|
461
|
-
* @param componentType1 第一个组件类型
|
|
462
|
-
* @param componentType2 第二个组件类型
|
|
463
|
-
* @returns 实体和组件的配对数组
|
|
464
|
-
*/
|
|
465
|
-
queryTwoComponents(componentType1, componentType2) {
|
|
466
|
-
const result = this.queryAllTyped(componentType1, componentType2);
|
|
467
|
-
return result.entities.map((entity, index) => ({
|
|
468
|
-
entity,
|
|
469
|
-
component1: result.components[index][0],
|
|
470
|
-
component2: result.components[index][1]
|
|
471
|
-
}));
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* 查询三个组件类型的实体,返回类型安全的结果
|
|
475
|
-
* @param componentType1 第一个组件类型
|
|
476
|
-
* @param componentType2 第二个组件类型
|
|
477
|
-
* @param componentType3 第三个组件类型
|
|
478
|
-
* @returns 实体和组件的配对数组
|
|
479
|
-
*/
|
|
480
|
-
queryThreeComponents(componentType1, componentType2, componentType3) {
|
|
481
|
-
const result = this.queryAllTyped(componentType1, componentType2, componentType3);
|
|
482
|
-
return result.entities.map((entity, index) => ({
|
|
483
|
-
entity,
|
|
484
|
-
component1: result.components[index][0],
|
|
485
|
-
component2: result.components[index][1],
|
|
486
|
-
component3: result.components[index][2]
|
|
487
|
-
}));
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* 执行单个条件查询
|
|
491
|
-
* @param condition 查询条件
|
|
492
|
-
* @returns 查询结果
|
|
493
|
-
*/
|
|
494
|
-
query(condition) {
|
|
601
|
+
queryByComponent(componentType) {
|
|
495
602
|
const startTime = performance.now();
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
// 生成缓存键
|
|
499
|
-
const cacheKey = this.generateCacheKey(condition);
|
|
603
|
+
this.queryStats.totalQueries++;
|
|
604
|
+
const cacheKey = `component:${componentType.name}`;
|
|
500
605
|
// 检查缓存
|
|
501
|
-
const cached = this.
|
|
606
|
+
const cached = this.getFromCache(cacheKey);
|
|
502
607
|
if (cached) {
|
|
503
|
-
this.
|
|
608
|
+
this.queryStats.cacheHits++;
|
|
504
609
|
return {
|
|
505
|
-
entities: cached
|
|
506
|
-
count: cached.
|
|
610
|
+
entities: cached,
|
|
611
|
+
count: cached.length,
|
|
507
612
|
executionTime: performance.now() - startTime,
|
|
508
613
|
fromCache: true
|
|
509
614
|
};
|
|
510
615
|
}
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (!result) {
|
|
515
|
-
result = this.filterEntitiesByCondition(this.entities, condition);
|
|
516
|
-
}
|
|
517
|
-
const executionTime = performance.now() - startTime;
|
|
616
|
+
// 使用索引查询
|
|
617
|
+
this.queryStats.indexHits++;
|
|
618
|
+
const entities = Array.from(this.entityIndex.byComponentType.get(componentType) || []);
|
|
518
619
|
// 缓存结果
|
|
519
|
-
this.
|
|
520
|
-
// 更新性能统计
|
|
521
|
-
this.updatePerformanceStats(executionTime, false);
|
|
620
|
+
this.addToCache(cacheKey, entities);
|
|
522
621
|
return {
|
|
523
|
-
entities
|
|
524
|
-
count:
|
|
525
|
-
executionTime,
|
|
622
|
+
entities,
|
|
623
|
+
count: entities.length,
|
|
624
|
+
executionTime: performance.now() - startTime,
|
|
526
625
|
fromCache: false
|
|
527
626
|
};
|
|
528
627
|
}
|
|
529
628
|
/**
|
|
530
|
-
*
|
|
531
|
-
* @param condition 查询条件
|
|
532
|
-
* @returns 查询结果或null(如果无法优化)
|
|
629
|
+
* 从缓存获取查询结果
|
|
533
630
|
*/
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
if (
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (condition.type === QueryConditionType.ALL && condition.componentTypes.length > 1) {
|
|
543
|
-
let smallestSet = null;
|
|
544
|
-
let smallestSize = Infinity;
|
|
545
|
-
for (const componentType of condition.componentTypes) {
|
|
546
|
-
const entities = this.entityIndex.byComponentType.get(componentType);
|
|
547
|
-
if (!entities)
|
|
548
|
-
return []; // 如果任何组件都没有实体,结果为空
|
|
549
|
-
if (entities.size < smallestSize) {
|
|
550
|
-
smallestSize = entities.size;
|
|
551
|
-
smallestSet = entities;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
if (!smallestSet)
|
|
555
|
-
return [];
|
|
556
|
-
// 从最小集合开始,检查每个实体是否包含所有其他组件
|
|
557
|
-
const result = [];
|
|
558
|
-
for (const entity of smallestSet) {
|
|
559
|
-
if (this.matchesCondition(entity, condition)) {
|
|
560
|
-
result.push(entity);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return result;
|
|
564
|
-
}
|
|
565
|
-
// 对于ANY查询,合并所有相关组件的实体集合
|
|
566
|
-
if (condition.type === QueryConditionType.ANY) {
|
|
567
|
-
const entitySet = new Set();
|
|
568
|
-
for (const componentType of condition.componentTypes) {
|
|
569
|
-
const entities = this.entityIndex.byComponentType.get(componentType);
|
|
570
|
-
if (entities) {
|
|
571
|
-
for (const entity of entities) {
|
|
572
|
-
entitySet.add(entity);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return Array.from(entitySet);
|
|
577
|
-
}
|
|
578
|
-
// 对于NONE查询,从所有实体中排除包含指定组件的实体
|
|
579
|
-
if (condition.type === QueryConditionType.NONE) {
|
|
580
|
-
const excludeSet = new Set();
|
|
581
|
-
for (const componentType of condition.componentTypes) {
|
|
582
|
-
const entities = this.entityIndex.byComponentType.get(componentType);
|
|
583
|
-
if (entities) {
|
|
584
|
-
for (const entity of entities) {
|
|
585
|
-
excludeSet.add(entity);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
return this.entities.filter(entity => !excludeSet.has(entity));
|
|
631
|
+
getFromCache(cacheKey) {
|
|
632
|
+
const entry = this.queryCache.get(cacheKey);
|
|
633
|
+
if (!entry)
|
|
634
|
+
return null;
|
|
635
|
+
// 检查缓存是否过期
|
|
636
|
+
if (Date.now() - entry.timestamp > this.cacheTimeout) {
|
|
637
|
+
this.queryCache.delete(cacheKey);
|
|
638
|
+
return null;
|
|
590
639
|
}
|
|
591
|
-
|
|
640
|
+
entry.hitCount++;
|
|
641
|
+
return entry.entities;
|
|
592
642
|
}
|
|
593
643
|
/**
|
|
594
|
-
*
|
|
595
|
-
* @param executionTime 执行时间
|
|
596
|
-
* @param fromIndex 是否来自索引
|
|
644
|
+
* 添加查询结果到缓存
|
|
597
645
|
*/
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
this.performanceStats.averageExecutionTime = totalTime / this.performanceStats.totalQueries;
|
|
603
|
-
// 更新缓存命中率
|
|
604
|
-
this.performanceStats.cacheHitRate = this.cacheHits / (this.cacheHits + this.cacheMisses);
|
|
605
|
-
// 更新索引命中率
|
|
606
|
-
if (fromIndex) {
|
|
607
|
-
this.performanceStats.indexHitRate = (this.performanceStats.indexHitRate * (this.performanceStats.totalQueries - 1) + 1) / this.performanceStats.totalQueries;
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
this.performanceStats.indexHitRate = (this.performanceStats.indexHitRate * (this.performanceStats.totalQueries - 1)) / this.performanceStats.totalQueries;
|
|
611
|
-
}
|
|
612
|
-
// 记录慢查询
|
|
613
|
-
if (executionTime > 10) { // 超过10ms的查询被认为是慢查询
|
|
614
|
-
this.performanceStats.slowQueries.push({
|
|
615
|
-
query: `执行时间: ${executionTime.toFixed(2)}ms`,
|
|
616
|
-
executionTime,
|
|
617
|
-
timestamp: Date.now()
|
|
618
|
-
});
|
|
619
|
-
// 只保留最近的50个慢查询
|
|
620
|
-
if (this.performanceStats.slowQueries.length > 50) {
|
|
621
|
-
this.performanceStats.slowQueries.shift();
|
|
622
|
-
}
|
|
646
|
+
addToCache(cacheKey, entities) {
|
|
647
|
+
// 如果缓存已满,清理最少使用的条目
|
|
648
|
+
if (this.queryCache.size >= this.cacheMaxSize) {
|
|
649
|
+
this.cleanupCache();
|
|
623
650
|
}
|
|
651
|
+
this.queryCache.set(cacheKey, {
|
|
652
|
+
entities: [...entities], // 复制数组避免引用问题
|
|
653
|
+
timestamp: Date.now(),
|
|
654
|
+
hitCount: 0
|
|
655
|
+
});
|
|
624
656
|
}
|
|
625
657
|
/**
|
|
626
|
-
*
|
|
627
|
-
* @param entities 实体数组
|
|
628
|
-
* @param condition 查询条件
|
|
629
|
-
* @returns 过滤后的实体数组
|
|
658
|
+
* 清理缓存
|
|
630
659
|
*/
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
660
|
+
cleanupCache() {
|
|
661
|
+
// 移除过期的缓存条目
|
|
662
|
+
const now = Date.now();
|
|
663
|
+
for (const [key, entry] of this.queryCache.entries()) {
|
|
664
|
+
if (now - entry.timestamp > this.cacheTimeout) {
|
|
665
|
+
this.queryCache.delete(key);
|
|
636
666
|
}
|
|
637
667
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
*/
|
|
646
|
-
matchesCondition(entity, condition) {
|
|
647
|
-
const entityMask = entity.componentMask;
|
|
648
|
-
switch (condition.type) {
|
|
649
|
-
case QueryConditionType.ALL:
|
|
650
|
-
// 实体必须包含所有指定组件
|
|
651
|
-
return (entityMask & condition.mask) === condition.mask;
|
|
652
|
-
case QueryConditionType.ANY:
|
|
653
|
-
// 实体必须包含至少一个指定组件
|
|
654
|
-
return (entityMask & condition.mask) !== BigInt(0);
|
|
655
|
-
case QueryConditionType.NONE:
|
|
656
|
-
// 实体不能包含任何指定组件
|
|
657
|
-
return (entityMask & condition.mask) === BigInt(0);
|
|
658
|
-
default:
|
|
659
|
-
return false;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* 创建组件类型的位掩码
|
|
664
|
-
* @param componentTypes 组件类型数组
|
|
665
|
-
* @returns 位掩码
|
|
666
|
-
*/
|
|
667
|
-
createMask(componentTypes) {
|
|
668
|
-
let mask = BigInt(0);
|
|
669
|
-
for (const componentType of componentTypes) {
|
|
670
|
-
if (ComponentRegistry.isRegistered(componentType)) {
|
|
671
|
-
mask |= ComponentRegistry.getBitMask(componentType);
|
|
668
|
+
// 如果还是太满,移除最少使用的条目
|
|
669
|
+
if (this.queryCache.size >= this.cacheMaxSize) {
|
|
670
|
+
const entries = Array.from(this.queryCache.entries());
|
|
671
|
+
entries.sort((a, b) => a[1].hitCount - b[1].hitCount);
|
|
672
|
+
const toRemove = Math.floor(this.cacheMaxSize * 0.2); // 移除20%
|
|
673
|
+
for (let i = 0; i < toRemove && i < entries.length; i++) {
|
|
674
|
+
this.queryCache.delete(entries[i][0]);
|
|
672
675
|
}
|
|
673
676
|
}
|
|
674
|
-
return mask;
|
|
675
677
|
}
|
|
676
678
|
/**
|
|
677
|
-
*
|
|
678
|
-
* @param condition 查询条件
|
|
679
|
-
* @returns 缓存键
|
|
679
|
+
* 清除所有查询缓存
|
|
680
680
|
*/
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return `${condition.type}:${typeNames}`;
|
|
681
|
+
clearQueryCache() {
|
|
682
|
+
this.queryCache.clear();
|
|
684
683
|
}
|
|
685
684
|
/**
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
685
|
+
* 公共方法:清理查询缓存
|
|
686
|
+
*
|
|
687
|
+
* 用于外部调用清理缓存,通常在批量操作后使用。
|
|
689
688
|
*/
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
689
|
+
clearCache() {
|
|
690
|
+
this.clearQueryCache();
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* 批量更新实体组件
|
|
694
|
+
*
|
|
695
|
+
* 对大量实体进行批量组件更新操作。
|
|
696
|
+
* 当更新数量超过阈值时,系统会自动使用WebAssembly加速。
|
|
697
|
+
*
|
|
698
|
+
* @param updates 更新操作列表,包含实体ID和新的组件掩码
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```typescript
|
|
702
|
+
* // 批量更新实体的组件配置
|
|
703
|
+
* const updates = [
|
|
704
|
+
* { entityId: 1, componentMask: BigInt(0b1011) },
|
|
705
|
+
* { entityId: 2, componentMask: BigInt(0b1101) }
|
|
706
|
+
* ];
|
|
707
|
+
* querySystem.batchUpdateComponents(updates);
|
|
708
|
+
* ```
|
|
709
|
+
*/
|
|
710
|
+
batchUpdateComponents(updates) {
|
|
711
|
+
if (this.wasmAvailable && updates.length > 100) {
|
|
712
|
+
try {
|
|
713
|
+
const entityIds = updates.map(u => u.entityId);
|
|
714
|
+
const masks = updates.map(u => u.componentMask);
|
|
715
|
+
WasmCore_1.ecsCore.batchUpdateMasks(entityIds, masks);
|
|
716
|
+
console.log(`WebAssembly加速批量更新 ${updates.length} 个实体`);
|
|
717
|
+
}
|
|
718
|
+
catch (error) {
|
|
719
|
+
console.warn('WebAssembly批量更新失败,回退到JavaScript实现:', error);
|
|
720
|
+
this.batchUpdateComponentsJS(updates);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
this.batchUpdateComponentsJS(updates);
|
|
725
|
+
}
|
|
726
|
+
// 批量更新后清除缓存
|
|
727
|
+
this.clearQueryCache();
|
|
693
728
|
}
|
|
694
729
|
/**
|
|
695
|
-
*
|
|
696
|
-
* @param cacheKey 缓存键
|
|
697
|
-
* @returns 缓存项或null
|
|
730
|
+
* JavaScript实现的批量更新
|
|
698
731
|
*/
|
|
699
|
-
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
this.cacheHits++;
|
|
706
|
-
return cached;
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
// 缓存过期,删除
|
|
710
|
-
this.queryCache.delete(cacheKey);
|
|
732
|
+
batchUpdateComponentsJS(updates) {
|
|
733
|
+
for (const update of updates) {
|
|
734
|
+
const entity = this.entities.find(e => e.id === update.entityId);
|
|
735
|
+
if (entity) {
|
|
736
|
+
// 注意:componentMask是只读属性,实际应用中需要通过添加/移除组件来更新
|
|
737
|
+
console.log(`更新实体 ${update.entityId} 的组件掩码: ${update.componentMask}`);
|
|
711
738
|
}
|
|
712
739
|
}
|
|
713
|
-
this.
|
|
714
|
-
return null;
|
|
740
|
+
this.rebuildIndexes();
|
|
715
741
|
}
|
|
716
742
|
/**
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
*
|
|
743
|
+
* 获取加速状态信息
|
|
744
|
+
*
|
|
745
|
+
* 返回当前查询系统的加速状态和性能信息。
|
|
746
|
+
* 包括WebAssembly可用性、缓存统计等详细信息。
|
|
747
|
+
*
|
|
748
|
+
* @returns 加速状态信息对象
|
|
720
749
|
*/
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
this.
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
750
|
+
getAccelerationStatus() {
|
|
751
|
+
return {
|
|
752
|
+
wasmEnabled: this.wasmAvailable,
|
|
753
|
+
currentProvider: this.wasmAvailable ? 'hybrid' : 'javascript',
|
|
754
|
+
availableProviders: ['javascript', 'hybrid'],
|
|
755
|
+
performanceInfo: {
|
|
756
|
+
entityCount: this.entities.length,
|
|
757
|
+
wasmEnabled: this.wasmAvailable,
|
|
758
|
+
cacheStats: {
|
|
759
|
+
size: this.queryCache.size,
|
|
760
|
+
hitRate: this.queryStats.totalQueries > 0 ?
|
|
761
|
+
(this.queryStats.cacheHits / this.queryStats.totalQueries * 100).toFixed(2) + '%' : '0%'
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
};
|
|
731
765
|
}
|
|
732
766
|
/**
|
|
733
|
-
*
|
|
767
|
+
* 切换加速提供者
|
|
768
|
+
*
|
|
769
|
+
* 兼容性接口,保持向后兼容。
|
|
770
|
+
* 系统会自动选择最佳的实现方式。
|
|
771
|
+
*
|
|
772
|
+
* @param providerName 提供者名称
|
|
773
|
+
* @returns 是否切换成功
|
|
734
774
|
*/
|
|
735
|
-
|
|
736
|
-
|
|
775
|
+
async switchAccelerationProvider(providerName) {
|
|
776
|
+
return true;
|
|
737
777
|
}
|
|
738
778
|
/**
|
|
739
|
-
*
|
|
779
|
+
* 创建组件掩码
|
|
780
|
+
*
|
|
781
|
+
* 根据组件类型列表生成对应的位掩码。
|
|
782
|
+
* 使用位掩码优化器进行缓存和预计算。
|
|
783
|
+
*
|
|
784
|
+
* @param componentTypes 组件类型列表
|
|
785
|
+
* @returns 生成的位掩码
|
|
740
786
|
*/
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
leastUsedKey = key;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
if (leastUsedKey) {
|
|
751
|
-
this.queryCache.delete(leastUsedKey);
|
|
787
|
+
createComponentMask(componentTypes) {
|
|
788
|
+
// 使用位掩码优化器创建掩码
|
|
789
|
+
const componentNames = componentTypes.map(type => type.name);
|
|
790
|
+
// 确保组件类型已注册到优化器
|
|
791
|
+
for (const name of componentNames) {
|
|
792
|
+
this.bitMaskOptimizer.registerComponentType(name);
|
|
752
793
|
}
|
|
794
|
+
return this.bitMaskOptimizer.createCombinedMask(componentNames);
|
|
753
795
|
}
|
|
754
796
|
/**
|
|
755
|
-
*
|
|
797
|
+
* 获取系统统计信息
|
|
798
|
+
*
|
|
799
|
+
* 返回查询系统的详细统计信息,包括实体数量、索引状态、
|
|
800
|
+
* 查询性能统计等,用于性能监控和调试。
|
|
801
|
+
*
|
|
802
|
+
* @returns 系统统计信息对象
|
|
756
803
|
*/
|
|
757
804
|
getStats() {
|
|
758
|
-
const totalQueries = this.cacheHits + this.cacheMisses;
|
|
759
|
-
const hitRate = totalQueries > 0 ? this.cacheHits / totalQueries : 0;
|
|
760
805
|
return {
|
|
761
806
|
entityCount: this.entities.length,
|
|
762
|
-
cacheSize: this.queryCache.size,
|
|
763
|
-
cacheHits: this.cacheHits,
|
|
764
|
-
cacheMisses: this.cacheMisses,
|
|
765
|
-
hitRate,
|
|
766
|
-
maxCacheSize: this.maxCacheSize,
|
|
767
807
|
indexStats: {
|
|
768
808
|
maskIndexSize: this.entityIndex.byMask.size,
|
|
769
809
|
componentIndexSize: this.entityIndex.byComponentType.size,
|
|
770
810
|
tagIndexSize: this.entityIndex.byTag.size,
|
|
771
811
|
nameIndexSize: this.entityIndex.byName.size
|
|
772
812
|
},
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
*/
|
|
779
|
-
getPerformanceReport() {
|
|
780
|
-
const stats = this.getStats();
|
|
781
|
-
let report = '=== 查询系统性能报告 ===\n';
|
|
782
|
-
report += `实体数量: ${stats.entityCount}\n`;
|
|
783
|
-
report += `总查询次数: ${stats.performanceStats.totalQueries}\n`;
|
|
784
|
-
report += `平均执行时间: ${stats.performanceStats.averageExecutionTime.toFixed(2)}ms\n`;
|
|
785
|
-
report += `缓存命中率: ${(stats.performanceStats.cacheHitRate * 100).toFixed(1)}%\n`;
|
|
786
|
-
report += `索引命中率: ${(stats.performanceStats.indexHitRate * 100).toFixed(1)}%\n`;
|
|
787
|
-
report += '\n=== 索引统计 ===\n';
|
|
788
|
-
report += `位掩码索引: ${stats.indexStats.maskIndexSize} 项\n`;
|
|
789
|
-
report += `组件类型索引: ${stats.indexStats.componentIndexSize} 项\n`;
|
|
790
|
-
report += `标签索引: ${stats.indexStats.tagIndexSize} 项\n`;
|
|
791
|
-
report += `名称索引: ${stats.indexStats.nameIndexSize} 项\n`;
|
|
792
|
-
if (stats.performanceStats.slowQueries.length > 0) {
|
|
793
|
-
report += '\n=== 慢查询记录 ===\n';
|
|
794
|
-
stats.performanceStats.slowQueries.slice(-10).forEach((query, index) => {
|
|
795
|
-
const time = new Date(query.timestamp).toLocaleTimeString();
|
|
796
|
-
report += `${index + 1}. ${query.query} (${time})\n`;
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
return report;
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* 优化索引配置
|
|
803
|
-
*/
|
|
804
|
-
optimizeIndexes() {
|
|
805
|
-
// 根据查询模式优化索引更新频率
|
|
806
|
-
if (this.performanceStats.totalQueries > 1000) {
|
|
807
|
-
// 高频查询场景,降低索引更新阈值
|
|
808
|
-
this.indexUpdateThreshold = 50;
|
|
809
|
-
}
|
|
810
|
-
else {
|
|
811
|
-
// 低频查询场景,提高索引更新阈值
|
|
812
|
-
this.indexUpdateThreshold = 200;
|
|
813
|
-
}
|
|
814
|
-
// 根据缓存命中率调整缓存大小
|
|
815
|
-
if (this.performanceStats.cacheHitRate < 0.5 && this.maxCacheSize < 200) {
|
|
816
|
-
this.maxCacheSize = Math.min(200, this.maxCacheSize * 1.5);
|
|
817
|
-
}
|
|
818
|
-
else if (this.performanceStats.cacheHitRate > 0.9 && this.maxCacheSize > 50) {
|
|
819
|
-
this.maxCacheSize = Math.max(50, this.maxCacheSize * 0.8);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* 批量查询多个条件
|
|
824
|
-
* @param queries 查询条件数组
|
|
825
|
-
* @returns 查询结果数组
|
|
826
|
-
*/
|
|
827
|
-
batchQuery(queries) {
|
|
828
|
-
const startTime = performance.now();
|
|
829
|
-
this.ensureIndexesUpdated();
|
|
830
|
-
const results = [];
|
|
831
|
-
for (const query of queries) {
|
|
832
|
-
const result = this.query(query);
|
|
833
|
-
results.push(result);
|
|
834
|
-
}
|
|
835
|
-
const totalTime = performance.now() - startTime;
|
|
836
|
-
console.log(`批量查询 ${queries.length} 个条件,总耗时: ${totalTime.toFixed(2)}ms`);
|
|
837
|
-
return results;
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* 设置缓存配置
|
|
841
|
-
* @param maxSize 最大缓存大小
|
|
842
|
-
* @param timeout 缓存超时时间(毫秒)
|
|
843
|
-
*/
|
|
844
|
-
setCacheConfig(maxSize, timeout) {
|
|
845
|
-
this.maxCacheSize = maxSize;
|
|
846
|
-
this.cacheTimeout = timeout;
|
|
847
|
-
}
|
|
848
|
-
/**
|
|
849
|
-
* 重置统计信息
|
|
850
|
-
*/
|
|
851
|
-
resetStats() {
|
|
852
|
-
this.cacheHits = 0;
|
|
853
|
-
this.cacheMisses = 0;
|
|
854
|
-
this.performanceStats = {
|
|
855
|
-
totalQueries: 0,
|
|
856
|
-
averageExecutionTime: 0,
|
|
857
|
-
cacheHitRate: 0,
|
|
858
|
-
indexHitRate: 0,
|
|
859
|
-
slowQueries: []
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
/**
|
|
863
|
-
* 预热查询缓存
|
|
864
|
-
* @param commonQueries 常用查询条件数组
|
|
865
|
-
*/
|
|
866
|
-
warmUpCache(commonQueries) {
|
|
867
|
-
console.log('开始预热查询缓存...');
|
|
868
|
-
const startTime = performance.now();
|
|
869
|
-
for (const condition of commonQueries) {
|
|
870
|
-
this.query(condition);
|
|
871
|
-
}
|
|
872
|
-
const endTime = performance.now();
|
|
873
|
-
console.log(`缓存预热完成,耗时: ${(endTime - startTime).toFixed(2)}ms`);
|
|
874
|
-
}
|
|
875
|
-
/**
|
|
876
|
-
* 获取实体变更监听器
|
|
877
|
-
* @param condition 查询条件
|
|
878
|
-
* @param callback 变更回调
|
|
879
|
-
* @returns 取消监听的函数
|
|
880
|
-
*/
|
|
881
|
-
watchQuery(condition, callback) {
|
|
882
|
-
let lastResult = this.query(condition).entities;
|
|
883
|
-
const checkChanges = () => {
|
|
884
|
-
const currentResult = this.query(condition).entities;
|
|
885
|
-
// 检查新增的实体
|
|
886
|
-
const added = currentResult.filter(entity => !lastResult.includes(entity));
|
|
887
|
-
if (added.length > 0) {
|
|
888
|
-
callback(added, 'added');
|
|
889
|
-
}
|
|
890
|
-
// 检查移除的实体
|
|
891
|
-
const removed = lastResult.filter(entity => !currentResult.includes(entity));
|
|
892
|
-
if (removed.length > 0) {
|
|
893
|
-
callback(removed, 'removed');
|
|
813
|
+
accelerationStatus: this.getAccelerationStatus(),
|
|
814
|
+
queryStats: {
|
|
815
|
+
...this.queryStats,
|
|
816
|
+
cacheHitRate: this.queryStats.totalQueries > 0 ?
|
|
817
|
+
(this.queryStats.cacheHits / this.queryStats.totalQueries * 100).toFixed(2) + '%' : '0%'
|
|
894
818
|
}
|
|
895
|
-
lastResult = currentResult;
|
|
896
819
|
};
|
|
897
|
-
// 使用定时器定期检查变更(实际项目中可能需要更高效的实现)
|
|
898
|
-
const intervalId = setInterval(checkChanges, 100);
|
|
899
|
-
return () => {
|
|
900
|
-
clearInterval(intervalId);
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
/**
|
|
904
|
-
* 获取查询结果的快照
|
|
905
|
-
* @param condition 查询条件
|
|
906
|
-
* @returns 查询快照
|
|
907
|
-
*/
|
|
908
|
-
createSnapshot(condition) {
|
|
909
|
-
const result = this.query(condition);
|
|
910
|
-
return {
|
|
911
|
-
entities: [...result.entities], // 创建副本
|
|
912
|
-
timestamp: Date.now(),
|
|
913
|
-
condition: { ...condition }
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
/**
|
|
917
|
-
* 比较两个查询快照
|
|
918
|
-
* @param snapshot1 第一个快照
|
|
919
|
-
* @param snapshot2 第二个快照
|
|
920
|
-
* @returns 比较结果
|
|
921
|
-
*/
|
|
922
|
-
compareSnapshots(snapshot1, snapshot2) {
|
|
923
|
-
const set1 = new Set(snapshot1.entities);
|
|
924
|
-
const set2 = new Set(snapshot2.entities);
|
|
925
|
-
const added = snapshot2.entities.filter(entity => !set1.has(entity));
|
|
926
|
-
const removed = snapshot1.entities.filter(entity => !set2.has(entity));
|
|
927
|
-
const unchanged = snapshot1.entities.filter(entity => set2.has(entity));
|
|
928
|
-
return { added, removed, unchanged };
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* 执行并行查询
|
|
932
|
-
* @param conditions 查询条件数组
|
|
933
|
-
* @returns Promise<查询结果数组>
|
|
934
|
-
*/
|
|
935
|
-
async parallelQuery(conditions) {
|
|
936
|
-
const promises = conditions.map(condition => Promise.resolve(this.query(condition)));
|
|
937
|
-
return Promise.all(promises);
|
|
938
|
-
}
|
|
939
|
-
/**
|
|
940
|
-
* 获取查询建议
|
|
941
|
-
* @param entities 实体数组
|
|
942
|
-
* @returns 查询优化建议
|
|
943
|
-
*/
|
|
944
|
-
getQuerySuggestions(entities) {
|
|
945
|
-
const suggestions = [];
|
|
946
|
-
// 分析组件使用频率
|
|
947
|
-
const componentFrequency = new Map();
|
|
948
|
-
for (const entity of entities) {
|
|
949
|
-
for (const component of entity.components) {
|
|
950
|
-
const type = component.constructor;
|
|
951
|
-
componentFrequency.set(type, (componentFrequency.get(type) || 0) + 1);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
// 建议基于高频组件的查询
|
|
955
|
-
const sortedComponents = Array.from(componentFrequency.entries())
|
|
956
|
-
.sort((a, b) => b[1] - a[1])
|
|
957
|
-
.slice(0, 5);
|
|
958
|
-
for (const [componentType, count] of sortedComponents) {
|
|
959
|
-
suggestions.push(`考虑为组件 ${componentType.name} 创建专门的查询(使用频率: ${count})`);
|
|
960
|
-
}
|
|
961
|
-
// 建议缓存配置
|
|
962
|
-
if (this.performanceStats.cacheHitRate < 0.5) {
|
|
963
|
-
suggestions.push('缓存命中率较低,考虑增加缓存大小或调整缓存策略');
|
|
964
|
-
}
|
|
965
|
-
// 建议索引优化
|
|
966
|
-
if (this.performanceStats.indexHitRate < 0.7) {
|
|
967
|
-
suggestions.push('索引命中率较低,考虑重建索引或优化查询条件');
|
|
968
|
-
}
|
|
969
|
-
return suggestions;
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* 导出查询统计数据
|
|
973
|
-
* @returns 统计数据的JSON字符串
|
|
974
|
-
*/
|
|
975
|
-
exportStats() {
|
|
976
|
-
const stats = this.getStats();
|
|
977
|
-
return JSON.stringify(stats, null, 2);
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* 导入查询统计数据
|
|
981
|
-
* @param statsJson 统计数据的JSON字符串
|
|
982
|
-
*/
|
|
983
|
-
importStats(statsJson) {
|
|
984
|
-
try {
|
|
985
|
-
const stats = JSON.parse(statsJson);
|
|
986
|
-
this.cacheHits = stats.cacheHits || 0;
|
|
987
|
-
this.cacheMisses = stats.cacheMisses || 0;
|
|
988
|
-
this.performanceStats = stats.performanceStats || this.performanceStats;
|
|
989
|
-
}
|
|
990
|
-
catch (error) {
|
|
991
|
-
console.error('导入统计数据失败:', error);
|
|
992
|
-
}
|
|
993
820
|
}
|
|
994
821
|
}
|
|
822
|
+
exports.QuerySystem = QuerySystem;
|
|
995
823
|
/**
|
|
996
|
-
*
|
|
997
|
-
*
|
|
824
|
+
* 查询构建器
|
|
825
|
+
*
|
|
826
|
+
* 提供链式API来构建复杂的实体查询条件。
|
|
827
|
+
* 支持组合多种查询条件,创建灵活的查询表达式。
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```typescript
|
|
831
|
+
* const result = new QueryBuilder(querySystem)
|
|
832
|
+
* .withAll(PositionComponent, VelocityComponent)
|
|
833
|
+
* .without(DeadComponent)
|
|
834
|
+
* .execute();
|
|
835
|
+
* ```
|
|
998
836
|
*/
|
|
999
|
-
|
|
837
|
+
class QueryBuilder {
|
|
1000
838
|
constructor(querySystem) {
|
|
1001
839
|
this.conditions = [];
|
|
1002
840
|
this.querySystem = querySystem;
|
|
1003
841
|
}
|
|
1004
842
|
/**
|
|
1005
|
-
* 添加"
|
|
1006
|
-
*
|
|
1007
|
-
* @
|
|
843
|
+
* 添加"必须包含所有组件"条件
|
|
844
|
+
*
|
|
845
|
+
* @param componentTypes 必须包含的组件类型
|
|
846
|
+
* @returns 查询构建器实例,支持链式调用
|
|
1008
847
|
*/
|
|
1009
848
|
withAll(...componentTypes) {
|
|
1010
849
|
this.conditions.push({
|
|
1011
850
|
type: QueryConditionType.ALL,
|
|
1012
851
|
componentTypes,
|
|
1013
|
-
mask: this.
|
|
852
|
+
mask: this.createComponentMask(componentTypes)
|
|
1014
853
|
});
|
|
1015
854
|
return this;
|
|
1016
855
|
}
|
|
1017
856
|
/**
|
|
1018
|
-
* 添加"
|
|
1019
|
-
*
|
|
1020
|
-
* @
|
|
857
|
+
* 添加"必须包含任意组件"条件
|
|
858
|
+
*
|
|
859
|
+
* @param componentTypes 必须包含其中任意一个的组件类型
|
|
860
|
+
* @returns 查询构建器实例,支持链式调用
|
|
1021
861
|
*/
|
|
1022
862
|
withAny(...componentTypes) {
|
|
1023
863
|
this.conditions.push({
|
|
1024
864
|
type: QueryConditionType.ANY,
|
|
1025
865
|
componentTypes,
|
|
1026
|
-
mask: this.
|
|
866
|
+
mask: this.createComponentMask(componentTypes)
|
|
1027
867
|
});
|
|
1028
868
|
return this;
|
|
1029
869
|
}
|
|
1030
870
|
/**
|
|
1031
|
-
* 添加"
|
|
1032
|
-
*
|
|
1033
|
-
* @
|
|
871
|
+
* 添加"不能包含任何组件"条件
|
|
872
|
+
*
|
|
873
|
+
* @param componentTypes 不能包含的组件类型
|
|
874
|
+
* @returns 查询构建器实例,支持链式调用
|
|
1034
875
|
*/
|
|
1035
876
|
without(...componentTypes) {
|
|
1036
877
|
this.conditions.push({
|
|
1037
878
|
type: QueryConditionType.NONE,
|
|
1038
879
|
componentTypes,
|
|
1039
|
-
mask: this.
|
|
880
|
+
mask: this.createComponentMask(componentTypes)
|
|
1040
881
|
});
|
|
1041
882
|
return this;
|
|
1042
883
|
}
|
|
1043
884
|
/**
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
885
|
+
* 执行查询并返回结果
|
|
886
|
+
*
|
|
887
|
+
* 根据已添加的查询条件执行实体查询。
|
|
888
|
+
*
|
|
889
|
+
* @returns 查询结果,包含匹配的实体和性能信息
|
|
1046
890
|
*/
|
|
1047
891
|
execute() {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
if (this.tagFilter !== undefined) {
|
|
1052
|
-
result = this.querySystem.queryByTag(this.tagFilter);
|
|
1053
|
-
}
|
|
1054
|
-
else if (this.nameFilter !== undefined) {
|
|
1055
|
-
result = this.querySystem.queryByName(this.nameFilter);
|
|
1056
|
-
}
|
|
1057
|
-
else {
|
|
1058
|
-
return {
|
|
1059
|
-
entities: [],
|
|
1060
|
-
count: 0,
|
|
1061
|
-
executionTime: 0,
|
|
1062
|
-
fromCache: false
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
else if (this.conditions.length === 1) {
|
|
1067
|
-
// 单条件查询,使用优化路径
|
|
892
|
+
const startTime = performance.now();
|
|
893
|
+
// 简化实现:目前只支持单一条件
|
|
894
|
+
if (this.conditions.length === 1) {
|
|
1068
895
|
const condition = this.conditions[0];
|
|
1069
896
|
switch (condition.type) {
|
|
1070
897
|
case QueryConditionType.ALL:
|
|
1071
|
-
|
|
1072
|
-
break;
|
|
898
|
+
return this.querySystem.queryAll(...condition.componentTypes);
|
|
1073
899
|
case QueryConditionType.ANY:
|
|
1074
|
-
|
|
1075
|
-
break;
|
|
900
|
+
return this.querySystem.queryAny(...condition.componentTypes);
|
|
1076
901
|
case QueryConditionType.NONE:
|
|
1077
|
-
|
|
1078
|
-
break;
|
|
1079
|
-
default:
|
|
1080
|
-
return {
|
|
1081
|
-
entities: [],
|
|
1082
|
-
count: 0,
|
|
1083
|
-
executionTime: 0,
|
|
1084
|
-
fromCache: false
|
|
1085
|
-
};
|
|
902
|
+
return this.querySystem.queryNone(...condition.componentTypes);
|
|
1086
903
|
}
|
|
1087
904
|
}
|
|
1088
|
-
|
|
1089
|
-
// 多条件查询
|
|
1090
|
-
result = this.querySystem.queryComplex(...this.conditions);
|
|
1091
|
-
}
|
|
1092
|
-
// 应用额外的过滤条件
|
|
1093
|
-
let entities = result.entities;
|
|
1094
|
-
// 标签过滤
|
|
1095
|
-
if (this.tagFilter !== undefined) {
|
|
1096
|
-
entities = entities.filter(entity => entity.tag === this.tagFilter);
|
|
1097
|
-
}
|
|
1098
|
-
// 名称过滤
|
|
1099
|
-
if (this.nameFilter !== undefined) {
|
|
1100
|
-
entities = entities.filter(entity => entity.name === this.nameFilter);
|
|
1101
|
-
}
|
|
1102
|
-
// 排序
|
|
1103
|
-
if (this.sortFunction) {
|
|
1104
|
-
entities = [...entities].sort(this.sortFunction);
|
|
1105
|
-
}
|
|
1106
|
-
// 偏移
|
|
1107
|
-
if (this.offsetCount !== undefined && this.offsetCount > 0) {
|
|
1108
|
-
entities = entities.slice(this.offsetCount);
|
|
1109
|
-
}
|
|
1110
|
-
// 限制
|
|
1111
|
-
if (this.limitCount !== undefined && this.limitCount > 0) {
|
|
1112
|
-
entities = entities.slice(0, this.limitCount);
|
|
1113
|
-
}
|
|
905
|
+
// 多条件查询的复杂实现留待后续扩展
|
|
1114
906
|
return {
|
|
1115
|
-
entities,
|
|
1116
|
-
count:
|
|
1117
|
-
executionTime:
|
|
1118
|
-
fromCache:
|
|
907
|
+
entities: [],
|
|
908
|
+
count: 0,
|
|
909
|
+
executionTime: performance.now() - startTime,
|
|
910
|
+
fromCache: false
|
|
1119
911
|
};
|
|
1120
912
|
}
|
|
1121
913
|
/**
|
|
1122
|
-
*
|
|
1123
|
-
* @returns 实体或null
|
|
1124
|
-
*/
|
|
1125
|
-
first() {
|
|
1126
|
-
const result = this.execute();
|
|
1127
|
-
return result.entities.length > 0 ? result.entities[0] : null;
|
|
1128
|
-
}
|
|
1129
|
-
/**
|
|
1130
|
-
* 检查是否有匹配的实体
|
|
1131
|
-
* @returns 是否有匹配
|
|
1132
|
-
*/
|
|
1133
|
-
any() {
|
|
1134
|
-
const result = this.execute();
|
|
1135
|
-
return result.count > 0;
|
|
1136
|
-
}
|
|
1137
|
-
/**
|
|
1138
|
-
* 获取匹配实体的数量
|
|
1139
|
-
* @returns 实体数量
|
|
1140
|
-
*/
|
|
1141
|
-
count() {
|
|
1142
|
-
const result = this.execute();
|
|
1143
|
-
return result.count;
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* 对每个匹配的实体执行操作
|
|
1147
|
-
* @param callback 回调函数
|
|
1148
|
-
*/
|
|
1149
|
-
forEach(callback) {
|
|
1150
|
-
const result = this.execute();
|
|
1151
|
-
result.entities.forEach(callback);
|
|
1152
|
-
}
|
|
1153
|
-
/**
|
|
1154
|
-
* 转换为数组
|
|
1155
|
-
* @returns 实体数组
|
|
1156
|
-
*/
|
|
1157
|
-
toArray() {
|
|
1158
|
-
const result = this.execute();
|
|
1159
|
-
return result.entities;
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* 创建组件类型的位掩码
|
|
1163
|
-
* @param componentTypes 组件类型数组
|
|
1164
|
-
* @returns 位掩码
|
|
914
|
+
* 创建组件掩码
|
|
1165
915
|
*/
|
|
1166
|
-
|
|
916
|
+
createComponentMask(componentTypes) {
|
|
1167
917
|
let mask = BigInt(0);
|
|
1168
|
-
for (const
|
|
1169
|
-
|
|
1170
|
-
|
|
918
|
+
for (const type of componentTypes) {
|
|
919
|
+
try {
|
|
920
|
+
const bitMask = ComponentStorage_1.ComponentRegistry.getBitMask(type);
|
|
921
|
+
mask |= bitMask;
|
|
922
|
+
}
|
|
923
|
+
catch (error) {
|
|
924
|
+
console.warn(`组件类型 ${type.name} 未注册,跳过`);
|
|
1171
925
|
}
|
|
1172
926
|
}
|
|
1173
927
|
return mask;
|
|
1174
928
|
}
|
|
1175
929
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
930
|
+
* 重置查询构建器
|
|
931
|
+
*
|
|
932
|
+
* 清除所有已添加的查询条件,重新开始构建查询。
|
|
933
|
+
*
|
|
934
|
+
* @returns 查询构建器实例,支持链式调用
|
|
1178
935
|
*/
|
|
1179
936
|
reset() {
|
|
1180
|
-
this.conditions
|
|
1181
|
-
this.tagFilter = undefined;
|
|
1182
|
-
this.nameFilter = undefined;
|
|
1183
|
-
this.limitCount = undefined;
|
|
1184
|
-
this.offsetCount = undefined;
|
|
1185
|
-
this.sortFunction = undefined;
|
|
1186
|
-
return this;
|
|
1187
|
-
}
|
|
1188
|
-
/**
|
|
1189
|
-
* 添加标签过滤条件
|
|
1190
|
-
* @param tag 标签
|
|
1191
|
-
* @returns 查询构建器实例
|
|
1192
|
-
*/
|
|
1193
|
-
withTag(tag) {
|
|
1194
|
-
this.tagFilter = tag;
|
|
1195
|
-
return this;
|
|
1196
|
-
}
|
|
1197
|
-
/**
|
|
1198
|
-
* 添加名称过滤条件
|
|
1199
|
-
* @param name 名称
|
|
1200
|
-
* @returns 查询构建器实例
|
|
1201
|
-
*/
|
|
1202
|
-
withName(name) {
|
|
1203
|
-
this.nameFilter = name;
|
|
1204
|
-
return this;
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* 限制结果数量
|
|
1208
|
-
* @param limit 最大结果数量
|
|
1209
|
-
* @returns 查询构建器实例
|
|
1210
|
-
*/
|
|
1211
|
-
limit(limit) {
|
|
1212
|
-
this.limitCount = limit;
|
|
1213
|
-
return this;
|
|
1214
|
-
}
|
|
1215
|
-
/**
|
|
1216
|
-
* 跳过指定数量的结果
|
|
1217
|
-
* @param offset 跳过的数量
|
|
1218
|
-
* @returns 查询构建器实例
|
|
1219
|
-
*/
|
|
1220
|
-
offset(offset) {
|
|
1221
|
-
this.offsetCount = offset;
|
|
1222
|
-
return this;
|
|
1223
|
-
}
|
|
1224
|
-
/**
|
|
1225
|
-
* 对结果进行排序
|
|
1226
|
-
* @param compareFn 比较函数
|
|
1227
|
-
* @returns 查询构建器实例
|
|
1228
|
-
*/
|
|
1229
|
-
orderBy(compareFn) {
|
|
1230
|
-
this.sortFunction = compareFn;
|
|
1231
|
-
return this;
|
|
1232
|
-
}
|
|
1233
|
-
/**
|
|
1234
|
-
* 按更新顺序排序
|
|
1235
|
-
* @returns 查询构建器实例
|
|
1236
|
-
*/
|
|
1237
|
-
orderByUpdateOrder() {
|
|
1238
|
-
return this.orderBy((a, b) => a.updateOrder - b.updateOrder);
|
|
1239
|
-
}
|
|
1240
|
-
/**
|
|
1241
|
-
* 按ID排序
|
|
1242
|
-
* @returns 查询构建器实例
|
|
1243
|
-
*/
|
|
1244
|
-
orderById() {
|
|
1245
|
-
return this.orderBy((a, b) => a.id - b.id);
|
|
1246
|
-
}
|
|
1247
|
-
/**
|
|
1248
|
-
* 按名称排序
|
|
1249
|
-
* @returns 查询构建器实例
|
|
1250
|
-
*/
|
|
1251
|
-
orderByName() {
|
|
1252
|
-
return this.orderBy((a, b) => a.name.localeCompare(b.name));
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
|
-
* 克隆查询构建器
|
|
1256
|
-
* @returns 新的查询构建器
|
|
1257
|
-
*/
|
|
1258
|
-
clone() {
|
|
1259
|
-
const newBuilder = new QueryBuilder(this.querySystem);
|
|
1260
|
-
newBuilder.conditions = this.conditions.map(c => ({ ...c }));
|
|
1261
|
-
newBuilder.tagFilter = this.tagFilter;
|
|
1262
|
-
newBuilder.nameFilter = this.nameFilter;
|
|
1263
|
-
newBuilder.limitCount = this.limitCount;
|
|
1264
|
-
newBuilder.offsetCount = this.offsetCount;
|
|
1265
|
-
newBuilder.sortFunction = this.sortFunction;
|
|
1266
|
-
return newBuilder;
|
|
1267
|
-
}
|
|
1268
|
-
/**
|
|
1269
|
-
* 添加自定义过滤器
|
|
1270
|
-
* @param predicate 过滤谓词
|
|
1271
|
-
* @returns 查询构建器实例
|
|
1272
|
-
*/
|
|
1273
|
-
filter(predicate) {
|
|
1274
|
-
const originalExecute = this.execute.bind(this);
|
|
1275
|
-
this.execute = () => {
|
|
1276
|
-
const result = originalExecute();
|
|
1277
|
-
const filteredEntities = result.entities.filter(predicate);
|
|
1278
|
-
return {
|
|
1279
|
-
...result,
|
|
1280
|
-
entities: filteredEntities,
|
|
1281
|
-
count: filteredEntities.length
|
|
1282
|
-
};
|
|
1283
|
-
};
|
|
937
|
+
this.conditions = [];
|
|
1284
938
|
return this;
|
|
1285
939
|
}
|
|
1286
|
-
/**
|
|
1287
|
-
* 映射查询结果
|
|
1288
|
-
* @param mapper 映射函数
|
|
1289
|
-
* @returns 映射后的结果数组
|
|
1290
|
-
*/
|
|
1291
|
-
map(mapper) {
|
|
1292
|
-
const result = this.execute();
|
|
1293
|
-
return result.entities.map(mapper);
|
|
1294
|
-
}
|
|
1295
|
-
/**
|
|
1296
|
-
* 查找第一个满足条件的实体
|
|
1297
|
-
* @param predicate 查找谓词
|
|
1298
|
-
* @returns 实体或null
|
|
1299
|
-
*/
|
|
1300
|
-
find(predicate) {
|
|
1301
|
-
const result = this.execute();
|
|
1302
|
-
if (predicate) {
|
|
1303
|
-
return result.entities.find(predicate) || null;
|
|
1304
|
-
}
|
|
1305
|
-
return result.entities[0] || null;
|
|
1306
|
-
}
|
|
1307
|
-
/**
|
|
1308
|
-
* 检查是否所有实体都满足条件
|
|
1309
|
-
* @param predicate 检查谓词
|
|
1310
|
-
* @returns 是否所有实体都满足条件
|
|
1311
|
-
*/
|
|
1312
|
-
every(predicate) {
|
|
1313
|
-
const result = this.execute();
|
|
1314
|
-
return result.entities.every(predicate);
|
|
1315
|
-
}
|
|
1316
|
-
/**
|
|
1317
|
-
* 检查是否有实体满足条件
|
|
1318
|
-
* @param predicate 检查谓词
|
|
1319
|
-
* @returns 是否有实体满足条件
|
|
1320
|
-
*/
|
|
1321
|
-
some(predicate) {
|
|
1322
|
-
const result = this.execute();
|
|
1323
|
-
return result.entities.some(predicate);
|
|
1324
|
-
}
|
|
1325
|
-
/**
|
|
1326
|
-
* 获取查询的调试信息
|
|
1327
|
-
* @returns 调试信息字符串
|
|
1328
|
-
*/
|
|
1329
|
-
getDebugInfo() {
|
|
1330
|
-
let info = '=== 查询构建器调试信息 ===\n';
|
|
1331
|
-
if (this.conditions.length > 0) {
|
|
1332
|
-
info += '组件条件:\n';
|
|
1333
|
-
this.conditions.forEach((condition, index) => {
|
|
1334
|
-
const typeNames = condition.componentTypes.map(t => t.name).join(', ');
|
|
1335
|
-
info += ` ${index + 1}. ${condition.type}: [${typeNames}]\n`;
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
if (this.tagFilter !== undefined) {
|
|
1339
|
-
info += `标签过滤: ${this.tagFilter}\n`;
|
|
1340
|
-
}
|
|
1341
|
-
if (this.nameFilter !== undefined) {
|
|
1342
|
-
info += `名称过滤: ${this.nameFilter}\n`;
|
|
1343
|
-
}
|
|
1344
|
-
if (this.limitCount !== undefined) {
|
|
1345
|
-
info += `限制数量: ${this.limitCount}\n`;
|
|
1346
|
-
}
|
|
1347
|
-
if (this.offsetCount !== undefined) {
|
|
1348
|
-
info += `偏移量: ${this.offsetCount}\n`;
|
|
1349
|
-
}
|
|
1350
|
-
if (this.sortFunction) {
|
|
1351
|
-
info += '已设置排序函数\n';
|
|
1352
|
-
}
|
|
1353
|
-
return info;
|
|
1354
|
-
}
|
|
1355
940
|
}
|
|
941
|
+
exports.QueryBuilder = QueryBuilder;
|
|
1356
942
|
//# sourceMappingURL=QuerySystem.js.map
|