import { Entity } from './Entity'; import { EntityList } from './Utils/EntityList'; import { IdentifierPool } from './Utils/IdentifierPool'; import { EntitySystem } from './Systems/EntitySystem'; import { ComponentStorageManager, ComponentRegistry, ComponentType } from './Core/ComponentStorage'; import { QuerySystem } from './Core/QuerySystem'; import { TypeSafeEventSystem } from './Core/EventSystem'; import { ReferenceTracker } from './Core/ReferenceTracker'; import { IScene, ISceneConfig } from './IScene'; import { getComponentInstanceTypeName, getSystemInstanceTypeName, getSystemMetadata } from './Decorators'; import { TypedQueryBuilder } from './Core/Query/TypedQuery'; import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer'; import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer'; import { ComponentPoolManager } from './Core/ComponentPool'; import { PerformanceMonitor } from '../Utils/PerformanceMonitor'; import { ServiceContainer, type ServiceType, type IService } from '../Core/ServiceContainer'; import { createInstance, isInjectable, injectProperties } from '../Core/DI'; import { createLogger } from '../Utils/Logger'; /** * 游戏场景默认实现类 * * 实现IScene接口,提供场景的基础功能。 * 推荐使用组合而非继承的方式来构建自定义场景。 */ export class Scene implements IScene { /** * 场景名称 * * 用于标识和调试的友好名称。 */ public name: string = ''; /** * 场景自定义数据 * * 用于存储场景级别的配置和状态数据。 */ // eslint-disable-next-line @typescript-eslint/no-explicit-any public readonly sceneData: Map = new Map(); /** * 场景中的实体集合 * * 管理场景内所有实体的生命周期。 */ public readonly entities: EntityList; /** * 实体ID池 * * 用于分配和回收实体的唯一标识符。 */ public readonly identifierPool: IdentifierPool; /** * 组件存储管理器 * * 高性能的组件存储和查询系统。 */ public readonly componentStorageManager: ComponentStorageManager; /** * 查询系统 * * 基于位掩码的高性能实体查询系统。 */ public readonly querySystem: QuerySystem; /** * 事件系统 * * 类型安全的事件系统。 */ public readonly eventSystem: TypeSafeEventSystem; /** * 引用追踪器 * * 追踪Component中对Entity的引用,当Entity销毁时自动清理引用。 */ public readonly referenceTracker: ReferenceTracker; /** * 服务容器 * * 场景级别的依赖注入容器,用于管理EntitySystem和其他服务的生命周期。 * 每个Scene拥有独立的服务容器,实现场景间的隔离。 */ private readonly _services: ServiceContainer; /** * 日志记录器 */ private readonly logger: ReturnType; /** * 性能监控器缓存 * * 用于监控场景和系统的性能。从 ServiceContainer 获取。 */ private _performanceMonitor: PerformanceMonitor | null = null; /** * 场景是否已开始运行 */ private _didSceneBegin: boolean = false; /** * 系统列表缓存 */ private _cachedSystems: EntitySystem[] | null = null; /** * 系统顺序脏标记 * * 当系统增删或 updateOrder 改变时标记为 true,下次访问 systems 时重新构建缓存 */ private _systemsOrderDirty: boolean = true; /** * 系统错误计数器 * * 跟踪每个系统的错误次数,用于自动禁用频繁出错的系统 */ private _systemErrorCount: Map = new Map(); /** * 最大允许错误次数 * * 系统错误次数超过此阈值后将被自动禁用 */ private _maxErrorCount: number = 10; /** * 获取场景中所有已注册的EntitySystem * * 按updateOrder排序。使用缓存机制,仅在系统变化时重新排序。 * * @returns 系统列表 */ public get systems(): EntitySystem[] { if (!this._systemsOrderDirty && this._cachedSystems) { return this._cachedSystems; } this._cachedSystems = this._rebuildSystemsCache(); this._systemsOrderDirty = false; return this._cachedSystems; } /** * 重新构建系统缓存 * * 从服务容器中提取所有EntitySystem并排序 */ private _rebuildSystemsCache(): EntitySystem[] { const allServices = this._services.getAll(); const systems = this._filterEntitySystems(allServices); return this._sortSystemsByUpdateOrder(systems); } /** * 从服务列表中过滤出EntitySystem实例 */ private _filterEntitySystems(services: IService[]): EntitySystem[] { return services.filter((service): service is EntitySystem => service instanceof EntitySystem); } /** * 按updateOrder排序系统 */ private _sortSystemsByUpdateOrder(systems: EntitySystem[]): EntitySystem[] { return systems.sort((a, b) => a.updateOrder - b.updateOrder); } /** * 通过类型获取System实例 * * @param systemType System类型 * @returns System实例,如果未找到则返回null * * @example * ```typescript * const physics = scene.getSystem(PhysicsSystem); * if (physics) { * physics.doSomething(); * } * ``` */ public getSystem(systemType: ServiceType): T | null { return this._services.tryResolve(systemType) as T | null; } /** * 标记系统顺序为脏 * * 当系统列表或顺序发生变化时调用,使缓存失效 */ public markSystemsOrderDirty(): void { this._systemsOrderDirty = true; } /** * 获取场景的服务容器 * * 用于注册和解析场景级别的服务(如EntitySystem)。 * * @example * ```typescript * // 注册服务 * scene.services.registerSingleton(PhysicsSystem); * * // 解析服务 * const physics = scene.services.resolve(PhysicsSystem); * ``` */ public get services(): ServiceContainer { return this._services; } /** * 创建场景实例 */ constructor(config?: ISceneConfig) { this.entities = new EntityList(this); this.identifierPool = new IdentifierPool(); this.componentStorageManager = new ComponentStorageManager(); this.querySystem = new QuerySystem(); this.eventSystem = new TypeSafeEventSystem(); this.referenceTracker = new ReferenceTracker(); this._services = new ServiceContainer(); this.logger = createLogger('Scene'); if (config?.name) { this.name = config.name; } } /** * 获取性能监控器 * * 从 ServiceContainer 获取,如果未注册则创建默认实例(向后兼容) */ public get performanceMonitor(): PerformanceMonitor { if (!this._performanceMonitor) { this._performanceMonitor = this._services.tryResolve(PerformanceMonitor) ?? new PerformanceMonitor(); } return this._performanceMonitor; } /** * 初始化场景 * * 在场景创建时调用,子类可以重写此方法来设置初始实体和组件。 */ public initialize(): void {} /** * 场景开始运行时的回调 * * 在场景开始运行时调用,可以在此方法中执行场景启动逻辑。 */ public onStart(): void {} /** * 场景卸载时的回调 * * 在场景被销毁时调用,可以在此方法中执行清理工作。 */ public unload(): void {} /** * 开始场景,启动实体处理器等 * * 这个方法会启动场景。它将启动实体处理器等,并调用onStart方法。 */ public begin() { // 标记场景已开始运行并调用onStart方法 this._didSceneBegin = true; this.onStart(); } /** * 结束场景,清除实体、实体处理器等 * * 这个方法会结束场景。它将移除所有实体,结束实体处理器等,并调用unload方法。 * * 执行顺序: * 1. 调用 unload() - 用户可以在此访问实体和系统进行清理 * 2. 清理所有实体 * 3. 清空服务容器,触发所有系统的 onDestroy() * * 注意: * - onRemoved 回调不会在 Scene.end() 时触发,因为这是批量销毁场景 * - 用户清理:在 Scene.unload() 中处理(可访问实体和系统) * - 系统清理:在 System.onDestroy() 中处理(实体已被清理) */ public end() { // 标记场景已结束运行 this._didSceneBegin = false; // 先调用用户的卸载方法,此时用户可以访问实体和系统进行清理 this.unload(); // 移除所有实体 this.entities.removeAllEntities(); // 清理查询系统中的实体引用和缓存 this.querySystem.setEntities([]); // 清空组件存储 this.componentStorageManager.clear(); // 清空服务容器(会调用所有服务的dispose方法,包括所有EntitySystem) // 系统的 onDestroy 回调会在这里被触发 this._services.clear(); // 清空系统缓存 this._cachedSystems = null; this._systemsOrderDirty = true; } /** * 更新场景 */ public update() { ComponentPoolManager.getInstance().update(); this.entities.updateLists(); const systems = this.systems; for (const system of systems) { if (system.enabled) { try { system.update(); } catch (error) { this._handleSystemError(system, 'update', error); } } } for (const system of systems) { if (system.enabled) { try { system.lateUpdate(); } catch (error) { this._handleSystemError(system, 'lateUpdate', error); } } } } /** * 处理系统执行错误 * * 记录错误信息并跟踪错误次数。当系统错误次数超过阈值时自动禁用该系统。 * * @param system 出错的系统 * @param phase 错误发生的阶段(update 或 lateUpdate) * @param error 错误对象 */ private _handleSystemError(system: EntitySystem, phase: 'update' | 'lateUpdate', error: unknown): void { const errorCount = (this._systemErrorCount.get(system) || 0) + 1; this._systemErrorCount.set(system, errorCount); this.logger.error( `Error in system ${system.constructor.name}.${phase}() [${errorCount}/${this._maxErrorCount}]:`, error ); if (errorCount >= this._maxErrorCount) { system.enabled = false; this.logger.error( `System ${system.constructor.name} has been disabled due to excessive errors (${errorCount} errors)` ); } } /** * 将实体添加到此场景,并返回它 * @param name 实体名称 */ public createEntity(name: string) { const entity = new Entity(name, this.identifierPool.checkOut()); this.eventSystem.emitSync('entity:created', { entityName: name, entity, scene: this }); return this.addEntity(entity); } /** * 清除所有EntitySystem的实体缓存 * 当实体或组件发生变化时调用 */ public clearSystemEntityCaches(): void { for (const system of this.systems) { system.clearEntityCache(); } } /** * 在场景的实体列表中添加一个实体 * @param entity 要添加的实体 * @param deferCacheClear 是否延迟缓存清理(用于批量操作) */ public addEntity(entity: Entity, deferCacheClear: boolean = false) { this.entities.add(entity); entity.scene = this; // 将实体添加到查询系统(可延迟缓存清理) this.querySystem.addEntity(entity, deferCacheClear); // 清除系统缓存以确保系统能及时发现新实体 if (!deferCacheClear) { this.clearSystemEntityCaches(); } // 触发实体添加事件 this.eventSystem.emitSync('entity:added', { entity, scene: this }); return entity; } /** * 批量创建实体(高性能版本) * @param count 要创建的实体数量 * @param namePrefix 实体名称前缀 * @returns 创建的实体列表 */ public createEntities(count: number, namePrefix: string = 'Entity'): Entity[] { const entities: Entity[] = []; // 批量创建实体对象,不立即添加到系统 for (let i = 0; i < count; i++) { const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut()); entity.scene = this; entities.push(entity); } // 批量添加到实体列表 for (const entity of entities) { this.entities.add(entity); } // 批量添加到查询系统(无重复检查,性能最优) this.querySystem.addEntitiesUnchecked(entities); // 批量触发事件(可选,减少事件开销) this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count }); return entities; } /** * 批量销毁实体 */ public destroyEntities(entities: Entity[]): void { if (entities.length === 0) return; for (const entity of entities) { entity.setDestroyedState(true); } for (const entity of entities) { entity.removeAllComponents(); } for (const entity of entities) { this.entities.remove(entity); this.querySystem.removeEntity(entity); } this.querySystem.clearCache(); this.clearSystemEntityCaches(); } /** * 从场景中删除所有实体 */ public destroyAllEntities() { this.entities.removeAllEntities(); this.querySystem.setEntities([]); } /** * 搜索并返回第一个具有名称的实体 * @param name 实体名称 */ public findEntity(name: string): Entity | null { return this.entities.findEntity(name); } /** * 根据ID查找实体 * @param id 实体ID */ public findEntityById(id: number): Entity | null { return this.entities.findEntityById(id); } /** * 根据标签查找实体 * @param tag 实体标签 */ public findEntitiesByTag(tag: number): Entity[] { const result: Entity[] = []; for (const entity of this.entities.buffer) { if (entity.tag === tag) { result.push(entity); } } return result; } /** * 根据名称查找实体(别名方法) * * @param name 实体名称 * @deprecated 请使用 findEntity() 代替此方法 */ public getEntityByName(name: string): Entity | null { return this.findEntity(name); } /** * 根据标签查找实体(别名方法) * * @param tag 实体标签 * @deprecated 请使用 findEntitiesByTag() 代替此方法 */ public getEntitiesByTag(tag: number): Entity[] { return this.findEntitiesByTag(tag); } /** * 查询拥有所有指定组件的实体 * * @param componentTypes - 组件类型数组 * @returns 查询结果 * * @example * ```typescript * const result = scene.queryAll(Position, Velocity); * for (const entity of result.entities) { * const pos = entity.getComponent(Position); * const vel = entity.getComponent(Velocity); * } * ``` */ public queryAll(...componentTypes: ComponentType[]): { entities: readonly Entity[] } { return this.querySystem.queryAll(...componentTypes); } /** * 查询拥有任意一个指定组件的实体 * * @param componentTypes - 组件类型数组 * @returns 查询结果 */ public queryAny(...componentTypes: ComponentType[]): { entities: readonly Entity[] } { return this.querySystem.queryAny(...componentTypes); } /** * 查询不包含指定组件的实体 * * @param componentTypes - 组件类型数组 * @returns 查询结果 */ public queryNone(...componentTypes: ComponentType[]): { entities: readonly Entity[] } { return this.querySystem.queryNone(...componentTypes); } /** * 创建类型安全的查询构建器 * * @returns 查询构建器,支持链式调用 * * @example * ```typescript * // 使用查询构建器 * const matcher = scene.query() * .withAll(Position, Velocity) * .withNone(Disabled) * .buildMatcher(); * * // 在System中使用 * class MovementSystem extends EntitySystem { * constructor() { * super(matcher); * } * } * ``` */ public query(): TypedQueryBuilder { return new TypedQueryBuilder(); } /** * 在场景中添加一个EntitySystem处理器 * * 支持两种使用方式: * 1. 传入类型(推荐):自动使用DI创建实例,支持@Injectable和@InjectProperty装饰器 * 2. 传入实例:直接使用提供的实例 * * @param systemTypeOrInstance 系统类型或系统实例 * @returns 添加的处理器实例 * * @example * ```typescript * // 方式1:传入类型,自动DI(推荐) * @Injectable() * class PhysicsSystem extends EntitySystem { * @InjectProperty(CollisionSystem) * private collision!: CollisionSystem; * * constructor() { * super(Matcher.empty().all(Transform)); * } * } * scene.addEntityProcessor(PhysicsSystem); * * // 方式2:传入实例 * const system = new MySystem(); * scene.addEntityProcessor(system); * ``` */ public addEntityProcessor(systemTypeOrInstance: ServiceType | T): T { let system: T; let constructor: ServiceType; if (typeof systemTypeOrInstance === 'function') { constructor = systemTypeOrInstance; if (this._services.isRegistered(constructor)) { const existingSystem = this._services.resolve(constructor) as T; this.logger.debug(`System ${constructor.name} already registered, returning existing instance`); return existingSystem; } if (isInjectable(constructor)) { system = createInstance(constructor, this._services) as T; } else { system = new constructor() as T; } } else { system = systemTypeOrInstance; constructor = system.constructor as ServiceType; if (this._services.isRegistered(constructor)) { const existingSystem = this._services.resolve(constructor); if (existingSystem === system) { this.logger.debug(`System ${constructor.name} instance already registered, returning it`); return system; } else { this.logger.warn( `Attempting to register a different instance of ${constructor.name}, ` + 'but type is already registered. Returning existing instance.' ); return existingSystem as T; } } } system.scene = this; system.setPerformanceMonitor(this.performanceMonitor); const metadata = getSystemMetadata(constructor); if (metadata?.updateOrder !== undefined) { system.setUpdateOrder(metadata.updateOrder); } if (metadata?.enabled !== undefined) { system.enabled = metadata.enabled; } this._services.registerInstance(constructor, system); // 标记系统列表已变化 this.markSystemsOrderDirty(); injectProperties(system, this._services); system.initialize(); this.logger.debug(`System ${constructor.name} registered and initialized`); return system; } /** * 批量注册EntitySystem到场景(使用DI) * * 自动按照依赖顺序注册多个System。 * 所有System必须使用@Injectable装饰器标记。 * * @param systemTypes System类型数组 * @returns 注册的System实例数组 * * @example * ```typescript * @Injectable() * @ECSSystem('Collision', { updateOrder: 5 }) * class CollisionSystem extends EntitySystem implements IService { * constructor() { super(Matcher.empty().all(Collider)); } * dispose() {} * } * * @Injectable() * @ECSSystem('Physics', { updateOrder: 10 }) * class PhysicsSystem extends EntitySystem implements IService { * @InjectProperty(CollisionSystem) * private collision!: CollisionSystem; * * constructor() { * super(Matcher.empty().all(Transform, RigidBody)); * } * dispose() {} * } * * // 批量注册(自动解析依赖顺序) * scene.registerSystems([ * CollisionSystem, * PhysicsSystem, // 自动注入CollisionSystem * RenderSystem * ]); * ``` */ public registerSystems(systemTypes: Array>): EntitySystem[] { const registeredSystems: EntitySystem[] = []; for (const systemType of systemTypes) { const system = this.addEntityProcessor(systemType); registeredSystems.push(system); } return registeredSystems; } /** * 添加系统到场景(addEntityProcessor的别名) * @param system 系统 */ public addSystem(system: EntitySystem) { return this.addEntityProcessor(system); } /** * 从场景中删除EntitySystem处理器 * @param processor 要删除的处理器 */ public removeEntityProcessor(processor: EntitySystem): void { const constructor = processor.constructor as ServiceType; // 从ServiceContainer移除 this._services.unregister(constructor); // 标记系统列表已变化 this.markSystemsOrderDirty(); // 重置System状态 processor.reset(); } /** * 从场景中删除系统(removeEntityProcessor的别名) * @param system 系统 */ public removeSystem(system: EntitySystem) { this.removeEntityProcessor(system); } /** * 获取指定类型的EntitySystem处理器 * * @deprecated 推荐使用依赖注入代替此方法。使用 `scene.services.resolve(SystemType)` 或使用 `@InjectProperty(SystemType)` 装饰器。 * * @param type 处理器类型 * @returns 处理器实例,如果未找到则返回null * * @example * ```typescript * @Injectable() * class MySystem extends EntitySystem { * @InjectProperty(PhysicsSystem) * private physics!: PhysicsSystem; * * constructor() { * super(Matcher.empty()); * } * } * ``` */ public getEntityProcessor(type: new (...args: unknown[]) => T): T | null { return this._services.tryResolve(type as ServiceType) as T | null; } /** * 获取场景统计信息 */ public getStats(): { entityCount: number; processorCount: number; componentStorageStats: Map; } { return { entityCount: this.entities.count, processorCount: this.systems.length, componentStorageStats: this.componentStorageManager.getAllStats() }; } /** * 获取场景的调试信息 */ public getDebugInfo(): { name: string; entityCount: number; processorCount: number; isRunning: boolean; entities: Array<{ name: string; id: number; componentCount: number; componentTypes: string[]; }>; processors: Array<{ name: string; updateOrder: number; entityCount: number; }>; componentStats: Map; } { const systems = this.systems; return { name: this.name || this.constructor.name, entityCount: this.entities.count, processorCount: systems.length, isRunning: this._didSceneBegin, entities: this.entities.buffer.map((entity) => ({ name: entity.name, id: entity.id, componentCount: entity.components.length, componentTypes: entity.components.map((c) => getComponentInstanceTypeName(c)) })), processors: systems.map((processor) => ({ name: getSystemInstanceTypeName(processor), updateOrder: processor.updateOrder, entityCount: processor.entities.length })), componentStats: this.componentStorageManager.getAllStats() }; } /** * 序列化场景 * * 将场景及其所有实体、组件序列化为JSON字符串或二进制Uint8Array * * @param options 序列化选项 * @returns 序列化后的数据(JSON字符串或二进制Uint8Array) * * @example * ```typescript * // JSON格式 * const jsonData = scene.serialize({ * format: 'json', * pretty: true * }); * * // 二进制格式(更小、更快) * const binaryData = scene.serialize({ * format: 'binary' * }); * ``` */ public serialize(options?: SceneSerializationOptions): string | Uint8Array { return SceneSerializer.serialize(this, options); } /** * 反序列化场景 * * 从序列化数据恢复场景状态 * * @param saveData 序列化的数据(JSON字符串或二进制Uint8Array) * @param options 反序列化选项 * * @example * ```typescript * // 从JSON恢复(自动从ComponentRegistry获取组件类型) * scene.deserialize(jsonData, { * strategy: 'replace' * }); * * // 从二进制恢复 * scene.deserialize(binaryData, { * strategy: 'replace' * }); * ``` */ public deserialize(saveData: string | Uint8Array, options?: SceneDeserializationOptions): void { SceneSerializer.deserialize(this, saveData, options); } /** 增量序列化的基础快照 */ private _incrementalBaseSnapshot?: unknown; /** * 创建增量序列化的基础快照 * * 在需要进行增量序列化前,先调用此方法创建基础快照 * * @param options 序列化选项 * * @example * ```typescript * // 创建基础快照 * scene.createIncrementalSnapshot(); * * // 进行一些修改... * entity.addComponent(new PositionComponent(100, 200)); * * // 计算增量变更 * const incremental = scene.serializeIncremental(); * ``` */ public createIncrementalSnapshot(options?: IncrementalSerializationOptions): void { this._incrementalBaseSnapshot = IncrementalSerializer.createSnapshot(this, options); } /** * 增量序列化场景 * * 只序列化相对于基础快照的变更部分 * * @param options 序列化选项 * @returns 增量快照对象 * * @example * ```typescript * // 创建基础快照 * scene.createIncrementalSnapshot(); * * // 修改场景 * const entity = scene.createEntity('NewEntity'); * entity.addComponent(new PositionComponent(50, 100)); * * // 获取增量变更 * const incremental = scene.serializeIncremental(); * console.log(`变更数量: ${incremental.entityChanges.length}`); * * // 序列化为JSON * const json = IncrementalSerializer.serializeIncremental(incremental); * ``` */ public serializeIncremental(options?: IncrementalSerializationOptions): IncrementalSnapshot { if (!this._incrementalBaseSnapshot) { throw new Error('必须先调用 createIncrementalSnapshot() 创建基础快照'); } return IncrementalSerializer.computeIncremental(this, this._incrementalBaseSnapshot as Parameters[1], options); } /** * 应用增量变更到场景 * * @param incremental 增量快照数据(IncrementalSnapshot对象、JSON字符串或二进制Uint8Array) * @param componentRegistry 组件类型注册表(可选,默认使用全局注册表) * * @example * ```typescript * // 应用增量变更对象 * scene.applyIncremental(incrementalSnapshot); * * // 从JSON字符串应用 * const jsonData = IncrementalSerializer.serializeIncremental(snapshot, { format: 'json' }); * scene.applyIncremental(jsonData); * * // 从二进制Uint8Array应用 * const binaryData = IncrementalSerializer.serializeIncremental(snapshot, { format: 'binary' }); * scene.applyIncremental(binaryData); * ``` */ public applyIncremental( incremental: IncrementalSnapshot | string | Uint8Array, componentRegistry?: Map ): void { const isSerializedData = typeof incremental === 'string' || incremental instanceof Uint8Array; const snapshot = isSerializedData ? IncrementalSerializer.deserializeIncremental(incremental as string | Uint8Array) : (incremental as IncrementalSnapshot); const registry = componentRegistry || (ComponentRegistry.getAllComponentNames() as Map); IncrementalSerializer.applyIncremental(this, snapshot, registry); } /** * 更新增量快照基准 * * 将当前场景状态设为新的增量序列化基准 * * @param options 序列化选项 * * @example * ```typescript * // 创建初始快照 * scene.createIncrementalSnapshot(); * * // 进行一些修改并序列化 * const incremental1 = scene.serializeIncremental(); * * // 更新基准,之后的增量将基于当前状态 * scene.updateIncrementalSnapshot(); * * // 继续修改 * const incremental2 = scene.serializeIncremental(); * ``` */ public updateIncrementalSnapshot(options?: IncrementalSerializationOptions): void { this.createIncrementalSnapshot(options); } /** * 清除增量快照 * * 释放快照占用的内存 */ public clearIncrementalSnapshot(): void { this._incrementalBaseSnapshot = undefined; } /** * 检查是否有增量快照 * * @returns 如果已创建增量快照返回true */ public hasIncrementalSnapshot(): boolean { return this._incrementalBaseSnapshot !== undefined; } }