TeamViewRelay 的共享协议源。
proto/teamviewer/v1/teamviewer.proto:唯一的 ProtoBuf 真相源buf.yaml:协议模块 lint / breaking 配置
- 本仓库只负责共享
.proto定义与buf规则。 - 本仓库不直接向消费仓库写入生成代码。
- Python / TypeScript / Java 生成物都由消费仓库在本地或 CI 中自行生成。
- 协议版本使用
proto/vX.Y.Z命名,例如proto/v0.6.0。 - 这里的 tag 表示“协议发布版本”,不是 Mod、后端或前端脚本的应用版本。
- 消费仓库必须锚定具体 submodule commit,不允许依赖本仓库 branch head 作为构建输入。
以下变更默认需要创建新的协议 tag:
- wire shape 变化,例如字段新增、删除、改名、类型变化、
oneof结构变化 - 兼容性基线变化,例如最低兼容协议版本变化
- 需要三端同步升级的协议语义变化
- 在本仓库修改
proto/teamviewer/v1/teamviewer.proto - 运行
buf lint - 在三个消费仓库验证生成与构建
- 创建并 push 协议 tag,例如
proto/v0.6.0 - 在消费仓库把
third_party/TeamViewRelay-Protocol升级到该 tag 对应 commit - 重新生成代码、运行测试并提交 submodule 指针更新
消费仓库通过 third_party/TeamViewRelay-Protocol 这一 git submodule 锚定本仓库的具体 commit:
Minecraft-TeamViewer-BackendMinecraft-TeamViewer-Web-ScriptMinecraft_TeamViewer
不要依赖本仓库的 branch head;应先发布协议 tag,再在消费仓库中更新 submodule 指针。
0.7.0 为每个房间维护“每个玩家 UUID 最新一次被观察到的 Tab 标签”,用于离线玩家的城镇归属分析。 它不是标签变更审计日志;实时 Tab 数据始终应覆盖历史缓存。
TabHistorySubscribeRequest/TabHistoryDigest:订阅数据集 head,digest 变化后再发起同步。TabHistorySyncRequest/TabHistorySyncChunk:支持完整镜像和从已知 revision 开始的增量镜像。TabHistoryLookupRequest/TabHistoryLookupChunk:按 UUID 或玩家名查询,不要求客户端下载整个房间。- 握手中的
TabHistoryCapabilities声明单消息的条目数、编码字节数和查询 selector 上限。max_chunk_entries不是房间玩家总数上限;数千条记录会拆成多个 chunk。 - 所有请求都隐式绑定当前 WebSocket 握手确定的
room_code,请求消息不接受另一个房间号。 TabPlayerEntry.scoreboard_team_id是原版计分板 Team 的内部名称;prefix、suffix、Team color 和解析后的 格式 span 分别透传。旧客户端继续读取无格式的display_name/prefixed_name。- digest 与 entry ETag 使用 SHA-256;完整/增量响应中的所有 chunk 使用同一个固定
TabHistoryHead。
0.7.1 统一将公开的 battle chunk 状态表示为结构化条目列表。坐标只出现在 ref 中,data 不再重复
dimension、chunkX、chunkZ,也不公开 dimension|chunkX|chunkZ 形式的合成 ID:
[
{
"ref": {
"dimension": "minecraft:overworld",
"chunkX": 12,
"chunkZ": 34
},
"data": {
"colorRaw": "#ff0000",
"colorMode": "raw_observed",
"mode": "simmc"
}
}
]实现内部可以用合成字符串或结构体作为索引,但 snapshot、patch、管理 API 和 0.7.1 摘要必须使用
{ref,data},不得把内部索引暴露为公开合同。ProtoBuf wire 消息原本已经使用
BattleChunkRef + BattleChunkValue,因此 0.7.1 不增加、删除或重编号字段。
Digest.battle_chunks 按连接协商出的协议版本选择合同:
< 0.7.1使用旧合同:按dimension|chunkX|chunkZ排序,每行是canonicalJson(key) + ":" + canonicalJson(flattenedValue) + "\n"。flattenedValue必须包含dimension、chunkX、chunkZ和业务字段。这保持与 0.7.0 Mod 及旧 Python Backend 一致。>= 0.7.1使用新合同:按(dimension, chunkX, chunkZ)排序,每行只写canonicalJson({"ref":ref,"data":data}) + "\n",不添加合成 key 前缀。- 两种合同都对完整字节串计算 SHA-1,使用小写十六进制的前 16 位。空集合摘要为
da39a3ee5e6b4b0d。
这里的 canonicalJson 递归按 JSON object key 的 Unicode 字典序排列;数组保持顺序;字符串使用标准
JSON 转义;null 字段先删除。Battle chunk 摘要不包含 observedAt、positionSampledAt、
alignmentSource、reporterId。缺失的 colorMode 按 raw_observed 参与摘要,以兼容旧数据。
固定测试向量如下(roomCode 为 default):
{
"ref": {"dimension":"minecraft:overworld","chunkX":1,"chunkZ":2},
"data": {"colorRaw":"#112233","colorMode":"raw_observed","mode":"simmc","roomCode":"default"}
}- 0.7.0 官方旧合同:
d6fccb4a1bd18438 - 0.7.0 Rust 历史缺陷投影(仅用于新 Mod 兼容旧服务端):
9cecf13aa4592a1c - 0.7.1
{ref,data}合同:31c63cd6e92bbc39
0.7.0 Rust 历史缺陷会从 flattenedValue 漏掉三个坐标字段。服务端不得继续生成该摘要;0.7.1 Mod
连接 0.7.0 服务端时可以同时接受官方旧合同和这个已知缺陷值,避免无休止全量重同步。
0.8.0 允许外部来源发布完整玩家目录、组织节点和组织关系边。关系图不进入实时 SnapshotFull,而是通过
独立查询消息按需读取,避免把玩家关系展开为 O(n²) 的玩家对。
ExternalSourceCapabilities在来源握手时声明数据集格式、覆盖范围和 stale 阈值。ExternalDatasetPublish分块发布关系快照或 patch;完整目录与实时在线 Tab 是不同数据集。PlayerDirectoryLookupRequest支持 UUID、稳定 player ID 和名字查询;名字可以返回多个同名玩家。PlayerRelationQueryRequest既可查询指定目标,也可在目标为空时筛选当前玩家的全部关系。PlayerReportPolicy是服务端建议,不是强制命令。只有健康且完整的外部在线目录才能建议抑制 Tab; 位置信息默认继续上报。- 数据过期后仍可查询,但响应必须设置
stale,由客户端决定如何展示。
teamviewer.v1 包名保持不变,0.8.0 只追加字段和消息。0.7.1 及更早的客户端会忽略新增字段,继续使用
实时 Tab、Tab 历史和本地手工关系标记。
- 不要在消费仓库中复制、重建或手改
.proto副本。 - 修改协议后,必须明确说明目标 tag、兼容性影响,以及需要更新哪些消费仓库。
- 任何破坏兼容的协议变更都必须显式记录,不允许“静默升级”。