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