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

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