请教新天龙八部lua源码 lua源码hook几个Lua函数

6176人阅读
游戏开发(36)
Lua脚本(7)
游戏服务器(14)
TL代码解析系列(3)
一、Lua脚本功能接口
1. LuaInterface.h/.cpp声明和实现LuaInterface。
LuaInterface成员如下:
//脚本引擎
FoxLuaScript
LuaCFuncRegister
//场景关联
//已经读取的脚本表
m_ScriptTable
主要方法:
Init(Scene* pScene);//完成Lua脚本环境的初始化和C导出函数的注册
Scene* GetOwner();
执行Lua脚本的C++接口,提供多达8个参数支持。
ExeScript( ScriptID_t scriptid, CHAR* funcname ) ;
ExeScript_D( ScriptID_t scriptid, CHAR* funcname, INT Param0 ) ;
ExeScript_DD( ScriptID_t scriptid, CHAR* funcname, INT Param0, INT Param1 ) ;
ExeScript_DDD( ScriptID_t scriptid, CHAR* funcname, INT Param0, INT Param1, INT Param2 ) ;
ExeScript_DDDD( ScriptID_t scriptid, CHAR* funcname, INT Param0, INT Param1, INT Param2, INT Param3 ) ;
LuaInterface::Init里面会初始化mLua引擎,注册C++提供给Lua脚本的函数(LuaCFuncRegister),并加载ScriptGlobal.lua脚本。
2. LuaCFuncRegister.cpp里面对所有导出到Lua的C++函数进行注册。
struct _Str2Func functbl[] =
{&AddEventList&,FuncProto(LuaFnAddNumText)},
{&GetMission&, FuncProto(LuaFnGetMission)},
{&GetMissionCount&, FuncProto(LuaFnGetMissionCount)},
{&SetMissionByIndex&, FuncProto(LuaFnSetMissionByIndex)},
{&AddMission&, FuncProto(LuaFnAddMission)},
{&AddMissionEx&, FuncProto(LuaFnAddMissionEx)},
{&SetMissionEvent&, FuncProto(LuaFnSetMissionEvent)},
这些C++函数的实现是在下列头文件中进行的:
#include &LuaFnTbl_Mission.h&
#include &LuaFnTbl_Misc.h&
#include &LuaFnTbl_Ability.h&
#include &LuaFnTbl_Attr.h&
#include &LuaFnTbl_Pet.h&
#include &LuaFnTbl_Battle.h&
#include &LuaFnTbl_Shop.h&
#include &LuaFnTbl_PetPlacard.h&
#include &LuaFnTbl_Scene.h&
#include &LuaFnTbl_Team.h&
#include &LuaFnTbl_DoAction.h&
#include &LuaFnTbl_Relation.h&
#include &LuaFnTbl_Guild.h&
#include &LuaFnTbl_City.h&
这些函数并不是功能的真正实现地方,真正的实现代码在Scene、Obj_Human等地方。这里只是集中转调而已。
3. 注册完成后,在Lua脚本中就可以使用类似AddMission接口调用C++里面功能。
二、Lua脚本位置
所有脚本在Bin\Public\Data\Script子目录中。
Bin\Public\Data\Script.dat是索引,里面保存了ScriptID和对应的脚本文件名。如:
888888=\scene.lua
888889=\mail.lua
888890=\player_login.lua
脚本ID是6位的。
三、脚本索引的初始化
每个场景都会进行脚本初始化,具体是在Scene::Load里面,在在m_pLuaInterface初始化之后。
m_pLuaInterface-&Init(this);
if( !m_pScriptFileMgr-&IsInit() )
m_pScriptFileMgr-&Init( FILE_SCRIPT, FALSE);
Log::SaveLog( SERVER_LOGFILE, &Load ../Public/Data/script.dat OK!& );
m_pScriptFileMgr-&Init将&888888=\scene.lua&拆开,保存ID和文件名到SFileData里面。所有的SFileData用SFileDataLink串起来。
四、脚本加载和调用
每个脚本的调用都是通过INT LuaFnCallScriptFunction(Lua_State* L);来进行的。该函数是一个C++函数,脚本里面调用名是CallScriptFunction,注册如下:
{&CallScriptFunction&, FuncProto(LuaFnCallScriptFunction)},
LuaFnCallScriptFunction的实现在文件LuaFnTbl_Misc.h里。
可以看到在,此函数:
l 把SFileData添加到pScene-&GetLuaInterface()-&m_ScriptTable表里面;
pSFileData = pScene-&GetLuaInterface()-&GetOwner()-&GetScriptFileMgr()-&GetFileData(scriptId);
pScene-&GetLuaInterface()-&m_ScriptTable.Add(
scriptId, pSFileData ) ;
l 然后加载脚本;
pScene-&GetLuaInterface()-&mLua.Load( const_cast&CHAR*&(filename)
l 最后调用脚本。
五、典型脚本的结构
见ScriptDef.h,定义了一些脚本接口函数,如,对于脚本805007,就是:
function x805007_OnDefaultEvent( sceneId, selfId,targetId );
有一些调用没有在这里定义宏,直接写在C++代码里面,如。
DEF_EVENT_ENTRY_FUNC_NAME
(&OnDefaultEvent&)
//脚本进入函数
DEF_ON_KILL_OBJECT_FUNC_NAME
(&OnKillObject&)
DEF_ON_ITEM_CHANGED_FUNC_NAME
(&OnItemChanged&)
DEF_ON_PET_CHANGED_FUNC_NAME
(&OnPetChanged&)
DEF_ON_ENTER_AREA_FUNC_NAME
(&OnEnterArea&)
DEF_ON_LEAVE_AREA_FUNC_NAME
(&OnLeaveArea&)
DEF_EVENT_ON_TIMER
(&OnTimer&)
DEF_MISSION_ACCEPT
(&OnMissionAccept&)
//接受任务
DEF_MISSION_ABANDON
(&OnAbandon&)
//放弃任务
DEF_MISSION_REFUSE
(&OnMissionRefuse&)
// 拒绝接受任务
DEF_MISSION_SUBMIT
(&OnMissionSubmit&)
//任务完成后,提交任务
DEF_MISSION_CHECK
(&OnMissionCheck&)
//任务完成条件检查
DEF_MISSION_CONTINUE
(&OnMissionContinue&)
//任务没完成,继续
六、样例分析
大理NPC赵天师脚本分析
脚本名:\odali_xinshoutian.lua,汗,居然叫这个名字,找了半天,一般的命名都是拼音。
x002030_g_scriptId = 002030
--所拥有的事件ID列表
x002030_g_eventList={204,208,212,214,217,223, , , , 0083,
,241,050022}
一般情况,每个event对于一个任务,也是一段脚本实现的。如210200对于:
;大理城新手指导任务
210200=\event\dali\edali_zhidao_0200.lua
--找人任务
--赵天师寻找蒲良
NPC脚本触发接口函数是OnDefaultEvent,在的里面触发。
ORESULT PushCommand_DefaultEvent( ObjID_t idNPC );
pCharacter-&getScene()-&GetLuaInterface()-&ExeScript_DDD(
DEF_EVENT_ENTRY_FUNC_NAME,
(INT)pCharacter-&getScene()-&SceneID(),
(INT)pCharacter-&GetID(),
(INT)pNPC-&GetID() ) ;
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:296686次
积分:3679
积分:3679
排名:第7427名
原创:59篇
转载:113篇
评论:25条
(1)(1)(1)(1)(3)(2)(4)(5)(2)(1)(1)(8)(1)(4)(2)(11)(1)(7)(2)(6)(6)(30)(11)(1)(1)(6)(1)(1)(2)(17)(1)(9)(21)(1)随笔 - 17&
文章 - 48&
trackbacks - 0
26272829303112345678910111213141517182021222324252627282930123456
阅读排行榜
评论排行榜
lua的VM执行代码是从lvm.c中的void luaV_execute(lua_State *L)开始:void&luaV_execute&(lua_State&*L)&{&&CallInfo&*ci&=&L-&&&LClosure&*&&TValue&*k;&&StkId&base;&newframe:&&/*&reentry&point&when&frame&changes&(call/return)&*/&&lua_assert(ci&==&L-&ci);&&cl&=&clLvalue(ci-&func);&&k&=&cl-&p-&k;&&base&=&ci-&u.l.base;&&/*&main&loop&of&interpreter&*/&&for&(;;)&{&&&&Instruction&i&=&*(ci-&u.l.savedpc++);&&&&StkId&&&&&if&((L-&hookmask&&&(LUA_MASKLINE&|&LUA_MASKCOUNT))&&&&&&&&&&&(--L-&hookcount&==&0&||&L-&hookmask&&&LUA_MASKLINE))&{&&&&&&Protect(traceexec(L));&&&&}&&&&/*&WARNING:&several&calls&may&realloc&the&stack&and&invalidate&`ra'&*/&&&&ra&=&RA(i);&&&&lua_assert(base&==&ci-&u.l.base);&&&&lua_assert(base&&=&L-&top&&&&L-&top&&&L-&stack&+&L-&stacksize);&&&&vmdispatch&(GET_OPCODE(i))&{&&&&&&vmcase(OP_MOVE,&&&&&&&&setobjs2s(L,&ra,&RB(i));&&&&&&)&&&&&&vmcase(OP_LOADK,&&&&&&&&TValue&*rb&=&k&+&GETARG_Bx(i);&&&&&&&&setobj2s(L,&ra,&rb);&&&&&&)&&&&&&vmcase(OP_LOADKX,&&&&&&&&TValue&*&&&&&&&&lua_assert(GET_OPCODE(*ci-&u.l.savedpc)&==&OP_EXTRAARG);&&&&&&&&rb&=&k&+&GETARG_Ax(*ci-&u.l.savedpc++);&&&&&&&&setobj2s(L,&ra,&rb);&&&&&&)&&&&&&vmcase(OP_LOADBOOL,&&&&&&&&setbvalue(ra,&GETARG_B(i));&&&&&&&&if&(GETARG_C(i))&ci-&u.l.savedpc++;&&/*&skip&next&instruction&(if&C)&*/&&&&&&)&&&&&&vmcase(OP_LOADNIL,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&do&{&&&&&&&&&&setnilvalue(ra++);&&&&&&&&}&while&(b--);&&&&&&)&&&&&&vmcase(OP_GETUPVAL,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&setobj2s(L,&ra,&cl-&upvals[b]-&v);&&&&&&)&&&&&&vmcase(OP_GETTABUP,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&Protect(luaV_gettable(L,&cl-&upvals[b]-&v,&RKC(i),&ra));&&&&&&)&&&&&&vmcase(OP_GETTABLE,&&&&&&&&Protect(luaV_gettable(L,&RB(i),&RKC(i),&ra));&&&&&&)&&&&&&vmcase(OP_SETTABUP,&&&&&&&&int&a&=&GETARG_A(i);&&&&&&&&Protect(luaV_settable(L,&cl-&upvals[a]-&v,&RKB(i),&RKC(i)));&&&&&&)&&&&&&vmcase(OP_SETUPVAL,&&&&&&&&UpVal&*uv&=&cl-&upvals[GETARG_B(i)];&&&&&&&&setobj(L,&uv-&v,&ra);&&&&&&&&luaC_barrier(L,&uv,&ra);&&&&&&)&&&&&&vmcase(OP_SETTABLE,&&&&&&&&Protect(luaV_settable(L,&ra,&RKB(i),&RKC(i)));&&&&&&)&&&&&&vmcase(OP_NEWTABLE,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&int&c&=&GETARG_C(i);&&&&&&&&Table&*t&=&luaH_new(L);&&&&&&&&sethvalue(L,&ra,&t);&&&&&&&&if&(b&!=&0&||&c&!=&0)&&&&&&&&&&luaH_resize(L,&t,&luaO_fb2int(b),&luaO_fb2int(c));&&&&&&&&checkGC(L,&ra&+&1);&&&&&&)&&&&&&vmcase(OP_SELF,&&&&&&&&StkId&rb&=&RB(i);&&&&&&&&setobjs2s(L,&ra+1,&rb);&&&&&&&&Protect(luaV_gettable(L,&rb,&RKC(i),&ra));&&&&&&)&&&&&&vmcase(OP_ADD,&&&&&&&&arith_op(luai_numadd,&TM_ADD);&&&&&&)&&&&&&vmcase(OP_SUB,&&&&&&&&arith_op(luai_numsub,&TM_SUB);&&&&&&)&&&&&&vmcase(OP_MUL,&&&&&&&&arith_op(luai_nummul,&TM_MUL);&&&&&&)&&&&&&vmcase(OP_DIV,&&&&&&&&arith_op(luai_numdiv,&TM_DIV);&&&&&&)&&&&&&vmcase(OP_MOD,&&&&&&&&arith_op(luai_nummod,&TM_MOD);&&&&&&)&&&&&&vmcase(OP_POW,&&&&&&&&arith_op(luai_numpow,&TM_POW);&&&&&&)&&&&&&vmcase(OP_UNM,&&&&&&&&TValue&*rb&=&RB(i);&&&&&&&&if&(ttisnumber(rb))&{&&&&&&&&&&lua_Number&nb&=&nvalue(rb);&&&&&&&&&&setnvalue(ra,&luai_numunm(L,&nb));&&&&&&&&}&&&&&&&&else&{&&&&&&&&&&Protect(luaV_arith(L,&ra,&rb,&rb,&TM_UNM));&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_NOT,&&&&&&&&TValue&*rb&=&RB(i);&&&&&&&&int&res&=&l_isfalse(rb);&&/*&next&assignment&may&change&this&value&*/&&&&&&&&setbvalue(ra,&res);&&&&&&)&&&&&&vmcase(OP_LEN,&&&&&&&&Protect(luaV_objlen(L,&ra,&RB(i)));&&&&&&)&&&&&&vmcase(OP_CONCAT,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&int&c&=&GETARG_C(i);&&&&&&&&StkId&&&&&&&&&L-&top&=&base&+&c&+&1;&&/*&mark&the&end&of&concat&operands&*/&&&&&&&&Protect(luaV_concat(L,&c&-&b&+&1));&&&&&&&&ra&=&RA(i);&&/*&'luav_concat'&may&invoke&TMs&and&move&the&stack&*/&&&&&&&&rb&=&b&+&base;&&&&&&&&setobjs2s(L,&ra,&rb);&&&&&&&&checkGC(L,&(ra&&=&rb&?&ra&+&1&:&rb));&&&&&&&&L-&top&=&ci-&&&/*&restore&top&*/&&&&&&)&&&&&&vmcase(OP_JMP,&&&&&&&&dojump(ci,&i,&0);&&&&&&)&&&&&&vmcase(OP_EQ,&&&&&&&&TValue&*rb&=&RKB(i);&&&&&&&&TValue&*rc&=&RKC(i);&&&&&&&&Protect(&&&&&&&&&&if&(cast_int(equalobj(L,&rb,&rc))&!=&GETARG_A(i))&&&&&&&&&&&&ci-&u.l.savedpc++;&&&&&&&&&&else&&&&&&&&&&&&donextjump(ci);&&&&&&&&)&&&&&&)&&&&&&vmcase(OP_LT,&&&&&&&&Protect(&&&&&&&&&&if&(luaV_lessthan(L,&RKB(i),&RKC(i))&!=&GETARG_A(i))&&&&&&&&&&&&ci-&u.l.savedpc++;&&&&&&&&&&else&&&&&&&&&&&&donextjump(ci);&&&&&&&&)&&&&&&)&&&&&&vmcase(OP_LE,&&&&&&&&Protect(&&&&&&&&&&if&(luaV_lessequal(L,&RKB(i),&RKC(i))&!=&GETARG_A(i))&&&&&&&&&&&&ci-&u.l.savedpc++;&&&&&&&&&&else&&&&&&&&&&&&donextjump(ci);&&&&&&&&)&&&&&&)&&&&&&vmcase(OP_TEST,&&&&&&&&if&(GETARG_C(i)&?&l_isfalse(ra)&:&!l_isfalse(ra))&&&&&&&&&&&&ci-&u.l.savedpc++;&&&&&&&&&&else&&&&&&&&&&donextjump(ci);&&&&&&)&&&&&&vmcase(OP_TESTSET,&&&&&&&&TValue&*rb&=&RB(i);&&&&&&&&if&(GETARG_C(i)&?&l_isfalse(rb)&:&!l_isfalse(rb))&&&&&&&&&&ci-&u.l.savedpc++;&&&&&&&&else&{&&&&&&&&&&setobjs2s(L,&ra,&rb);&&&&&&&&&&donextjump(ci);&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_CALL,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&int&nresults&=&GETARG_C(i)&-&1;&&&&&&&&if&(b&!=&0)&L-&top&=&ra+b;&&/*&else&previous&instruction&set&top&*/&&&&&&&&if&(luaD_precall(L,&ra,&nresults))&{&&/*&C&function?&*/&&&&&&&&&&if&(nresults&&=&0)&L-&top&=&ci-&&&/*&adjust&results&*/&&&&&&&&&&base&=&ci-&u.l.base;&&&&&&&&}&&&&&&&&else&{&&/*&Lua&function&*/&&&&&&&&&&ci&=&L-&&&&&&&&&&&ci-&callstatus&|=&CIST_REENTRY;&&&&&&&&&&goto&&&/*&restart&luaV_execute&over&new&Lua&function&*/&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_TAILCALL,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&if&(b&!=&0)&L-&top&=&ra+b;&&/*&else&previous&instruction&set&top&*/&&&&&&&&lua_assert(GETARG_C(i)&-&1&==&LUA_MULTRET);&&&&&&&&if&(luaD_precall(L,&ra,&LUA_MULTRET))&&/*&C&function?&*/&&&&&&&&&&base&=&ci-&u.l.base;&&&&&&&&else&{&&&&&&&&&&/*&tail&call:&put&called&frame&(n)&in&place&of&caller&one&(o)&*/&&&&&&&&&&CallInfo&*nci&=&L-&&&/*&called&frame&*/&&&&&&&&&&CallInfo&*oci&=&nci-&&&/*&caller&frame&*/&&&&&&&&&&StkId&nfunc&=&nci-&&&/*&called&function&*/&&&&&&&&&&StkId&ofunc&=&oci-&&&/*&caller&function&*/&&&&&&&&&&/*&last&stack&slot&filled&by&'precall'&*/&&&&&&&&&&StkId&lim&=&nci-&u.l.base&+&getproto(nfunc)-&&&&&&&&&&&int&&&&&&&&&&&/*&close&all&upvalues&from&previous&call&*/&&&&&&&&&&if&(cl-&p-&sizep&&&0)&luaF_close(L,&oci-&u.l.base);&&&&&&&&&&/*&move&new&frame&into&old&one&*/&&&&&&&&&&for&(aux&=&0;&nfunc&+&aux&&&&aux++)&&&&&&&&&&&&setobjs2s(L,&ofunc&+&aux,&nfunc&+&aux);&&&&&&&&&&oci-&u.l.base&=&ofunc&+&(nci-&u.l.base&-&nfunc);&&/*&correct&base&*/&&&&&&&&&&oci-&top&=&L-&top&=&ofunc&+&(L-&top&-&nfunc);&&/*&correct&top&*/&&&&&&&&&&oci-&u.l.savedpc&=&nci-&u.l.&&&&&&&&&&oci-&callstatus&|=&CIST_TAIL;&&/*&function&was&tail&called&*/&&&&&&&&&&ci&=&L-&ci&=&&&/*&remove&new&frame&*/&&&&&&&&&&lua_assert(L-&top&==&oci-&u.l.base&+&getproto(ofunc)-&maxstacksize);&&&&&&&&&&goto&&&/*&restart&luaV_execute&over&new&Lua&function&*/&&&&&&&&}&&&&&&)&&&&&&vmcasenb(OP_RETURN,&&&&&&&&int&b&=&GETARG_B(i);&&&&&&&&if&(b&!=&0)&L-&top&=&ra+b-1;&&&&&&&&if&(cl-&p-&sizep&&&0)&luaF_close(L,&base);&&&&&&&&b&=&luaD_poscall(L,&ra);&&&&&&&&if&(!(ci-&callstatus&&&CIST_REENTRY))&&/*&'ci'&still&the&called&one&*/&&&&&&&&&&return;&&/*&external&invocation:&return&*/&&&&&&&&else&{&&/*&invocation&via&reentry:&continue&execution&*/&&&&&&&&&&ci&=&L-&&&&&&&&&&&if&(b)&L-&top&=&ci-&&&&&&&&&&&lua_assert(isLua(ci));&&&&&&&&&&lua_assert(GET_OPCODE(*((ci)-&u.l.savedpc&-&1))&==&OP_CALL);&&&&&&&&&&goto&&&/*&restart&luaV_execute&over&new&Lua&function&*/&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_FORLOOP,&&&&&&&&lua_Number&step&=&nvalue(ra+2);&&&&&&&&lua_Number&idx&=&luai_numadd(L,&nvalue(ra),&step);&/*&increment&index&*/&&&&&&&&lua_Number&limit&=&nvalue(ra+1);&&&&&&&&if&(luai_numlt(L,&0,&step)&?&luai_numle(L,&idx,&limit)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&:&luai_numle(L,&limit,&idx))&{&&&&&&&&&&ci-&u.l.savedpc&+=&GETARG_sBx(i);&&/*&jump&back&*/&&&&&&&&&&setnvalue(ra,&idx);&&/*&update&internal&index&*/&&&&&&&&&&setnvalue(ra+3,&idx);&&/*&and&external&index&*/&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_FORPREP,&&&&&&&&const&TValue&*init&=&&&&&&&&&const&TValue&*plimit&=&ra+1;&&&&&&&&const&TValue&*pstep&=&ra+2;&&&&&&&&if&(!tonumber(init,&ra))&&&&&&&&&&luaG_runerror(L,&LUA_QL("for")&"&initial&value&must&be&a&number");&&&&&&&&else&if&(!tonumber(plimit,&ra+1))&&&&&&&&&&luaG_runerror(L,&LUA_QL("for")&"&limit&must&be&a&number");&&&&&&&&else&if&(!tonumber(pstep,&ra+2))&&&&&&&&&&luaG_runerror(L,&LUA_QL("for")&"&step&must&be&a&number");&&&&&&&&setnvalue(ra,&luai_numsub(L,&nvalue(ra),&nvalue(pstep)));&&&&&&&&ci-&u.l.savedpc&+=&GETARG_sBx(i);&&&&&&)&&&&&&vmcasenb(OP_TFORCALL,&&&&&&&&StkId&cb&=&ra&+&3;&&/*&call&base&*/&&&&&&&&setobjs2s(L,&cb+2,&ra+2);&&&&&&&&setobjs2s(L,&cb+1,&ra+1);&&&&&&&&setobjs2s(L,&cb,&ra);&&&&&&&&L-&top&=&cb&+&3;&&/*&func.&+&2&args&(state&and&index)&*/&&&&&&&&Protect(luaD_call(L,&cb,&GETARG_C(i),&1));&&&&&&&&L-&top&=&ci-&&&&&&&&&i&=&*(ci-&u.l.savedpc++);&&/*&go&to&next&instruction&*/&&&&&&&&ra&=&RA(i);&&&&&&&&lua_assert(GET_OPCODE(i)&==&OP_TFORLOOP);&&&&&&&&goto&l_&&&&&&)&&&&&&vmcase(OP_TFORLOOP,&&&&&&&&l_tforloop:&&&&&&&&if&(!ttisnil(ra&+&1))&{&&/*&continue&loop?&*/&&&&&&&&&&setobjs2s(L,&ra,&ra&+&1);&&/*&save&control&variable&*/&&&&&&&&&&&ci-&u.l.savedpc&+=&GETARG_sBx(i);&&/*&jump&back&*/&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_SETLIST,&&&&&&&&int&n&=&GETARG_B(i);&&&&&&&&int&c&=&GETARG_C(i);&&&&&&&&int&&&&&&&&&Table&*h;&&&&&&&&if&(n&==&0)&n&=&cast_int(L-&top&-&ra)&-&1;&&&&&&&&if&(c&==&0)&{&&&&&&&&&&lua_assert(GET_OPCODE(*ci-&u.l.savedpc)&==&OP_EXTRAARG);&&&&&&&&&&c&=&GETARG_Ax(*ci-&u.l.savedpc++);&&&&&&&&}&&&&&&&&luai_runtimecheck(L,&ttistable(ra));&&&&&&&&h&=&hvalue(ra);&&&&&&&&last&=&((c-1)*LFIELDS_PER_FLUSH)&+&n;&&&&&&&&if&(last&&&h-&sizearray)&&/*&needs&more&space?&*/&&&&&&&&&&luaH_resizearray(L,&h,&last);&&/*&pre-allocate&it&at&once&*/&&&&&&&&for&(;&n&&&0;&n--)&{&&&&&&&&&&TValue&*val&=&ra+n;&&&&&&&&&&luaH_setint(L,&h,&last--,&val);&&&&&&&&&&luaC_barrierback(L,&obj2gco(h),&val);&&&&&&&&}&&&&&&&&L-&top&=&ci-&&&/*&correct&top&(in&case&of&previous&open&call)&*/&&&&&&)&&&&&&vmcase(OP_CLOSURE,&&&&&&&&Proto&*p&=&cl-&p-&p[GETARG_Bx(i)];&&&&&&&&Closure&*ncl&=&getcached(p,&cl-&upvals,&base);&&/*&cached&closure&*/&&&&&&&&if&(ncl&==&NULL)&&/*&no&match?&*/&&&&&&&&&&pushclosure(L,&p,&cl-&upvals,&base,&ra);&&/*&create&a&new&one&*/&&&&&&&&else&&&&&&&&&&setclLvalue(L,&ra,&ncl);&&/*&push&cashed&closure&*/&&&&&&&&checkGC(L,&ra&+&1);&&&&&&)&&&&&&vmcase(OP_VARARG,&&&&&&&&int&b&=&GETARG_B(i)&-&1;&&&&&&&&int&j;&&&&&&&&int&n&=&cast_int(base&-&ci-&func)&-&cl-&p-&numparams&-&1;&&&&&&&&if&(b&&&0)&{&&/*&B&==&0?&*/&&&&&&&&&&b&=&n;&&/*&get&all&var.&arguments&*/&&&&&&&&&&Protect(luaD_checkstack(L,&n));&&&&&&&&&&ra&=&RA(i);&&/*&previous&call&may&change&the&stack&*/&&&&&&&&&&L-&top&=&ra&+&n;&&&&&&&&}&&&&&&&&for&(j&=&0;&j&&&b;&j++)&{&&&&&&&&&&if&(j&&&n)&{&&&&&&&&&&&&setobjs2s(L,&ra&+&j,&base&-&n&+&j);&&&&&&&&&&}&&&&&&&&&&else&{&&&&&&&&&&&&setnilvalue(ra&+&j);&&&&&&&&&&}&&&&&&&&}&&&&&&)&&&&&&vmcase(OP_EXTRAARG,&&&&&&&&lua_assert(0);&&&&&&)&&&&}&&}}此函数先从CallInfo中取出运行的lua closure,取出这个closure的寄存器的base指针和closure的函数Proto的常量列表k。debug hook进入for循环开始执行代码,先取出当前指令Instruction,根据Lua State的hook mask来判断是否需要hook代码执行,这个hook代码执行就是lua提供给外界调试代码的库,我们可以使用这个debug库实现自己的调试器,两年前我使用这个debug实现过一个简单的lua调试器。(博客:&代码放在github上:)lua提供了四种hookmask,分别是:#define&LUA_MASKCALL&&&&&(1&&&&LUA_HOOKCALL)#define&LUA_MASKRET&&&&&(1&&&&LUA_HOOKRET)#define&LUA_MASKLINE&&&&&(1&&&&LUA_HOOKLINE)#define&LUA_MASKCOUNT&&&&&(1&&&&LUA_HOOKCOUNT)LUA_MASKCALL表示每次调用函数的时候hook;LUA_MASKRET表示每次函数返回的时候hook;LUA_MASKLINE表示每行执行的时候hook;LUA_MASKCOUNT表示每执行count条lua指令hook一次,这里的count是debug.sethook ([thread,] hook, mask [, count])中传递的。LUA_MASKLINE和LUA_MASKCOUNT类型的hook是在函数的开头这段代码里hook:& &&if&((L-&hookmask&&&(LUA_MASKLINE&|&LUA_MASKCOUNT))&&&&&&&&&&&(--L-&hookcount&==&0&||&L-&hookmask&&&LUA_MASKLINE))&{&&&&&&Protect(traceexec(L));&&&&}而LUA_MASKCALL和LUA_MASKRET类型的hook则分别在call和return的时候hook,具体是在ldo.c中的luaD_precall和luaD_poscall中hook。如果设置了debug hook,那执行指令的时候就会检测一下是否需要调用hook函数。若需要LUA_MASKLINE或LUA_MASKCOUNT的hook则调用lvm.c中的traceexec函数,而traceexec函数通过调用ldo.c中的luaD_hook函数完成;若需要LUA_MASKCALL或LUA_MASKRET的hook则ldo.c中的luaD_precall和luaD_poscall会对hook进行检测,最终还是调用到ldo.c中的luaD_hook函数完成。void&luaD_hook&(lua_State&*L,&int&event,&int&line)&{&&lua_Hook&hook&=&L-&&&if&(hook&&&&L-&allowhook)&{&&&&CallInfo&*ci&=&L-&&&&&ptrdiff_t&top&=&savestack(L,&L-&top);&&&&ptrdiff_t&ci_top&=&savestack(L,&ci-&top);&&&&lua_Debug&&&&&ar.event&=&event;&&&&ar.currentline&=&&&&&ar.i_ci&=&&&&&luaD_checkstack(L,&LUA_MINSTACK);&&/*&ensure&minimum&stack&size&*/&&&&ci-&top&=&L-&top&+&LUA_MINSTACK;&&&&lua_assert(ci-&top&&=&L-&stack_last);&&&&L-&allowhook&=&0;&&/*&cannot&call&hooks&inside&a&hook&*/&&&&ci-&callstatus&|=&CIST_HOOKED;&&&&lua_unlock(L);&&&&(*hook)(L,&&ar);&&&&lua_lock(L);&&&&lua_assert(!L-&allowhook);&&&&L-&allowhook&=&1;&&&&ci-&top&=&restorestack(L,&ci_top);&&&&L-&top&=&restorestack(L,&top);&&&&ci-&callstatus&&=&~CIST_HOOKED;&&}}我们发现这个调用的hook函数是注册在L-&hook中的C函数指针,我们通过debug.sethook注册的hook函数是lua的函数,那这个注册的C函数肯定是用来完成lua函数与C函数之间的转换。L-&hook这个函数指针的注册是通过ldebug.c中的lua_sethook函数完成:LUA_API&int&lua_sethook&(lua_State&*L,&lua_Hook&func,&int&mask,&int&count)&{&&if&(func&==&NULL&||&mask&==&0)&{&&/*&turn&off&hooks?&*/&&&&mask&=&0;&&&&func&=&NULL;&&}&&if&(isLua(L-&ci))&&&&L-&oldpc&=&L-&ci-&u.l.&&L-&hook&=&&&L-&basehookcount&=&&&resethookcount(L);&&L-&hookmask&=&cast_byte(mask);&&return&1;}在ldblib.c中的db_sethook中调用了lua_sethook函数,这个hook函数是ldblib.c中的hookf:static&void&hookf&(lua_State&*L,&lua_Debug&*ar)&{&&static&const&char&*const&hooknames[]&=&&&&{"call",&"return",&"line",&"count",&"tail&call"};&&gethooktable(L);&&lua_pushthread(L);&&lua_rawget(L,&-2);&&if&(lua_isfunction(L,&-1))&{&&&&lua_pushstring(L,&hooknames[(int)ar-&event]);&&&&if&(ar-&currentline&&=&0)&&&&&&lua_pushinteger(L,&ar-&currentline);&&&&else&lua_pushnil(L);&&&&lua_assert(lua_getinfo(L,&"lS",&ar));&&&&lua_call(L,&2,&0);&&}}这个函数就把注册的lua hook函数取出来然后调用,传递的hook类型作为hook函数的第一参数,分别是{"call", "return", "line", "count", "tail call"}。寄存器结构lua是寄存器虚拟机,它为每个函数在运行时期最多分配250个寄存器。函数运行时都是通过这些寄存器来操作数据,指令操作寄存器的参数都是记录着相应寄存器的下标。在for循环中,通过RA(i)获取到指令i的参数A的寄存器,lua指令格式在上一篇中有介绍(),RA宏获得A参数的寄存器下标,再加上当前运行函数的base指针,就可以得出相应的寄存器。再之后通过GET_OPCODE(i)获得opcode并进入switch-case,分别针对每条指令类型取出相应的其它指令参数并执行。lua寄存器结构如图:对每条指令分别根据指令类型操作A、B、C、Ax、Bx、sBx参数,参数可以是寄存器的下标,也可以是Proto的常量列表k的下标。case的第一条指令OP_MOVE就是最简单的指令,从指令i中取出参数B,然后把B指向的TValue赋值给A指向的TValue。从常量列表中把TValue load到寄存器中的指令有两种,分别是OP_LOADK和OP_LOADKX。在OP_LOADK中,参数Bx就是Proto的常量列表的下标,然后简单的将这个TValue load到寄存器RA(i)中,如果一个函数的常量很多,个数超过了,参数Bx(14~31bits,共18位)的表示范围,这时候就要使用OP_LOADKX指令表示。在OP_LOADKX指令中,会继续读取下一条指令,下一条指令的类型是OP_EXTRAARG,它的参数是Ax(6~31bits,共26位)来表示Proto的常量列表的下标,这样常量的个数就扩大到了26位的表示范围。函数调用的栈结构lua的函数调用指令是OP_CALL和OP_TAILCALL,实际上函数调用是通过luaD_precall完成,这个函数判断被调用的函数是否是C函数,如果是C函数的话那就将函数执行完返回,如果不是则准备好一些基本数据,并把指令切换到被调用的lua函数的指令地址上,然后执行被调用函数的指令。int&luaD_precall&(lua_State&*L,&StkId&func,&int&nresults)&{&&lua_CFunction&f;&&CallInfo&*&&int&n;&&/*&number&of&arguments&(Lua)&or&returns&(C)&*/&&ptrdiff_t&funcr&=&savestack(L,&func);&&switch&(ttype(func))&{&&&&case&LUA_TLCF:&&/*&light&C&function&*/&&&&&&f&=&fvalue(func);&&&&&&goto&C&&&&case&LUA_TCCL:&{&&/*&C&closure&*/&&&&&&f&=&clCvalue(func)-&f;&&&&&Cfunc:&&&&&&luaD_checkstack(L,&LUA_MINSTACK);&&/*&ensure&minimum&stack&size&*/&&&&&&ci&=&next_ci(L);&&/*&now&'enter'&new&function&*/&&&&&&ci-&nresults&=&&&&&&&ci-&func&=&restorestack(L,&funcr);&&&&&&ci-&top&=&L-&top&+&LUA_MINSTACK;&&&&&&lua_assert(ci-&top&&=&L-&stack_last);&&&&&&ci-&callstatus&=&0;&&&&&&if&(L-&hookmask&&&LUA_MASKCALL)&&&&&&&&luaD_hook(L,&LUA_HOOKCALL,&-1);&&&&&&lua_unlock(L);&&&&&&n&=&(*f)(L);&&/*&do&the&actual&call&*/&&&&&&lua_lock(L);&&&&&&api_checknelems(L,&n);&&&&&&luaD_poscall(L,&L-&top&-&n);&&&&&&return&1;&&&&}&&&&case&LUA_TLCL:&{&&/*&Lua&function:&prepare&its&call&*/&&&&&&StkId&base;&&&&&&Proto&*p&=&clLvalue(func)-&p;&&&&&&luaD_checkstack(L,&p-&maxstacksize);&&&&&&func&=&restorestack(L,&funcr);&&&&&&n&=&cast_int(L-&top&-&func)&-&1;&&/*&number&of&real&arguments&*/&&&&&&for&(;&n&&&p-&&n++)&&&&&&&&setnilvalue(L-&top++);&&/*&complete&missing&arguments&*/&&&&&&base&=&(!p-&is_vararg)&?&func&+&1&:&adjust_varargs(L,&p,&n);&&&&&&ci&=&next_ci(L);&&/*&now&'enter'&new&function&*/&&&&&&ci-&nresults&=&&&&&&&ci-&func&=&&&&&&&ci-&u.l.base&=&base;&&&&&&ci-&top&=&base&+&p-&&&&&&&lua_assert(ci-&top&&=&L-&stack_last);&&&&&&ci-&u.l.savedpc&=&p-&&&/*&starting&point&*/&&&&&&ci-&callstatus&=&CIST_LUA;&&&&&&L-&top&=&ci-&&&&&&&if&(L-&hookmask&&&LUA_MASKCALL)&&&&&&&&callhook(L,&ci);&&&&&&return&0;&&&&}&&&&default:&{&&/*&not&a&function&*/&&&&&&func&=&tryfuncTM(L,&func);&&/*&retry&with&'function'&tag&method&*/&&&&&&return&luaD_precall(L,&func,&nresults);&&/*&now&it&must&be&a&function&*/&&&&}&&}}lua的函数调用栈是通过一个CallInfo的链表来表示,每一个CallInfo链表元素表示一层函数调用,每个CallInfo通过prev和next指针分别指向前面的函数和后面的函数。CallInfo中的base和top分别指向这个调用栈帧的起始地址和结束地址,base到top这些栈空间在函数运行内部就是可用的寄存器。func则指向这个被调用函数的closure所在lua栈中的地址。函数CallInfo链表结构与lua的栈的格式的关系有如下3种:1.被调用函数为普通lua函数时,调用者把被调用函数的closure放到栈中,然后把传入函数的参数依次放入栈中。被调用者的CallInfo中的func指针指向它所属的closure,并把这个运行时期的base指针指向传进来的第一参数,如下图:2.被调用函数是vararg(变参)的lua函数时,被调用者的CallInfo的func还是指向相应的closure,固定参数则会复制一份,并把原来的设置为nil,而多出的参数则保留在原始位置,并将base指针指向复制的第一个实参。这样,base指针前面的就是多出的参数,即固定的参数是从base指针指向的地方开始,而变参数则在base指针前面,这样可以保证后续的指令访问固定的参数跟非可变参数函数(第一种情况)时一致。例如:一个变参函数function f(a, b, ...) end,这样调用f(1, 2, 3, 4),那么会把1和2复制一份,分别作为a和b的实参,3和4则保留在原始位置,也就是在base指针之前。3.被调用的函数是C函数的时候,CallIInfo的top指向L-&top + LUA_MINSTACK(20),为C函数操作lua栈预留的最小stack空间,在被调用的C函数中若使用的lua栈空间比较多时,需要调用lua_checkstack来向lua申请保证有足够的栈空间使用,不然就会出现lua stack overflow的错误。函数调用完成后,lua通过指令OP_RETURN返回,这时候,最后一个CallIInfo就回收了。在回收之前,通过luaD_poscall来将函数的返回值复制到相应的位置,函数返回值复制到的位置的起点就是closure的位置,把closure覆盖掉。若调用的CallInfo表示的是C函数时,也是通过luaD_poscall完成返回值的复制。int&luaD_poscall&(lua_State&*L,&StkId&firstResult)&{&&StkId&&&int&wanted,&i;&&CallInfo&*ci&=&L-&&&if&(L-&hookmask&&&(LUA_MASKRET&|&LUA_MASKLINE))&{&&&&if&(L-&hookmask&&&LUA_MASKRET)&{&&&&&&ptrdiff_t&fr&=&savestack(L,&firstResult);&&/*&hook&may&change&stack&*/&&&&&&luaD_hook(L,&LUA_HOOKRET,&-1);&&&&&&firstResult&=&restorestack(L,&fr);&&&&}&&&&L-&oldpc&=&ci-&previous-&u.l.&&/*&'oldpc'&for&caller&function&*/&&}&&res&=&ci-&&&/*&res&==&final&position&of&1st&result&*/&&wanted&=&ci-&&&L-&ci&=&ci&=&ci-&&&/*&back&to&caller&*/&&/*&move&results&to&correct&place&*/&&for&(i&=&&i&!=&0&&&&firstResult&&&L-&&i--)&&&&setobjs2s(L,&res++,&firstResult++);&&while&(i--&&&0)&&&&setnilvalue(res++);&&L-&top&=&&&return&(wanted&-&LUA_MULTRET);&&/*&0&iff&wanted&==&LUA_MULTRET&*/}尾递归lua对于递归会有尾递归优化,如果一个函数调用是尾递归的话,那么函数的调用栈是不会增长的。lua通过OP_TAILCALL指令完成尾递归调用,这条指令的前面一段跟OP_CALL相似,通过luaD_precall增加函数调用栈信息CallInfo。当luaD_precall返回时,调用的不是C函数,则会将新增的CallInfo与上一个CallInfo栈帧合并,然后把新增的CallInfo移除掉,这样的尾递归调用就不会导致栈帧增长了。lua的其它指令就是很明确的操作一些寄存器和常量来完成代码执行。
阅读(4802)
&re: lua源码剖析(三):VM& 13:42&
lz的分析很赞请问一下:变参数的函数被调用时,参数为啥需要复制一份,base指针则指向复制的那些实参的第一个地址。&&&&&&
&re: lua源码剖析(三):VM& 15:56&
@zenk我对这段描述不够清楚,现在已经补上了。复制的只是固定的那些参数,&...&所代表的变参并没有复制,保留在base指针前面,这样可以保证后续指令访问固定的那些参数和非可变参数函数是一致。&&&&&&
&re: lua源码剖析(三):VM& 08:06&
@airtrack按照你说的意思访问变参数的寄存器应该是小于0的,但是看了一下源代码没发现lua是如何处理,估计是OP_VARARG这个没理解透彻想请教一下,lua如何处理访问变参数的,比如说可以通过arg这个本地变量访问,那lua什么时候创建这个本地变量(看了一下午源码没明白)谢了&&&&&&
&re: lua源码剖析(三):VM& 09:13&
@zenkOP_VARARG这条指令就是访问...参数的,可以看到用int n = cast_int(base - ci-&func) - cl-&p-&numparams - 1;来计算可变参数变参的个数(cl-&p-&numparams是这个函数的固定参数的个数),接下来在判断B参数是否为0,如果为0,那就有多少个变参就复制多少个,不然就复制B - 1个变参。在lua中如下代码会生成OP_VARARG指令:function f(...)
print(...)end至于arg变量,在lua 5.2中已经不会自动打包可变参数...为一个arg变量,在lua 5.2中要使用arg变量可以这样写:function f(...)
local arg = table.pack(...)end&&&&&&
&re: lua源码剖析(三):VM& 13:45&
@airtrack谢谢,受教了&&&&&&

我要回帖

更多关于 易语言hook封包源码 的文章

 

随机推荐