北大侠客行MUD论坛

 找回密码
 注册
搜索
热搜: 新手 wiki 升级
楼主: ltblue

我好像误入歧途了

[复制链接]
 楼主| 发表于 2015-4-23 17:02:11 | 显示全部楼层
回复  cmud


    个人认为最理想的机器,并不是协程的随意引用,因为那会造成大量的回调函数的使用,并 ...
littleknife 发表于 2015-4-22 06:02 PM



以上是代码上我不懂的地方

理念上,还有点糊涂

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里的每个触发,其实也是并行的,相当于多线程,也能实现异步的作用吧?
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2015-4-23 17:08:09 | 显示全部楼层
1.
if boatcome==0 then
        do_dazuo()
end-----等待过程中打坐开始。
在没有叫船的情况下,直接就先打坐?又或者这个协程不会立即执行?
我理解渡江的过程应该是:
先叫船,船没到的前提下,再打坐等。这个逻辑我没看懂




1. 因为do_dazuo()是独立协程,故此执行到这里程序不会停止,会继续向下执行主程序。
2. 至于是先叫船在打坐还是船到了打坐,这里可以用检查状态的模块来完成。我这里的意思是叫船开始的时候就反馈回去信息:可以打坐。


也就是说,这里只是做了一个协程,说可以打坐啦,但是由于目前协程没有yield,所以那个打坐协程根本没有运行,直到duhe协程yield了,打坐协程才会运行。而如果进入船了,那么打坐协程就算运行了也会立刻结束。是这个意思吧?
如果这样的话,说明我对协程的理解还有误区,请问这个问题:
协程是否只能自杀,不能他杀?就是说,只能自己yield,或者代码结束,而不能用其他协程来终止,是吗?
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2015-4-23 17:11:43 | 显示全部楼层
2.
('^(> |).*渡船\\w*" |^(> |)设定环境变数:no_more = \"notEnter\"')
单引号套双引号的目的是什么呢?没懂两者的关系,如果我写,大概会写成
(”^(> |).*渡船\\w*|^(> |)设定环境变数:no_more = \"notEnter\“)


单引号和双引号是一个意思。为什么用单引号因为环境变量的设定后面有个双引号,为了看清楚故此外边用的是单引号。



这段还是不太懂
你这里面一共是1对单引号,3个双引号,其中两个是\",就是游戏里的双引号,是吧?
红色字体是双引号
('^(> |).*渡船\\w*" |^(> |)设定环境变数:no_more = \"notEnter\"')
因为我很少用游戏里set来做触发,所以不太清楚,呵呵
那这里的第一个双引号,就是\\w*之后的那个,是干什么用的呢?不太懂
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 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.发呆状态(即如果正在发呆(比如渡河,比如等杀手),正在干什么(是打坐还是吐纳还是练功还是读书)等等。)之所以把这个独立出来,是因为这个不能移动,只能在原地做,属于没事的时候干的,可以跟其他东西同时运行。
不知道这样的设定可行吗?
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 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("到地方了的触发")没区别吧?
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 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的机会啊。
没懂这个意思
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
发表于 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是指总任务需要框架的意思,当然这里可以先不写。在总的任务文件里调用框架也可。比如:

  1. function Center_Control()---比如这里是个控制中心。在这里加入框架wait.make,同样可以作用于DO_JOB(),也就是DO_JOB不用加wait.make了的意思。
  2.         wait.make()
  3.         if can_do_job then
  4.                         DO_JOB()
  5.         elseif can_do_job1 then
  6.                         DO_JOB1()
  7.         elseif can_do_job2 then
  8.                         DO_JOB2()
  9.         end
  10.         end)
  11. 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函数后才被挂起,同一时间内只有一个协程正在运行。
