清华西游记吧 关注:139贴子:1,462
  • 13回复贴,共1

mushclient 学习贴

取消只看楼主收藏回复

mushclient如此高大上,但是由于门槛较高,很多人都是望而却步,包括我自己也是。
早在2012年之前,wena就游说过我很多次体验一下mushclient
每次我都哼哼哈哈的答应的很好听,
完了继续优化改善我那一塌糊涂无可救药的zmud721
至到wena当wiz,升做admin,到现在消无声息,好几年时间一晃就过去了
至到2015年我实在是忍受不了zmud462无故弹窗报错,又不告知是什么地方有错
再加上确实是无聊,嗯,一定是太无聊了,
才下定决心啃啃mush这块硬骨头
下面记录一下我学习mushclient的体会
本人基础很差,不过好在脸皮厚,也不怕行家笑话
希望我写出来的一些粗浅经验,可以帮助其他也想学习mushclient的小伙伴


1楼2015-01-19 21:48回复
    安装mushclient跳过不提,新出的mushclient是绿色版,不需要安装,解压缩就能运行,免费软件,也没有注册或者破解的麻烦,老毛子真的是可以。
    运行mush之后的第一件事情就是新建游戏,如下图:

    World:这里我填的是大米的id,不同的大米填不同的id,这个字符串是可以在脚本里面用WorldName()获取的,可以用于做来多id公用机器的判断入口。
    这个字符串会显示在mush的界面上,如下图:

    mud 服务器的地址和ip就不必说了

    另外一个需要设置的地方就是Connecting
    Name:可填可不填,填在这里的字符串可以在脚本里面用函数GetOption获取到,我前面用了WorldName()获得id,这里也就没用到了。
    Password:和上面的Name一样,可以脚本里面获取到。在登录id的时候,可以从这里获取密码,而不需要把密码写在触发里面,对多id公用的机器还是很有用的。
    Connect:这里可以设置连接后自动登录的语句,不过功能很简单,我没有使用。
    好了,填完上面的几个参数,就能连接上thu xyj了。
    这一部分没什么难度,看看就会


    2楼2015-01-19 22:01
    回复
      登录到游戏画面,第一个要做的事情,肯定是做自动登录,
      不然每次都输入gb,n,id,passwd,多麻烦啊,是吧
      点菜单“Game”->"Configure"->"Triggers",活着直接键盘输入ctrl+shift+8
      mush支持很多键盘快捷方式,开始用总记不住,用多了慢慢就记得一些了。
      现在Triggers这一栏还是空白的,点下面的Add添加一个触发

      Enable,勾上就启用这一句触发,不勾上就不启用,好理解
      Regular expression :启用正则表达式。启用吧,用mushclient必须得会点正则表达式
      Send:内容和目标。我选择的是send to Script,内容是一个函数CommonLib.Loginset()
      意思是用语法函数来执行命令
      Group:和zmud的class类似,可以用EnableGroup()这个函数来启用和不启用某一个group的东西


      3楼2015-01-19 22:12
      回复
        开始举点实例说明mushclient的方便之处
        在zmud上面,检查身上的物品,筐里面的物品,篮子里面的物品,是一个很烦人的事情
        我在721下写了很多关于检查物品的语句,实在是非常繁琐


        一大堆%ismember,#if 时间长了,自己都不记得以前写的是些啥
        在mushclient上发现实现这个功能是如此轻松:
        mush支持多行匹配,能一次搞定。如下:
        你身上带着下列这些东西(负重 11%):
        一百一十五棵人参(Ren shen)
        十六棵灵芝(Ling zhi)
        十粒金丹(Jin dan)
        三十一两银子(Silver)
        □樟木盾(Zhangmu dun)
        □黄金战甲(Huangjin zhanjia)
        □死神的魔爪(Deathtouch)
        一百一十九两黄金(Gold)
        八十五文钱(Coin)
        □宣花斧(Xuanhua fu)
        宣花斧(Xuanhua fu)
        二架时空穿梭器(Chuansuo qi)
        三尖两刃枪(Sanliang spear)
        三尖两刃枪(Sanliang spear)
        水晶腰牌(Yao pai)
        竹筐(Kuang)
        传送卷轴(Teleport scroll)
        □兽皮披风(Shoupi pifeng)
        □五彩天衣(Tian yi)
        □细编翠竹花篮(Hua lan)
        □佛宝舍利子(Tooth)
        钢枪(Oracle)
        □钢护腕(Icestorm)

        勾选上REgular Expression ,然后勾上Multi-line
        你身上带着下列这些东西\(负重\s*(\d+)%\):\n(?:^.{2}(.*)\((.*)\)\n)*\z
        翻译一下:\n是换行 \s 是空格,^是匹配一行的开始,$匹配行末 *表示0个或者多个
        你身上带着下列这些东西(负重 11%): 这一行用下面的匹配
        你身上带着下列这些东西\(负重\s*(\d+)%\):\n
        ----用(\d+)来获取到11,\n换一行
        一百一十五棵人参(Ren shen)
        □黄金战甲(Huangjin zhanjia)
        (?:^.{2}(.*)\((.*)\)\n)*\z
        ----^匹配一行开始 .{2}匹配2个空格或者□ (.*)获取第一个数据\(和\)匹配'('和')',第一个(.*)获取括号内的数据,\n换行。整句前面加上(?: 后面加上)*,说明匹配0个或者多个这样的数据 \z我不能确切的知道是什么意思,mushclient的帮助上这么用,我也跟着用。^_*
        这样%1获得了11,%2获得了"一百一十五棵人参",%3获得了"Ren shen"
        后面持续的行,%2继续获得"黄金战甲",%2继续获得Huangjin zhanjia
        一直到出现不匹配的行,也就是"i"命令结束
        数据全部获取到,脚本相对就容易了,
        CommonLib.GetItemNum(AllItemInfo.InBody,"%2",string.lower("%3"))
        大概的功能是将获取到的物品id和数量整理一下,放到
        最终将结果存在AllItemInfo.InBody这个table里面:AllItemInfo.InBody这个table里面,如下:
        huangjin zhanjia = 1
        hua lan = 1
        silver = 31
        kuang = 1
        shoupi pifeng = 1
        gold = 119
        deathtouch = 1
        chuansuo qi = 2
        coin = 85
        oracle = 1
        zhangmu dun = 1
        icestorm = 1
        yao pai = 1
        tian yi = 1
        jin dan = 1
        teleport scroll = 1
        ren shen = 115
        ling zhi = 16
        tooth = 1
        sanliang spear = 2
        xuanhua fu = 2
        要想知道自己身上有多少人参,只需要用AllItemInfo.InBody["ren shen"]就行了
        btw:AllItemInfo.InBody 和AllItemInfo["InBody"]是一个意思
        获取筐或者花篮里面的物品,只用改一句触发就ok了
        这是一个编制精细的竹筐,用来装一些杂物。\n里面有:\n(?:\s{6}(.*)\((.*)\)\n)*\z
        这是一只精美的套层绢底雕花缎边细编翠竹花篮。\n类型.*\n里面有:\n(?:\s{6}(.*)\((.*)\)\n)*\z


        5楼2015-01-19 22:51
        回复(2)

          看了上面的正则表达式配多行触发,要想获得这个3 3 2是不是很轻松了呢?
          不过2个星期前,我刚开始用mush的时候还不会多行触发,用的是非常笨拙的方式

          触发只有一句,重点在脚本上:
          function GambleInfo.GambleJudgeNum(judgenum) 摘抄如下:
          if judgenum==1 then string_judge="根据耳力判断骰子是:"
          elseif judgenum==2 then string_judge="根据眼力判断骰子是:"
          elseif judgenum==3 then string_judge="又补道:现在开盘。"
          end
          for i=GetLinesInBufferCount(),GetLinesInBufferCount()-500,-1 do
          if GetLineInfo(i,1)==string_judge then
          GameListenNum=i
          end
          if GameListenNum > 0 then break end
          end
          上面的脚本for i=GetLinesInBufferCount(),GetLinesInBufferCount()-500,-1 do
          意思是从服务器发过来的数据开始朝前查最多500行,
          if GetLineInfo(i,1)==string_judge then
          GameListenNum=i
          end
          如果发现哪一行和需要匹配的字符串相同,记录行号。然后分析紧跟着的后面的几行的数据
          匹配的字符串对应听的骰子,看的骰子和庄家报的盘
          local NumberTable={"   "," ● ","●  ","  ●","● ●"}
          每行的骰子有5个不同的数据,对应0,1,1,1,2
          下面计算第一个骰子是多少
          GameNum1=0
          for j=1,3 do
          local str2=string.sub(GetLineInfo(GameListenNum+1+j,1),3,8)
          for i=1,#NumberTable do
          if str2==NumberTable[i] then
          GameNumber=i
          break
          end
          end
          GameNum1=GameNum1+GambleInfo.GambleStringToNum(GameNumber)
          end
          最开始写的脚本了,大概是从每行计算一个数出来,三个数相加
          比如三点就是1+1+1
          二点就是0+2+0


          6楼2015-01-19 23:14
          回复
            继续实例:
            在thu挂机,无人值守的情况下,id总是会碰到不可预料的问题导致正常运行的任务流程中断,比如碰到拦路怪,网络卡顿导致命令过多,走路到一半断线重连等等。所以要想无人值守,最必要的一个工作就是能让发呆的id回到能飞的地方,重新开始找点事情做。有钱的主,比如当wiz以前的cksh,尝试飞一下,飞不动就用穿梭器搞定,简单粗暴,有钱就是如此任性;大部分人都做不到如此任性,普通青年都是是带个pillow,不能飞就cast chuqiao,到地府去睡觉,从红楼梦出来;我在721下实现的更加复杂,所以问题也更多。比如:chuqiao之前得判断是否还有法力吧?没法力了是不是得mingsi一下?万一是在安全期发呆,不允许冥思,还得走开一步?等等等等,是如此的烦人。我最讨厌看到的就是挂机几个月不管,偶尔去瞄一眼,发现有个id在安全期发呆,内力法力食物饮水精神气血全部为0,身上啥都没有,还不知道是哪里出的问题。虽然最近1,2年没有碰到这种情况了,但是碰到也很头疼。
            闲话少说,mush得益于强大的lua语法,所有不能飞的地方,都能直接走出来。对,就是走出来,就这么简单。
            hao的展室 -
            老玩家的展室。
            这里唯一的出口是 out。
            天上人间 元昊的蜡像(Hao npc)
            id发呆之后,先确定自己所在的位置:

            这个工作不难,就是把房间名称存在CommonVar.RoomName
            剩下的就是读数据,走出来


            函数fWalkToCross(),从table读取到tWalkToCross["hao的展厅"] = "out;e;#7 d",然后用Execute执行,就走到了十字街头,要走到麻将房、赌博房或者货堆发呆,也就容易了。
            所有需要走出来的地方,只需要完善table就行了。


            8楼2015-01-20 20:51
            回复
              调试了一个周末,基本上可以在mush上半手动半自动的跑一下李靖任务,目前还只是slowwalk。
              主要的问题在于把zmud上的遍历地图移植到mush上面来,像changan,chechi这种比较单纯的地图已经能顺利的跑完。但是有特殊地点的比如龙宫,雪山,长安西,还得慢慢调试
              搭了一个门派的框架,准备适应各门派和id的扩展
              faction = {
              jjf = {
              Spells = "baguazhou",
              Force="lengquan-force",
              weapon1="sanliang spear",
              weapon2="xuanhua fu",
              weapon3="xuanhua fu",
              LearnSkill={"force","parry","dodge","lengquan-force","spells","axe","unarmed"},
              LianSkill={"bawang-qiang","yanxing-steps","sanban-axe","changquan"},
              EnableSkill = "enable axe sanban-axe;enable spear bawang-qiang;enable parry bawang-qiang;enable dodge yanxing-steps"
              },
              psd = {},
              moon = {},
              }
              function faction.jjf.mieyaopfm(str)
              local ename = str
              if (not task.buffer.lianhua ) and (idclass.spells=="buddhism") then
              Send("cast lianhua");
              end
              Execute("unwield all");
              if (task.PfmTimes or 0 ) < 5 then
              Execute("enable axe sanban-axe;enable parry sanban-axe;wield xuanhua fu;wielddagger")
              Send("perform sanban on "..ename)
              else
              if idclass.oracle then Send("wield oracle") end
              Execute("wield sanliang spear;enable parry bawang-qiang;wielddagger")
              Send("perform bawangshanggong on "..ename)
              end
              if idclass.spells == "buddhism" then
              task.buffer.lianhua = false
              Send("buff")
              end
              Send("wear all")
              end


              14楼2015-01-25 22:30
              回复
                在mush上挂了一天,晚上回来发现在舍利塔里面发呆
                于是增加了舍利塔的随机走路,也不知道有用没有。
                得等到下次进舍利塔的时候观察观察
                walkout = {
                }
                function walkout:SheLiTaOut()
                setdog(200)
                EnableGroup("walkout",true)
                self.DirList = nil
                Execute("unset brief_all;unset brief")
                Send("look")
                Send("follow check walkout script NextStep")
                end
                --[[ 碗子山 -
                ]]
                function walkout:GetRoomName(str)
                self.room = str
                end
                function walkout:NextStep()
                local out
                if self.room == "囚洞" and not self.DirList then
                out = "zuan"
                else
                out = math.random(1,#self.DirList)
                print(out)
                out = self.DirList[out]
                print(out)
                for k,v in pairs(self.DirList) do
                print(self.room,v)
                if self.room == "舍利塔" and v == "westdown" then
                out = "wd"
                break
                end
                end
                end
                print("out is "..out)
                if out == "wd" then
                Send(out)
                EnableGroup("walkout",false)
                Execute("timeout")
                else
                Send(out)
                DoAfter(0.1,"follow check walkout script NextStep")
                end
                end
                --[[
                这里明显的出口是 southeast、eastdown 和 southdown。
                ^\s+这里.*的出(.*)。$
                ]]
                function walkout:SetExit(str)
                local str = string.gsub(str,"口是 ","");
                str = string.gsub(str,"路","");
                str = string.gsub(str," 和 ","|");
                str = string.gsub(str,"、","|");
                self.DirList = split(str,"|");
                end


                15楼2015-01-26 20:55
                回复
                  分享下editplus的正则表达式便利性
                  ["0000"] = 0,
                  ["0001"] = 1,
                  ["0010"] = 2,
                  ["0011"] = 3,
                  ["0100"] = 4,
                  ["0101"] = 5,
                  ["0110"] = 6,
                  ["0111"] = 7,
                  ["1000"] = 8,
                  ["1001"] = 9,
                  ["1010"] = a,
                  ["1011"] = b,
                  ["1100"] = c,
                  ["1101"] = d,
                  ["1110"] = e,
                  ["1111"] = f,
                  想把后面的0,1,2,3,4,5...分别加上“”,挨个手写会很烦
                  在editplus上ctrl+h,叫出替换选项

                  Find what 用= (.),匹配,并获取一个单字符,Replace用 = "\1", \1表示获取的第一个数据。如果有多个匹配和用\2 \3 \4等来表示。类似于mush和zmud的%1 %2 %3 %4
                  鼠标点两下,就变成下面这样了
                  ["0000"] = "0",
                  ["0001"] = "1",
                  ["0010"] = "2",
                  ["0011"] = "3",
                  ["0100"] = "4",
                  ["0101"] = "5",
                  ["0110"] = "6",
                  ["0111"] = "7",
                  ["1000"] = "8",
                  ["1001"] = "9",
                  ["1010"] = "a",
                  ["1011"] = "b",
                  ["1100"] = "c",
                  ["1101"] = "d",
                  ["1110"] = "e",
                  ["1111"] = "f",
                  当然,用于更多数据输入的时候会更体现其方便性
                  每天写一点,搞得像记流水账了,也确实没啥好写的,mush机器写到现在,基本上都是工作量了。现在机器应该能全自动做李靖任务,灭妖间隙去打坐冥思,自动学习技能写了一半还没有开始调试。剩下守城、开封任务和如来任务,都只是工作量和调试的问题。
                  这两天学习了一下mush的自带函数GetLineInfo和GetStyleInfo,这个周末整理一下,写下mush下log法官字库的方法。纯属技术交流,不存在商业行为,应该不违反叶子坑或者鸽子王们没有公示的《关于禁止法官机器的若干规定》的种种说法
                  同时再次感谢wena同学,虽然他可能不会再回来玩,也不会看到这个帖子了。


                  16楼2015-01-28 20:45
                  回复
                    突然发现上一次回帖是在2015年2月,一转眼过去半年多了,这半年再也没摸过mushclient和lua。鉴于这半年手动炒股失败,考虑开始上机器人炒股,经过简单的比较,初步考虑采用python。为了学习新姿势,必要的做法是用玩游戏的方式来学。所以,后面有可能会更新一下mushclient+python。
                    作为一个年近中年的连伪码农都算不上的非码农,是不是太拼了点呢。


                    20楼2015-09-11 17:26
                    回复
                      经过几天内的摸索
                      不建议使用mushclient+python
                      mushclient只支持python2,而且对汉字的处理很讨厌,需要手动处理unicode/utf-8/gbk等格式的转换
                      而且编写脚本不如lua友好方便
                      我从原来的lua脚本搬到python,搬的不那么顺利


                      21楼2015-09-20 10:55
                      回复
                        一晃一个月又过去了。最近比较忙,没什么时间写机器,有时候周末或者半夜抽空写写,居然也开始能24小时挂机了,可以跑跑袁天罡灭妖,白板学习,李靖灭妖,自动学习。没时间看盘,因此bug多多。发布了几次袁天罡白板机器之后,就没有再发布了。以后,看情况吧。mush对于不会的玩家来说,除非机器做到完全自动不出错,不然一旦出错,就只能干瞪眼,不知道怎么切换回手动模式。这点和zmud不太一样。


                        22楼2015-10-31 19:57
                        回复
                          更新一点干活:mushclient 连接数据库。
                          mushclient支持access,mysql,sqlite,等等众多数据库。考虑到sqlite不需要配置服务器装一堆软件,一个本地数据库文件就搞定,比较适合thu的挂机应用。
                          mush原生支持sqlite,有两种访问方式,一种是mush自己写的sqlite3接口,以DataBase关键字作为入口,比如
                          DatabaseOpen ("db", -- database ID
                          GetInfo (66) .. "mytestdb.sqlite", -- file name
                          6)
                          DatabaseClose ("db") -- close it
                          另外一种是采用lsqlite3接口,这是一种基于通用的lua访问sqlite3的数据接口,也是mush原生支持的。考虑到通用性,还是采用lsqlite3吧。哪天失业了,没准可以去帮人写lua sqlite代码糊口。&_@
                          上代码:
                          function lib.sql.init2()
                          local tmp
                          mydb = sqlite3.open (GetInfo (66) .. "./worlds/lua/thu.db")
                          tmp = mydb:exec ([[
                          CREATE TABLE IF NOT EXISTS yuan(
                          myid TEXT,
                          item INTEGER default 0,
                          start_time INTEGER,
                          level INTEGER,
                          cplace TEXT,
                          cname TEXT,
                          ename TEXT,
                          reward_wx INTEGER default 0,
                          reward_dx INTEGER default 0,
                          reward_qn INTEGER default 0,
                          use_time INTEGER default 0,
                          flag INTEGER default 0
                          );
                          ]])
                          mydb:close()
                          end
                          用于创建一个表,内容下面看看就知道了
                          id:灭妖数量:时间:时间_秒:难度:地点:怪名:怪id:奖励武学:道行:qn:灭妖用时:是否完成

                          统计一堆id每天的灭妖情况,大数据分析地图盲点,不同id哪个灭妖容易一些,脚本改进之后是否能提高生产力等等。
                          后面有条件,做一个quest的数据库,如来灭妖的数据库,可以分析不同任务的奖励情况,进行合理的选择。
                          下面是填数据的脚本:
                          function lib.sql.insert_lijing()
                          local line,sql
                          line = lib.sql.get_max_line(idinfo.myid,"lijing") + 1
                          sql = "INSERT INTO lijing (myid,item,in_time,start_time,level,cplace,cname,ename) VALUES ('"..idinfo.myid.."',"..line..",'"..os.date().."',"..task.lijing.start_time..","..task.lijing.task_level..",'"..task.lijing.cplace.."','"..task.lijing.cname.."','"..task.lijing.ename.."');"
                          mydb = sqlite3.open (GetInfo (66) .. "./worlds/lua/thu.db")
                          print(mydb:exec (sql))
                          mydb:close()
                          end
                          ---------zmud没有办法做这些东西。这也是我为什么放弃zmud的原因之一。


                          23楼2015-10-31 20:19
                          回复
                            继续数据库,开封Quest系统,很多人都用zmud跑过,我用zmud721跑了好长时间的quest,里面绝大部分内容都是我重新整理验证过的,不想重复劳动,于是搬到mush上面来。这个工作不难做。
                            zmud打开quest数据库,export成csv文件。
                            运行sqlite3.exe,执行下面的操作:

                            第一句.open quest_thu.db,打开/创建一个名字叫quest_thu.db的数据文件
                            第二句.separator,采用,作为分隔符,csv文件是这样分割的。
                            第三句.import 721.csv quest将zmud导出的数据库文件721.csv导入到表quest里面
                            我们list一下看看:
                            select * from quest;

                            Done!
                            剩下的就是把zmud的一堆alias搬到lua的表里面来执行


                            24楼2015-10-31 20:29
                            回复