TC官方合作论坛

标题: 讨论一下TC多线程使用大漠或天使插件问题[已反馈问题结果] [打印本页]

作者: perock    时间: 2013-12-5 23:59
标题: 讨论一下TC多线程使用大漠或天使插件问题[已反馈问题结果]
本帖最后由 perock 于 2013-12-10 15:48 编辑

小弟目前使用大漠+TC开发一游戏挂机,现有一点问题百思不得其解,外挂中使用三个线程:
线程1,负责监视血值变化,血值突然减少就立即使用随机,这个线程中主要是做ocr文字,然后判断血值,如果为真才会去按键,这个线程必须有。
线程2,用于监视地点和做一些挂机前的初始工作,比如修理装备,买材料等。这线程也是循环做判断,特定条件才会按键,也是必须的。
线程3,打怪与捡物品,这线程中就有频繁操作按键与鼠标,这个线程也是必须。

以上三线程中都使用大漠后台鼠标与键盘、找字、找图等功能。并且三个线程中使用一个大漠实例对象。
问题出来了,三个线程启动后,线程3打怪偶尔会停止运行(代码不执行了,没任何debug输出,不知何原因),这也只是偶尔出现,无固定时间,有时运行10分钟就停了,有时20分钟停了。
因目前tc并不能查询线程状态,所以只是推测线程是不是崩溃了。

看过大漠chm中讲过,多线程中键鼠可能冲突,所以我尽可能的减少了不同线程的键鼠操作,当心键鼠冲突引起线程停止。
我用了许多方法去测试了,包括怀疑是不是多线程键鼠操作冲突,插件bug,tc线程bug等,所以我尽量避免多线程操作鼠标键盘。
其实,以上三线程同时操作键鼠的机率不大,比如线程1不可能频繁去按键,因为被人杀也少遇到,所以线程1只会频繁去做ocr
线程2中与线程3也不会键鼠冲突,因为线程2只会在回城后执行一系列键鼠操作,而线程3中判断了当前地点是不是打怪地点,如果否就不会执行键鼠操作,如此,也不会出现键鼠冲突问题。

其次,怀疑大漠插件内部问题,造成tc线程崩溃而停止。这个没法测试,只能看看论坛朋友们有无遇到过。
最后,tc线程bug这个问题,估计不成立,因为tc仅仅调了winapi启动线程而已。


以前所述均在无逻辑问题前提下讨论的,比如线程未同步去修改某些数据啊,死循环啊,线程被其它线程暂停啊,都没有这样问题。


