相信大家都玩过分区分服的游戏,输入账号,选择区服(或是先选择区服,再登录账号,这两个逻辑不同),进入游戏,一刀999。
游戏中的每个区就相当于一个独立世界,每个区的玩家都是隔离开的,大家互不可见,每个区享受独立的资源,独立的事件,独立的BOSS等。
为什么要分区分服?
对于玩家来说,如果不是因为资源争抢耗时耗力,大家更希望在线的玩家越多越好,最好是弄一个地球Online最完美。
但由于很多限制,我们不得不考虑把玩家分批次的放在不同的次元中。
CPU的限制:在有频繁运算的游戏中,他的CPU占用会是很高的,比如寻路、战斗。他们占用CPU很高的原因是因为执行他们逻辑非常频繁,可能跟玩家操作有关,也可能跟体验有关,在RPG中,这些操作大概每60毫秒会计算一次,所以如果在线的人数很高,这个CPU的占用率会呈线性增长。
带宽限制:有时候玩家会反馈游戏很卡,我们就会问是网络卡还是前端卡,有些资深玩家就会明白其中的意思。如果是动画播放一顿一顿的,那极大可能是前端,也就是自己的电脑卡。如果是人物移动或者玩家下达指令,角色有明显的延迟,那就是网络卡,一般玩家都会直接说垃圾服务器,服务器很卡。
在RPG游戏中,带宽是非常需要把控到位的,比如最常见的控制手法就是控制屏幕消息广播的范围。什么意思呢,看图。
每个角色进入你的视野或走出视野,都必须要通知到他周围的玩家,这个行为是随着走路频率来检测的,刚才说的频率是60毫秒一次,那么这个检测也是60毫秒一次,每个玩家的坐标变动导致的区域变动(坐标变动可以用前文中提到的预演来优化),必须要通知到周围的人。
那么你可以计算一下,屏幕内每一个玩家的增添都会让网络的压力呈指数增长,这还只算了走路,如果还有其他的呢,比如战斗逻辑,BUFF。所以有时候如果策划要让你时时同步每个人的BUFF信息,你就可以告诉他,带宽压力太大。
为了节约带宽,我们可以做很多措施,比如,不重要的伤害数值只告诉自己,BUFF信息只在小场景同步(比如:副本),大场景的BOSS伤害排行只同步给有仇恨且在范围内的玩家等等,这里面需要扣的性能太多太多,根据需求而定。
大家可以使用超大分辨率去玩一些RPG游戏,你会发现你的视野并没有随着屏幕的增大而增大。
其他一些限制:内存大小,游戏是高反馈类型的应用,所以数据尽量存内存,如果在线人数过多,内存就有可能爆掉,影响其他玩家的体验。其实还有很多很多原因,比如数据隔离,玩家想转区,那就得重新买一次道具、装备等等。
为了保证大部分玩家的正常体验,游戏服务器一般都会设定一个预估的最大人数(根据压测数据或一些性能指标来定)。如果达到这个阈值,就会排队。
分区分服怎么做?
我们一般给每个玩家,每个帮会甚至是每个区服数据都拟定一个key,这个key是用来区别区服的。
key一般由渠道ID+区服ID组成,每个玩家创建的区服是一定的,他所生成的数据也会一直与这个原始key一致,这样不管后面数据去哪,我们都可以追溯到这个玩家原来的区服。
渠道ID为一个byte,暂定渠道不超过255个,如果超过了,恭喜你,你太牛逼了。
区服ID最大为0xFFFF,暂定不超过65535个(还记得这个数字吗),如果超过了,那么你更牛逼了。
他们两个由一个位运算,组合成一个key,我们把它叫做区域ID(AreaID),算法如下。
static int areaID(byte majorID, int minorID) {
Conditions.args(
minorID >= MINOR_MIN && minorID <= MINOR_MAX,
"%s<=minorID<=%s,yours:%s",
MINOR_MIN,
MINOR_MAX,
minorID);
Conditions.args(majorID >= MAJOR_MIN, "majorID>=%s,yours:%s", MAJOR_MIN, majorID);
return ((majorID & 0XFF) << 16) | ((minorID & 0xFFFF));
}
每个涉及到区服的数据,比如玩家、帮会,都要求实现一个接口,AreaObj,接口的方法就是返回一个AreaID,这样我们就能保证,这些数据拥有区服的唯一性。
什么是连服?
连服,是一个逻辑上的概念,就是一组特定的服务器在某些玩法下互通数据。
为什么是一组服务器,因为连服的概念其实就是内存上的隔离,上面讲了区服的AreaID,明白的同学一下就能想到,我们把所有数据都用一个Map来存,Map的声明形式就是Map<Integer,?>,那个Integer就是AreaID。
如果要让玩家数据互通怎么办,很简单,根据另外一个玩家的AreaID去取相应数据就行了。
比如,平时的练级地图,我们这样存放Map<Integer,Scene>,玩家每次选择地图,只返回相应AreaID的就行了。
假设现在有一个公共副本,大家都可以参与,那么我们再放一组其他Scene在另一个特别的集合里就好。
连服是一个Trick,小把戏,开发起来其实区分一下就行了,玩家还以为他们真是隔离的,但实际上,想让他们在一起很容易。
连服有个缺点,那就是如果服务器宕掉了,这一组服务器都会GG,或者说如果开服就必须开一组。
为什么要连服?
连服其实目的很简单,节约资源,降低成本,因为大多数游戏的单个区服玩家并不会特别多(特别是滚服游戏,页游就是典型的滚服游戏),合服又会有一个最低时间要求,总会有一些区服成为鬼服,但我们又不能让这个鬼服一直占用着资源,于是我们可以在这个鬼服的“旁边”加N多的服务器。
第二个是伪跨服,大家知道,要实现跨服是很困难的,进程间的交互让人头疼,而连服对于玩家来说就是跨服,他们又不关心我这次对战或者活动是垮进程还是跨机器,他们只知道,我看见其他区服的玩家了,就叫跨服,跨服的玩法会显得更正式,更大型一点。
什么是合服?
合服我不用解释大家都知道,就是把大家的数据合在一起,这样大家就能见面了,又会有新的矛盾和团体产生,刺激新一轮大战。合服的目的就是一个,合并人少的区服,让玩家觉得不孤单,不是在单机。
怎么合服?
上面讲到,每个涉及区服的数据,我们都会绑定一个AreaID作为key,这个key可以追溯该数据的创建区服。
每个数据的唯一ID,我在以前的文中也提到过,算法也给过,他同样是用这个AreaID生成的。
我们有key和唯一ID作为区分,合服的时候只管把所有数据合并在一起就行了。
但是合并在一起且有连服的情况下,我们必须要区分出这个玩家的创建区服和当前区服,根据逻辑不同,使用不同的key。