C++经验
经验1.1 NULL安全
服务器是一个非常关注数据安全的东西,尤其对于C/C++
程序来说,空指针和野指针是一个非常重要且一定要避免的问题,与之同级的还有数组越界的问题。
经验1.2 善用unsigned long long int
当年OI的经验告诉我们u64
可以存很大的数,于是这个类型就用来解决了算法结束后答案过大导致int32溢出的问题,但没想到,这个东西用来当“身份证”有奇效!
我们的游戏中涉及大量的元素,包括但不限于大量的NPC,大量的同时在线的玩家,大量的建筑,大量的怪物……各种各样不同的元素。游戏将所有的实体都使用一个u64 rid
作为唯一标记,通过哈希函数和哈希表实现rid查万物。
经验1.3 善用Enum
枚举类型Enum在C++中经常当做一个常数来使用,在一些只给程序员使用的配置项,比如数组长度,请求超时,资源类型,使用这个可以避开污染Define、全局变量的问题。
经验1.4 善用do while()
do while()
在学习过程中作为一种很冷门的循环语法其实一直不被重视,但是实际工作中,do while()在优化代码可读性方面有奇效。
借助它一定运行一次的特性我们可以用do while(0)
使代码只运行一次,在其中使用break来中断循环,以此起到一种平替inline function
的用法。
借助它一定运行一次的特性我们可以用do while(1)
使代码反复运行,在其中使用break来中断循环,以此起到一种“输入数据”不合规则重新输入的功能。
工程经验
经验2.1 性能和易读性
在开发过程当中,我们经常会遇到代码性能和代码可读性的矛盾,在某些时候我们只能选择其一,所以在代码性能和代码可读性的平衡中,我们有以下原则:
“当一件事情没有达到性能瓶颈时,就不要去考虑性能问题,易读性优先。”
经验2.2 代码生成
在很多情况下,我们的需求其实都是简单且重复的,比如对某一些变量的增删改查,那么对于这种类型的需求,其实我们可以采用代码生成的方式来解决,通过简单的配置,直接生成对应的代码,能够极大地提高开发效率。
代码生成的原理很简单,通过一段配置,结合相应模板就能生成一份代码,但是代码生成的细节很值得深入研究,例如怎么设计模板代码,怎么保证系统的可扩展性等。
经验2.3 表达式系统
所谓表达式系统,其实是一种脚本语言,它由关键字-函数-公式
构成。在游戏开发中,策划决定表达式内容,程序决定表达式运行时机,以及表达式中支持的函数和公式。之所以不使用Lua转而构建一个自己的系统,是因为Lua本身的运行速度较慢,而表达式则是基于C++实现的一种方便调用C++函数的脚本,因此运行速度要比Lua快的多,常用在PVP战斗系统当中。
经验2.4 版本管理
其实版本管理不论对于什么体量的团队来说都是一个难题,我们没法用程序去强行限制人的行为,所以团队内部需要形成一些常见的规矩,所有人都“原则上”遵守这个规矩办事,就能避免很多问题了。
对于一个多人协作的项目而言,开发阶段一定会涉及到多端的协同问题,客户端有客户端的需求,策划有策划的想法,服务器又有服务器的实现方式,因此我们需要形成一些内部统一的工作流,并准备好备用方案,每个部门各自维护自己的版本,版本分为若干类,并交由版本管理工具来管理。
在我实习的单位中,服务器组使用的是下面这样的版本管理方案:
服务器组的代码使用SVN进行维护,没有像Git那么多的分支,因为服务器组人数不多,所以大家都在同一个分支上进行开发,因为很多代码都基于代码生成工具生成,因此svn上只保存了生成代码用到的配置文件,而生成的代码并不由svn管理,合并冲突也会少很多。
我们把svn的版本称为开发版,不对客户端和策划公开;但是对于客户端和策划来说他们又需要私服进行开发调试,那么服务器组就需要每过一段时间发布一个新版本,这个更新频率非常快,有时版本构建机都需要排队使用,依次构建,构建成功并上传至版本库之后,面向客户端和策划开放,他们可以将自己的私服更新到这个版本,用来开发调试。
相应的客户端也是一样,组内的版本管理工具保证开发者的代码处于最新状态,构建开发版并公开给其他组保证其他组能第一时间进行开发调试。