在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
以下内容转载自 http://blog.csdn.net/lalate/article/details/51498869
本文所述内容,并不涉及服务器集群的进程划分与拓扑结构. - router: 数据转发,多进程按负载分担,支持点对点,广播,主从,哈希等几种常见的数据转发逻辑. - gamesvr: 提供客户端接入逻辑,以及常规的游戏逻辑(如道具,商城,等等...), 多实例按负载分担. - dbagent: 提供数据库访问服务,多进程按哈希分布. - matchsvr: 提供战斗匹配服务,多进程主从备份. - 其他服务进程,不再列举. 本文所述框架以C++为基础层,以lua为业务层,旨在解决以下3个问题. 2.1 如何方便的在进程之间通信?
假想一个情景:我们要从gamesvr向matchsvr发一个请求,将一个玩家队伍加入匹配队列. 在协议描述的XML中定义这个消息结构,呃,可能还要嵌套结构. 转换XML,生成对应的.h,.c,.tdr之类的. 在gamesvr中编写发送消息代码. 在matchsvr编写消息处理逻辑代码. 呃,对了,可能还要在派发消息的地方注册一下消息
而上面这些,跟业务逻辑有关的,其实只有3,4,其他都是累赘.
local mode = 3; -- 3v3 local team = {...}; --队伍成员列表 local robot = false; --从gamesvr调用matchsvr的消息函数: OnMatchRequest remote.CallMatchSvr("OnMatchRequest", app.iId, mode, robot, team);
下面为matchsvr的响应代码: --远程调用OnMatchRequest的实现 --约定所有远程调用都必须定义在全局表s2s中 --s2s含义为: server to server function s2s.OnMatchRequest(svr, mode, robot, team) -- 加入匹配池... end 2.2 如何使得我们的开发过程更加顺畅,运维响应更加及时. 2.2.1 开发过程 继续上面的2.1节的场景,在传统的C++实现中,想想,程序员写完两边的消息代码,要继续干什么? 1. 关掉服务器,嗯,如果共用服务器,还得吼一下: 我要关服了. 2. make ... 3. 启动服务器. 4. 呃,客户端联调的兄弟,你重新登录一下,对了,记得要开几个客户端重新组队哦.
真繁琐啊,能不能简单点? 2.2.2 运维事件
假定现在运营环境发现一个严重Bug,而我们知道只要简单的改一行代码就好了. 2.3 如何彻底摆脱空指针,野指针,内存越界等顽疾,提供更加稳定的服务?
嗯,这个就属于lua语言本身的特性了. 3. 历史
lua在游戏领域的应用,大概是从<魔兽世界>火起来的. - 远程调用: 从此摆脱C++层面的协议定义,数据组织编码,编译更新等繁琐的过程. - 数据存储: C++层面无需关心数据存储结构,无需再写大量的DB操作代码(MySQL);
其实这两个点都是基于lua序列化&反序列化的. - <剑网三>的服务端属于计算密集型(3D场景逻辑,战斗技能,AI,等等). - 作为笔者入行的第一个项目,出于对性能的谨慎,限制了lua在服务端的应用广度.
2011年初,我离开服务了6年多的<剑网三>团队,出去创业.
2015年的春天,怀着对创业的绝望,我来到了腾讯. 4. 技术基础 4.1 lua的C++绑定 4.1.1 实现原理 1. 为每个导出的class创建了一个table(lazy模式), 其中存放了class的成员函数指针以及成员变量偏移. 2. 在上面的class专属table中,我们将表中的__index, __newindex指向我们的定制的C++函数. 3. 对象首次被push到lua时,会创建一个table与之绑定,称为影子对象,该table中记录了对象指针,并以第1步的table作为其元表. 4. 当在脚本中通过影子对象访问C++对象的成员时,通过元表的__index, __newindex方法定向到C++对象成员. 5. C++对象上也记录了影子对象的引用,在对象析构的时候将清除影子对象中存放的指针. 4.1.2 C++对象导出示例 .h 中的class 声明代码: // class 声明中需要插入一行 DECLARE_LUA_CLASS
class CPlayer char m_szName[32]; int m_iLevel; int luaSay(lua_State* L); DECLARE_LUA_CLASS(CPlayer); // 声明导出CPlayer类 };
.cpp 中的实现代码: // 在 CPP 中增加如下的导出声明 IMPL_LUA_CLASS_BEGIN(CPlayer) EXPORT_LUA_STRING(m_szName) EXPORT_LUA_INT(m_iLevel) EXPORT_LUA_FUNCTION(luaSay) IMPL_LUA_CLASS_END()
int CPlayer::luaSay(lua_State* L) { // ... return 0; } 注意,这不是实际存在的代码;对于业务逻辑都在 lua 中实现的项目而言,真正需要导出的 C++ 代码极少. 4.1.3 主要特性 - 在lua中读写对象的C++成员变量(也可声明为只读). - 在lua中调用对象的C++成员函数. - 在lua中对影子对象添加新的"成员变量","成员函数". - 在lua中覆盖对象中导出的C++函数. - 在C++中调用影子对象上的lua函数. 4.1.4 实际使用示例 C++部分代码: 在玩家登陆时调用login.lua中定义的lua函数. void OnPlayerLogin(lua_State* L, int iConnIdx, CPlayer* player) { CSafeStack guard(L); // 除了获取文件中的函数,还有其他的相关的API // 用来获取影子对象上的函数,以及全局 table 中的函数等等 Lua_GetFileFunction(L, "login.lua", "OnPlayerLogin"); lua_pushinteger(L, iConnIdx); Lua_PushObject(L, player); Lua_XCall(L, 2, 0); }
lua部分代码: 响应上面C++代码触发的玩家登陆事件. function OnPlayerLogin(connIdx, player) --访问成员变量: 读 log_debug("player login, name="..player.szName); --访问成员变量: 写 player.iLevel = 1; --调用成员函数 player.Say("皇上吉祥"); --在player对象上加入新的函数/变量 player.OnExit = function() -- do something ! end end 4.1.5 另一个实现
关于lua的C++绑定,其实还有另一个基于C++14的实现(还在完善中,欢迎提意见:). 4.2 lua文件沙盒 4.2.1 代码示例 关键函数: import 在main.lua中import另外两个文件: a.lua, b.lua --main.lua a = import("a.lua"); b = import("b.lua");
print("a.txt="..a.txt); --输出: A a.txt = "X"; --修改 a 中的变量,不影响 b. print("b.txt="..b.txt); --输出: B
a.lua,注意它也import了b.lua,但是b.lua在main.lua中已经加载了,两处会引用同一份实例. txt = "A"; b = import("b.lua"); print("b.txt="..b.txt); --输出: B
|
请发表评论