下面的代码是我能给你的参考:

  1. function a()
  2.         wait.make(function()        
  3.         print("a is here!!")
  4.         wait.time(1)
  5.         print("wait 1 s then a is here!!")
  6.         end)
  7. end
  8. function b()
  9.         wait.make(function()        
  10.         print("b is here!!")
  11.         wait.time(1)
  12.         print("wait 1 s then b is here!!")
  13.         end)
  14. end
  15. function c()
  16.         wait.make(function()        
  17.                 print("c is here!!")
  18.                 wait.time(1)
  19.                 print("wait 1 s then c is here!!")
  20.         end)
  21. end
  22. function tt()
  23.                         wait.make(function()
  24.                         a()
  25.                         print("tt is here!!")
  26.                         b()
  27.                         c()
  28.         
  29.         end)
  30. end
  31. tt()
  32. Result=[[

  33. a is here!!
  34. tt is here!!
  35. b is here!!
  36. c is here!!
  37. wait 1 s then a is here!!
  38. wait 1 s then b is here!!
  39. wait 1 s then c is here!!

  40. ]]
复制代码
---------------------------------------------------------------------------------------------------------------------------------------
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()是否停止或继续。

----------------------------------------------------------------------------------------------------
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2015-4-27 22:40:22 | 显示全部楼层
---------------------------------------------------------------------------------------------------- ...
littleknife 发表于 2015-4-23 06:47 PM



抱歉,这几天没空,所以没研究
我觉得我基本明白你的意思了

不过即使现在,我仍然觉得,很多情况下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回主模块后,主协程不结束,而是等待空闲,继续执行,这样估计就能方便了
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2015-4-27 22:56:29 | 显示全部楼层
3.
将所有任务的每个步骤,作为多个不带协程的函数,然后在主协程中,按照这些任务的重要程度,进行排序,优先执行最要紧的函数。
在每个函数中,只要有wait,就按照第1条的方式写,这样避免傻等。
之所以把任务拆成步骤,是因为可以提高效率,比如,接任务之后,找到杀手之前,顺路的话,可以去吃个饭,取个钱,或者做个quest什么的,并不一定按照顺序的流程走。
在主协程之外,还有一些其他协程,主要有:
1.发呆协程:在原地不动等的情况下,进行的协程,包括打坐、吐纳、练功等。这个协程不影响主状态,而是单独有一个状态来控制。
2.各类状态协程:hp、cha、item、score等状态查询的协程。随时make,用完即关,属于临时协程。
3.战斗协程:在各种战斗中,包括杀挡路、pk、任务、杀捣乱等。主要负责根据状态进行吸气、放pfm等。这个其实可以整合到主协程里,但我玩的mud存在一种情况,就是忽然来杀手的情况,所以必须有一个协程时刻准备着,不然主协程在跑路,杀手来了没人管,就挂掉了(其实是送信任务,理论上应该同时来两个杀手,但有时候只来一个,还有时候先来一个,过很久再来一个,我不可能一直等第二个来,因为可能不会来,这样就得一边走路,一边防杀手,没有协程就容易出问题,所以不如干脆吧战斗独立放一个协程)

其他的各种突发情况,比如被pk什么的,都统一放在触发里,用触发来叫协程来处理,都是一些杂七杂八的东西了。

不知道这套思路是不是可行,请教一下
辛苦了,呵呵
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
发表于 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)

其用法可以参考下面的格式:

  1. function fight_check()

  2.         local triggerlist={
  3.         "^[> |]*你想收谁作弟子\\w*",
  4.         "^[> |]*你想[要]*收(.+)为弟子\\w*",        
  5.         }
  6.                 ------------------------------
  7.                 local l,w,s
  8.                
  9.                  for i=1,4 do
  10.                         repeat
  11.                             run("shou "..playerid.."'s robber "..i)
  12.                             l,w,s=wait.regexp({
  13.                                                 "^[> |]*你想收谁作弟子\\w*","^[> |]*你想[要]*收(.+)为弟子\\w*",        
  14.                                                 })
  15.                         until l
  16.                                 
  17.                         if string.find(l,"为弟子") then
  18.                                                 fight_NPC()                                                
  19.                        end
  20.                end--for               
  21.                 ------------------------------
  22. end
复制代码
至于机器怎么写,完全凭个人的喜好。个人认为其核心的问题还是流程设计,只要能细化到一定程度的流程,代码反而不是很困难。

我写机器的思路,一般还是参考jarlyyn的思路:也就是考虑做些状态判定的函数,最后用状态返回值来判定执行什么动作。当然,我一般是用wait.lua模块来这么做而已。
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|北大侠客行MUD ( 京ICP备16065414号-1 )

GMT+8, 2024-11-24 11:28 AM , Processed in 0.010063 second(s), 12 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表