返回列表 发帖

[Mudlet] 开发点滴第(一)期

本帖最后由 ecloud 于 2020-4-10 03:47 PM 编辑

每期5楼,请勿插队

一、关于等待
Mudlet跟其他客户端最大的不同是没有sleep/wait这样的阻塞式等待,Mudlet的核心逻辑是非阻塞式多线程的
想达到sleep的效果,只能使用tempTimer,所以使用起来会显得很怪异
Mudlet提供一个基于线程暂停的Coroutines,但是这个东西仍然不是sleep/wait,所以并不能原样复制别的客户端的编程风格
我推荐使用基于反射的编程风格,而不是大量滥用tempTimer。也就是说更多的依靠触发器实现 消息->反射 机制,而非傻傻的读秒
注意tempTimer是系统级调用,局部和临时变量是带不进去的,要写成类似这样
  1. local name = matches[2]
  2. tempTimer(2.4, function() echo("hello, "..name.."!\n") end)
复制代码
北大侠客行MUD,中国最好的MUD

本帖最后由 ecloud 于 2020-4-10 03:47 PM 编辑

二、speedwalk
这个函数藏在map大类里面,最开始我都给忽略了
后来试了一下,发现了一些问题,最大的问题是,su,eu,ed 这样的方向是不被它识别的,它会把 su 当作一个s和一个u来看待
因此,speedwalk函数在pkuxkx中几乎没有什么用武之地,只有像长安、北京这样非常平坦的城市可以使用
speedwalk的一个好处是,如果你要走来回,只需要定义一个路径,回来的时候直接使用 speedwalk("xxx", true) 就可以返回了,少了一遍反向整理路径的麻烦
不过这个函数的功能启发了我,我会考虑自己实现一个支持各种方向的替代物,包括返回哟

TOP

本帖最后由 ecloud 于 2020-4-10 04:53 PM 编辑

三、Event

Event是Mudlet的精髓,建议大家用好它
Mudlet自带的很多系统Event非常实用,比如下载完成的Event,就可用于显示fullme图片,这样比傻傻的读秒要好
一定要注意的是,定义的handle必须要杀掉
  1. function antirobot.pic()
  2.   killAnonymousEventHandler(web_handler)
  3.   local f = io.open(robot_path, "r")
  4.   local content = ""
  5.   if f then content = f:read("*a"); io.close(f) end
  6.   if content ~= "" then
  7.     local url_start = content:find("src=") + 6
  8.     local url_end = content:find(".jpg") + 3
  9.     local pic_url = base_url .. content:sub(url_start, url_end)
  10.     downloadFile(pic_path, pic_url)
  11.     pic_handler = registerAnonymousEventHandler("sysDownloadDone", "fullme.show")
  12.     tempTimer(antirobot.ttl, [[killAnonymousEventHandler(]] ..pic_handler .. [[)]])
  13.   end
  14. end

  15. function fullme.pic()
  16.   killAnonymousEventHandler(web_handler)
  17.   local f = io.open(robot_path, "r")
  18.   local content = ""
  19.   if f then content = f:read("*a"); io.close(f) end
  20.   if content ~= "" then
  21.     local url_start = content:find("src=") + 6
  22.     local url_end = content:find(".jpg") + 3
  23.     local pic_url = base_url .. content:sub(url_start, url_end)
  24.     disableTimer("一分钟系统定时器")
  25.     downloadFile(pic_path, pic_url)
  26.     pic_handler = registerAnonymousEventHandler("sysDownloadDone", "fullme.show")
  27.     tempTimer(fullme.ttl+1, [[enableTimer("一分钟系统定时器")]])
  28.     tempTimer(fullme.ttl, [[killAnonymousEventHandler(pic_handler)]])
  29.   end

  30. end

  31. function antirobot.reload()
  32.   if antirobot.type == 1 then
  33.     fullme.timer = 0
  34.     downloadFile(robot_path, fullme.url)
  35.     web_handler = registerAnonymousEventHandler("sysDownloadDone", "fullme.pic")  
  36.   end
  37.   downloadFile(robot_path, antirobot.url)
  38.   web_handler = registerAnonymousEventHandler("sysDownloadDone", "antirobot.pic")  
  39. end
复制代码

TOP

本帖最后由 ecloud 于 2020-4-11 04:24 PM 编辑

四、注意变量的作用域

