[Code Review] 整体代码质量、架构缺陷与技术债务全景审查报告 #7
Labels
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Depends on
#8 [Bug] tbl.ts 字符串表解码器偏差:线性假想索引未对齐暗黑2官方哈希桶(Hash Table)键值格式
troytt/diablo2-web
#9 [显存/性能] SpriteRenderer 缺少显存资源释放:WebGL2 纹理、缓冲与程序持续泄漏
troytt/diablo2-web
#10 [架构重构] 场景系统架构割裂:抽离统一游戏世界领域层,消除 map-scene 沙盒与 act-scene 大地图割裂
troytt/diablo2-web
#11 [代码质量/鲁棒性] 底层二进制解码过度依赖非空断言 !:缺失防御性越界校验与异常统一
troytt/diablo2-web
#12 [工程化/环境] 测试与构建脚本执行强依赖特定 Node.js 运行时:引入 tsx 解决跨平台与 CI 无法运行 TypeScript 脚本
troytt/diablo2-web
#13 [音频/多媒体] MPQ ADPCM + Huffman 音频解码缺失:支持 572 个音频成员并接入 Web Audio API
troytt/diablo2-web
#14 [测试工程] 缺少现代化单元测试框架:引入 Vitest 规范化测试套件、覆盖率统计与持续集成
troytt/diablo2-web
Reference: troytt/diablo2-web#7
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
项目全景代码质量与架构审查报告
经过对
troytt/diablo2-web仓库全模块(MPQ 容器、各二进制格式解码器、游戏模拟子系统、WebGL2 渲染器、锁步网络层、场景与打包管线)的深度静态分析、逻辑审计与验证套件执行,现将项目的整体质量评价、核心缺陷与高危风险、技术债务及演进重构建议汇总如下。一、整体质量与工程亮点
strict: true,exactOptionalPropertyTypes: true,noImplicitOverride: true,noFallthroughCasesInSwitch: true)的前提下,src/源码目录中实现了 0 个as any,0 个: any。readonly冻结接口字段,从类型系统层面天然保障了状态不可变性,杜绝了多模块间就地修改(In-place mutation)导致的潜在 desync。scripts/pack-act-assets.ts将原本在客户端需要发起的 11,000+ 次 MPQ HTTP Range 分片请求与复杂解码,预先烘焙为 2048² 调色板索引 PNG 图集与紧凑的scene.json,把 Act 1 的网络请求数从 1,344 次骤降至 4 次,首屏时间从 12s 缩短至 1.5s。二、核心架构缺陷与高危质量问题
1. 【高危】
tbl.ts格式假设偏差严重(标准 D2 实际为哈希表结构而非线性索引)src/formats/tbl.tstbl.ts实现的是简单的[CRC][Count][Offsets...][UTF-16LE Strings]线性顺序布局。lib/tbl-writer.ts)。.tbl文件(string.tbl,expansionstring.tbl,patchstring.tbl)均是基于哈希桶(Hash Table)、元素头(TBLHeader)与以 null 结尾的 ANSI/Code-Page 字符串键值结构。CHI/string.tbl解码出全是乱码;level-names-zh.ts硬编码中文场景名;2. 【高危】
SpriteRenderer缺少显存资源释放(WebGL2 显存持续泄露)src/render/renderer.ts,src/scene/act-scene.tsSpriteRenderer在构造函数中分配了 Program、VertexShader、FragmentShader、VAO、VBO 以及白底/图集纹理;addAtlas每次加载关卡切片均调用gl.createTexture()创建 2048×2048 的 GPU 纹理;gl.deleteTexture、gl.deleteProgram、gl.deleteBuffer或gl.deleteVertexArray,SpriteRenderer甚至没有提供destroy()或dispose()接口。WebGL Context Lost。3. 【架构】场景系统严重割裂(
map-scene沙盒与act-scene漫游器断层)src/scene/map-scene.tsvssrc/scene/act-scene.tsmap-scene.ts(约 1200 行)拥有完整的战斗、怪物 AI、攻击/施法、掉落拾取、背包、技能槽、NPC 交谈与存档读档;但它只能在夹具小地图中运行。act-scene.ts(约 1125 行)接入了真实的 5 个 Act、35 个正式关卡预解包数据和女法师 DCC/COF,但它仅仅是个漫游器,没有任何战斗、怪物、NPC、背包和交互系统。window.__d2web*)。World、Entity、Combat)没有与视图层(Scene)解耦,导致在真实地图中无法体验已经写好的完整游戏玩法。4. 【鲁棒性】底层二进制解码过于依赖非空断言
!(缺失越界防御)src/formats/bitstream.ts、pcx.ts、cel.ts、cof.ts、dcc.ts、dt1.ts等data[i]!压制 TypeScript 的空安全检查。CelError/MpqError),而是直接抛出运行时的TypeError: Cannot read properties of undefined。5. 【工程】测试与工具链缺乏跨平台可移植性
package.jsonpackage.json的所有测试命令(verify:*)直接写为node scripts/*.ts。amaro剥离的 Node.js 运行时。ERR_UNKNOWN_FILE_EXTENSION或ERR_NO_TYPESCRIPT: Node.js is not compiled with TypeScript support。npm run verify:all无法开箱即用,CI 流水线亦易受 Node 运行时环境差异影响。6. 【功能完整度】生成关卡(101 关)与音频系统长期搁置
src/game/maze.ts、src/game/wilderness.ts、src/mpq/decompress.tsmaze.ts(1796 行)与wilderness.ts(909 行)实现了庞大的随机迷宫与野外生成器,但因为碎片引用缺失与拼接可达率低,在pack-act-assets.ts中被全部跳过,导致网页端缺少 101 个真实关卡(包含鲜血荒野等标志性区域)。三、技术债务与代码异味清单
src/game/character.ts:195const sprite = entry.sprite as DccFile强制转型src/game/save.ts:174snapshot.inventory.placed as PlacedItem[]强转src/game/objects.ts:76OBJECT_ROOT = 'data\\global\\objects\\'硬编码:m标记自适应切换monstersscripts/verify-*.tsconsole.log与退出码src/net/lockstep.tsStateHash,达成完全确定性同步四、演进重构建议路线图
devDependencies中引入tsx,将package.json中的node scripts/*.ts统一调整为tsx scripts/*.ts,保证任何 Node 环境均能稳定跑通全部 verify 脚本;verify-combat、verify-items、verify-net等迁移为标准测试用例。src/formats/tbl.ts:依照 D2 标准哈希桶布局重写解码器,打通string.tbl真实数据源,淘汰level-names-zh.ts临时静态表。SpriteRenderer实现dispose()方法,在场景切换与销毁时显式调用gl.deleteTexture等回收 GPU 显存;subarray与索引做安全边界校验,替换裸!断言。GameWorld领域核心层,把map-scene.ts的战斗、物品、NPC、UI 与acts.html的真实 5-Act 大地图融为一体。关联子 Issue 拆分清单 (Sub-Issues Breakdown)
a80ee2c)已处理 — 代码审查母议题
本议题点出的三个其他议题未覆盖的实质缺陷已全部修复(提交以
refs #7标注):StateHash遗漏背包 / 地面掉落 / 任务进度 —— 这是最严重的一个:两个 peer 可以在「谁捡到了什么」上产生分歧,而 desync 检测器却报告一致。现已实现computeLockstepHash(src/net/lockstep.ts:263),用 FNV-1a 折叠,地面物品用顺序无关求和。swapped positions differ: PASS (1152271537 vs 3289542833)、reordered array equal: PASS。成立的原因是每个元素在求和前先经过乘法型 FNV-1a 折叠。net-scene.ts里的digestWorld()。该边界打开后已正确接线,digestWorld()已移除。变异测试(使mix(invHash)失效)可同时触发单测与集成测试。as转换,其中一处作用在反序列化的存档数据上 ——src/game/save.ts已改为带asserts value is PlacedItem的结构化校验;src/game/character.ts的as DccFile改为flatMap+ 类型守卫。any与 3 处每 tick 的重复分配。另外发现并修复了一个无人报告的缺口:
scripts/verify-tbl.ts早已存在且可用,但package.json里根本没有verify:tbl这个 key,等于从未被执行。现已补上,并把verify:tbl、verify:audio、verify:formats一并接入verify:all。两点需要如实说明(不静默略过):
(A)
SpriteRenderer的 VRAM 泄漏指控已过时。 该问题早在a80ee2c中随已关闭的 #9 修复。我最初误判它只在beforeunload时释放,进一步追查后撤回该判断:幕选择器走的是location.search赋值(整页导航),deleteAtlas覆盖了角色句柄与两个页面数组(act-scene.ts:960-966),且packBase循环在每条成功路径上都 return,因此不会堆叠 renderer。此处无需改动。(B) 「101 个生成关卡」(
maze.ts/wilderness.ts) 本次明确不在范围内。 理由:它只出现在本议题的观察部分,并未进入本议题自己给出的四阶段整改路线图(该路线图恰好对应 #12/#14 → #8 → #9/#11 → #10);且本仓库缺少samples/d2,pack-act-assets.ts根本无法运行,无从验证。建议另开议题跟踪。当前
main的整体状态(冷装npm ci后实测):tsc --noEmitvitest run--sequence.shufflegrep ": any|as any" src/tests/中的process.exit