其中game工程生成的库文件game.lib居然有近四百兆之巨,mangosd和realmd在连接这个库的时候也要花上好长一段时间。当然,mangos现在的代码量也确实不少了,这也就要考虑到大的工程项目的源代码管理及模块工程划分,至少,像mangos现在这样,代码全部放入game目录中,并且就做为一个大的工程的方式,其弊端是已经显现了。另外还有功能模块划分的问题,需要找一个功能的实现时,不知道该到哪块代码去找,而要扩展某部分功能时,也是无从下手。
模块划分使用最广泛也是最容易实现的应该算是按接口编程了,实现的方法不用我多说,程序员都知道。其好处也是显而易见的,定义了接口之后,接口的实现便可以作为一个独立模块,也就可以单独为一个工程了。
可以拿mangos处理玩家登录的过程来做个比方,现在mangos的做法是一个很长的顺序执行的过程,如果玩家在队伍中,则向队友发送上线消息,如果玩家有好友,则向好友发送上线消息,如果玩家有公会,则向会员发送上线消息,等等。这些代码都是直接调用各功能部分的代码来实现,如果这里考虑一下模块划分,并定义出相应的接口,那就可以改成调用好友模块的上线处理接口,调用组队模块的上线处理接口,调用会会模块的上线处理接口,等等。
有了接口后,这几个模块可以在另外的工程中实现,不用再混在game工程中。对象通过定义好的接口来调用,这样只要没有改动接口,模块的实现修改都不会影响到game工程。
还可以再进一步,在游戏逻辑的处理上再做一些解耦合。还是上面这个例子,玩家在登录时,先调用组队模块接口,再调用好友模块接口,再调用公会模块接口......这些顺序的执行过程将这些模块紧紧地耦合在了一起,当游戏逻辑变得越来越复杂时,类似的接口及调用数量会呈爆炸式的增长,这也将会成为另一个巨大的问题。
一个可行的方法是使用被称作事件或者信号的对象来实现解耦合。仍然拿上面的例子来说,当玩家登录成功时,玩家对象发出一个“玩家已登录”的事件或者信号,对此事件感兴趣的模块,会响应这个事件并且做出相应的逻辑处理,具体来说就是好友模块会向该玩家的好友广播上线消息,组队模块会向该玩家的队友广播上线消息,公会模块会向该玩家所在的公会广播会员上线消息,等等。注册感兴趣的事件及响应事件的处理过程都是在各独立模块内部完成,玩家对象本身并不知道也不需要知道有这么些过程。这样,想要删除或者扩展功能就比较的方便了。
sigslot这个开源库就提供了我们所要的这项功能。在玩家对象内部定义一个Signal对象,功能模块从has_slot派生,并且将自己连接到玩家对象的signal对象上,这样当玩家对象的signal对象被emit时便会调用到该模块内。在收到这个信号时你可能还需要一些参数,至少应该知道到底是谁登录了吧,没关系,signal中可以带任意多个参数,完全由你来控制,但遗憾的是他的slot不支持返回值。如果你不能容忍这样大的一个功能缺失的话,boost::signal或许可以满足你的要求,但太过于复杂的东西我一向不大喜欢,boost就属于这一类,虽然他非常的强大。
还有一个可考虑的选择是FastDelegate,不过你得自己做一些封装才能实现我们上面提到的类似功能。虽然FastDelegate基本上只是实现了一个安全的回调函数的功能,但是自己封装出来的东西或许更适合你的需求,也可以试一试。