- 注册时间
 - 2022-8-23
 
- 最后登录
 - 2024-3-6
 
- 在线时间
 - 2 小时
 
 
 
 
 
编程入门 
  
	- 天马币
 - 22 
 
 
 
 
 | 
 
最近在学习lua多任务, 用于智辅游戏服务器消息的处理, 因为脚本消息很多, 
每分钟有几十条到上百条, 如果不能多任务并行处理, 会拖慢游戏,也不实用 
整理下, 做个笔记, 以免忘了, 代码也是百度来的, 改改能用 
lua用5.3.1版, 和我原来的5.1版本有不兼营, 按网上的方法改成支持中文变量 
这样挺好的, 代码也不用注释, 取名字方便,也短, 不然如果用英文 
背包-->bag 血-->HP 兰-->MP 还好翻译好记 
怪物-->monster 人物-->human 就不有点长,不好记 
地面物品-->dropeditem 装备-->useitem 狂欢应典活动-->KHQD 太不直观了 
xx地图 xxNPC xx任务 就十分难翻译了, 
闲话结束, 正文开始 
---------------------------------------------- 
 
--LUA协程和多任务 
--3个同时运行的协程为例 
--每隔一定的间隔输出文字, 协程状态 
--第1个 隔101毫秒 输出5次 
--第2个 隔101毫秒 输出2次 
--第3个 隔101毫秒 输出3次 
--实现协程的关键是要 延时 = mylib.delay 这个函数 
--标准库里没有这个函数 用C扩展为DLL库来使用 
--先启用定时器, 然后把协程挂起(yield), 定时器时间到了再唤醒(resume) 
--这样就能实现多个任务同时运行 
--协程一次只能有1个运行(running), 其它都是在挂起状态(suspended) 
--当协程运行结束后, 就是停止状态(dead) 
--协程挂起(yield), 唤醒(resume), 应用有一定限制,就是不能在C和LUA间交叉使用 
--即在C中挂起的,就要在C中唤醒, Lua中挂起的,也要在LUA中唤醒 
--协程的开销不大, 初略估计了下, 开启运行2万多个协程,结束后故意不消毁,内存约增加20多M 
--1个协程的开销约1k多内存 
 
--Lua脚本 
延时 = mylib.delay 
 
function 显示(a,b) 
  print(a,b,'co1='..coroutine.status(co1),'co2='..coroutine.status(co2),'co3='..coroutine.status(co3)) 
end 
 
function 协程(序号,次数,毫秒) 
    for i=1,次数 do 
        延时(毫秒) 显示(序号,i*毫秒 ) 
    end 
    显示(序号,'结束' ) 
end 
 
co1=coroutine.create(协程) coroutine.resume(co1, '脚本1', 5, 101) 
co2=coroutine.create(协程) coroutine.resume(co2, '脚本2', 2, 301) 
co3=coroutine.create(协程) coroutine.resume(co3, '脚本3', 3, 501) 
 
--运行结果 
036CBF54 脚本1  101     co1=running     co2=suspended   co3=suspended 
036CBF54 脚本1  202     co1=running     co2=suspended   co3=suspended 
036CC01C 脚本2  301     co1=suspended   co2=running     co3=suspended 
036CBF54 脚本1  303     co1=running     co2=suspended   co3=suspended 
036CBF54 脚本1  404     co1=running     co2=suspended   co3=suspended 
036CC0E4 脚本3  501     co1=suspended   co2=suspended   co3=running 
036CBF54 脚本1  505     co1=running     co2=suspended   co3=suspended 
036CBF54 脚本1  结束    co1=running     co2=suspended   co3=suspended 
036CC01C 脚本2  602     co1=dead        co2=running     co3=suspended 
036CC01C 脚本2  结束    co1=dead        co2=running     co3=suspended 
036CC0E4 脚本3  1002    co1=dead        co2=dead        co3=running 
036CC0E4 脚本3  1503    co1=dead        co2=dead        co3=running 
036CC0E4 脚本3  结束    co1=dead        co2=dead        co3=running 
 
--延时 = mylib.delay 的C代码 用C++Builder 6环境 
--调用delay时,首先创建1个定时器 
  int id = SetTimer(NULL, NULL, ms, (TIMERPROC)LuaTimerProc); 
--然后保存协程句柄,定时器id到TList LuaTimerList,到定时器回调时有用 
  LuaTimerList->Add((void*)L); 
  LuaTimerList->Add((void*)id); 
--然后,挂起协程 
--时间到了,定时器回调函数运行 
--因为定时器只用一次就够了, 故销毁之 
    KillTimer(hWnd, uIDEvent); 
--在TList LuaTimerList找到对应的记录,获得协程句柄,唤醒之 
    lua_resume(L, 0, 0); 
--注意, 在dll退出时, 记得销毁LuaTimerList, 不然内存略有泄漏 
    case DLL_PROCESS_DETACH: 
        delete LuaTimerList; 
        break; 
--在lua中使用方法, (编译后的库名为mylib.dll) 
    mylib = require('mylib') 
    mylib.delay(100)  --延时100ms 
--函数只能在协程内使用, 在主线程使用会出错 
--不过没关系, 可以让所有脚本都在协程运行,按下列方式即可 
function f() 
-------------------- 
--这里插入要执行的内容 
-------------------- 
end 
co=coroutine.create(f) 
coroutine.resume(co) 
 
 
 