作者: perock    时间: 2013-12-6 00:01
  1. function 打怪与寻路()
  2.     bossIsDaed = true
  3.     hadKillBoss = false
  4.     x,y
  5.     while(true)
  6.         
  7.         if(新武尊辅助.获取当前地点() != "玛雅一层")
  8.             return 0
  9.         endif
  10.         
  11.         debug.print("开始换地方")               
  12.         新武尊辅助.停止挂机()
  13.         新武尊辅助.更换地点()
  14.         
  15. 打开辅助:  
  16.         //查找辅助窗口是否打开? 没有则打开
  17.         if(dm.FindStr(0,0,w,h,"挂机","43.89.98-0.0.0",1.0,x,y) == -1)
  18.             dm.MoveTo(辅助x+10,辅助y+10)
  19.             if(辅助x <= 0 && 辅助y <= 0)
  20.                 debug.print("辅助坐标有错")
  21.             endif
  22.             help.sleep(200)
  23.             dm.LeftClick()
  24.             help.sleep(200)
  25.         endif
  26.         
  27.         加载附近怪x,加载附近怪y
  28.         //如果没找到,说明tab页不是挂机页,则要点击挂机页
  29.         if (dm.FindStr(0,0,w,h,"加载附近怪","43.89.98-0.0.0",1.0,加载附近怪x,加载附近怪y) == -1)                    
  30.             if(dm.FindStr(0,0,w,h,"挂机","43.89.98-0.0.0",1.0,x,y) == -1)
  31.                 debug.print("未找到挂机-打开辅助")
  32.                 goto 打开辅助
  33.                 help.sleep(200)
  34.             endif
  35.             
  36.             dm.MoveTo(x+10,y+10)
  37.             help.sleep(200)
  38.             dm.LeftClick()
  39.             debug.print("加载附近怪")
  40.             if(dm.FindStr(0,0,w,h,"加载附近怪","43.89.98-0.0.0",1.0,x,y) == -1)
  41.                 debug.print("加载附近怪-打开辅助")
  42.                 goto 打开辅助
  43.             endif
  44.             dm.MoveTo(x+10,y+10)
  45.             
  46.             help.sleep(200)
  47.             dm.LeftClick()
  48.             help.sleep(200)
  49.             a = x
  50.             b = y
  51.         else
  52.             
  53.             dm.MoveTo(加载附近怪x+10,加载附近怪y+10)
  54.             help.sleep(200)
  55.             dm.LeftClick()
  56.             help.sleep(300)
  57.             a = 加载附近怪x
  58.             b = 加载附近怪y
  59.         endif
  60.         
  61. aa:
  62.         debug.print("while 查怪")
  63.         while(dm.FindStr(0,0,w,h,"猛犸教主|奴玛教主|重装使者|妖月金刚|邪恶蛇蝎|妖月血魔","0.0.100-0.0.0",1.0,x,y) != -1)
  64.             新武尊辅助.启动Z键()
  65.             debug.print("进入while查怪")
  66.             help.sleep(300)
  67.             if(dm.FindStr(0,0,w,h,"加载附近怪","43.89.98-0.0.0",1.0,x,y) == -1)
  68.                 debug.print("查怪 - 转到打开辅助")
  69.                 goto 打开辅助
  70.             endif
  71.             dm.MoveTo(x+10,y+10)
  72.             debug.print("点击加载附近怪 x"&x &"y"&y)
  73.             help.sleep(200)
  74.             dm.LeftClick()
  75.             help.sleep(200)
  76.             hadKillBoss = true
  77.             findBossTime = system.gettickcount()
  78.         endwhile
  79.         
  80.         if(x>0 && y>0)
  81.             goto aa
  82.         endif   
  83.         
  84.         if(type.int(findBossTime+50000) <= type.int(system.gettickcount()) )
  85.             debug.print("超时未找到怪,随机飞")
  86.             新武尊辅助.active()
  87.             dm.KeyPress(keys.VK_6)
  88.             help.sleep(1000)
  89.         endif
  90.         
  91.         if (hadKillBoss == true)
  92.             debug.print("关闭所有窗口")
  93.             新武尊辅助.关闭所有打开的窗口()
  94.             新武尊辅助.停止挂机()
  95.             新武尊辅助.拾取物品()
  96.         endif
  97.         hadKillBoss = false
  98.         
  99.     endwhile
  100. endfunction
复制代码


这是线程3中的代码,欢迎大家指点一下。
作者: perock    时间: 2013-12-6 00:03
  1. function 监视血值()
  2.     x,y,lastHp
  3.     t = edit.gettext("edtUseRndHP")
  4.    
  5.     if (dm.FindStr(0,0,302,122,"辅助","43.89.98-0.0.0",1.0,x,y) == -1)
  6.         help.messagebox("没有找到'辅助'!")
  7.         return 0
  8.     endif
  9.    
  10.     while(true)
  11.         s = dm.Ocr(x-27,y-45,x+69,y-31,"0.0.100-0.0.0",1.0)
  12.         if(s == "")
  13.             //help.messagebox("血值出错")
  14.             debug.print("血值出错")
  15.         endif
  16.         pos = str.findstr(s,"/")
  17.         curHp = type.int(str.strsub(s,0,pos))
  18.         sumHp = type.int(str.strsub(s,pos+1,str.strleng(s)))
  19.         
  20.         if(curHp < sumHp * HP百分比)
  21.             NeedPickHP = true
  22.         else
  23.             NeedPickHP = false
  24.         endif
  25.         if (curHp + 一次性掉血 < lastHp)
  26.             新武尊辅助.active()
  27.             
  28.             dm.KeyPress(keys.VK_6)   
  29.             
  30.             if(combo.gettext("ComboBox0") == "矿洞挖矿")
  31.                 help.sleep(1000)
  32.                 dm.KeyPress(keys.VK_Z)
  33.             endif
  34.             
  35.         endif
  36.         help.sleep(200)
  37.         lastHp = curHp
  38.     endwhile
  39. endfunction
