標籤:

狀態同步的錄像系統

一般認為幀同步的錄像會更容易實現,因為對於每個客戶端,戰鬥由用戶操作命令序列驅動,只需要記錄命令序列,在播放錄像時再重播這個序列即可。對於狀態同步,其實錄像的原理和幀同步比較類似,因為驅動伺服器執行戰鬥邏輯的也是用戶的命令序列,我們同理可以記錄每個伺服器幀用戶的輸入,在播放錄像時回放用戶的輸入,即可復現戰鬥過程。

復現戰鬥過程的本質是要做到完全一致的命令序列,在完全一致的時間軸上,在完全相同的環境(相同的環境目前主要是隨機性一致)下執行,即可得到完全一致的結果。要注意幾個比較核心的問題:

  1. 要保證錄像和戰鬥是完全一致的命令序列,需要按順序記錄處理的命令。這個雖然看起來比較簡單,但是實際上卻比較容易踩坑,要注意接受到用戶命令的時間點和真正處理用戶命令的時間點之間的差異。
  2. 狀態同步戰鬥伺服器執行命令序列最好的做法是定幀處理命令,這樣就可以在播放的錄像的時候將命令序列劃分到不同的幀去執行,能夠比較容易實現一致的時間軸。反之,如果命令是實時處理的,在復現時對時間精度要求極高,不易實現復現。
  3. 所有計時都由唯一的delta_time驅動,如果伺服器幀是10幀,delta_time取const 100ms,這裡要注意的是delta_time不能獲取真實的幀與幀之間的時間差,因為delta_time會有很低的概率出現波動,不是100ms。這一點也是用來保證時間軸一致的。
  4. 隨機性可控,要保證錄像和實際戰鬥的隨機序列的一致,一般使用同樣的隨機種子即可,如果使用了第三方的庫也要注意保證隨機一致的問題。這個是保障戰鬥是在相同的隨機環境下執行。
  5. 驅動狀態同步計算的不僅僅是用戶輸入。對於我們的遊戲的某個版本,為了提高子彈命中和扣血的匹配,所有子彈的扣血由客戶端命中後向伺服器請求觸發,獲取扣血結果進行顯示。這個子彈命中的命令是實時處理的,這點與我們第一條要求相悖,我們要做到完全同樣的時間點執行子彈命中是比較麻煩的一件事,但是好在我們有第二點原則,所有時間都是由delta_time驅動,我們只需要在每幀的用戶輸入序列執行前執行與上一幀之間的子彈命中命令,即可完成復現。這裡有一個假設是,伺服器每幀的執行期間不會有子彈命中命令到達,這個我們通過加鎖來實現。在遊戲後面的版本中,子彈的命中改為由伺服器驅動。
  6. 保證所有命令的執行不會有並發
  7. 容器的訪問順序問題。不要使用對象直接做key,因為對象的內存地址是不穩定的,這樣訪問順序可能會存在不確定性。

推薦閱讀:

skynet初探
記錄一次伺服器宕機分析過程(2)-深入Lua GC
遊戲排行榜
卡牌戰鬥系統設計概述

TAG:遊戲伺服器 |