九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

小小游戏程序员四个月工作总结

分 3 部分写,工作内容及其解释,实际我做的事,我获得的经验。

1、windows下的错误捕获及Dump。(Error report and dump)

内容:事实上是程序未处理异常的捕获及minidump。

我做的事情:这是我在公司除了看代码干的第一件事。总监很给时间。让我用大半天的时间看完了SEH(windows结构化异常)那3章的内容,受益匪浅。其次让我分析了C++函数调用的汇编代码,去了解为什么core dump能实现及其原理。然后再让我仔细的去分析了breakpad(一个google的开源项目)的源代码,了解了其在windows下的实现原理,然后才让我开始写。期间我主要做的事情就是写文档与跟总监讲解这些过程的原理,总监完成所有实际的代码编写。最后由我来修改了一个breakpad本身不支持Unicode导致程序中出现的一个bug。然后我开始写的是dump的提交系统,就是程序异常退出时那个问你要不要提交dump交由程序人员分析的哪个对话框,才写到一半(刚出了个对话框)就开始转入下一个工作,本来说以后回来再做,但是直到今天也没有机会再回来完成。实际完成时间一周左右。

经验:光这个小事情,我懂了一大堆东西,从windows SEH到函数调用,dump的机制,到dump文件的使用。更是对一书的价值深有体会,可惜当时各大网站都没有卖,到了几个月后才知道原来是新的第4版出来了,老版断货。当然,现在已经买了。

2、windows&Linux下的底层文件系统(FileSystem):

内容:事实上这个文件系统牵扯很多,主要包括一个类似MPQ的文件打包系统将大量的小文件全部打包成一个大文件,并通过哈希表,块表来索引,以减少文件的I/O时间,并方便统一的加解密。

我做的事情:最开始用了半周的时间去弄清楚了MPQ的源代码,不得不佩服一下一文(一书?)的作者Justin Olbrantz(Quantam),他是一个伟大的人物,能够从暴雪的strom。dll文件及一堆加密二进制数据中中分析出整个MPQ的格式及操作方式,不得不佩服,甚至连暴雪的哈希算法,加密算法都通过strom。dll文件推出来了。然后通过暴雪的地图编辑器解析了MPQ文件的写的方法,除了自叹不如,没有其他办法。光弄清楚MPQ的原理还不行啊,自己取其精华,弄了一个简化版,但是功能绝对不弱,就最后的测试数据来看,比开源的stromlib还要略高。中间的过程要说一下,这个打包格式让我深刻认识了所有的数据都不过就是二进制代码,中途第一次开始正式的写概要设计文档和详细设计文档,最绝的是最开始什么都不懂,拿着自己对mpq的分析就当概要设计去老总那儿开会去了,结果被狠狠的批了一顿,这时才知道什么叫设计,什么叫前期分析,什么叫需求。这里还要一提的就是总监在这个时候主动承担了责任,说他任务没有分配清楚,我一直感激不尽。打包设计文档在最后反复的进行了修改,比如添加和改变了filelist(文件名列表)的存储方式,为文件的快速比较添加了文件最后修改时间,CRC32值,md5值等信息。还修改了一次这些信息的存储方式。最后还为filelist添加了预留空间的功能。再后来,做补丁更新的时候,为了达到中途断电,下次还能正常进行更新的效果,为其添加了修改即建立备份文件的机制。再再后来,由于游戏加入了声音,需要新建一个独立的线程,导致了一个调试到吐血的bug,最后才知道是多线程引起的,然后为其加入了线程安全的机制。这个工作之所以说这么多,是因为我工作4个月以来,最主要的成果和工作内容就是对此打包格式的设计及不断的修改,它也是如此的重要,支撑着整个游戏的文件读取写入更新等内容。打包格式完成以后,写了对此打包格式的上层封装代码,即filesystem的接口层。这一层的普通文件读取工作主要由总监完成了,我完成的是打包格式读取那一部分的代码和后期bug的调试工作。然后完成了文件序列化(FileSerialize)类写入部分的编码工作和后期的调试工作。其中读取部分的工作也是总监做的。然后我完成了整个文件系统(包括打包系统)向Linux的移植工作,中间写了一大堆的移植支持代码,这里说明一下,公司的要求是一套代码两边运行,所以大量的宏定义避免不了。还好公司以前有些代码已经做了相关工作,不然还是个大任务。