复制代码


这是线程1,监视血值变化的线程代码,欢迎大家指正。
作者: perock    时间: 2013-12-6 00:06
  1. function 初始化监视()
  2.     while(true)
  3.         s = 新武尊辅助.获取当前地点()
  4.         if(s == "沙漠土城")
  5.             新武尊辅助.修理装备()
  6.             新武尊辅助.购买令牌()
  7.             token = edit.gettext("edtTokenLoc")
  8.             新武尊辅助.active()
  9.             dm.KeyPress(keys.TransKey(token))
  10.             help.sleep(1000)
  11.         endif
  12.         help.sleep(1000)
  13.     endwhile   
  14. endfunction
复制代码


这是线程2代码, 主要是监视是否回城,这段代码主要是判断地点。当然,这个代码也可以放在线程3中。
作者: jrflsh    时间: 2013-12-6 00:14
你代码内有没有使用线程关闭操作?
我写的一个脚本有过这样的问题,线程关闭之后重新开启,开启成功已经返回线程句柄了。
然后就没有然后了,线程就这样开启之后莫名其妙的不执行了
作者: perock    时间: 2013-12-6 00:17
jrflsh 发表于 2013-12-6 00:14
你代码内有没有使用线程关闭操作?
我写的一个脚本有过这样的问题,线程关闭之后重新开启,开启成功已经返 ...

线程统一在外挂停止时执行关闭的,中途没有其它代码去暂停或停止任何线程。
作者: perock    时间: 2013-12-6 00:20
  1. function start_click()
  2.     path=help.getrcpath("rc:dm.dll")
  3.    
  4.     regRet = help.regdll(path,true)
  5.     if (!regRet)
  6.         help.messagebox("注册DLL失败!")
  7.         return 0
  8.     endif
  9.     dm = com("dm.dmsoft") //创建dm对象
  10.     hwnd = dm.GetMousePointWindow()
  11.     if (hwnd <= 0)
  12.         help.messagebox("没有找到游戏窗口")
  13.         return 0
  14.     endif
  15.     //bindRet = dm.BindWindow(hwnd,"gdi","windows","windows",1)
  16.     dmRet = dm.SetWindowState(hwnd,1)
  17.     bindRet = dm.BindWindowEx(hwnd, "gdi", "windows", "windows","dx.public.fake.window.min", 0)
  18.     if (bindRet == 0)
  19.         help.messagebox("bind失败!")
  20.         return 0
  21.     endif
  22.     dm.SetKeypadDelay("windows",50)
  23.     base_path = help.getrcpath("rc:")
  24.     dm_ret = dm.SetPath(base_path)
  25.     dm_ret = dm.SetDict(0,"myDic.txt")
  26.     新武尊辅助.初始基坐标()
  27.     新武尊辅助.初始化变量()
  28.     if(combo.gettext("ComboBox0") == "玛雅一层挂机")
  29.         线程查血 = thread.beginthread("新武尊辅助.监视血值","")
  30.         if(线程查血 == null || 线程查血 <= 0)
  31.             help.messagebox("线程查血 线程未启动成功")
  32.         endif
  33.         线程初始监视 = thread.beginthread("新武尊辅助.初始化监视","")
  34.         if(线程初始监视 == null || 线程初始监视 <= 0)
  35.             help.messagebox("线程初始监视 线程未启动成功")
  36.         endif
  37.         help.sleep(3000)
  38.         线程打怪 = thread.beginthread("新武尊辅助.打怪与寻路","")
  39.         if(线程打怪 == null || 线程打怪 <= 0)
  40.             help.messagebox("线程打怪 线程未启动成功")
  41.         endif
  42.     endif
  43.    
  44.    
  45.     control.enable("start",false)
  46.     control.enable("btnEnd",true)
  47.     statue = true
  48. endfunction
