UGC1.0 地图数据与发布:从快照到审核上线
这篇回答 UGC 平台最基础也最容易被低估的问题:一张正在编辑的地图,怎样变成可下载、可审核、可回滚、可运行的线上版本。
面试官:你在 UGC1.0 的地图数据链路做了什么?
30 秒回答
我早期直接负责过地图模型、封面图、上传下载和发布链路,后续又做了多文件地图数据的压缩、加密、上传下载以及地图大小治理。我的理解是,地图保存不是写一个文件,发布也不是再调一次上传接口。它们是一条版本化事务:先冻结编辑快照,校验 Schema 和依赖,再按清单序列化、压缩加密、用 MD5 避免重复上传,最后提交审核并生成线上版本。失败只能影响本次任务,不能覆盖上一个成功版本。
90 秒回答
一张 UGC 地图至少包含场景对象、逻辑、层级数据、资源引用、封面和发布说明。早期单文件容易实现,但地图变大后,任何小改动都可能重新序列化和上传全部内容,也不利于定位坏档。因此系统逐步转向多文件交付:根据明确文件清单收集数据,保持归档路径,异步压缩并加密,上传时用内容摘要识别未变化文件;下载后再校验、解密、解压和加载。
发布侧还要串起试玩或预检、封面采集、文本与图片审核、资源权限、上传和发布状态。这里最重要的设计是“作者源、发布快照、线上制品”三者分离:创作者可以继续编辑,但本次发布始终面向冻结输入;审核中和已上线版本也有独立身份。我的直接工作集中在客户端地图与交付链路,完整审核服务和全部资源平台是协作上下游。
面试官:地图数据应该怎样分层?
直接回答
我会分成四类,而不是把所有内容塞进一个大对象:
| 数据 | 例子 | 关键要求 |
|---|---|---|
| 结构数据 | 对象身份、父子关系、类型、属性 | 稳定 Schema、确定性序列化、版本迁移 |
| 逻辑数据 | 元件连接、脚本、变量、任务规则 | 引用完整、运行域明确、安全校验 |
| 资源数据 | 图片、模型、音频、材质、预制体 | 稳定资源 ID、版本、权限、平台变体 |
| 发布元数据 | 标题、说明、封面、玩法标签、版本 | 审核状态和线上版本可追溯 |
地图正文只保存稳定引用,不保存开发机本地路径。资源文件和结构数据可以拆分交付,但必须由同一 manifest 描述它们属于哪个地图快照。
面试官:为什么要做多文件地图?
单文件的优点是原子性直观,但随着地图增大,会出现四个问题:保存与上传放大、局部损坏难定位、资源无法独立缓存、多人或分层编辑难拆分。多文件可以按场景层、逻辑域或资源类别组织,使未变化内容复用已有版本。
代价是完整性问题从“一个文件是否存在”升级为“整组文件是否属于同一个版本”。因此必须增加:
- manifest:文件逻辑名、归档路径、大小、摘要、版本和可选性。
- 快照 ID:阻止混用不同保存批次的文件。
- 原子提交:只有必需文件全部可用,快照才可见。
- 缺失诊断:明确缺哪个文件、哪个依赖、属于哪个阶段。
- 兼容规则:旧版本未知字段、缺省字段和逐级迁移策略。
面试官:多文件是怎样打包和上传的?
项目中我做过按文件列表和目录规则异步压缩,并把压缩与加密串起来。抽象后的链路是:
flowchart LR
SNAP["冻结地图快照"]
LIST["生成文件与目录清单<br/>逻辑路径 / 摘要 / 版本"]
ZIP["异步压缩指定文件<br/>汇报文件级进度"]
ENC["加密临时归档"]
DIFF["与云端摘要比较<br/>筛出变化文件"]
UP["上传制品"]
META["提交地图版本与 Manifest"]
SNAP --> LIST --> ZIP --> ENC --> DIFF --> UP --> META归档时必须区分源文件路径和 archive path。前者只在本机有效,后者决定下载解压后的逻辑布局。上传成功后再提交版本元数据,避免服务先看到一个引用不完整的地图。
面试官:MD5 去重是不是就能保证数据正确?
不能。摘要在这里主要用于内容寻址、变化检测和传输校验,不等同于业务完整性,也不替代安全签名。
正确流程是先在冻结快照上计算摘要,用它判断内容是否变化;上传后校验服务端记录或返回;manifest 还要校验文件数量、必需项、Schema 和资源引用。即使每个文件摘要都正确,也可能把不同快照的文件拼到一起,所以还需要版本或快照身份。
面试官:为什么压缩和加密要用临时文件?
因为最终路径应只出现完整产物。先写临时文件,压缩成功后再加密到目标,全部成功后原子替换或提交;任一步失败都删除临时文件。若直接覆盖正式文件,中途崩溃可能留下“路径存在但内容不完整”的假成功。
异步对象还要持有到回调完成。取消或页面退出时不能立即释放后台仍在使用的压缩器,也不能让旧任务回调更新新任务进度。
面试官:快速连续保存为什么容易失败?
如果两个保存任务同时读取可变编辑态,它们可能共用临时目录、覆盖进度、交叉更新 MD5 或让较早任务最后提交,导致线上版本倒退。修复不能只靠禁用按钮,而应在领域层串行化同一地图的提交。
一种稳妥设计是:
编辑操作持续写入 Working Copy
-> Save 请求生成 Snapshot N
-> Snapshot N 进入单地图任务队列
-> 后续 Save 若内容相同则合并;不同则生成 Snapshot N+1
-> 只有任务拥有提交令牌时才能更新远端版本指针页面按钮可以防误触,但任务队列、快照身份和提交序号才是正确性保证。
面试官:保存、自动保存和发布有什么区别?
| 操作 | 目标 | 是否必须审核 | 失败影响 |
|---|---|---|---|
| 撤销/重做 | 回退一次编辑意图 | 否 | 当前编辑事务 |
| 自动保存 | 防止本地或会话数据丢失 | 否 | 最近恢复点 |
| 云端草稿保存 | 跨设备继续编辑 | 通常不进入公开审核 | 草稿版本 |
| 发布 | 生成可分发、可运行版本 | 是 | 本次发布任务,旧线上版保持不变 |
它们可以共享 Schema、脏标记和序列化能力,但不能共用一个粗糙的“保存成功”布尔值。
面试官:完整发布状态机怎么画?
Editing
-> FreezeSnapshot
-> Validate
-> CaptureCover
-> SerializeAndPackage
-> UploadArtifacts
-> SubmitReview
-> WaitingReview
-> PublishVersion
-> Published
任一步 -> Failed / Cancelled
-> 保留上一线上版本
-> 记录可重试阶段与错误预检应该尽量放在昂贵操作之前。Schema 错误、资源缺失、超过硬上限和必崩问题直接阻断;性能建议或非关键质量问题可以警告。上传成功但提交审核失败时,已上传的不可变制品可以在有效期内复用,不必每次全部重传。
面试官:发布前为什么要进入试玩或拍照状态?
封面需要反映运行时可见结果,而不是编辑器中的 Gizmo、选中框和隐藏规则。项目早期做过发布前截图、相机移动、运行状态切换以及 Android/iOS 图片方向和黑图问题治理。
正确做法是把拍照建模成独立状态:保存当前编辑上下文,切换到受控预览状态,冻结编辑输入,等待必要资源和渲染稳定,再采集图片,最后无论成功失败都恢复相机、对象可见性和输入。不能用若干 UI 显隐临时拼出拍照模式。
面试官:文本、图片、模型和脚本的安全审核一样吗?
不一样,它们的成本和风险不同:
| 内容 | 客户端前置检查 | 平台审核重点 | 运行时防线 |
|---|---|---|---|
| 文本 | 长度、编码、空值、明显非法字符 | 敏感语义与上下文 | 展示转义、举报和下架 |
| 图片 | 格式、尺寸、透明度、文件大小 | 图像内容安全 | 审核状态、替换和缓存失效 |
| 模型/资源 | 格式、依赖、数量、容量 | 版权、恶意资产和质量 | 资源预算、白名单、隔离加载 |
| 脚本 | 语法、API、依赖 | 权限和恶意行为策略 | 沙箱、配额、服务端校验、异常隔离 |
客户端检查的价值是尽早反馈和节省上传成本,但客户端不可信,不能承担最终安全裁决。审核结果也不是一个永久布尔值,而应绑定资源版本和策略版本。
面试官:云端资源怎么管理?
地图应保存 资源 ID + 版本或版本约束 + 用途,资源平台管理文件清单、审核态、权限和平台变体。下载缓存键至少包含资源身份、版本和平台,不能只用文件名。
发布地图前需要遍历依赖图:资源是否存在、当前作者是否有权限、审核是否通过、目标平台产物是否齐全、引用是否形成循环。资源删除也必须查询引用者,选择阻断删除、替换引用或固定旧版本,而不是普通 CRUD。
面试官:下载和加载怎样保证不产生坏地图?
下载先进入临时目录,按 manifest 拉取和校验全部必需文件,再解密解压到新版本目录。加载前验证 Schema 与资源引用,成功后原子切换“当前版本”指针。旧版本在新版本验证完成前不能删除,这样失败时仍可回退。
对部分可选资源可以降级,但结构文件或关键逻辑缺失必须失败。错误信息要包含地图版本、文件逻辑名、阶段和原因,不能只有“加载失败”。
面试官:怎样做版本兼容?
读取时先识别 Schema 版本,再按 v1 -> v2 -> v3 逐级迁移到当前内存模型。每步迁移必须幂等、有测试样例,并保留未知字段或给出明确丢弃策略。写出时只由当前版本序列化器产生确定结果。
批量治理旧地图时先用离线解析工具扫描,不直接在用户首次打开时赌一次迁移。遇到无法迁移的数据,可以只读打开、提示修复或保留旧运行时,而不是写回一个半迁移文件。
面试官:你怎样验证这条链?
数据测试
- 当前版本往返序列化结果一致,输出顺序确定。
- 每个历史 Schema 都有迁移样例,重复迁移不改变结果。
- 缺文件、坏摘要、错误密钥、截断包和未知字段有明确诊断。
- 多次保存、取消、崩溃恢复和任务乱序不会倒退版本。
发布测试
- 封面失败、上传部分成功、审核拒绝和轮询超时可恢复。
- 发布期间继续编辑,不会改变本次冻结快照。
- Android、iOS 和 PC 的图片方向、格式与目标制品一致。
- 旧线上版本在新版本失败时仍可进入和游玩。
项目提交能证明我长期处理了这些链路和问题,但缺少统一的成功率、节省流量和发布耗时报告,所以目前不对外给收益百分比。
面试官:你个人负责和团队能力的边界是什么?
我直接负责或长期投入过地图模型、封面采集、上传下载、MD5 去重、多文件压缩加密、地图大小治理以及多端发布问题。云端资源库、审核服务和平台级质量门禁是协作系统;我做过客户端接入和问题闭环,也能解释完整设计,但不会说成整个平台都由我实现。
快速背诵版
我把 UGC 地图交付理解为版本化事务,而不是文件上传。编辑阶段先冻结不可变快照,按 manifest 序列化场景、逻辑和资源引用;多文件按逻辑归档路径异步压缩加密,用摘要做变化检测和传输校验;完整产物上传后才提交审核与发布版本。封面、文本、图片、资源和脚本走各自检查,客户端负责前置校验与体验,服务端负责最终安全。失败时本次任务可重试,上一线上版本不受影响。我的直接工作覆盖地图模型、封面、上传下载、多文件链路和大小治理,平台审核和资源服务是协作边界。
责任边界
| 内容 | 面试表述 |
|---|---|
| 地图模型、封面、上传下载与多文件交付 | 可说“我直接负责/长期投入” |
| 压缩加密、MD5 去重与地图大小治理 | 可说“我实现或推进过” |
| 完整云资源平台与审核服务 | 说“上下游协作和客户端接入” |
| 当前团队源码中的所有发布能力 | 说“项目体系/源码复盘”,不冒充个人交付 |
| 流量、耗时和成功率提升 | 无统一报告前不报百分比 |