redstone_machine...吧 关注:3,609贴子:60,858

EM的实验验证和源码解析

只看楼主收藏回复

运动计算和爆炸计算不统一 (Motion and Explosion are not uniform),简称EM,对于这个Bug在此贴中将提出一个有说服力的,有支撑的解释,并且从完全不同的方面给出几种有效的解决方案


IP属地:广东1楼2017-03-13 23:55回复
    运动计算和爆炸计算不统一 (Motion and Explosion are not uniform)
    简介:
    简称EM,属于爆炸和运动计算顺序的Bug
    PS:它真的坑了很多人
    定义:
    对于任意几个TNT实体,如果它们具有下面两个特点
    1. 可以因本身的爆炸效果而影响对方
    2. 在同一Gametick爆炸(在此爆炸的含义是变量fuse≤0)
    那么会使这几个TNT实体按照进入实体更新队列的顺序依次让其它TNT实体产生1 Gametick的偏移,再进行爆炸处理,直到在此Gametick没有fuse≤0的TNT实体
    下面将带你从实验计算和源码辨析两个方面来理解和解决EM


    IP属地:广东2楼2017-03-13 23:55
    回复
      EM的实验验证:
      首先让我们看一个用来演示EM的结构

      按照常理两个TNT实体应该在水中同时爆炸,而不会产生方块破坏
      但是实际上两个TNT实体还是炸掉了这个结构

      这个现象的原因就是EM,这两个TNT集束堆可以因本身爆炸而影响对方,而且在同一Gametick爆炸,所以产生了EM,产生偏移(EM具体作用流程和产生原因随后会说)


      IP属地:广东3楼2017-03-13 23:57
      回复
        关于位移量的实验计算:
        如下图,我们有这个装置来测量这个偏移量,虽然会有一定误差,但是依然可以大致得到一些有用的数据


        就这样,测得三组的实验数据:
        10个推进TNT:7.5 m
        15个推进TNT:10.7 m
        20个推进TNT:14.9 m
        根据TNT实体的X轴和Z轴方向运动的关于时间n(单位Gametick)的运动函数和爆炸效果函数

        可以解得三组关于时间T的解

        这些解都十分接近1 Gametick 所以在误差允许的范围里,不妨就认为EM会产生1 Gametick的偏移量
        当然仅仅靠实验得出的结论是不可靠的,因此我们再看一下EM的源码解析


        IP属地:广东4楼2017-03-14 00:01
        回复
          EM的源码解析:
          首先对于一个实体来说,当它被创建,便会按创建顺序进入一个实体更新队列,每一Gametick里,基本都会按照队列顺序更新一次,所以对于实体来说它的更新是有顺序的,但是它们都是在同一Gametick中,基本无法显示出差异,所以我们均可以认为它们同时更新
          然而很不幸的是TNT实体就是特例之一,多个TNT实体的队列顺序恰巧会影响它们的最终处理结果,这也就是EM产生的原因
          那现在让我们看一下为何TNT实体的队列顺序会影响最终的结果
          下面是net.minecraft.entity.item.EntityTNTPrimed类里的onUpdate方法
          (覆盖了net.minecraft.entity.Entity中的onUpdate )
          用来更新TNT实体的位置和逻辑状态

          部分未标识在图中的变量等含义
          prevPos 是前驱位置
          motion 是运动量,类似于“速度”
          onGround 若所判断的实体在地面上则为true,反之则为false
          fuse TNT实体的爆炸引信时间,初始值为80
          isRemote 若属于客户端则为true,若属于服务器则为false
          可以看到TNT实体的运动计算优先于爆炸计算


          IP属地:广东5楼2017-03-14 00:03
          回复
            这帖子及时


            来自iPhone客户端6楼2017-03-14 00:03
            回复
              接下来再看看“Explode”的源码部分
              net.minecraft.entity.item.EntityTNTPrimed类里的explode方法

              net.minecraft.world.World类里的createExplosion和newExplosion方法

              于是经过了一系列的调用,终于可以看到真正的爆炸处理方法了XD
              net.minecraft.world.Explosion类的doExplosionA方法和doExplosionB方法
              doExplosionA:
              1. 经处理后,把将要破坏的的方块列入“破坏列表”,等待doExplosionB方法处理
              2. 把爆炸产生的实体运动处理完 (更新受爆炸影响实体的Motion值)


              doExplosionB:
              1. 处理并产生爆炸的粒子效果
              2. 将“破坏列表”的方块设置成air方块,并按照方块性质生成掉落物
              3. 根据参数判断是否应该产生fire

              可以看到在此依然是先进行运动设置,再进行爆炸处理,不过这两个方法是紧挨着循序执行,所以不是产生EM的原因


              IP属地:广东7楼2017-03-14 00:05
              回复
                但是如果将这几处源码结合在一起,就可以看出EM产生根本原因了
                1. 首先由于实体更新队列,实体大多都是顺序更新,这就导致了即使是同时放出的TNT实体也有先后的处理顺序
                2. 又因为TNT实体的运动计算要先于爆炸计算,虽然对于第一个处理的TNT实体,没有什么大的影响
                3. 但是当它本身的爆炸处理开始时,它的doExplosionA方法对旁边的TNT实体产生了Motion更新(Motion可以近似看作“速度”,然而其实Motion不是正经的速度XD),然后doExplosionB给予爆炸的破坏效果
                4. 结束并删除第一个TNT实体后,第二个TNT实体开始更新,但是因为第一个TNT实体的doExplosionA方法改变了它的Motion值,而且它的运动计算先于爆炸计算,所以他进行一段偏移再爆炸,而且刚好是1 Gametick的位移
                5. 就这样产生了EM的效果


                IP属地:广东8楼2017-03-14 00:06
                回复
                  同样的,我们举个例子,还是这张图

                  原因:在激活时因为红石线的激活微时序,导致两个TNT——A和B先后进入实体更新队列,当80 Gametick后,A_TNT先处理运动事件,但是由于它的Motion变量值为0,所以不移动,然后进行ATNT的爆炸事件,于是A_TNT的爆炸效果改变了B_TNT实体的Motion值,导致B_TNT先处理运动事件的时候,向相对的方向偏移,移出水后处理爆炸事件,破坏方块,而且只有一侧破坏


                  IP属地:广东9楼2017-03-14 00:07
                  回复
                    对这种解释的实例验证:
                    上面提到了红石线的微延时,那么也就是意味着我可以控制TNT实体在同一Gametick 的爆炸顺序?
                    没错,的确可以,下面就是用来验证这种解释的实验


                    先激活Red处的红石线(注意命令方块的激活顺序),再激活blue处红石线
                    所以Red处的发射器发射的TNT实体先进入实体更新队列,产生EM后自然让blue处的TNT实体向左偏移,导致左侧破坏


                    此图反之,可以初步验证这个解释的正确性,也说明TNT实体爆炸的微控是可以实现的,虽然我认为爆炸微控没有什么太大的用处XD


                    IP属地:广东10楼2017-03-14 00:08
                    回复
                      关于以前一些EM的误解:
                      没错我打算发这个解释贴之前,我一直倡导的理论是集束堆同时偏移,貌似也误导了很多炮党XD,在此做出深深的道歉
                      下面我将会为以前集束堆同时偏移的例子进行解释:
                      1.在实际实践中,总会发现两个TNT集束堆并不是一个移动一个不动,而是两个集束堆都会向相对方向偏移,这不是跟之前说的矛盾了么?
                      不,并不是的,如果你仔细想一想,你会发现TNT集束堆不同于TNT实体的一点——数量
                      两个TNT集束堆比较多的TNT实体数,而往往两个集束堆的TNT实体进入实体更新队列的顺序更加多样,所以完全可以产生此次A集束堆的1个TNT实体处理完后,又开始处理B集束堆的1个TNT实体,这样作用到两个集束堆上就会互相偏移
                      2.当然想要真正解释清楚还是得拿以前我经常用的一个例子:

                      没错,你一定想说:“这个就是1个TNT实体跟1个TNT集束堆,它不符合你现在的解释”
                      可是如果你尝试着更改拉杆位置,就会发现因为微延时这个也会有炸膛的时候,并不是怎么样都不会炸XD(其实我也是刚刚发现这个,误导了你们真是抱歉了Orz)
                      这也告诉我们理论优先于实验,实验服务于理论


                      IP属地:广东11楼2017-03-14 00:10
                      回复
                        EM的解决方案:
                        1.结构方面:
                        竟然有这个Bug,那还是解决这种问题的,而EM产生影响最大的就是落弹模块
                        首先标准落弹的方法之所以会产生大量的误差,就是因为EM的影响,导致两个方向的推进TNT,虽然同时爆炸,但实际位置向相对方向大幅偏移,导致弹坑很不精准,对此的解决方案就是尽量减小两个方向推进TNT的爆炸接触(最好让他们完全不影响对方)
                        在此利用了我以前发现的一个黑科技——透门原理
                        指TNT实体可以通过打开的栅栏门,而爆炸冲击却无法通过
                        用透门原理隔开两个推进TNT的爆炸冲击,这样就不会产生位移,所以我们可以做出落弹部分,如下图所示

                        这种是平抛和落弹的结合,相对于普通平抛它是单炮口设计,缺点是依旧有死角(原点附近是死角),而且近距离点阵不准(摩擦作用),但是远距离借鉴了标准平抛的优点,异常的精准

                        这种完全悬空(推进TNT在下落中途爆炸,最底下不是封死的,有空隙,为了打击原点)优点是无死角,但是有一点不太精准(在误差允许的范围内,大概是0.5~2格)


                        IP属地:广东12楼2017-03-14 00:15
                        回复
                          2.源码方面:
                          其实做那么多结构来解决这个Bug实在是不值得,而且PE里面还没有EM,所以不妨让我们玩一些比较暴力的方法——改源码XD
                          更改前:

                          更改后:

                          区别就是把爆炸处理提前,并且将fuse的临界值从fuse≤0改为fuse<0
                          这样TNT实体即使顺序更新,但是每次更新都先处理爆炸计算(删除实体),这样就不会位移了
                          但是其实这样改不是很专业,不过的确是很有效的
                          PS:个人虽然很讨厌这个Bug,但是动不动就改源码也是不好的习惯,所以不到结构限制而万不得已,最好不要更改!


                          IP属地:广东13楼2017-03-14 00:23
                          收起回复
                            好了,到这里大概也就没有什么了,还有在此其实我要感谢一个人,那就是@让他该人士说
                            他曾经提出过这个解释的雏形,但是我当时没有怎么在意XD,不过真的给了我一些启发


                            IP属地:广东14楼2017-03-14 00:23
                            回复
                              然后@一波小伙伴


                              IP属地:广东15楼2017-03-14 00:24
                              收起回复