ecloud 发表于 2020-4-10 23:35:54

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

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

每期5楼,请勿插队

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

ecloud 发表于 2020-4-10 23:36:11

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

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

ecloud 发表于 2020-4-10 23:36:17

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

三、Event

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

function fullme.pic()
killAnonymousEventHandler(web_handler)
local f = io.open(robot_path, "r")
local content = ""
if f then content = f:read("*a"); io.close(f) end
if content ~= "" then
    local url_start = content:find("src=") + 6
    local url_end = content:find(".jpg") + 3
    local pic_url = base_url .. content:sub(url_start, url_end)
    disableTimer("一分钟系统定时器")
    downloadFile(pic_path, pic_url)
    pic_handler = registerAnonymousEventHandler("sysDownloadDone", "fullme.show")
    tempTimer(fullme.ttl+1, [])
    tempTimer(fullme.ttl, [])
end

end

function antirobot.reload()
if antirobot.type == 1 then
    fullme.timer = 0
    downloadFile(robot_path, fullme.url)
    web_handler = registerAnonymousEventHandler("sysDownloadDone", "fullme.pic")
end
downloadFile(robot_path, antirobot.url)
web_handler = registerAnonymousEventHandler("sysDownloadDone", "antirobot.pic")
end

ecloud 发表于 2020-4-10 23:36:29

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

四、注意变量的作用域

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

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

function battle.autopfm()
createtr()
if #pfmCDGroup > 1 then
    createCDtr()
end
if battle.mode == "AAA" then AAA()
elseif battle.mode == "Loop" then Loop()
elseif battle.mode == "Bubble" then Bubble()
elseif battle.mode == "NA" then return
else
    display("Battle模式设置错误!")
end
end

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

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

ecloud 发表于 2020-4-10 23:36:37

本帖最后由 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里不好用

shgy 发表于 2020-4-11 02:21:56

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

man 发表于 2020-4-12 21:11:44

求大佬讲讲AI 识别{:7_276:}

sansouci 发表于 2020-5-18 06:52:40

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

championa 发表于 2020-9-13 21:34:58

看到你的帖子,我也想用用mudlet了,不过对编程是只知皮毛,得结合你的慢慢学习了。感谢大佬分享,有空得多开开课哈。

vadi 发表于 2020-11-4 16:23:56

This might be useful for waiting in a more 'synchronous' way: https://forums.mudlet.org/viewtopic.php?f=6&t=22875&p=45701
页: [1] 2
查看完整版本: [Mudlet] 开发点滴第(一)期