忙中偷閑,經過幾天的努力,將lua指令碼嵌入到系統中。之前公司的時候,偌大一個伺服器全部使用C++編寫,對於新手經常發生一些宕機事件,被主程責罵。在後來接觸的一些人中,發現很多公,都已經引入lua來適應多變的環境和敏捷開發!正如一個主程所說的,在n年前網易已經指令碼為王了,現在很多公司拿著C++不放,作為開發人員不苦逼才怪! 想想在廣州開發遊戲的日子,每次在群裡面看到營運說某某伺服器上面有coredump檔案時,總是驚出冷汗,趕緊用gdb去查詢,是哪行代碼引起的宕機;還要應對主程的責罵!其實不僅是程式,就是策劃也經常因為設定檔的問題引起宕機!引入lua,不僅是為了程式開發的方便,更是為了應對苛刻的電訊廠商,新手的邏輯Bug等。在這些方面lua所表現出來的優異,自是不必說。再加上其小巧靈活,嵌入方便,用來嵌入到遊戲伺服器之中,處理邏輯實在是太方面了!
在底層通訊方面還是傳統的C++ 和 epoll 或者iocp ,在成功接收到用戶端的訊息之後,按照協議將其封裝成MessageBlock的結構(關於MessageBlock ,可以參考我之前的部落格 http://www.cnblogs.com/archy_yu/archive/2012/09/07/2674909.html ),傳遞給邏輯線程,邏輯線程,在訊息佇列裡面取訊息,然後處理,引入lua之後,就是要將這個MessageBlock類的對象傳遞給lua,讓lua來處理!那麼好,我們要將必要的介面暴漏給lua,為了方便在lua 和 C++之間協調,我們引入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 到lualua_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 domsg = peek_msg()if msg == nil thensleep(1)elseprocess_msg(msg)endendendlocal function process_msg(msg)msg_head = msg:read_int()if s_msghead[msg_head] thens_msghead[msg_head](msg)endend
--在此註冊訊息處理邏輯local function init_msghead()s_msghead[10001] = process_10001s_msghead[10002] = process_10002endlocal function process_10002(msg)msg.read_int(a);msg.read_string(b);--processremsg = MessageBlock(50);remsg.write_int(100)remsg.write_string("ok");respond_to_client(remsg);end--process_10001init_msghead()run_logic()
這樣就實現了用lua來處理所有的邏輯!
我們調整一下代碼邏輯,就可以實現諸如熱更新的功能,來應對日益激烈的頁遊山寨競爭!之前就有聽說過,說為什麼搞IT的人看起來都很閑,說營運總是在重啟,web前端開發人員總是在上傳,C++開發人員總是在編譯。因為語言特性,C++開發人員總要面臨編譯的問題,對於伺服器代碼,如果全是C++來編寫,必然也是要面對這個問題,據說之前公司一個團隊的後端代碼要編譯三個小時才能編譯出可執行檔來。用lua來寫主邏輯,其他的底層代碼用庫封裝,豈不是又解決了一個問題!
歡迎討論!!!