整个文件系统差不多也就完成了。中间值得一提的就是windows本身的文件读取机制很奇怪。读取普通文件的效率起伏很大,有时很快,有时慢的要死,而且切换目录对于文件读取的效率有很大的影响。并且出人意料的是一旦你用了seek,那么整个文件的读取效率都会奇低,这是我开始没有注意到而导致序列化速度奇慢的原因。

经验:二进制数据,计算机的所有数据不就是二进制的数据吗?没有什么大不了的,经过文件系统的设计及实现,深深的懂得了什么叫关心效率,什么叫代码的健壮。听了太多的现在计算机速度越来越快不需要关心效率的话,在这里完全都不适用,不关心效率,还做这么多工作干什么??

3、文件更新包

内容:实际包括普通文件的比较更新和打包格式文件的比较和更新。更新包(patch)的制作,自解压运行模块。

我做的工作:更新包(patch)格式的设计及编码,自解压运行模块的设计及编码。因为打包系统里面有比较包的接口,所以我做的主要就是patch包的格式设计了。本来想套用打包系统的,后来因为老总想让此系统更加通用,被老总否定。于是重新设计了一套文件系统,包括索引表和文件内容,相当于一个简单的打包系统了。说明一下的是我仅确定了patch的格式和提供了patch制作的接口,实际的patch制作过程由工具组的兄弟调用接口完成。而自解压模块有开源代码,我实际也仅仅做了点修补工作。实际工作1周左右。

经验:又见二进制数据,又一个文件格式,在制作patch时用了序列化函数,自己写了一套内存序列化的类。使用起来的确方便。比起以前的打包格式中全靠底层接口来write,read要方便许多,对序列化的理解深入了不少。不过这里有个疑问就是公司的序列化为什么不用C++的stream系统来完成,而是全用C语言那一套,靠函数而不靠重载«操作符呢?我个人感觉上,«,»操作和序列化就类似于序列化,也可能是从序列化发展过来的。

4、游戏界面(gameui)相关

内容:为游戏制作一个与游戏主窗口并行的外部聊天的窗口,和让游戏最小化到系统图标(TrayIcon)的功能。TrayIcon状态还能出现相应的提示信息。

我做的工作:其实不多,外部聊天窗口就是利用交换链来渲染,共用游戏中窗口的XML配置文件来确定框架。TrayIcon状态完成也很容易,难得就是提示消息窗口的出现和消息的响应。和正确的显示半透明的PNG图。为了达到需求还得要求点击提示窗口确定位置,游戏中有相应动作的发生。实际工作完成用了近两周。

经验:了解了公司游戏的框架,包括gameui,消息订阅模式的使用等。最主要的了解来自于让3个消息提示窗口正常的响应消息。要知道,这些全部要求用win32程序完成,不是MFC,所以消息的响应全部靠我自己维护的外部的map完成,深刻的理解了总监所说的MFC消息分配发送机制。

5、游戏性能的优化相关

内容:分析游戏中显示tooltips速度过慢的原因,分析对比3种XML解析器的效率,并确定使用哪一种XML解析器。(tinyXML,expatXML,XerceXML)

我做的工作:分析tooltips为什么速度过慢,最简单不过了,不过是大片的插入时间检验,最后得出结果。最后对游戏中底层分配创建框架窗口的方式进行了优化。原程序是大面积的if else string对比,优化成类工厂创建方式。效率提高不少。接着分析XML解析器,这可是个难工作,因为我不仅要分析对比他们的效率都要求放到游戏中实际的检测,所以我还得负责写此XML解析器在游戏中使用的接口,另外还由于游戏中现有的XML解析文件都是utf8的,还要求我换到unicode去,最后还需要了解每个XML解析器的接口,然后测效率。有意思的是,tinyXML(游戏中现有XML解析器)写出来的XML文件有的地方不够规范,其他两个XML解析器不能正常解析,还是我手动调整了不规范的地方(主要是少空格)然后才能正常解析。这些工作都花了我1周多的时间。

经验:检验程序效率的方法,实践中体会2-8原理,在实践中应用工厂模式,体会其在C++中的使用方式(太多的宏),在实践中进一步确定了重复工作时,正则表达式非常复杂替换的应用效果。这次有近80个类,不是靠替换,我会吐血的。了解XML文件解析器的接口,了解了XML文件解析器的一些相关信息,现在忘了不少-_-!。

6、游戏的脚本系统(lua)

