summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 16:04:56 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 16:04:56 +0800
commit18977f1147a3778b90caad48a53e760c79349175 (patch)
treec04a1a8261301edd3a88b851e1389e735fcfdcde
parentb0a7e1f23e1168725c62019824182ad3e24f45c5 (diff)
downloadliving-village-18977f1147a3778b90caad48a53e760c79349175.tar.gz
fix(desktop): RiverLantern 模式限定消除TW + 中文维护文档
Game.fs 733/741 两条编译警告(FS0049 未限定模式绑 + FS0026 不可达)由 Qualified VillageArt.RenderPropKind.RiverLantern 修复,全仓 0 警告;Leader 所指 Interaction.fs:191 并无编译警告(已记录于验资说明);docs/维护说明.md 首开:模块职责/O 不变量/信息 digest 变化处理/可复制命令。
-rw-r--r--docs/维护说明.md76
-rw-r--r--src/LivingVillage.Desktop/Game.fs2
2 files changed, 77 insertions, 1 deletions
diff --git a/docs/维护说明.md b/docs/维护说明.md
new file mode 100644
index 0000000..de5698a
--- /dev/null
+++ b/docs/维护说明.md
@@ -0,0 +1,76 @@
+# 维护说明
+
+结论先行:主分支保持 0 警告 0 错误 + Desktop 69/69、Kernel 50/50 全绿;性能基线
+final_digest 固定 953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C。
+本文记录模块职责、不变量与逐字可复制的验收命令。
+
+## 模块职责
+
+- `src/LivingVillage.Kernel/`:纯确定性模拟(需求/关系/谣言/年鉴/交易),无渲染依赖。
+ - `Sim.fs`:世界结构、`Sim.step` 主推进、对话/交易/谣言决策、地图边界
+ `configureBounds`(默认 64×48,可到 512×384)。
+ - `WorldSave.fs`:存档序列化。V2 头携带地图宽高,V1 旧档读入自动回退 64×48。
+ - `Personality.fs / Interaction.fs(fsm) / M6*.fs`:NPC 行为状态机与里程碑数据。
+- `src/LivingVillage.Desktop/`:MonoGame 渲染与交互层。
+ - `VillageArt.fs`:图集加载与世界/人物/室内绘制;CC0 覆盖层(LV_ASSET_PACK=cc0)。
+ - `Interaction.fs`:M5 视图与情境解析(FloatingPrompt / panelLines / 情境提示)。
+ - `M6Presentation.fs`:昼夜光照纯函数(lightingBlend)与渲染档(Blend/LanternGlow)。
+ - `TitleScreen.fs / Game.fs`:标题布局与主循环;LV Autos(sample / flow / record / map tour)。
+ - `ProceduralMap.fs`:512×384 确定性地形(splitmix 随机游走河道 + 三倍频值噪声 + 泊松布点)。
+ - `SampleScript.fs`:自动演示脚本(靠近→提示→交互→关闭→离开全流程)。
+- `src/LivingVillage.Headless/`:headless 入口 + PerformanceProbe(digest、基跑)。
+- `scripts/make-jiangnan-art.py / make-cc0-art.py`:原创与 CC0 素材确定性生成。
+- `third_party/`:Kenney Tiny Farm、Puny Characters(均 CC0 1.0,来源与 sha256 在 PROVENANCE.md)。
+
+## 模拟状态不变量(验收依据)
+
+1. **数组快照**:`Sim.world.Npcs` 数组与 `InteractionResolver.resolve` / `refreshPrompt` 的
+ 调用之间必须保持恒定——选择/提示路径从不 mutation(PrototypeTests 快照回归)。
+2. **确定性 RNG**:世界推进只随 `World.Rng`(splitmix)与 `(seed)` 派生,禁止墙钟与
+ `System.Random`。昼夜/波纹/入场/动画帧全是 `tick → 值` 纯函数。
+3. **存档 v1/v2**:`WorldSave` v2 头携带 `width height`,V1 读入回退默认。`worldDigest`
+ 对版本头与边界 token 不敏感(剥头后重构 V1 常量语义再 SHA256),摘要只绑定模拟状态。
+4. **地图 512×384 大世界**:`ProceduralMap.generate` 同 seed 两次生成逐字节一致;
+ 村核矩形(24..38 × 16..34)由生成器不变式保证可通行。
+
+## 性能基线命令
+
+```bash
+dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll --performance-baseline
+```
+
+- 口径:seed=42,npc=4,warmup=120,measure=6000,repetitions=3(见首行 `performance_config`)。
+- 门槛:`final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C` 三次一致 + `performance_determinism=PASS`。
+- digest 变化处理:先比对 `WorldSave.save world` 全文文本(版本头变化不构成失败即 digest 规范化后等价);
+ 再查 Sim.step 数值路径是否被改动(不允许)。历史归属见 `artifacts/perf-independent/*/README.md`。
+
+## 已验证命令
+
+```bash
+# 完整重建(0 警告 0 错误)
+dotnet clean LivingVillage.sln 2>&1 | tail -n1
+rm -rf src/*/bin src/*/obj
+dotnet restore LivingVillage.sln
+dotnet build LivingVillage.sln -c Release 2>&1 | tail -n3
+
+# 全量测试
+dotnet test src/LivingVillage.Desktop.Tests -c Release --no-build
+dotnet test src/LivingVillage.Kernel.Tests -c Release --no-build
+
+# 素材再生成(确定性,输出 byte-identical)
+python3 scripts/make-jiangnan-art.py && python3 scripts/make-cc0-art.py
+
+# Xvfb 录屏取证(沿用既有 pN harness 外参即可;harness 不入库)
+pkill -f "Xvfb :99"; Xvfb :99 -screen 0 1280x720x24 & sleep 2
+ffmpeg -y -f x11grab -video_size 1280x720 -framerate 10 -i :99 -pix_fmt yuv420p out.mp4
+LV_AUTOPLAY_SAMPLE=1 LV_AUTOPLAY_DAYLIGHT=1 LV_AUTOPLAY_RECORD=1 \
+ dotnet src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll
+```
+
+## 取证快照位置(近期)
+
+- `/tmp/lv-p2-ui/`、`/tmp/lv-p3-fixes/`(标题/键位/P5 提示与修)
+- `/tmp/lv-p6-art/`(昼夜光照/四帧行走/标题入场)
+- `/tmp/lv-p7-map/`(512×384 大世界巡游帧 + 夜灯对照)
+- `/tmp/lv-p8-cc0/`(CC0 同 seed 对照帧)
+- `artifacts/perf-independent/<TS>/`(digest 3 联测回读)
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index 62ba8f0..9fab5f7 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -730,7 +730,7 @@ type LivingVillageGame() as this =
let ringAlpha = int (18.0f * glow)
for prop in renderPlan.Props do
match prop.Kind with
- | RiverLantern ->
+ | VillageArt.RenderPropKind.RiverLantern ->
let position = VillageArt.worldTile renderPlan prop.Position
let screenX = position.X * Sim.tilePixels - int camera.X + Sim.tilePixels / 2
let screenY = position.Y * Sim.tilePixels - int camera.Y - Sim.tilePixels / 2