忙中偷闲,经过几天的努力,将lua脚本嵌入到系统中。之前公司的时候,偌大一个服务器全部使用C++(www.cppentry.com)编写,对于新手经常发生一些宕机事件,被主程责骂。在后来接触的一些人中,发现很多公,都已经引入lua来适应多变的环境和敏捷开发!正如一个主程所说的,在n年前网易已经脚本为王了,现在很多公司拿着C++(www.cppentry.com)不放,作为开发人员不苦逼才怪! 想想在广州开发游戏的日子,每次在群里面看到运维说某某服务器上面有coredump文件时,总是惊出冷汗,赶紧用gdb去查询,是哪行代码引起的宕机;还要应对主程的责骂!其实不仅是程序,就是策划也经常因为配置文件的问题引起宕机!引入lua,不仅是为了程序开发的方便,更是为了应对苛刻的运营商,新手的逻辑Bug等。在这些方面lua所表现出来的优异,自是不必说。再加上其小巧灵活,嵌入方便,用来嵌入到游戏服务器之中,处理逻辑实在是太方面了!
在底层通讯方面还是传统的C++(www.cppentry.com) 和 epoll 或者iocp ,在成功接收到客户端的消息之后,按照协议将其封装成MessageBlock的结构(关于MessageBlock ,可以参考我之前的博客 http://www.cnblogs.com/archy_yu/archive/2012/09/07/2674909.html ),传递给逻辑线程,逻辑线程,在消息队列里面取消息,然后处理,引入lua之后,就是要将这个MessageBlock类的对象传递给lua,让lua来处理!那么好,我们要将必要的接口暴漏给lua,为了方便在lua 和 C++(www.cppentry.com)之间协调,我们引入lua_tinker库
为了将接口暴漏给lua,我们在类MessageBlock 中添加了一个新的成员函数!如下面的代码:
class MessageBlock{ ... /// int write_char(const char value); int write_short(const short value); int write_int(const int value); int write_string(const char* ss); char read_char(); short read_short(); int read_int(); char* read_string();}int MessageBlock::write_char(const char value){ (*this) << value; return 0;}int MessageBlock::write_short(const short value){ (*this) << value; return 0;}int MessageBlock::write_int(const int value){ (*this) << value; return 0;}int MessageBlock::write_string(const char* ss){ (*this) << ss; return 0;}char MessageBlock::read_char(){ char a; (*this) >> a; return a;}short MessageBlock::read_short(){ short s=0; (*this) >> s; return s;}int MessageBlock::read_int(){ int i=0; (*this) >> i; return i;}char* MessageBlock::read_string(){ static char ss[150]; (*this) >> ss; return ss;}
之前数据的拼接和取值,我都是重载了操作符,为了配合lua的调用,这里添加上述接口! 我们通过使用lua_tinker将这些接口暴漏给Lua。关于如何引入lua_tinker和lua_tinker的介绍请参考百度,或者联系作者!
extern "C"{ #include "lua.h" #include "lualib.h" #include "lauxlib.h"};#include "lua_tinker.h"//包含必要的头文件int GameMonitor::lua_init(){ this->L = lua_open(); //函数是用于打开Lua中的所有标准库,如io库、string库等。 luaopen_base(this->L); // luaopen_string(this->L); return 0;}int GameMonitor::lua_class(){ //注册MessageBlock 到lua lua_tinker::class_add<MessageBlock>(this->L,"MessageBlock"); return 0;}int GameMonitor::lua_method(){ //注册MessageBlock 的构造函数等lua_tinker::class_con<MessageBlock>(this->L,lua_tinker::constructor<MessageBlock,int>); lua_tinker::class_def<MessageBlock>(this->L,"write_int",&MessageBlock::write_int); lua_tinker::class_def<MessageBlock>(this->L,"read_int",&MessageBlock::read_int); lua_tinker::class_def<MessageBlock>(this->L,"write_string",&MessageBlock::write_string); lua_tinker::class_def<MessageBlock>(this->L,"read_string",&MessageBlock::read_string); lua_tinker::def(this->L, "peek_msg", peek_msg);
lua_tinker::def(this->L, "respond_to_client",respond_to_client);
return 0;}int GameMonitor::lua_member(){ //如需要,注册一些成员 return 0;}int GameMonitor::lua_batfile(){ //运行脚本 进入主逻辑循环 lua_tinker::dofile(this->L,"LuaScript/MsgPrc.lua"); return 0;}
如此,我们就可以像这样来处理消息,如下面的代码(MsgPrc.lua):
--msg = MessageBlock(1024)----msg:write_int(10)----print (msg:read_int())----a = "sdsdcds"----msg:write_string(a);----print (msg:read_string())local s_msghead = {}local msg
--主逻辑,死循环,用来处理消息local function run_logic() while true do msg = peek_msg() if msg == nil then sleep(1) else process_msg(msg) end endendlocal function process_msg(msg) msg_head = msg:read_int() if s_msghead[msg_head] then s_msghead[msg_head](msg) end end
--在此注册消息处理逻辑local function init_msghead() s_msghead[10001] = process_10001 s_msghead[10002] = process_10002endlocal function process_10002(msg) msg.read_int(a); msg.read_string(b); --process remsg = MessageBlock(50); remsg.write_int(100) remsg.write_string("ok"); respond_to_client(remsg); end--process_10001init_msghead()run_logic() 这样就实现了用lua来处理所有的逻辑!
我们调整一下代码逻辑,就可以实现诸如热更新的功能,来应对日益激烈的页游山寨竞争!之前就有听说过,说为什么搞IT的人看起来都很闲,说运维总是在重启,web前端开发者总是在上传,C++(www.cppentry.com)开发者总是在编译。因为语言特性,C++(www.cppentry.com)开发者总要面临编译的问题,对于服务器代码,如果全是C++(www.cppentry.com)来编写,必然也是要面对这个问题,据说之前公司一个团队的后端代码要编译三个小时才能编译出可执行文件来。用lua来写主逻辑,其他的底层代码用库封装,岂不是又解决了一个问题!
欢迎讨论!!!