Random Thoughts
Recent content on Random Thoughts
马上订阅 Random Thoughts RSS 更新: https://blog.joway.io/index.xml
从动物森友会聊主机游戏联机机制
最近在玩动物森友会的时候时常会遇到一些迷之联机问题,在网上一番搜索,发现大家的答案都趋于用玄学来解释,于是便有了兴致想在原理上搞懂这些问题产生的根源以及动森这款游戏的一些联机设定背后的技术原因。
事先声明,本人并不从事游戏行业亦非主机游戏长期玩家,如有纰漏或其他角度的补充,欢迎在评论区告知。
游戏是如何同步的
我们首先来看看一般游戏是如何来做同步的。
想象两个独立房间里分别有甲、乙两个玩家,他们要远程下一局象棋。他们每下一步前都需要先获知到当前棋盘的情况,此时能够有两种实现方式。
第一种叫做锁步同步,原理是玩家每操作一步就通知给另外一个玩家,彼此同步当前的操作序列,通过这些有时序的操作,就能够计算出当前棋局的状态。但它不允许中间丢失任何一步的信息,否则就会出现非常大的计算偏差。
第二种叫做状态同步,顾名思义是玩家每操作一步,就同步整个棋盘的状态。这种方式可以容忍中间某些状态丢失,最终得到的状态依旧还是一致的。
在实际实践中,针对那种玩家操作非常高频的游戏会更多使用锁步同步,例如王者荣耀。而对于那些卡牌类游戏更偏向于直接用状态同步。
游戏是如何联机的
通信架构
无论是上述哪种同步方式,我们都需要通过网络在多个主机间交换数据。我们现在将场景转换成甲、乙、丙三个人一起下跳棋。为保证三个人最终得到的游戏状态都是一致的,我们往往需要有一台 Host 主机来作为权威主机,其他主机只能通过权威主机下发的数据(状态/操作序列)来更新自己本地的游戏数据。
在这里我们假设甲来做「Host」,乙、丙每操作一步,都需要先发送给甲确认,无误后再发送该操作被确认的信息给乙、丙,乙、丙此时才能够认为操作成功并将画面更新到最新的状态。甲主机上在任意时刻都存有当前游戏的真正状态,其他主机只是在 follow 甲主机的状态以更新自己的游戏画面。
在上述模式下,由于甲主机既要作为游戏主机,又要作为状态同步的主机,当联机用户数一多,甲主机就会不堪重负,出现所谓的「炸机/炸岛」现象。另外,这种模式会需要甲主机一直存活,只能作为短时间内的伺服方案。所以有些游戏会引入一个外部自建/官方的服务器来承担这个状态同步的功能,例如我的世界。但究其原理是一模一样的。
NAT 穿透
在了解完上面的基础知识后,我们能够发现,在不考虑外部服务器的情况下,我们会对玩家主机间的网络有以下几点要求:
- 甲能够向乙、丙发送数据
- 乙、丙能够向甲发送数据
- 乙、丙之间不需要有网络联通保障
虽然上述要求看起来很容易,但是由于现在网络运营商都会不同程度地使用 NAT 技术,所以导致要让两台家用主机建立双向通信并不是一件非常容易的事情。
家用网络一般有四种不同的 NAT 类型:
Full-cone NAT:
- Once an internal address (iAddr:iPort) is mapped to an external address (eAddr:ePort), any packets from iAddr:iPort are sent through eAddr:ePort.
- Any external host can send packets to iAddr:iPort by sending packets to eAddr:ePort.
(Address)-restricted-cone NAT:
- Once an internal address (iAddr:iPort) is mapped to an external address (eAddr:ePort), any packets from iAddr:iPort are sent through eAddr:ePort.
- An external host (hAddr:any) can send packets to iAddr:iPort by sending packets to eAddr:ePort only if iAddr:iPort has previously sent a packet to hAddr:any. “Any” means the port number doesn’t matter.