请选择 进入手机版 | 继续访问电脑版

GA地精研究院

 找回密码
 立即注册
查看: 2839|回复: 20

[演示&系统] 深入研究触发与handle

[复制链接]
发表于 2009-4-15 07:47:24 | 显示全部楼层 |阅读模式
和linzefei测试了一晚上的一些结论

思路
当一个建造中的建筑取消,会同时触发建筑取消事件,和单位死亡事件。
那么这至少可以摆脱,触发时间和次数不同是否导致掉线的问题,
从而能够单独的研究触发本身产生的结果。

测试1
环境: 空白地图,增加function H2I,默认对战触发
触发: 在初始化时,同步产生一个触发 tr = CreateTrigger()
事件: 对玩家0注册 玩家0单位死亡事件,对玩家1注册 玩家0建筑取消事件
条件: 无
动作:
1. 显示 被取消的建筑GetCancelledStructure() 的H2I值
2. 产生一个timer tm = CreateTimer(),而后在屏幕显示其H2I值

测试流程
玩家0建造。
玩家0取消建造。

测试结果
1. 分别注册事件,并未掉线。并且在以后的测试中都未在这一步掉线。
     可以认为触发如果在同时注册事件,并且注册同数量的事件,则事件内容不影响同步。
    【评点:同时间注册同数量事件,会在handle表中同时产生同等数量的event handle。】
2. 玩家0的屏幕上显示0,玩家1的屏幕上显示了一个单位的Handle值。
    【评点:玩家0注册单位死亡,因此没有被取消的建筑,而玩家1注册建筑取消,因此有被取消的建筑。】
3. 玩家0建造并取消建造以后,两个玩家同时产生了一个timer:
     玩家0产生的timer Handle值比玩家1产生的timer Handle值小1!
     而游戏在这时仍未掉线!
     //当时的情况
     //玩家0, 单位死亡事件,H2I(tm) = 1048787
     //玩家1, 建筑取消事件,H2I(tm) = 1048788
    【评点:为什么相差1。是在屏幕上显示了被取消的建筑,因此建筑取消事件的玩家1的handle多了1么?】
4. 随意挪动农民走路,攻击,几十秒后掉线。
    【评点:handle已经不同步,掉线不奇怪,奇怪的是掉的这么慢】

于是尝试把显示GetCancelledStructure()去掉。

测试2
环境 / 触发 / 事件:同测试1
动作:
-禁用- 显示 被取消的建筑GetCancelledStructure() 的H2I值
产生一个timer tm = CreateTimer(),而后在屏幕显示其H2I值

测试流程
同1

测试结果
1. 和测试1完全相同,玩家0 timer显示1048787,而玩家1 timer显示1048788!
【评点:看来和调用GetCancelledStructure()无关
2. 同样在一段时间后掉线了

ps.实际上之所以无关是因为保留了默认触发,而默认对战触发中有【建筑开始建造】的触发
而这个被取消的建筑之前也经过了这个触发,因此他有handle值,因此不管怎么调用一个指向他的指针
也不会产生新的handle!这就好像只要有一个单位u的handle存在,无论设几个u1 = u, u2 = u,
u作为一个handle在除了泄露以外的情况一直不变,不会增加一个单位也不会减少一个单位!

这里有另一个结论是事件被触发会使得handle从预留变为分配
我在后面会详细讲关于这个结论的由来。


继续说测试,既然不是因为调用而多了1,那么为什么会多1
猜测可能是返回了什么结果不同
翻开 Common.j

// EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL
constant native GetCancelledStructure takes nothing returns unit
constant native GetConstructedStructure takes nothing returns unit

...

// EVENT_PLAYER_UNIT_DEATH
constant native GetDyingUnit takes nothing returns unit
constant native GetKillingUnit takes nothing returns unit

unitevent和playerunitevent所用的是完全一致的,在cj注释里也写了。

可以看到,common.j 按类型列出了某个事件所对应的可用函数。
由于都是玩家单位事件,他们也都可以用native GetTriggerUnit,
因此都是对应3个函数。

