---轉自CSDN
一、前言
????????無論是端游、頁游、手游如果是采用了MMO即時戰斗游戲模式,基本都會遇到同屏多角色實時移動、釋放技能、戰斗等場景,于是自然也需要實現如何管理同屏內各種角色的信息同步:例如角色的位置、以及角色身上的裝備、時裝、buffer等狀態的實時切換。同步在網絡游戲中是非常重要的,它保證了每個玩家在屏幕上看到的東西大體是一樣的,解決同步問題的最簡單的方法就是把每個玩家的動作都向其他玩家廣播一遍,這里其實就存在一些問題:1向哪些玩家廣播,廣播哪些消息;2如果網絡延遲怎么辦。角色的定義一般包括人物、怪物、寵物、NPC等,由于這各種角色在地圖上基本處于隨時不規則移動并且各種屬性信息也處于不斷變化中(例如:變身、穿脫裝備,甚至使用隱身藥水),所以需要實現地圖的區塊上各種角色的列表管理、切換地圖、進出區塊管理,實時同步角色的位置信息,以及附近角色的屬性信息變化到游戲內相應的玩家身上,而且這些信息的同步需要實時,否則基本就失去了即時戰斗的意義。一般會采用長連接的方式,方便實時推送交互信息。同時由于MMO網絡游戲環境的復雜性,管理好角色信息的同時還需要保證游戲的公平性,防止作弊、外掛,例如:判定人物的移動速度異常或者瞬間移動,糾正人物釋放技能的時間間隔等,相信只要在公網運營過的游戲都多少會遇到防作弊的問題。本文主要結合參與開發并在外網運營了幾年的一款MMORPG游戲做討論和分析,游戲規模國內最高同時30w在線,同區最高上w人,相信會有一定的實戰參考意義,當然也有討論和改進的空間,這也是寫這篇文章的主要目的。
二、地圖以及角色管理
????????無論是3D還是2D游戲:既然是即時地圖戰斗,那就自然有空間的概念,于是就產生了地圖,一般游戲內玩家最經常發生的交互也是在地圖上面發生的。mmorpg的地圖一般會有固定的一些屬性:例如:地圖的寬度、高度、最大角色數、地圖上面怪物的AI、以及常用的九宮格劃分區塊大小等等屬性。地圖寬高度用于控制地圖的大小,最大角色數用于控制地圖的最大承載容量,防止過載,影響玩家體驗。例如:圖1對地圖的部分關鍵屬性進行定義
2.1地圖區塊劃分
????????通常的游戲玩法:地圖上面的玩家在地圖里面只需要看到視野內周圍發生的事情,并不需要關心不同地圖,甚至相同地圖離自己很遠的地方此刻正在發生的實時場景,即使要關心,一般也是通過聊天公告等信息同步,并不需要收看現場直播)。于是,對地圖采用分而治之的方法,把每張地圖進行區塊切分,定義好區塊的大小,例如圖1采用正方形的劃分方法,規定每個區塊的邊長為6,一般區塊的大小不會經常進行隨意變動(除非在一些特殊的副本地圖里面,該值如果進行了變化則需要進行特殊處理)這就是地圖區塊的概念。于是每個在線玩家在地圖上面都會被定位到屬于自己的區塊,而當玩家在地圖上面移動,則會在不同的區塊之間進行來回的切換。同時,玩家在地圖里面必然需要實時看到周圍地圖發生的場景,一般采用九宮格的方式,如圖2,3.:也就是說會實時同步包括玩家所在區塊在內的周邊9個區塊的角色信息給予相應的玩家,理論上玩家只能看到9宮內發生的事情。
2.2地圖管理
????????劃分好了地圖區塊之后,地圖的管理至少還要包括:a阻擋的信息:包括靜態阻擋和動態阻擋:角色移動的同時需要考慮地圖區塊里面的阻擋信息(例如:來自角色阻擋、來自地圖固定建筑的阻擋等)b角色管理:需要管理地圖上面角色實時信息,并且維護各個區塊的最新角色實時列表信息:用于九宮格內玩家信息的同步。對于進入地圖固定區塊的玩家需要實時同步自己的信息給予附近的玩家,告訴他們有角色進入視野了,相反也要同步區塊周圍的角色信息給該玩家,同時,對于離開地圖區塊的玩家,需要同步信息告訴附近的玩家離開視野的消息,保證下一幀該角色不會再出現在該區塊上。而處在同個9宮格內的玩家,也需要互相同步屬性信息,保證看到的是最新的角色屬性變化位置信息等;并且地圖上面的NPC、怪物等角色自動刷新也需要地圖邏輯來處理,例如怪物死亡之后,需要處理怪物退出游戲世界,一般還要讓怪物經過一段時間自動復活,重新加入地圖,另外還有地圖上面怪物的AI,會在另外一篇文章單獨討論。
????????具體角色在地圖上面管理代碼的實現:針對所有角色我們首先采用定時刷新的機制,在所有的角色身上綁定定時器,例如:在GamePlayer,GameMonster,GameNpc定時觸發刷新機制:根據玩家實時所在的地圖比較前后所在的區塊是否一致,如果不一致,自然就需要處理附近玩家有角色進出視野的信息。例如:角色A定時觸發了刷新機制,發現已經從地圖亞特蘭蒂斯區塊99進入到了亞特蘭蒂斯98區塊,這時,自然就要重新計算玩家的九宮格區塊變化,通知相關有區塊信息變化上面的地圖角色位置信息:并且需要實時維護一份每個區塊每張地圖上面的角色列表,這樣做的目的:作為地圖管理者,有必要知道當前我的地圖上到底都有誰,常用于玩家附近的聊天,玩家同地圖的聊天,并且根據玩法一般還有地圖刷怪通知該地圖所有玩家的信息等需求。另外,單獨針對玩家的位置信息管理,則還跟游戲的特定玩法有關系,例如可以飛地圖的游戲,則當玩家實時切換地圖之后,則會直接觸發進出區塊視野的信息,而并不需要等到定時器觸發來更新角色位置信息,還有玩家重新登錄或者退出游戲,自然而然也要實時處理相應的位置同步信息;還有玩家換裝、使用技能、上下坐騎等都即時發消息通知九宮格內的玩家同步屬性信息
三、人物的移動
????????對于mmorpg,玩家的移動幾乎無時不在,并且相對于怪物的移動,寵物的移動等,玩家的移動更加核心,更加復雜不可控。特別是在大規模團戰中,玩家會經常移動,于是需要管理好地圖上玩家的移動,如果管理不好,則會出現大規模的外掛等,嚴重影響游戲的公平性,對于整個游戲也幾乎是毀滅性的打擊
3.1人物移動實現方法
????????通常對于游戲內玩家的移動有幾種處理方法:1客戶端只通知服務器要移動的位置,但并不需要經過后臺的驗證就直接開始移動了,通常服務器需要對最終客戶端移動的位置進行校驗,如果沒有該步檢測,那外掛就可以為所欲為了2客戶端每一次移動都需要通過服務器的驗證,然后再進行移動,該方法在網絡延遲的情況下,會變得比較不流暢,給玩家帶來很不爽的感覺。方法1同樣存在問題:同步的誤差,特別是在網絡延遲特別嚴重的時候:比如有一個玩家A向服務器發了條指令,說我現在在P1點,要去P2點。指令發出的時間是T0,服務器收到指令的時間是T1,然后向周圍的玩家廣播這條消息,消息的內容是“玩家A從P1到P2”有一個在A附近的玩家B,收到服務器的這則廣播的消息的時間是T2,然后開始在客戶端上畫圖,A從P1到P2點。這個時候就存在一個不同步的問題,玩家A和玩家B的屏幕上顯示的畫面相差了T2-T1的時間,要解決該問題,參考了之前的一篇文章,大致的內容如下:“有個解決方案:預測拉扯,首先要定義一個值叫:預測誤差。然后需要在服務器端每個玩家連接的類里面加一項屬性,叫latency,然后在玩家登陸的時候,對客戶端的時間和服務器的時間進行比較,得出來的差值保存在latency里面。還是上面的那個例子,服務器廣播消息的時候,就根據要廣播對象的latency,計算出一個客戶端的CurrentTime,然后在消息頭里面包含這個CurrentTime,然后再進行廣播。并且同時在玩家A的客戶端本地建立一個隊列,保存該條消息,直到獲得服務器驗證就從未被驗證的消息隊列里面將該消息刪除,如果驗證失敗,則會被拉扯回P1點。然后當玩家B收到了服務器發過來的消息“玩家A從P1到P2”這個時候就檢查消息里面服務器發出的時間和本地時間做比較,如果大于定義的預測誤差,就算出在T2這個時間,玩家A的屏幕上走到的地點P3,然后把玩家B屏幕上的玩家A直接拉扯到P3,再繼續走下去,這樣就能保證同步。更進一步,為了保證客戶端運行起來更加smooth,我并不推薦直接把玩家拉扯過去,而是算出P3偏后的一點P4,然后用(P4-P1)/T(P4-P3)來算出一個很快的速度S,然后讓玩家A用速度S快速移動到P4,這樣的處理方法是比較合理的,這種解決方案的原形在國際上被稱為(Full plesiochronous),當然,該原形被我篡改了很多來適應網絡游戲的同步,所以而變成所謂的:預測拉扯”
方法1實現:進行人物移動管理,需要定義以下相應的移動消息:具體的消息定義如下
(a)MSG_PLAYERMOVINGPOSTOSERVER //客戶端向服務器端發送移動中玩家位置改變
(b)MSG_PLAYERMOVINGPOSANDDIRTOSERVER, //移動中玩家位置和朝向改變
(c)MSG_PLAYERPOSTOSERVER,//原地不動玩家的位置消息
(d)MSG_PLAYERPOSANDDIRTOSERVER,//原地不動玩家的位置和朝向消息
????????消息a和b負責向服務器同步人物需要移動到的目標位置和朝向信息,服務器需要對該位置信息進行阻擋、狀態判斷等合法性檢測通過后,則同步角色位置信息到9宮格內的其它角色,相反如果失敗例如移動到阻擋里面,則需要通知客戶端糾正位置。消息c和d則同時用于前后臺校驗玩家的位置信息,例如角色一定時間內移動后最終停下來的位置。
四、防作弊
????????常用的前后端消息加密,以及客戶端加殼的機制幾乎已經是通用的做法,所以這里不做重復,而且再高明的加密或者加殼幾乎都有被破解的可能,但這些機制依然要堅持使用,至少可以提高作弊的成本,可以延長游戲的壽命,下面再描述我們目前除了消息加密和加殼之外采用的方法
限制客戶端發送移動消息的頻率:一般游戲內玩家并不需要進行太過于頻繁的移動,就算需要頻繁的移動客戶端也可以對移動進行合并處理再上報移動位置信息,所以對于頻繁的移動消息完全可以當做非法請求不處理。目的用于防止外掛封包頻繁的發送移動消息,進行非法的快速移動(例如運營中發現玩家使用變速齒輪等插件,用于搶掉落寶箱等場景,會有玩家進行瞬移到寶箱附近拿走物品,這時候守門的人就崩潰了,嚴重影響了游戲的公平性)
移動距離檢測:記錄客戶端每次發送移動消息的服務器時間間隔,根據人物的正常移動速度,算出合法的移動范圍(一般需要加上一定的誤差,由于網絡的延遲等原因,不可能做到100%精確),如果發現不正常的移動速度,一般先采用和平的方法,讓該移動消息失效。目的用于防止外掛封包發送不符合人物移動速度的位移信息
消息時間校驗:使用外掛的玩家,例如變速齒輪等插件,而且變速齒輪可以調整倍數,所以一般可以嘗試出游戲的檢測頻率,因此必須采取手段防止玩家使用該插件。分析出變速齒輪的原理,一般是通過修改API函數GETTICKCOUNT和TIMEGETTIME,騙過了游戲和程序的定時器導致游戲和程序速度被改變。服務端發送時間種子到客戶端.客戶端做個差值.舉個例子:服務端發來的種子是timeGetTime()=2000,客戶端本地取時間是timeGetTime()=1000那么差值就是1000客戶端所有的協議中增加時間字clienttime=timeGetTime()+1000到服務端。服務端取當前時間對這個時間做個容錯校驗.容錯范圍需要你自己調節.一般最好設大點.不然容易誤判.
五、運營中遇到的問題和優化空間
5.1服務器性能瓶頸
????????即時戰斗類游戲一般都會設計有跨服戰、國戰等這樣的玩法,會遇到某時段同屏角色數非常多的特殊場景,這時候大量的角色戰斗中移動和釋放技能,上下坐騎、必然會造成消息量暴增,服務器壓力驟增。以線上運營的游戲為例,解決辦法:
????????首先,對峰值期間的消息進行統計分析,對頻繁發送并且流量大的消息進行重點監測,例如:分析出來大量角色移動進入區塊,同步角色信息包括人物身上的時裝,坐騎,寵物、裝備等,會有一個峰值。但游戲中一般大規模團戰的地圖中,玩家一般最先關心的是敵人的動向位置信息,反而對人物的坐騎,時裝,裝備等信息可以延后,于是可以對某些特殊的場景例如國戰地圖,跨服戰地圖進行特殊的刷新機制進行優化,當區塊內角色數到達一定數量后,同步信息只同步人物位置,模型等信息,減少消息的流量
????????其次,為了防止高峰時期服務器處理消息量過大,待處理消息隊列以及發送隊列擁擠,造成雪崩。對消息進行分級別定義,定義消息的時候進行消息級別定義,目前分為低、中、高三種消息類型,并且限制每種類型在等待處理的消息隊列中的最大個數,每種消息類型在隊列中大于特定的值,則直接丟棄,不處理。例如:服務器ping消息,人物跳躍等則可以定義為優先級低的消息,同理對于服務器需要發送出去的消息包也進行分級
5.2刷錢刷經驗
????????一般外網運營一段時間的游戲很多都會遇到刷錢刷經驗的bug,也許一些沒有交易系統或者休閑類的游戲不會遇到,不過反正我們遇到了,就算沒遇到做好預防措施也是必要的。解決方案是:請數值策劃定制好根據游戲玩法角色對應一天最多能獲得多少經驗和金幣,由服務器進行合法性檢測,如果超過了閥值則必須采取處理措施。我們模仿了現實社會,給游戲設計了一張監獄地圖,監獄顧名思義就是給犯法的人準備的,游戲里面發現有作弊,或者刷錢刷經驗等的行為都會自動被傳送到該地圖,該地圖沒有傳送點,只能一直呆在里面不能打怪升級也不能交易等諸多限制,進入該地圖的玩家只有等坐牢時間到期了或者通過客服申訴成功,才會被傳送出該地圖