天马阁

 找回密码
 立即注册
                                        →→→→→→→→→→→→ 1点击查看所有VIP教程目录长列表(总教程数269个) 2办理VIP详情进入 ←←←←←←←←←←←←
1 x64CE与x64dbg入门基础教程 7课 已完结 2 x64汇编语言基础教程 16课 已完结 3 x64辅助入门基础教程 9课 已完结 4 C++x64内存辅助实战技术教程 149课 已完结
5 C++x64内存检测与过检测技术教程 10课 已完结 6 C+x64二叉树分析遍历与LUA自动登陆教程 19课已完结 7 C++BT功能原理与x64实战教程 29课 已完结 8 C+FPS框透视与自瞄x64实现原理及防护思路 30课完结
64驱?封? 9 64反驱? 10 64位V? 11 绝? 12 ???课?
13 64透 ? 14 64U ? 15 64Q ? 16 64功 ?
17 64U ? 18 64模 ? 19 64多 ? 20 64网 ?
21 64注 ? 22 64火 ? 23 64棋 ? 24 64自二链L?
25 64破 ? VIP会员办理QQ: 89986068   
【请先加好友,然后到好友列表双击联系客服办理,不然可能无法接受到信息。】
27 加入2000人交流群637034024 3 28 免责声明?
查看: 1482|回复: 0

LUA协程和多任务笔记

[复制链接]

11

主题

0

回帖

13

积分

编程入门

Rank: 1

天马币
22
发表于 2024-2-29 13:03:05 | 显示全部楼层 |阅读模式
最近在学习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);
    }
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

天马阁|C/C++辅助教程|安卓逆向安全| 论坛导航|免责申明|Archiver||网站地图
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表天马阁立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2010-2021 All Right Reserved.
快速回复 返回顶部 返回列表