日志补记下半场:三件套的落地细节、行宽为何定 100、PTH 为何不开、pyright 两个关键开关;新增三条可复用环境坑(write_text 会把 LF 变 CRLF、同文件多次 Edit 互相覆盖、git status 与 git diff 不一致的判别)。 项目长期约定同步更新:运行方式改为 uv 单一可信来源,新增代码风格约定与质量门禁命令。
4.5 KiB
4.5 KiB
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