feat(core): 启用 TypeScript 最严格的类型检查 (#199)

* feat(core):  启用 TypeScript 最严格的类型检查

* ci: 配置 Codecov 以适应类型安全改进

* fix(core): 修复 CodeQL 安全警告

* fix(core): eslint.config.mjs
This commit is contained in:
YHH
2025-10-31 16:14:23 +08:00
committed by GitHub
parent 011d795361
commit c58e3411fd
56 changed files with 622 additions and 495 deletions

View 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'
]
}
];

View File

@@ -42,7 +42,7 @@ module.exports = {
verbose: true,
transform: {
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'tsconfig.json',
tsconfig: 'tsconfig.test.json',
useESM: false,
}],
},

View File

@@ -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",

View File

@@ -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配置

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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({

View File

@@ -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);

View File

@@ -15,11 +15,10 @@ import {
EventListenerConfig,
EventStats
} from './EventSystem';
import {
ECSEventType,
EventPriority,
EVENT_TYPES,
EventTypeValidator
import {
ECSEventType,
EventPriority,
EventTypeValidator
} from '../CoreEvents';
/**

View File

@@ -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;
}
/**

View File

@@ -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 })
};
}

View File

@@ -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);

View File

@@ -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的映射
*

View File

@@ -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;
}
}
}
}

View File

@@ -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';
/**

View File

@@ -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;

View File

@@ -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';

View File

@@ -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 })
}
});
}

View File

@@ -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
};

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -17,7 +17,7 @@ export abstract class PassiveSystem extends EntitySystem {
* 不进行任何处理
* @param entities 实体数组,未被使用
*/
protected override process(entities: Entity[]): void {
protected override process(_entities: Entity[]): void {
// 被动系统不进行任何处理
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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]不存在会被转为NaNNaN的位运算始终返回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]不存在会被转为NaNNaN的位运算始终返回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;
}
}
}

View File

@@ -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];
}

View File

@@ -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);
}

View File

@@ -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);
});
}

View File

@@ -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;
}
/**

View File

@@ -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 })
};
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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',

View File

@@ -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 })
};
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -143,7 +143,7 @@ export class LoggerManager {
this._loggerFactory = factory;
// 清空已创建的 logger, 下次获取时使用新工厂方法
this._defaultLogger = undefined;
delete (this as any)._defaultLogger;
this._loggers.clear();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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);
});
});
});

View File

@@ -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应该抛出错误', () => {

View File

@@ -7,11 +7,6 @@
// 设置测试超时时间(毫秒)
jest.setTimeout(10000);
// 模拟控制台方法以减少测试输出噪音
const originalConsoleLog = console.log;
const originalConsoleWarn = console.warn;
const originalConsoleError = console.error;
// 在测试环境中可以选择性地静默某些日志
beforeAll(() => {
// 在测试开始前确保 WorldManager 使用无定时器配置

View File

@@ -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,

View 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"
]
}