那么,看他们在这种情况下应当返回的值
//建筑取消事件
触发单位 返回这个建筑。
被取消的建筑单位 返回这个建筑。
被建造的建筑单位 返回这个建筑。

//单位死亡事件
触发单位 返回这个建筑。
死亡单位 返回这个建筑。
凶手单位 返回null,因为取消的建筑没有被凶手杀死。

【点评:原来凶手单位的不同才是导致handle不同的凶手!
如果粗略的理解,单位死亡事件比建筑取消事件返回的结果少了个单位,多了个null,
但是这个建筑应该已经有handle了呀!难道返回的handle并非建筑handle?】

测试3
尝试显示TriggerAction的H2I
结果:TriggerAction一样。
说明之前肯定是同步的,并不是注册不同事件产生了这个1的差异,而是事件触发后产生的。
难道这才是不能异步触发的真正原因?

测试4
既然不同事件触发会产生差异,那么事件触发是否产生handle呢?
如果每次触发都会产生handle, war3的效率岂不是岌岌可危?dota那种地图还能玩吗?

修改一开始的触发,令其动作为空。
增加一个触发:当玩家按ESC,创建一个timer并显示H2I值。

流程:玩家0建造-取消(用鼠标,以避免触发ESC)-而后按ESC
结果:
1. 两边产生的timer H2I又一致了!并且一直按ESC产生timer都一致!
2. 过了一会还是掉线了。
【点评:看来产生的差异在触发执行完毕后又没有了,为什么?
可能性A. 因为触发相关的Getxxx函数的环境不在了,因此消掉了。
可能性B. 因为这个建筑挂了,没有泄露,所以他的handle被XXOO了】

【既然又同步了,为什么还是掉线了呢?】

测试5
尝试把第一个触发的动作里加上TriggerSleepAction等待20秒。
按ESC创造timer的触发不变。

流程:玩家0建造-取消-按ESC(< 20秒)
结果:
1. ESC创造的Timer仍旧一致!
2. 又掉线了。
【点评:暂时我还不能判断到底是因为环境还是因为单位本身没了,
但猜测是因为单位没了:因为两个触发的动作是虚拟机中的两个线程,
通常来说,即使触发A等待很久,此时触发B执行动作,触发A中的环境相关函数并不会因此就失效,
如果他产生handle的话也不可能在A结束之前就忽然没有了。】
当然也可能是其他原因?

测试6
如果强制执行开始所定义的触发(忽略条件)会如何?会像事件发生时那样产生1的差距吗?

流程:触发1仍然是对两个玩家注册不同事件,动作是创建timer并显示
触发2,按ESC时,执行触发1。(TriggerExecute)
结果:
两边完全同步,产生的timer同步,并且无论怎么弄也不掉线
【点评:除了触发执行次数,估计跟call没区别。
不掉线是因为两边全部3个事件函数都返回null,所以同步了】


测试7
现在开始要换个思路了。既然知道了事件的返回值会*暂时*占用handle,
并且会对handle表造成不可逆转的影响,那么如果用同样的事件会如何?

还是注册两个事件,这次都是单位死亡事件,但是玩家不同

触发1
对玩家0注册玩家0单位死亡事件。
对玩家1注册玩家1单位死亡事件。
动作是创造timer并显示。

触发2
按ESC触发。
动作是一个新加的单位伤害全地图100点。

流程:增加了一个玩家6的英雄,以及玩家0和玩家1各一个房子,只剩下10点血。
按ESC,单位伤害全地图100点,两个房子爆了。

结果:直接掉线。
不同之处只是,玩家6的大法师伤害范围会导致玩家0产生5个timer,玩家1不产生
而如果大法师是玩家0的就会玩家0不产生,玩家1产生1个timer
至于不用伤害范围而用其他办法同时摧毁这2个房子(比如下雪)
结果也一样是掉线。
【点评:按理WE放的房子都是CreateUnit出来的,所以有handle...
那么导致掉线的是什么?】



【暂时的结论】
没有更多的测试,但初步结论如下:

1. 触发同时注册同等数量个事件不掉线。
2. 事件触发时会根据事件不同、具体情况不同(例如凶手单位是否null)
    产生若干个新handle,而尽管他们指向已有的handle,却占据新位置。
