我的世界幽灵方块详解

时间:2017-08-13 来源:88130安卓下载 作者:佚名

  我的世界幽灵方块详解。那下面给大家分享的是关于幽灵方块的一些信息哦~有感兴趣的玩家不妨进来看看哦~当然对幽灵方块不太了解的玩家不妨进来看看哦~希望大家喜欢。

  游戏园我的世界官方群:325049520(已满) 二群:256070479 欢迎各路喜爱我的世界的小伙伴们加入讨论!

  玩服务器的小伙伴们可以加入:141931866 群一起联机玩游戏哦!

  如果你是腐竹的话可以给我们投稿你的服务器哦~投稿地址点我进入

  如果你有心仪的作品或者心得分享的话,欢迎来游戏园投稿,大家可以点击>>>投稿<<<进行投稿哦~ 有奖品哦~

  接下来的问题是幽灵方块的生成。

  毕竟是研究生成原因,为了防止太多的东西干扰视线,我把它化简了一下

  可以看出,它所做的事情不过是给了向上的活塞一个负脉冲。同时,粘性活塞把它推走。

  一句输出信息...

  BlockPos{x=0, y=4, z=0} 是红色活塞

  BlockPos{x=1, y=4, z=0}是橙色史莱姆

  BlockPos{x=1, y=4, z=1}是黄色活塞

  玻璃是幽灵方块

  receive blockevent push at piston BlockPos{x=0, y=4, z=0} in server world worldTime: 78435

  do pushing! pushlist: [BlockPos{x=1, y=4, z=0}]

  receive blockevent push at piston BlockPos{x=0, y=4, z=0} in server world worldTime: 78435

  receive blockevent push at piston BlockPos{x=0, y=4, z=0} in client world worldTime: 78433

  do pushing! pushlist: [BlockPos{x=1, y=4, z=0}, BlockPos{x=1, y=4, z=1}]

  我的世界幽灵方块详解。那下面给大家分享的是关于幽灵方块的一些信息哦~有感兴趣的玩家不妨进来看看哦~当然对幽灵方块不太了解的玩家不妨进来看看哦~希望大家喜欢。

  游戏园我的世界官方群:325049520(已满) 二群:256070479 欢迎各路喜爱我的世界的小伙伴们加入讨论!

  玩服务器的小伙伴们可以加入:141931866 群一起联机玩游戏哦!

  如果你是腐竹的话可以给我们投稿你的服务器哦~投稿地址点我进入

  如果你有心仪的作品或者心得分享的话,欢迎来游戏园投稿,大家可以点击>>>投稿<<<进行投稿哦~ 有奖品哦~

  接下来的问题是幽灵方块的生成。

  毕竟是研究生成原因,为了防止太多的东西干扰视线,我把它化简了一下

  可以看出,它所做的事情不过是给了向上的活塞一个负脉冲。同时,粘性活塞把它推走。

  一句输出信息...

  BlockPos{x=0, y=4, z=0} 是红色活塞

  BlockPos{x=1, y=4, z=0}是橙色史莱姆

  BlockPos{x=1, y=4, z=1}是黄色活塞

  玻璃是幽灵方块

  receive blockevent push at piston BlockPos{x=0, y=4, z=0} in server world worldTime: 78435

  do pushing! pushlist: [BlockPos{x=1, y=4, z=0}]

  receive blockevent push at piston BlockPos{x=0, y=4, z=0} in server world worldTime: 78435

  receive blockevent push at piston BlockPos{x=0, y=4, z=0} in client world worldTime: 78433

  do pushing! pushlist: [BlockPos{x=1, y=4, z=0}, BlockPos{x=1, y=4, z=1}]

  然后便有了进一步的debug信息:关于对PushList的修改

  第1次receive blockevent:

  pushList add tile.slime

  pushList add tile.pistonBase

  receive event push at piston BlockPos{x=0, y=4, z=0} in server world worldTime: 103006

  pushList add tile.slime

  do pushing! pushlist: [BlockPos{x=1, y=4, z=0}] destroyList: []

  第2次receive blockevent 可以看出,这次什么都没有做。:

  receive event push at piston BlockPos{x=0, y=4, z=0} in server world worldTime: 103006

  第3次receive blockevent(客户端):

  receive event push at piston BlockPos{x=0, y=4, z=0} in client world worldTime: 103004

  pushList add tile.slime

  pushList add tile.pistonBase

  do pushing! pushlist: [BlockPos{x=1, y=4, z=0}, BlockPos{x=1, y=4, z=1}] destroyList: []

  add slime

  add piston

  receive event push in server

  //此处清空了一次pushList

  add slime

  receive event push in client

  add slime

  add pistonBase

  这就是问题所在。

  那么为什么会这样呢?另一个输出:

  receive event push at piston BlockPos{x=0, y=4, z=0} in server world ghost piston extended: true

  receive event push at piston BlockPos{x=0, y=4, z=0} in client world ghost piston extended: false

  就是这样。在收到blockevent后,一个活塞是伸出的,另一个活塞则不是。

  再来看一遍简化的更新顺序,对是否伸出的修改又是怎样呢?

  lever activated

  //server

  set GHOSTPISTON extends= false

  (NTE to BlockEvent)

  set GHOSTPISTON extends= true

  receive event push at piston. ghost piston extended: true

  do pushing! pushlist: SLIME

  //client

  receive event IMMEDIATLY!!!

  receive event push at piston. ghost piston extended: false

  do pushing! pushlist: SLIME, GHOSTPISTO

  注意,里面没有专门针对于client的修改。不过对于服务端和客户端很不熟悉,同步机制不了解

  这说明要么extends=false是被同步到客户端的,但=true没有,要么客户端在=false和=true之间完成了推出

  重新开始。

  对于这个活塞,它做了些什么呢?

  推出

  在服务端add event

  在服务端add event

  在客户端add event

  收回

  在服务端add event

  在客户端add event

  server set piston state pos: BlockPos{x=-3, y=1, z=1} DISEXTENDED

  server set piston state pos: BlockPos{x=-3, y=1, z=1} EXTENDED

  server to move: [BlockPos{x=-3, y=1, z=2}]

  server set piston state pos: BlockPos{x=-2, y=1, z=2} EXTENDED

  client set piston state pos: BlockPos{x=-3, y=1, z=1} DISEXTENDED

  client to move: [BlockPos{x=-3, y=1, z=2}, BlockPos{x=-3, y=1, z=1}]

  client set piston state pos: BlockPos{x=-2, y=1, z=2} EXTENDED

  client set piston state pos: BlockPos{x=-3, y=1, z=1} EXTENDED

  client set piston state pos: BlockPos{x=-4, y=1, z=1} DISEXTENDED

  另外,就是发现Thread类有获取StackTrace的功能,于是在上文的基础上打印了一下来玩玩。由于太长,只打印了方法名。

  server set piston state pos: BlockPos{x=-3, y=1, z=1} DISEXTENDED

  checkForMove; onNeighborBlockChange; notifyBlockOfStateChange; notifyNeighborsOfStateExcept; notifyNeighbors; breakBlock; setBlockState; setBlockState; updateTick; tickUpdates; tick; updateTimeLightAndEntities; tick; tick; run; run;

  server set piston state pos: BlockPos{x=-3, y=1, z=1} EXTENDED

  onBlockEventReceived; fireBlockEvent; sendQueuedBlockEvents; tick; updateTimeLightAndEntities; tick; tick; run; run;

  server to move: [BlockPos{x=-3, y=1, z=2}]

  server set piston state pos: BlockPos{x=-2, y=1, z=2} EXTENDED

  onBlockEventReceived; fireBlockEvent; sendQueuedBlockEvents; tick; updateTimeLightAndEntities; tick; tick; run; run;

  client set piston state pos: BlockPos{x=-3, y=1, z=1} DISEXTENDED

  invalidateRegionAndSetBlock; handleMultiBlockChange; processPacket; processPacket; run; call; run; runGameLoop; run; main; main;

  client to move: [BlockPos{x=-3, y=1, z=2}, BlockPos{x=-3, y=1, z=1}]

  client set piston state pos: BlockPos{x=-2, y=1, z=2} EXTENDED

  onBlockEventReceived; addBlockEvent; handleBlockAction; processPacket; processPacket; run; call; run; runGameLoop; run; main; main;

  client set piston state pos: BlockPos{x=-3, y=1, z=1} EXTENDED

  invalidateRegionAndSetBlock; handleMultiBlockChange; processPacket; processPacket; run; call; run; runGameLoop; run; main; main;

  client set piston state pos: BlockPos{x=-4, y=1, z=1} DISEXTENDED

  update; updateEntities; runTick; runGameLoop; run; main; main;

  可以看出,服务端和客户端都进行了活塞状态的变化。

  服务端推出的时候幽灵在推出,客户端推出时它在收回。

  值得注意的是,客户端都是通过服务端发送的packet来运作的。只不过第二个包和第三个包交换了顺序。因为处在不同的线程,所以无法获取发送包的过程

  对于packet的构造方法的追根溯源,我们发现,传给客户端的block action是和服务端的BE queue同时开始处理的。对于方块更新,则是在setblock时先缓冲在一个集合里,在PI更新里打包发送给客户端(player instance,玩家实例 老实说,我觉得mcp反混淆得不好,翻译成区块更新更准确些。)不过我们不需要关注它都做了些什么,只知道它只存在于服务端世界,在时间轴上比BE略微靠前就够了。

  于是时间轴成了这样:

  a活塞收回(NTE)

  加入BU列表:a收回

  发包给客户端:a收回(PI)

  a活塞推出(BE)

  加入BU列表:a推出

  b活塞推(BE),没有推出a

  发包给客户端:b推出(BE)

  发包给客户端:a推出(第二个PI)

  于是便解释了。唯一的问题在于上图的那个装置为什么产生不了?

  正躺在床上,突然想通了。活塞在收到信号时会判断能不能推。既然不能推,那么就不会收到事件了。而使用屎来姆时,游戏是认为可以推出一个方块(屎来姆)的。然后才能收到BE更新。

  仔细观察下一楼的时间轴,我们发现,a活塞的下降沿好像没什么用!

  实际上它是有作用的。因为我们需要,在BE中,a活塞推出先于b活塞推出。而它们的先后顺序是随位置与方向不定的。a活塞的下降沿就给了它一个先入为主(因为中继器优先于比较器)的机会,保证a活塞一定先进入BE。

  同时,进入BE时,除了a活塞的状态外,一切东西都完好如初。游戏的算法认为,这时再去弄一回BlockEvent就有点太鸡肋,于是直接把a活塞设置为推出状态了事。于是a活塞在下一tick的PI才收到包,而不是这一tick的BE。就是这个时间差导致了幽灵方块的产生。

  于是时间轴成了这样(BU是方块更新列表):

  //有些无关紧要的东西,比如b加入BU,就省略了

  //服务端

  NTE

  a活塞下降沿

  a活塞设置为收回状态

  a加入BU

  a加入BE

  b活塞上升沿

  b加入BE

  PI

  【发包】->(客户端)a设置为收回状态

  BE

  a活塞设置为推出状态

  a加入BU //因为上楼所说的原因,没有让客户端收到BlockEvent

  b活塞执行推出

  【发包】->(客户端)b进入BlockEvent

  第二个PI

  【发包】->(客户端)a设置为推出状态

  //客户端

  a活塞设置为收回状态

  b收到BlockEvent

  b活塞执行推出

  a设置为推出状态(可惜a活塞已经被推走了)

  于是幽灵方块便产生了。

  接下来的工作是把幽灵方块的特性验证并解释

  【活塞的推拉对幽灵方块的影响】

  对这个现象做出了一个解释,这个解释和我的解释也是基本相似的。

  不过对于活塞的推拉,我们可以问一个哲学的问题:什么是推?什么是拉?

  看上去很简单。推就是活塞在上边沿所做的运动,拉就是活塞在下边沿所做的运动。

  不过有了史莱姆方块后,我们发现了一些共通的地方。

  左边活塞的伸出对于铁块来说不更像是拉吗?右面活塞的收回对于铁块来说不更像是推吗?

  对于这个问题,我们有更好的定义:对于活塞推拉运动中的一个方块,若其本身将被一个非空气方块替换,那么这就是推;反之就是拉。只有这样,我们才能更好地描述不可推动方块或者推动掉落方块的行为。

  不过这和我们的主题无关,这只是用来类比的东西。

  “影响到幽灵方块的推和拉对关系” 并不完全代表实际上的推和拉。它只是推拉的一般情况下表现的某些性质而已。

  一般地,当服务端发生了方块替换并且把这个方块替换发送到客户端时,此处的幽灵方块将会消失。最常见的水开门方法也就是应用了这个原理。是否发送到客户端则是已知的。

  先让我们看看活塞推出与收回的一般运动:

  对于活塞的推出(以下只涉及在收到be之后的动作,并且摧毁方块的算法被忽略了)

  把自己设为推出状态

  把所有将要推出的方块设为空气,并在它的前方放置一个推出中的活塞臂方块(带有方块实体)。

  把自己的前方变成推出中的活塞臂方块(将要变成活塞臂)

  对于活塞的收回

  把自己设为退出中的活塞臂方块(没错,就是自己)

  把自己前方设为空气(原来的活塞臂)

  把所有将要推动的方块设为空气,并在前方放置活塞臂(和推出一样)

  然后咱们来捋一捋活塞收回的过程:

  服务端的活塞收回,活塞臂被替换为空气

  发送方块替换数据包

  发送方块事件数据包

  客户端收到方块事件数据包,执行收回,把原来的活塞臂替换成移动中的幽灵方块

  客户端收到方块替换数据包,把移动中的幽灵方块替换为空气。

  而在推出时便没有这个问题。服务端的活塞根本没有影响到幽灵方块将被推到的位置,自然不会发送方块替换数据包给客户端,也就不会纠正幽灵方块了。

  至此,我们可以推广出影响幽灵方块的一般推拉规律:

  在BE中的推拉过程中服务端改变了幽灵方块即将到达的位置的方块,则幽灵方块被删除

  因此,对于这个情况(史莱姆块都是幽灵方块),将会发生什么呢?

  首先,这些史莱姆将会收回。然后因为活塞臂被替换成空气,活塞所对的那个史莱姆块消失了。

  这和事实是相符的。

  有些事情真的不需要想那么复杂

  【幽灵方块的批量清除】

  这个现象由@杨月华132 最先发现。

  这个问题实在是很简单...我躺在床上一边和舍友抱怨为什么红色雾霾预警都不停课,一边玩手机,然后就弄明白了。

  如图。被文字遮挡住的是3个在(0,0)区块内的幽灵活塞。接下来我输入一些指令:/fill 0 5 0 7 5 7 stone

  所造成的效果是在这个区块放置了64个石头。

  然后幽灵方块就消失了。

  在每一tick的PI,给客户端同步方块信息时,为了节约资源,有3种同步方式:同步单个方块,同步多个方块,同步整个区块。

  当需要同步的方块超过或等于64个时,就会把整个区块内的方块信息同步给客户端,于是客户端内的幽灵方块便消失了。

  所以批量消除的根本,不是因为推出了多少幽灵方块,而是因为在服务端产生了许多方块更新,如活塞的推出,红石线的激活等等

  然后就是幽灵史莱姆方块所产生的电梯了。在每次循环中,客户端会给服务端传递玩家的坐标,而不传递玩家的运动速度。所以,站在幽灵方块上,玩家会不停被设置在幽灵方块上,但是因为服务端不认为有这个方块,玩家的掉落速度就不断增加。因为玩家的坐标不停被重设,也就不会因为掉到别的方块上而停止了。

  mc中实体的掉落速度是成加速度的。对于加速度什么公式我并不清楚,反正就是从0不断加速罢了...经过测试,最快速度差不多稳定在-3.9。也就是每个gt会下落3.9m。在下图中,当速度达到足够快时,玩家就会趁着数据同步的间隙触发压力板,使红石灯亮起。

  当玩家受到伤害时,服务端会把玩家的速度同步给客户端。这时候,史莱姆把玩家反弹了上去。有意思的是,即使是在空中,玩家在服务器里的掉落速度还是-3.9左右呢...

  可以看出,在制造幽灵电梯时,上升的高度取决于服务端中下落的速度。所以上升的高度是有上限的。并且在受到伤害时掉落速度是-3.9。要想达到这个值,就得提供合适的延时。比如说,用下图的压力板来检测。当红石灯亮起时,速度已经达到了-3.75。再增加一点延时的话,就可以得到最高的速度了!

  PS:在此非常感谢我的世界玩家xiaohane2的分享。

  以上就是我的世界幽灵方块详解。更多精彩尽在游戏园我的世界专区。

  相关攻略推荐:

  我的世界1.9幽灵方块生成器详解

  我的世界1.9.1幽灵方块电梯制作教程

  我的世界1.9幽灵方块电梯制作教程

  我的世界1.9幽灵方块黑科技

热门搜索

手游排行榜

  • 最新排行
  • 最热排行
  • 评分最高