# wuxia-time
**Repository Path**: aowind/wuxia-time
## Basic Information
- **Project Name**: wuxia-time
- **Description**: 武侠时光 · 门派经营 —— 3D 武侠门派经营游戏(FastAPI 权威世界 + three.js 渲染视窗)
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-20
- **Last Updated**: 2026-10-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 岁月 · 江湖
> 一款用浏览器打开的 **3D 武侠门派经营** 游戏 —— 开山立派、招贤纳士、营建山门、经营田产、铸兵研武。
> 后端是一个**权威世界**,前端只是一扇**渲染视窗**。
>
> 🔴 **2026-09-27 门派改造落地**:门派场景整体重构为《了不起的修仙模拟器》式的**殖民地模拟**
> —— 蓝图建造 / 房间环境 / 弟子需求与心情 / 时辰日程 / 内外门修炼 / 敌袭防御。
> 旧的「点一下盖楼」体系已退役,详见 `docs/参考-了不起的修仙模拟器-拆解与门派改造方案.md`
> 与决策台账 `docs/game-design-decisions.md`(D-001~D-028)。
**🎮 线上体验:**(共享服务器 · 子路径部署;同机 AIGPart 平台首页仍是 )
> 🔑 **已接 AIGPart 单点登录**:平台账号从 AIGPart 点进游戏会自动登录(`/play/menpai/sso`);老账号按已验证邮箱首次 SSO 自动绑定,进度不变;邮箱密码登录保留。详见 [SSO 接入清单](docs/设计-AIGPart SSO 接入清单.md)。
---
## 一、这是什么
一个完整的 3D 门派经营游戏,也是**一整套 AI 资产生产线**的落地现场。
玩家从一座荒山起家:营建大殿与库房、招揽门人、开垦农田、参悟武学;门人会自己走动、
做事、成长。世界按真实时间推进,玩家不在线时门派照常运转。
除经营之外,还有一条**完整的主线剧情**:点右下角的「**岁月**」进面板,
顶部三枚**卷页签**可切 —— 三卷各自独立推进、进度分开记:
| 卷 | 章 / 战 | 战斗等级 | 讲什么 | 卷末奖励 |
|---|---|---|---|---|
| **第一章《岁月》** | 5 层 60 章 / 30 战 | 3 → 66 | 雨夜收留华山弟子**令狐冲**,陪他走完华山、福州、洛阳、西湖、黑木崖 | 天资质剑客**令狐冲** |
| **第二章《天龙八部》** | 3 层 12 章 / 6 战 | 68 → 98 | 无量山遇**段誉**、杏子林看**乔峰**被逐、雁门关外并肩御敌 | 天资质**虚竹**(少林禅杖) |
| **第三章《雪山飞狐》** | 3 层 12 章 / 6 战 | 102 → 122 | 辽东道上识**胡斐**、苗家揭开二十年前的毒刀旧账、雪谷决战 | 天资质刀客**胡斐** |
难度是**连续**的(3 → 66 → 98 → 122),数据体检专门查"卷间不许回退"。
对话有**人物立绘与场景背景**(57 张 AI 出图,与全项目同一套画风),
文案支持**行内 markdown**(`**粗体**` 会真的渲染成粗体,而不是把星号打出来);
对话**整层可点**——点画面任意处(场景、立绘、空白)都算"下一步",
只有 ✕ / 跳过 / 继续 三个按钮各管各的。
门派之外还有一片**江湖**:点右下角的「**江湖**」(罗盘)推开**世界大地图** ——
**另一套 3D 视窗**,**648 米见方的连续地形**(山峦、林地、河湖、城池,
是一整张曲面而不是一格格方块),城镇之间由**自动生成的道路网**相连。
派**队伍出城游历**:左键点地即出发,途中会遇上随机游荡的**山匪流寇**,撞上便触发**遭遇战**;
可同时派出**多支队伍**,随时挑一支指挥(脚下铺出**脚印虚线**标出去向),
左下角一张**缩略舆图**实时框出你正在看的范围。
> 💡 项目最初是为了**给自家孩子做一款能长期玩的游戏**,因此格外在意:
> 数值要看得懂、操作要直接、进度要能累积。
### 玩法循环:殖民地的每一天
> 🔴 旧「8 步新手引导」已随旧建筑体系退役(D-020)——新玩法靠**默认日程与自动认领**
> 即可自转,玩家按需介入。
早上辰时,弟子们从卧房醒来(精力回满,心情因「睡在自己的床上」+3)。外门弟子按日程
进入工作:有人去柴房外砍树,木料落地成堆;搬运工把木料搬进仓库;昨天画的 3 间卧房
蓝图开始有人搬料、砌墙、盖顶。巳时,新卧房落成;内门弟子整个上午在静室打坐,修为
随房间环境与功法匹配度缓慢累积;外门弟子在演武场练武攒**筑基值**,攒满换一次晋升
内门的机会。午时按日程去膳堂吃灶台做好的熟食。傍晚,打造台前的铁匠交出一把宝器
长剑(手感好还能出神兵)。入夜,山贼摸到山门外(声望越高来得越强),护山阵给全门
加防御……体力不济、心情低落的弟子会怠工甚至叛逃——经营就是让这一切良性循环。
### 玩法入口(主界面左侧与右侧的常驻按钮)
| 入口 | 干什么 |
|---|---|
| **营造 / 蓝图** | 殖民地营造:家具/生产/功能建筑点地落蓝图,墙/门/地板拖拽连画 |
| **规划** | 范围指令:砍伐 / 开采 / 采集 / 清树桩 / 圈田(谷子·棉花·药草·灵芝) |
| **工坊** | 熔炉/灶台/药炉/织机/打造台的配方队列(含兵器谱与神兵) |
| **工作** | 每位门人的活计优先级(搬运/建造/采集农活/生产炊事)+ **时辰日程表** |
| **账房 / 镖局 / 藏经阁** | 旧功能入口迁移(货架求购 / 镖局任务板 / 研习) |
---
## 二、玩法系统
| 系统 | 内容 | 配置 |
|---|---|---|
| **殖民地营造**(批1/2) | **蓝图建造**:画蓝图 → 弟子搬运 → 施工竣工(1 格 = 1 段墙,拖拽连画);26 种 v2 建筑(结构/家具/生产/功能/阵法),部分有声望门槛 | `buildings_v2.json` |
| **弟子需求**(批3) | 饱食/精力/心情衰减与自动吃睡;**心情词条**驱动心情;床位认领与单间卧房;心情过低怠工、**叛逃**判定 | `colony_balance.json` `thoughts.json` |
| **采集种植**(批4) | 门派内资源实体(树/岩/铁矿脉/玉脉/药草丛/浆果丛,砍树留桩复生);**范围指令**(砍/挖/采/清);**农田**四种作物按天生长 | `colony_balance.json` |
| **生产打造**(批4) | 五产出台配方队列(熔炉/灶台/药炉/织机/打造台):缺料备料→工时→**品质 roll**(手艺+心情);打造台兵器谱:品阶看材料、**品质与材料无关**、宝器概率出**神兵** | `recipes.json` |
| **时辰日程**(批5) | 每位门人 12 时辰 × 8 档,3 套模板 + 逐格编辑;日程即命令;**参研**在藏经阁产参悟点 | `colony_balance.json#schedule_templates` |
| **内外门双轨**(批6) | 外门练武攒**筑基值**,满门槛+筑基丹+静室+席位**晋升内门**;内门修习**心法**(6 本,参悟解锁武技)、打坐修为、**闭关突破**(走火入魔赌局)、**心魔**(清心丹解) | `gongfa.json` |
| **灵气场**(批6) | 地脉×2 + 聚灵阵:静室灵气浓度与陈设美观决定修炼系数 | `colony_balance.json` |
| **敌袭防御**(批7) | 声望越高来犯越强(惯匪→强盗头子):预警→徘徊→**攻墙拆墙**→山门决战(护山阵防+15%);战胜掉落落地堆、战败被掠 | `colony_balance.json#raid_templates` |
| **离线留任**(批8) | 离线按**每时辰一粗步**补演(封顶 48 时辰):吃睡/打坐/练武/工台照常运转,登录收**离线简报** | `colony_balance.json` |
| **招募** | 招贤榜刷新,按声望解锁档位,门人有资质与专长;**超编自动转客卿**(价 ×1.5,只走大地图) | `recruit.json` |
| **镖局** | 派门人出镖赚声望与资源,有任务轮换与在途任务卡(入口:**镖局**按钮) | `tasks.json` |
| **账房** | 货架出售 + 求购交付(入口:**账房**按钮,保底 1 级开放) | `balance.json#shop` |
| **武学** | 19 门门派武学(3 大学派)+ 10 门武器技能(**研习+参悟解锁**) | `techs.json` `skills.json` `gongfa.json` |
| **装备** | **7 个槽位** × 5 档品质(凡品→良品→精品→宝器→神兵);打造台产出入库 | `equipment.json` |
| **成长** | **110 级 = 11 境界 × 10 小级**(境门上闸+闭关突破),老档自动迁移;外功等级终身成长 | `realms.json` |
| **资源** | **10 种资源**(基础 6 + 玉石/铁锭/布匹/寒玉)的生产、采集与消耗闭环 | `resources.json` |
| **景致** | 植被 / 装饰可拖动、染色、删除(删除记法为黑名单) | `scene.py` |
| **游历 · 世界大地图** | **独立 3D 视窗**:648 米见方**连续地形**(山 / 林 / 水 / 城池)+ 自动生成的**道路网**(城镇互通);派队伍出城,遇**山匪流寇**即触发遭遇战;**多队并行**、选一支指挥、脚印虚线标去向、角落**缩略舆图**。**行军耗粮**(粮尽则饥饿减速) | `balance.json#world` |
| **大地图经营**(2026-09-24) | **采集派遣**:点资源点=派队开采,货载满/矿枯竭自动停,收队自动交货(离匪巢近的矿风险高);**城镇委托板**:每镇常驻 2 条委托(剿匪 / 寻药 / 送信 / 护送),完成即补、涨**城镇好感**(好感「友善」后该镇休整免费);**驿站休整**:队伍停在城旁可付银当场痊愈伤员;**游历奇遇**:离路野行按地形概率触发**随机事件**(17 条池:奇遇 / 危机 / 人情 / 环境),奖励资源 / 经验 / 好感,甚至**奇人入伙** | `events.json` `balance.json#world` |
| **战术战斗** | **3D 全屏战场**:10×10 格子**只做布阵占位**,开战后人物**自由移动**(连续坐标,不按格子走)、主动靠近敌人、**CD 一到就出手不等别人**;兵器决定射程与攻击表现 | `battle.json` |
| **主线「岁月」** | **三卷 84 章 / 42 战**(岁月 60+30 · 天龙八部 12+6 · 雪山飞狐 12+6)。面板顶部**卷页签**切换,每卷进度独立;本章对话 → 开战 → 打赢结算(资源/经验/装备)。对话支持**行内 md**、**整层点击**推进。每卷通关迎一位**天资质**伙伴入派(令狐冲 / 虚竹 / 胡斐) | `mainline.json` |
| **剧情战斗与结算** | 战场有**悬停人物 tips**(名字/阵营/生命/内力)、**脚下阵营环**(我方绿 / 敌方红 / 我方 NPC 蓝,随尸体消失)、**头顶生命值小条**、**左侧双方统计面板**(头像 / 名称 / 生命 / 内力 / **累计伤害** / 当前释放的武学);**没有回合**——各按兵器 CD 出手,同刻并发不排队;章节战斗打完**自动收起剧情层**。战报进「汇报簿」(游历遭遇战有**独立版式**:遭遇对象 / 双方人数 / 交手次数 / 缴获折损 / 谁负了伤) | `battle.json` |
| **音画** | 2 首 BGM + **15 段过场视频**(任务成败)+ Web Audio 合成音效 | `audio.js` `scene.py` |
> 🔴 **数值全部外置**在 `game/data/*.json` —— 改玩法不用动代码。这是项目最硬的一条规矩。
---
### 界面与规则(2026-09-24 / 25 两批)
| 改动 | 要点 |
|---|---|
| **门人卡牌** | 人物牌收敛成**一个共享模块**(`ui/member_card.js`)——主界面门人条、**游历选人**、战斗牌条同源;悬停 tips 给**完整信息**(身份 / 境界 / 资质 / 战力 / 四维含「外物」加成 / 体力经验 / 擅长 / 随身 / 武艺 / 此刻在做什么) |
| **游历选人** | 用**人物卡牌**选人(红框 + ✓ 选中、负伤压灰);**已在其它队伍里的人不出现在候选**(并写明"另有 N 人已在游历队伍中");队伍卡点一下即**镜头对准该队**,卡上列着**正在跑的委托**,点任务看详情与"怎么完成" |
| **大地图** | 地面**压平到同一平面**(林地/山地不再各自鼓包)、**道路贴地**(原先抬高 0.9 米,会把行走的队伍下半身盖住)、据点标记按**插值地面**摆放不陷地、**小地图像素级重绘**(1 格 = 1 像素 + 双线性放大 + 山体阴影,不再是一格格方块)、水面顶点级渐变 |
| **大地图交互** | 右侧**据点图标栏**(按 本门/城镇/门派/山匪/官府 分列);点图标或点据点弹**交互菜单**(定位 / 查看委托 / 驿站休整 / 征讨 / 派队采集,够不着的事项置灰并写明原因);点地目标圈缩到**直径 3.8 米** |
| **大殿升级** | 新增**下行锁的完整口径**:非资源、非景观的建筑**必须都已建成**(没建造按 **0 级**算)且等级 ≥ 大殿当前等级,大殿才能升级;理由里会点名"还差谁、现几级 / 尚未建造" |
| **营建收尾** | 位置选定、动工成功 ⇒ **收摆放态 + 关建筑浮窗**,但**保留营造模式与建造坞**(建完接着建下一座是常态) |
| **城内门人** | 走位三约束:不穿建筑(落在任何建筑占地里就换落脚点)、不出城墙(越界钳回院内)、做事时站在**对应建筑旁**(锚点由后端按活动下发) |
### 大地图 / 走镖收口(2026-09-26)
| 改动 | 要点 |
|---|---|
| **押镖到达即交割** | 抵达判定从「端点 → 城心」改成**本 tick 走过的折线**扫掠(擦着城畿过去也算到);遭遇/商队交涉造成的**临时停车**记下目的地,交涉一结束**自动续行**(旧版停一次就永远停在半路 ⇒ 委托永远不完成);「金镖缺镖头 / 护队人数不够」不再静默 continue,改为事件流 + 日志簿一行 |
| **去掉 18 米格子** | 遭遇 / 光点 / 抵达的判定半径改成**米口径**(travel_encounter_radius_m 1.0 米、world_event_spot_radius_m 1.0 米、town_arrive_radius_m 36 米);队伍每 tick 的轨迹按 travel_event_resolution_m(0.5 米)**重采样**,遭遇判定吃这条亚米级折线;山匪漫游目标点改**连续浮点**(不再抽整数格心)。老格口径键保留为兜底(只在米键缺席时才读) |
| **左键按住拖动移图** | 左键与中/右键一样映射 PAN(enablePan 显式打开);_bindMouse 仍按位移 + 时长区分「拖动」与「轻点派队」,底部提示补上「左键按住拖动 = 移图」 |
| **奇遇 3D 问号** | 每个事件光点旁立一枚 **3.0 米**高的立体问号(圆环 + 短竖 + 圆点三件基本体,无外部资产),绕 Y 轴朝向镜头、按事件类别着色、夜里自发光更亮 |
| **大地图昼夜** | 与城池同一套 game_minutes → solar → t 公式(MapScene3D.applyDaylight):主光/半球光/补光的颜色与强度、天空盒压暗、雾色转深蓝;各城池与据点夜里挂**暖色光晕**(白天整组隐藏)+ 固定 3 盏**点光源池**(数量恒定 ⇒ 不触发全场景着色器重编译),按镜头就近分配 |
## 三、成长体系:一条不断档的成长轴
游戏的"耐玩"全压在成长轴上 —— 从掌门到门人、从建筑到武学,每一层都在涨:
```
门派规模 ← 营建 27 座 × 10 档 ← 资源产出 ↑
↑ ↑
门人数量 ← 建筑容量 资源积累
↑ ↑
门人战力 ← 四维属性 + 装备 7 槽 ← 打造 / 掉落
↑ ↑
110 级境界(11 境 × 10 级) ← 历练 / 出镖
```
| 轴 | 阶梯 | 说明 |
|---|---|---|
| **境界** | **110 级 = 11 境界 × 10 小级** | 菜鸟 → … → 武圣;**境门上闸**:每境第 10 级需**闭关突破**(走火入魔赌局) |
| **外功** | 每门 **10 级** | 等级由**累计熟练度**现推;练武档在演武场累积 |
| **内功** | 6 本心法 × 参悟 | 打坐修为(五系数公式)+ 参悟**逐级解锁武技**;匹配度看资质与兵器契合 |
| **生产手艺** | 0~100 | 干生产/打造活累积,喂品质与工时 |
| **筑基值** | 0~100(外门) | 练武/淬体汤累积;满门槛+筑基丹+静室**晋升内门** |
| **武器技能** | 每门 **10 级** | 5 种兵器各 2 门;**参悟解锁** + 练武熟练度 |
| **装备** | 5 档品质 × 7 槽位 | 凡品 → 良品 → 精品 → 宝器 → 神兵(打造台出品) |
> 🔴 **老档必须能无损迁移**。等级制从「55 级(11 境 × 5 小级)」升到「110 级(11 境 × 10 小级)」时,
> 换算规则是 **`L → 2L − 1`** —— 保境界内相对位置,
> 「老 55 级(武圣圆满)」→「新 109 级(武圣归元)」,**境界一寸不退**。
> 存档用 `realm_ver` 版本号识别老档(缺键视作 1),迁移一次后写回 2,**幂等**。
---
## 四、架构:后端是权威世界,前端只是视窗
这是整个项目最重要的设计决定。
```
┌─────────────────────────────────────────────────────────────┐
│ 浏览器(渲染视窗) │
│ three.js 场景 · HUD · 拾取 · 过场视频 · 卡片与浮窗 │
│ —— 不持有任何游戏逻辑,只负责「把世界画出来」 │
└───────────────────────────┬─────────────────────────────────┘
│ WebSocket(全量快照)+ HTTP(命令)
┌───────────────────────────▼─────────────────────────────────┐
│ 游戏后端 gamenet(权威世界 · Python/FastAPI) │
│ 时间推进 · 建造校验 · 门人行为 · 资源产出 · 存档落盘 │
│ 零第三方逻辑依赖:游戏数值与规则全部自己实现 │
└─────────────────────────────────────────────────────────────┘
▲
│ 读取
game/data/*.json(数值配置 · 唯一真相源)
```
**为什么要这么分?**
- **反作弊与一致性**:前端改任何变量都无法欺骗后端,一切以服务端状态为准。
- **进度不丢**:刷新、换设备、断线重连,世界都从后端快照恢复。
- **可测试**:数值与规则是纯逻辑,能在**不开浏览器**的前提下跑完 **1,074 条断言**(2026-10-18 实测)。
- **离线推进**:时间由后端按真实时间推进,关掉页面门派仍在运转。
---
## 五、技术栈
| 层 | 选型 | 说明 |
|---|---|---|
| 权威世界 | **Python 3.11 + FastAPI + uvicorn** | 纯自研逻辑,零游戏引擎依赖 |
| 实时同步 | **WebSocket** | 后端推全量快照,前端重放 |
| 渲染 | **three.js 0.186 + Vite 8** | 正交相机、InstancedMesh、动态昼夜 |
| 数值配置 | **`game/data/*.json`** | 建筑 / 门人 / 武学 / 境界,改这里即改玩法 |
| 资产 | **GLB(AI 生成)+ PNG** | 由项目内资产生产线产出,见第九节 |
| 部署 | **Nginx + PM2** | Nginx 单入口(静态直出 + 反代),PM2 守护后端 |
### 代码规模
| 部分 | 规模 |
|---|---|
| 后端逻辑 `gamenet` | **42 个 `.py` · 34,176 行** |
| 前端渲染 `frontend/src` | **26 个 `.js` · 28,748 行** |
| 无头浏览器探针 `web/tools` | **184 个 `.cjs`**(其中 `probe_*.cjs` **89** 个,其余是共享 helper / 门禁 / 断言台账工具) |
| 工具链 `tools/` | **84 个 `.py`**(顶层一级;资产导入 / 减面 / 图标生成 / 视频转码 / 表格生成;另有 13 个 `_` 前缀的一次性脚本不计) |
| 逻辑自检断言 | `selftest.py` · **1,074 条**(2026-10-18 实测 1,074 通过 / 0 失败;同日两次计时 **393.4s / 328.6s**;静态 `check()` 调用点 **1,069**) |
| 游戏资产 | **177.9 MB**(**128 个 GLB** + 112 张图标 + **57 张剧情美术** + 15 段过场视频 + 2 首 BGM) |
> 📏 **统计口径(2026-10-18 实测)**:行数按换行符计、**含空行**;资产体积按 1024 进制(字节 ÷ 1024²)。
> PowerShell 的 `Measure-Object -Line` 会漏掉空行,**不要用它核数**。
---
## 六、目录结构
```
.
├── web/ # ★ 游戏本体(唯一主线)
│ ├── backend/
│ │ └── gamenet/ # 权威世界:逻辑 / 存档 / 账户 / 场景常量
│ │ └── world/ # · 世界大地图:地形生成 / 道路网(MST+A*)/ 游历与遭遇
│ ├── frontend/ # 渲染视窗:three.js 场景 + HUD
│ │ ├── src/
│ │ │ ├── scene/ # · map3d.js —— 大地图渲染器(独立 renderer,不借主场景)
│ │ │ └── ui/ # · worldmap.js —— 大地图面板 / 指令 / 缩略舆图
│ │ └── public/
│ └── tools/ # 无头浏览器探针 + 验收门禁
│
├── game/
│ ├── data/ # ★ 数值配置(唯一真相源,改了立刻生效)
│ └── assets/ # ★ 游戏资产(177.9 MB)
│ ├── models/ # · 128 个 GLB(建筑 / 景物 / 门人 · 批18 另加 12 绑定 + 12 动作包)
│ ├── ui/ # · 界面图 + icons/(112 张图标)+ videos/(15 段过场)
│ ├── story/ # · 主线剧情美术:28 张人物立绘 + 29 张场景背景
│ ├── audio/ # · 2 首 BGM(山居 / 夜阑)
│ └── textures/ # · 贴图
│
├── tools/ # Python 工具链:资产导入 / 减面 / 校验 / 服务管理
├── docs/ # 设计总纲 / 接口契约 / 部署与资产文档
├── deploy/ # 部署配置:PM2 / Nginx / 推送脚本
│
├── asset-forge/ # 资产生产线:文生图 → 图生3D
├── rig-forge/ # 骨骼绑定工坊
├── model-forge/ # Blender 建模脚本生成
└── audio-forge/ # BGM / 音效合成(纯 numpy)
```
> 🔴 `game/data/*.json` 与 `game/assets/` 是**不可再生的核心资产**,请勿随手删除。
> 具体说:`game/data/*.json` · `game/assets/models/*.glb` · `game/assets/ui/*.png`
> · `game/assets/story/*` · `web/backend/{accounts,saves}/` —— **绝不 `rm -rf game/`**。
>
> 📌 **两个"立绘"不是一回事**(代码与文档里都会出现):
> `assets/ui/icons/body_*.png` 是**装备界面的人形剪影**(纯色、无细节,上面压 7 个孔位);
> `assets/story/portrait_*.png` 是**剧情对话的人物立绘**(五官清晰、要认出谁在说话)。
---
## 七、快速开始
### 前置
- Python **3.11+**
- Node.js **18+**
- 首次运行前安装依赖:
```bash
# 后端(零游戏依赖,只需要 Web 框架)
cd web/backend && pip install -r requirements.txt
# 前端
cd web/frontend && npm install
```
### 一键启动(推荐)
```bash
# Linux / macOS
bash run_all.sh
# Windows
run_all.bat
```
脚本会依次:构建前端 → 拉起游戏后端 → 拉起资源生成器 → 起单入口代理。
打开 **** 即可游玩。
### 手动启动(调试用)
```bash
# ① 游戏后端 → :8001(本地默认;生产用 GAMENET_PORT=8000)
cd web/backend && python -m gamenet.server
# ② 资源生成器 → :8788(可选,只有要产资产时才需要)
cd asset-forge/backend && ASSET_FORGE_BASE=/forge python run.py
# ③ 单入口代理 → :8080(把上面两个合成一个入口)
python tools/serve_single_entry.py --host 0.0.0.0 --port 8080
```
### 服务与端口
| 服务 | 端口 | 说明 |
|---|---|---|
| 游戏后端 `gamenet` | **8001** | 权威世界 + WebSocket(本地与当前生产服务器都是 8001;8000 留给同机 idle-wuxia) |
| 资源生成器 `asset-forge` | **8788** | 挂 `/forge` 前缀,产 2D/3D 资产 |
| 骨骼绑定工坊 `rig-forge` | **8789** | 模型绑骨 |
| 单入口代理 | **8080** | 对外统一入口(生产交给 Nginx) |
> Windows 上有一套常驻服务管理(脱离终端、不弹窗、输出入日志):
> `python tools/services.py {status|start|stop|restart|logs}`
> 💡 **本地端口已避开 :8000**(2026-10-18):本机 :8000 被 `D:\idle-wuxia` 的 `node server/index.mjs` 占用,
> 游戏后端默认改 **8001**,:8080 反代指到 8001 —— 打开 :8080 仍是本游戏;生产服务器 8.134.249.230 的 :8000 也被 idle-wuxia 占用,因此同样是 **8001**。
> ⚠️ `services.py status` 只按端口判断:若 :8001 被别的进程占用,`services.py start` 会误判「已在运行」并跳过 gamenet。
> 排查:`Get-NetTCPConnection -LocalPort 8001` 拿 PID → `Get-Process -Id | fl Path`;或看 `_logs\game.log`。
---
## 八、测试与验收
项目有**三层验收**,改完代码请按顺序跑:
### ① 纯逻辑自检(约 6 分钟,首选)
```bash
# 约 6 分钟(2026-10-18 两次实测 393.4s / 328.6s)
cd web/backend && python -m gamenet.selftest
```
> ⚠️ **验收命令建议带 `PYTHONIOENCODING=utf-8`**(PowerShell:`$env:PYTHONIOENCODING="utf-8"`;cmd:`set PYTHONIOENCODING=utf-8`)——
> 否则 GBK 控制台会在含「⇒」的断言文案处直接 `UnicodeEncodeError` 崩掉,看着像断言失败,其实是编码崩了。
> ⚠️ 必须用 `-m` 模块方式跑。直接 `python web/backend/gamenet/selftest.py` 会因相对导入报错。
覆盖时间推进、建造校验、资源产出、门人行为、存档往返、主线推进、大地图游历与遭遇等
**1,074 条断言**(2026-10-18 实测;含主线**逐卷**体检:每卷的章号连续 / 层次对齐 / 敌人与说话人引用有效 /
难度单调且卷间攀升 / 卷末奖励齐备)。
### ② 定点探针(无头浏览器)
`web/tools/` 下是针对各模块的无头浏览器探针(共 **184 个** `.cjs`,其中 `probe_*.cjs` **89** 个),只在你改动的那块跑即可:
```bash
# 殖民地模拟(门派改造批 1-8 · colony 组,串行跑)
node web/tools/run_parallel.cjs --suite colony # 10 探针组:营造/暂停/房间/心跳/生产链×2/日程/修炼/敌袭
node web/tools/probe_colony_build.cjs # 蓝图 → 搬料施工 → 竣工
node web/tools/probe_colony_produce.cjs # 砍树→木桌 端到端(--timeout 900000)
node web/tools/probe_colony_forge.cjs # 铁锭→兵器 端到端(--timeout 900000)
node web/tools/probe_colony_schedule.cjs # 时辰日程 + 练武外功
node web/tools/probe_colony_cultivate.cjs # 打坐修为 / 参悟
node web/tools/probe_colony_raid.cjs # 敌袭状态机
node web/tools/probe_colony_unlock.cjs # 声望解锁
python tools/bench_colony.py # 性能压测:48 弟子满载 tick / 快照体积
# 主线「岁月」(三卷 84 章 / 42 战 / 立绘 / 场景背景)
node web/tools/probe_years.cjs # 63 判据:卷页签 / 目录 / 对话 / 立绘 / 背景 / 整层点击 / md 渲染 / 滚动位置 / 战斗链路
python tools/verify_mainline.py # **逐卷**数据体检(引用 / 层号 / 难度 / 奖励)
node web/tools/probe_battle.cjs # 139 判据:布阵 / 3D 战场 / 自由移动 / 并发出手 / 兵器表现 / 结算
# 成长与卡片
node web/tools/probe_skill_level.cjs # 武学熟练度等级
node web/tools/probe_tech_cards.cjs # 武学卡片(学派配色)
node web/tools/probe_equip_cards.cjs # 装备卡片(品质配色)
node web/tools/probe_equip_skill_ui.cjs # 装备与技能面板
# 门人与江湖
node web/tools/probe_members.cjs # 门人渲染与标签
node web/tools/probe_task_card_dock.cjs # 在途任务卡
node web/tools/probe_log_dock.cjs # 日志坞与未读图标
node web/tools/probe_worldmap.cjs # 世界大地图:连续地形 / 45° 锁死 / 派队游历 / 脚印 / 多队 / 缩略舆图 / 遭遇链路(123 判据)
node web/tools/probe_wmap_time.cjs # 大地图时辰与昼夜 / 各城池据点夜灯 / 奇遇 3D 问号 / 左键按住拖动移图(19 判据)
# 音视频
node web/tools/probe_audio.cjs # BGM 与音效
node web/tools/probe_video_shield.cjs # 过场视频屏蔽
# 界面细节
node web/tools/probe_button_text.cjs # 按钮文案
node web/tools/probe_flyout_scroll.cjs # 浮窗滚动
node web/tools/probe_member_lv.cjs # 门人等级显示
node web/tools/probe_member_tag_badge.cjs # 门人头标
node web/tools/probe_body_figure.cjs # 人物立绘
# 基础
node web/tools/probe_camera_buttons.cjs # 左键 / 右键相机操作
node web/tools/probe_boot_time.cjs # 首屏耗时
```
> 🔴 **探针不要串跑**(一个循环里连续跑多个)。大场景冷启动会互相污染,
> 导致失败点游走、出现**假红**。一次只跑一个。
### ③ 门禁(四道,改完探针各跑一次)
```bash
node web/tools/_chk_shot_timeout.cjs # ① 截图超时必须显式给足
node web/tools/_chk_probe_backtick.cjs # ② 模板串内禁写反引号(探针 + 前端源码)
node web/tools/_chk_shared_imports.cjs # ③ 共享 helper 用了必须导入
node web/tools/_chk_probe_eval.cjs # ④ 模板串里的行尾注释必须补 \n(本轮 191 处)
node web/tools/_chk_asset_paths.cjs # 诊断:前端源码禁相对 assets/ 路径(子路径部署会 404)
```
四道门禁(另加 `_chk_mat.cjs`、`_chk_asset_paths.cjs` 两条诊断)守的都是**同一类坑:静默失败** ——
测试还绿着,但证据已经不可信了。
**① 截图超时**:本场景是 **2000 万面级软渲染**,整页截图实测可超 15 秒,
而 Playwright 默认只给 30 秒。超时的后果**不是报错而是静默** ——
截图异常被 `catch` 吞成一行 `[warn]`,**判据照样全绿,图却没出(或还是上一轮的旧图)**。
> 记住那句话:**判据全绿 ≠ 图是新的。**
> 规则:**落盘的截图**(带 `path`)必须显式 `timeout ≥ 60000`;不落盘的逼帧截图自动豁免。
**② 反引号**:探针把页面侧代码写成模板字符串,若在那段代码的**注释里**写了反引号,
会**提前把串闭合**。后面内容变成普通 JS —— **语法仍然合法**,
`node --check` 一声不响,报出来的是别处某个变量 `not defined`,跟真因隔着十万八千里。
(**前端源码同样受管** —— 那里也大量使用模板串。)
**③ 共享导入**:用了没导入只是语法合法、**运行时才** `ReferenceError`。
而探针普遍要跑几分钟(等建造 / 等结算),崩在第五分钟,
症状看着像"产品功能坏了",排查方向整条跑偏。
**④ 拼接串里的行尾注释**:探针把页面侧代码拼成长串交给浏览器执行,串里 `//` 之后
**必须补 `\n`** —— 否则拼出来是**一整行**,把后面的 `return` 与收尾括号一起注释掉。
最毒的地方在于:`READ_MAP` 这类**公共读数**一旦坏,二十多条断言会**一起假红**,
看着像"整个功能都坏了"。`_chk_probe_eval.cjs` 把全部拼接串抽出来逐个 `node --check`。
**诊断 ⑤ 相对 assets 路径**(2026-10-18 生产事故):前端源码里出现 `` `assets/…` `` 这种**相对字面量**时,
根路径部署(本地)看着一切正常,子路径部署却会解析成 `/play/menpai/app/assets/…`(多一段 `/app`)→ 404。
资源 URL 只能走 `appUrl('/assets/…')`(`src/base.js`)或后端下发的 `ui_dir` / `models_dir`。
`_chk_asset_paths.cjs` 静态扫全部前端源码,命中即红。
> 📌 这些门禁的共同点是:**把规矩写成断言,别写成提醒**。
> ② 的注释里明写着"这条警告本来就在文档里写着,本轮还是又踩了一次 —— 靠记性守不住"。
### 关于 `e2e.cjs`
`web/tools/e2e.cjs` 是完整端到端脚本(真点按钮 + 截图),但**成本极高**:
首屏 50 秒以上、一轮十几分钟、多会话并发常把后端重启掉。
⇒ **已降级为"按需"**。日常验收请用 ① + ②,只在确有必要时跑整轮 e2e。
---
## 九、AI 资产生产线
游戏里的建筑、景物、门人形象、图标、BGM 与过场视频,都由项目内这套**生产线**产出:
```
建筑 / 景物 / 门人(128 GLB):
提示词 → [asset-forge] 文生图 → 图生3D → GLB
↓
[rig-forge] 骨骼绑定
↓
[model-forge] Blender 脚本生成 / 规整
↓
tools/ 导入 → game/assets/models/
图标(112 张):
tools/gen_*.py → [asset-forge] 豆包 Seedream → PNG
↓
game/assets/ui/icons/
├── gen_building_icons.py 建筑 25 张
├── gen_realm_icons.py 境界 11 张
├── gen_equip_icons.py 装备 70 张
├── gen_ui_icons.py 界面 4 张
└── gen_body_figures.py 装备界面「人形剪影」2 张
⚠️ 与下面的「剧情立绘」**不是一回事**:
剪影要"无细节、低对比"(上面压 7 个孔位图标);
剧情立绘要"五官清晰、认得出是谁在说话"。
剧情美术(57 张 = **人物立绘 28** + **场景背景 29**):
tools/gen_story_art_plan.py ← 从 mainline.json 算出"需要哪些"(清单可复算)
→ game/data/story_art.json
tools/gen_story_art.py → Seedream 出图 → _tmp/story_art/
tools/make_story_art.py --auto-pick ← 打分自动挑图(+ --overview 出总览给人看)
tools/make_story_art.py --install ← 抠白 / 满幅 → game/assets/story/
过场视频(15 段):
tools/prep_task_videos.py(从原片转码)→ MP4 → game/assets/ui/videos/
BGM / 音效:
[audio-forge](纯 numpy 合成)→ WAV → game/assets/audio/
```
| 组件 | 职责 | 依赖 |
|---|---|---|
| `asset-forge` | 文生图(豆包 Seedream)+ 图生3D(腾讯混元生3D) | 需自行配置密钥 |
| `rig-forge` | 给静态模型程序化绑定骨架,导出可动 GLB | Blender |
| `model-forge` | 用文本模型生成 Blender 建模脚本并执行 | Blender + 文本模型 |
| `audio-forge` | BGM 与音效合成(无缝循环 `wrap_tail`) | 纯 numpy |
| `tools/gen_*_icons.py` | 批量生成各类图标(建筑 / 境界 / 装备 / 界面 / 立绘) | 同 asset-forge |
| `tools/prep_task_videos.py` | 过场视频转码入库(7 种任务 × 成败 + 入场) | ffmpeg |
| `tools/art_style.py` | **美术风格锚点** —— 出图前统一风格,保证一整套像一家人 | 纯 Python |
| `tools/gen_buildings.py` · `gen_realms.py` | **从公式生成数值表**(十档 / 110 级) | 纯 Python |
| `tools/gen_story_art_plan.py` | **从 `mainline.json` 反算美术清单**(该画哪些人、哪些景) | 纯 Python |
| `tools/gen_mainline_volumes.py` | **主线多卷的真相源**:把 `mainline.json` 升级成 `volumes` 并写入卷二/卷三(改台词改这里重跑;**永不重建卷一**) | 纯 Python |
| `tools/verify_mainline.py` | **主线数据体检**(逐卷:引用完整性 / 章号连续 / 层与章对齐 / 难度单调与卷间攀升 / 奖励键 / 模型存在) | 纯 Python |
| `tools/gen_story_art.py` | 剧情立绘 / 场景出图(支持 `--shard` 分片并行、`--skip-existing`) | 同 asset-forge |
| `tools/make_story_art.py` | 立绘抠白 + 场景满幅;**打分自动挑图** + 总览图 | Pillow |
| `tools/gen_master_avatar.py` | **掌门头像出图**(男/女候选 + 新旧 76px 对比图;2026-09-24 头像年轻化) | 同 asset-forge |
**剧情美术的三个关键取舍**(详见 `docs/剧情美术规划.md`):
1. **"立绘"与"装备界面的剪影"是两套相反的要求**,别混:
剪影要"无细节"(上面压孔位图标),立绘要"五官清晰"(要认出谁在说话)。
⇒ 所以 `art_style.py` 里是**两个独立风格句**(`STYLE_BODY` / `STYLE_PORTRAIT`),
不共用"画法"部分(只共用题材与画风内核)。
2. **抠图的阈值要按题材定,不能沿用图标的**:图标那套把掩码放宽到 2.2 倍色距
(为吃掉 AI 画的浅灰衬纸框),而"浅灰"正好等于**人物身上的浅色衣料**
⇒ 会把衣服抠出洞。剧情立绘改用**窄阈值 + 不放宽**,
并带一条可量化判据(**"吃掉非背景像素"的比例 < 1%**)。
3. **白晕要去"混合",不是去"像素"**(2026-09-24 抠图换代):窄阈值抠白后,人物边缘
总有 1~3px 的"人物色+白底"**混合带** —— 不够白、抠不掉,原样留着就是
叠在深色场景上的一圈白毛边。新算法(`make_story_art.py#_defringe_white`)按
`观测色 = a·人物色 + (1−a)·纯白` **反解**出人物真色与透明度:
白晕物理消失,且**一粒人物像素都不用吃**(28 张立绘实测"吃掉非背景"
最高 0.03%,红线 1%)。对比"腐蚀 1px"方案:腐蚀会把发丝啃秃,这个不会。
**装备图标的一个巧思**:Seedream **不产透明底**(回的是 JPEG),所以图标做成
「**底色牌 + 中央物品**」的形态 —— **底色直接就是品质色**。
于是"品质不同、边框色不同"由底色承担,**省掉整条抠图链路**,
也避开了抠图边缘毛刺这类经典翻车点。
> 🔁 前端 `equip_card.js` 留了「**有 `icon` 就出 `
`,否则回落 SVG**」的口子 ——
> 真图没到位时自动降级,**主流程不会被图标阻塞**。
> 🔁 **2026-09-28 出图模型换成豆包 Seedream 5.0 Flash**(`doubao-seedream-5-0-flash-260915`)。
> 同区域 / 同提示词实测(脚本留档 `_tmp/seedream_probe/`):
> **4 张候选墙钟 14.3s**(旧 4.0 组图 93.7s,快 6 倍)· 单图 2K 11~15s · `1024x1024` / `2304x1440` 按显式尺寸原样出图。
> 两个坑(`ark_client.py` 里已有挡与兜底,这里记一笔免得再踩):
> ① 5.0 Flash **不支持组图** —— `sequential_image_generation` 连传 `"disabled"` 都回 400,
> `ArkClient` 因此把 n>1 自动改走「**N 路并发单图**」(实测 4 张两两灰度均差 15~30/255,不是重复图);
> ② **不支持 `size="4K"`** —— 预设只认 1K / 1.5K / 2K,或显式 `宽x高`(总像素 921,600~4,624,220);
> 传 4K 在本地就报错,不浪费一次请求。
> 📌 **换模型只改 `asset-forge/backend/.env` 的 `ARK_MODEL_ID`** —— asset-forge 与 `tools/gen_*.py`
> 全部共用 `ArkClient`,没有第二处硬编码。
> ⚠️ **单价必须同步改 `ARK_PRICE_PER_IMAGE`**(4.0 = ¥0.20、5.0 Pro = ¥0.30;5.0 Flash 的官方价
> 以火山方舟控制台账单为准,当前沿用 ¥0.20 **宁高不低**),否则台账记账会失真 ——
> 后端 `COST.PRICE_2D_PER_IMAGE` 与各工具的预估打印都从这里读。
> 🔴 **使用资产生产线前需自备密钥**:复制 `asset-forge/backend/.env.example`
> 为 `.env` 并填入自己的凭证。`.env` 已被 `.gitignore` 忽略,**不会入库**。
>
> 🔴 **提示词里别写字面色号 / 数字年龄**(2026-09-24 实测):模型会把
> "近似 #F0E3CF" 当画面元素**原样画进图里**(右缘出现色号字样),
> 把"三十二岁"画成背景题字 —— 色用文字描述("老宣纸浅米色"),
> 年龄用"而立之年"这类**无数字**的说法。发现这类泄漏,重出前先除掉病根。
>
> 💡 连登录页的背景图都是 AI 生成的(`game/assets/ui/login_bg.jpg`,1920×1200),
> 且是**图生图**产出 —— 拿加载图当参考图,才能保证"跟原画风一致"。
>
> 📖 出图前先读 **`docs/美术风格设定.md`** —— 纸色 / 墨色 / 画风的统一口径在那儿。
>
> ⏸️ **未接线**:`audio-forge` 已产出第三首 `bgm_menpai_jianghu.wav`(江湖),
> 但**尚未接入游戏音频表** —— 目前线上只有「山居(昼)」「夜阑(夜)」两首。
> 接线方式见 `audio.js` 顶部注释(拷进 `game/assets/audio/` + `TRACKS` 加一条)。
---
## 十、部署
生产环境是一台**共享服务器**(8.134.249.230,同机还有 AIGPart / idle-wuxia / occ-game / dsh-relay):
```
公网 :80 ─► AIGPart 的 Nginx ─┬─► /play/wuxia/ 别的游戏(idle-wuxia :8000)
├─► /play/menpai/ 本项目 → 127.0.0.1:8001
└─► / AIGPart 平台首页
▲
PM2: menpai-game(127.0.0.1:8001,autorestart + 开机自启)
```
**要点**:
- 本项目**不占 80 根路径、不新增监听端口、不改别人的 location**:只在 AIGPart 的 :80 server 块里加一行 `include /etc/nginx/conf.d/menpai-locations.inc;`,该文件由 `deploy/prod/nginx-menpai-locations.inc` 维护(proxy_pass 末尾的 `/` 会剥掉 `/play/menpai` 前缀)。
- 后端固定 `127.0.0.1:8001`(同机 8000 已被 idle-wuxia 占用),:80 由 Nginx 收口。
- 前端用 `web/frontend/public/app-base.js` 在**运行时**补挂载前缀:同一份代码既能跑根路径(本地 / 单入口反代),也能跑 `/play/menpai/` 子路径;生产构建带 `VITE_BASE=/play/menpai/app/`。
- 静态、API、WebSocket 全部走 `/play/menpai/` 子路径反代;`/play/menpai/` 会 301 到 `/play/menpai/app/`。
- PM2 的 `interpreter: 'none'` **必写** —— 否则它拿 node 去解释 Python,进程秒退。
线上入口:(同机 AIGPart:;江湖:)。
详细运维见 `docs/生产部署-阿里云.md` 与 `deploy/prod/`。
### 更新线上
```bash
bash deploy/prod/push.sh # 同步代码 + 重建前端 + 重启后端
bash deploy/prod/push.sh --no-build # 只改后端时用(快)
bash deploy/prod/push.sh --data # 改了 game/data/*.json 数值
bash deploy/prod/push.sh --assets # 新增/替换了 GLB 或界面图(整包 116M,较慢)
```
> ⚠️ 推送脚本同步代码时会**主动避开** `web/backend/{accounts,saves}/` ——
> 那是玩家活数据,覆盖等于**玩家进度归零**。
>
> 💡 **只改了几个资产时,别用 `--assets` 传整包**。先算差异再单独传(清单 + 同名大小两层):
> ```bash
> find game/assets -type f -printf '%P\n' | sort > /tmp/l.txt
> ssh 'cd /opt/menpai/game/assets && find . -type f -printf "%P\n" | sort' > /tmp/r.txt
> comm -23 /tmp/l.txt /tmp/r.txt # 服务器缺的
> ```
> 实测一次发布只缺 63 个文件(18 MB),省下 100 MB 传输。
### 发布后验收
```bash
node web/tools/probe_prod_assets.cjs # ① 资产**全量对账** —— 纯读,不污染数据
node web/tools/probe_prod_tutorial.cjs # ② 端到端链路 —— ⚠️ 会注册账号,跑完必清
```
**① 资产全量对账**:读本地 `game/assets` 目录树 → 逐个查线上能否取到。
**新增任何资产都自动被覆盖,零维护**(早期版本是硬编码抽样清单,加一类资产就要
回来手工补样本 —— "忘了补"的后果是"新资产坏了但探针全绿")。
实测 220 个文件约 **5 秒**跑完。
🔴 必须用浏览器而不是 `curl` —— 图标 / 视频文件名含**中文**(`weapon_神兵.png`),
curl 在 Git Bash 下传中文路径会 **404**,那是 **curl 的编码问题**,不是产品故障;
浏览器发请求时**必然 URL 编码**,才与服务端 UTF-8 文件名对得上。
**② 端到端链路**:注册 → 立派开宗 → 进 3D 场景 → 新手任务卡片与浮窗,**31 项判据**。
判据全绿的标准:`CONSOLE_ERRORS` / `HTTP_BAD` / `REQ_FAILED` **三项全空**。
> ⚠️ ②**会往生产库注册真账号** ⇒ 跑完必须清理(三处对账:账号 / 会话 / 存档):
> ```bash
> ssh menpai-aliyun 'pm2 stop menpai-game' # ① 先停(引擎会把内存写回磁盘)
> scp deploy/prod/clean_test_accounts.py menpai-aliyun:/opt/menpai/web/backend/_c.py
> ssh menpai-aliyun 'cd /opt/menpai/web/backend && python3 _c.py && rm -f _c.py'
> ssh menpai-aliyun 'pm2 start menpai-game' # ③ 再起
> ```
> 清理脚本按**有效用户名单反查孤儿**,`aowind@qq.com` 白名单永不删。
### 🔴 改了地形 / 放置区之后的额外一步
挪了河道、城墙、放置边界之后,**已建建筑不会被回溯检查** ——
`placement_check` 只在**新放置**时跑。于是楼可能"长在河里"却不报任何错,
要等玩家自己看见。
发版前用**新代码**验一遍**真实存档**:
```bash
python tools/verify_buildings_in_zone.py <存档目录> # 只读
```
它复用产品的 `placement_check()` —— **不自己重写一份几何**
(两处各写一份 = 判据迟早分家)。输出每座建筑合法 / 非法,
非法时会提示"别直接把新代码发上去当无事发生"。
> 💡 取证方式:把线上存档 `scp` 到本地临时目录,用本地(新)代码验。
> 无 `x/z` 的建筑(如城墙,位置由代码按常量算)会自动跳过。
---
## 十一、注意事项
### 🔴 绝不入库的三样东西
| 路径 | 内容 | 后果 |
|---|---|---|
| `web/backend/accounts/` | 真人账号与会话 | 隐私泄露 |
| `web/backend/saves/` | 玩家存档 | 隐私泄露 + 进度污染 |
| `asset-forge/backend/.env` | 云端 API 密钥 | 密钥泄露、被盗刷 |
三者均已在 `.gitignore` 中排除。**新增敏感文件时,请同步补规则。**
> ⚠️ **`.gitignore` 不支持行尾注释**。写成 `web/render/ # 出图目录` 会被当成
> 字面模式(连空格和井号一起),规则**永远匹配不上** —— 而且不报错。
> 注释必须独占一行。改完用 `git check-ignore --no-index -v <路径>` 验证。
>
> ⚠️ **探针出图目录有三个落点**,新增探针时别只加一个:
> `web/render/` · `web/tools/_shots/` · **`web/tools/render/`**(多数探针的默认输出)。
> 排查已入库的出图:`git ls-files <目录> | wc -l`;
> 找还有哪些落点:`grep -rn "'render'" web/tools/*.cjs`。
### 改代码前请留意
1. **改常量 / 规格后,务必跑依赖它的测试** —— 断言是跟着规格走的。
2. **新增 HTTP 接口必须鉴权**(`require_session`),免鉴权的只有 `/api/auth/*`
与 `/api/health|scene|meta`。
3. **前端使用的字段名必须与后端 `scene.py` 逐字一致** ——
拼错不会报错,会静默退回旧行为,非常难查。
4. **`game/data/*.json` 是数值唯一真相源**,不要在代码里再写一份。
5. **改 `web/tools/` 下的探针后,跑一次三道门禁**(截图超时 / 反引号 / 共享导入)。
6. **布局常量的唯一真相源是 `web/backend/gamenet/scene.py`**。
7. 🔴 **改「等级 / 档位」这类数字轴之前,先想老档怎么办** ——
存档里的数字**含义**变了,不迁移就是"老玩家一夜掉五个境界"。
正确姿势:给存档加**版本号**(`realm_ver` 那样),缺键视作老版 ⇒ 走迁移 ⇒ 写回新版号(幂等)。
发版前**用真实存档验一遍迁移**,别只在合成数据上跑(合成用例只能证明公式对,
证明不了"线上那几份老档真被迁移了")。
```bash
# 发版前:先备份,再验(脚本是只读的)
ssh 'cd /opt/menpai/web/backend && cp -r accounts saves /opt/menpai/_bak_$(date +%s)'
scp tools/verify_legacy_migration.py :/opt/menpai/_v.py
ssh 'cd /opt/menpai/web/backend && python3 /opt/menpai/_v.py; rm -f /opt/menpai/_v.py'
```
8. 🔴 **老档迁移的表在 `web/backend/gamenet/core/models.py` 的 `RealmSystem`** ——
老公式(`legacy_exp_needed`)要**固化在迁移器里**,不能跟着新公式一起改,
否则再也算不出老档的"当前级内进度"。
### 环境说明
本项目的画面在**软件渲染(SwiftShader)**下也要求可用,因此对性能与等待上限格外敏感:
无头环境下一切"等待上限"都必须比实测最慢值留足余量,否则会得到**假红**。
---
## 十二、文档索引
| 文档 | 内容 |
|---|---|
| `docs/参考-了不起的修仙模拟器-拆解与门派改造方案.md` | **门派改造总方案**(竞品拆解 / 八批计划 / 分系统设计) |
| `docs/game-design-decisions.md` | **决策台账**(D-001~D-028 全部拍板 · 批 1–18 验收与 2026-10-18 落库记录) |
| `docs/设计-AIGPart SSO 接入清单.md` | **AIGPart 单点登录接入**:江湖侧三处改动 + 老账号按 email 迁移 platformUid 方案(待拍板) |
| `docs/游戏设计总纲.md` | 玩法与数值的权威定义(含公式) |
| `docs/技术架构总纲.md` | 前后端分工与通信协议 |
| `docs/*-接口契约.md` | 门人 / 招募 / 日志等模块的接口约定 |
| `docs/生产部署-阿里云.md` | 生产环境部署与运维 |
| `docs/一体部署与运维.md` | 本地一键部署原理 |
| `docs/资产总表.md` · `资产台账.md` | 资产清单与成本台账 |
| `docs/3D资产生产体系-总体建设方案.md` | 资产生产线设计 |
| `docs/剧情美术规划.md` | **主线立绘 / 场景**的清单、规格、生产链路与"挑图边界" |
| `docs/项目全景-功能与模块总览.md` | 一页看懂全部功能与模块边界(新人/OX 交接用) |
| `docs/设计-走镖系统融入方案.md` | 走镖(押镖 / 期限 / 镖职 / 分号 / 商队)的设计与数值落点 |
| `docs/参考-运镖天下之四大镖局-设定整理.md` | 走镖系统的参考素材整理(非权威,仅背景) |
---
## 许可
- 本项目(代码与项目自产资产)以 **MIT License** 开源,完整条款见 [LICENSE](LICENSE)。
- `vendor/` 下的第三方素材保留其原始许可(如 KayKit Character Animations 为 CC0 1.0),
出处与许可声明见 [vendor/dl/CREDITS.md](vendor/dl/CREDITS.md)。