feat(core): 启用 TypeScript 最严格的类型检查 (#199)
* feat(core): 启用 TypeScript 最严格的类型检查 * ci: 配置 Codecov 以适应类型安全改进 * fix(core): 修复 CodeQL 安全警告 * fix(core): eslint.config.mjs
This commit is contained in:
50
packages/core/eslint.config.mjs
Normal file
50
packages/core/eslint.config.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default [
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['src/**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'semi': ['error', 'always'],
|
||||
'quotes': ['error', 'single', { avoidEscape: true }],
|
||||
'indent': ['error', 4, { SwitchCase: 1 }],
|
||||
'no-trailing-spaces': 'error',
|
||||
'eol-last': ['error', 'always'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'arrow-parens': ['error', 'always'],
|
||||
'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }],
|
||||
'no-console': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'warn',
|
||||
'@typescript-eslint/no-unsafe-call': 'warn',
|
||||
'@typescript-eslint/no-unsafe-return': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||
'@typescript-eslint/no-non-null-assertion': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'node_modules/**',
|
||||
'dist/**',
|
||||
'bin/**',
|
||||
'coverage/**',
|
||||
'**/*.min.js',
|
||||
'**/*.d.ts'
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -42,7 +42,7 @@ module.exports = {
|
||||
verbose: true,
|
||||
transform: {
|
||||
'^.+\\.tsx?$': ['ts-jest', {
|
||||
tsconfig: 'tsconfig.json',
|
||||
tsconfig: 'tsconfig.test.json',
|
||||
useESM: false,
|
||||
}],
|
||||
},
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
"test:performance": "jest --config jest.performance.config.cjs",
|
||||
"test:coverage": "jest --coverage --config jest.config.cjs",
|
||||
"test:ci": "jest --ci --coverage --config jest.config.cjs",
|
||||
"test:clear": "jest --clearCache"
|
||||
"test:clear": "jest --clearCache",
|
||||
"type-check": "tsc --noEmit",
|
||||
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
||||
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix"
|
||||
},
|
||||
"author": "yhh",
|
||||
"license": "MIT",
|
||||
@@ -54,6 +57,9 @@
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^20.19.17",
|
||||
"@eslint/js": "^9.37.0",
|
||||
"eslint": "^9.37.0",
|
||||
"typescript-eslint": "^8.46.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"rimraf": "^5.0.0",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Time } from './Utils/Time';
|
||||
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
||||
import { PoolManager } from './Utils/Pool/PoolManager';
|
||||
import { DebugManager } from './Utils/Debug';
|
||||
import { ICoreConfig, IECSDebugConfig, IUpdatable, isUpdatable } from './Types';
|
||||
import { ICoreConfig, IECSDebugConfig } from './Types';
|
||||
import { createLogger } from './Utils/Logger';
|
||||
import { SceneManager } from './ECS/SceneManager';
|
||||
import { IScene } from './ECS/IScene';
|
||||
@@ -502,7 +502,7 @@ export class Core {
|
||||
|
||||
if (this._instance._debugManager) {
|
||||
this._instance._debugManager.stop();
|
||||
this._instance._debugManager = undefined;
|
||||
delete this._instance._debugManager;
|
||||
}
|
||||
|
||||
// 更新Core配置
|
||||
|
||||
@@ -7,20 +7,14 @@
|
||||
import type { ServiceContainer } from '../ServiceContainer';
|
||||
import type { IService, ServiceType } from '../ServiceContainer';
|
||||
|
||||
/**
|
||||
* 依赖注入元数据键
|
||||
*/
|
||||
const INJECTABLE_METADATA_KEY = Symbol('injectable');
|
||||
const INJECT_METADATA_KEY = Symbol('inject');
|
||||
const INJECT_PARAMS_METADATA_KEY = Symbol('inject:params');
|
||||
const UPDATABLE_METADATA_KEY = Symbol('updatable');
|
||||
|
||||
/**
|
||||
* 依赖注入元数据存储
|
||||
*/
|
||||
const injectableMetadata = new WeakMap<any, InjectableMetadata>();
|
||||
const injectMetadata = new WeakMap<any, Map<number, ServiceType<any> | string | symbol>>();
|
||||
const updatableMetadata = new WeakMap<any, UpdatableMetadata>();
|
||||
type Constructor = abstract new (...args: unknown[]) => unknown;
|
||||
|
||||
const injectableMetadata = new WeakMap<Constructor, InjectableMetadata>();
|
||||
const injectMetadata = new WeakMap<Constructor, Map<number, ServiceType<IService> | string | symbol>>();
|
||||
const updatableMetadata = new WeakMap<Constructor, UpdatableMetadata>();
|
||||
|
||||
/**
|
||||
* 可注入元数据接口
|
||||
@@ -34,13 +28,13 @@ export interface InjectableMetadata {
|
||||
/**
|
||||
* 依赖列表
|
||||
*/
|
||||
dependencies: Array<ServiceType<any> | string | symbol>;
|
||||
dependencies: Array<ServiceType<IService> | string | symbol>;
|
||||
|
||||
/**
|
||||
* 属性注入映射
|
||||
* key: 属性名, value: 服务类型
|
||||
*/
|
||||
properties?: Map<string | symbol, ServiceType<any>>;
|
||||
properties?: Map<string | symbol, ServiceType<IService>>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,17 +76,15 @@ export interface UpdatableMetadata {
|
||||
* ```
|
||||
*/
|
||||
export function Injectable(): ClassDecorator {
|
||||
return function <T extends Function>(target: T): T {
|
||||
const existing = injectableMetadata.get(target);
|
||||
return function (target: Function): void {
|
||||
const existing = injectableMetadata.get(target as Constructor);
|
||||
|
||||
injectableMetadata.set(target, {
|
||||
injectableMetadata.set(target as Constructor, {
|
||||
injectable: true,
|
||||
dependencies: [],
|
||||
properties: existing?.properties
|
||||
...(existing?.properties && { properties: existing.properties })
|
||||
});
|
||||
|
||||
return target;
|
||||
};
|
||||
} as ClassDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,9 +117,9 @@ export function Injectable(): ClassDecorator {
|
||||
* ```
|
||||
*/
|
||||
export function Updatable(priority: number = 0): ClassDecorator {
|
||||
return function <T extends Function>(target: T): T {
|
||||
return function (target: Function): void {
|
||||
// 验证类原型上是否有update方法
|
||||
const prototype = (target as any).prototype;
|
||||
const prototype = (target as Constructor & { prototype: { update?: unknown } }).prototype;
|
||||
if (!prototype || typeof prototype.update !== 'function') {
|
||||
throw new Error(
|
||||
`@Updatable() decorator requires class ${target.name} to implement IUpdatable interface with update() method. ` +
|
||||
@@ -136,13 +128,11 @@ export function Updatable(priority: number = 0): ClassDecorator {
|
||||
}
|
||||
|
||||
// 标记为可更新
|
||||
updatableMetadata.set(target, {
|
||||
updatableMetadata.set(target as Constructor, {
|
||||
updatable: true,
|
||||
priority
|
||||
});
|
||||
|
||||
return target;
|
||||
};
|
||||
} as ClassDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,13 +142,13 @@ export function Updatable(priority: number = 0): ClassDecorator {
|
||||
*
|
||||
* @param serviceType 服务类型标识符
|
||||
*/
|
||||
export function Inject(serviceType: ServiceType<any> | string | symbol): ParameterDecorator {
|
||||
return function (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) {
|
||||
export function Inject(serviceType: ServiceType<IService> | string | symbol): ParameterDecorator {
|
||||
return function (target: object, _propertyKey: string | symbol | undefined, parameterIndex: number) {
|
||||
// 获取或创建注入元数据
|
||||
let params = injectMetadata.get(target);
|
||||
let params = injectMetadata.get(target as Constructor);
|
||||
if (!params) {
|
||||
params = new Map();
|
||||
injectMetadata.set(target, params);
|
||||
injectMetadata.set(target as Constructor, params);
|
||||
}
|
||||
|
||||
// 记录参数索引和服务类型的映射
|
||||
@@ -175,16 +165,16 @@ export function Inject(serviceType: ServiceType<any> | string | symbol): Paramet
|
||||
*
|
||||
* @param serviceType 服务类型
|
||||
*/
|
||||
export function InjectProperty(serviceType: ServiceType<any>): PropertyDecorator {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
let metadata = injectableMetadata.get(target.constructor);
|
||||
export function InjectProperty(serviceType: ServiceType<IService>): PropertyDecorator {
|
||||
return function (target: object, propertyKey: string | symbol) {
|
||||
let metadata = injectableMetadata.get((target as { constructor: Constructor }).constructor);
|
||||
|
||||
if (!metadata) {
|
||||
metadata = {
|
||||
injectable: true,
|
||||
dependencies: []
|
||||
};
|
||||
injectableMetadata.set(target.constructor, metadata);
|
||||
injectableMetadata.set((target as { constructor: Constructor }).constructor, metadata);
|
||||
}
|
||||
|
||||
if (!metadata.properties) {
|
||||
@@ -201,7 +191,7 @@ export function InjectProperty(serviceType: ServiceType<any>): PropertyDecorator
|
||||
* @param target 目标类
|
||||
* @returns 是否可注入
|
||||
*/
|
||||
export function isInjectable(target: any): boolean {
|
||||
export function isInjectable(target: Constructor): boolean {
|
||||
const metadata = injectableMetadata.get(target);
|
||||
return metadata?.injectable ?? false;
|
||||
}
|
||||
@@ -212,7 +202,7 @@ export function isInjectable(target: any): boolean {
|
||||
* @param target 目标类
|
||||
* @returns 依赖注入元数据
|
||||
*/
|
||||
export function getInjectableMetadata(target: any): InjectableMetadata | undefined {
|
||||
export function getInjectableMetadata(target: Constructor): InjectableMetadata | undefined {
|
||||
return injectableMetadata.get(target);
|
||||
}
|
||||
|
||||
@@ -222,7 +212,7 @@ export function getInjectableMetadata(target: any): InjectableMetadata | undefin
|
||||
* @param target 目标类
|
||||
* @returns 参数索引到服务类型的映射
|
||||
*/
|
||||
export function getInjectMetadata(target: any): Map<number, ServiceType<any> | string | symbol> {
|
||||
export function getInjectMetadata(target: Constructor): Map<number, ServiceType<IService> | string | symbol> {
|
||||
return injectMetadata.get(target) || new Map();
|
||||
}
|
||||
|
||||
@@ -243,10 +233,10 @@ export function createInstance<T>(
|
||||
container: ServiceContainer
|
||||
): T {
|
||||
// 获取参数注入元数据
|
||||
const injectParams = getInjectMetadata(constructor);
|
||||
const injectParams = getInjectMetadata(constructor as Constructor);
|
||||
|
||||
// 解析依赖
|
||||
const dependencies: any[] = [];
|
||||
const dependencies: unknown[] = [];
|
||||
|
||||
// 获取构造函数参数数量
|
||||
const paramCount = constructor.length;
|
||||
@@ -264,7 +254,7 @@ export function createInstance<T>(
|
||||
);
|
||||
} else {
|
||||
// 类类型
|
||||
dependencies.push(container.resolve(serviceType as ServiceType<any>));
|
||||
dependencies.push(container.resolve(serviceType as ServiceType<IService>));
|
||||
}
|
||||
} else {
|
||||
// 没有@Inject标记,传入undefined
|
||||
@@ -282,8 +272,8 @@ export function createInstance<T>(
|
||||
* @param instance 目标实例
|
||||
* @param container 服务容器
|
||||
*/
|
||||
export function injectProperties<T>(instance: T, container: ServiceContainer): void {
|
||||
const constructor = (instance as any).constructor;
|
||||
export function injectProperties<T extends object>(instance: T, container: ServiceContainer): void {
|
||||
const constructor = (instance as { constructor: Constructor }).constructor;
|
||||
const metadata = getInjectableMetadata(constructor);
|
||||
|
||||
if (!metadata?.properties || metadata.properties.size === 0) {
|
||||
@@ -294,7 +284,7 @@ export function injectProperties<T>(instance: T, container: ServiceContainer): v
|
||||
const service = container.resolve(serviceType);
|
||||
|
||||
if (service !== null) {
|
||||
(instance as any)[propertyKey] = service;
|
||||
(instance as Record<string | symbol, unknown>)[propertyKey] = service;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +295,7 @@ export function injectProperties<T>(instance: T, container: ServiceContainer): v
|
||||
* @param target 目标类
|
||||
* @returns 是否可更新
|
||||
*/
|
||||
export function isUpdatable(target: any): boolean {
|
||||
export function isUpdatable(target: Constructor): boolean {
|
||||
const metadata = updatableMetadata.get(target);
|
||||
return metadata?.updatable ?? false;
|
||||
}
|
||||
@@ -316,7 +306,7 @@ export function isUpdatable(target: any): boolean {
|
||||
* @param target 目标类
|
||||
* @returns 可更新元数据
|
||||
*/
|
||||
export function getUpdatableMetadata(target: any): UpdatableMetadata | undefined {
|
||||
export function getUpdatableMetadata(target: Constructor): UpdatableMetadata | undefined {
|
||||
return updatableMetadata.get(target);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface IService {
|
||||
* 服务类型
|
||||
*
|
||||
* 支持任意构造函数签名,以便与依赖注入装饰器配合使用
|
||||
* 使用 any[] 以允许任意参数类型的构造函数
|
||||
*/
|
||||
export type ServiceType<T extends IService> = new (...args: any[]) => T;
|
||||
|
||||
@@ -108,7 +109,7 @@ export class ServiceContainer {
|
||||
* 自动收集所有使用@Updatable装饰器标记的服务,供Core统一更新
|
||||
* 按优先级排序(数值越小越先执行)
|
||||
*/
|
||||
private _updatableServices: Array<{ instance: any; priority: number }> = [];
|
||||
private _updatableServices: Array<{ instance: IService; priority: number }> = [];
|
||||
|
||||
/**
|
||||
* 注册单例服务
|
||||
@@ -138,7 +139,7 @@ export class ServiceContainer {
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
type: type as ServiceType<IService>,
|
||||
factory: factory as ((container: ServiceContainer) => IService) | undefined,
|
||||
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
|
||||
lifetime: ServiceLifetime.Singleton
|
||||
});
|
||||
|
||||
@@ -170,7 +171,7 @@ export class ServiceContainer {
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
type: type as ServiceType<IService>,
|
||||
factory: factory as ((container: ServiceContainer) => IService) | undefined,
|
||||
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
|
||||
lifetime: ServiceLifetime.Transient
|
||||
});
|
||||
|
||||
@@ -191,7 +192,7 @@ export class ServiceContainer {
|
||||
* container.registerInstance(Config, config);
|
||||
* ```
|
||||
*/
|
||||
public registerInstance<T extends IService>(type: new (...args: any[]) => T, instance: T): void {
|
||||
public registerInstance<T extends IService>(type: ServiceType<T>, instance: T): void {
|
||||
if (this._services.has(type as ServiceType<IService>)) {
|
||||
logger.warn(`Service ${type.name} is already registered`);
|
||||
return;
|
||||
@@ -391,7 +392,7 @@ export class ServiceContainer {
|
||||
*/
|
||||
public updateAll(deltaTime?: number): void {
|
||||
for (const { instance } of this._updatableServices) {
|
||||
instance.update(deltaTime);
|
||||
(instance as IService & { update: (deltaTime?: number) => void }).update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ export class ArchetypeSystem {
|
||||
}
|
||||
|
||||
if (componentTypes.length === 1) {
|
||||
const archetypes = this._componentToArchetypes.get(componentTypes[0]);
|
||||
const archetypes = this._componentToArchetypes.get(componentTypes[0]!);
|
||||
if (archetypes) {
|
||||
for (const archetype of archetypes) {
|
||||
matchingArchetypes.push(archetype);
|
||||
|
||||
@@ -9,7 +9,6 @@ export class ComponentPool<T extends Component> {
|
||||
private resetFn?: (component: T) => void;
|
||||
private maxSize: number;
|
||||
private minSize: number;
|
||||
private growthFactor: number;
|
||||
|
||||
private stats = {
|
||||
totalCreated: 0,
|
||||
@@ -21,14 +20,14 @@ export class ComponentPool<T extends Component> {
|
||||
createFn: () => T,
|
||||
resetFn?: (component: T) => void,
|
||||
maxSize: number = 1000,
|
||||
minSize: number = 10,
|
||||
growthFactor: number = 1.5
|
||||
minSize: number = 10
|
||||
) {
|
||||
this.createFn = createFn;
|
||||
this.resetFn = resetFn;
|
||||
if (resetFn) {
|
||||
this.resetFn = resetFn;
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
this.minSize = Math.max(1, minSize);
|
||||
this.growthFactor = Math.max(1.1, growthFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +143,7 @@ interface ComponentUsageTracker {
|
||||
*/
|
||||
export class ComponentPoolManager {
|
||||
private static instance: ComponentPoolManager;
|
||||
private pools = new Map<string, ComponentPool<any>>();
|
||||
private pools = new Map<string, ComponentPool<Component>>();
|
||||
private usageTracker = new Map<string, ComponentUsageTracker>();
|
||||
|
||||
private autoCleanupInterval = 60000;
|
||||
@@ -169,7 +168,7 @@ export class ComponentPoolManager {
|
||||
maxSize?: number,
|
||||
minSize?: number
|
||||
): void {
|
||||
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize, minSize));
|
||||
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize, minSize) as unknown as ComponentPool<Component>);
|
||||
|
||||
this.usageTracker.set(componentName, {
|
||||
createCount: 0,
|
||||
@@ -186,7 +185,7 @@ export class ComponentPoolManager {
|
||||
|
||||
this.trackUsage(componentName, 'create');
|
||||
|
||||
return pool ? pool.acquire() : null;
|
||||
return pool ? (pool.acquire() as T) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,8 +287,16 @@ export class ComponentPoolManager {
|
||||
/**
|
||||
* 获取全局统计信息
|
||||
*/
|
||||
getGlobalStats() {
|
||||
const stats: any[] = [];
|
||||
getGlobalStats(): Array<{
|
||||
componentName: string;
|
||||
poolStats: ReturnType<ComponentPool<Component>['getStats']>;
|
||||
usage: ComponentUsageTracker | undefined;
|
||||
}> {
|
||||
const stats: Array<{
|
||||
componentName: string;
|
||||
poolStats: ReturnType<ComponentPool<Component>['getStats']>;
|
||||
usage: ComponentUsageTracker | undefined;
|
||||
}> = [];
|
||||
|
||||
for (const [name, pool] of this.pools.entries()) {
|
||||
stats.push({
|
||||
|
||||
@@ -53,7 +53,7 @@ export class ComponentStorage<T extends Component> {
|
||||
*/
|
||||
public getComponent(entityId: number): T | null {
|
||||
const index = this.entityToIndex.get(entityId);
|
||||
return index !== undefined ? this.dense[index] : null;
|
||||
return index !== undefined ? this.dense[index]! : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,17 +76,17 @@ export class ComponentStorage<T extends Component> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const component = this.dense[index];
|
||||
const component = this.dense[index]!;
|
||||
const lastIndex = this.dense.length - 1;
|
||||
|
||||
if (index !== lastIndex) {
|
||||
// 将末尾元素交换到要删除的位置
|
||||
const lastComponent = this.dense[lastIndex];
|
||||
const lastEntityId = this.entityIds[lastIndex];
|
||||
|
||||
const lastComponent = this.dense[lastIndex]!;
|
||||
const lastEntityId = this.entityIds[lastIndex]!;
|
||||
|
||||
this.dense[index] = lastComponent;
|
||||
this.entityIds[index] = lastEntityId;
|
||||
|
||||
|
||||
// 更新被交换元素的映射
|
||||
this.entityToIndex.set(lastEntityId, index);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ export class ComponentStorage<T extends Component> {
|
||||
*/
|
||||
public forEach(callback: (component: T, entityId: number, index: number) => void): void {
|
||||
for (let i = 0; i < this.dense.length; i++) {
|
||||
callback(this.dense[i], this.entityIds[i], i);
|
||||
callback(this.dense[i]!, this.entityIds[i]!, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ export class ComponentStorage<T extends Component> {
|
||||
*/
|
||||
export class ComponentStorageManager {
|
||||
private static readonly _logger = createLogger('ComponentStorage');
|
||||
private storages = new Map<Function, ComponentStorage<any> | SoAStorage<any>>();
|
||||
private storages = new Map<Function, ComponentStorage<Component> | SoAStorage<Component>>();
|
||||
|
||||
/**
|
||||
* 检查组件类型是否启用SoA存储
|
||||
@@ -262,11 +262,11 @@ export class ComponentStorageManager {
|
||||
*/
|
||||
public getStorage<T extends Component>(componentType: ComponentType<T>): ComponentStorage<T> | SoAStorage<T> {
|
||||
let storage = this.storages.get(componentType);
|
||||
|
||||
|
||||
if (!storage) {
|
||||
// 检查是否启用SoA优化
|
||||
const enableSoA = (componentType as any).__enableSoA;
|
||||
|
||||
const enableSoA = (componentType as unknown as { __enableSoA?: boolean }).__enableSoA;
|
||||
|
||||
if (enableSoA) {
|
||||
// 使用SoA优化存储
|
||||
storage = new SoAStorage(componentType);
|
||||
@@ -275,11 +275,11 @@ export class ComponentStorageManager {
|
||||
// 默认使用原始存储
|
||||
storage = new ComponentStorage(componentType);
|
||||
}
|
||||
|
||||
|
||||
this.storages.set(componentType, storage);
|
||||
}
|
||||
|
||||
return storage;
|
||||
|
||||
return storage as ComponentStorage<T> | SoAStorage<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,7 +301,7 @@ export class ComponentStorageManager {
|
||||
*/
|
||||
public getComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): T | null {
|
||||
const storage = this.storages.get(componentType);
|
||||
return storage ? storage.getComponent(entityId) : null;
|
||||
return storage ? (storage as ComponentStorage<T> | SoAStorage<T>).getComponent(entityId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,7 +323,7 @@ export class ComponentStorageManager {
|
||||
*/
|
||||
public removeComponent<T extends Component>(entityId: number, componentType: ComponentType<T>): T | null {
|
||||
const storage = this.storages.get(componentType);
|
||||
return storage ? storage.removeComponent(entityId) : null;
|
||||
return storage ? (storage as ComponentStorage<T> | SoAStorage<T>).removeComponent(entityId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,8 +358,8 @@ export class ComponentStorageManager {
|
||||
/**
|
||||
* 获取所有存储器的统计信息
|
||||
*/
|
||||
public getAllStats(): Map<string, any> {
|
||||
const stats = new Map<string, any>();
|
||||
public getAllStats(): Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }> {
|
||||
const stats = new Map<string, { totalSlots: number; usedSlots: number; freeSlots: number; fragmentation: number }>();
|
||||
|
||||
for (const [componentType, storage] of this.storages.entries()) {
|
||||
const typeName = getComponentTypeName(componentType as ComponentType);
|
||||
|
||||
@@ -15,11 +15,10 @@ import {
|
||||
EventListenerConfig,
|
||||
EventStats
|
||||
} from './EventSystem';
|
||||
import {
|
||||
ECSEventType,
|
||||
EventPriority,
|
||||
EVENT_TYPES,
|
||||
EventTypeValidator
|
||||
import {
|
||||
ECSEventType,
|
||||
EventPriority,
|
||||
EventTypeValidator
|
||||
} from '../CoreEvents';
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,7 +77,7 @@ export class ECSFluentAPI {
|
||||
*/
|
||||
public findFirst(...componentTypes: ComponentType[]): Entity | null {
|
||||
const result = this.querySystem.queryAll(...componentTypes);
|
||||
return result.entities.length > 0 ? result.entities[0] : null;
|
||||
return result.entities.length > 0 ? result.entities[0]! : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { Entity } from '../../Entity';
|
||||
import type { ComponentConstructor, ComponentInstance, ComponentTypeMap } from '../../../Types/TypeHelpers';
|
||||
import type { ComponentConstructor } from '../../../Types/TypeHelpers';
|
||||
import { Matcher, type QueryCondition } from '../../Utils/Matcher';
|
||||
|
||||
/**
|
||||
@@ -179,8 +179,12 @@ export class TypedQueryBuilder<
|
||||
this._all = (all || []) as TAll;
|
||||
this._any = (any || []) as TAny;
|
||||
this._none = (none || []) as TNone;
|
||||
this._tag = tag;
|
||||
this._name = name;
|
||||
if (tag !== undefined) {
|
||||
this._tag = tag;
|
||||
}
|
||||
if (name !== undefined) {
|
||||
this._name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,8 +326,8 @@ export class TypedQueryBuilder<
|
||||
all: [...this._all] as ComponentConstructor[],
|
||||
any: [...this._any] as ComponentConstructor[],
|
||||
none: [...this._none] as ComponentConstructor[],
|
||||
tag: this._tag,
|
||||
name: this._name
|
||||
...(this._tag !== undefined && { tag: this._tag }),
|
||||
...(this._name !== undefined && { name: this._name })
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,6 @@ export class QuerySystem {
|
||||
dirtyChecks: 0
|
||||
};
|
||||
|
||||
private resultArrayPool: Entity[][] = [];
|
||||
private poolMaxSize = 50;
|
||||
|
||||
constructor() {
|
||||
this.entityIndex = {
|
||||
byTag: new Map(),
|
||||
@@ -78,20 +75,6 @@ export class QuerySystem {
|
||||
this.archetypeSystem = new ArchetypeSystem();
|
||||
}
|
||||
|
||||
private acquireResultArray(): Entity[] {
|
||||
if (this.resultArrayPool.length > 0) {
|
||||
return this.resultArrayPool.pop()!;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private releaseResultArray(array: Entity[]): void {
|
||||
if (this.resultArrayPool.length < this.poolMaxSize) {
|
||||
array.length = 0;
|
||||
this.resultArrayPool.push(array);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实体列表并重建索引
|
||||
*
|
||||
@@ -378,30 +361,6 @@ export class QuerySystem {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 多组件查询算法
|
||||
*
|
||||
* 针对多组件查询场景的高效算法实现。
|
||||
* 通过选择最小的组件集合作为起点,减少需要检查的实体数量。
|
||||
*
|
||||
* @param componentTypes 组件类型列表
|
||||
* @returns 匹配的实体列表
|
||||
*/
|
||||
private queryMultipleComponents(componentTypes: ComponentType[]): Entity[] {
|
||||
const archetypeResult = this.archetypeSystem.queryArchetypes(componentTypes, 'AND');
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const archetype of archetypeResult.archetypes) {
|
||||
for (const entity of archetype.entities) {
|
||||
result.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 查询包含任意指定组件的实体
|
||||
*
|
||||
@@ -715,7 +674,7 @@ export class QuerySystem {
|
||||
private generateCacheKey(prefix: string, componentTypes: ComponentType[]): string {
|
||||
// 快速路径:单组件查询
|
||||
if (componentTypes.length === 1) {
|
||||
const name = getComponentTypeName(componentTypes[0]);
|
||||
const name = getComponentTypeName(componentTypes[0]!);
|
||||
return `${prefix}:${name}`;
|
||||
}
|
||||
|
||||
@@ -1258,7 +1217,7 @@ export class QueryBuilder {
|
||||
|
||||
// 简化实现:目前只支持单一条件
|
||||
if (this.conditions.length === 1) {
|
||||
const condition = this.conditions[0];
|
||||
const condition = this.conditions[0]!;
|
||||
switch (condition.type) {
|
||||
case QueryConditionType.ALL:
|
||||
return this.querySystem.queryAll(...condition.componentTypes);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { Component } from '../Component';
|
||||
import type { Entity } from '../Entity';
|
||||
import type { IScene } from '../IScene';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
|
||||
const logger = createLogger('ReferenceTracker');
|
||||
|
||||
/**
|
||||
* WeakRef 接口定义
|
||||
@@ -98,11 +95,6 @@ export class ReferenceTracker {
|
||||
*/
|
||||
private _references: Map<number, Set<EntityRefRecord>> = new Map();
|
||||
|
||||
/**
|
||||
* 当前Scene的引用
|
||||
*/
|
||||
private _scene: IWeakRef<IScene> | null = null;
|
||||
|
||||
/**
|
||||
* 注册Entity引用
|
||||
*
|
||||
@@ -272,15 +264,6 @@ export class ReferenceTracker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Scene引用
|
||||
*
|
||||
* @param scene Scene实例
|
||||
*/
|
||||
public setScene(scene: IScene): void {
|
||||
this._scene = new WeakRefImpl(scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册Entity到Scene的映射
|
||||
*
|
||||
|
||||
@@ -463,7 +463,7 @@ export class SoAStorage<T extends Component> {
|
||||
}
|
||||
|
||||
private updateComponentAtIndex(index: number, component: T): void {
|
||||
const entityId = this.indexToEntity[index];
|
||||
const entityId = this.indexToEntity[index]!;
|
||||
const complexFieldMap = new Map<string, any>();
|
||||
const highPrecisionFields = (this.type as any).__highPrecisionFields || new Set();
|
||||
const serializeMapFields = (this.type as any).__serializeMapFields || new Set();
|
||||
@@ -802,8 +802,11 @@ export class SoAStorage<T extends Component> {
|
||||
const newIndexToEntity: number[] = [];
|
||||
|
||||
for (let newIndex = 0; newIndex < activeEntries.length; newIndex++) {
|
||||
const [entityId, oldIndex] = activeEntries[newIndex];
|
||||
|
||||
const entry = activeEntries[newIndex];
|
||||
if (!entry) continue;
|
||||
|
||||
const [entityId, oldIndex] = entry;
|
||||
|
||||
newEntityToIndex.set(entityId, newIndex);
|
||||
newIndexToEntity[newIndex] = entityId;
|
||||
|
||||
@@ -811,17 +814,26 @@ export class SoAStorage<T extends Component> {
|
||||
if (newIndex !== oldIndex) {
|
||||
// 移动数值字段
|
||||
for (const [, array] of this.fields.entries()) {
|
||||
array[newIndex] = array[oldIndex];
|
||||
const value = array[oldIndex];
|
||||
if (value !== undefined) {
|
||||
array[newIndex] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 移动字符串字段
|
||||
for (const [, stringArray] of this.stringFields.entries()) {
|
||||
stringArray[newIndex] = stringArray[oldIndex];
|
||||
const value = stringArray[oldIndex];
|
||||
if (value !== undefined) {
|
||||
stringArray[newIndex] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 移动序列化字段
|
||||
for (const [, serializedArray] of this.serializedFields.entries()) {
|
||||
serializedArray[newIndex] = serializedArray[oldIndex];
|
||||
const value = serializedArray[oldIndex];
|
||||
if (value !== undefined) {
|
||||
serializedArray[newIndex] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import { ComponentPoolManager } from './Core/ComponentPool';
|
||||
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
||||
import { ServiceContainer, type ServiceType } from '../Core/ServiceContainer';
|
||||
import { createInstance, isInjectable, injectProperties } from '../Core/DI';
|
||||
import { isUpdatable, getUpdatableMetadata } from '../Core/DI/Decorators';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,11 +8,24 @@ import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
import {
|
||||
getSerializationMetadata,
|
||||
isSerializable,
|
||||
SerializationMetadata
|
||||
getSerializationMetadata
|
||||
} from './SerializationDecorators';
|
||||
|
||||
/**
|
||||
* 可序列化的值类型
|
||||
*/
|
||||
export type SerializableValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| SerializableValue[]
|
||||
| { [key: string]: SerializableValue }
|
||||
| { __type: 'Date'; value: string }
|
||||
| { __type: 'Map'; value: Array<[SerializableValue, SerializableValue]> }
|
||||
| { __type: 'Set'; value: SerializableValue[] };
|
||||
|
||||
/**
|
||||
* 序列化后的组件数据
|
||||
*/
|
||||
@@ -30,7 +43,7 @@ export interface SerializedComponent {
|
||||
/**
|
||||
* 组件数据
|
||||
*/
|
||||
data: Record<string, any>;
|
||||
data: Record<string, SerializableValue>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,12 +66,12 @@ export class ComponentSerializer {
|
||||
|
||||
const componentType = component.constructor as ComponentType;
|
||||
const typeName = metadata.options.typeId || getComponentTypeName(componentType);
|
||||
const data: Record<string, any> = {};
|
||||
const data: Record<string, SerializableValue> = {};
|
||||
|
||||
// 序列化标记的字段
|
||||
for (const [fieldName, options] of metadata.fields) {
|
||||
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
|
||||
const value = (component as any)[fieldName];
|
||||
const value = (component as unknown as Record<string | symbol, SerializableValue>)[fieldName];
|
||||
|
||||
// 跳过忽略的字段
|
||||
if (metadata.ignoredFields.has(fieldName)) {
|
||||
@@ -125,7 +138,7 @@ export class ComponentSerializer {
|
||||
? options.deserializer(serializedValue)
|
||||
: this.deserializeValue(serializedValue);
|
||||
|
||||
(component as any)[fieldName] = value;
|
||||
(component as unknown as Record<string | symbol, SerializableValue>)[fieldName] = value;
|
||||
}
|
||||
|
||||
return component;
|
||||
@@ -178,7 +191,7 @@ export class ComponentSerializer {
|
||||
*
|
||||
* 处理基本类型、数组、对象等的序列化
|
||||
*/
|
||||
private static serializeValue(value: any): any {
|
||||
private static serializeValue(value: SerializableValue): SerializableValue {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
@@ -219,11 +232,12 @@ export class ComponentSerializer {
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.serializeValue(value[key]);
|
||||
if (type === 'object' && typeof value === 'object' && !Array.isArray(value)) {
|
||||
const result: Record<string, SerializableValue> = {};
|
||||
const obj = value as Record<string, SerializableValue>;
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
result[key] = this.serializeValue(obj[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -236,7 +250,7 @@ export class ComponentSerializer {
|
||||
/**
|
||||
* 默认值反序列化
|
||||
*/
|
||||
private static deserializeValue(value: any): any {
|
||||
private static deserializeValue(value: SerializableValue): SerializableValue {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
@@ -248,14 +262,15 @@ export class ComponentSerializer {
|
||||
}
|
||||
|
||||
// 处理特殊类型标记
|
||||
if (type === 'object' && value.__type) {
|
||||
switch (value.__type) {
|
||||
if (type === 'object' && typeof value === 'object' && '__type' in value) {
|
||||
const typedValue = value as { __type: string; value: SerializableValue };
|
||||
switch (typedValue.__type) {
|
||||
case 'Date':
|
||||
return new Date(value.value);
|
||||
return { __type: 'Date', value: typeof typedValue.value === 'string' ? typedValue.value : String(typedValue.value) };
|
||||
case 'Map':
|
||||
return new Map(value.value);
|
||||
return { __type: 'Map', value: typedValue.value as Array<[SerializableValue, SerializableValue]> };
|
||||
case 'Set':
|
||||
return new Set(value.value);
|
||||
return { __type: 'Set', value: typedValue.value as SerializableValue[] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,11 +280,12 @@ export class ComponentSerializer {
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.deserializeValue(value[key]);
|
||||
if (type === 'object' && typeof value === 'object' && !Array.isArray(value)) {
|
||||
const result: Record<string, SerializableValue> = {};
|
||||
const obj = value as Record<string, SerializableValue>;
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
result[key] = this.deserializeValue(obj[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { ComponentSerializer, SerializedComponent } from './ComponentSerializer';
|
||||
import { IScene } from '../IScene';
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
import type { IScene } from '../IScene';
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentSerializer, SerializedComponent } from './ComponentSerializer';
|
||||
import { SerializedEntity } from './EntitySerializer';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
@@ -204,7 +203,7 @@ export class IncrementalSerializer {
|
||||
active: entity.active,
|
||||
enabled: entity.enabled,
|
||||
updateOrder: entity.updateOrder,
|
||||
parentId: entity.parent?.id
|
||||
...(entity.parent && { parentId: entity.parent.id })
|
||||
});
|
||||
|
||||
// 快照组件
|
||||
@@ -286,7 +285,7 @@ export class IncrementalSerializer {
|
||||
active: entity.active,
|
||||
enabled: entity.enabled,
|
||||
updateOrder: entity.updateOrder,
|
||||
parentId: entity.parent?.id,
|
||||
...(entity.parent && { parentId: entity.parent.id }),
|
||||
components: [],
|
||||
children: []
|
||||
}
|
||||
@@ -325,7 +324,7 @@ export class IncrementalSerializer {
|
||||
active: entity.active,
|
||||
enabled: entity.enabled,
|
||||
updateOrder: entity.updateOrder,
|
||||
parentId: entity.parent?.id
|
||||
...(entity.parent && { parentId: entity.parent.id })
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import type { IScene } from '../IScene';
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage';
|
||||
import { EntitySerializer, SerializedEntity } from './EntitySerializer';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
@@ -514,7 +513,7 @@ export class SceneSerializer {
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
version: data.version,
|
||||
errors: errors.length > 0 ? errors : undefined
|
||||
...(errors.length > 0 && { errors })
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
@@ -543,7 +542,7 @@ export class SceneSerializer {
|
||||
return {
|
||||
name: data.name,
|
||||
version: data.version,
|
||||
timestamp: data.timestamp,
|
||||
...(data.timestamp !== undefined && { timestamp: data.timestamp }),
|
||||
entityCount: data.metadata?.entityCount || data.entities.length,
|
||||
componentTypeCount: data.componentTypeRegistry.length
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ export class VersionMigrationManager {
|
||||
public static registerComponentMigration(
|
||||
componentType: string,
|
||||
fromVersion: number,
|
||||
toVersion: number,
|
||||
_toVersion: number,
|
||||
migration: ComponentMigrationFunction
|
||||
): void {
|
||||
if (!this.componentMigrations.has(componentType)) {
|
||||
@@ -98,7 +98,7 @@ export class VersionMigrationManager {
|
||||
*/
|
||||
public static registerSceneMigration(
|
||||
fromVersion: number,
|
||||
toVersion: number,
|
||||
_toVersion: number,
|
||||
migration: SceneMigrationFunction
|
||||
): void {
|
||||
this.sceneMigrations.set(fromVersion, migration);
|
||||
|
||||
@@ -65,7 +65,7 @@ interface EventListenerRecord {
|
||||
* ```
|
||||
*/
|
||||
export abstract class EntitySystem<
|
||||
TComponents extends readonly ComponentConstructor[] = []
|
||||
_TComponents extends readonly ComponentConstructor[] = []
|
||||
> implements ISystemBase, IService {
|
||||
private _updateOrder: number;
|
||||
private _enabled: boolean;
|
||||
@@ -83,7 +83,6 @@ export abstract class EntitySystem<
|
||||
*/
|
||||
private _entityIdMap: Map<number, Entity> | null;
|
||||
private _entityIdMapVersion: number;
|
||||
private _entityIdMapSize: number;
|
||||
|
||||
/**
|
||||
* 统一的实体缓存管理器
|
||||
@@ -158,7 +157,6 @@ export abstract class EntitySystem<
|
||||
|
||||
this._entityIdMap = null;
|
||||
this._entityIdMapVersion = -1;
|
||||
this._entityIdMapSize = 0;
|
||||
|
||||
// 初始化logger
|
||||
this.logger = createLogger(this.getLoggerName());
|
||||
@@ -281,7 +279,6 @@ export abstract class EntitySystem<
|
||||
// 清理实体ID映射缓存
|
||||
this._entityIdMap = null;
|
||||
this._entityIdMapVersion = -1;
|
||||
this._entityIdMapSize = 0;
|
||||
|
||||
// 清理所有事件监听器
|
||||
// 调用框架销毁方法
|
||||
@@ -425,7 +422,7 @@ export abstract class EntitySystem<
|
||||
const idSet = new Set<number>();
|
||||
|
||||
for (let i = 0; i < len; i = (i + 1) | 0) {
|
||||
idSet.add(entities[i].id | 0);
|
||||
idSet.add(entities[i]!.id | 0);
|
||||
}
|
||||
return idSet;
|
||||
}
|
||||
@@ -497,13 +494,12 @@ export abstract class EntitySystem<
|
||||
|
||||
const len = allEntities.length;
|
||||
for (let i = 0; i < len; i = (i + 1) | 0) {
|
||||
const entity = allEntities[i];
|
||||
const entity = allEntities[i]!;
|
||||
entityMap.set(entity.id | 0, entity);
|
||||
}
|
||||
|
||||
this._entityIdMap = entityMap;
|
||||
this._entityIdMapVersion = version;
|
||||
this._entityIdMapSize = len;
|
||||
|
||||
return entityMap;
|
||||
}
|
||||
@@ -608,7 +604,7 @@ export abstract class EntitySystem<
|
||||
*
|
||||
* @param entities 要处理的实体列表
|
||||
*/
|
||||
protected process(entities: readonly Entity[]): void {
|
||||
protected process(_entities: readonly Entity[]): void {
|
||||
// 子类必须实现此方法
|
||||
}
|
||||
|
||||
@@ -720,7 +716,7 @@ export abstract class EntitySystem<
|
||||
*
|
||||
* @param entity 被添加的实体
|
||||
*/
|
||||
protected onAdded(entity: Entity): void {
|
||||
protected onAdded(_entity: Entity): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
@@ -731,7 +727,7 @@ export abstract class EntitySystem<
|
||||
*
|
||||
* @param entity 被移除的实体
|
||||
*/
|
||||
protected onRemoved(entity: Entity): void {
|
||||
protected onRemoved(_entity: Entity): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
@@ -812,6 +808,7 @@ export abstract class EntitySystem<
|
||||
|
||||
if (listenerIndex >= 0) {
|
||||
const listener = this._eventListeners[listenerIndex];
|
||||
if (!listener) return;
|
||||
|
||||
// 从事件系统中移除
|
||||
listener.eventSystem.off(eventType, listener.listenerRef);
|
||||
@@ -958,7 +955,7 @@ export abstract class EntitySystem<
|
||||
processor: (entity: Entity, index: number) => void
|
||||
): void {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
processor(entities[i], i);
|
||||
processor(entities[i]!, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,7 +1028,7 @@ export abstract class EntitySystem<
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): Entity | undefined {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
if (predicate(entities[i], i)) {
|
||||
if (predicate(entities[i]!, i)) {
|
||||
return entities[i];
|
||||
}
|
||||
}
|
||||
@@ -1060,7 +1057,7 @@ export abstract class EntitySystem<
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): boolean {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
if (predicate(entities[i], i)) {
|
||||
if (predicate(entities[i]!, i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1089,7 +1086,7 @@ export abstract class EntitySystem<
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): boolean {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
if (!predicate(entities[i], i)) {
|
||||
if (!predicate(entities[i]!, i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export abstract class PassiveSystem extends EntitySystem {
|
||||
* 不进行任何处理
|
||||
* @param entities 实体数组,未被使用
|
||||
*/
|
||||
protected override process(entities: Entity[]): void {
|
||||
protected override process(_entities: Entity[]): void {
|
||||
// 被动系统不进行任何处理
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export abstract class ProcessingSystem extends EntitySystem {
|
||||
* 处理实体,每帧调用processSystem方法进行处理
|
||||
* @param entities 实体数组,未被使用
|
||||
*/
|
||||
protected override process(entities: Entity[]): void {
|
||||
protected override process(_entities: Entity[]): void {
|
||||
// 调用子类实现的processSystem方法进行实体处理
|
||||
this.processSystem();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ import { Entity } from '../Entity';
|
||||
import { EntitySystem } from './EntitySystem';
|
||||
import { Matcher } from '../Utils/Matcher';
|
||||
import { Time } from '../../Utils/Time';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
import type { IComponent } from '../../Types';
|
||||
import { PlatformManager } from '../../Platform/PlatformManager';
|
||||
import type { IPlatformAdapter, PlatformWorker } from '../../Platform/IPlatformAdapter';
|
||||
import { getSystemInstanceTypeName } from '../Decorators';
|
||||
@@ -221,7 +219,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
enableWorker: config.enableWorker ?? true,
|
||||
workerCount: validatedWorkerCount,
|
||||
systemConfig: config.systemConfig,
|
||||
entitiesPerWorker: config.entitiesPerWorker,
|
||||
...(config.entitiesPerWorker !== undefined && { entitiesPerWorker: config.entitiesPerWorker }),
|
||||
useSharedArrayBuffer: config.useSharedArrayBuffer ?? this.isSharedArrayBufferSupported(),
|
||||
entityDataSize: config.entityDataSize ?? this.getDefaultEntityDataSize(),
|
||||
maxEntities: config.maxEntities ?? 10000
|
||||
@@ -321,10 +319,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
this.workerPool = new PlatformWorkerPool(
|
||||
workers,
|
||||
this.sharedBuffer // 传递SharedArrayBuffer给Worker池
|
||||
);
|
||||
this.workerPool = new PlatformWorkerPool(workers);
|
||||
} catch (error) {
|
||||
this.logger.error(`${this.systemName}: Worker池初始化失败`, error);
|
||||
this.config.enableWorker = false;
|
||||
@@ -355,7 +350,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
const sharedMethodStr = sharedProcessMethod.toString();
|
||||
const sharedFunctionBodyMatch = sharedMethodStr.match(/\{([\s\S]*)\}/);
|
||||
if (sharedFunctionBodyMatch) {
|
||||
sharedProcessFunctionBody = sharedFunctionBodyMatch[1];
|
||||
sharedProcessFunctionBody = sharedFunctionBodyMatch[1] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +486,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
// 1. 数据提取阶段
|
||||
const entityData: TEntityData[] = [];
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
entityData[i] = this.extractEntityData(entities[i]);
|
||||
entityData[i] = this.extractEntityData(entities[i]!);
|
||||
}
|
||||
|
||||
// 2. 分批处理
|
||||
@@ -541,12 +536,12 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
if (results && typeof (results as any).then === 'function') {
|
||||
(results as Promise<TEntityData[]>).then(finalResults => {
|
||||
entities.forEach((entity, index) => {
|
||||
this.applyResult(entity, finalResults[index]);
|
||||
this.applyResult(entity, finalResults[index]!);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
entities.forEach((entity, index) => {
|
||||
this.applyResult(entity, (results as TEntityData[])[index]);
|
||||
this.applyResult(entity, (results as TEntityData[])[index]!);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -595,7 +590,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
if (!this.sharedFloatArray) return;
|
||||
|
||||
for (let i = 0; i < entities.length && i < this.config.maxEntities; i++) {
|
||||
const entity = entities[i];
|
||||
const entity = entities[i]!;
|
||||
const data = this.extractEntityData(entity);
|
||||
const offset = i * this.config.entityDataSize; // 使用配置的数据大小
|
||||
|
||||
@@ -670,7 +665,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
if (!this.sharedFloatArray) return;
|
||||
|
||||
for (let i = 0; i < entities.length && i < this.config.maxEntities; i++) {
|
||||
const entity = entities[i];
|
||||
const entity = entities[i]!;
|
||||
const offset = i * this.config.entityDataSize; // 使用配置的数据大小
|
||||
|
||||
// 从SharedArrayBuffer读取数据
|
||||
@@ -832,7 +827,7 @@ export abstract class WorkerEntitySystem<TEntityData = any> extends EntitySystem
|
||||
return {
|
||||
enabled: this.config.enableWorker,
|
||||
workerCount: this.config.workerCount,
|
||||
entitiesPerWorker: this.config.entitiesPerWorker,
|
||||
...(this.config.entitiesPerWorker !== undefined && { entitiesPerWorker: this.config.entitiesPerWorker }),
|
||||
maxSystemWorkerCount: this.getMaxSystemWorkerCount(),
|
||||
isProcessing: this.isProcessing,
|
||||
sharedArrayBufferSupported: this.isSharedArrayBufferSupported(),
|
||||
@@ -871,31 +866,20 @@ class PlatformWorkerPool {
|
||||
}> = [];
|
||||
private busyWorkers = new Set<number>();
|
||||
private taskCounter = 0;
|
||||
private sharedBuffer: SharedArrayBuffer | null = null;
|
||||
private readonly logger = createLogger('PlatformWorkerPool');
|
||||
|
||||
constructor(
|
||||
workers: PlatformWorker[],
|
||||
sharedBuffer: SharedArrayBuffer | null = null
|
||||
workers: PlatformWorker[]
|
||||
) {
|
||||
this.sharedBuffer = sharedBuffer;
|
||||
this.workers = workers;
|
||||
|
||||
// 为每个Worker设置消息处理器
|
||||
for (let i = 0; i < workers.length; i++) {
|
||||
const worker = workers[i];
|
||||
if (!worker) continue;
|
||||
|
||||
// 设置消息处理器
|
||||
worker.onMessage((event) => this.handleWorkerMessage(i, event.data));
|
||||
worker.onError((error) => this.handleWorkerError(i, error));
|
||||
|
||||
// 如果有SharedArrayBuffer,发送给Worker
|
||||
if (sharedBuffer) {
|
||||
worker.postMessage({
|
||||
type: 'init',
|
||||
sharedBuffer: sharedBuffer
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -946,14 +930,15 @@ class PlatformWorkerPool {
|
||||
if (!this.busyWorkers.has(i) && this.taskQueue.length > 0) {
|
||||
const task = this.taskQueue.shift()!;
|
||||
this.busyWorkers.add(i);
|
||||
const worker = this.workers[i]!;
|
||||
|
||||
this.workers[i].postMessage({
|
||||
worker.postMessage({
|
||||
id: task.id,
|
||||
...task.data
|
||||
});
|
||||
|
||||
// 存储任务信息以便后续处理
|
||||
(this.workers[i] as any)._currentTask = task;
|
||||
(worker as any)._currentTask = task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface BitMask64Data {
|
||||
|
||||
export class BitMask64Utils {
|
||||
/** 零掩码常量,所有位都为0 */
|
||||
public static readonly ZERO: Readonly<BitMask64Data> = { base: [0, 0], segments: undefined };
|
||||
public static readonly ZERO: Readonly<BitMask64Data> = { base: [0, 0] };
|
||||
|
||||
/**
|
||||
* 根据位索引创建64位掩码
|
||||
@@ -37,7 +37,7 @@ export class BitMask64Utils {
|
||||
if (bitIndex < 0) {
|
||||
throw new Error(`Bit index ${bitIndex} out of range [0, ∞)`);
|
||||
}
|
||||
const mask: BitMask64Data = { base: [0, 0], segments: undefined };
|
||||
const mask: BitMask64Data = { base: [0, 0] };
|
||||
BitMask64Utils.setBit(mask, bitIndex);
|
||||
return mask;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export class BitMask64Utils {
|
||||
* @returns 低32位为输入值、高32位为0的掩码
|
||||
*/
|
||||
public static fromNumber(value: number): BitMask64Data {
|
||||
return { base: [value >>> 0, 0], segments: undefined};
|
||||
return { base: [value >>> 0, 0] };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,10 @@ export class BitMask64Utils {
|
||||
// 基础区段就包含指定的位,或任意一个参数不含扩展区段,直接短路
|
||||
if(baseHasAny || !bitsSegments || !maskSegments) return baseHasAny;
|
||||
// 额外检查扩展区域是否包含指定的位 - 如果bitsSegments[index]不存在,会被转为NaN,NaN的位运算始终返回0
|
||||
return maskSegments.some((seg, index) => (seg[SegmentPart.LOW] & bitsSegments[index][SegmentPart.LOW]) !== 0 || (seg[SegmentPart.HIGH] & bitsSegments[index][SegmentPart.HIGH]) !== 0)
|
||||
return maskSegments.some((seg, index) => {
|
||||
const bitsSeg = bitsSegments[index];
|
||||
return bitsSeg && ((seg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) !== 0 || (seg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) !== 0);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +92,9 @@ export class BitMask64Utils {
|
||||
// 对mask/bits中都存在的区段,进行hasAll判断
|
||||
if(maskSegments){
|
||||
for (let i = 0; i < Math.min(maskSegmentsLength,bitsSegments.length); i++) {
|
||||
if((maskSegments[i][SegmentPart.LOW] & bitsSegments[i][SegmentPart.LOW]) !== bitsSegments[i][SegmentPart.LOW] || (maskSegments[i][SegmentPart.HIGH] & bitsSegments[i][SegmentPart.HIGH]) !== bitsSegments[i][SegmentPart.HIGH]){
|
||||
const maskSeg = maskSegments[i]!;
|
||||
const bitsSeg = bitsSegments[i]!;
|
||||
if((maskSeg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) !== bitsSeg[SegmentPart.LOW] || (maskSeg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) !== bitsSeg[SegmentPart.HIGH]){
|
||||
// 存在不匹配的位,直接短路
|
||||
return false;
|
||||
}
|
||||
@@ -97,7 +102,8 @@ export class BitMask64Utils {
|
||||
}
|
||||
// 对mask中不存在,但bits中存在的区段,进行isZero判断
|
||||
for (let i = maskSegmentsLength; i < bitsSegments.length; i++) {
|
||||
if(bitsSegments[i][SegmentPart.LOW] !== 0 || bitsSegments[i][SegmentPart.HIGH] !== 0){
|
||||
const bitsSeg = bitsSegments[i]!;
|
||||
if(bitsSeg[SegmentPart.LOW] !== 0 || bitsSeg[SegmentPart.HIGH] !== 0){
|
||||
// 存在不为0的区段,直接短路
|
||||
return false;
|
||||
}
|
||||
@@ -120,7 +126,11 @@ export class BitMask64Utils {
|
||||
//不含扩展区域,或基础区域就包含指定的位,或bits不含拓展区段,直接短路。
|
||||
if(!maskSegments || !baseHasNone || !bitsSegments) return baseHasNone;
|
||||
// 额外检查扩展区域是否都包含指定的位 - 此时bitsSegments存在,如果bitsSegments[index]不存在,会被转为NaN,NaN的位运算始终返回0
|
||||
return maskSegments.every((seg, index) => (seg[SegmentPart.LOW] & bitsSegments[index][SegmentPart.LOW]) === 0 && (seg[SegmentPart.HIGH] & bitsSegments[index][SegmentPart.HIGH]) === 0);
|
||||
return maskSegments.every((seg, index) => {
|
||||
const bitsSeg = bitsSegments[index];
|
||||
if (!bitsSeg) return true;
|
||||
return (seg[SegmentPart.LOW] & bitsSeg[SegmentPart.LOW]) === 0 && (seg[SegmentPart.HIGH] & bitsSeg[SegmentPart.HIGH]) === 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +170,7 @@ export class BitMask64Utils {
|
||||
}else if(!aSeg && bSeg){
|
||||
//aSeg不存在,则必须要求bSeg全为0
|
||||
if(bSeg[SegmentPart.LOW] !== 0 || bSeg[SegmentPart.HIGH] !== 0) return false;
|
||||
}else{
|
||||
}else if(aSeg && bSeg){
|
||||
//理想状态:aSeg/bSeg都存在
|
||||
if(aSeg[SegmentPart.LOW] !== bSeg[SegmentPart.LOW] || aSeg[SegmentPart.HIGH] !== bSeg[SegmentPart.HIGH]) return false;
|
||||
}
|
||||
@@ -248,8 +258,10 @@ export class BitMask64Utils {
|
||||
|
||||
// 对每个段执行或操作
|
||||
for (let i = 0; i < otherSegments.length; i++) {
|
||||
targetSegments[i][SegmentPart.LOW] |= otherSegments[i][SegmentPart.LOW];
|
||||
targetSegments[i][SegmentPart.HIGH] |= otherSegments[i][SegmentPart.HIGH];
|
||||
const targetSeg = targetSegments[i]!;
|
||||
const otherSeg = otherSegments[i]!;
|
||||
targetSeg[SegmentPart.LOW] |= otherSeg[SegmentPart.LOW];
|
||||
targetSeg[SegmentPart.HIGH] |= otherSeg[SegmentPart.HIGH];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,8 +289,10 @@ export class BitMask64Utils {
|
||||
|
||||
// 对每个段执行与操作
|
||||
for (let i = 0; i < otherSegments.length; i++) {
|
||||
targetSegments[i][SegmentPart.LOW] &= otherSegments[i][SegmentPart.LOW];
|
||||
targetSegments[i][SegmentPart.HIGH] &= otherSegments[i][SegmentPart.HIGH];
|
||||
const targetSeg = targetSegments[i]!;
|
||||
const otherSeg = otherSegments[i]!;
|
||||
targetSeg[SegmentPart.LOW] &= otherSeg[SegmentPart.LOW];
|
||||
targetSeg[SegmentPart.HIGH] &= otherSeg[SegmentPart.HIGH];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,8 +319,10 @@ export class BitMask64Utils {
|
||||
|
||||
// 对每个段执行异或操作
|
||||
for (let i = 0; i < otherSegments.length; i++) {
|
||||
targetSegments[i][SegmentPart.LOW] ^= otherSegments[i][SegmentPart.LOW];
|
||||
targetSegments[i][SegmentPart.HIGH] ^= otherSegments[i][SegmentPart.HIGH];
|
||||
const targetSeg = targetSegments[i]!;
|
||||
const otherSeg = otherSegments[i]!;
|
||||
targetSeg[SegmentPart.LOW] ^= otherSeg[SegmentPart.LOW];
|
||||
targetSeg[SegmentPart.HIGH] ^= otherSeg[SegmentPart.HIGH];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,10 +333,12 @@ export class BitMask64Utils {
|
||||
public static clear(mask: BitMask64Data): void {
|
||||
mask.base[SegmentPart.LOW] = 0;
|
||||
mask.base[SegmentPart.HIGH] = 0;
|
||||
for (let i = 0; i < (mask.segments?.length ?? 0); i++) {
|
||||
const seg = mask.segments![i];
|
||||
seg[SegmentPart.LOW] = 0;
|
||||
seg[SegmentPart.HIGH] = 0;
|
||||
if (mask.segments) {
|
||||
for (let i = 0; i < mask.segments.length; i++) {
|
||||
const seg = mask.segments[i]!;
|
||||
seg[SegmentPart.LOW] = 0;
|
||||
seg[SegmentPart.HIGH] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,9 +364,11 @@ export class BitMask64Utils {
|
||||
target.segments.push([0,0]);
|
||||
}
|
||||
// 逐个重写
|
||||
for (let i = 0; i < length; i++) {
|
||||
const targetSeg = target.segments![i];
|
||||
const sourSeg = source.segments![i];
|
||||
const targetSegments = target.segments;
|
||||
const sourceSegments = source.segments;
|
||||
for (let i = 0; i < sourceSegments.length; i++) {
|
||||
const targetSeg = targetSegments[i]!;
|
||||
const sourSeg = sourceSegments[i]!;
|
||||
targetSeg[SegmentPart.LOW] = sourSeg[SegmentPart.LOW];
|
||||
targetSeg[SegmentPart.HIGH] = sourSeg[SegmentPart.HIGH];
|
||||
}
|
||||
@@ -362,7 +382,7 @@ export class BitMask64Utils {
|
||||
public static clone(mask: BitMask64Data): BitMask64Data {
|
||||
return {
|
||||
base: mask.base.slice() as BitMask64Segment,
|
||||
segments: mask.segments ? mask.segments.map(seg => [...seg]) : undefined
|
||||
...(mask.segments && { segments: mask.segments.map(seg => [...seg] as BitMask64Segment) })
|
||||
};
|
||||
}
|
||||
|
||||
@@ -393,7 +413,7 @@ export class BitMask64Utils {
|
||||
|
||||
for (let i = -1; i < totalLength; i++) {
|
||||
let segResult = '';
|
||||
const bitMaskData = i == -1 ? mask.base : mask.segments![i];
|
||||
const bitMaskData = i == -1 ? mask.base : mask.segments![i]!;
|
||||
let hi = bitMaskData[SegmentPart.HIGH];
|
||||
let lo = bitMaskData[SegmentPart.LOW];
|
||||
if(radix == 2){
|
||||
@@ -429,7 +449,7 @@ export class BitMask64Utils {
|
||||
public static popCount(mask: BitMask64Data): number {
|
||||
let count = 0;
|
||||
for (let i = -1; i < (mask.segments?.length ?? 0); i++) {
|
||||
const bitMaskData = i == -1 ? mask.base : mask.segments![i];
|
||||
const bitMaskData = i == -1 ? mask.base : mask.segments![i]!;
|
||||
let lo = bitMaskData[SegmentPart.LOW];
|
||||
let hi = bitMaskData[SegmentPart.HIGH];
|
||||
while (lo) {
|
||||
@@ -470,7 +490,7 @@ export class BitMask64Utils {
|
||||
segments.push([0, 0]);
|
||||
}
|
||||
}
|
||||
return segments[targetSegIndex];
|
||||
return segments[targetSegIndex] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,8 +73,8 @@ export class BitMaskHashMap<T> {
|
||||
|
||||
// 查找是否存在 secondaryHash
|
||||
for (let i = 0; i < bucket.length; i++) {
|
||||
if (bucket[i][0] === secondary) {
|
||||
bucket[i][1] = value;
|
||||
if (bucket[i]![0] === secondary) {
|
||||
bucket[i]![1] = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -90,8 +90,8 @@ export class BitMaskHashMap<T> {
|
||||
const bucket = this.buckets.get(primary);
|
||||
if (!bucket) return undefined;
|
||||
for (let i = 0; i < bucket.length; i++) {
|
||||
if (bucket[i][0] === secondary) {
|
||||
return bucket[i][1];
|
||||
if (bucket[i]![0] === secondary) {
|
||||
return bucket[i]![1];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -106,7 +106,7 @@ export class BitMaskHashMap<T> {
|
||||
const bucket = this.buckets.get(primary);
|
||||
if (!bucket) return false;
|
||||
for (let i = 0; i < bucket.length; i++) {
|
||||
if (bucket[i][0] === secondary) {
|
||||
if (bucket[i]![0] === secondary) {
|
||||
bucket.splice(i, 1);
|
||||
this._size--;
|
||||
if (bucket.length === 0) {
|
||||
@@ -125,7 +125,7 @@ export class BitMaskHashMap<T> {
|
||||
|
||||
*entries(): IterableIterator<[BitMask64Data, T]> {
|
||||
for (const [_, bucket] of this.buckets) {
|
||||
for (const [secondary, value] of bucket) {
|
||||
for (const [_secondary, value] of bucket) {
|
||||
// 无法还原原始 key(只存二级 hash),所以 entries 返回不了 key
|
||||
yield [undefined as any, value];
|
||||
}
|
||||
|
||||
@@ -265,16 +265,16 @@ export class Bits {
|
||||
*/
|
||||
public static fromBinaryString(binaryString: string): Bits {
|
||||
const cleanString = binaryString.replace(/\s/g, '');
|
||||
let data: BitMask64Data = { base: undefined!, segments: undefined};
|
||||
let data: BitMask64Data;
|
||||
if (cleanString.length <= 32) {
|
||||
const num = parseInt(cleanString, 2);
|
||||
data.base = [num >>> 0, 0];
|
||||
data = { base: [num >>> 0, 0] };
|
||||
} else {
|
||||
const loBits = cleanString.substring(cleanString.length - 32);
|
||||
const hiBits = cleanString.substring(0, cleanString.length - 32);
|
||||
const lo = parseInt(loBits, 2);
|
||||
const hi = parseInt(hiBits, 2);
|
||||
data.base = [lo >>> 0, hi >>> 0];
|
||||
data = { base: [lo >>> 0, hi >>> 0] };
|
||||
}
|
||||
return new Bits(data);
|
||||
}
|
||||
@@ -286,16 +286,16 @@ export class Bits {
|
||||
*/
|
||||
public static fromHexString(hexString: string): Bits {
|
||||
const cleanString = hexString.replace(/^0x/i, '');
|
||||
let data: BitMask64Data = { base: undefined!, segments: undefined};
|
||||
let data: BitMask64Data;
|
||||
if (cleanString.length <= 8) {
|
||||
const num = parseInt(cleanString, 16);
|
||||
data.base = [num >>> 0, 0];
|
||||
data = { base: [num >>> 0, 0] };
|
||||
} else {
|
||||
const loBits = cleanString.substring(cleanString.length - 8);
|
||||
const hiBits = cleanString.substring(0, cleanString.length - 8);
|
||||
const lo = parseInt(loBits, 16);
|
||||
const hi = parseInt(hiBits, 16);
|
||||
data.base = [lo >>> 0, hi >>> 0];
|
||||
data = { base: [lo >>> 0, hi >>> 0] };
|
||||
}
|
||||
return new Bits(data);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IPoolable } from '../../Utils/Pool/IPoolable';
|
||||
* 实现IPoolable接口,支持对象池复用以减少内存分配开销。
|
||||
*/
|
||||
class PoolableEntitySet extends Set<Entity> implements IPoolable {
|
||||
constructor(...args: unknown[]) {
|
||||
constructor(..._args: unknown[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export class ComponentSparseSet {
|
||||
const lastIndex = this._componentMasks.length - 1;
|
||||
if (entityIndex !== lastIndex) {
|
||||
// 将最后一个位掩码移动到当前位置
|
||||
this._componentMasks[entityIndex] = this._componentMasks[lastIndex];
|
||||
this._componentMasks[entityIndex] = this._componentMasks[lastIndex]!;
|
||||
}
|
||||
this._componentMasks.pop();
|
||||
}
|
||||
@@ -165,7 +165,7 @@ export class ComponentSparseSet {
|
||||
}
|
||||
|
||||
if (componentTypes.length === 1) {
|
||||
return this.queryByComponent(componentTypes[0]);
|
||||
return this.queryByComponent(componentTypes[0]!);
|
||||
}
|
||||
|
||||
// 构建目标位掩码
|
||||
@@ -182,7 +182,7 @@ export class ComponentSparseSet {
|
||||
|
||||
// 遍历所有实体,检查位掩码匹配
|
||||
this._entities.forEach((entity, index) => {
|
||||
const entityMask = this._componentMasks[index];
|
||||
const entityMask = this._componentMasks[index]!;
|
||||
if (BitMask64Utils.hasAll(entityMask, targetMask)) {
|
||||
result.add(entity);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ export class ComponentSparseSet {
|
||||
}
|
||||
|
||||
if (componentTypes.length === 1) {
|
||||
return this.queryByComponent(componentTypes[0]);
|
||||
return this.queryByComponent(componentTypes[0]!);
|
||||
}
|
||||
|
||||
// 构建目标位掩码
|
||||
@@ -225,7 +225,7 @@ export class ComponentSparseSet {
|
||||
|
||||
// 遍历所有实体,检查位掩码匹配
|
||||
this._entities.forEach((entity, index) => {
|
||||
const entityMask = this._componentMasks[index];
|
||||
const entityMask = this._componentMasks[index]!;
|
||||
if (BitMask64Utils.hasAny(entityMask, targetMask)) {
|
||||
result.add(entity);
|
||||
}
|
||||
@@ -251,9 +251,9 @@ export class ComponentSparseSet {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entityMask = this._componentMasks[entityIndex];
|
||||
const entityMask = this._componentMasks[entityIndex]!;
|
||||
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||||
|
||||
|
||||
return BitMask64Utils.hasAny(entityMask, componentMask);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ export class ComponentSparseSet {
|
||||
*/
|
||||
public forEach(callback: (entity: Entity, mask: BitMask64Data, index: number) => void): void {
|
||||
this._entities.forEach((entity, index) => {
|
||||
callback(entity, this._componentMasks[index], index);
|
||||
callback(entity, this._componentMasks[index]!, index);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ export class EntityList {
|
||||
const idsToRecycle: number[] = [];
|
||||
|
||||
for (let i = this.buffer.length - 1; i >= 0; i--) {
|
||||
idsToRecycle.push(this.buffer[i].id);
|
||||
this.buffer[i].destroy();
|
||||
idsToRecycle.push(this.buffer[i]!.id);
|
||||
this.buffer[i]!.destroy();
|
||||
}
|
||||
|
||||
// 批量回收ID
|
||||
@@ -142,7 +142,7 @@ export class EntityList {
|
||||
*/
|
||||
public findEntity(name: string): Entity | null {
|
||||
const entities = this._nameToEntities.get(name);
|
||||
return entities && entities.length > 0 ? entities[0] : null;
|
||||
return entities && entities.length > 0 ? entities[0]! : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,9 +209,9 @@ export class Matcher {
|
||||
all: [...this.condition.all],
|
||||
any: [...this.condition.any],
|
||||
none: [...this.condition.none],
|
||||
tag: this.condition.tag,
|
||||
name: this.condition.name,
|
||||
component: this.condition.component
|
||||
...(this.condition.tag !== undefined && { tag: this.condition.tag }),
|
||||
...(this.condition.name !== undefined && { name: this.condition.name }),
|
||||
...(this.condition.component !== undefined && { component: this.condition.component })
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export class SparseSet<T> {
|
||||
|
||||
// 如果不是最后一个元素,则与最后一个元素交换
|
||||
if (index !== lastIndex) {
|
||||
const lastItem = this._dense[lastIndex];
|
||||
const lastItem = this._dense[lastIndex]!;
|
||||
this._dense[index] = lastItem;
|
||||
this._sparse.set(lastItem, index);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ export class SparseSet<T> {
|
||||
*/
|
||||
public forEach(callback: (item: T, index: number) => void): void {
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
callback(this._dense[i], i);
|
||||
callback(this._dense[i]!, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export class SparseSet<T> {
|
||||
public map<U>(callback: (item: T, index: number) => U): U[] {
|
||||
const result: U[] = [];
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
result.push(callback(this._dense[i], i));
|
||||
result.push(callback(this._dense[i]!, i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -167,8 +167,8 @@ export class SparseSet<T> {
|
||||
public filter(predicate: (item: T, index: number) => boolean): T[] {
|
||||
const result: T[] = [];
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
if (predicate(this._dense[i], i)) {
|
||||
result.push(this._dense[i]);
|
||||
if (predicate(this._dense[i]!, i)) {
|
||||
result.push(this._dense[i]!);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -182,7 +182,7 @@ export class SparseSet<T> {
|
||||
*/
|
||||
public find(predicate: (item: T, index: number) => boolean): T | undefined {
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
if (predicate(this._dense[i], i)) {
|
||||
if (predicate(this._dense[i]!, i)) {
|
||||
return this._dense[i];
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,7 @@ export class SparseSet<T> {
|
||||
*/
|
||||
public some(predicate: (item: T, index: number) => boolean): boolean {
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
if (predicate(this._dense[i], i)) {
|
||||
if (predicate(this._dense[i]!, i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export class SparseSet<T> {
|
||||
*/
|
||||
public every(predicate: (item: T, index: number) => boolean): boolean {
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
if (!predicate(this._dense[i], i)) {
|
||||
if (!predicate(this._dense[i]!, i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ export class SparseSet<T> {
|
||||
|
||||
// 检查映射关系的正确性
|
||||
for (let i = 0; i < this._dense.length; i++) {
|
||||
const item = this._dense[i];
|
||||
const item = this._dense[i]!;
|
||||
const mappedIndex = this._sparse.get(item);
|
||||
if (mappedIndex !== i) {
|
||||
return false;
|
||||
|
||||
@@ -110,8 +110,9 @@ export class WorldManager implements IService {
|
||||
|
||||
const worldConfig: IWorldConfig = {
|
||||
name: worldId,
|
||||
debug: this._config.debug,
|
||||
...config
|
||||
...(this._config.debug !== undefined && { debug: this._config.debug }),
|
||||
...(config?.maxScenes !== undefined && { maxScenes: config.maxScenes }),
|
||||
...(config?.autoCleanup !== undefined && { autoCleanup: config.autoCleanup })
|
||||
};
|
||||
|
||||
const world = new World(worldConfig);
|
||||
|
||||
@@ -99,7 +99,7 @@ export class PlatformDetector {
|
||||
platform,
|
||||
confident,
|
||||
features,
|
||||
adapterClass
|
||||
...(adapterClass && { adapterClass })
|
||||
};
|
||||
}
|
||||
|
||||
@@ -195,11 +195,11 @@ export class PlatformDetector {
|
||||
const info: Record<string, any> = {};
|
||||
|
||||
// 基础检测
|
||||
info.userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown';
|
||||
info.platform = typeof navigator !== 'undefined' ? navigator.platform : 'unknown';
|
||||
info['userAgent'] = typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown';
|
||||
info['platform'] = typeof navigator !== 'undefined' ? navigator.platform : 'unknown';
|
||||
|
||||
// 全局对象检测
|
||||
info.globalObjects = {
|
||||
info['globalObjects'] = {
|
||||
window: typeof window !== 'undefined',
|
||||
document: typeof document !== 'undefined',
|
||||
navigator: typeof navigator !== 'undefined',
|
||||
@@ -210,7 +210,7 @@ export class PlatformDetector {
|
||||
};
|
||||
|
||||
// Worker相关检测
|
||||
info.workerSupport = {
|
||||
info['workerSupport'] = {
|
||||
Worker: typeof Worker !== 'undefined',
|
||||
SharedWorker: typeof SharedWorker !== 'undefined',
|
||||
ServiceWorker: typeof navigator !== 'undefined' && 'serviceWorker' in navigator,
|
||||
@@ -219,13 +219,13 @@ export class PlatformDetector {
|
||||
};
|
||||
|
||||
// 性能相关检测
|
||||
info.performance = {
|
||||
info['performance'] = {
|
||||
performanceNow: typeof performance !== 'undefined' && typeof performance.now === 'function',
|
||||
hardwareConcurrency: typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : undefined
|
||||
};
|
||||
|
||||
// 其他API检测
|
||||
info.apiSupport = {
|
||||
info['apiSupport'] = {
|
||||
Blob: typeof Blob !== 'undefined',
|
||||
URL: typeof URL !== 'undefined',
|
||||
createObjectURL: typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function',
|
||||
|
||||
@@ -2,15 +2,12 @@ import type { Core } from '../Core';
|
||||
import type { ServiceContainer } from '../Core/ServiceContainer';
|
||||
import { IPlugin } from '../Core/Plugin';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
import type { Scene } from '../ECS/Scene';
|
||||
import type { IScene } from '../ECS/IScene';
|
||||
import type { Entity } from '../ECS/Entity';
|
||||
import type { Component } from '../ECS/Component';
|
||||
import type { EntitySystem } from '../ECS/Systems/EntitySystem';
|
||||
import { WorldManager } from '../ECS/WorldManager';
|
||||
import { Injectable, Inject } from '../Core/DI/Decorators';
|
||||
import { Injectable } from '../Core/DI/Decorators';
|
||||
import type { IService } from '../Core/ServiceContainer';
|
||||
import type { PerformanceData } from '../Utils/PerformanceMonitor';
|
||||
|
||||
const logger = createLogger('DebugPlugin');
|
||||
|
||||
@@ -115,7 +112,7 @@ export class DebugPlugin implements IPlugin, IService {
|
||||
/**
|
||||
* 安装插件
|
||||
*/
|
||||
async install(core: Core, services: ServiceContainer): Promise<void> {
|
||||
async install(_core: Core, services: ServiceContainer): Promise<void> {
|
||||
this.worldManager = services.resolve(WorldManager);
|
||||
|
||||
logger.info('ECS Debug Plugin installed');
|
||||
@@ -222,16 +219,18 @@ export class DebugPlugin implements IPlugin, IService {
|
||||
private getSystemInfo(system: EntitySystem): SystemDebugInfo {
|
||||
const perfStats = system.getPerformanceStats();
|
||||
|
||||
const performance = perfStats ? {
|
||||
avgExecutionTime: perfStats.averageTime,
|
||||
maxExecutionTime: perfStats.maxTime,
|
||||
totalCalls: perfStats.executionCount
|
||||
} : undefined;
|
||||
|
||||
return {
|
||||
name: system.constructor.name,
|
||||
enabled: system.enabled,
|
||||
updateOrder: system.updateOrder,
|
||||
entityCount: system.entities.length,
|
||||
performance: perfStats ? {
|
||||
avgExecutionTime: perfStats.averageTime,
|
||||
maxExecutionTime: perfStats.maxTime,
|
||||
totalCalls: perfStats.executionCount
|
||||
} : undefined
|
||||
...(performance !== undefined && { performance })
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -139,8 +139,8 @@ export class ComponentDataCollector {
|
||||
const keys = Object.keys(item);
|
||||
for (let i = 0; i < Math.min(keys.length, 20); i++) {
|
||||
const key = keys[i];
|
||||
if (key === 'entity' || key === '_entity' || key === 'constructor') continue;
|
||||
|
||||
if (!key || key === 'entity' || key === '_entity' || key === 'constructor') continue;
|
||||
|
||||
const value = item[key];
|
||||
itemSize += key.length * 2;
|
||||
|
||||
@@ -233,19 +233,20 @@ export class ComponentDataCollector {
|
||||
|
||||
for (let i = 0; i < maxProps; i++) {
|
||||
const key = ownKeys[i];
|
||||
|
||||
if (key === 'constructor' ||
|
||||
if (!key) continue;
|
||||
|
||||
if (key === 'constructor' ||
|
||||
key === '__proto__' ||
|
||||
key === 'entity' ||
|
||||
key === 'entity' ||
|
||||
key === '_entity' ||
|
||||
key.startsWith('_cc_') ||
|
||||
key.startsWith('__')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
size += 16 + (key.length * 2);
|
||||
|
||||
|
||||
const value = obj[key];
|
||||
if (value !== undefined && value !== null) {
|
||||
size += this.estimateObjectSize(value, visited, depth + 1);
|
||||
|
||||
@@ -235,7 +235,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
}
|
||||
}
|
||||
|
||||
public update(deltaTime?: number): void {
|
||||
public update(_deltaTime?: number): void {
|
||||
if (!this.isRunning || !this.config.enabled) return;
|
||||
|
||||
this.frameCounter++;
|
||||
@@ -728,7 +728,7 @@ export class DebugManager implements IService, IUpdatable {
|
||||
const keys = Object.keys(system);
|
||||
for (let i = 0; i < Math.min(keys.length, 15); i++) {
|
||||
const key = keys[i];
|
||||
if (key === 'entities' || key === 'scene' || key === 'constructor') continue;
|
||||
if (!key || key === 'entities' || key === 'scene' || key === 'constructor') continue;
|
||||
|
||||
const value = (system as Record<string, unknown>)[key];
|
||||
size += key.length * 2;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IEntityDebugData } from '../../Types';
|
||||
import { Entity } from '../../ECS/Entity';
|
||||
import { Component } from '../../ECS/Component';
|
||||
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from '../../ECS/Decorators';
|
||||
import { getComponentInstanceTypeName } from '../../ECS/Decorators';
|
||||
import { IScene } from '../../ECS/IScene';
|
||||
|
||||
/**
|
||||
@@ -369,21 +369,6 @@ export class EntityDataCollector {
|
||||
}
|
||||
|
||||
|
||||
private getArchetypeDistribution(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
|
||||
const distribution = new Map<string, number>();
|
||||
|
||||
if (entityContainer && entityContainer.entities) {
|
||||
entityContainer.entities.forEach((entity: any) => {
|
||||
const signature = entity.componentMask?.toString() || '0';
|
||||
const existing = distribution.get(signature);
|
||||
distribution.set(signature, (existing || 0) + 1);
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(distribution.entries())
|
||||
.map(([signature, count]) => ({ signature, count, memory: 0 }))
|
||||
.sort((a, b) => b.count - a.count);
|
||||
}
|
||||
|
||||
private getArchetypeDistributionWithMemory(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
|
||||
const distribution = new Map<string, { count: number; memory: number; componentTypes: string[] }>();
|
||||
@@ -419,22 +404,6 @@ export class EntityDataCollector {
|
||||
}
|
||||
|
||||
|
||||
private getTopEntitiesByComponents(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
||||
if (!entityContainer || !entityContainer.entities) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return entityContainer.entities
|
||||
.map((entity: any) => ({
|
||||
id: entity.id.toString(),
|
||||
name: entity.name || `Entity_${entity.id}`,
|
||||
componentCount: entity.components?.length || 0,
|
||||
memory: 0
|
||||
}))
|
||||
.sort((a: any, b: any) => b.componentCount - a.componentCount);
|
||||
}
|
||||
|
||||
|
||||
private getTopEntitiesByComponentsWithMemory(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
|
||||
if (!entityContainer || !entityContainer.entities) {
|
||||
return [];
|
||||
@@ -527,14 +496,14 @@ export class EntityDataCollector {
|
||||
|
||||
for (let i = 0; i < maxKeys; i++) {
|
||||
const key = keys[i];
|
||||
if (excludeKeys.includes(key) ||
|
||||
key === 'constructor' ||
|
||||
if (!key || excludeKeys.includes(key) ||
|
||||
key === 'constructor' ||
|
||||
key === '__proto__' ||
|
||||
key.startsWith('_cc_') ||
|
||||
key.startsWith('__')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const value = item[key];
|
||||
itemSize += key.length * 2;
|
||||
|
||||
@@ -736,12 +705,12 @@ export class EntityDataCollector {
|
||||
|
||||
// 如果没有找到任何属性,添加一些调试信息
|
||||
if (Object.keys(properties).length === 0) {
|
||||
properties._info = '该组件没有公开属性';
|
||||
properties._componentId = getComponentInstanceTypeName(component);
|
||||
properties['_info'] = '该组件没有公开属性';
|
||||
properties['_componentId'] = getComponentInstanceTypeName(component);
|
||||
}
|
||||
} catch (error) {
|
||||
properties._error = '属性提取失败';
|
||||
properties._componentId = getComponentInstanceTypeName(component);
|
||||
properties['_error'] = '属性提取失败';
|
||||
properties['_componentId'] = getComponentInstanceTypeName(component);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Time } from '../Time';
|
||||
export class PerformanceDataCollector {
|
||||
private frameTimeHistory: number[] = [];
|
||||
private maxHistoryLength: number = 60;
|
||||
private lastGCCount: number = 0;
|
||||
private gcCollections: number = 0;
|
||||
private lastMemoryCheck: number = 0;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ export class WebSocketManager {
|
||||
private isConnected: boolean = false;
|
||||
private reconnectAttempts: number = 0;
|
||||
private maxReconnectAttempts: number = 5;
|
||||
private reconnectInterval: number = 2000;
|
||||
private url: string;
|
||||
private autoReconnect: boolean;
|
||||
private reconnectTimer?: ReturnType<typeof setTimeout>;
|
||||
@@ -67,7 +66,7 @@ export class WebSocketManager {
|
||||
if (this.ws) {
|
||||
this.autoReconnect = false; // 主动断开时不自动重连
|
||||
this.ws.close();
|
||||
this.ws = undefined;
|
||||
delete (this as any).ws;
|
||||
}
|
||||
this.isConnected = false;
|
||||
}
|
||||
@@ -101,13 +100,6 @@ export class WebSocketManager {
|
||||
this.maxReconnectAttempts = attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置重连间隔
|
||||
*/
|
||||
public setReconnectInterval(interval: number): void {
|
||||
this.reconnectInterval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计划重连
|
||||
*/
|
||||
@@ -120,7 +112,7 @@ export class WebSocketManager {
|
||||
this.reconnectAttempts++;
|
||||
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.connect().catch(error => {
|
||||
this.connect().catch(_error => {
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ export class LoggerManager {
|
||||
|
||||
this._loggerFactory = factory;
|
||||
// 清空已创建的 logger, 下次获取时使用新工厂方法
|
||||
this._defaultLogger = undefined;
|
||||
delete (this as any)._defaultLogger;
|
||||
this._loggers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,37 +108,8 @@ import type { IService } from '../Core/ServiceContainer';
|
||||
export class PerformanceMonitor implements IService {
|
||||
private _systemData = new Map<string, PerformanceData>();
|
||||
private _systemStats = new Map<string, PerformanceStats>();
|
||||
private _warnings: PerformanceWarning[] = [];
|
||||
private _isEnabled = false;
|
||||
private _maxRecentSamples = 60; // 保留最近60帧的数据
|
||||
private _maxWarnings = 100; // 最大警告数量
|
||||
|
||||
// 性能阈值配置
|
||||
private _thresholds: PerformanceThresholds = {
|
||||
executionTime: { warning: 16.67, critical: 33.33 }, // 60fps和30fps对应的帧时间
|
||||
memoryUsage: { warning: 100, critical: 200 }, // MB
|
||||
cpuUsage: { warning: 70, critical: 90 }, // 百分比
|
||||
fps: { warning: 45, critical: 30 },
|
||||
entityCount: { warning: 1000, critical: 5000 }
|
||||
};
|
||||
|
||||
// FPS监控
|
||||
private _fpsHistory: number[] = [];
|
||||
private _lastFrameTime = 0;
|
||||
private _frameCount = 0;
|
||||
private _fpsUpdateInterval = 1000; // 1秒更新一次FPS
|
||||
private _lastFpsUpdate = 0;
|
||||
private _currentFps = 60;
|
||||
|
||||
// 内存监控
|
||||
private _memoryCheckInterval = 5000; // 5秒检查一次内存
|
||||
private _lastMemoryCheck = 0;
|
||||
private _memoryHistory: number[] = [];
|
||||
|
||||
// GC监控
|
||||
private _gcCount = 0;
|
||||
private _lastGcCheck = 0;
|
||||
private _gcCheckInterval = 1000;
|
||||
|
||||
constructor() {}
|
||||
|
||||
@@ -168,7 +139,7 @@ export class PerformanceMonitor implements IService {
|
||||
* @param systemName 系统名称
|
||||
* @returns 开始时间戳
|
||||
*/
|
||||
public startMonitoring(systemName: string): number {
|
||||
public startMonitoring(_systemName: string): number {
|
||||
if (!this._isEnabled) {
|
||||
return 0;
|
||||
}
|
||||
@@ -397,9 +368,6 @@ export class PerformanceMonitor implements IService {
|
||||
public dispose(): void {
|
||||
this._systemData.clear();
|
||||
this._systemStats.clear();
|
||||
this._warnings = [];
|
||||
this._fpsHistory = [];
|
||||
this._memoryHistory = [];
|
||||
this._isEnabled = false;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ export class TimerManager implements IService, IUpdatable {
|
||||
|
||||
public update() {
|
||||
for (let i = this._timers.length - 1; i >= 0; i --){
|
||||
if (this._timers[i].tick()){
|
||||
this._timers[i].unload();
|
||||
if (this._timers[i]!.tick()){
|
||||
this._timers[i]!.unload();
|
||||
this._timers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,24 +49,24 @@ describe('DI - 依赖注入装饰器测试', () => {
|
||||
|
||||
describe('@Injectable 装饰器', () => {
|
||||
test('应该正确标记类为可注入', () => {
|
||||
expect(isInjectable(SimpleService)).toBe(true);
|
||||
expect(isInjectable(DependentService)).toBe(true);
|
||||
expect(isInjectable(SimpleService as any)).toBe(true);
|
||||
expect(isInjectable(DependentService as any)).toBe(true);
|
||||
});
|
||||
|
||||
test('未标记的类不应该是可注入的', () => {
|
||||
expect(isInjectable(NonInjectableService)).toBe(false);
|
||||
expect(isInjectable(NonInjectableService as any)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@Inject 装饰器', () => {
|
||||
test('应该记录参数注入元数据', () => {
|
||||
const metadata = getInjectMetadata(DependentService);
|
||||
const metadata = getInjectMetadata(DependentService as any);
|
||||
expect(metadata.size).toBe(1);
|
||||
expect(metadata.get(0)).toBe(SimpleService);
|
||||
});
|
||||
|
||||
test('应该记录多个参数的注入元数据', () => {
|
||||
const metadata = getInjectMetadata(MultiDependencyService);
|
||||
const metadata = getInjectMetadata(MultiDependencyService as any);
|
||||
expect(metadata.size).toBe(2);
|
||||
expect(metadata.get(0)).toBe(SimpleService);
|
||||
expect(metadata.get(1)).toBe(DependentService);
|
||||
|
||||
@@ -17,7 +17,9 @@ class DependentService {
|
||||
public testService?: TestService;
|
||||
|
||||
constructor(...args: unknown[]) {
|
||||
this.testService = args[0] as TestService | undefined;
|
||||
if (args[0] !== undefined) {
|
||||
this.testService = args[0] as TestService;
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
@@ -507,10 +507,10 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
||||
expect(allStats.has('PositionComponent')).toBe(true);
|
||||
|
||||
const testStats = allStats.get('TestComponent');
|
||||
expect(testStats.usedSlots).toBe(2);
|
||||
|
||||
expect(testStats?.usedSlots).toBe(2);
|
||||
|
||||
const positionStats = allStats.get('PositionComponent');
|
||||
expect(positionStats.usedSlots).toBe(1);
|
||||
expect(positionStats?.usedSlots).toBe(1);
|
||||
});
|
||||
|
||||
test('应该能够清空所有存储器', () => {
|
||||
@@ -591,7 +591,7 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
||||
expect(manager.hasComponent(2, TestComponent)).toBe(true);
|
||||
|
||||
const stats = manager.getAllStats();
|
||||
expect(stats.get('TestComponent').usedSlots).toBeLessThan(entityCount);
|
||||
expect(stats.get('TestComponent')?.usedSlots).toBeLessThan(entityCount);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -108,8 +108,8 @@ describe('WorldManager', () => {
|
||||
};
|
||||
|
||||
const world = worldManager.createWorld('configured-world', worldConfig);
|
||||
|
||||
expect(world.name).toBe('ConfiguredWorld');
|
||||
|
||||
expect(world.name).toBe('configured-world');
|
||||
});
|
||||
|
||||
test('重复的World ID应该抛出错误', () => {
|
||||
|
||||
@@ -7,11 +7,6 @@
|
||||
// 设置测试超时时间(毫秒)
|
||||
jest.setTimeout(10000);
|
||||
|
||||
// 模拟控制台方法以减少测试输出噪音
|
||||
const originalConsoleLog = console.log;
|
||||
const originalConsoleWarn = console.warn;
|
||||
const originalConsoleError = console.error;
|
||||
|
||||
// 在测试环境中可以选择性地静默某些日志
|
||||
beforeAll(() => {
|
||||
// 在测试开始前确保 WorldManager 使用无定时器配置
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"importHelpers": true,
|
||||
|
||||
18
packages/core/tsconfig.test.json
Normal file
18
packages/core/tsconfig.test.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
},
|
||||
"include": [
|
||||
"tests/**/*",
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"bin"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user