Yggdrasil 材质处理
AsterYggdrasil 现在有两套材质入口。
面向启动器和 authlib-injector 兼容工具的是 Yggdrasil 上传接口:
PUT /api/yggdrasil/api/user/profile/{uuid}/{skin|cape}
DELETE /api/yggdrasil/api/user/profile/{uuid}/{skin|cape}
GET /api/yggdrasil/textures/{hash}面向站点用户的是 wardrobe 和 profile 绑定接口:
GET /api/v1/wardrobe/textures
POST /api/v1/wardrobe/textures/{skin|cape}
PUT /api/v1/profiles/minecraft/{uuid}/textures/{skin|cape}
DELETE /api/v1/profiles/minecraft/{uuid}/textures/{skin|cape}Yggdrasil 协议接口返回协议格式,不使用项目 API envelope。/api/v1/... 站点和管理接口继续返回统一 envelope。
Wardrobe 和公共材质库
Wardrobe 是用户自己的材质库。上传到 wardrobe 的材质默认只属于当前用户,用户可以把其中一张绑定到自己的 Minecraft profile,也可以把材质设置为公开并提交到公共材质库。
公共材质库由运行时配置控制:
texture_library_enabled: 是否启用公共材质库。texture_library_review_required: 用户发布材质到公共材质库时是否必须经过管理员审核。
用户侧常用流程:
- 上传 skin/cape 到 wardrobe。
- 把材质 visibility 改为
public。 - 提交公共材质库。
- 如果需要审核,等待管理员通过;如果不需要审核,提交后直接成为
published。 - 其他登录用户可以从公共材质库复制该材质到自己的 wardrobe。
公共材质库只展示已经发布的公开材质。私有材质、待审核材质、被打回材质和已下架材质不会出现在公共列表或公共详情里。
公共材质库相关站点 API:
GET /api/v1/texture-library/tags
GET /api/v1/texture-library/textures
GET /api/v1/texture-library/textures/{texture_id}
POST /api/v1/texture-library/textures/{texture_id}/copy
POST /api/v1/texture-library/textures/{texture_id}/reports
POST /api/v1/wardrobe/textures/{texture_id}/library-submission
DELETE /api/v1/wardrobe/textures/{texture_id}/library-submission复制公共材质需要登录。复制后会在当前用户 wardrobe 中创建或复用一条材质记录,默认保持私有,不会自动再次发布。
审核、下架和举报
材质的公共库状态由 library_status 表示。当前主要状态包括:
private: 不在公共材质库中。pending_review: 已提交,等待管理员审核。published: 已发布到公共材质库。rejected: 管理员打回。
管理员可以在材质库后台处理提交:
GET /api/v1/admin/texture-library/textures
GET /api/v1/admin/texture-library/textures/{texture_id}
POST /api/v1/admin/texture-library/textures/{texture_id}/approve
POST /api/v1/admin/texture-library/textures/{texture_id}/reject
POST /api/v1/admin/texture-library/textures/{texture_id}/unpublish用户可以举报已经发布的公共材质。举报必须登录,不能举报自己的材质,不能举报私有、待审核、打回或已下架材质。同一用户对同一材质只能保留一条 pending 举报。
举报有独立状态,不和材质的 library_status 混用:
pending: 等待管理员处理。accepted: 举报成立。rejected: 举报被驳回。
管理员可以在举报队列中成立或驳回举报:
GET /api/v1/admin/texture-library/reports
GET /api/v1/admin/texture-library/reports/{report_id}
POST /api/v1/admin/texture-library/reports/{report_id}/accept
POST /api/v1/admin/texture-library/reports/{report_id}/reject举报成立会把对应材质从公共材质库下架,并把处理备注写入材质的审核意见。材质所有者在 wardrobe 里可以看到下架状态和处理说明。
如果管理员没有从举报队列处理,而是直接从材质管理页下架一个已发布材质,系统也会把该材质现有 pending 举报标记为 accepted,并复用下架备注作为举报处理备注。这样举报队列不会残留已经被直接处理的 pending 记录。
上传校验
上传文件必须是 image/png。服务端会先读取 PNG header 获取尺寸,再解码图像,避免在尺寸校验前把潜在 PNG bomb 完整读入内存。
允许的尺寸:
- skin:
64x32的整数倍,或64x64的整数倍。 - cape:
64x32的整数倍,或22x17的整数倍。
不符合尺寸的文件会返回 Yggdrasil 协议错误,错误类型为 IllegalArgumentException。
Cape 22x17 兼容
authlib-injector 文档允许旧式 cape 使用 22x17 的整数倍,但它不是标准 cape 画布。AsterYggdrasil 在上传处理阶段会把这种 cape 补透明到对应的标准 64x32 画布:
22x17会保存为64x32。44x34会保存为128x64。- 原图像素从左上角开始保留。
- 新增区域填充完全透明像素。
这个归一化发生在存储前,所以后续 metadata、公开读取、hash、引用计数和孤儿清理都基于处理后的 PNG。客户端拿到的 URL 永远指向已经重编码后的安全 PNG。
重编码和 Hash
服务端会把上传 PNG 解码为 RGBA,再重新编码为 PNG。这样可以去掉与位图无关的 PNG 元数据,避免客户端分发用户上传文件中的额外数据。
材质 hash 使用处理后 PNG 文件内容计算,并作为公开 URL 的最后一段:
/api/yggdrasil/textures/{sha256}Minecraft/authlib-injector 客户端会把 URL 文件名当作材质标识缓存,所以同一张处理后图片会稳定命中同一个 hash。
Public URL 和 skinDomains
textures property 中的 URL 必须是客户端可访问的绝对 URL。普通部署只需要配置 public_site_url,服务端会派生出 {public_site_url}/api/yggdrasil/textures/{hash}。如果配置了高级覆盖项 yggdrasil_public_base_url,服务端会优先使用第一个可用的 http/https URL。公开读对象存储/CDN 可以额外配置 yggdrasil_texture_public_base_url,让已上传材质使用 {base}/{storage_key};默认皮肤仍走 Yggdrasil API。
配置 yggdrasil_texture_public_base_url 后,管理前端和用户前端的材质预览也会直接加载该对象存储/CDN URL。桶或 CDN 必须允许对外服务站点来源的匿名 GET/HEAD CORS 读取。上传不需要浏览器 CORS,AsterYggdrasil 始终由服务端 streaming 上传。
authlib-injector 会校验材质 URL 的域名是否在 metadata 的 skinDomains 中。metadata 会自动包含 Mojang 官方域名 .minecraft.net、.mojang.com,以及当前有效 texture URL 的 host。yggdrasil_skin_domains 只用于额外允许 CDN 或外部材质域名。
存储和维护
当前支持 local、S3 和 MinIO object storage,并通过 object_storage 静态配置选择 backend。上传成功后只保存处理后的 PNG,不保存原始上传文件。
运行时维护任务包括:
yggdrasil-texture-cleanup: 删除没有数据库引用的材质对象。yggdrasil-storage-consistency-check: 检查数据库记录指向的对象是否缺失,以及 object storage key 是否和数据库 hash 一致。yggdrasil-token-cleanup: 清理过期或已吊销的 Yggdrasil token。
删除材质会先删除数据库引用,再按引用计数清理对象。多个 profile 引用同一 hash 时,不会误删仍被引用的对象。