add # 新增对资源文件的维护

This commit is contained in:
2026-10-26 18:54:19 +08:00
parent 72e54c3573
commit b41db58ea5
790 changed files with 14534 additions and 2 deletions
+114
View File
@@ -0,0 +1,114 @@
# 作物目录独立维护与发布
`catalogue/source/` 是日常维护的正式来源,不再读取 Farm 仓库或旧作物库。
首次迁出保留 202 条作物的全部业务字段、768 张图片的原始字节及现有布局。
原 `assets.zip` 和小型 `land-soils.zip` 字节不变,旧迁移基线保持原样。
## 源目录
- `plants.json`:根对象仅含 `schema: 1` 和 `plants` 数组。
- `metadata.json`:维护画布、布局、定位、panel 等绘图配置;发布工具重建
`plants` 和 `images`,不要手工修改这两个生成字段。
- `images/<原 ZIP 路径>`:PNG/JPEG 等原始图片,每个文件对应同名 ZIP 成员。
- `land-soils.zip`:精确包含 `soil/红土地.png`、`soil/黑土地.png`、
`soil/金土地.png` 三张可解码图片。
作物 `id` 为永久编号,不能重排、复用、删除或更名。名称唯一且不超过
128 字符,不能为 `.`、`..` 或包含 `/`、`\`。数值仅允许 64 位有符号
范围内非负整数,`id`、`time`、`crop`、
`harvest` 必须为正;`time` 为小时,转换为秒仍须在范围内。
`level/buy/vipBuy/experience/price` 分别控制等级、种子普通币价格、点券价格、
经验和收获物售价。`isVip`、`isBuy` 和可选 `enabled` 仅允许 0 或 1;
可选 `crop_type` 默认为 1,只有类型 1 可新种植。
`phase` 为逗号分隔的正整数秒值,按首次出现去重后须严格递增且至少两项;
它与 `general/offsetH/offsetW/offsetX/offsetY` 一并生成展示元数据。
其他已有字段原样保留。图片原有缺图占位行为保留,但不能删除已经发布的
图片路径;新增作物应同时补充所需阶段图片。
发布工具独立校验完整展示契约:30 个地块位置、schema 4 作物放置、
面板字段及贴图、虫动画、六主题装饰、画布与图片尺寸,以及所有必需
背景和通用图片引用。schema 不接受布尔值,坐标不接受布尔值或非有限数。
## 编辑、检查、生成
需要 Python 3.10+ 和 Pillow;发布脚本不安装依赖、不访问网络或玩家数据库。
从本仓库根目录运行:
```text
python scripts/prepare_catalogue.py --source catalogue/source --output catalogue --version catalogue-20261026-1 --check
python -m unittest discover -s tests -p test_prepare_catalogue.py
```
`--check` 只读核对源与当前最新发布版本,源已修改时会拒绝,且不会生成
目录或替换清单。变更后先运行测试,再显式生成:
```text
python scripts/prepare_catalogue.py --source catalogue/source --output catalogue --version catalogue-20261027-1
python scripts/prepare_catalogue.py --source catalogue/source --output catalogue --version catalogue-20261027-1 --check
```
例如把已有条目的 `price` 改为新售价,或新增从未使用过的 ID;不要把
旧 ID 改成另一个作物。`isBuy=0` 表示禁售,`enabled=0` 禁止新购买和新播种。
生成时对照当前已校验版本,拒绝删除/更名旧 ID 或丢失已有图片路径。
产物位于 `catalogue/releases/<release>/`,包含清单和精确四个文件:
`plants.json`、`metadata.json`、`assets.zip`、`land-soils.zip`。
生成完整目录后,最后原子替换 `catalogue/manifest.json` 发现入口;失败
不会切换最新版本。已存在版本必须逐字节相同,不能原地覆盖。相同内容
重复生成须沿用相同版本标签;仅更换标签不能覆盖相同内容的历史清单。
已经发布的标签也不能用于内容不同的新版本,修改内容时必须使用新标签。
发布操作按输出目录同时使用进程内线程锁和系统文件锁,锁覆盖读取最新
版本、兼容性检查与最终替换,防止并发旧发布删除刚新增的 ID。系统锁在
进程退出时自动释放;`.publish.lock` 是应保留的空状态锁文件,不应删除,
仓库已忽略它。`--check` 保持只读,不创建或修改锁文件。
ZIP 使用固定成员顺序、2020-01-01 时间、Unix 0644 文件权限和 STORED 格式,
保留图片原始字节。输出不依赖文件修改时间或 Farm 相邻工作区。
临时 `.prepare-*` 目录不是有效发布版本,可在确认没有发布进程后清理。
## 清单协议与 HTTP
清单严格包含 `schema/version/release/files`:schema 固定 1;version 为
1~64 个字母数字或 `._-`;files 精确对应四文件,每项仅含正整数
`size_bytes` 和 64 位小写 `sha256`。release 为 files 对象经键排序、紧凑
UTF-8 JSON 序列化后的 SHA256,不接受 URL 或自定义路径。
清单最大 16 KiB;plants 2 MiB;metadata 4 MiB;各 ZIP 64 MiB;两个 ZIP
解压后图片总量不超过 256 MiB。源发布工具校验业务数据与图片,Rust 服务
启动校验各历史清单关系、文件大小和摘要;客户端也校验完整版本。
这些摘要检测不一致和损坏,不构成发布账号被接管时的真实性保证。
设置 `FARM_CATALOGUE_DIR=./catalogue` 后,服务启动加载最新清单及所有
历史版本为不可变内存快照:
| 接口 | 行为 |
|---|---|
| `GET /v1/catalogue/manifest.json` | 最新清单,`no-cache`,支持 ETag |
| `GET /v1/catalogue/releases/{release}/{file}` | 精确白名单四文件和该版本 `manifest.json`;一年 immutable 缓存,支持 ETag |
接口公开只读,不需要用户资料接口令牌。未配置目录时返回 503;未知版本
和文件返回 404;无效目录或摘要导致启动失败,并写入既有日志。
运行中不重读磁盘,发布后须重启 Service。旧素材/字体接口及用户资料鉴权
保持独立。所有历史版本都会驻留内存,部署前须按版本数量规划内存。
GitHub/Gitea 同样从本仓库 raw 根路径的 `catalogue/manifest.json` 和
`catalogue/releases/<release>/<file>` 获取相同字节。应先发布完整版本目录,
最后发布发现入口,保留历史版本;本工具仅准备本地文件,不执行提交、
推送或部署。真实远端下载与生产重启需在发布后另行验证。
## 重启生效与兼容回滚
机器人仅在启动时检查一次更新,同一次运行使用一个完整版本,失败优先
回退已校验缓存,再回退插件初始版本。若选定版本无法覆盖玩家已持有或
已种植的 ID,应拒绝业务初始化,恢复兼容版本后再启动,不能清空玩家数据。
新播种使用本次启动目录;已经播种的地块继续使用原结算快照。出售使用
当前目录售价,调价也会影响旧库存。回滚应从当前源发布一个新兼容版本:
恢复旧数值,但保留期间新增的全部 ID、名称及图片,需要下架时使用禁售/
禁播字段。不能把最新清单直接退回缺少新 ID 的老版本。
Farm 随包的轻量 bootstrap 由显式同步工具从本地完整 release 刷新,日常
Service 发布不要求同时改 Farm;三个机器人分发包继续不捆绑大型素材 ZIP。
字体保持独立发布,不纳入四文件目录版本。
+126
View File
@@ -0,0 +1,126 @@
# 作物数据与素材独立维护设计
日期:2026-10-26。状态:用户已确认,实施中;本地完成后记录验证证据。
## 目标与已确认决定
- 作物数值及可维护素材的正式来源迁入 `farm_service`,不再从农场插件的
`legacy` 基线生成日常发布数据。
- 用户已明确选择:机器人仅在启动时检查更新。运行中不定时更新,
本次启动选定的版本持续使用到关闭。
- 真寻、NoneBot、AstrBot 共用同一加载实现,沿用已有 Github、Gitea、
farm_service 资源源选择与独立的服务基础地址配置。
- 保留玩家数据库、地块中保存的作物结算快照及旧迁移基线。
- 本轮只完成本地实现、验证和维护说明;提交、推送、部署及安装另行授权。
## 方案取舍
1. 启动时检查完整资源版本:用户最终选择。可以独立发布作物更新,
同一次机器人运行期间不切换规则,生命周期与缓存管理较简单。
2. 运行中定时更新:用户已改为不采用。需要额外处理交易和渲染任务
跨版本切换、任务取消与长期更新调度。
3. 只迁移维护源、插件固定版本:不采用。更新数值仍需重新发插件。
## 正式来源与发布产物
- Service 仓库保存可编辑的作物目录、图片源和绘图配置。旧目录中的
202 条作物及现有图片仅作为首次迁出的来源;迁出后不修改旧基线。
- 作物保留有业务意义的字段;移除将旧数据库摘要当成日常维护来源
的依赖。新增数据协议说明、字段校验和维护示例。
- 发布工具由上述来源可复现生成 `plants.json`、`metadata.json` 和
`assets.zip`、`land-soils.zip`,以及绑定四者的发布清单。字体继续单独维护。
- 清单包含协议版本、发布版本和各文件的大小、SHA256;下载路径固定,
不接受玩家输入路径。每个发布版本存于不可变版本目录,最新清单
仅作为版本发现入口,保留历史版本供旧客户端和回滚使用。
- GitHub/Gitea 从 Service 仓库读取公开文件;Service 提供等价的公开
只读清单及版本文件接口,不复用用户资料接口令牌。
- 发布工具先生成并校验完整版本,最后才更新最新版本指针。
发布后的版本目录不得原地覆盖;修正内容应生成新版本。
## 插件启动与失败处理
1. 日志初始化后、玩家数据库迁移和业务服务初始化前,读取本地当前版本。
2. 在线检查所选资源源的最新清单,限制大小、超时及 HTTPS 地址格式;
不自动跨源下载,不请求占位 Service 地址。
3. 若版本未变化,复核并复用本地内容;否则下载缺少文件至独立临时目录。
4. 校验协议、大小、SHA256、作物业务字段、唯一编号及名称、展示配置
与目录对应关系、ZIP 成员集合和图片内容。任何一项失败都不切换版本。
5. 校验全部通过后,以不可变目录保存候选版本,原子更新本地当前版本
指针。业务目录、绘图元数据和图片始终取自同一完整版本。
6. 在线检查或下载失败时使用当前已验证缓存;缓存缺失或损坏时使用
随插件生成的初始作物目录与匹配展示配置。初始版本图片仍按已有
外部资源策略加载;完全离线且没有图片缓存时提供文字功能。
7. 本次运行不再次切换目录,不创建更新定时任务。网络操作在后台线程
执行;超时、拒绝原因、采用版本和回退来源写入日志,不记录凭据。
启动前须确认选定目录覆盖现有玩家库使用的作物 ID。若缓存损坏且
初始目录不包含玩家已持有的新作物,应明确拒绝业务初始化并提示恢复
兼容版本;不得清除库存、替换 ID 或伪装成正常启动。空数据目录仍可
通过初始目录离线启动。读取玩家库仅用于兼容性检查,不导出真实数据。
最新清单通过已有发布方 HTTPS 地址获取,摘要用于一致性和损坏检测,
不宣称其可防御发布账号或源站被接管。未知协议版本必须拒绝并回退。
## 存量兼容与字段约束
- 作物 ID 永不重排、复用或更换含义。新增作物使用从未使用过的新 ID。
- 本轮不支持删除或更名已有作物;发布工具对照前一版本拒绝这类操作。
下架用 `isBuy=0`,禁止新购买和新播种用 `enabled=0`,保留旧库存结算。
- 已播种地块继续使用数据库中的不可变作物定义,更新不重写成熟时间、
产量、经验、季数和再生长规则;新播种使用本次启动加载的目录。
- 出售使用当前目录价格,包含更新前已存在的库存。这沿用现有行为,
必须在维护说明中提示调价影响。
- 新版本应保留已有作物的展示资源引用及存量绘制所需阶段图片;
原有缺图占位行为保留,不要求首次迁移凭空补齐历史缺图。
- 回滚采用“发布新的兼容版本恢复旧数值”,保留期间新增的全部 ID
及其图片,必要时禁售或禁播。不能将最新指针直接退回缺少新增作物
的历史版本。客户端对照本地已验证版本拒绝删除和更名,避免旧库存
无法查询或出售。这是实现者根据稳定 ID 约束提出的回滚方案。
- 不执行玩家数据清理、重置、隐式批量迁移或远程管理写入。
## 代码和 CI 调整
- Service 实现发布文件校验、公开只读分发与可重复构建工具;提供独立
源目录和更新步骤,日常维护不要求旁边存在 Farm 开发仓库。
- Farm 核心继续只处理已校验的离线目录,不增加网络或 Pillow 依赖。
- 共享适配层负责启动下载、完整版本选择和故障回退;渲染器支持传入
同版本元数据,避免继续强制读取插件内固定 `metadata.json`。
- Farm 保留一套可复现的初始数据产物,提供显式刷新初始版本的工具;
CI 校验该版本自身一致性,不访问生产最新数据,不要求相邻 Service
仓库,也不再要求日常目录与旧 `plant.db` 字节导出一致。
- 旧基线完整性与迁移测试继续独立运行;不能通过刷新基线哈希来放行
新作物。三个框架打包包含共享加载模块及必要初始数据,继续不捆绑
大型 `assets.zip` 或字体 ZIP。
## 验收场景
1. 首次迁出作物数值、图片与现有版本一致,旧基线保持原样。
2. 在 Service 正式来源新增作物或调整数值,发布工具生成有效新版本;
不修改 Farm 业务源码或旧基线,重启测试运行实例即可加载。
3. 一次启动只采用一个完整版本;文件混用、摘要错误、非法字段、未知
协议、删除/复用旧 ID 等情况被拒绝,当前缓存不受损。
4. 网络超时、404、资源源不可用时优先复用已验证缓存;无缓存时初始
目录仍能提供文字业务,图片不可用有明确日志。
5. 旧种植快照结算不变,新播种使用新规则,旧库存按当前价格出售。
更新后新增作物已进入库存时,缺少该 ID 的回滚版本会被拒绝;
兼容回滚保留该 ID。缓存丢失且初始目录不兼容时拒绝业务初始化。
6. Service 与 GitHub/Gitea 使用同样的发布内容;历史版本可读取,未知
路径和版本拒绝,用户资料鉴权及字体分发行为保持有效。
7. 三框架包与离线 CI 通过,不依赖真实远端或相邻开发仓库;提供
“编辑—检查—生成—发布—重启生效—回滚”的维护说明。
## 实施与验证边界
使用 Python 标准库为主的共享下载与缓存,沿用 Rust/Axum 服务结构。
开发测试使用临时目录和人工测试数据,不访问真实玩家库或启动机器人。
覆盖发布生成、HTTP 路由、异常缓存、并发写入、规则快照及三端打包;
Rust 执行格式、测试和静态检查,Python 执行针对性与最终离线检查。
真实 GitHub/Gitea 发布、生产下载、Service 部署及宿主消息验证不作为
本地通过的结论,单独列出尚未执行事项。
## 实施决定
- 用户已确认本设计作为实施与验收依据,采用当前工作区,不提交、合并或推送。
- 实施前重新查询 Comet,返回无可恢复的 active change,未强建额外工作流。
- Service 已迁出独立源并生成初始四文件版本,提供启动校验和公开只读分发;
维护步骤见 `catalogue.md`。跨端最终验证由实施计划统一记录。