|
和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,因此。。。
但我不是暴雪,我没法用锦标赛阿!
|
评分
-
查看全部评分
|