第一部理解章 · 同步不是广播,是状态复制
这一节不要求动手,把第一部七章揉成一个能贯穿 Vol.2 全卷的心智模型。
两种心智模型
Section titled “两种心智模型”「网络」可以用两种很不一样的方式去想。
第一种:广播模型。客户端之间互相发消息,谁触发了什么事就广播一下,大家收到都做相应反应。这是绝大多数人接触多人游戏时的初始直觉,也是 VRChat 入门教程里常见的解释方式:「按钮按下,事件广播,大家都看到灯亮了」。
第二种:状态复制模型。每个对象有一份「真相」,托管在某个 Owner 手里。其他客户端不发消息「我看到了什么」,而是订阅这份真相,被动收到状态的复制。
两种模型在简单场景下结果一样:A 按按钮,B 看到灯亮。但是一旦面对迟入、Owner 离开、网络拥塞,结果完全不同。
广播模型解释不了「为什么迟入玩家看不到一小时前那次按钮触发」。它会让人写出「那就再广播一次给迟入玩家吧」这种工程上很重的反应。VRChat 没给这种 API。
状态复制模型一句话就解释了:事件不会重放,状态会复制。迟入玩家进来不是收到「过去发生过的事」,而是收到「现在这一份状态是什么」。
把第一部读成一句话
Section titled “把第一部读成一句话”第一部的所有内容都可以折叠成一句话:
VRChat 的网络系统的工作是把状态从 Owner 复制到其他人。事件是一次性绕过这套复制的捷径,仅适合「过了就过了」的瞬时动作。
每一章都是这句话的一个面。
第 1 章和第 2 章 回答被复制的是什么。拆状态本质是在拆「要被复制的那份真相」;四种通道是不同形态状态的复制方式:Object Sync 复制 Transform,同步变量复制结构化字段,本地完全不复制,事件不复制只通知。
第 3 章和第 4 章 回答谁负责复制、复制怎么发生。Owner 是这份状态的托管者,Master 是「没人主动接管时」的兜底托管。RequestSerialization 是 Owner 请求把它托管的状态打包发出去,OnDeserialization 是复制方拿到一份新真相。
第 5 章到第 7 章 回答复制的边界。迟入只收当前状态、不重放历史。复制有 payload 上限和总带宽两条预算。测试矩阵在验证的就是这一句:真相在 Owner 切换、迟入、拥塞这些场景下有没有被正确复制。
读到这里,「同步」这个词在 Vol.2 余下章节里就不再是一个含糊的动词了。每次写到「这个值要同步」,可以问自己四个问题:
- 这份状态托管在谁手里?(Owner)
- 它通过哪个通道被复制?(同步变量 / Object Sync / 池)
- 它需要被复制给谁?(所有人 / 当前回合的玩家 / 只复制一次给迟入)
- 复制失败时怎么降级?(
OnPostSerialization报告 /IsClogged/ 重试)
四个问题答完,同步设计就清晰了。
把广播心智留给「事件」
Section titled “把广播心智留给「事件」”广播心智不是错的,它只是被用错了地方。在 Vol.2 里,广播心智专属于一档:档④(瞬时事件) 是一次性广播,过了就过了。
档①、档②、档③都不是广播,是状态复制。把这条边界划清楚,会发现写代码时哪些用 SendCustomNetworkEvent、哪些用 [UdonSynced] 自然就能选对。
这一节的三句话
Section titled “这一节的三句话”网络系统不是「客户端互相发消息」,是「Owner 把状态复制给其他人」。事件是绕过复制的捷径,只在「过了就过了」的瞬时动作里用。设计同步时先问 Owner 是谁,再选通道(档①/②/③/④),最后才想清失败时怎么兜底。
- Gabriel Gambetta · Fast-Paced Multiplayer — 状态复制 vs 输入广播两种模型的经典文献,VRChat 不是其原模型,但思想可借鉴。
- VRChat Creator Docs · Networking and Synchronization — 把官方文档当作「状态复制」视角再读一遍。
- 附录 · 术语表 — 复习第一部的所有术语。