3. 不知道事件产生的新handle是暂时的还是永久的,至少对于单位死亡事件来说是暂时的。
4.a 暂时不掉线是因为影响同步的handle被消灭了。这甚至包括第一次造出的h2i值都不同的timer
尽管他们不同,但我并没有用他们;后来出现的handle会自动使用他前面和后面的空白;
在一段时间内看起来,后造的handle都是同步的。

4.b 后来掉线是因为他还是造成了永久的,无法消除的影响。
在dota以及很多复杂的地图里,我们也见过这种情况,本该立即掉线的行为却等了好一段时间才掉。
推论:同步并非类似校验handle表hash,而更类似于对其中每个单位进行校验,而且并不是很频繁,
并且也有其效率问题。
p.s. 校验的一定是handle而不是实际地址,因为单位的实际地址不应该也不可能一样。
另一个支持的证据是,unit的大多数据只要一改立刻掉线,但如果解释为监视所有数据,
又无法解释许多handle例如group, timer, effect等的内容和参数可以任意改变。
所以应当是每个变量分别校验,根据类型校验,如此等等。



结论是什么呢?如果按现在的推论,单位事件至少会有GetTriggerUnit所以会有unit
玩家事件有GetTriggerPlayer,如果有唯一一个触发可能异步运行,
大概是。。
// EVENT_GAME_TOURNAMENT_FINISH_SOON
constant native GetTournamentFinishSoonTimeRemaining takes nothing returns real

游戏事件没有基础的返回值,而这个事件只返回real,因此。。。
但我不是暴雪,我没法用锦标赛阿!

评分

参与人数 1威望 +4 收起 理由
血戮魔动冰 + 4 口水流~好东东~~

查看全部评分

 楼主| 发表于 2009-4-15 09:15:23 | 显示全部楼层
继续写另一个方面。
触发,事件与“隐形”handle

之前尝试捕捉合成的角鹰骑士单位,结果很奇怪。

在不用任何触发和不用groupenum的前提下,用I2U暴力搜索handle表,
结果找不到。但如果用groupenum或者触发就一定能找到。

是搜索出了毛病吗?

做一张空地图,只有弓箭和角鹰,非常简单
先用groupenum搜到合成的角鹰骑士,记下h2i值。
修改地图,在不改变任何handle的条件下(包括之前用到的group)
仅仅不用groupenum,而直接I2U(记下的h2i值)

结果如何?结果这是个无效单位变量!

各方面的测试都证明,这个值还真对了,非他莫属,
很容易测试出,比他小的handle都不变,比他大的(后创造的)
handle也不变,那么他必然是第一次测试时记下的h2i值。

类似的情况也发生在其他地方。
一张空白地图,默认对战触发,用暴力搜索可以搜到新造的建筑。
但如果去掉默认对战触发,则暴力搜索不到。
这是因为对战触发中在bj中含有检测建筑建造的触发!

是因为触发中调用了GetTriggerUnit()或者类似的返回单位的函数
导致它从无效变成有效了吗?

做了一个测试,去掉所有默认触发,自定义触发-建筑开始建造
条件和动作什么都没有

之后再用暴力搜索,成功搜出了。

这证明产生handle的并非触发的环境函数,而是触发的事件!


在此我大胆的猜测,以往的一些看法的大前提就是错的,
实际上war3根本就不依赖于jass运作,一个单位完全可以不在handle表中
(仅仅由于计数器认为创造了一个单位所以预留了一个空格)

触发,timer等被创造出的东西有handle。同样被函数创造出的unit有handle。

其余所有的我们司空见惯的unit,item之所以可以得到其值,
是因为定义了触发事件去得到他!如果不定义触发,
则他们在jass环境看来根本不存在!


现在计划做的事情

1.
测试如果玩家选中了角鹰骑士,能否用GroupEnumUnitsSelected得到他,是否掉线。
对于一般的单位来说是不掉线的,因为一般单位在默认触发有训练完毕触发,因此都有handle。
此外GroupEnumUnitsSelected是目前看来唯一不掉线的GroupEnum,如果要问为什么其他的
会掉线,我猜要么是因为选择可以不同步(选择是唯一先执行后同步的操作)
要么是因为其他GroupEnum很容易搜到一些本来没有handle的单位,使他们获得了handle分配,
从而导致不同步。

