游戏服务器 每个连接 一个tolua luastatestate吗

一个简单的游戏服务器框架(二)
我的图书馆
一个简单的游戏服务器框架(二)
3397人阅读
& & & & 暴雪魔兽都是用Lua,我们毫无疑问的用lua了。在某知名游戏流出的源代码里面,google一下就可以找到,可以看到其采用windows服务器,代码里面能够发现大量的lua脚本,所以,这里,就说说怎样将lua脚本与我们的游戏结合,从而使我们能迅速配置一个游戏系统。
& & & &进入一个游戏,我们就看到场景,场景上的物件(包括玩家,npc,道具),我们的脚本配置差不多就针对这两个元素了。
& & & 一。场景
& & & 直白一点,就是一张地图和在这张地图上的一些属性、规则。那么我们针对任何一个场景,为场景提供在Lua里面调用的接口,那么,对任何一个场景上的规则,不需要我们程序员去动手写什么代码了,完全可以让策划去配置了,只要告诉他们接口就可以了。比如,一个场景的持续时间为1小时,那么我们提供一个actionTime(int)的接口,那么策划只需要通过这个接口,写上时间,那么这个场景就维持一个小时,如果这个场景作为一个副本使用,那么这个副本什么时候开启,里面有什么怪,这些怪死了有什么动作等等这些,完全不需要我们程序去干涉任何事情,他们自己去弄就行了,在游戏
我的世界 里玩家就可以自己搭建关卡这些,就像策划去配一个副本,场景是一样的。
& & &二。npc
& & &以设计AI为例,我们提供程序需要的所有AI接口,然后一个npc被打了该怎样,巡逻的时候该怎样,被点击该怎么样,与我们程序没关系了,他们自己去配置就可以了。同样,做一个任务系统,这个太庞大了,有时一个任务就有上万行代码了,实在是太复杂了,这就必须得节省我们程序的时间,搭建一个好的脚本框架,可以为我们的开发节省大量的时间,并且迅速配置出一个游戏系统,我想完美这方面就做得很好,他们的游戏产出很快。
& & 玩家的处理和上面也差不多了。
& &上面只是说了我们为什么要搭建好一个号的lua游戏脚本框架,下面就说说怎样做一个框架了,只能从自己的能力,自己的代码,来说一点了:
& &1.使用tolua++把我们c++代码导入到lua里面,这个不需要什么功夫,只需要编写自己的pkg文件就可以,在.h文件里使用tolua_begin tolua_end来标记需要导入的代码就可以了,这样我们需要导入到lua的类、函数、变量就可以在lua里面使用了;
& & 2.为了在lua里能使用c++里面的类,那么我们必须在lua中定义和类相同名字的table,使用这个和类同名的table来访问类中的成员函数、变量。
& & &问题出现了,在游戏里面那么多的npc,我们必须为每个id的npc指定一个唯一的table,然后调用到一个具体npc时,使用npc+id的方法来访问这一张表,我们必须为npc这个大类定义一张统一的table,定义里面的共同行为,当出现不同行为时,去找到这个npc在lua里面对应的table,调用里面的方法就可以了,同理,场景做相同的处理。
& &从这段时间写代码的经验来看,这个框架的c++代码还算比较好写,没什么难度,难的就是这部分lua代码,可能是自己初学lua,不是很了解,接下来自己就是写好这个lua框架代码了
馆藏&25726
TA的最新馆藏
喜欢该文的人也喜欢Lua 5.1 参考手册(六) - A闪网LinkCategory Lua 5.1 参考手册(六)云风几年前译的Lua 5.1文档,这里直接转载过来,方便记录。
3.7 - 函数和类型
在这里我们按字母次序列出了所有 C API 中的函数和类型。
typedef void * (*lua_Alloc) (void *ud,
void *ptr,
size_t osize,
size_t nsize);
Lua 状态机中使用的内存分配器函数的类型。 内存分配函数必须提供一个功能类似于 realloc 但又不完全相同的函数。 它的参数有 ud ,一个由 lua_newstate 传给它的指针; ptr ,一个指向已分配出来或是将被重新分配或是要释放的内存块指针; osize ,内存块原来的尺寸; nsize ,新内存块的尺寸。 如果且只有 osize 是零时,ptr 为 NULL 。 当 nsize 是零,分配器必须返回 NULL; 如果 osize 不是零,分配器应当释放掉 ptr 指向的内存块。 当 nsize 不是零,若分配器不能满足请求时,分配器返回 NULL 。 当 nsize 不是零而 osize 是零时,分配器应该和 malloc 有相同的行为。 当 nsize 和 osize 都不是零时,分配器则应和 realloc 保持一样的行为。 Lua 假设分配器在 osize &= nsize 时永远不会失败。
这里有一个简单的分配器函数的实现。 这个实现被放在补充库中,由 luaL_newstate 提供。
static void *l_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) {
/* not used */
if (nsize == 0) {
free(ptr);
return NULL;
return realloc(ptr, nsize);
这段代码假设 free(NULL) 啥也不影响,而且 realloc(NULL, size) 等价于 malloc(size)。 这两点是 ANSI C 保证的行为。
lua_atpanic
lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
设置一个新的 panic (恐慌) 函数,并返回前一个。
如果在保护环境之外发生了任何错误, Lua 就会调用一个 panic 函数,接着调用 exit(EXIT_FAILURE), 这样就开始退出宿主程序。 你的 panic 函数可以永远不返回(例如作一次长跳转)来避免程序退出。
panic 函数可以从栈顶取到出错信息。
void lua_call (lua_State *L, int nargs, int nresults);
调用一个函数。
要调用一个函数请遵循以下协议: 首先,要调用的函数应该被压入堆栈; 接着,把需要传递给这个函数的参数按正序压栈; 这是指第一个参数首先压栈。 最后调用一下 lua_call; nargs 是你压入堆栈的参数个数。 当函数调用完毕后,所有的参数以及函数本身都会出栈。 而函数的返回值这时则被压入堆栈。 返回值的个数将被调整为 nresults 个, 除非 nresults 被设置成 LUA_MULTRET。 在这种情况下,所有的返回值都被压入堆栈中。 Lua 会保证返回值都放入栈空间中。 函数返回值将按正序压栈(第一个返回值首先压栈), 因此在调用结束后,最后一个返回值将被放在栈顶。
被调用函数内发生的错误将(通过 longjmp)一直上抛。
下面的例子中,这行 Lua 代码等价于在宿主程序用 C 代码做一些工作:
a = f(&how&, t.x, 14)
这里是 C 里的代码:
lua_getfield(L, LUA_GLOBALSINDEX, &f&);
/* 将调用的函数 */
lua_pushstring(L, &how&);
/* 第一个参数 */
lua_getfield(L, LUA_GLOBALSINDEX, &t&);
/* table 的索引 */
lua_getfield(L, -1, &x&);
/* 压入 t.x 的值(第 2 个参数)*/
lua_remove(L, -2);
/* 从堆栈中移去 't' */
lua_pushinteger(L, 14);
/* 第 3 个参数 */
lua_call(L, 3, 1); /* 调用 'f',传入 3 个参数,并索取 1 个返回值 */
lua_setfield(L, LUA_GLOBALSINDEX, &a&);
/* 设置全局变量 'a' */
注意上面这段代码是“平衡”的: 到了最后,堆栈恢复成原由的配置。 这是一种良好的编程习惯。
lua_CFunction
typedef int (*lua_CFunction) (lua_State *L);
C 函数的类型。
为了正确的和 Lua 通讯,C 函数必须使用下列 定义了参数以及返回值传递方法的协议: C 函数通过 Lua 中的堆栈来接受参数,参数以正序入栈(第一个参数首先入栈)。 因此,当函数开始的时候, lua_gettop(L) 可以返回函数收到的参数个数。 第一个参数(如果有的话)在索引 1 的地方,而最后一个参数在索引 lua_gettop(L) 处。 当需要向 Lua 返回值的时候,C 函数只需要把它们以正序压到堆栈上(第一个返回值最先压入), 然后返回这些返回值的个数。 在这些返回值之下的,堆栈上的东西都会被 Lua 丢掉。 和 Lua 函数一样,从 Lua 中调用 C 函数也可以有很多返回值。
下面这个例子中的函数将接收若干数字参数,并返回它们的平均数与和:
static int foo (lua_State *L) {
int n = lua_gettop(L);
/* 参数的个数 */
lua_Number sum = 0;
for (i = 1; i &= i++) {
if (!lua_isnumber(L, i)) {
lua_pushstring(L, &incorrect argument&);
lua_error(L);
sum += lua_tonumber(L, i);
lua_pushnumber(L, sum/n);
/* 第一个返回值 */
lua_pushnumber(L, sum);
/* 第二个返回值 */
/* 返回值的个数 */
lua_checkstack
int lua_checkstack (lua_State *L, int extra);
确保堆栈上至少有 extra 个空位。 如果不能把堆栈扩展到相应的尺寸,函数返回 false 。 这个函数永远不会缩小堆栈; 如果堆栈已经比需要的大了,那么就放在那里不会产生变化。
void lua_close (lua_State *L);
销毁指定 Lua 状态机中的所有对象(如果有垃圾收集相关的元方法的话,会调用它们), 并且释放状态机中使用的所有动态内存。 在一些平台上,你可以不必调用这个函数, 因为当宿主程序结束的时候,所有的资源就自然被释放掉了。 另一方面,长期运行的程序,比如一个后台程序或是一个 web 服务器, 当不再需要它们的时候就应该释放掉相关状态机。这样可以避免状态机扩张的过大。
lua_concat
void lua_concat (lua_State *L, int n);
连接栈顶的 n 个值, 然后将这些值出栈,并把结果放在栈顶。 如果 n 为 1 ,结果就是一个字符串放在栈上(即,函数什么都不做); 如果 n 为 0 ,结果是一个空串。 连接依照 Lua 中创建语义完成(参见 §2.5.4 )。
lua_cpcall
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);
以保护模式调用 C 函数 func 。 func 只有能从堆栈上拿到一个参数,就是包含有 ud 的 light userdata。 当有错误时, lua_cpcall 返回和 lua_pcall 相同的错误代码, 并在栈顶留下错误对象; 否则它返回零,并不会修改堆栈。 所有从 func 内返回的值都会被扔掉。
lua_createtable
void lua_createtable (lua_State *L, int narr, int nrec);
创建一个新的空 table 压入堆栈。 这个新 table 将被预分配 narr 个元素的数组空间 以及 nrec 个元素的非数组空间。 当你明确知道表中需要多少个元素时,预分配就非常有用。 如果你不知道,可以使用函数 lua_newtable。
int lua_dump (lua_State *L, lua_Writer writer, void *data);
把函数 dump 成二进制 chunk 。 函数接收栈顶的 Lua 函数做参数,然后生成它的二进制 chunk 。 若被 dump 出来的东西被再次加载,加载的结果就相当于原来的函数。 当它在产生 chunk 的时候,lua_dump 通过调用函数 writer (参见 lua_Writer) 来写入数据,后面的 data 参数会被传入 writer 。
最后一次由写入器 (writer) 返回值将作为这个函数的返回值返回; 0 表示没有错误。
这个函数不会把 Lua 返回弹出堆栈。
int lua_equal (lua_State *L, int index1, int index2);
如果依照 Lua 中 == 操作符语义,索引 index1 和 index2 中的值相同的话,返回 1 。 否则返回 0 。 如果任何一个索引无效也会返回 0。
int lua_error (lua_State *L);
产生一个 Lua 错误。 错误信息(实际上可以是任何类型的 Lua 值)必须被置入栈顶。 这个函数会做一次长跳转,因此它不会再返回。 (参见 luaL_error)。
int lua_gc (lua_State *L, int what, int data);
控制垃圾收集器。
这个函数根据其参数 what 发起几种不同的任务:
LUA_GCSTOP: 停止垃圾收集器。
LUA_GCRESTART: 重启垃圾收集器。
LUA_GCCOLLECT: 发起一次完整的垃圾收集循环。
LUA_GCCOUNT: 返回 Lua 使用的内存总量(以 K 字节为单位)。
LUA_GCCOUNTB: 返回当前内存使用量除以 1024 的余数。
LUA_GCSTEP: 发起一步增量垃圾收集。 步数由 data 控制(越大的值意味着越多步), 而其具体含义(具体数字表示了多少)并未标准化。 如果你想控制这个步数,必须实验性的测试 data 的值。 如果这一步结束了一个垃圾收集周期,返回返回 1 。
LUA_GCSETPAUSE: 把 data/100 设置为 garbage-collector pause 的新值(参见 §2.10)。 函数返回以前的值。
LUA_GCSETSTEPMUL: 把 arg/100 设置成 step multiplier (参见 §2.10)。 函数返回以前的值。
lua_getallocf
lua_Alloc lua_getallocf (lua_State *L, void **ud);
返回给定状态机的内存分配器函数。 如果 ud 不是 NULL ,Lua 把调用 lua_newstate 时传入的那个指针放入 *ud 。
lua_getfenv
void lua_getfenv (lua_State *L, int index);
把索引处值的环境表压入堆栈。&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
Lua游戏开发中关于C接口学习教程
摘要:Lua游戏开发中关于C接口是本文要介绍的内容,主要是来学习LUA中关于C接口的使用方法,具体内容的实现来看本文详解。用lua快一年了,因为引擎部分比较少改动,所以一直没用过它的C接口,都是在写脚本。年前看书时写了一个小的demo做学习用,好像当时遇到些困难,但是没有记录下来,几乎都忘了。这里贴点源码出来做备忘吧:)lua的语法还是比较简单,其官网(www.lua.org)上有电子文档(www.lua.org/pil/),看一看就会了。不过学会一门语言的语法跟用好一门语言还是两
Lua游戏开发中关于C接口是本文要介绍的内容,主要是来学习LUA中关于C接口的使用方法,具体内容的实现来看本文详解。
用lua快一年了,因为引擎部分比较少改动,所以一直没用过它的C接口,都是在写脚本。年前看书时写了一个小的demo做学习用,好像当时遇到些困难,但是没有记录下来,几乎都忘了。这里贴点源码出来做备忘吧:)lua的语法还是比较简单,其官网(www.lua.org)上有电子文档(www.lua.org/pil/),看一看就会了。不过学会一门语言的语法跟用好一门语言还是两回事,好在它的源码也不多,多看看源码理解就深了。
首先说我比较讨厌lua的几个地方:
1、把数组和table混在一起,数组可以很方便取得size,而table就只能自己遍历去数。
2、没有continue,经常出现循环里面嵌套N层if。
3、最最无聊的就是变量默认是global的,要显示声明local才是本地变量。
大概就这几个公认的问题了,下面贴代码:)
程序实现了一个lua解释器,其实就是读入lua语句然后解释执行,用了readline是为了输入方便。另外启动的时候load了一个叫init.lua的脚本文件,提供了几个api供脚本使用,全部代码如下:(csdn怎么不提供附件功能呢)main.hpp #include&unistd.h&#include&sys/param.h&#include&errno.h&#include&stdlib.h&#include&iostream&#include&readline/readline.h&#include&readline/history.h&#include&lua.hpp&externlua_State*L; boolexelua(constchar*); boolinit_script(); intlua_getcwd(lua_State*); intlua_dir(lua_State*); voidregister_api(lua_State*); voidcreate_table(lua_State*); main.cpp #include&main.hpp& lua_State*L; intmain(intargc,char**argv) ...{ L=luaL_newstate();//创建一个lua运行环境,可以传入一个内存管理参数 luaL_openlibs(L);//打开常用lib if(!init_script())//load脚本 return-1; register_api(L);//注册api create_table(L);//创建一个table char*input=NULL; while(1) ...{ input=readline(&&&&);//提示输入 if(input) ...{ if(*input) ...{ if(exelua(input))//执行输入的语句 add_history(input);//增加到历史命令 } free(input); input=NULL; } else ...{ } } lua_close(L); return0; } boolexelua(constchar*line) ...{ interror=luaL_loadbuffer(L,line,strlen(line),&line&)||lua_pcall(L,0,0,0);//load并执行 if(error) ...{ std::cerr&&lua_tostring(L,-1)&&std:: lua_pop(L,1); } } boolinit_script() ...{ if(luaL_dofile(L,&init.lua&)!=0) ...{ std::cerr&&&loadinit.luafailed&; } lua_pushnumber(L,1);//传入参数 lua_getglobal(L,&__init__&);//获取脚本中__init__变量 if(lua_isfunction(L,-1))//判断__init__是否一个函数 ...{ if(lua_pcall(L,0,1,NULL)!=0)//调用__init__ ...{ std::cerr&&&call__init__error&; } intret=lua_tonumber(L,-1)||lua_toboolean(L,-1);//取得__init__的返回值 lua_pop(L,1); if(!ret) ...{ std::cerr&&&__init__failed&; } } } api.cpp #include&dirent.h&#include&main.hpp& intlua_getcwd(lua_State*L)//获取当前工作目录 ...{ charpath[MAXPATHLEN]; bzero(path,MAXPATHLEN); if(lua_gettop(L)!=0)//不需要参数 ...{ luaL_argerror(L,0,&noargexpected&); return0; } if(!getcwd(path,MAXPATHLEN)) ...{ luaL_error(L,&getcwderror%d,%s&,errno,strerror(errno)); return0; } lua_pushlstring(L,path,strlen(path));//将返回值压栈 return1;//返回返回值个数 } intlua_dir(lua_State*L)//取得目录下元素 ...{ constchar*path=luaL_checkstring(L,1); DIR*dir=opendir(path); if(!dir) ...{ lua_pushnil(L); lua_pushstring(L,strerror(errno)); return2; } inti=1; structdirent* lua_newtable(L);//把所有元素放到一个table中,以数组返回 while(ent=readdir(dir)) ...{ lua_pushnumber(L,i++); lua_pushstring(L,ent-&d_name); lua_settable(L,-3); } closedir(dir); return1; } voidregister_api(lua_State*L)//注册api ...{ lua_register(L,&getcwd&,lua_getcwd);//脚本中可以使用getcwd调用lua_getcwd lua_register(L,&dir&,lua_dir); constluaL_Regmylib[]= ...{ ...{&getcwd&,lua_getcwd}, ...{&dir&,lua_dir}, ...{NULL,NULL}, }; luaL_register(L,&tlib&,mylib);//注册一个名为tlib的模块,tlib.getcwd() } voidcreate_table(lua_State*L)//创建一个table ...{ lua_newtable(L); lua_pushnumber(L,123); lua_setfield(L,-2,&id&); lua_pushcfunction(L,lua_getcwd); lua_setfield(L,-2,&fun&); lua_setglobal(L,&tb&); } init.lua function__init__() print(&__init__ok&) return1; end MakefileCPPFLAGS=-Wall-g-O0-I/usr/local/include/lua51/ LIB=-L/usr/local/lib/lua51/-llua-lreadline CC=g++ SRC=main.cppapi.cpp OBJ=${SRC:%.cpp=%.o} all:dependmain depend: @$(CC)-MM$(SRC)&.depend -include.depend main:$(OBJ) $(CC)$(OBJ)$(CPPFLAGS)$(LIB)
clean: -rm-rf*.omain.depend
以上代码在freebsd 6.2 gcc 3.4.6 lua 5.1.2下编译通过。
小结:Lua游戏开发中关于C接口学习教程的内容介绍完了,希望通过本文的学习能对你有所帮助!
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
为您提供0门槛上云实践机会
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
Lua游戏开发中关于C接口学习教程相关信息,包括
的信息,所有Lua游戏开发中关于C接口学习教程相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
International没有更多推荐了,
不良信息举报
举报内容:
Lua数据结构 — lua_State(六)
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!博客分类:
/**断开一个连接,移除epoll监听,通知lua*/
void remove_fd(int fd, lua_State *L)
lua_State *Lx = lua_newthread(L);
lua_getglobal(Lx, F_ONCLOSE); // 调用lua里的onclose函数
lua_pushinteger(Lx, fd);
ret = lua_pcall(Lx, 1, LUA_MULTRET, 0);
if(ret != 0)
fprintf(stderr, "%s\n", lua_tostring(Lx, -1));
lua_settop(L, 0);
//从fds和clients中删除,释放内存
idx = fds[fd];
fds[fd] = -1;
free(clients[idx]);
clients[idx] = NULL;
//移除epoll监听,关闭连接
struct epoll_
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &e);
close(fd);
/**接受连接请求,多个*/
void accept_fd(int listenfd)
struct sockaddr_
socklen_t len = sizeof(addr);
while((fd = accept(listenfd, (struct sockaddr *)&addr, &len)) & 0)
add_fd(fd, TRUE);
浏览: 40119 次
来自: 天津
hibernate search 在db海量数据检索时,因为先 ...
魔力猫咪 写道根据CAP理论,Consistency(一致性) ...
根据CAP理论,Consistency(一致性), Avail ...
中文论坛,英文发言,牛B机中的战斗机~
标记一下,回头接着看。。。
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'}

我要回帖

更多关于 lua 游戏服务器框架 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信