遊戲在真實的環境中,有些特殊狀況須要處理,本文介紹技能模塊是如何處理人爲做弊和現實中的網絡致使的一些問題。服務器
主要介紹四個部分:網絡
防外掛優化
網絡延遲問題解決server
網絡卡頓和抖動blog
流量優化隊列
注意,本文默認介紹的是玩家的技能處理,也就是技能的控制端在玩家的客戶端。對於控制端在服務器的小怪,基本沒有前三個問題。遊戲
每一個遊戲的技能系統的實現不一樣,處理方式也有可能不太同樣,本文所使用的技能系統參考以前寫的文章:ip
1.防外掛 同步
因爲技能是客戶端先行,所以技能模塊不少邏輯是放在客戶端的,由客戶端控制技能流程而且通知服務器執行相應的功能。因而可知,技能是由客戶端發起的,服務端必須對收到的技能執行命令進行驗證以保證技能確實是可用的,防止玩家經過外掛重複發送技能釋放消息無限次釋放技能。消息隊列
每一個遊戲技能系統實現不一樣,可能對應的邏輯也不太同樣。基本原則是:
服務端保存技能釋放可用性的相關信息,好比技能CD、技能藍量等。
技能結算在服務端執行,客戶端管理技能執行流程。
服務端每次真正的釋放技能以前,對技能進行判斷是否可用。
服務端收到的技能執行消息後,根據實現系統的規則進行相應驗證。
下面,以咱們的技能系統爲例,介紹咱們是如何實現防外掛的。
在咱們系統中,技能同步包括三類同步消息:
技能根節點enter (root_enter): 表示技能樹根結點進入執行,表示一個技能樹開始。
技能葉子節點enter(action_enter): 表示技能的執行節點進入執行,表示一個技能執行模塊開始執行,有一個執行模塊有後搖時間,根據後搖時間,他會自動結束。
根節點exit(root_exit) :表示技能樹結束。
服務端會接受到客戶端發來的這三種技能消息,其中,服務端收到rootenter和actionenter消息後,須要對消息的真實性進行驗證,root_exit表示技能結束,和防外掛沒有關係,無需驗證。
因爲技能的信息都是以rootenter來控制的,好比技能CD、技能耗藍和沉默/暈眩等致使的技能是否可用,所以,當服務端收到rootenter的時候,首先要判斷這個技能是否真的能夠釋放,判斷後進入相關邏輯。
actionenter是技能真正的執行消息,技能模塊並無方法判斷一個執行節點(技能樹葉子節點)是否可用,所以,當收到actionenter的消息時,只能根據root節點的信息進行判斷。咱們進行了兩種判斷:
判斷一:在rootenter和rootexit執行期間,表示正在執行這個大技能,收到action_enter後,判斷這個action是否屬於正在執行的大技能葉子節點,若不屬於,不能執行。
判斷二:咱們還判斷了action_enter消息對應的執行節點的順序,保證執行節點是按照合法的順序執行的,而不能一直執行某一個特別牛逼的葉子節點。
此外,服務端執行action節點的時候,不能同時執行多個,每次只能執行一個action節點,而且須要持續相應的時間(action節點的後搖時間),上一個action節點執行結束後才能執行下一個節點。
基於以上機制,能夠保證服務端收到的技能消息只有合法的消息才能夠執行。
戰鬥模塊的防外掛主要包括技能模塊移動模塊,把這兩塊解決外掛問題,戰鬥模塊基本就能解決外掛問題了。
2.網絡延遲
在真實的網絡環境中,網絡延遲是不免的。形成的結果是,服務端執行邏輯一直晚於客戶端。這種網絡延遲並不會形成錯誤,可是在網絡延遲大的時候會形成表現和結算不能對應上。爲了解決着這個問題,通常採起的方式是基於網絡延遲時間讓服務端加快執行速度,去追趕客戶端。
以技能結算爲例,當server端收到action_enter消息後,根據當前時間和客戶端開始時間能夠計算出網絡延遲,將服務端的前搖時間減小兩個網絡延遲,當客戶端收到技能結算消息時,正好是客戶端的技能結算時間。
3.網絡波動和卡頓
對於手機遊戲來講,網絡波動和卡頓也是不免的,這種狀況形成的結果就是,本來按必定的順序以必定的時間間隔到達服務器/客戶端的消息,可能同時到達了服務器/客戶端,或者時間間隔忽大忽小。
通常來講,當服務器因爲網絡卡頓同時接收到多個技能開始執行的消息時,能夠經過兩種方式進行處理。
接到技能執行消息後立刻執行,這樣致使的問題時可能在同一幀收到多條技能執行消息,而且在同一幀執行多個技能。這個策略的好處是處理方式比較簡單,並且能讓服務器儘快的跟客戶端同步。可是爲了防止玩家使用外掛同時發送多個技能執行請求,這個策略是不可行的。
當接收到多條技能執行消息時,按序依次執行技能,每一個技能的執行時間結束後,才執行下一條技能。這種策略的問題是,若服務器的延遲與客戶端較大,若是玩家一直在不停的放技能,會致使服務器與客戶端的延遲愈來愈大。
爲了解決此類問題,咱們在客戶端和服務端採起不一樣的處理方式。
3.1 proxy服務端(主控端是玩家控制的客戶端)
服務器是技能真正執行的地方,須要保證技能正確的執行。所以,服務端基於第二種方式解決網絡卡頓,同時增長了一些邏輯,以保證服務端不會和客戶端延遲過大。
3.1.1 消息隊列
proxy服務端以隊列的形式保存下來收到的技能消息並依次執行。
當服務端收到技能同步消息後,就將消息存入隊列,技能執行模塊就根據隊列依次執行,其中,actionenter會執行一段時間,後搖時間點到達後結束。rootenter和root_exit對技能執行狀態進行控制。
3.1.2 防止服務端延遲過大
在某些狀況下,服務端可能遲於戶端較長時間。好比網絡卡頓,致使客戶端屢次釋放技能的消息同時到達服務端。爲了解決這種問題,咱們經過兩種機制,讓服務端追趕客戶端。
當接收到一個新的rootenter信息時,立刻清空掉隊列中的全部技能消息,執行隊列中對應的rootexit消息。而後執行新的root_enter信息。此策略表現是:當玩家執行一個新的技能,服務端以前尚未執行的技能就再也不執行了。
葉子節點的持續時間(後搖時間)根據網絡延遲進行必定的減小,給定一個係數好比0.8,一方面保證服務端不會快速的執行多個action節點,同時可讓服務端儘快的追上客戶端。
3.2 proxy客戶端 (主控端是玩家控制的客戶端或者是服務器控制的怪物)
客戶端只是執行技能的表現,在網絡條件較差的狀況下,咱們只要保證遊戲的正確性(不出錯誤,不影響服務器正確運行,網絡條件變好後遊戲能夠正確運行)便可,至於表現和用戶體驗,盡力就能夠了。
在咱們遊戲中,客戶端的proxy端採用的是接到消息後立刻執行的策略。客戶端接到既能執行信息,那麼就把以前正在執行的技能中止掉,而後執行新的就行了。
4.流量優化
流量的優化基本上沒有太多通用的技巧,最基本也是最重要的就是:不要同步沒有意義的消息。
這句話是廢話,可是也是流量優化的指導方向。聽起來很簡單,可是實現起來很是難,甚至想不一樣步冗餘信息是不可能的。
爲何說這件事很難,甚至是不可能的呢?
一,只有梳理清楚了執行邏輯,才能肯定哪些同步消息是必要的,哪些是冗餘的。執行邏輯必定要清晰。
二,好比一條消息,某些狀況是不用同步的,有的狀況又要同步。那麼發,仍是不發。再細節一點,好比一個參數,有的狀況不須要同步,有的狀況須要同步。若是對每種狀況進行特殊化編寫代碼,代碼可讀性可能較差,若是發送一個通用的同步消息,可能消息量比較大。那麼,作到什麼程度?大概作到遊戲流量能夠接受的成都就行了。
還有些tips能夠減少流量信息,好比:
有些常見的string,甚至是全部的string,能夠將其轉爲一個int,客戶端服務端都知道這個int表明什麼 便可。
有些float,能夠經過乘以100轉爲int傳到客戶端,客戶端再除以100便可。等...
5.其餘
本文基於底層的消息是保證消息順序、保證不掉包、保證消息不被篡改、保證消息沒有重發的。這件事,自己實現起來可能更加複雜。