归总游戏系统的消息,大致有四种。
- 服务器消息
- 仅影响客户单机渲染的客户端消息,无需同步
- 需要服务器确认后方可执行的客户端消息
- 需要服务器确认但客户端可以预渲染的客户端消息
- client.processInput(); 其中可能会产生玩家动作指令,比如移动位置,发起攻击,对话,买卖武器等等,向服务器发送消息
- server.processInput(); 服务器会接收到这个事件,并对该事件的有效性进行确认
- server.updateScene(); 根据确认的结果,服务器更新场景相关物件的相关状态,并将确认后的事件压入感兴趣的玩家消息栈(一个玩家总是对其自身感兴趣,所以玩家发出的动作指令造成的一系列后续同步消息,会同步到其自身,而且当其预渲染状态与服务器失同步时,以服务器上的为权威版本)
- server.renderScence(); 服务器向玩家广播其消息栈
- client.processInput(); 客户端收到服务器发来的同步消息,更新其所管理的局部场景状态
- client.renderScene(); 客户端渲染其局部场景,玩家将根据其所见的局部场景进行相应的操作,同步过程将回到第一步,重新进入同步循环
服务器在T0时刻说物件O需要在时间T1到达位置P1,现在玩家A和玩家B都将收到这个同步消息。玩家A的网络延时是t1,它收到消息时时间应该是T0+t1;玩家B网络延时是t2,它收到消息时间为T0+t2。所以玩家A和玩家B会各自调整自己的局部场景中物件O的移动速度,确保它在T1时间到达P1。通过服务器总控这个同步过程,平滑了网络延时带来的时间差异,因为客户端可以根据服务器的指示以及它收到的时间自动调节物件的移动速度。(在不涉及到位置的时候,网络延时的问题不大,还记得位置是关键信息么)但上述方法没有解决客户机和服务器时钟不同步的问题。比如说,服务器时间为T0时,玩家A的时钟指向TA,玩家B指向TB。那么达到服务器的T1时刻时,玩家A和B指向的时间分别是TA+T1-T0和TB+T1-T0。玩家之间时间轴不一致,和服务器也可能不一致。所以,同步的基本问题,我们还得解决解决,那就是时钟同步,俗称对表。
但是对表,还是会涉及网络延时。比如说,玩家登陆时,服务器告诉你,它此刻时间是T0,但到达玩家的时候,它的时间不是T0了,而是T0+t,t=网络延时。但是客户端又怎么能知道t是多少呢?这是个棘手的问题。据我所知,这个世界性难题,还没有办法完美地解决。
实践上解决的办法,就是客户和服务器进行握手,观察多次网络传输前后两端的时间差异,然后取其均值加到客户时钟。具体的算法我就不写了,很简单,看官自己想一下就明白了。
至此
同步问题就讲完了。下面开始讲......