如果是后者,则GroupEnumUnitsSelected搜到角鹰骑士会掉线,那么究竟掉不掉就有待测试了

而如果是前者,则可以出现两个玩家一边拥有某个单位的handle,一边则是无效单位
尽管其实预留了位置,不会导致后面handle错位
那么如果这种情况可以同步,就说明同步校验在无效单位上不起作用,
就说明可以使用japi逆转这个过程,
把某个handle改成‘无效单位’从而获得任意更改他的权力!
对于单位可能没有意义,但对于触发来说呢?

2.
研究game.dll 中是否有废弃的,没写在common.j里的native 函数
忽然想到common.j 里的native未必是全部存在的函数;
因为用japi可以设计一个函数而不在jass定义他。
(也许会发现各种无敌牛逼改数据改内存debug函数)
回复 支持 反对

使用道具 举报

发表于 2009-4-15 12:31:38 | 显示全部楼层
嗯,有同感。
LS总结很好。
回复 支持 反对

使用道具 举报

发表于 2009-4-15 13:03:17 | 显示全部楼层
【2.
研究game.dll 中是否有废弃的,没写在common.j里的native 函数
忽然想到common.j 里的native未必是全部存在的函数;
因为用japi可以设计一个函数而不在jass定义他。
(也许会发现各种无敌牛逼改数据改内存debug函数)】
…………………………邪恶的japi啊……
这样一来,jass的防作弊看来要被彻底搞掉了………………
回复 支持 反对

使用道具 举报

发表于 2009-4-15 13:59:00 | 显示全部楼层
为什么会被搞掉了呢,LS思维跳得好远啊
回复 支持 反对

使用道具 举报

发表于 2009-4-15 17:08:09 | 显示全部楼层
其余所有的我们司空见惯的unit,item之所以可以得到其值,
是因为定义了触发事件去得到他!如果不定义触发,
则他们在jass环境看来根本不存在!<---这个是全文的精华所在
回复 支持 反对

使用道具 举报

发表于 2009-4-15 17:52:21 | 显示全部楼层
引用第1楼thewisp1于2009-04-15 09:15发表的  :
做一张空地图,只有弓箭和角鹰,非常简单
先用groupenum搜到合成的角鹰骑士,记下h2i值。
修改地图,在不改变任何handle的条件下(包括之前用到的group)
仅仅不用groupenum,而直接I2U(记下的h2i值)

结果如何?结果这是个无效单位变量!

各方面的测试都证明,这个值还真对了,非他莫属,
很容易测试出,比他小的handle都不变,比他大的(后创造的)
handle也不变,那么他必然是第一次测试时记下的h2i值。

.......
有没有可能是把handle值加在Max+1的位置上了
因为原来的那两个位置要留给弓箭和角鹰兽
所以没有被占用?.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-4-15 19:13:17 | 显示全部楼层
引用第6楼_yihui于2009-04-15 17:52发表的  :

有没有可能是把handle值加在Max+1的位置上了
因为原来的那两个位置要留给弓箭和角鹰兽
所以没有被占用?.
从1048576一致搜到1049xxx
当时的handle绝对没有500个
回复 支持 反对

使用道具 举报

发表于 2009-4-15 19:19:07 | 显示全部楼层
类似的情况也发生在其他地方。
一张空白地图,默认对战触发,用暴力搜索可以搜到新造的建筑。
但如果去掉默认对战触发,则暴力搜索不到。
这是因为对战触发中在bj中含有检测建筑建造的触发!
<----------这个说法呢。。。你知道我是从来不用bj的。。。。
回复 支持 反对

使用道具 举报

发表于 2009-4-15 19:19:57 | 显示全部楼层
其实我的认识是:
一个(可以得到的)handle,当没有任何东西指向他的时候,就会自动被垃圾回收机制消除。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-4-15 19:30:29 | 显示全部楼层
只要曾经有事件捕捉到他,不需要任何东西指向他,他就存在了