内容:为游戏中添加新的脚本系统,了解脚本系统在游戏中的应用,并设法为游戏UI编辑器添加脚本管理功能。

我做的工作:了解脚本系统与游戏现有内容的结合接口设计。理解lua代码中C++程序的运行时调用。(利用tolua++)。学习了lua语言:)。用了一周多的时间其中有3天公司什么工作都没有给我分配,就让我好好的学lua。。。。太感激了最后这个工作做了一周多就转向下一项工作,这个工作没有任何成果就废弃。

经验:真正的算是了解了一门脚本语言lua,对lua的嵌入式应用感受颇深。为了达到这个目的,lua的设计之简洁让我佩服不已,使用起来的确非常方便,除了与C语言的调用接口虚拟堆栈有点复杂外,lua为C语言设计库,C语言为lua设计模块都是非常之简单。让我对脚本语言产生兴趣,开始重拾python。边看边实践的看完了整个一本,呵呵,巴西人也真挺强啊。

7、游戏日志服务器

内容:将游戏中需要记录的所有事件全部写入mysql数据库。

我做的工作:首先熟悉了现有的ODBC框架,并将其应用于mysql数据库,后又因为ODBC效率偏低而改为纯mysql C API的调用。仿照原有ODBC框架,写mysql CAPI的封装,用了两层连接池以支持多数据库,多连接。以udp通信收包,收到即写入mysql数据库。根据需求,设计了5个通讯包,在公司原有的服务器框架上,构建日志服务器。并写了一个自动创建相关库的辅助程序。实际现在正在做的工作。

经验:进一步的了解了网络及多线程编程。重新熟悉了SQL语句(中途学过n次,因为工作中不用又忘了n次)。了解了mysql这个数据库的使用及其API。了解了公司的服务器框架,(真是精巧啊)。更加感激这个实际上有正则表达式和vim这个东西,没有他们,60多个字段的包会将我写崩溃的。

其他:

  1. 因为从lua产生的兴趣,开始回顾以前学习的python,并开始认真的对待。以前的确学过一些时间,但是因为后来还是去学C++了,没有太在意。现在重新学习的感觉是python的库实在是#@%#@$%$#@^丰富的太夸张了。

  2. 因为学习lua没有太好用的编辑器,重拾vim,第一次用vim是在大学用linux的时候,学了一下有个初步印象。没有好好用。后来再学vim是后来学win32汇编的时候的确没有合适的编辑器可以用,当时想到的就是使用vim这个万能编辑器。那时才算能将vim做一般的编辑器来用的水平了。后来在做filesystem到linux的移植的时候,又重新回顾了vim的一些用法,到了写lua程序的时候,已经驾轻就熟了,vim的确属于c++一类,没有学会百般难用,学会以后受用无穷。

这里特别谈一点我感觉特别受用的vim的使用经验: :)

1. u转换小写,U转换大小特别好用,普通模式配合g来使用
2. CTRL-A,CTRL-X配合q的记录功能简直可以实现你想要的任何复杂重复性操作。
3. vim有很大一组命名的寄存器,你可以指定使用。用@
4. 在windows下希望能和外部的CTRL-C,CTRL-V共用剪贴板,使用设置
set clipborad=unnamed。
5. map一族学会怎么使用以后,你可以实现你想要的n多功能,比如我就喜欢普通模式下enter,backspace,space按键不仅仅是移动,也是和插入模式一样的操作。那么可以这样:

" 添加空格
nmap <Space> i<Space><Esc>l

" back删除
nmap <BS> i<BS><Esc>l

" 回车向下移动
nmap <CR> i<CR><Esc>

我还喜欢能有个全部选择的功能,可以这样:
" 全选
nmap <C-Q> ggVG

6. << >> 操作很好用:)特别对于python这种不能自动排版的语言来说。
好了,其他的就不多讲了,自己体会体会吧:)
  1. 正则表达式的应用我可以说是感觉非常之好,可以实现很多很多你本来需要大量重复性机械运动才能实现的功能。(以下以VS2005windows的语法为准,并且我仅仅是回顾和介绍,不一定还准确。)简单的说一下,比如我有一次忘了在所有的日志输出后面加\n表示换行,一个一个寻找那可就费事了。可以这样:用{LOG("。#}{");\n}查找,用\1\n\2匹配,就相当于在最后的”前加了一个\n符号,这样你先替换一两个看看对不对,直接就可以来个全局的替换,很有效率的完成了需要大量且容易遗漏的操作。