在Mudlet中开发程序,整个程序被分割成脚本、别名、触发器等部分,分散在系统中
如果你还继续传统的整体化编程思维,就会混乱,尤其是变量的作用域
举例如下:这个例子的意思是,我设置了pfmA,B,C等几个全局对象,同时它们又可以存在于pfmCDGroup这个集合里。那么似乎看上去我穷举出A,B,C跟我遍历pfmCDGroup是一样的结果?
  1. --创建发射和命中触发器
  2. local function createtr()
  3.   pfmA.firetr = tempTrigger(pfmA.firemsg, [[pfmA:fired()]], 1)
  4.   pfmA.misstr = tempTrigger(pfmA.missmsg, [[pfmA:missed()]], 1)
  5.   pfmB.firetr = tempTrigger(pfmB.firemsg, [[pfmB:fired()]], 1)
  6.   pfmB.misstr = tempTrigger(pfmB.missmsg, [[pfmB:missed()]], 1)
  7.   pfmC.firetr = tempTrigger(pfmC.firemsg, [[pfmC:fired()]], 1)
  8.   pfmC.misstr = tempTrigger(pfmC.missmsg, [[pfmC:missed()]], 1)
  9. end

  10. --给共同CD的技能创建触发器
  11. local function createCDtr()
  12.   for k, p in ipairs(pfmCDGroup) do
  13.     battle.shareCDtr[k] = tempTrigger(p.firemsg, [[battle.groupCD()]])
  14.   end
  15. end

  16. function battle.autopfm()
  17.   createtr()
  18.   if #pfmCDGroup > 1 then
  19.     createCDtr()
  20.   end
  21.   if battle.mode == "AAA" then AAA()
  22.   elseif battle.mode == "Loop" then Loop()
  23.   elseif battle.mode == "Bubble" then Bubble()
  24.   elseif battle.mode == "NA" then return
  25.   else
  26.     display("Battle模式设置错误!")
  27.   end
  28. end

  29. function battle.groupCD()
  30.   for k, p in ipairs(pfmCDGroup) do
  31.     p:CD()
  32.   end
  33. end
复制代码
在这段代码中createtr()这个函数写的不够优雅,似乎看上去应该写成createCDtr()里面的for循环模式
那么我为什么使用看上去愚蠢的挨个穷举的方式呢?
因为这里面涉及到了tempTrigger()这个系统级函数,该函数是被Mudlet引擎执行的,因此它所能够接受的必须是全局变量,比如pfmA, B ,C 这些都是全局变量,名称固定。
而for循环里的p是个局部变量。在我这个例子里,p.firemsg 由于直接得到一个静态字符串,是可以作为参数传进去的,而 p:CD() 这种对象方法就无法成为tempTrigger()的参数,因为系统层面不知道p这个对象的存在。你可以在一个循环里直接执行 p:CD() , 就像我后面那个函数里所使用的那样。但是你不能把它作为一个参数传给tempTrigger()

这一点是Mudlet编程非常需要注意的地方。所有以“系统注册”形式存在的函数,比如tempTrigger, tempTimer等,都只能接受静态的全局变量,不能传句柄和对象(除非你深拷贝)
因此,不要介怀使用全局变量,此“全局”非彼“全局”,这里的全局就是给这些系统级函数用的,不要在乎什么优雅不优雅,Mudlet编程需要转换思维

TOP

本帖最后由 ecloud 于 2020-4-11 04:38 PM 编辑

五、display和echo

这俩其实差的挺多。
display可以显示任何类型的变量,包括table和nil,因此更适合用来排错。display一个字符串会带双引号,我目前还没搞明白怎么去掉,这个很讨厌。
echo则是让输出看上去就跟游戏里面的文字融为一体,字形字号都一样。但是echo只能处理字符串。cecho、decho和hecho都是echo的马甲,区别在于显示彩色的表达形式。cecho有个色卡,在这里:https://wiki.mudlet.org/w/File:ShowColors.png
注意这个色卡里面的有些名称在css里不好用

TOP

admire!~~
看一楼内容感觉回到了多线程编程的课堂
大佬何时出一篇攻略,叫“走近Linux内核”,俺好好学习一下子o(* ̄︶ ̄*)o
犹如华山夹着细雪的微风

TOP

求大佬讲讲AI 识别

TOP

好久没更新了,加油,我想试试linux无桌面的环境,不知道支持得怎么样,有用过的吗?

TOP

返回列表