ltblue
发表于 2015-4-23 17:02:11
回复cmud
个人认为最理想的机器,并不是协程的随意引用,因为那会造成大量的回调函数的使用,并 ...
littleknife 发表于 2015-4-22 06:02 PM http://pkuxkx.com/forum/images/common/back.gif
以上是代码上我不懂的地方
理念上,还有点糊涂
1.
貌似是把所有任务,每个写成一个函数是吧?
看你DO_JOB这个函数,自身没有make
可是你举例子的渡河和打坐,自身都有wait.make啊
按照你的理念,是应该自身带make的吗?还是不带?
又或者说,渡河和打坐这两个模块特殊,不得不用协程?
2.
关于协程,我有几个根本性的问题不懂
(1)
协程是不是只能自己yield,或者整个协程代码结束,才能交出控制权?
如果我某个协程正在运行,但我没yield,我调用了其他协程,是不是得等自己yield了,其他协程才能运行?
(2)
如果3个协程,a、b、c,都处于运行态,目前a正在运行,然后a协程yield了,那么是b协程抢占还是c协程抢占?还是随机?我感觉好像是随机的。
3.
对于各种突发情况处理,按照你的思路,是不是应该同时出现多个协程?
比如:我正在等渡船,在打坐,忽然有人pk我,我怎么处理?
我的理解,按照你的思路,大概是有一个专门负责pk的协程,一直处于yield状态,用wait.regexp的临时触发,来等待运行。一旦开始运行,就根据目前的状态变量,暂停其他运行中的协程(但如果协程必须自己yield,就不好办了,这个基础的东西我不确定),运行自身代码。等pk结束,再运行其他协程,yield自己。
是不是这么个意思?
4.
我最不能理解的是,用wait写的目的,比zmud式的触发,强在哪里呢?
论代码的简便性,应该是zmud式的简单。状态变量用触发也可以做,wait.regexp也是用的触发器啊,难道只是为了版本更新?那个用插件也可以吧?
用wait,难免要
wait.regexp(a | b | c | d)
if a then
if b then
if c then
其实,用mush的触发应该可以更方便吧?把if then的内容放到触发内容里,看起来也很清晰啊。然后通过开关触发组来确定当前触发内容。
关键是,用wait.regexp,我觉得相当于把每个”触发“都得用wait.regexp来做,而很多触发是并行的,这样触发就需要n个协程才行,而每个协程之间的协调又很麻烦。
其实zmud里的每个触发,其实也是并行的,相当于多线程,也能实现异步的作用吧?
ltblue
发表于 2015-4-23 17:08:09
1.
if boatcome==0 then
do_dazuo()
end-----等待过程中打坐开始。
在没有叫船的情况下,直接就先打坐?又或者这个协程不会立即执行?
我理解渡江的过程应该是:
先叫船,船没到的前提下,再打坐等。这个逻辑我没看懂
1. 因为do_dazuo()是独立协程,故此执行到这里程序不会停止,会继续向下执行主程序。
2. 至于是先叫船在打坐还是船到了打坐,这里可以用检查状态的模块来完成。我这里的意思是叫船开始的时候就反馈回去信息:可以打坐。
也就是说,这里只是做了一个协程,说可以打坐啦,但是由于目前协程没有yield,所以那个打坐协程根本没有运行,直到duhe协程yield了,打坐协程才会运行。而如果进入船了,那么打坐协程就算运行了也会立刻结束。是这个意思吧?
如果这样的话,说明我对协程的理解还有误区,请问这个问题:
协程是否只能自杀,不能他杀?就是说,只能自己yield,或者代码结束,而不能用其他协程来终止,是吗?
ltblue
发表于 2015-4-23 17:11:43
2.
('^(> |).*渡船\\w*" |^(> |)设定环境变数:no_more = \"notEnter\"')
单引号套双引号的目的是什么呢?没懂两者的关系,如果我写,大概会写成
(”^(> |).*渡船\\w*|^(> |)设定环境变数:no_more = \"notEnter\“)
单引号和双引号是一个意思。为什么用单引号因为环境变量的设定后面有个双引号,为了看清楚故此外边用的是单引号。
这段还是不太懂
你这里面一共是1对单引号,3个双引号,其中两个是\",就是游戏里的双引号,是吧?
红色字体是双引号
('^(> |).*渡船\\w*" |^(> |)设定环境变数:no_more = \"notEnter\"')
因为我很少用游戏里set来做触发,所以不太清楚,呵呵
那这里的第一个双引号,就是\\w*之后的那个,是干什么用的呢?不太懂
ltblue
发表于 2015-4-23 17:18:03
3.
local Here_DOC=和 local outBoat=
这里是改状态吗?如果是改状态,用local的话,这个函数结束了,变量不就释放了吗?不是很懂
这里的变量Here_DOC只是个说明变量,并没有意义。这里的意思是在这个位置写入你需要的代码。替换整个local Here_DOC部分的意思。
另外,local的作用范围是函数体范围,故若local写在哪里,就会作用到哪里。
这部分大概懂了,就是在这里设定变量是吧?
我考虑除了一般的气血内力什么的以外,状态设定4部分
1.当前位置
2.目前的任务阶段(送信?推车?慕容?包括接任务找npc等等,都分别算一个任务。比如推车中,去接任务,包括喝水,取钱,等等)
3.当前状态(主要是目前在做什么,是在渡河?是在跑路?是在打架?是在发呆?等等)
4.发呆状态(即如果正在发呆(比如渡河,比如等杀手),正在干什么(是打坐还是吐纳还是练功还是读书)等等。)之所以把这个独立出来,是因为这个不能移动,只能在原地做,属于没事的时候干的,可以跟其他东西同时运行。
不知道这样的设定可行吗?
ltblue
发表于 2015-4-23 17:20:44
4.
repeat
local l=wait.regexp("到地方了的触发",1)
until l
这里为什么要加repeat和until l呢?直接把1这个参数去掉不行吗?就是直接写
local l=wait.regexp("到地方了的触发")不行吗?
可以,但是若不触发,也就是服务端没有触发语句出现,程序将会停在那里而不执行下去,也就是wait的机制。这里1的意思是1秒后若没有触发文字则这个触发失效,继续执行下面的代码。
repeat ... until 和 while true do ... end都是循环,依据喜好写即可。
这个我觉得倒是懂,不过......
如果
local l=wait.regexp("到地方了的触发",1),如果抓到了变量,那么当然就会继续下去。如果没抓到变量,1秒后仍然程序会再次运行
local l=wait.regexp("到地方了的触发",1),如果一直没抓到,程序会一直在那里重复啊,这根直接local l=wait.regexp("到地方了的触发")没区别吧?
ltblue
发表于 2015-4-23 17:24:31
5.
if l and string.find(l,"notEnter") then
boatcome=0
local Here_DOC=--说明:这里也可以空着。就是确认了没在船上。其实就是状态判定一下。(就是判定自己是不是在船上)判断不在船上,就返回一个状态参数。
return
end
这里是指没进去船,然后等的情况吧?状态修改为什么用local的问题我刚才提了,为什么这里要return呢?没进去船,不是应该等船来吗?return之后整个函数不就结束了吗?
如果加的话,是不是应该写个wait.regexp(“船来了”)比较好呢?
还是不懂
这里的l指触发行,string.find(l,)就是触发行里出现了什么符号的意思。之所以用return的意思是退出循环,返回函数值。就是函数结束了的意思。
你的最后的话我理解为对wait.lua的机制还是没有理解透彻。应仔细阅读wait.lua文件。
注意:wait.regexp这个方法的意思是等待触发行出现,若没有对应触发文字,则程序会暂停在这里。若有失效时间设定,则过了失效时间后执行下面的程序代码。
我觉得,如果船没来,不是应该等待船吗?等船的时候不是应该也在这个函数里吗?如果return了,不是退出这个函数了吗?
换个角度说:如果这里return了,等出现”船来了“的字样的时候,根本没有动作啊,会一直在那里打坐吧?
由于return了,这个while也不会起作用了(因为都退出函数了),所以根本没有把boatcome变成1的机会啊。
没懂这个意思
littleknife
发表于 2015-4-23 18:47:26
本帖最后由 littleknife 于 2015-4-23 06:48 PM 编辑
---------------------------------------------------------------------------------------------------------------------------------------
1.
貌似是把所有任务,每个写成一个函数是吧?
看你DO_JOB这个函数,自身没有make
可是你举例子的渡河和打坐,自身都有wait.make啊
按照你的理念,是应该自身带make的吗?还是不带?
又或者说,渡河和打坐这两个模块特殊,不得不用协程?
wait.make我个人一般是在最早的总配置文件中使用,以后的各种任务由于总配置文件已经设定了wait框架,故此所有代码其实均在wait框架下。
这里DO_JOB的make是指总任务需要框架的意思,当然这里可以先不写。在总的任务文件里调用框架也可。比如:
function Center_Control()---比如这里是个控制中心。在这里加入框架wait.make,同样可以作用于DO_JOB(),也就是DO_JOB不用加wait.make了的意思。
wait.make()
if can_do_job then
DO_JOB()
elseif can_do_job1 then
DO_JOB1()
elseif can_do_job2 then
DO_JOB2()
end
end)
end
至于是不是所有任务都写成一个函数完全凭个人喜好。当然可以把任务分解为几个函数块,在组合在一起,没问题。
另外,do_dazuo之所以有个wait.make的意思就是它是脱离于duhe这个任务,协同渡河程序块运行的,故称为协程(这个是我的理解)。
---------------------------------------------------------------------------------------------------------------------------------------
2.
关于协程,我有几个根本性的问题不懂
(1)
协程是不是只能自己yield,或者整个协程代码结束,才能交出控制权?
如果我某个协程正在运行,但我没yield,我调用了其他协程,是不是得等自己yield了,其他协程才能运行?
(2)
如果3个协程,a、b、c,都处于运行态,目前a正在运行,然后a协程yield了,那么是b协程抢占还是c协程抢占?还是随机?我感觉好像是随机的。
我个人经过测试,协程不影响主程序执行顺序,也就是说主程序发现有协程存在会跳过协程代码而进行之后的代码。故控制权仍然在主程序。
多个协程的判断问题:a、b、c的优先顺序是按代码写入顺序来决定的。谁写在前面谁先执行,但是都执行后各自并不互相影响,这些可以自编代码测试。
当然可以yield,但是用yield要用coroutine这个函数。而由于wait.lua这个协程工具的存在。不必用coroutine也可。也就是当你wait.regexp的时候,就已经执行了coroutine.yield了,按Lua的解释,Lua所支持的协程全称被称作协同式多线程(collaborative multithreading)。Lua为每个coroutine提供一个独立的运行线路。然而和多线程不同的地方就是,coroutine只有在显式调用yield函数后才被挂起,同一时间内只有一个协程正在运行。
下面的代码是我能给你的参考:
function a()
wait.make(function()
print("a is here!!")
wait.time(1)
print("wait 1 s then a is here!!")
end)
end
function b()
wait.make(function()
print("b is here!!")
wait.time(1)
print("wait 1 s then b is here!!")
end)
end
function c()
wait.make(function()
print("c is here!!")
wait.time(1)
print("wait 1 s then c is here!!")
end)
end
function tt()
wait.make(function()
a()
print("tt is here!!")
b()
c()
end)
end
tt()
Result=[[
a is here!!
tt is here!!
b is here!!
c is here!!
wait 1 s then a is here!!
wait 1 s then b is here!!
wait 1 s then c is here!!
]]
---------------------------------------------------------------------------------------------------------------------------------------
3.
对于各种突发情况处理,按照你的思路,是不是应该同时出现多个协程?
比如:我正在等渡船,在打坐,忽然有人pk我,我怎么处理?
我的理解,按照你的思路,大概是有一个专门负责pk的协程,一直处于yield状态,用wait.regexp的临时触发,来等待运行。一旦开始运行,就根据目前的状态变量,暂停其他运行中的协程(但如果协程必须自己yield,就不好办了,这个基础的东西我不确定),运行自身代码。等pk结束,再运行其他协程,yield自己。
是不是这么个意思?
一般我的做法是用永久触发AddTrigger来建立永久触发来激活需要单独处理的特殊事件,比如PK等等。至于任务中的特殊事情当然可以是写任务时建立个协程来做。但是不是出于yield状态,而是出于while true do ... end 循环状态,相当于AddTrigger。但是不是yield,因为yield会暂停程序执行。用wait.lua模块不用yield,用wait.regexp就可以。但是得理解它的正确含义,即“等待触发字符在执行”的意思。
----------------------------------------------------------------------------------------------------
4.
我最不能理解的是,用wait写的目的,比zmud式的触发,强在哪里呢?
论代码的简便性,应该是zmud式的简单。状态变量用触发也可以做,wait.regexp也是用的触发器啊,难道只是为了版本更新?那个用插件也可以吧?
用wait,难免要
wait.regexp(a | b | c | d)
if a then
if b then
if c then
其实,用mush的触发应该可以更方便吧?把if then的内容放到触发内容里,看起来也很清晰啊。然后通过开关触发组来确定当前触发内容。
关键是,用wait.regexp,我觉得相当于把每个”触发“都得用wait.regexp来做,而很多触发是并行的,这样触发就需要n个协程才行,而每个协程之间的协调又很麻烦。
其实zmud里的每个触发,其实也是并行的,相当于多线程,也能实现异步的作用吧?
这个应该说每个人的喜好问题。zmud的触发属于永久式的,不进行触发控制容易出现触发混乱问题,而且zmud机器人对任务流程逻辑的表述个人认为没有mush清楚,故此对维护及更新,以至于全自动执行机器带来很大的困难。zmud的机器思路是在触发下进行书写代码,Mush的思路是依据流程逻辑写机器,也就是代码发出后返回触发信息再进行处理的意思,两种风格而已。
至于是不是需要大量的协程来完成机器也是因人而异,你若喜欢把任务分割,以返回函数的方式书写机器,当然可以多个分块。但是不一定用多个协程。
完全可以配合MUSH自带的触发建立函数AddTrigger(相当于zmud中建立触发)和wait.lua一同写机器人已达到自动执行的目的。
----------------------------------------------------------------------------------------------------
也就是说,这里只是做了一个协程,说可以打坐啦,但是由于目前协程没有yield,所以那个打坐协程根本没有运行,直到duhe协程yield了,打坐协程才会运行。而如果进入船了,那么打坐协程就算运行了也会立刻结束。是这个意思吧?
如果这样的话,说明我对协程的理解还有误区,请问这个问题:
协程是否只能自杀,不能他杀?就是说,只能自己yield,或者代码结束,而不能用其他协程来终止,是吗?
函数是顺序执行的,也就是渡船的第一句就是先判定语句,若成功就会执行do_dazuo(),只不过执行的同时还会向下执行下面的内容罢了,不需要yield。do_dazuo()和duchuan()是同时执行的。
----------------------------------------------------------------------------------------------------
('^(> |).*渡船\\w*" |^(> |)设定环境变数:no_more = \"notEnter\"')
('^(> |).*渡船\\w* |^(> |)设定环境变数:no_more = \"notEnter\"')
渡船的后面的双引号是打错了,呵呵。
最后,个人理解:机器人的制作其实核心思路还是流程设计,也就是第一步干什么接下来干什么。那么进一步解释一下:就是说do_dazuo()平行于主函数duchuan()执行,由主函数返回各种状态来决定do_dazuo()是否停止或继续。
----------------------------------------------------------------------------------------------------
ltblue
发表于 2015-4-27 22:40:22
---------------------------------------------------------------------------------------------------- ...
littleknife 发表于 2015-4-23 06:47 PM http://pkuxkx.com/forum/images/common/back.gif
抱歉,这几天没空,所以没研究
我觉得我基本明白你的意思了
不过即使现在,我仍然觉得,很多情况下lua的处理模式其实相对繁琐,lua的方式是“我发出指令,我确切的知道我可能得到哪些回复,因此我对各种情况都有准备”。但是,mud里有各种各样的突发问题,经常会出现某些情况下“什么都可能触发”的情况,这样就极难处理。wait.regexp只能同时处理一个或几个情况,总不可能所有的东西都放在一个wait.regexp里。而如果要同时有多个wait.regexp,则需要多个协程,这样很影响程序的稳定性吧。
你也说过,对于很多突发事件,是用永久的触发写在mush里的,这就意味着仍然离不开“触发器”这个拐棍,毕竟wait.regexp本质上也是写个临时触发器罢了。而这些“突发事件”的处理,必然也会影响到整个主协程,主模块。
不过,打算用你的这种思路写一下,毕竟对我来说是个很大的挑战吧,呵呵
我考虑了一下,打算大量用以下几种语法来写,你看怎么样?
1.大量使用
while ture do
if 当前状态=渡河 then
l=wait.regexp(“船来了|XXX”,0.5)
if l then
if l=“船来了” then
Send(duhe)
elseif l=“XXX” then
XXX
end
end
else
return
end
end
这样的模块的目的是,当我在等的时候,不是傻等,因为在等待的时候可能有其他方面出现了问题,我必须保证我一直处在等船的状态,而没有“被pk、被捣乱、掉线”等等情况。
这个“状态”变量是独占的,一旦状态有修改,那么其他所有的模块,不管哪个在运行,通通return回到主控制平台。
2.采用独占模块的方式
因为我很可能有多个协程同时调用同一个模块,而我本身不希望一个模块被同时多次调用,有可能某个模块还没结束,又被另外一个协程再次调用,所以我考虑设置一个全局table来记录每个模块的运行状态,大体是
模块开始
if 模块状态==运行 then
return “再来一次”
end
模块状态=运行
XXX
模块结束
举例子:我正在打坐,打坐模块正在运行,状态=打坐。忽然被pk,我让状态=pk,然后调用了pk协程,我用0.3秒的时间杀了敌人(我奇强......),然后pk协程结束,我考虑继续打坐,再次调用了打坐函数,但是原来的打坐函数尚未来得及return,就发现状态又=打坐了,然后就继续打坐,而我不知道这个情况,又效用了一遍打坐,这样两个协程就同时打坐了。
由于wait.regexp后面的时间不宜太短,不然太吃资源,我设置0.5秒我都怕不行,再短就更不行了,所以我怕在0.5秒内,状态变出去又变回来,就惨了。
不过,可以考虑return回主模块后,主协程不结束,而是等待空闲,继续执行,这样估计就能方便了
ltblue
发表于 2015-4-27 22:56:29
3.
将所有任务的每个步骤,作为多个不带协程的函数,然后在主协程中,按照这些任务的重要程度,进行排序,优先执行最要紧的函数。
在每个函数中,只要有wait,就按照第1条的方式写,这样避免傻等。
之所以把任务拆成步骤,是因为可以提高效率,比如,接任务之后,找到杀手之前,顺路的话,可以去吃个饭,取个钱,或者做个quest什么的,并不一定按照顺序的流程走。
在主协程之外,还有一些其他协程,主要有:
1.发呆协程:在原地不动等的情况下,进行的协程,包括打坐、吐纳、练功等。这个协程不影响主状态,而是单独有一个状态来控制。
2.各类状态协程:hp、cha、item、score等状态查询的协程。随时make,用完即关,属于临时协程。
3.战斗协程:在各种战斗中,包括杀挡路、pk、任务、杀捣乱等。主要负责根据状态进行吸气、放pfm等。这个其实可以整合到主协程里,但我玩的mud存在一种情况,就是忽然来杀手的情况,所以必须有一个协程时刻准备着,不然主协程在跑路,杀手来了没人管,就挂掉了(其实是送信任务,理论上应该同时来两个杀手,但有时候只来一个,还有时候先来一个,过很久再来一个,我不可能一直等第二个来,因为可能不会来,这样就得一边走路,一边防杀手,没有协程就容易出问题,所以不如干脆吧战斗独立放一个协程)
其他的各种突发情况,比如被pk什么的,都统一放在触发里,用触发来叫协程来处理,都是一些杂七杂八的东西了。
不知道这套思路是不是可行,请教一下
辛苦了,呵呵
littleknife
发表于 2015-4-28 19:15:46
本帖最后由 littleknife 于 2015-4-28 07:18 PM 编辑
lua的方式是“我发出指令,我确切的知道我可能得到哪些回复
这在MUSH里是完全可以做到的。因为可以设定环境变量的缘故,形如:set xxx okey 。
wait.regexp只能同时处理一个或几个情况,总不可能所有的东西都放在一个wait.regexp里。
1)wait.regexp这个方法经过对wait.lua的升级修正。论坛里有升级后的wait.lua。它的参数是可以带表格的,
2)wait.regexp方法是可以返回参数的,前三个参数是:触发行,触发变量,触发形式(即:line,wildcards,styles)
其用法可以参考下面的格式:
function fight_check()
local triggerlist={
"^[> |]*你想收谁作弟子\\w*",
"^[> |]*你想[要]*收(.+)为弟子\\w*",
}
------------------------------
local l,w,s
for i=1,4 do
repeat
run("shou "..playerid.."'s robber "..i)
l,w,s=wait.regexp({
"^[> |]*你想收谁作弟子\\w*","^[> |]*你想[要]*收(.+)为弟子\\w*",
})
until l
if string.find(l,"为弟子") then
fight_NPC()
end
end--for
------------------------------
end
至于机器怎么写,完全凭个人的喜好。个人认为其核心的问题还是流程设计,只要能细化到一定程度的流程,代码反而不是很困难。
我写机器的思路,一般还是参考jarlyyn的思路:也就是考虑做些状态判定的函数,最后用状态返回值来判定执行什么动作。当然,我一般是用wait.lua模块来这么做而已。
页:
1
2
3
4
5
6
7
8
9
10
[11]
12