再说个更复杂的例子,这个例子中我综合了vim和正则表达时的使用:

比如:在确定包的时候,有60多个字段,我需要绑定60多个参数才能完成一个SQL语句,大量的操作都是bind[n]。xx = yy;类似的操作,但是每个n,xx,yy都需要你手工填写,因为都是不一样的。n的自增长,你可以通过C++语言的i++来代替,但是那会带来不直观的效果,因为你编写代码时无法与相应的字段一一对应。手工填写每个n,写60多次,会很痛苦,这里用vim的q记录,下一n行,CTRL-A增长1,q结束。[count]@执行那么多次,一次完成:)至于后面的xxx是包中的一个字段,可以通过正则表达式的匹配来完成,首先我利用的是生成表的SQL语句,我当时写的时候是一行一个字段,去掉CREATE TABLE等头尾字段,类似于

ValueLv TINY NOT NULL,

ItemID INT NOT NULL,

以mySQL绑定参数的语句为例:

最主要的是类似下面的两条:

bind[n]。buffer_type= MYSQL_TYPE_LONG;

bind[n]。buffer= (char *)&int_data;

首先类型,类型这一句可以通过查找TINY,INT这个字段来匹配。

其次int_data这个,需要的是包中的实际一个字段,这里我投机取巧的地方在于

表名正好就是包中的去了前缀mi的实际成员变量名。

于是可以通过

{:w+} {:w+} NOT NULL,\n

来匹配一行SQL语句,mi\1就是变量名,由于INT在buffer_type中叫LONG,所以只

能分两次来生成,首先找到所有的TINY,再找到全部的INT。类似下面这样

匹配 {:w+} TINY NOT NULL,\n

替换: bind[n]。buffer_type = MYSQL_TYPE_TINY;

bind[n]。buffer = (char *)&mi\1;

一个全局替换以后,所有TINY类型的SQL语句全部编程你想要的参数绑定语句了,

INT和其他类型操作类似:),当参数的确多的时候,比如我需要操作的是100多个

字段,那这样的操作简化的可就不是一点点啊:)。

  1. 看完了原版的<Effective C++>,真的感触挺多,的确感觉自己对c++的理解上升了一个层次,感受最多的是4,5,6三章,基本上自己慢慢去适应Meyers提出的建议,在碰到一次析构函数没有正常调用时,第一时间反应正确,基类没有虚析构。。。。

  2. 看了半本booch的<面向对象分析与设计>,感觉自己还没有到工程领域高度那个阶段。。。。放弃。本人目前毕竟还是个单纯,简单的IT工人,小程序员而已。其实不是不想看。。。学术性太强,看的摸不找边际。

再其他:

  1. 老总要的是什么?大局观,思路清晰。不得不说一下,我们老总的技术实力实在是够强的,从服务器到客户端无所不管,无所不通,而且无所不思路比我们清晰。他说的做事要讲究方法,首先要思路清晰的教训一直牢记脑海,在公司,我感觉自己更像是在学校,做个学习的学生而已。

  2. 程序员是什么?要先满足需求(游戏里面是策划),还要围着运维转。的确是,老总说了原因,程序的开发就算有难度,但是开发就是一下的事,开发完了就算完了,而别人的使用是长时间的,有一点点别扭也是别扭很长时间,累计起来就是很不方便。不过我确得承认老总的话的确是对的,难得是其作为研发的头还能有这样的认识。自叹不如。

  3. 关于策划,常听策划的头教训手下,本人似乎也挺受教。。。。呵呵,还是思路问题,策划也需要思路,不仅仅是文字工作而已。”人物的血加了,怪不加?像话?一开始的引导让人卡了,怎么做的?”游戏任务的引导是此时我才有了清晰的认识。以前光就玩,从来没有从开发层次来想过。

“开始应该让人上手啊,难的东西给高级玩家去玩,不能一开始就难倒普通玩家。”

真理。。。。。

“策划为主还是美工为主?怎么能因为美工这样画的,策划就去迁就美工?”

这才叫真正的思路。。。。与老总的运维,策划为主如出一辙。。。。。

够了。。。。等再过几个月再来总结吧。。。。。呵呵

分类:  随笔 
标签:  工作总结 

By 九天雁翎

2008年08月31日 | 九天雁翎的博客

前一篇: lua table输出函数(可以输出嵌套表格) 后一篇: 工作和学习就是不一样啊