我抓包膚淺的分析了下LOL的網絡設計。
先說觀察到的一些現象:
A)協議基於UDP
B)采用純C/S模型(無C2C過程),
C)99% 包大小length小於576bytes,最大1038bytes。(100%小於1500bytes)
D)發包的頻率是平均0.017s
然后一一的分析:
A)協議基於UDP不用多說
B)對MOBA這類強調微操作的游戲來講,對英雄角色之間的影響巨大,對同步的要求高,玩家任何操作的不一致性都會帶來很差的體驗。
但網絡的延遲是絕對的,對這里的同步我的理解是只要在在玩家反應時間內完成同步就行,人類的反應也是有延遲的(一般來說200ms吧)。然后我玩LOL中,看到的現象,
1)技能都反應時間,一般就是發送或者指示的耗時,SPELL的耗時等
2)攻擊的時候也是有攻擊速度,對高攻速的ADC來說有攻擊物體飛行的時間。
3)我覺得最有意思的,在幾次服務器卡頓時我注意觀察了下,遠離敵人時英雄的走動不是強同步的,反之則是!
前面兩點是游戲設計上的,不多做討論,第三點上面,我覺得LOL雖然游戲地圖不大,但也是有戰場概念的(BattleField)。這一點下面繼續討論。
我認為使用純C/S模型原因有
1)對一致性來說,比網狀結構要好,某個玩家的網絡不好,對其他玩家的體驗影響較小(相對的)
2)反作弊要容易一些
3)星狀結構簡單,騰訊也財大氣粗,機房服務器都不是事
C)在我截的大概3400個包里只有30個是大於576bytes的。看來LOL的網絡協議設計的時候是強調對最小MTU的支持。
D)這里的頻率應該是每幀都會同步一個。這個速率相當高,大過了我的想象。之前在分析某個ARPG頁游(明朝傳奇)的時候,我發現游戲和服務器的同步速度是大概3個/s。游戲在這種速度下體驗還是很流暢(戰斗都是P2E,看得到其他玩家在同一個地圖上跑動,我不知道有沒有P2P,沒玩那么深)。
這里我覺得C)和D)都是在為反應時間服務的。
然后說說我的看法,
首先我覺得要把所有包都設計為576bytes以內是有難度的,10個英雄的狀態(等級,裝備,技能,HP,MP(PR),BUFF,DEBUFF 各種CD時間等),和地圖上物體(小兵,野怪)的時間片狀態,加上玩家的OP,以及包的驗證,時間,隨機數seed各種,是很大的,所以我大膽猜測這個設計上應該有如下原則:
1)傳輸協議以玩家OP為主,比如移動
struct Operation
{
uint8_t actor;
uint8_t op_type;
uint16_t time; //用time做random_seed?
};
struct MoveOperation : public Operation
{
uint16_t x,y;
};
struct AttackOperation: public Operation
{
uint8_t targets[MAX_TARGET];
};
struct SpellOperation: public Operation
{
uint8_t spell_type;
union {
uint8_t targets[MAX_TARGET];
uint16_t center_x, center_y;
};
};
這樣定義某個OP的操作,然后各個客戶端以及服務器再維護其狀態。
2)設立檢查點,強行同步狀態
比如將進入或退出戰場(BattleField)的時候,某些團控技能spell或者起作用的時候,將玩家英雄各個關鍵狀態(比如裝備,HP,MP,BUFF,DEBUFF各種CD時間)值hash或crc一下,然后做一次檢查,如果狀態值和服務器不對,客戶端強行和服務器同步相關量,甚至直接判定客戶端掉線。
3)重點同步戰場(BattleField)以及同視野內的各個玩家OP。
首先這里的“戰場”我想應該是敵我玩家所在的位置里可能發生交戰的區域,和視野的區別是,一方可能是隱身或者隱藏在草叢里等。
這里如果是實現的話,設定一個邊界條件后,用一個四叉樹即可。
對非戰場以及同視野內玩家弱同步。
4)對地圖內的NPC同步的話,這里也有難度,
我想到的就是各個NPC的位置各個客戶端用強規則的有限狀態機維護好(比如某個時間點,NPC一定在某個位置上,發生碰撞時一定會停下),對未在視野上的,由服務器傳狀態給客戶端,然后用服務器判定NPC的攻擊,或者被誰殺死的行為。
現在想到的就這些。
另外服務器上,因為對服務器IO要求很高,我想反作弊服務器和AI服務器肯定是分開設計的。