复制代码


这是启动热键所触发的代码,此代码启动线程。
作者: jrflsh    时间: 2013-12-6 00:20
那你查查看是不是卡死在哪个循环里面去了
作者: cylhb    时间: 2013-12-6 00:21
不要认为机会不大就代表没机会。
多线程同一对象键鼠操作可以先用临界区或者创建事件试试。

当然有时候也不要想太复杂。
先直接从线程3里面的代码入手。每个入口和出口点都加一下输出语句。当不打怪的时候看看输出语句处于什么位置来大概判断问题所在。
作者: perock    时间: 2013-12-6 00:25
jrflsh 发表于 2013-12-6 00:20
那你查查看是不是卡死在哪个循环里面去了

线程3的while中有print,如果是此处死循环,那么while断点应有断下来,并且调试器应也能输出调试信息。但现在一切都没有。
作者: perock    时间: 2013-12-6 00:27
cylhb 发表于 2013-12-6 00:21
不要认为机会不大就代表没机会。
多线程同一对象键鼠操作可以先用临界区或者创建事件试试。

是,我下一步要做的就是,将所有键鼠操作全部封成新方法,在方法中作临界同步,确保同一时间只有一个线程在操作键鼠,这也是唯一能测试的方法了。
作者: dongyijun1979    时间: 2013-12-6 00:43
估计跟你具体的游戏相关。你一个线程一个插件对象。

作者: perock    时间: 2013-12-6 00:48
dongyijun1979 发表于 2013-12-6 00:43
估计跟你具体的游戏相关。你一个线程一个插件对象。

大漠插件,最多只充许二个线程中Bind同一个窗口,不过我之前也做过这样的测试,在线程内实例化dm,让每个线程使用单独的dm,但问题似乎依然存在。
作者: dongyijun1979    时间: 2013-12-6 00:52
记得我老师说过:
有时候需要:(1)主线程子线程均创建插件对象
            (2)主线程创建,子线程不用创建,就用主线程创建的插件对象
            (3)我忘了
全因游戏而定。

作者: dongyijun1979    时间: 2013-12-6 00:55
我所说的主线程,子线程是相对而言的。你别误解了。
B线程里创建了C线程。我这里将B线程叫主线程,C线程叫子线程。尽管B可能是由A线程创建的。
作者: dongyijun1979    时间: 2013-12-6 01:20
不过我之前也做过这样的测试,在线程内实例化dm,让每个线程使用单独的dm,但问题似乎依然存在
你测试的时候,确实是用的三个线程里各一个插件对象吗?你不是说:
大漠插件,最多只充许二个线程中Bind同一个窗口






---------------------------------------------------------------------------------------------------------------------------------------


我老师的轩辕传奇辅助,46000+的代码,用了两个子线程。我的意思:你的线程1,线程2能不能合成为一个(检测线程),线程3其实就是 任务线程。



---------------------------------------------------------------------------------------------------------------------------------------

想起来了。 第三种情况是:主线程不用插件对象,子线程里各一个插件对象
建议你把三种方式都试一下。


---------------------------------------------------------------------------------------------------------------------------------------
没写过大家伙,缺乏实战经验。我能想到的就这么多了~







作者: kaixindexifan    时间: 2013-12-6 10:20
perock 发表于 2013-12-6 00:25
线程3的while中有print,如果是此处死循环,那么while断点应有断下来,并且调试器应也能输出调试信息。但 ...

并且三个线程中使用一个大漠实例对象





