我抓包肤浅的分析了下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服务器肯定是分开设计的。