feat(world): 大规模世界架构重构 — WorldStore/WorldRuntime 统一持久化与流式加载#75
Merged
Conversation
实现基于 IndexedDB 的权威世界存储与运行时 chunk 流式装载架构: - WorldStore: IndexedDB 权威数据源,WorldMeta/RegionRecord/ChunkRecord 三级存储 - RegionCache: 内存缓存层,按需从 IndexedDB 加载 region,降低主线程阻塞 - PersistenceWorker: 新增 clearWorld/WorldMeta/RegionRecord 操作,支持 DB 版本升级 - Chunk.loadFromRecord: 纯装载路径,跳过 BlockScatterManager.scatter,降低装载延迟 - WorldGenerationService: 预生成阶段跨 region overflow 方块回退机制,防止跨边界结构丢失 - Player.setWorld: runtime-streaming 阶段不再隐式调 gen(),由 World 显式控制装载 - index.html: 新世界创建时 forceReset: true,自动清除旧 IndexedDB 数据 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- _mergeOverflowBlocks 返回详细的诊断数据:resolved/unresolved 数量、目标/来源 chunk 热点、距离分布 - 新增跨 region overflow 诊断测试 - 测试 mock 支持 WorldStore postMessage 接口 - 修复 teardownEnvironment 中 undefined 判断 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e large structure generation Refactor pre-generation from chunk-level independent worker calls to region-level unified worker calls. This eliminates duplicate large structure generation caused by multiple chunk tasks executing the same candidate (the original problem: spacing logic prevents structure A and B from overlapping, but can't prevent the same structure A being executed by chunk(1,1), chunk(1,2), chunk(2,1), chunk(2,2) separately). Key changes: - Extract generateChunkWithSharedState() in WorldWorker.js — reusable chunk generation function with injectable shared state (candidate index, large structure dedup set) - Add handleRegionGeneration() — coordinates 8x8 chunk region generation with shared candidate index and cross-chunk dedup - Add resolveOverflowWithinRegion() with O(1) Set-based dedup — routes overflow blocks between chunks within the same region, replacing the main thread's _mergeOverflowBlocks - Simplify WorldGenerationService._generateRegion() — from 100+ loop iterations with halo routing to a single worker.postMessage call - Remove unnecessary post-processing in region path (scatteredBlocks, meshData, AO/visibility checks) — only blockDataBlocks + routing needed for region-level generation Backward compatible: runtime chunk loading path unchanged. Old methods (_generateChunkWithRouting, _mergeOverflowBlocks) preserved. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The mock returned flat chunk data but _generateRegion expects a chunks map keyed by chunkKey, causing Object.entries to fail. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous mock only returned a single chunk, but _generateRegion expects a chunks map for the entire region. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- sync seed in region-level worker path (WorldWorker.js:1392) handleRegionGeneration now calls setSeed(seed) before generation, ensuring terrainGen/noise use the same seed as the runtime chunk path. - await structure preload before region generation (WorldWorker.js:1393) handleRegionGeneration now awaits structuresPreload, preventing silent omission of JSON-driven buildings on cold worker starts. - filter per-chunk entities to prevent 64x data bloat (WorldWorker.js:1279) generateChunkWithSharedState now filters modGunMan/rovers to only entities within the current chunk bounds before returning. - await saveRegionRecord with proper error propagation (WorldGenerationService.js) _generateRegion now rejects on persistence failure so the caller knows the region was not saved. generateInitialWorld and expandWorldIfNeeded wrap per-region calls in try/catch to skip failed regions without blocking the entire pre-generation flow. Closes #68 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… blocks - Upgrade IndexedDB version from 2 to 3 - Add world_overflow object store with keyPath regionKey - Add saveOverflowBlocks/getOverflowBlocks/removeOverflowBlocks APIs - Include world_overflow in clearWorld transaction - Update old-connection detection to check for missing world_overflow store Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add saveOverflowBlocks/getOverflowBlocks/removeOverflowBlocks wrapper methods to WorldStore, delegating to PersistenceService via saveOverflowBlocks/getOverflowBlocks/removeOverflowBlocks RPC messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…om region generation Modify resolveOverflowWithinRegion to collect and return unresolved cross-region overflow block data, so the main thread can distribute them later. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… overflow blocks - Add _crossRegionOverflowMap in constructor to stage inter-region overflow blocks. - Add _collectCrossRegionOverflow to gather unresolvedOverflowBlocks from worker results. - Add _distributeCrossRegionOverflow to merge overflow into already-generated regions in the same batch, or persist leftovers to world_overflow store for future expansion. - Add _consumeOverflowForRegion to merge previously persisted overflow blocks when a new region is generated during world expansion. - Wire collection into _generateRegion callback after saving region record. - Wire distribution into generateInitialWorld after all regions in the batch finish. - Wire consumption + re-distribution into expandWorldIfNeeded after expansion regions are generated and before bounds are updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, and consume tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n thread - Remove IndexedDB persistence for cross-region overflow blocks. Overflow is now kept in _crossRegionOverflowMap and merged automatically when the target region is generated during expansion. - Add _yieldIfNeeded helper to yield the main thread every 5000 iterations in dense loops, preventing FPS drops after game entry. - Remove _consumeOverflowForRegion and background overflow distribution from generateInitialWorld, eliminating the long loading-modal hang after pre-generation. - _generateRegion now merges pending overflow before saving the region record. - Update tests to match pure-memory semantics (DB_VERSION 3, async _collectCrossRegionOverflow, removed persistence tests). - Add design doc and implementation plan for cross-region overflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add _regionLoadPromises Map in WorldRuntime to prevent N concurrent IndexedDB reads for the same region during chunk streaming - Refactor ensureChunkData to reuse ensureRegion, eliminating duplicated cache-miss logic - Add prefetchRegions() to warm up adjacent regions ahead of player movement - Add 500ms prefetch timer in World (runtime-streaming phase only) with dispose cleanup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…e iteration Replace full blockData scan in _registerLightSources/_unregisterLightSources with a dedicated Set index that tracks light-emitting block coordinates. The index is maintained synchronously across all blockData write paths: - _injectBlockData (bulk load / record restore) - _updateBlockState (single block mutation) - removeBlocksBatch (batch deletion) - acceptScatteredBlocks / appendScatteredBlocks (worker result / overflow) This eliminates iterating 3000-4000 blockData entries per chunk during finalizeNonDeferredPhase, reducing light registration from ~8-15ms to <1ms. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…es on chunk load pendingRuntimeEntities was initialized as null and never assigned a non-null value, making the entity restore logic in finalizeNonDeferredPhase dead code. This caused zombie nests, turrets, and minecarts to be lost when chunks were loaded at runtime (player exploring new areas). Fixes both loading paths: - loadFromRecord: now merges persistence cache entities and calls finalizeNonDeferredPhase to trigger restore. - assembleEntityPhase: now includes minecarts in the entity merge and populates pendingRuntimeEntities from the merged snapshot. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…egions Prevents TypeError in test environments where worldRuntime is replaced with a partial mock that lacks the prefetchRegions method. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Design for migrating turret/minecart/zombieNest persistence from persistenceService.cache to WorldStore RegionRecord.runtimeEntities. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Detailed task-by-task plan for migrating turret/minecart/zombieNest persistence to WorldStore RegionRecord.runtimeEntities. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…in design and plan - Add `id` field to minecart data format in design doc - Replace position-based dedup with UUID-based dedup for cross-chunk moves - Add Step 5 in plan: global UUID dedup in restoreMinecartsForChunk Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ysis - 设计文档:从简单字段迁移方案重写为带根因分析的修订版,新增三层真相来源、统一生命周期、渲染与 consolidation 边界、仓储层设计 - 实现计划:从按 Manager 逐个修改改为按基础设施和生命周期重新组织,新增 RuntimeEntityRepository、激活/停用语义、旧路径兼容桥、回归验证矩阵 - 明确 blockData 与 runtimeEntities 职责边界,禁止 Manager 直接操作 WorldStore
…s on chunk unload/reload
This commit fixes the root cause where blockData was lost during chunk
unloading due to an async flush race condition, and restores the
session-level cache overlay mechanism to work correctly with the new
WorldStore architecture.
Changes:
- PersistenceService:
- Auto-create session snapshots when cache is missing in recordChangeForChunk
- Add ensureChunkSnapshot, snapshotChunkBlocks, hydrateChunkBlocks,
replaceChunkBlocks for unified session overlay management
- Fix saveChunkData to merge data without overwriting existing entities/blocks
- Chunk.loadFromRecord:
- Ensure persistenceService cache snapshot exists on entry
- Use cache.blocks as authority over chunkRecord.blockData (overlay semantics)
- Keep runtime entities recovery from cache.entities unchanged
- World (chunk unload):
- Fix unload order: snapshot blocks -> stop minecarts -> async flush ->
dispose -> delete from active set
- snapshotChunkBlocks is now synchronous and happens before any async work
- WorldRuntime:
- flushChunk/flushBeforeUnload now accept an optional blockDataSnapshot
so they no longer depend on the active chunk still being present
- TurretManager:
- Add id to turret snapshots
- Use id for deduplication, with position as compatibility fallback
- restoreTurretsForChunk reuses snapshot id via restoredId option
- ZombieNestManager:
- Add id and lastSpawnTime to nest snapshots
- Use id for deduplication
- restoreNestsForChunk reuses snapshot id and lastSpawnTime
- MinecartManager:
- Use id for deduplication in saveMinecartToSnapshot
- restoreMinecartsForChunk skips existing ids to avoid duplicates
- Tests:
- Add test-runtime-session-persistence.js covering unload/reload scenarios
- Update test-persistence.js to match auto-snapshot behavior
- Docs:
- Mark Phase 1 as complete in design and plan documents
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation Second phase of the special entity compatibility migration. Unifies the persistence path for runtime entities (turrets, zombie nests, minecarts) from persistenceService.cache.entities into worldStore ChunkRecord.runtimeEntities. Key changes: - Extend ChunkRecord format with runtimeEntities field - flushBeforeUnload now writes entities to worldStore (world_regions IndexedDB) - loadFromRecord prefers runtimeEntities (new format), falls back to cache.entities (old format) with progressive migration from world_deltas - collectSnapshot reads from worldStore instead of cache (manual save path unified) - Three entity managers gain getEntitiesForChunk(cx, cz) methods - finalizeNonDeferredPhase becomes async to support progressive migration NOTE: This is a near-complete draft that needs extensive review, testing, and manual regression verification before shipping. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r runtimeEntities - ensureChunkData/getChunkRecord/getChunkRecordsInRegion now project runtimeEntities - flushBeforeUnload serializes blockDataSnapshot (Map → plain object) - flushChunk and flushAllDirty carry runtimeEntities to prevent split-brain Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
核心问题: - flushBeforeUnload 写回 worldStore 后,region cache 未同步更新,导致同会话 reload 读到旧 chunkRecord - loadFromRecord 在无 runtimeEntities 时会无脑清空 ShadowStore,把同会话内仍存活的炮塔/巢穴误删 变更内容: - WorldRuntime.flushBeforeUnload: 写回后调用 _updateRegionCacheChunkRecord 同步更新缓存中的 chunkRecord - WorldRuntime.migrateLegacyEntities: 异步回填成功后也同步更新 region cache - WorldRuntime._updateRegionCacheChunkRecord: 新增方法,将最新 chunkRecord 写回 region cache - Chunk.loadFromRecord: 仅当 ShadowStore 也无存活数据时才清空,避免同会话内 live 数据被误清 - 新增两项测试覆盖 flushBeforeUnload 缓存同步和 ShadowStore 回退路径 关联 #69 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
统一三条 flush 路径的 blockData 取数策略为"显式快照 > dirty snapshot > region cache > live chunk", 避免仅实体脏化时回退到主线程将 live Map 整块序列化。 变更内容: - WorldRuntime._resolveSerializedBlockData: 新增,按优先级链解析序列化 blockData - WorldRuntime._resolveStaticEntities / _resolveRuntimeSeedData: 新增,统一从 chunk 或 cache 回退取数 - WorldRuntime._getSerializedBlockMetrics: 新增,计算 blockCount 和 serializedBytes - WorldRuntime._recordFlushPerf: 新增,统一 perf 埋点入口 - flushChunk / flushAllDirty / flushBeforeUnload: 重构为调用上述新方法 - flushAllDirty: 累计总 blockCount 和 serializedBytes,在最终 perf 事件中输出 - flushBeforeUnload: 增加 perf 记录 - 测试覆盖: flushChunk/flushAllDirty 复用 region cache 不触发 live Map 序列化, 以及 flush-all-dirty 的 perf 事件验证 关联 #69 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
新增 command/run-tests.js,使用 Playwright Chromium headless 模式 在 CLI 中运行浏览器测试套件,替代手动点击页面的方式。 支持 --verbose 和 --port 参数,测试失败时退出码为 1。 更新 CLAUDE.md,添加 TDD Red-Green-Refactor 工作流说明, 明确各阶段运行 run-tests.js 的时机。
…ldStore WorldRuntime 中不再直接依赖 PersistenceService,实体迁移改为通过 WorldStore.getLegacyChunkDelta 读取旧档。Chunk.loadFromRecord 不再 回退 cache.entities,仅从 chunkRecord.runtimeEntities 或同会话 ShadowStore 恢复运行时实体。 WorldStore 新增 loadChunkRecord/commitChunkRecord/commitRuntimeEntities/ getLegacyChunkDelta 等专用方法,WorldRuntime 通过 _commitChunkRecord 统一 提交,兼容新旧 WorldStore API。 同步更新测试用例,验证新路径下的迁移行为与空结构补齐逻辑, 修复 ShadowStore 未清理导致的测试间污染问题。
…istenceService 依赖 - ChunkGenerator.gen 改为从 WorldStore.loadChunkRecord 读取权威数据,并构建兼容 Worker 的 snapshot - ChunkPersistence.saveDebounced 统一通过 worldRuntime.flushChunk 写回,移除旧 saveChunkData 路径 - World unload 时统一通过 WorldRuntime.flushBeforeUnload 写回,消除 persistenceService 回退 - PlaygroundService 移除 persistenceService.cache 回退查找路径 - 新增 bootstrapping 和 WorldStore 读取路径的测试覆盖 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…O 尖峰 - flushBeforeUnload 改为将稳定快照入队而非立即 commit,chunk 卸载不再阻塞等待写盘 - 新增 flushPendingUnloadQueueWithinBudget 方法,runtime idle 时按 region 合批消费队列 - WorldStore 新增 applyRegionPatch 增量更新接口,PersistenceWorker 支持对应消息 - World.dispose 接入 flushAllPendingWork 确保退出时尽力写回剩余数据 - getRuntimeIdleStats 暴露 unload flush 队列指标用于遥测监控 - 新增大量测试覆盖入队/去重/region合批/退出flush等场景 关联 #69
- loadFromRecord 不再同步完成 build + finalize,改为只入装配队列交由调度器切片执行 - finalizeNonDeferredPhase 在 runtime-streaming 下延迟光源注册与 AO 刷新到 deferred finalize - ChunkAssemblyScheduler 限制同轮只处理初始队列任务,防止新入队后续阶段连击 - onChunkFinalized 支持 deferAORefresh 选项,避免 finalized 当帧立即刷新 AO - 新增测试覆盖装配队列切片行为与 deferred finalize 延迟逻辑 关联 #69
- Replace sourceHash with O(1) contentRevision for cache invalidation - Add data-layer fidelity tasks (WorldStore/WorldRuntime/PersistenceWorker) before runtime cache consumption - Define renderCache serialization spec for TypedArray persistence - Add contentRevision monotonicity constraints across flush/unload/patch - Mark worker fallback as P1 optional, P0 uses main-thread stable fallback - Handle cross-region overflow pollution (dirty-by-overflow -> miss) - Require Chunk instance to explicitly hold contentRevision/renderCache Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
新增基于兴趣集调度的 chunk 加载节流方案 v2,替代 renderCache 方向: - 设计文档:ChunkLoadScheduler 调度器 + 双层 consolidation 限流架构 - 实施计划:9 个 Task,从 feature flags 到 consolidation 队列的完整步骤 - 视觉对比:renderCache vs renderDelay 方案 CPU 尖峰分布对比图 关键改进: - 新增 pending 过时任务淘汰机制(玩家快速移动时丢弃旧区域 key) - consolidation 改为两层限流,保留现有 idle grace 语义不被破坏 - 首帧立即创建最近 chunk + 冷却帧控制,统一测试语义 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
设计文档升级: - 将总体策略从两条链路扩展为三条,新增 ChunkAssemblyScheduler runtime 保守准入 - 统一 onExpansionFinished retry 入口至 ChunkLoadScheduler,防止旁路洪峰 - 新增 runtime-build 单任务耗时观测指标(runtimeBuildLastMs / MaxMs / LongTaskCount) - consolidation 双层限流增加 in-flight 上限,降低 worker 回包同帧聚集概率 - 修复 _processDeferredConsolidationQueue 可能循环调用 scheduleConsolidation 的问题 计划文档同步: - 拆分 Task 6(runtime assembly 收紧)与 Task 7(观测指标) - 扩充 Task 9 测试覆盖范围,新增 in-flight 与循环调用验证 - 完成标准增加 onExpansionFinished 不绕过调度器、runtime assembly 不超标 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 新增设计约束章节:明确优化范围、非目标、正确性要求 - 精化 Worker Cache 语义:命中时刷新 LRU 顺序、clearWorld 清空缓存 - 新增 WorldRuntime 运行时缓存语义:_regionCache 允许保存部分 region - Task 1 补充:所有写路径(saveRegionRecord / applyRegionPatch / saveRegionRecordsBatch / clearWorld)同步更新 Worker cache - Task 3 补充:_upsertRegionCacheChunkRecord 注入机制,维持 flush/unload 基线 - Task 4 补充:测试需断言 _regionCache 注入、mock 改为 getChunkRecord - Task 5 新增:回归检查与性能验收步骤 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
将 ensureChunkData 的读取路径从整包 region(9MB)改为 chunk 级裁剪(~100KB), postMessage 传输量降低约 90%。 核心变更: - PersistenceWorker 新增 LRU region 缓存(最多 6 个 region),新增 getChunkRecord 方法,命中缓存时直接裁剪目标 chunk 返回,未命中时读取 DB 并缓存 - WorldStore.getChunkRecord 改为直接调用 Worker 的 getChunkRecord action, 不再在主线程裁剪整包 region - WorldRuntime.ensureChunkData 走 chunk 级读取,不再调用 ensureRegion - 新增 _upsertRegionCacheChunkRecord 维持运行时最小 region 基线(__partial 标记) - flushAllDirty 对 partial region 走 applyRegionPatch 增量写,避免覆盖未加载 chunk - 引入 __runtimeEntitiesWasDefault 标记解决 Worker 侧默认值填充导致的旧档迁移跳过问题 - 写路径深 clone(structuredClone)避免 applyRegionPatch 原地修改污染缓存 - 更新测试 mocks 从 getRegionRecord 改为 getChunkRecord,新增 partial region 测试 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
为 chunk 装载路径添加细粒度性能监控,覆盖主线程与 Worker 端各阶段耗时, 支持按 chunk 聚合的性能报告输出,便于定位装载瓶颈。 核心变更: - ChunkPerfMonitor 新增 aggregateChunkLoadPerf / printChunkLoadPerfReport / 帧级耗时监控(startFrameChunkPerf / endFrameChunkPerf) - Chunk.loadFromRecord 注入各子阶段打点(injectBlockData、staticEntities、 entityRestore、enqueueAssembly),buildMeshesFromRecord 拆分 4 个子阶段 - Chunk._injectBlockData 添加 clear / iterate / write 细分计时 - ChunkAssemblyScheduler 记录排队延迟(queueWaitMs)与 stage 执行前后状态对比 - WorldWorker 端新增 _workerPerfPhases 返回子阶段耗时(faceCulling、 buildBlockDataBlocks、buildScatteredBlocks、buildMeshData、buildRouting) - World._onChunkWorkerResult 接收并透传 Worker 端子阶段打点 - GlobalInstancedMeshManager.replaceChunkVisibleBlocks / patchChunkVisibleBlocks 添加性能记录 - World 暴露 printChunkLoadPerf / getChunkLoadPerf 供浏览器控制台调试 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 新增 MemoryWorldStore 作为运行期世界级权威内存存储 - World 启动时导入旧存档到内存,新世界生成直写内存 - chunk record 读路径优先从内存获取,回退 IndexedDB - 方块修改立即同步到 MemoryWorldStore.applyBlockMutation - 旁路 unload/background flush 运行期主链路 - 拆分 chunk 装配为 runtime-hydrate → runtime-build-mesh → runtime-finalize 三阶段 - GlobalInstancedMeshManager 新增 applyChunkDelta 增量 patch - 补充性能打点:world.runtime-chunk-record-memory、global-instanced-mesh.delta-patch 等 - 新增 MemoryWorldStore 单元测试 当前状态:读路径切换和 flush 旁路已完成,但 stage 拆分仅完成 「拆分」本身,尚未实现「可中断」,接下来需要实现可中断装配
将 runtime-hydrate 和 runtime-build-mesh 两个 stage 改为可中断的分批执行模式, 确保单个 stage 耗时不超过 scheduler 预算(~3ms),避免帧卡顿。 核心改动: - Chunk 新增 _assemblyProgress 游标状态,支持跨 scheduler 调用恢复执行 - 新增 _clearForBlockInjection / _injectBlockDataBatch 分批注入 blockData - 新增 _buildMeshFromExistingBlockDataIncremental 状态机(iterate → convert-group → visible → build-mesh) - 静态 _computeTransformMatrix 内联计算变换矩阵,避免 per-block 的 THREE.Object3D 开销 - Scheduler 处理 'continue'/'done' 返回值,实现 stage 重新入队或推进到下一阶段 - 新增 chunk.inject-block-data.partial / chunk.build-mesh-increment.partial 性能打点 旧版同步方法(_loadFromCachedRecord / _buildMeshFromExistingBlockData)保留不变, 供非 scheduler 场景(测试/首屏生成)使用。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
在所有测试执行完毕后补充一次最终进度回调,确保进度条显示 100% 后再隐藏,避免因最后几个快速测试导致视觉上进度丢失。
复用预分配的缓存数组/Set 替代 Array.from、[...set] 展开和每帧 new, 减少主循环 GC 压力。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WorldWorker 在 bootstrap 阶段生成的 AO 数据不会被存入 MemoryWorldStore 权威数据源, 且在 finalize 阶段会被 _refreshAOFromStableSource → AOWorker 全量重算并覆写。 将 Worker 侧 AO 计算替换为常量 aoLow/aoHigh=1,减少 bootstrap 阶段的每方块 6 方向遮挡查询。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
健康检查技能重大升级: - 新增 iOS (ObjC/Swift) 项目体检能力,含头文件注释、内存管理、命名规范检查 - 新增 9 种语言专属 checker:cpp, flutter, go, ios, java, js, php, python, ruby, rust - 新增 5 个检查维度:架构、错误处理、可观测性、性能、技术栈合规 - run-check.sh 支持多语言自动路由分发 - 新增 generate-detailed-report.sh 生成详细体检报告 - 重新调整评分权重体系,降低对遗留代码结构问题的惩罚 - 附带 AO 热路径实现计划文档更新 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- AOWorker: 删除 createOcclusionCheckerFromCache 中每次合并 blockData 大对象的逻辑,改为按世界坐标直接定位 chunkCache 查询 - Chunk: _markAllBlocksDirtyAO 优先遍历可见实例(instanceIndexMap/visibleKeys),只标记 face culling 后的可见方块 - Chunk: _markBoundaryDirtyAO 改为按邻居方向直接生成边界影响带,不再扫描整个 blockDataArray - Chunk: 新增 _markCornerDirtyAO 处理对角邻居方向的角点影响带补刷新 - World: onChunkAOStable 新增对角邻居(3x3x3 邻域)的角点补刷新逻辑 - Chunk: _refreshAOFromStableSource 非 fullRefresh 路径跳过 fullSync 调用,减少冗余通信 - 全链路添加 chunk.ao-refresh 系列性能打点 - 新增两条 AO 热路径回归测试
consolidation 重建整个 chunk 的 InstancedMesh 时,WorldWorker 写入的 中性 AO 值错误(aoLow=1, aoHigh=1 导致几乎所有顶点 AO=0,即全黑), 且重建后非脏方块的原有正确 AO 值被丢弃,仅 dirtyAOPositions(3x3x3 范围)被 AOWorker 修正,其余方块保持深色。 修复方案: 1. ChunkConsolidation 新增旧 AO 提取/恢复机制 — 清理旧 mesh 前提取 所有可见方块的 AO,新 mesh 构建后对非脏位置恢复旧 AO 值。同时支持 本地路径(group.children)和全局路径(GlobalInstancedMeshManager)。 2. WorldWorker 中性 AO 值修正为 0x00ffffff(全亮),作为安全网处理 面剔除变化后新出现方块的 AO 初值。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
之前跳过 dirty 位置不恢复 AO,依赖异步 AO 刷新填补,导致合并完成后 有短暂窗口 AO 掉回中性值。现在 dirty 位置仍优先写回旧 AO,只有 新出现且无旧值的 dirty 实例才同步补算当前 AO。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WorldStore/RegionCache/WorldRuntime三层架构,统一 chunk 读写路径,消除旧 PersistenceService 双写问题ChunkPerfMonitor),unload 路径异步写盘消除同步 IO 尖峰test-world-runtime.js、test-memory-world-store.js、test-world-generation-cross-region.js等测试,总计 339 测试全部通过Test Plan
npm run start,确认世界加载、实体持久化、跨区域移动正常🤖 Generated with Claude Code