[attach]13158[/attach][attach]13159[/attach]








作者: bocai7821    时间: 2013-12-6 14:25
写成一个线程吧
怎么简单的功能还要3个线程啊
改成一线程会简单很多
作者: myaoao    时间: 2013-12-6 16:50
多线程序最容易出现这个情况了,当需要键鼠操作时,只保证有一个线程工作就OK了。
作者: perock    时间: 2013-12-6 16:50
bocai7821 发表于 2013-12-6 14:25
写成一个线程吧
怎么简单的功能还要3个线程啊
改成一线程会简单很多

写成一个线程有以下问题:

在打怪或捡物品的时候,如果有人pk你,而此时代码尚未执行到查血处,那么在几秒中内就可能被人pk掉。

打怪与捡物过程有一系列判断与键鼠操作,另外还会有sleep,如果用一个线程,恐怕时刻要执行查血,如此,效率会很低。
作者: perock    时间: 2013-12-6 16:55
kaixindexifan 发表于 2013-12-6 10:20
并且三个线程中使用一个大漠实例对象。

谢谢您能我发图,这个chm我看了,之前也曾使用多个实例去bind一个窗口,后来发现它与使用一个实例并无差别,所以仍又改用一个实例了,不过在线程中尽量避免了同时操作键鼠。
作者: kaixindexifan    时间: 2013-12-6 17:01
perock 发表于 2013-12-6 16:55
谢谢您能我发图,这个chm我看了,之前也曾使用多个实例去bind一个窗口,后来发现它与使用一个实例并无差 ...

并无差别??????

那你意思 大漠哥脑袋短路咯
作者: bocai7821    时间: 2013-12-6 20:12
perock 发表于 2013-12-6 16:50
写成一个线程有以下问题:

在打怪或捡物品的时候,如果有人pk你,而此时代码尚未执行到查血处,那么在几 ...

慢肯定慢点,0.00几秒吧.忽略不计吧
本来打怪的时候肯定要判断血蓝保护,打死怪后也要判断
是否有血蓝,没有就打坐回血蓝,在回血蓝种
被怪攻击,自动反击,反击完后后继续打坐回蓝血,
接着判断是否回城买卖,是否超出挂机范围,如果超出返回挂机点,
判断是否要加辅助技能,在捡起物品,最后在选怪,选中怪后判断是否是精英怪或者是BOSS,如果是
在根据设置判断换不换怪,如果不换,接着判断是否出现反外挂答题,有答题,然后判断血蓝,在打怪,打怪中
用完一个技能判断怪物死亡否,如果死直接后面技能就不用了直接换怪
我大多数写打怪都是这样
反正就是一个线程
打怪中要判断反外挂答题和血蓝保护,
打死怪后要判断辅助技能,是否会回城,是否超出挂机范围,是否血蓝保护,是否有血蓝,是否捡起
选怪是否精英或者BOSS
全部加清理判断0.3秒左右吧
反正忽略不计吧
作者: bocai7821    时间: 2013-12-6 20:16
perock 发表于 2013-12-6 16:50
写成一个线程有以下问题:

在打怪或捡物品的时候,如果有人pk你,而此时代码尚未执行到查血处,那么在几 ...

不过写辅助技能有点复杂
毕竟辅助技能都几十分钟才使用一次
所以要用到系统.启动时间来判断
作者: perock    时间: 2013-12-10 15:47
分享一下问题解决方法:

1,不要轻易怀疑IDE与插件bug
2,这个偶尔引起线程假崩溃的凶手是
  1. if(新武尊辅助.获取当前地点() != "玛雅一层")
  2.        return 0
  3. endif
复制代码


这个代码看似没问题,实际上风险非常大,在做ocr时偶尔可能识别不正确,如此将会触发return 退出线程,造成无法察觉问题。
所以将安全起见 将 return 改成continue




欢迎光临 TC官方合作论坛 (http://bbs.52tc.co/) Powered by Discuz! X3.1