我的世界幽灵方块详解
我的世界幽灵方块详解。那下面给大家分享的是关于幽灵方块的一些信息哦~有感兴趣的玩家不妨进来看看哦~当然对幽灵方块不太了解的玩家不妨进来看看哦~希望大家喜欢。
游戏园我的世界官方群: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的分享。
以上就是我的世界幽灵方块详解。更多精彩尽在游戏园我的世界专区。
相关攻略推荐:
相关文章
更多+热门搜索
手游排行榜
- 最新排行
- 最热排行
- 评分最高
-
角色扮演 大小:250.92MB
-
冒险解谜 大小:28.5M
-
其他游戏 大小:134.25MB
-
系统工具 大小:1.77MB
-
角色扮演 大小:500MB
-
其他游戏 大小:59.06MB
-
其他游戏 大小:262 MB
-
其他游戏 大小:225.08MB
-
休闲益智 大小:25.63MB
-
应用软件 大小:29.23 MB