//--------------------------------------------------------------------------- 
//Lua扩展库测试 
 
#pragma hdrstop 
 
//--------------------------------------------------------------------------- 
 
#pragma package(smart_init) 
#include <vcl.h> 
#include <math.h> 
#include <windows.h> 
#include "LuaMylib.h" 
 
TList *LuaTimerList; 
 
//void _stdcall LuaTimerProc(int hwnd, int uMsg, int idEvent, int time) 
void   CALLBACK   LuaTimerProc(HWND hWnd, UINT nMsg, UINT uIDEvent, DWORD dwTime) 
{ 
    KillTimer(hWnd, uIDEvent); 
    for (int i=(LuaTimerList->Count / 2) -1;i>=0; i--) 
    { 
        UINT id = (UINT)LuaTimerList->Items[i*2+1]; 
        if (id == uIDEvent) 
        { 
            lua_State *L = (lua_State*)LuaTimerList->Items[i*2]; 
            LuaTimerList->Delete(i*2); 
            LuaTimerList->Delete(i*2); 
            lua_resume(L, 0, 0); 
        } 
    } 
} 
 
static int delay (lua_State *L) { 
  if (lua_gettop(L) == 0) return 0; 
  int ms = lua_tointeger(L, 1); 
  int id = SetTimer(NULL, NULL, ms, (TIMERPROC)LuaTimerProc); 
  if (LuaTimerList == NULL )LuaTimerList = new(TList); 
  LuaTimerList->Add((void*)L); 
  LuaTimerList->Add((void*)id); 
  lua_pop(L, 1);//弹出参数 
  return lua_yield(L, 0); 
} 
 
 
static const luaL_Reg mylib[] = { 
  {"delay",   delay}, 
  {NULL, NULL} 
}; 
 
 
/* 
** Open test library 
*/ 
LUALIB_API int luaopen_mylib (lua_State *L) { 
    luaL_newlib(L, mylib); 
    return 1; 
} 
 
 
 
 
 
--C中实现协程 
--以脚务器消息接收为例 
--编写一个消息处理的函数Lua(伪代码) 
function Recv(Recog, Ident, Param, Tag, Series, buf, len) 
    --if 定义[Ident] and 定义[Ident].过滤==1 then return end; 
    print('收',翻译(Ident,Recog,Param,Series,Tag,len,buf)) 
    mylib.delay(5000) 
    --print('协程结束') 
    回收协程() 
end 
 
--编写一个消息转发Lua的C函数 相当于Lua下列语句,挂到消息接收上 
static _stdcall void SocketRecvHook(int buf, int len) 
local co=coroutine.create(recv) 
...转化内存数据到变量 
coroutine.resume(co, Recog, Ident, Param, Tag, Series, buf, len) 
库名.copool=co //必须, 不然协程被当垃圾回收出错, 也可以留在栈里 
 
--如此每收到1个消息就开启1个协程, 所以协程数量增长很快, 
--如果协程运行结束, 就要进行回收, 即可在C中编写,也可在LUA中 
function 回收协程() 
    local 总数=0 
    local 回收=0 
    for i in pairs(mir.copool) do 
        总数 = 总数+1 
        if coroutine.status(mir.copool)=='dead' then 
            mir.copool=nil --垃圾回收器会自动回收该协程 
            回收=回收+1 
        end 
    end 
    --if 回收>0 then print('协程总数,回收,剩余=',总数,回收,总数-回收) end 
end 
 
 
--C代码如下 
static _stdcall void SocketRecvHook(int buf, int len) 
{ 
    //调用原来的程序 
    OrgSocketRecvDecode(buf, len); 
 
    //调用LUA程序 
    PTDefaultMessage pm = (PTDefaultMessage)(buf+4); 
    //co=coroutine.create(recv) 
    lua_getglobal(L, "coroutine");  //--->+1 
    lua_getfield(L, -1, "create"); //调用函数coroutine.create//--->+2 
    //获得self.recv 
    lua_pushlightuserdata(L, (void*)&Key); //压入地址   //--->+3 
    lua_gettable(L, LUA_REGISTRYINDEX);//table在栈顶    //--->+4 
    lua_getfield(L, -1, "recv"); //self.Recv           //--->+5 
    lua_remove(L, -2);//移除table                      //--->+4 
    if (lua_isfunction(L, -1)) 
    { 
    lua_call(L, 1, 1);//1个参数,1个结果->co            //--->+2 
    //库名.copool=co 
    savepool(IntToStr(GetTickCount())+"_R_"+IntToStr(pm->Ident) ); 
    //coroutine.resume(co,参数) 
    lua_getglobal(L, "coroutine"); 
    lua_getfield(L, -1, "resume"); //调用函数coroutine.resume 
    lua_pushvalue(L, -3);//复制co 
    lua_pushinteger(L, pm->Recog);//1 
    lua_pushinteger(L, pm->Ident);//2 
    lua_pushinteger(L, pm->Param);//3 
    lua_pushinteger(L, pm->Tag);//4 
    lua_pushinteger(L, pm->Series);//5 
    lua_pushinteger(L, buf + 16);//6 
    lua_pushinteger(L, len - 16);//7 
    lua_call(L, 1+7, 0);//1+7个参数,0个结果->co 
    lua_pop(L, 3); 
    } 
    else 
    { 
        lua_pop(L, 3); 
    } 
} |  
  
 |   
 
 
 
 |