??xml version="1.0" encoding="utf-8" standalone="yes"?> 我用的wxLua版本信息?code>wxLua 2.8.7.0 built with wxWidgets 2.8.8Q也是LuaForWindows_v5.1.4-40.exeq个安装包里自带的wxLua。我不知道其他wxWidgets版本里wxListCtrl怎样Q但我用的版本里wxListCtrl是不支持~辑里面的子item的。在我用的report模式下,子item也就是特定某一行一列的item?/p>
google了一下,发现(zhn)剧地需要自己实玎ͼ主要是自己昄一个wxTextCtrlQ?/p>
其原理就是获取到当前鼠标点击所在的子item位置Q然后在此位|显CZ个wxEditCtrl卛_。以上代码需要依赖我之前写的Lua里实现简单的c?对象中的代码Q同时依赖以下针对wxListCtrl的扩展接口: 在我看到的wxWidgets官方文档里,其实wxListCtrl已经?code>GetSubItemRect接口Qƈ且在另一些示例代码里Q也看到?code>GetItemText接口Q但是,我用的版本里没有,所以只好自己写。基于以上,要用这个可以支持编辑子item的wxListCtrlQ可以: 也就是通过wx.wxListCtrlTextEditq个函数做下处理Q这个函数返回的是本w的wxListCtrl。当然更好的方式是用承之cȝ方式Q开发一U新的控Ӟ但在Lua中,针对usedatacd的扩展貌似只能这样了?/p>
最好吐槽下Q这个控件扩展其实很恶心。本来我打算当编辑控件失ȝ点后隐藏它Q但是往~辑控g上注册KILL_FOCUS事g始终不v作用Q我又打弄个ESC键盘事gL动取消,但显然wxTextCtrl是不支持键盘事g的。好吧,凑合用了?/p>
--
-- file: wxListCtrlTextEdit.lua
-- author: Kevin Lynx
-- date: 08.06.2012
--
local EditList = {}
-- get the column by an abs point
function EditList:getColumn(x)
local cols = self.listctrl:GetColumnCount()
local cx = 0
for i = 0, cols - 1 do
local w = self.listctrl:GetColumnWidth(i)
if x <= cx + w then return i end
cx = cx + w
end
return -1
end
-- when a mouse down, show a text edit control
function EditList:onLeftDown(evt)
if self.editor:IsShown() then
self:closeEditor()
end
local p = evt:GetPoint()
local row = evt:GetIndex()
local col = self:getColumn(p.x)
local rect = wx.wxListCtrlEx.GetSubItemRect(self.listctrl, row, col)
rect:SetHeight(rect:GetHeight() + 5) -- adjust
self.editor:SetSize(rect)
self.editor:Show()
self.editor:SetValue(wx.wxListCtrlEx.GetItemText(self.listctrl, row, col))
self.editor:SetFocus()
self.col = col
self.row = row
end
function EditList:closeEditor()
if not self.editor:IsShown() then return end
self.editor:Hide()
self.listctrl:SetItem(self.row, self.col, self.editor:GetValue())
end
function EditList:initialize()
self.editor = wx.wxTextCtrl(self.listctrl, wx.wxID_ANY, "", wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxTE_PROCESS_ENTER + wx.wxTE_RICH2)
self.editor:Connect(wx.wxEVT_COMMAND_TEXT_ENTER, function () self:closeEditor() end)
-- not work actually
self.editor:Connect(wx.wxEVT_COMMAND_KILL_FOCUS, function () self:closeEditor() end)
self.editor:Hide()
end
function wx.wxListCtrlTextEdit(listctrl)
local o = {
listctrl = listctrl,
editor = nil,
}
local editlist = newObject(o, EditList)
editlist:initialize()
listctrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, function (evt) editlist:onLeftDown(evt) end)
listctrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_FOCUSED, function () editlist:closeEditor() end)
return listctrl
end
--
-- file: wxListCtrlExtend.lua
-- author: Kevin Lynx
-- date: 08.07.2012
-- brief: extend some util functions to wx.wxListCtrl
--
wx.wxListCtrlEx = {}
function wx.wxListCtrlEx.GetSubItemRect(listctrl, item, col)
local rect = wx.wxRect()
listctrl:GetItemRect(item, rect)
local x = 0
local w = 0
for i = 0, col do
w = listctrl:GetColumnWidth(i)
x = x + w
end
return wx.wxRect(x - w, rect:GetY(), w, rect:GetHeight())
end
function wx.wxListCtrlEx.GetItemText(listctrl, item, col)
local info = wx.wxListItem()
info:SetId(item)
info:SetColumn(col)
info:SetMask(wx.wxLIST_MASK_TEXT)
listctrl:GetItem(info)
return info:GetText()
end
list = wx.wxListCtrlTextEdit(wx.wxListCtrl(dialog, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxLC_REPORT))
我们使用tolua++手工l定c/c++接口到l(f)ua中,在绑定的接口实现里,需要取Z入的参数。tolua++中提供了一pdtolua_toxxx函数Q例如:
lua_Number tolua_tonumber(lua_State *L, int narg, lua_Number def)
const char *tolua_tostring(lua_State *L, int narg, const char *def)
q些函数都有一个def参数。乍一看,q些函数使用h很简单。传入lua_StateQ传入参数在栈中的位|,然后再传一?strong>p|后返回的默认倹{?/p>
我重点要说的是这里这?strong>p|Q按正常E序员的理解Q针对lua而言Q什么情况下失败呢Qlua语言里函数参数支持不传,此时实参为nilQ将nil转换Z个ccd必然p|Q参数类型不正确不失败?你传一个user dataQc里按数字来取Q这也算p|?/p>
q么单的APIq需要多U结什么呢Q然后我们浩荡荡地写了上百个接口,什么tolua_tostring/tolua_tonumber的用少说也?00了吧Q?/p>
然后有一天,服务器宕ZQ空指针:
/* p|q回""Q还能省I指针的判断 */
const char *name = tolua_tostring(L, 1, "");
if (name[0] == '\0') { /* IZd判断?*/
...
}
跟踪后发玎ͼ脚本里传入的是nilQ这里的name取出来是NULLQ而不?#8221;“Q的地址Q。然后吐槽了一下这个APIQ辛苦地修改了所有类g码,增加对空指针的判断。我没有多想?/p>
故事l箋Q有一天服务器虽然没宕机,但功能不正常?
float angle = (float) tolua_tonumber(L, 1, 2 * PI);
...
q个意思是Q这个函数的参数1默认?*PIQ什么是默认Qlua里某函数参数不传Q或传nil是使用默认。因Z传的话,q个实参本n是nil。但Qtolua_tonumber的行Z是这LQ它的实现真是偷?
TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def)
{
return lua_gettop(L)<abs(narg) ? def : lua_tonumber(L,narg);
}
TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def)
{
return lua_gettop(L)<abs(narg) ? def : lua_tostring(L,narg);
}
意思是Q只有当你不传的时候,它才q回默认|否则׃llua的API来管Q而luaq些API是不支持应用层的默认参数的,对于lua_tonumber错误时就q回0Qlua_tostring错误时就q回NULL?/p>
q种其行为和其带来的common sense不一致的API设计Q实在让疹{什么是common sense呢?像一个UI库里的按钮,我们都知道有click事gQhover事gQUI库的文档甚至都不需要解释什么是click什么是hoverQ因为大家看到这个东西,有了共识,无需废话Q这是common sense。就像tolua的这些APIQ非常普通,大家一看都期待在意外情况下你能q回def倹{但它竟然不是。实在不行,你可以模仿lua的checkpd函数的实现嘛:
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
lua_Number d = lua_tonumber(L, narg);
if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
tag_error(L, narg, LUA_TNUMBER);
return d;
}
卻IҎ(gu)不用L查栈问题Q直接在lua_tonumber之后再做包装查。何况,lua需要你L查栈吗?当你讉K了栈外的元素Ӟlua会自动返回一个全局帔RluaO_nilobject:
static TValue *index2adr(lua_State *L, int idx) {
...
if (o >= L->top) return cast(TValue*, luaO_nilobject);
}
另,E序(zhn)剧也来源于臆想?/p>
在Lua中,因为函数也是第一类值,所以会出现将函数作为另一个函数的参数,或者函数作 为函数的返回值。这种机制在很多地方都能代码更灵活更简洁,例如:
table.sort(table [,comp])
这里的comp就要求传入一个函数,我们在调用时,大概会有如下形式:
table.sort(t, comp) -- 直接写函数名 table.sort(t, local_comp) -- 某个局部函数 table.sort(t, function (a, b) xxx end ) -- 临时构造一个匿名函数
其中最后一种方式最为灵活,任意时候在需要的时候构造一个匿名函数。这种在Lua自身的 环境中使用,自然没有问题。但是,当我们在C/C++中注册一些函数到Lua环境中,而这些 函数也需要使用函数参数的时候,问题就出来了。
Lua本身是不支持将Lua函数作为函数参数传入C/C++的,不管这个想要传入的函数是全局的 、局部的、或者匿名的(匿名的本质上也算局部的)。一般情况下,我们唯一的交互方式, 不是传入一个函数,而是一个全局函数名。C/C++保存这个函数名,在需要回调Lua的时候, 就在Lua全局表中找到这个函数(根据函数名),然后再调用之。情况大致如下:
function lua_func () xxx end cfunc(lua_func) -- wrong cfunc("lua_func") -- right
我们这回的脚本模块,策划会大量使用需要回调函数的C/C++函数。显然,创建大量的全局 函数,先是从写代码的角度看,就是很伤神的。
我们最终需要的方式,大概如下:
cfunc(lua_func) -- ok cfunc(function () xxx end) -- ok local xxx = function () xxx end cfunc(xxx) -- ok
要解决这个问题,我的思路是直接在Lua层做一些包装。因为C/C++那边仅支持传入一个全局 函数名(当然不一定得全局的,根据实际情况,可能在其他自己构造的表里也行),也就是 一个字符串,所以我的思路就是将Lua函数和一个唯一的字符串做映射。:
function wrap (fn) local id = generate_id() local fn_s = "__callback_fn"..id _G[fn_s] = fn return fn_s end
这个wrap函数,就是将一个函数在全局表里映射到一个字符串上,那么在使用时:
cfunc(wrap(function () xxx end)) cfunc(const char *fn_name, xxx); -- cfunc的原型
cfunc是C/C++方注册进Lua的函数,它的原型很中规中矩,即:只接收一个函数名,一个字 符串,如之前所说,C/C++要调用这个回调函数时,就根据这个字符串去查找对应的函数。 脚本方在调用时,如果想传入一个匿名函数了,就调用wrap函数包装一下即可。
上面的方法有个很严重的问题,在多次调用wrap函数后,将导致全局表也随之膨胀。我们需 要想办法在C/C++完成回调后,来清除wrap建立的数据。这个工作当然可以放到C/C++来进行 ,例如每次发生回调后,就设置下全局表。但这明显是不对的,因为违背了接口的设计原则 ,这个额外的机制是在Lua里添加的,那么责任也最好由Lua来负。要解决这个问题,就可以 使用Lua的metamethods机制。这个机制可以在Lua内部发生特定事件时,让应用层得到通知。 这里,我们需要关注__call事件。
Lua中只要有__call metamethod的值,均可被当作函数调用。例如:
ab(1, 2)
这里这个函数调用形式,Lua就会去找ab是否有__call metamethod,如果有,则调用它。这 个事实暗示我们,一个table也可以被调用。一个改进的wrap函数如下:
local function create_callback_table (fn, name) local t = {} t.callback = fn setmetatable (t, {__call = -- 关注__call function (func, ...) -- 在t(xx)时,将调用到这个函数 func.callback (...) -- 真正的回调 del_callback (name) -- 回调完毕,清除wrap建立的数据 end }) return t end function wrap (fn) local id = generate_func_id() -- 产生唯一的id local fn_s = "_callback_fn"..id _G[fn_s] = create_callback_table(fn, fn_s) -- _G[fn_s]对应的是一个表 return fn_s end
在我们的C/C++程序中,依然如往常一样,先是从_G里取出函数名对应的对象。虽然这个对 象现在已经是一个table。然后lua_call。
上面的代码是否会在原有基础上增加不可接受的性能代价?虽然我没有做过实际测试,但是 从表明看来,排除meta table在Lua里的代价,也就多了几次Lua函数调用。
最后,感叹一下,Lua里的table及metatable机制,实在非常强大。这种强大不是功能堆砌 出来的强大,而是简单东西组合出来的强大。其背后的设计思想,着实让人佩服。
4.26.2011 Update
之前的文中说“Lua本身是不支持将Lua函数作为函数参数传入C/C++的“,这句话严格来说不 正确(由某网友评论)。假设函数cfun由c/c++注册,我们是可以编写如下代码的:
cfunc(print) -- 传入Lua函数
但是问题在于,我们无法取出这个函数并保存在c/c++方。Lua提供了一些接口用于取cfunc 的参数,例如luaL_checknumber(封装lua_tonumber)。但没有类似luaL_checkfunction的 接口。Lua中的table有同样的问题。究其原因,主要是Lua中的函数没有直接的c/c++数据结 构对应。
;; END
另一个同事在iptux的基上修改的Q结果大概是因ؓiptux的代码不是那么容易修改,׃了了之了。这?/font>
刚发布的luafeiq功能非常单,仅支持与飞秋Q包括大部分兼容IP messager的IMQ进行单聊,?/font>消息?/font>
收发Q简易的消息盒子Q暂存未L息)。因为选的库都是跨q_的,所以很Ҏ(gu)的luafeiq也是?/font>q_的,
最主要的是我想在linux下用?/font>
之所以选用luaQ一斚w是想l练luaQ另一斚w则是因ؓ开发效率。前D|间在android下写了些java代码Q?/font>
用java写代码觉得甚为爽快(当然不了完)。这几天写了千把行的luaQ也许有3K行,未统计过Q,
感觉也不错。综合来_q些高语言的很多好用的语法Ҏ(gu),例如闭包QclosureQ,垃圾回收Q都提高
了不写代码的速度。当Ӟlua于我而言也算不上完美的语a。例如我l常因ؓ变量敲错字母Q而在q行?/font>
才暴露nil错误。这也许可以通过诸如IDE之类的工具在写代码的时候就l予提示。lua 在遇C个符hQ?/font>
默认地将其处理ؓ全局的。关于这个语法特性早有h提出不爽Q只能说大家设计的准则不一栗(在我?/font>
目里,我直接改写了全局变量的metatableQ从而防止策划随意定义全局变量Q?/font>
再来谈谈实现q程中的一些琐事。因为飞U也是IP messager协议的兼容实玎ͼ很多通信除了可以使用
抓包软g分析外,q可以直接通过IP messager的源码来了解。所以,基础通信协议的实现过E也比较
单。飞U与飞秋之间发送私聊消息是l过加密的。其加密q程也不单,更重要的是,我ƈ不想费?/font>
多时间在q上面。后来发现其实可以通过上线消息里某个标志位表明自己不需要加密。这个标志就是消息头
里的option。上U广播出ȝ消息里一旦表明自׃加密Q那么以后和飞秋通信也就不需要解密了?/font>
发送私聊消息时Q消息里会携带一个消息ID。这个ID可以通过L法生成Q例如直接取time的倹{接收到
Ҏ(gu)的消息时Q需要取IDQ然后加入回应消息。对Ҏ(gu)到回应消息后Q就知道自己发送成功。这个过E?/font>
是ip messager在UDP上做的消息可靠验证,q程也比较简单?/font>
聊消息在之前提到过Q是通过UDP多播实现。我们可以接收所有群的消息。如果之前已l处于某个群里,
那么一旦你上线后(q播上线消息Q,你就可以直接在这个群里发a。但如果你之前不在这个群里,?/font>
可以通过多播一个加入群的消息,然后可以不误来地在这个群里发a。详l的消息值和实现都可以从
luafeiq的代码里dQmessage_sender.luaQ?/font>
在linux下接收windows上的飞秋消息Q是需要做字符~码转换的。因为luafeiq使用IUP作ؓUI库,IUP?/font>
linux下用GTK作ؓ底层实现Q默认全部是UTF8~码。luafeiq里我自己写了个lua库,用于~码转换?/font>
话说IUP作ؓ一个UI库,q是比较不错的。正如其介绍文档里所_学习曲线低,基本上看一会文档,可?/font>
直接使用了。luafeiq使用的IUP版本臛_需?.0以上。当初在linux下ؓ了安装IUP3.3Q基本花?个小?/font>
旉Q各U奇怪的没多大意义的错误信息。后来换?.2版本Q居然一下子和谐了Q无限怨念?/font>
luafeiq目前攑֜googlecode的版本,可以说是一个很不负责Q的版本。早上我才刚把字W编码{换的代码
调试好。今天已l请假,安׃台电(sh)脑,也就试不了q个字符~码转换是否真的能正常工作。我?/font>
windows下dump了些字符Q看上去能正常功能。明天得回老家q春节,上不了网Q烦性就提前发布了?/font>
luafeiq目地址Q?a title="http://code.google.com/p/luafeiq/" >http://code.google.com/p/luafeiq/
说白了,也就是要让tolua++在生成的代码文g开头插?include "stdafx.h"?
修改代码其实很简单。tolua++分析pkg文g及生成代码文件其实都是通过lua代码完成的?br>在src/bin/lua目录下,或者在源代码里toluabind.c里(把对应的lua代码直接以ASCII码?br>复制了过来)即ؓq些代码?
首先修改package.lua里的classPackage::preamble函数Q可以看函数会生成一些代?br>文g_模仿着卛_写下如下代码Q?
if flags['I'] then
output( '#include "..flags['I'] )
end
从上下文代码可以看出flags是个全局变量Q保存了命o行参数?
然后修改tolua.c代码文gQ让其往lua环境里传入命令行参数Q?
case 'I':setfield(L,t,"I",argv[++i];break;
本来Q这样修改后基本可以让tolua++支持通过命o行指定是否插入预~译_
tolua++ -o test.cpp -H test.h -I stdafx.h test.pkg
不过事情q很顺利,通过开启TOLUA_SCRIPT_RUN宏来让tolua++通过src/bin/lua下的lua
代码来完成功能,l果后来发现basic.luag有问题。无奈之下,只好用winhex之类的工
h修改q的package.lua转换为unsigned char B[]|于toluabind.c里,卛_正常处理?
很早前在折腾挂vLUA脚本支持Ӟ接触到l(f)ua_yieldq个函数。lua manual中给的解释是Q?
This function should only be called as the return expression of a C function?
而这个函C般是在一个注册到LUA环境中的C函数里被调用。lua_CFunction要求的原型里
Q函数的q回值必返回要q回到LUA脚本中的值的个数。也是_在一个不需要挂L
lua_CFunction实现里,也就是一个不需要return lua_yield(...的实现里Q我应该return
一个返回g数?
但是Z么调用lua_yield必L在return表达式里Q当时很天真Q没LIӞ反正发现
不按照lua manual里说的做是不行。而且关键是,lua manual׃告诉你ؓ什么?
最q突然就惛_q个问题Q决定去搞清楚这个问题。侯捯了,源码面前了无U密。我甚至
在看代码之前Q还琢磨着LUA是不是操作了堆栈Q系l堆栈)之类的东ѝ结果随便跟了下
代码真的让我很汗颜。有时候h犯傻了真的是一个?zhn)剧。诺单的一个问题会被h搞得很神
U:
解释执行调用一个注册进LUA的lua_CFunction是在ldo.c里的luaD_precall函数里,有如?br>代码Q?
n = (*curr_func(L)->c.f)(L); /* do the actual call */
lua_lock(L);
if (n < 0) /* yielding? */
return PCRYIELD;
else {
luaD_poscall(L, L->top - n);
return PCRC;
}
多的我就不说了,别h注释写得很清楚了Q注册进ȝlua_CFunction如果q回值小?Q这
个函数就向上层返回PCRYIELDQ从名字可看出是告诉上层需要YIELD。再扑ֈl(f)ua_yield?br>数的实现Q恰好该函数p?1?
要再往上层跟,会到l(f)vm.c里luaV_execute函数Q看h应该是虚拟机在解释执行指oQ?
case 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 */
L->savedpc = pc;
switch (luaD_precall(L, ra, nresults)) {
case PCRLUA: {
nexeccalls++;
goto reentry; /* restart luaV_execute over new Lua function */
}
case PCRC: {
/* it was a C function (`precall' called it); adjust results */
if (nresults >= 0) L->top = L->ci->top;
base = L->base;
continue;
对于PCRYIELDq回|直接忽略处理了?
需?/strong>
受WOW的媄响,LUA来多地被应用于游戏中。脚本被用于游戏中主要用于策划编写游戏规则相兟뀂实际运用中Q?br>我们会将很多宿主语言函数l定到LUA脚本中,使脚本可以更多地控制E序q行。例如我们可以绑定NPCDialog之类的函?br>到LUA中,然后{划便可以在脚本里控制游戏中弹出的NPC对话框?br> 我们现在面͘q样的需求:对于宿主E序而言Q某些功能是不能dE序逻辑的(对于游戏E序其如此Q,但是?br>了方便策划,我们又需要让脚本看v来被d了。用NPCDialog举个例子Q在脚本中有如下代码 Q?br>
对于{划而言QNPCDialog应该是阻塞的Q除非玩家操作此对话框,点击OK或者关闭,不然该函C会返回。而对?br>宿主E序C++而言Q我们如何实现这个函数呢Q?
昄Q该函数不能dQ否则它会阻塞整个游戏线E,q对于服务器而言是不可行的。但是如果该函数立即q回Q那
么它q没有收集到玩家对于那个对话框的操作?br> lgQ我们要做的是,让脚本感觉某个操作阻塞,但事实上宿主E序q没有阻塞?
事g机制
一个最单的实现Q对于CE序员而言也许也是优美的)Q就是用事件机制。我们将对话框的操作l果作ؓ一个事件?br>脚本里事实上没有哪个函数是阻塞的。ؓ了处理一?#8220;d”函数的处理结果,脚本向宿ȝ序注册事件处理器Q同GUI事g
处理其实是一LQ,例如脚本可以q样Q?br>
宿主E序保存事g处理器onEvent函数名,当玩家操作了对话框后Q宿ȝ序回调脚本中的onEventQ完成操作?br> 事实上我怿有很多h实是这么做的。这样做其实是把一个顺序执行的代码,分成了很多块。但是对于sleep
q样的脚本调用呢Q例如:
那么采用事g机制可能会把代码分解ؓQ?br>
代码看v来似乎有炚w看了Q最重要的是它不易编写,{划估计会抓狂的。我惻I对于非专业程序员而言Q程序的
序执行可能理解h更ؓҎ(gu)?
SOLVE IT
我们的解x案,其实只有一句话Q当脚本执行到阻塞操作时Q如NPCDialogQ,挂v脚本Q当宿主E序某个操作?br>成时Q让脚本从之前的挂v点l执行?br> q不是一U假想的功能。我在刚开始实现这个功能之前,以ؓLUA不支持这个功能。我臆想着如下的操作:
脚本Q?br> ret = NPCDialog("Hello bitch")
if ret == 0 then print("OK") end
宿主E序Q?br>
某个地方某个操作完成了:
lua_resume_script( L );
当我实现了这个功能后Q我猛然发现Q实际情况和我这里想的差不多Q有Ҏ(gu)颜)?/font>
认识Coroutine
coroutine是LUA中类似线E的东西Q但是它其实和fiber更相伹{也是_它是一U非抢占式的U程Q它的切换取?br>于Q务本w,也就是取决你Q你军_它们什么时候发生切换。徏议你阅读lua manual了解更多?br> coroutine支持的典型操作有Qlua_yield, lua_resumeQ也是我们需要的挂v和l执行?br> lua_Stateg是一个coroutineQ或者按照LUA文档中的另一U说法,是一个thread。我q里之所以用’g‘?br>因ؓ我自׃无法定Q我只能_lua_State看v来就是一个coroutine?br> LUA提供lua_newthread用于手工创徏一个coroutineQ然后将新创建的coroutine攄于堆栈顶Q如同其他new出来?br>对象一栗网上有帖子说lua_newthread创徏的东西与脚本里调用coroutine.create创徏出来的东西不一P但是Ҏ(gu)?br>的观察来看,他们是一L。lua_newthreadq回一个lua_State对象Q所以从q里可以看出Q?#8220;lua_State看v来就是一?br>coroutine”。另外,|上也有创徏新的coroutine代h(hun)很大Q但是,一个lua_State的代仯有多大?当然Q我没做q?br>试Q不敢多a?br> lua_yield用于挂v一个coroutineQ不q该函数只能用于coroutine内部Q看看它的参数就知道了?br> lua_resume用于启动一个coroutineQ它可以用于coroutine没有q行时启动之Q也可以用于coroutine挂v旉新启?br>之。lua_resume在两U情况下q回Qcoroutine挂v或者执行完毕,否则lua_resume不返回?br> lua_yield和lua_resume对应于脚本函敎ͼcoroutine.yield和coroutine.resumeQ徏议你写写脚本E序感受下coroutineQ?br>例如Q?br>
REALLY SOLVE IT
你可能会惛_Q我们ؓ脚本定义一个mainQ然后在宿主E序里lua_newthread创徏一个coroutineQ然后将main放进去,
当脚本调用宿ȝ序的某个’d‘操作Ӟ宿主E序获取C前创建的coroutineQ然后yield之。当操作完成Ӟ再resume
之?br> 事实上方法是对的Q但是没有必要再创徏一个coroutine。如之前所_一个lua_State看上d是一个coroutineQ?br>而恰好,我们始终都会有一个lua_State。感觉上Q这个lua_State像是main coroutine。(像你的ȝE)
思\是q样Q因为具体实现时Q还是有些问题,所以我|列每个步骤的代码?br> 初始lua_State时如你^时所做:
注册脚本需要的宿主E序函数到L里:
载入脚本文gq执行时E微有点不同Q?br>
在你?#8217;d‘函数里需要挂起coroutineQ?br>
注意Qlua_yield函数非常特别Q它必须作ؓreturn语句被调用,否则会调用失败,具体原因我也不清楚。而在q里Q?br>它作为lua_CFunction的返回|会不会引发错误?因ؓlua_CFunctionU定q回gؓ该函数对于脚本而言的返回g数?br>实际情况是,我看到的一些例子里都这样安排lua_yieldQ所以i do what they do?
在这个操作完成后Q如玩家操作了那个对话框Q,宿主E序需要唤醒coroutineQ?br>
大致步骤p些。如果你要单独创建新的lua_StateQ反而会搞得很麻烦,我开始就是那L做的QL实现不了自己
预想中的效果?
相关下蝲Q?/strong>
例子E序中,我给了一个sleep实现。脚本程序调用sleep时将被挂P宿主E序不断查当前时_当时间到Ӟresume
挂v的coroutine?a href="http://www.shnenglu.com/Files/kevinlynx/lua_test_coro.rar" target=_blank>下蝲例子
8.13补充
可能有时候,我们提供l脚本的函数需要返回一些值给脚本Q例如NPCDialogq回操作l果Q我们只需要在宿主E序里lua_resume
之前pushq回值即可,当然Q需要设|lua_resumeW二个参Cؓq回g数?br>
2.9.2010
lua_yield( L, nResults )W二个参数指定返回给lua_resume的g数。如下: