Files
SimpleFarm/.workbuddy/memory/MEMORY.md
T
snowy-yang 52004571c8 docs: 记录工程化改造(uv + ruff + pyright)的决策与踩过的坑
日志补记下半场:三件套的落地细节、行宽为何定 100、PTH 为何不开、pyright 两个关键开关;新增三条可复用环境坑(write_text 会把 LF 变 CRLF、同文件多次 Edit 互相覆盖、git status 与 git diff 不一致的判别)。

项目长期约定同步更新:运行方式改为 uv 单一可信来源,新增代码风格约定与质量门禁命令。
2026-09-18 21:28:20 +08:00

4.5 KiB
Raw Blame History

SimpleFarm 项目长期约定

是什么

终端农场经营游戏 + 小型多人游戏服务器(v2.0)。游戏跑在服务端进程里, TUI 是纯客户端,同时开放 HTTP + WebSocket 接口给 AI/脚本调用(服务端不内置 AI)。

运行方式(v2.0 起由 uv 统一管理)

  • 单一可信来源是 pyproject.toml,.venv/ 由 uv 创建;uv.lock 要提交(可复现构建依据)。 加/删依赖走 uv add / uv remove,不要手工编辑依赖表,也不要再列 pip install。
  • 所有命令带 uv run 前缀:uv run main.py serve --daemon,网页 http://127.0.0.1:8765/,文档 /docs
  • 玩:uv run main.py play --farm 名字;单机:uv run main.py solo
  • AI/脚本入口:main.py actions | do <动作> '<JSON>' | batch | state --json | watch | bot
  • 测试:uv run python -m unittest discover -s tests(97 项)
  • 质量门禁(改动后都应跑一遍): uv run ruff check . / uv run ruff format . / uv run pyright
  • 不用 uv 的人:requirements.txt 是 uv export 的产物,只读、不要手工改。 README 与 main.py 顶部保留了一段"没装 uv?把 uv run 去掉即可"的兜底说明,别删。

架构约定(改动时必须遵守)

  • 分层:farm.engine 只管规则,不认识网络/终端;farm.tui 不认识 socket; farm/server/* 不认识终端。新增玩法动作只改 farm/server/actions.py 一张表。
  • 接口同构:farm.client.RemoteFarm 必须与 FarmEngine 同名同语义, 这样 tui.py 不需要区分本地/远程。加动作时两边都要补。
  • 多牧场:一个 farm = 一个玩家 = 一份 saves/<名字>.json + 一个引擎 + 独立时钟; 时钟由一个 FarmClock 线程统一驱动,不要给每个牧场开线程。
  • 存档类动作:save / reset 必须由会话(FarmSession)处理, 不能直接落到 engine.save()(会写默认路径、覆盖别人的档)。
  • 实时通道:WebSocket(/ws)是唯一实时通道,推送节流 250ms,消息带自增 seq。 写操作优先走 WS(带 id 回执与 snapshot),只有 ws_closed/timeout/network 才退回 HTTP 重试;业务错误必须原样抛出,不能重试。

环境禁忌(本机特有)

  • Bash 的 coreutils 残缺(ls/head/tail/grep 缺失),不要用管道,会整条命令失败; 查看/删除文件一律写 Python 脚本。
  • 环境设了 HTTP_PROXY 但没有 no_proxy:任何本机 HTTP 访问都要走 farm/net.py 的 open_url()(回环直连),否则会拿到 502 并误判为"服务器没起来"。
  • Windows 下守护进程的 pid 必须由服务进程自己写(见 farm/server/__main__.py), 父进程的 Popen().pid 不可信。
  • 写文件必须指定行尾:Windows 上 pathlib.write_text() 会把 \n 转成 \r\n, 破坏本仓库"统一 LF"的约定(.gitattributes 已声明 eol=lf)。 用脚本改文件时一律 read_bytes()/write_bytes(),或 write_text(..., newline="\n")。
  • 同一文件在一条消息里多次 Edit 会被静默覆盖(只落地一部分,随后报出"幽灵"错误)。 批量改同一文件时改用幂等 Python 补丁脚本:整文件读入 → 精确匹配替换 → 逐条打印 ALREADY/APPLIED/MISS,跑完即删。判断"已应用"要 if new in text, 不要比对两行文本(会出现 new 是 old 子串的假阳性)。
  • git status 报 M 但 git diff 为空,是索引 stat 缓存过期,不是真差异; git add -A(内容相同时等于无操作)即恢复干净。

代码风格约定(ruff / pyright 已固化在 pyproject.toml)

  • 行宽 100;字符串统一双引号;isort 把 farm 视为第一方。
  • 类型标注用 PEP 585/604 写法(dict[str, int] / X | None),不要 Dict / Optional。
  • 吞异常要显式:contextlib.suppress(...) 优先于 try/except/pass。
  • 异常链:转换后重抛用 raise ... from exc;有意隐藏原始上下文用 from None(不要省略,会触发 B904)。
  • 路径保持 str(不要为了 lint 而转 Path);farm/__init__.py 的惰性 __all__ 导出是刻意设计。
  • 改完代码的标准动作:uv run ruff format . && uv run ruff check . && uv run pyright && uv run python -m unittest discover -s tests

相关文档

  • 项目入口文档:README.md(接口表、WS 协议、边界处理、设计取舍全在里面)
  • 当天工作记录:.workbuddy/memory/2026-09-18.md