【总结】游戏服务器的几个设计原则
功能版本化,模块化
这是现在我们项目开始重视的部分了。随着策划大佬的版本计划的发布,程序可以提前思考规划新功能和原有功能的关联度,最好能够做到能动态关闭和打开新功能,以避免外网随时可能出现的bug。同时开新服的时候可以做到版本控制,否则代码耦合较高的话后面会比较困难,这个事情越早做越好。如果是小作坊的话确实需要一个服务器的架构师或者是相关经验者,这个很重要。
数据结构重视
游戏服务器有个重要的特点是与其他互联网业务不同,【在线】的状态特别多,即成千上万的用户数据需要保持在内存中,DB可能需要额外的一到两层缓存来确保数据的安全。用精确(是精确,不只是正确)的数据结构可以节约流量,这个到后面带来的影响是巨大的。比如不超过255用byte(unsigned char),3万内用short,21亿内用int。但是也要避免埋坑,比如用int来表时间戳的话,2^31-1换算成秒的时间戳刚好是北京时间的2038-01-19 11:14:07。虽然我们的游戏活不到那个时候,而且游戏的自然逻辑也不可能出现超过这个的时间,但是上次我们的GM客服在永封玩家的时候设置了一个超过这个格式的时间,然后服务器恰好那个逻辑没容错,就崩了。。(可以预见的是快到这个时间的时候会有一大批外包的网站和App挂掉)。一个char是2byte,一个int64才8byte,所以有时候该用还是得用。
设计上偏向对玩家不利
游戏设计上把玩家基本上当做是混沌的,人性上是自私和贪婪的。游戏的出了bug,如果是属于玩家损害的bug,那什么论坛什么贴吧各种群喷成狗;如果是玩家收益的bug,玩家就两眼一闭当做没看见,有时还各种私下交流和利益交换,这个bug甚至到最后都无法被开发者所探知。就像服务器出bug的严重度远远高于客户端出bug的严重度一样,对公司的造成的损失远远大于对玩家造成的损失,而且同时玩家是很容易满足的,一般适量的补偿就能平息玩家的怒火。所以对于各位社畜来说,不管是出于保命还是出于代码质量,设计上请偏向对玩家不利吧:充值永远是先扣款再给游戏币,购买物品永远是先扣币再给道具,兑换永远是先删除道具再添加道具。其实不仅是系统服务器,逻辑服务器也是应该这样。
分布式的功能需要随时调整
事实上每个游戏哪怕是照搬某个哪怕是现有的游戏架构,具体到某个服务器设计细节的时候还是会有差异。世界服,比赛服,匹配服,好友服,DB服等等这些架构并不是一开始就是规划出这样的,一般考虑到每个功能服务器的承载量,功能服不宜太多,太多的话要考虑某个服宕掉后其他连接的服的正常运转,数量越多防错复杂就越高;也不宜太少,某个服功能太过耦合需要考虑把部分功能单独迁出去或者独立成一个服。举两个例子,我们的服一开始房间服和匹配服是同一个功能,后来我们出现了队伍匹配,那么房间和匹配功能就不再相关,于是房间服就从匹配服中单独提出来;我们的城镇服算是一个比较重要的逻辑服,但是它又同时作为其他重要服的中转服,这么设计出于安全不太合适,于是单独设立了一个基本上只用于协议中转的透传服,在一些玩家跨服的功能上就不在依赖城镇服。
在公司内部的分享会上有说,其实一些通用的服完全可以交给运维去做,比如聊天服,好友服和比赛日志服,另外我们的好友功能基本上不是独立功能,要依赖于公司的平台,这样能方便公司的平台账号部门管理运营。当然这个部分并不是我们社畜所需要考虑的,有当然是极好的,没有也无所谓。
养成预留参数的好习惯
并不是所有情况下都能热更代码的,特别是服务器热更,更一次用户掉百分之几,被产品姐姐们说的抬不起头来。一些代码还没有办法热更,比如数据库字段的添加(需要重启缓存)和策划表的热更(涉及到C#代码的苹果审核)。所以我们适时可以预留个备用的整型字段或者用一个不是太长的字符串来放杂项,这样新功能就不会再受到大版本的限制了。
想办法减少和策划的扯皮
很多时候策划给你什么你就去写什么,有的时候感觉是不对的但是还是硬着头皮写了,这明显是不对的。有些可有可无或者有争议的地方,该说就得说,因为这东西放出去最后会一直被玩家喷,然后客服各种收到玩家的熟悉的建议,最后还是得改,还不如一开始就改。同时记得冰山原则,策划给到程序的哪怕是一个很简单的想法,程序给到策划完成的功能,也大概率是把每种方案都实现了让策划去选、去调。所以玩家玩到的东西也是策划思考的可能1/5,然后策划思考的东西也可能只有程序的1/3,程序得缝缝补补把策划方案的遗漏全部补起来,然后「尽可能给上策划足够的自由度」,能够让策划动态配的东西全部加到表里去,这样对他也好,对你也好。
参考内容
我自己,一年半的分布式服务器开发者和写了半年的U3D客户端(重合), C++ Lua NodeJS