反之如果他不存在,又如何指向他
回复 支持 反对

使用道具 举报

发表于 2009-4-15 21:44:26 | 显示全部楼层
引用第9楼eff于2009-04-15 19:19发表的  :
其实我的认识是:
一个(可以得到的)handle,当没有任何东西指向他的时候,就会自动被垃圾回收机制消除。

测试过了.
abc 3个单位 a<b<c  handle差值1
一开始a handle 分配了
i2u无法搜到bc
然后分配;了c(比如用选择单位事件)
搜索会发现ac之间有个空位
即使新建单位 也不会占用了

此时在分配b 就可以i2u搜索到了.

顺便说下.
returns unit 也会导致分配(i2u除外)  比如CreateUnit
回复 支持 反对

使用道具 举报

发表于 2009-4-15 21:46:45 | 显示全部楼层
应该说事件 和returns unit都会导致分配。
事件 会让jass可操作单位 所以必须分配handle

而CreateUnit 等 也会导致 jass可操作所以也分配

GroupEnum同理
回复 支持 反对

使用道具 举报

发表于 2009-4-16 08:29:50 | 显示全部楼层
引用第11楼linzefei于2009-04-15 21:44发表的  :


测试过了.
abc 3个单位 a<b<c  handle差值1
一开始a handle 分配了
.......
那么,也就是说一个单位或者其他Handle创建后会在Handle表里给它留下个位置
但是不会把它的值(大约是内存地址什么的)给添加上
直到我们给它注册事件时才添上这个值
于是,这个添加值的过程就是在事件 和returns 的执行中运行的
那么我们能否修改这个Handel对应的值,也就是说,对应不同玩家,同一个Handle对应的是不同单位
而这样是否会掉线?
估计会直接出错跳出吧……
回复 支持 反对

使用道具 举报

发表于 2009-4-16 09:10:45 | 显示全部楼层
一开始就给它留了Handle?研究越来越深入了。
回复 支持 反对

使用道具 举报

发表于 2009-4-16 13:37:35 | 显示全部楼层
不同时间获取同一单位,handle不同,
预留位置也变来变去,真恶心。。。(还是指向真handle的临时handle的问题?)


MB这种研究。。。
回复 支持 反对

使用道具 举报

发表于 2009-4-17 02:55:17 | 显示全部楼层
补充下。。。空位说也许只是个假象

TheWisp和我 再次测试发现
如果一方的空位分配了。1方未分配
那么同步新建单位 此时单位的handle并不一致 分配的那方handle会比未分配的大(未分配方占用了 空位,也许我11楼当时没测试完成)
此后任何新增handle的 变化都存在那个差值  而且一直不掉线


aeris提出了个 链表说

也就是handle的校验只校验那个链表 而不是实际handle值

这个假说 很符合目前的测试结果

比如TheWisp测试了 创建1000个计时器
1方删除前500个 1方删除后500个
那样以后新增handle都差值500
仍是不会掉线,也许2方都是只剩下500个计时器 链表长度一致 所以仍同步

目前结论。。。
回复 支持 反对

使用道具 举报

发表于 2009-4-17 07:18:15 | 显示全部楼层
如果在那500空位插入其他Handle变量呢,会怎样?
掉线
如果掉的话就是说明不光检查长度
还要检查顺序
回复 支持 反对

使用道具 举报

发表于 2009-4-17 09:30:29 | 显示全部楼层
引用第16楼linzefei于2009-04-17 02:55发表的  :
补充下。。。空位说也许只是个假象

TheWisp和我 再次测试发现
如果一方的空位分配了。1方未分配
那么同步新建单位 此时单位的handle并不一致 分配的那方handle会比未分配的大(未分配方占用了 空位,也许我11楼当时没测试完成)
.......


我没成功实现过。。
回复 支持 反对

使用道具 举报

发表于 2009-4-17 12:02:25 | 显示全部楼层
ls试试这个

handle空位测试2.w3x

15 KB, 下载次数: 29

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2019-12-10 20:59 , Processed in 0.688255 second(s), 32 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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