项目全景 · 功能与模块总览
现场快照:2026-09-26,基于工作区实际代码与数据(含未提交改动)。 本文是「读出来的现状」,不是「设计意图」。凡与 README / docs 冲突处,以本文的核实结果为准并标注证据。 本次只做了调研与新增本文档,未改动任何代码、数据或配置。 行数口径与编辑器 totalLines 一致(PowerShell 的 Measure-Object -Line 会少算,勿用)。
0. 一页纸结论
| 项 | 现状 |
|---|---|
| 作品 | 《岁月 · Jianghu》—— 浏览器打开的 3D 武侠Sect Management(经营 + 挂机 + 养成 + 战术战斗 + 主线剧情 + 世界大地图) |
| 架构 | 后端是权威世界,前端只是视窗:Python + FastAPI/uvicorn 持有全部规则与状态;three.js 只负责渲染与拾取 |
| 通信 | WebSocket 推全量快照 + HTTP 发命令;快照每 4 秒一拍(balance.json#tick.seconds_per_tick = 4) |
| 运行时 | 四个服务此刻全部在线::8000 作品后端 · :8788 资源生成器 · :8789 骨骼工坊 · :8080 单入口 |
| 规模 | 后端 33 个 py 31,596 行 · 前端 24 个 js + css 30,862 行 · 探针 168 个 cjs 56,529 行 · Python 工具链 119 个 31,367 行 · 数值配置 17 个 JSON 29,098 行 |
| 资产 | GLB 74 · 图标 112 · 剧情美术 57 · 过场视频 15 · BGM 2 ≈ 154 MB |
| 玩法量 | 25 座建筑 × 10 档 · 110 级境界(11 境 × 10 级)· 6 种资源 · 19 门武学 + 14 门武器技能 · 7 槽 × 5 档装备 · 45 条玩家指令 |
| 主线 | 6 卷 × 60 章 = 360 章 / 169 战,难度 3 → 164 连续,卷末各送一位传奇资质角色 |
| 测试 | 三层:逻辑自检(1398 条断言全绿,实测 8.0 分钟)+ 168 个无头浏览器探针 + 5 道门禁 |
| 最要紧 | 30 个文件未提交(+19,505 / −2,340 行):六卷主线、走镖系统、账房货架、4 座建筑出链 —— 主分支落后工作区一大截 |
1. 这是什么作品
玩家从一座荒山起家:选地立派 → 营建山门 → 招募门人 → 开渠种田 → 铸兵研武 → 派队出镖 → 推主线 → 出城游历整张Jianghu大地图。
三条并行的核心体验:
- 经营线(门派内景):建筑十连升、资源闭环、门人自己走动做事成长、新手 8 步引导串起核心循环。
- 养成线:110 级境界、四维属性 + 装备 7 槽 + 武学 19 门 + 武器技能 14 门,逐层叠加。
- Jianghu线:648 米见方连续地形大地图、道路网、城镇委托、走镖、剿匪、游历奇遇(17 条事件池)、战术战斗。
设计出发点写在 README:给自家孩子做一款能长期玩的作品 —— 数值要看得懂、操作要直接、进度要能累积。这条直接解释了许多取舍(开局给即时正反馈、拒绝 pay-to-win、离线可推进、错误提示必须写成人话)。
2. 架构:后端权威世界 + 前端视窗
浏览器(渲染视窗 · 不持有任何作品逻辑)
├─ scene/world.js 门派内景 3D(正交相机 · 常驻 renderer)
├─ scene/map3d.js Jianghu大地图 3D(透视相机 · 独立 renderer)
├─ battle/battle3d.js 战术战场 3D(独立 renderer)
└─ ui/hud.js 全部面板 / 浮窗 / 卡片
│ WebSocket 全量快照(4 秒一拍) ▲ ▼ HTTP 指令(POST /api/command)
▼
作品后端 gamenet(Python · 权威世界)
├─ 时间推进 · 建造校验 · 门人行为 · 资源产出 · 任务结算 · 战斗演算 · 存档落盘
└─ 零作品引擎依赖 —— 纯逻辑,可在不开浏览器的前提下跑完千余条断言
▲ 读
game/data/*.json(数值唯一真相源 · 改玩法不用动代码)
2.1 进程与端口
| 服务 | 端口 | 说明 |
|---|---|---|
| 作品后端 gamenet | 8000 | 权威世界 + WebSocket(当前在线) |
| 资源生成器 asset-forge | 8788 | 挂 /forge 前缀,产 2D/3D 资产(当前在线) |
| 骨骼绑定工坊 rig-forge | 8789 | 模型程序化绑骨(当前在线) |
| 单入口代理 | 8080 | 对外统一入口(当前在线) |
| 生产 | 80 | Nginx 单入口静态直出 + /api、/ws 反代 8000;PM2 守护 |
Windows 常驻服务:python tools/services.py (status|start|stop|restart|logs)—— 经计划任务,脱离终端不弹窗。
2.2 协议
HTTP 21 个端点(server.py):
| Endpoint | 作用 | 鉴权 |
|---|---|---|
| POST /api/auth/register · login · logout | 注册 / 登录 / 登出 | 免 |
| POST /api/auth/password · GET /api/auth/me | 改Password / 校验会话 | 需 |
| GET /api/health · /api/scene · /api/meta | 健康 / 场景清单 / 静态数据字典 | 免 |
| GET /api/state · /api/map | 完整状态快照 / 世界地图 | 需 |
| GET /api/journal · GET /api/journal/{id} · POST /api/journal/read | 日志摘要 / 详情 / 逐条已读 | 需 |
| POST /api/command | 统一指令入口(45 条命令) | 需 |
| POST /api/random_name · random_sect | 随机姓名 / 门派名 | 需 |
| POST /api/new_game · POST /api/control | 开新局 / save·load·offline | 需 |
| WS /ws | 实时快照流(token 走 query) | 需 |
WebSocket 消息:服务端 → hello / tick / command_result / new_game / loaded / pong;客户端 → ping(20 秒心跳)/ command / resync。鉴权失败先 accept 再 close(1008) —— 这样前端能区分「没登录」和「服务挂了」。
前端网络三件事(net/api.js):快照落地 + 指令发送 + 断线重连(退避 400ms × retry、上限 8;1008 不重连,清会话并广播 AUTH_LOST_EVENT)。
2.3 时间与离线
- 1 tick = 4 真实秒 = 10 作品分钟 ⇒ 1 作品日 ≈ 9.6 真实分钟。
- seconds_per_tick 是唯一油门(暂停/倍速已于 2026-09-19 整体删除,别再引入「每秒多跑几个 tick」式加速)。
- 离线结算(balance.json#offline):rate 0.6、cap_hours 8 —— 离线按在线速率的 60% 补,最多补 8 小时。
- 自动存档:每 15 秒脏检查后写盘;成功指令才标脏(失败的空操作不触发全量写盘)。
2.4 一个 tick 里按顺序发生什么
server.py 的 _tick_loop 每 0.05 秒轮询一次,攒够一个 tick 才推进(单帧最多补 30 个 tick)。engine.tick()(engine.py:322)内部顺序:
- mark_dirty(世界每 tick 必变)
- step_world —— 队伍速度(按队内最慢的人 + 减速/饥饿/马厩)→ travel.step(山匪漫游 / AI 商队 / 玩家队伍走路 / 遭遇扫掠)→ ResourceSystem.tick_guild(资源产出与粮食消耗)→ TechTree.research_tick(研习进度)→ 所有 AI 门派 grow
- _tick_travel_world —— 奇遇冷却与减速计时 / 抵达矿点转正式开采 / 开采产出入队 / 行军耗粮(粮尽发 food_out 并减速)/ 送信押镖到达判定(逾期转失败)/ 光点扫掠触发奇遇
- _tick_travel_side —— 光点重生与兜底补点 / 商队兜底补 / 城镇委托到期扫除
- _tick_tasks —— 在途任务 elapsed 推进与到点结算(在营造之前)
- _tick_buildings —— 新盖与升阶推进、竣工播报
- _tick_practice —— 演武场练武到点结算
- _tick_shop —— 账房客人波次 + 求购生成/过期
- tick_count += 1;clock.advance(1)
- 招贤榜按期换榜 / 任务板按墙钟换榜(排除在途模板)/ 新手任务 state 型目标固化
- 落笔日志簿(升级 / 任务 / 研习 / 武艺 / 营造竣工 / 升阶竣工),再广播事件
新手任务的「达到某状态」必须在 tick 里固化:粮食是会花掉的,不固化就会出现「攒到 200 打勾 → 花掉 → 任务退回未完成」。
2.5 三条不可违反的工程纪律
- 数值全部外置 game/data/*.json,代码不出现魔法数字(balance.json#meta.note 原文)。
- 后端权威:前端不持有任何作品逻辑;战斗判定、资源结算、成长曲线全在后端,前端只重放。
- 老档必须无损迁移:改「等级/档位」数字轴前先想老档;加版本号(如 realm_ver),缺键视作老版 → 迁移 → 写回新版号(幂等)。55 级 → 110 级的换算是 L → 2L − 1,境界一寸不退。
3. 目录地图与规模
| 路径 | 角色 | 规模 |
|---|---|---|
| web/backend/gamenet/ | 权威世界(作品本体逻辑) | 33 py · 31,596 行 |
| web/frontend/src/ | 渲染视窗 | 24 js + style.css · 30,862 行 |
| web/frontend/index.html | 全部面板/层的 DOM 宿主 | 1,078 行 |
| web/tools/ | 无头浏览器探针 + 门禁 | 168 cjs · 56,529 行 |
| game/data/ | 数值配置(唯一真相源) | 17 json · 29,098 行 |
| game/assets/ | 作品资产 | 154 MB |
| tools/ | Python 工具链(资产生成/校验/运维) | 119 py · 31,367 行 |
| docs/ | Design Docs | 25 md · 7,691 行 |
| asset-forge/ | 资产生产线:文生图 → 图生3D | 后端 41 py · 12,651 行;前端 36 文件 · 12,772 行 |
| rig-forge/ | 骨骼绑定工坊 | 23 文件 · 4,422 行 |
| model-forge/ | Blender 建模脚本生成 | 16 py · 3,797 行 |
| audio-forge/ | BGM/音效合成(纯 numpy) | 3 py · 818 行 |
| deploy/ | PM2 / Nginx / 推送脚本 | 15 文件 |
| _tmp · _shots · render · asset_forge_data · _backup* · _archive | 临时产物与历史备份(已 gitignore) | 约 8 GB |
仓库已跟踪 864 个文件;.git 193.6 MB;工作区含忽略目录共 9.26 GB。
4. 作品功能全清单
4.1 营建(25 座建筑 × 10 档)
- 分类:核心 3(山门 / 大殿 / 议事厅)· 功能 7(弟子居所 / 仓库 / 厨房 / 药房 / 账房 / 藏经阁 / 丹房)· 生产 5(伐木场 / 采石场 / 农田 / 铁矿场 / 药园)· 军事 6(演武场 / 藏兵阁 / 城墙 / 校场 / 练功房 / 铁匠铺)· 景观 4(灯笼 / 石狮 / 牌匾 / 喷泉)。
- 每座 10 档,1~3 档逐字段等于扩容前的基线(老玩家零跳变);每档有耗时,等级越高等越久(L1 恒 0)。
- 大殿是链条顶端:其他建筑等级不能超过大殿;大殿升级要求「非资源、非景观建筑必须都已建成且等级 ≥ 大殿当前等级」,拒绝理由会点名「还差谁、现几级 / 尚未建造」。
- 资源楼数量上限 = clamp(1 + 楼层(大殿等级 / 2), 1, max_count=5);被大殿压着时保留并置灰,写明「大殿升到 N 级可多建 1 座」。
- 两种「不入链」:scenery: true(灯笼/石狮/喷泉 = 纯景观,可建不升级、不出头顶标签)vs buildable: false(厨房 / 藏兵阁 / 城墙 / 校场 = 压根不能建,建造坞不出现、can_build 拒绝、不算大殿前置)。
- fixed_pos:位置固定、不可挪动不可拆除(城墙、山门)。
- 自由度:拖动重排、旋转、拆除返还 50%;城外也能放置。
- 数据:buildings.json(upgrade_levels 由 tools/gen_buildings.py 生成,别手改);布局常量唯一真相源是 scene.py(GRID = 3.0 米)。
4.2 资源与经济(6 种)
银两 / 木材 / 石料 / 粮食 / 铁矿 / 药材,各有仓储上限。开局:银 800 · 木 400 · 石 300 · 粮 500 · 铁 150 · 药 100,门人 5 人(AI 6 人)。 生产来自生产类建筑;粮食按门人数消耗(balance.json#resource.food_upkeep_per_member),断粮掉忠诚。
📌 README 写「7 种资源」,实际 6 种 —— 真气(qi)已于 2026-09-22 从资源表下架(改由门人四维的「内力」承担上限),读档时 models.py 会剔除老档里的 qi 键。
4.3 门人与成长
- 境界:110 级 = 11 境界 × 10 小级(菜鸟→黑狗→豺狼→猛虎→雄狮→万象→青龙→白虎→玄武→成神→武圣);经验公式 exp_next(L) = 8L² + 12L + 100。
- 资质五档:传奇(weight 0,主线通关专属,修炼 ×3.2)/ 天 4‰ ×1.6 / 地 12 ×1.35 / 玄 32 ×1.15 / 黄 52 ×1.0;资质越高初始忠诚区间越低(刻意代价)。
- 擅长 9 种:弟子 / 杂役 / 护院 / 剑客 / 刀客 / 弓手 / 暗器客 / 酒客 / 说书人 —— 决定外形模型与展示。
- 体力:上限 100,按真实时间 1 点/分钟恢复(不绑 tick,否则改流速等于改回体力)。
- 真气:上限 = 四维里的「内力」;按分钟比例恢复;押镖出发时可消耗真气释放武器技能换成功率与收益加成。
- 行为:门人在 3D 里自动走动、做事(8 种活动:押镖在外 / 巡守 / 修炼 / 研习 / 看火 / 议事 / 歇息 / 闲逛),按身份偏好链 + 志愿序位让位分配决定谁去哪;站位不钉死,人多时环绕活动锚点散开;走位三约束(不穿建筑、不出城墙、做事时站在对应建筑旁)。
- 数据:realms.json · recruit.json · balance.json#stamina/#qi/#training。
4.4 招募(山门招贤榜)
- 入口是牌匾建筑的浮窗(按 effect.recruit 分派,不按 id)。
- 每 3 作品日自动换榜;可花银两催榜(有冷却,冷却期间失败且不扣钱)。
- 双门槛:掌门境界 = 可招上限天花板(max(1, min(round(掌门等级 × 0.6), 掌门等级 − 2)),掌门永远比手下强至少 2 级);门派声望 = 席位与效率。
- 门派等级 1~9(声望阈值 0 / 15 / 34 / 58 / 88 / 124 / 166 / 214 / 268),等级越高席位越多、换榜更快、招募更便宜。
- 指令:recruit_hire / recruit_board_reroll;旧 recruit 已废弃(只回一句人话)。
4.5 镖局与任务(10 个模板 · 难度 1~9)
- 三类:内务 1~3(巡守山门 / 操练弟子 / 修缮屋舍)· 缉拿 4~6(鸡鸣狗盗 / 剿灭匪伙 / 擒贼擒王)· 镖局 7~9(护送银两车 / 商货押运 / 暗镖秘宝),另有护送信笺等入门票。
- 体力消耗与难度一一对应:cost = 5 × 难度 + 5(10 → 50)。
- 失手率 fail_chance = base + 难度 × 0.03 − 门派战力 × 0.0005。
- 一趟最多派 5 人;同时能接几件由建筑的 tasks.slots 决定。
- 任务板按墙钟每半小时整榜重贴(不挂作品时间:否则改流速就顺带改了换榜周期);在途任务模板不重复上墙。
- 数据:tasks.json · balance.json#task。
4.6 武学与武器技能
- 门派武学 19 门 / 3 大学派(外功 7 · 内功 6 · 奇门 6),品阶 1~5(凡品/良品/精品/宝器/神兵)由前置链深度决定(无前置 = 凡品,每深一层进一阶);藏经阁等级决定能研到几阶。
- 武器技能 14 门 = 7 种兵器 × 2 门(入门 → 进阶);在演武场学会与练习(同时最多 4 人),每门 10 级,等级由累计熟练度现推(只存累计值 ⇒ 调曲线不伤老档)。
- 学成的技能:1 永久给四维百分比加成 2 押镖出发时消耗真气释放,本次任务额外加成。
- 数据:techs.json · skills.json · balance.json#skill。
📌 README 写「5 种兵器 × 2 = 10 门」,实际 7 种兵器(2026-09-23 新增戟 halberd、飞镖 dart)⇒ 14 门。
4.7 装备与打造(7 槽 × 5 档)
兵器 / 头盔 / 护甲 / 靴 / 坐骑 / 戒指 / 项链;品阶 凡品 → 良品 → 精品 → 宝器 → 神兵。 铁匠铺是打造的唯一入口,等级决定可造品阶上限与出优品概率。 🔴 装备只有两个合法去处:某人身上,或公库(逐出门人时身上的装备必须收回)。 数据:equipment.json。
4.8 账房:货架出售 + 求购交付(🆕 未提交)
- 货架格数 = 5 + 2 × (等级 − 1) ⇒ L10 = 23 格;可把铁匠铺产出的装备上架,标价随品质走(凡品 : 神兵 = 1 : 3.2)。
- 每 10 tick 来 5 位客人各买一件,银两按标价进账;上架中的装备不能穿戴(否则客人来时东西已穿走)。
- 求购:定时发布(同时最多 3 条、间隔 30 tick、有效期 90 tick),接单后按「品阶 + 部位 + 兵器种类」逐项匹配交付,酬劳 ×1.5。
- 数据:balance.json#shop;浮窗宿主 inspector = shop。
4.9 战术战斗
- 3D 全屏战场:10×10 格子只做布阵占位(上 5 行敌方 / 下 5 行我方;首发 5 人,随大殿等级每 2 级放开 1 人);开战后人物自由移动(连续坐标),主动靠近最近的敌人。
- 没有回合:各按兵器 CD 出手,同刻并发不排队(250ms 同步窗内的一批一起动;前端按后端返回的 at 虚拟毫秒调度,fire-and-forget)。
- 普攻 CD 由兵器决定:剑 1500 / 刀 1700 / 飞镖 1800 / 枪 2000 / 棍 2000 / 戟 2100 / 弓 2600 ms;武学技能 CD 约 6.5 秒(我方)/ 7 秒(敌),伤害 ×1.75 且消耗内力上限的 34%。
- 射程:近身一律 1 格(剑/刀/枪/戟/棍),飞镖 3 格 · 弓 5 格;徒手也是 1。
- AoE:枪与棍 —— 以主目标为圆心、半径 1 格的圆,波及目标伤害 ×0.55。
- 伤害:raw = atk × 1.4 × mult;减伤 = dnf × 0.5;dmg = max(atk × 0.12, raw − 减伤) × U(0.88, 1.12) × 暴击(12%,×1.65);每个被击目标各自判暴击;下限 1.0。
- 目标选择:射程内血量最低者(补刀优先);移动避让 min_gap 0.6 格;超时 60 秒比双方平均剩余血量比例。
- 前端一次判定都不做:后端整场算完回传 script,前端只演出。
- 三类战斗共用一套层:自由对战 battle_test(只读,不发奖励)/ 主线 mainline_battle(写盘,发奖励)/ 游历遭遇(无布阵)。
- 数据:battle.json(前后端唯一真相源)· core/tactical.py。
4.10 主线剧情「岁月」(🆕 未提交:已从 3 卷扩到 6 卷)
| 卷 | 标题 | 章 | 战 | 战斗等级 | 卷末角色 |
|---|---|---|---|---|---|
| v1 | 笑傲Jianghu | 60 | 30 | 3 ~ 66 | 令狐冲(传奇 · 剑客 · 宝器剑) |
| v2 | 天龙八部 | 60 | 25 | 68 ~ 98 | 虚竹(传奇 · 酒客 · 神兵棍) |
| v3 | 雪山飞狐 | 60 | 24 | 102 ~ 122 | 胡斐(传奇 · 刀客 · 神兵刀) |
| v4 | 射雕英雄传 | 60 | 30 | 122 ~ 136 | 郭靖(传奇 · 弓手 · 神兵弓) |
| v5 | 神雕侠侣 | 60 | 30 | 136 ~ 150 | 杨过(传奇 · 剑客 · 神兵剑) |
| v6 | 鹿鼎记 | 60 | 30 | 150 ~ 164 | 韦小宝(传奇 · 暗器客 · 神兵镖) |
| 合计 | 360 | 169 | 3 → 164 连续 | 6 位传奇 |
每卷 5 层 × 12 章;卷解锁链:新档只解锁卷一,通关第 N 卷才解锁第 N+1 卷(界面与命令层双重拦截)。 每章结构 = brief + lines[](对话,支持行内 markdown)+ 可选 battle + reward。 对话体验:人物立绘 + 场景背景(57 张 AI 美术)、打字机逐句、整层可点推进、章末自动交棒战斗、战斗结束自动收起剧情层并推进。 🔴 mainline_advance 只认「没有 battle 的章」—— 否则前端一句「我推进第 58 章」就能跳过东方不败白拿奖励(安全边界)。 数据:mainline.json(由 tools/gen_mainline_volumes.py 生成,手改会被覆盖)· 体检 tools/verify_mainline.py。
4.11 Jianghu大地图与游历
- 独立 3D 视窗(不共享主场景 renderer):36×36 格 × 18 米 = 648 米见方,一整张连续曲面(不是一格格方块);俯角锁死 45°,旋转关闭,全键平移。
- 地形生成:hash 内插 value noise + fBm(纯函数,不消费 rng);高度切水/丘陵/山,湿度管林地;从山顶刻河(默认 3 条);最大陆块之外全部沉水;5 条可玩性约束(出生点安全圈无敌人 / 4 格内有木石粮 / 城镇间距 ≥ 8 / 至少一镇可达 / AI 战力差 ≤ 1.5 倍),体检不过换盐值重试。
- 道路网:Prim 最小生成树保证连通 + 额外直连与环边,每条边用 A 星在真地形上寻路(森林 1.7 倍代价、山地 3.2 倍、据点格 +14 且起终点豁免,防路穿建筑)。
- 派队出城游历:每队 5 人;左键点地即出发(脚下铺脚印虚线标去向);多队并行,随时切换指挥;角落缩略舆图实时框出视野。
- 行军耗粮(粮尽则饥饿减速);途中撞上山匪流寇触发遭遇战。
- 采集派遣:点资源点派队开采,货载满 / 矿枯竭自动停,收队自动交货。
- 城镇委托板:每镇常驻 2 条(剿匪 / 寻药 / 送信 / 护送,权重 4/3/3/3),完成即补,涨城镇好感(平常 → 友善 ≥60 → 深交 ≥80;友善后该镇休整免费)。
- 驿站休整:队伍停在城旁可付银(每伤兵 15)当场痊愈伤员。
- 游历奇遇:地图上的光点(基数 8,触发后定时在别处重生、总量恒定),踩到即按事件池结算 —— 17 条(奇遇 / 危机 / 人情 / 环境),奖励资源 / 经验 / 好感,甚至奇人入伙。
- 数据:balance.json#world(126 个键)· events.json · 后端 world/ · 前端 scene/map3d.js + ui/worldmap.js。
📌 后端只存整数格;「1 格 = 18 米」这个尺度只写在前端 map3d.js,米制换算不在权威侧。
4.12 走镖系统(🆕 未提交,借鉴《运镖天下之四大镖局》)
押镖三档(铜 / 银 / 金,权重 6/3/1)+ 暗镖(好感 ≥80 或自设分号,10% 概率);带档位 / 押金 / 期限三件套。 接镖付押金 → 送达退押金、早到 ×1.2、全队忠诚 +2 → 镖职晋升(第 6 趟升「镖师」,忠诚先 +2 再 −8)→ 金镖需「镖头」压阵才交割;逾期 = 好感 −5 + 押金没收;没人接的悄悄换新。 Jianghu打点:买路钱(太平期间山匪放行,太平一过照常遭遇)· 护镖令(在途委托期限 +2 日)· 摆宴(花银补回晋升掉掉的忠诚)· 马厩(每匹千里马 +6% 速度)· 分号(多路并行)· 商队抉择(劫还是放)。 设计案:docs/设计-走镖系统融入方案.md。
4.13 日志(汇报簿)与新手引导
- 日志由后端生成、后端持有、后端存档;快照只带 total / latest_id / unread[] / unread_count —— 详情不进快照(曾经日志占快照 65% 体积、26KB/40KB,阻塞事件循环)。
- 条目类型:门人升级 / 建筑升阶 / 营造竣工 / 研习完成 / 武艺学成 / 任务结算;任务条目有详情(成员逐人经验与忠诚变动、资源入库、叙事 4 段 + 收尾句)。
- 未读是逐条的(不是「最大已读编号」—— 那样被点开的那条永远不会翻成已读)。
- 新手引导 8 步(数据驱动,tutorial.json):开山立派 → 生财有道 → 广纳贤才 → 初出Jianghu → 铸炉起灶 → 锋刃初成 → 藏经纳典 → 研经习武。
4.14 音画
2 首 BGM(山居 / 夜阑,按时辰切换 + 淡入淡出)· 15 段过场视频(7 种任务 × 成败 + 入场)· Web Audio 合成音效。 第三首Jianghu BGM 已产出但尚未接线。
5. 模块设计
5.1 后端 gamenet(33 文件 · 31,596 行;不含 selftest 为 22,311 行)
| 模块 | 行数 | 职责 |
|---|---|---|
| selftest.py | 9,285 | 逻辑自检 24 个分节(不开浏览器验全部规则) |
| engine.py | 5,352 | 总控台:tick 心跳 + 45 条指令派发 + 离线结算 + 存档 |
| scene.py | 3,543 | 门派 3D 布局单一真相源(摆位/铺装/城墙/河道/植被/门人/光照/相机/清单)+ 放置合法性校验 |
| core/models.py | 2,929 | 领域模型:RealmSystem / QualitySystem / Character / BuildingInstance / Guild / Task(含老档迁移器) |
| world/travel.py | 1,868 | 游历世界:玩家队伍 / 山匪漫游 / AI 商队 / 光点 / 负伤与镖职 |
| core/tactical.py | 1,146 | 战术战斗:布阵、自由移动、CD、射程、AoE、伤害、结算 |
| server.py | 831 | FastAPI:会话注册表、21 个端点、tick 循环、自动存档、WS |
| mainline.py | 699 | 多卷主线:进度只存 done 一个整数,其余现算 |
| journal.py | 697 | 日志簿:升级/任务/游历的中文叙事(仅标准库) |
| core/recruit.py | 520 | 声望 → 门派等级;招贤榜(换榜/催榜/聘用) |
| world/generator.py | 497 | 参数化确定性世界生成 + 5 条可玩性约束 |
| core/systems.py | 488 | 资源 / 科技 / 招募 / 锻造 / 战斗 / 任务六大系统 |
| world/factions.py | 381 | 势力:门派 / AI / 山匪 / 官府 / 城镇 |
| accounts.py | 348 | 账户与会话(pbkdf2_hmac sha256 24 万轮 + 每用户盐) |
| tutorial.py | 341 | 8 步新手链(事实与判定分离) |
| units/roster.py | 327 | 门人「此刻在做什么、在哪」(全工程唯一算活动处) |
| world/roads.py | 303 | 道路网:Prim MST 拓扑 + A 星真地形几何 |
| core/stat_system.py | 289 | 四维(hp/mp/atk/dnf)与装备属性换算(战力唯一出口) |
| world/towns.py | 274 | 城镇委托板 + 好感度 |
| world/events.py | 265 | 游历奇遇(数据驱动,代码只认 effect 动词) |
| world/world_map.py | 258 | 世界地图纯数据(地形格/资源点/势力/路网/游历容器) |
| datadb.py | 252 | game/data/*.json 只读缓存与查询 |
| core/taskboard.py | 235 | 悬赏榜:接一张少一张、每半小时整榜重贴 |
| naming.py | 190 | 武侠姓名 / 门派名生成与校验 |
| config.py | 114 | 路径 / 网络 / 会话配置(全部 env 可覆盖) |
| rng.py | 74 | 确定性随机流(同 seed 同序列) |
| core/clock.py | 73 | 作品时钟、离线收益、时刻文案 |
| __init__.py ×6 | 1~12 | 包占位(其中 combat/、tasks/ 是完全空壳,实现都在 core.*) |
结构统计:46 个 class、224 个模块级 def、604 个方法。
45 条玩家指令(engine.py:871-952 分发表):
| 分类 | 指令 |
|---|---|
| 建造 | build · upgrade · demolish · place_building · move_dress · demolish_dress |
| 成长 | research · craft · equip · unequip · learn_skill · stop_practice · dismiss · rename · setup |
| 招募 | recruit_hire · recruit_board_reroll(recruit 已废弃) |
| 任务 | start_task |
| 战斗 | battle · battle_test · battle_lineup |
| 主线 | mainline_chapter · mainline_battle · mainline_advance |
| 游历 | travel_start · travel_move · travel_battle · travel_return · travel_harvest · travel_rest |
| 城镇委托 | town_quests · town_quest_accept · town_quest_abandon · town_quest_submit |
| 走镖 | escort_buy_pass · escort_gov_pass · escort_feast · escort_buy_horse · escort_branch_buy · caravan_choice |
| 账房 | shop_list · shop_unlist · shop_order_accept · shop_order_deliver |
🔴 分发表里有几条刻意不合并的边界:battle(地图抽象结算)vs battle_test(只读演算)vs mainline_battle(写盘发奖);mainline_advance 只认无战斗的章。派发语义:未知 type 失败;异常兜住不挂服务;成功且非 saved/no_dirty 才标脏。
存档结构:SAVE_VERSION = 1;顶层 version / saved_at / params / seed / tick_count / clock / active_tasks / journal / world;门派段含 resources / buildings / members / techs / practice / equipment / leader_id / recruit_board / task_board / dress_pos / dress_removed / tutorial_facts / realm_ver / battle_lineup / mainline / shop_*。 slots 落 saves/<slot>.json(.tmp → replace 原子写);按账户分目录 users/<uid>;自动存档每 15 秒且只在脏时写。 迁移分两层:文件级 version 不符直接拒载(不做格式迁移,所以项目坚持「加字段不升 SAVE_VERSION」);字段级 realm_ver < 2 触发 L → 2L−1 迁移(幂等)、building_pos 槽位转实例(只读进)、老档补路网/山匪/委托板/光点、剔除已下架键(qi、聚灵阵)。
5.2 前端 src(24 个 js + 1 个 css · 30,862 行;index.html 1,078 行)
| 模块 | 行数 | 职责 |
|---|---|---|
| ui/hud.js | 5,566 | HUD + 十余扇浮窗 + 8 个建筑功能面板 |
| style.css | 5,270 | 全部样式 |
| scene/world.js | 4,523 | 门派内景 3D:光照/地面/建筑/门人/拾取/自由摆放状态机 |
| scene/map3d.js | 2,981 | Jianghu大地图 3D:高度场/路网/据点/队伍/标签(独立 renderer) |
| battle/battle3d.js | 2,533 | 战斗 3D:地块/人物/相机/动画/刀光剑影/拾取(不碰规则) |
| battle/battle.js | 2,315 | 战斗流程层:布阵 → 取回放脚本 → 时间轴演出 → 结算 |
| ui/worldmap.js | 1,616 | 大地图面板调度:队伍卡/据点菜单/委托板/选人/缩略舆图 |
| main.js | 928 | 唯一装配根:注入 send / 快照 / meta,落地快照,全局热键 |
| ui/years.js | 788 | 主线:卷页签目录 + 剧情对话层(打字机/立绘/推进) |
| ui/equip_card.js | 533 | 装备图标/卡片/网格/分配菜单(品阶配色 + SVG 回落) |
| scene/lantern_glow.js | 527 | 灯笼发光:真点光源池 + 软边光斑(GLSL) |
| ui/setup.js | 456 | 开局设定覆盖层(掌门性别/姓名/门派名)+ 重开确认 |
| audio.js | 403 | BGM/SFX 管理、按时辰切换、偏好持久化 |
| realm.js | 316 | 境界徽章与资质色板(纯函数) |
| ui/tooltip.js | 316 | 全局悬停 tips(延迟/边界翻转/抑制原生 title) |
| ui/building_icon.js | 302 | 建筑图标 SVG 与说明文案 |
| ui/tech_card.js | 287 | 武学卡片与网格(学派配色) |
| net/api.js | 237 | 网络层:REST 包装 + Net 类(WS 快照流) |
| ui/login.js | 227 | 登录/注册覆盖层 |
| scene/loader.js · ui/sundial.js | 172 | GLB 模型库(加载/预热/实例化)· 日晷表盘与天色曲线 |
| ui/member_card.js | 160 | 门人卡/门人 tips 统一版式(头像框 + 资质色) |
| ui/res_icons.js · ui/md.js · net/auth.js | 89 / 87 / 58 | 资源图标 · 行内 Markdown · 本地会话 |
依赖拓扑:index.html → main.js(唯一装配点)→ hud / world / battle / years / worldmap。 hud 不 import 任何 3D 模块,3D 层也不 import hud;三个 3D 场景各自持有独立 renderer(刻意不共享,以免Sign out时恢复不全)。
UI 面板清单(宿主 id 见 hud.js 的 FLYOUT_HOSTS):建筑浮窗(演武场/打造/研习/库容/议事厅/招贤榜/账房/升阶)· 战力构成 · 战报 · 确认浮层 · 汇报簿 · 案卷详情 · 门人详情 · 任务详情 · 新手任务 · 装备分配菜单 · 账户菜单 · 时间表盘 · 仓库 · 飘字 · 加载遮罩;大地图另有队伍卡 / 据点图标栏 / 交互菜单 / 委托板 / 选人 / 缩略舆图 / 商队对话 / 遭遇横幅;主线有章节目录与剧情层。
5.3 数据层(17 个 JSON · 唯一真相源)
| 文件 | 大小 | 内容 |
|---|---|---|
| mainline.json | 725 KB | 六卷 360 章(由脚本生成) |
| buildings.json | 96 KB | 25 座 × 10 档(upgrade_levels 由脚本生成) |
| story_art.json | 42 KB | 剧情美术规格(27 立绘 + 29 场景) |
| ai_slim_plan.json | 39 KB | AI 巨模瘦身计划(56 条) |
| balance.json | 24 KB | 全部平衡数值(含 world 段 126 键) |
| battle.json | 18 KB | 战斗唯一真相源(网格/冷却/伤害/射程/AoE/移动/敌人) |
| realms.json | 17 KB | 110 级 × 曲线(由脚本生成) |
| tutorial.json | 10 KB | 新手 8 步 |
| techs.json · skills.json | 7 / 5 KB | 19 门武学 / 14 门武器技能 |
| events.json | 6 KB | 17 条奇遇事件 |
| recruit.json · equipment.json · tasks.json · names.json · resources.json | 5 / 5 / 5 / 4 / 1 KB | 招募 / 装备 / 任务 / 姓名 / 资源 |
⚠️ asset_pipeline.json 已废弃(_deprecated: true,后继者 ai_slim_plan.json)。
5.4 资产生产线
提示词 → asset-forge(豆包 Seedream 文生图)→ 图生3D(腾讯混元)
→ rig-forge(程序化绑骨)→ model-forge(Blender 脚本)
→ tools/ 导入 → game/assets/models/(74 GLB)
图标 112 张 → tools/gen_*_icons.py(建筑 25 / 境界 11 / 装备 70 / 界面 4 / 人形剪影 2)
剧情美术 57 张 → gen_story_art_plan.py(从 mainline.json 反算清单)→ gen_story_art.py → make_story_art.py
过场视频 15 段 → prep_task_videos.py(ffmpeg 转码)
BGM → audio-forge(纯 numpy 合成,wrap_tail 无缝循环)
三个值得记住的取舍:
- 立绘与装备界面剪影是两套相反的要求(剪影要无细节、立绘要五官清晰)⇒ art_style.py 里是两个独立风格句。
- 抠图阈值按题材定:图标那边放宽到 2.2 倍色距(为吃掉 AI 画的浅灰衬纸框),而「浅灰」正等于人物浅色衣料 ⇒ 会把衣服抠出洞;立绘改用窄阈值 + 反解白晕(观测色 = a·人物色 + (1−a)·纯白),红线是「吃掉非背景像素 < 1%」。
- 装备图标做成「底色牌 + 中央物品」 —— 底色直接就是品质色,省掉整条抠图链路。
5.5 测试与验收(三层)
- 逻辑自检:cd web/backend 后跑 python -m gamenet.selftest
24 个分节:数据层 / 世界生成 / 开局配置 / tick 循环 / 资源上限 / 指令层 / 大殿升级前置 / 建筑升阶耗时 / 招贤榜·科技·锻造 / 任务 / 战斗 / 快照与存档 / 日志已读 / 改名 / 开局设定 / 场景清单 / 建筑放置 / 账户与会话 / 进度 / 新手任务链 / 战术网格战斗 / 大地图经营 / 走镖 / 走镖第三批。 源码内 1,385 个 check() 调用点;本次实测执行 1398 条断言、0 失败、耗时 8.0 分钟(README 记「1331 条」与「秒级」,两个口径都已落后)。
- 定点探针(无头浏览器,共 168 个 cjs,其中 probe_* 76 个):node web/tools/probe_xxx.cjs
🔴 不要串跑(大场景冷启动互相污染会导致失败点游走、出现假红)。
- 五道门禁:截图超时必须显式给足(软渲染整页截图可超 15 秒,超时的后果是静默 —— 判据全绿但图不是新的)· 模板串内禁写反引号 · 共享 helper 用了必须导入 · 拼接串里的行尾注释必须补换行 · 材质诊断。
四条门禁守的都是同一类坑:静默失败 —— 测试还绿着,但证据已经不可信了。
5.6 部署与运维
生产:Nginx 单入口 + PM2 守护。静态由 Nginx 直出(实测 uvicorn 日志中 /app/ 出现 0 次),/api 与 /ws 反代 8000;缓存三档(哈希产物 immutable 1y / 其余协商 / API no-store);PM2 必须写 interpreter: none(否则拿 node 解释 Python,进程秒退)。 更新:bash deploy/prod/push.sh(可带 --no-build / --data / --assets)—— 推送时主动避开 web/backend/{accounts,saves}/(覆盖等于玩家进度归零)。 发布后:资产全量对账探针(纯读,约 5 秒)+ 端到端探针(会注册真账号,跑完必须清理)。
6. 当前状态与风险清单
6.1 🔴 最要紧:30 个文件未提交,主分支落后工作区一大截
git status 显示 30 个已修改文件(+19,505 / −2,340 行),HEAD 停在 2026-09-25 01:46,远程为 gitee: aowind/wuxia-time。未提交的是四个成体系的新功能:
| 批次 | 内容 | 主要涉及 |
|---|---|---|
| 主线六卷 | v1 由《岁月》改名《笑傲Jianghu》;v2/v3 由 12 章扩到 60 章;新增 v4 射雕 / v5 神雕 / v6 鹿鼎 ⇒ 84 章 → 360 章、42 战 → 169 战;卷解锁链;卷末角色一律传奇资质 | mainline.json(+15,093 行)· mainline.py · selftest.py |
| 走镖系统 | 押镖三档 / 押金 / 期限 / 镖职晋升 / 金镖 / 买路钱 / 护镖令 / 摆宴 / 马厩 / 分号 / 商队抉择 / 护送战 | travel.py(+601)· engine.py · worldmap.js(+330) |
| 账房 | 货架出售 + 客人波次 + 求购交付 | engine.py · balance.json#shop · buildings.json |
| 建筑出链 | 厨房 / 藏兵阁 / 城墙 / 校场标 buildable: false | buildings.json · models.py · selftest.py |
⚠️ 这些改动已经跑在本地运行中的服务上(:8000 在线),但没有进版本库。一旦误执行 git checkout 或 git stash,就是几万行工作消失。建议优先提交。
6.2 文档与代码的偏差
| 文档 | 状态 |
|---|---|
| README.md | 主文档,质量高,但数字落后于工作区:主线 3 卷 84 章(实际 6 卷 360 章)· 建筑 27 座(实际 25)· 资源 7 种(实际 6,qi 已下架)· 武器技能 10 门(实际 14)· 后端 26,800 行(实际 31,596)· 前端 22 个 js(实际 24 + css)· 探针 157(实际 168)· 断言 1331(静态 1,385 个调用点) |
| docs/作品设计总纲.md | 历史文档:55 级(实际 110)· 建筑 Lv1→5(实际 10 档)· 离线 12h/0.45(实际 8h/0.6)· 装备 4 部位(实际 7 槽)· 战斗「胜率对拼」(实际 CD 制战术战斗)· 地图 64×64 网格(实际 36×36 连续地形)· 正文仍写 Godot 4 / GDScript;且完全没覆盖主线、游历、走镖、账房、体力/真气/武学熟练度、日志、新手引导 —— 而这些恰是现在的玩法主体 |
| docs/技术架构总纲.md | 自带横幅声明引擎章节过时;没有任何 HTTP 路径 / WS 消息 / 快照结构 / 端口 / tick 频率(README 却把它标为「通信协议」);正文仍写「REST + JSON,不需要 WebSocket」(那条只针对工具链);引用的 docs/技术架构-Godot工程.md、技术架构-美术资源工具.md 不存在 |
| 三份接口契约(门人 / 招募 / 日志) | 字段名与不变量仍有价值,但 tick 口径全错(都按「1 tick = 1 真实秒」写,实际 4 秒);日志的「未读」模型已被代码推翻(现有 POST /api/journal/read + 快照 unread[]);招募契约把已实现的 core/recruit.py 标为「待落地 ⏳」 |
建议:以 README + game/data/*.json 为现行真值源;作品设计总纲降级为「设计意图与公式来源」;三份接口契约需按 tick = 4 秒 与逐条已读模型重新校订;技术架构总纲待重写为 Web 版。
6.3 工程债与代码级发现
- 注释/兜底值与配置漂移(core/tactical.py):注释写「弓还是 3」实际 bow = 5(飞镖才是 3);写「我方近战射程 2」实际近战 = 1;cd_cfg 的兜底值仍是旧值(jitter 600、sync 100),而其上配置已改成 first_act_jitter_ms = 0、sim_sync_ms = 250 —— 若 battle.json 丢字段,兜底会复现一个已被否决的旧节奏。
- 存档没有升级通道:load 对 version != SAVE_VERSION 一律拒载;目前全靠「加字段不升版本 + .get 兜底 + 读档补课」撑着(已知权衡,但是长期风险点)。
- 玩法数值仍写在代码里:DEMOLISH_REFUND = 0.5、UNREAD_BRIEF_MAX = 8、DEFAULT_SEED 等,与「数值全部外置」的口径不完全一致(scene.py 的布局常量是 README 明确承认的例外)。
- 两处人工同步的真相源:队伍配色 PARTY_COLORS 在 worldmap.js 与 map3d.js 各写一份;缩略图 MINI_COLORS 与 TERRAIN_COLOR 同色相不同明度(注释要求逐字一致,但没有共享模块兜底)。
- 已删功能留下的死物:hud.js 的 FLYOUT_HOSTS 仍列 world-modal(2D Jianghu浮窗已删)· style.css 残留 #world-modal / .wmap-side 规则 · index.html 注释仍写 #wmap-side(实际 #wmap-parties)· map3d.js 注释引用已删除的 hud.drawWorldMap。
- 已删建筑的残留资产:game/assets/models/bld_liaowangta_01.glb(瞭望塔)仍在库中,而当前 25 座建筑里没有它。
- 空壳包:combat/、tasks/ 是完全空占位(实现都在 core.*)。
- selftest 自身注释与断言矛盾:断言写 25 座建筑,同文件注释仍写 27(历史改动未清)。
- 心跳开销:settle_stamina / settle_train 每 0.05 秒循环调用(靠内部锚点节流);step_world 每 tick 重读 balance 并逐队伍乘算速度。
- 体积:工作区 9.26 GB,其中 asset_forge_data 4.17 GB · _tmp 1.24 GB · _backup_slim_20260920 877 MB · _shots 654 MB · render 342 MB · _archive 201 MB(均已 gitignore,但占磁盘)。
- 缺失的可复现证据:两份契约引用的 _tmp/verify_roster.py、_tmp/verify_journal.py 已不在仓库。
- ✅ 正面项:前端源码内字面 TODO/FIXME/HACK = 0;设计理由全部写在注释里(本项目最突出的协作特征);三样敏感物(accounts/ · saves/ · .env)零入库,_backup_accounts/ 也被 /_backup*/ 规则挡住 —— 隐私红线守住了。
6.4 本次未验证 / 未完成
- 自检已跑完:全绿 —— python -m gamenet.selftest 实测 1398 通过 / 0 失败,Sign out码 0,耗时 8.0 分钟。两点要记:1 断言数比 README 的「1331 条」多 67 条(在途新增的六卷 / 走镖 / 账房断言);2 README 写「秒级」,实测已是分钟级(8 分钟) —— 覆盖 360 章主线体检 + 169 场战斗 + 全量场景几何后,这个描述需要改;日常改动若指望「秒级反馈」,建议按模块跑探针,或给自检加分段开关。
- 未跑任何无头浏览器探针(需浏览器与冷启动,且按项目规矩一次只跑一个)。
- 本次调研全程只读,唯一新增文件是本文档。
7. 快速上手
# 一键启动(Windows)—— 构建前端 + 拉起两个后端 + 单入口代理
run_all.bat # 打开 http://127.0.0.1:8080/
run_all.bat --no-build # 跳过构建
# 手动(调试)
cd web/backend && python -m gamenet.server # :8000
cd asset-forge/backend && ASSET_FORGE_BASE=/forge python run.py # :8788
python tools/serve_single_entry.py --host [服务器地址] --port 8080 # :8080
# Windows 常驻服务
python tools/services.py status | start | stop | restart | logs
# 改完代码必跑
cd web/backend && python -m gamenet.selftest # 1 逻辑自检
node web/tools/probe_<模块>.cjs # 2 定点探针(一次只跑一个)
node web/tools/_chk_shot_timeout.cjs # 3 四道门禁
node web/tools/_chk_probe_backtick.cjs
node web/tools/_chk_shared_imports.cjs
node web/tools/_chk_probe_eval.cjs
改代码前请记住:改常量后跑依赖它的断言 · 新增 HTTP 接口必须鉴权 · 前端字段名必须与后端 scene.py 逐字一致(拼错静默退回旧行为)· 数值改 game/data/*.json 而不是代码 · 改「等级/档位」前先想老档 · 布局常量看 scene.py。