From 2b61c025c26394c3293bcc12a2d0b2301cb4c7ee Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 14 Jul 2022 21:54:01 +0100 Subject: [PATCH] Workaround items in blockentity NBT not being processed correctly in 1.19.10 closes #5154 this hack sends only the bare essential data to create the tiles in LevelChunkPacket, and then separately sending the full tile data using BlockActorDataPacket afterwards. This is necessary because the client doesn't handle items correctly in NBT when chunks are sent without using the SubChunkRequest system. In 4.6 this is observed with incorrect items shown in item frames; in 5.0 it's seen with items simply not showing up at all (difference due to modernization of the serialization format in 5.0). --- src/network/mcpe/NetworkSession.php | 18 ++++++++++++++++++ .../mcpe/serializer/ChunkSerializer.php | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index b75d38147..9fde7ed23 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; +use pocketmine\block\tile\Spawnable; use pocketmine\data\bedrock\EffectIdMap; use pocketmine\entity\Attribute; use pocketmine\entity\effect\EffectInstance; @@ -58,6 +59,7 @@ use pocketmine\network\mcpe\handler\PreSpawnPacketHandler; use pocketmine\network\mcpe\handler\ResourcePacksPacketHandler; use pocketmine\network\mcpe\handler\SpawnResponsePacketHandler; use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; +use pocketmine\network\mcpe\protocol\BlockActorDataPacket; use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; @@ -959,6 +961,22 @@ class NetworkSession{ try{ $this->queueCompressed($promise); $onCompletion(); + + //TODO: HACK! we send the full tile data here, due to a bug in 1.19.10 which causes items in tiles + //(item frames, lecterns) to not load properly when they are sent in a chunk via the classic chunk + //sending mechanism. We workaround this bug by sending only bare essential data in LevelChunkPacket + //(enough to create the tiles, since BlockActorDataPacket can't create tiles by itself) and then + //send the actual tile properties here. + //TODO: maybe we can stuff these packets inside the cached batch alongside LevelChunkPacket? + $chunk = $currentWorld->getChunk($chunkX, $chunkZ); + if($chunk !== null){ + foreach($chunk->getTiles() as $tile){ + if(!($tile instanceof Spawnable)){ + continue; + } + $this->sendDataPacket(BlockActorDataPacket::create(BlockPosition::fromVector3($tile->getPosition()), $tile->getSerializedSpawnCompound())); + } + } }finally{ $world->timings->syncChunkSend->stopTiming(); } diff --git a/src/network/mcpe/serializer/ChunkSerializer.php b/src/network/mcpe/serializer/ChunkSerializer.php index 473ad615d..493dfc6ab 100644 --- a/src/network/mcpe/serializer/ChunkSerializer.php +++ b/src/network/mcpe/serializer/ChunkSerializer.php @@ -24,8 +24,11 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\serializer; use pocketmine\block\tile\Spawnable; +use pocketmine\block\tile\Tile; +use pocketmine\block\tile\TileFactory; use pocketmine\data\bedrock\BiomeIds; use pocketmine\data\bedrock\LegacyBiomeIdToStringIdMap; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\TreeRoot; use pocketmine\network\mcpe\convert\RuntimeBlockMapping; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; @@ -38,6 +41,7 @@ use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\SubChunk; use function chr; use function count; +use function get_class; use function str_repeat; final class ChunkSerializer{ @@ -125,9 +129,19 @@ final class ChunkSerializer{ public static function serializeTiles(Chunk $chunk) : string{ $stream = new BinaryStream(); + $nbtSerializer = new NetworkNbtSerializer(); foreach($chunk->getTiles() as $tile){ if($tile instanceof Spawnable){ - $stream->put($tile->getSerializedSpawnCompound()->getEncodedNbt()); + //TODO: HACK! we send only the bare essentials to create a tile in the chunk itself, due to a bug in + //1.19.10 which causes items in tiles (item frames, lecterns) to not load properly when they are sent in + //a chunk via the classic chunk sending mechanism. We workaround this bug by sendingBlockActorDataPacket + //in NetworkSession to set the actual tile properties after sending the LevelChunkPacket. + $nbt = CompoundTag::create() + ->setString(Tile::TAG_ID, TileFactory::getInstance()->getSaveId(get_class($tile))) + ->setInt(Tile::TAG_X, $tile->getPosition()->getFloorX()) + ->setInt(Tile::TAG_Y, $tile->getPosition()->getFloorY()) + ->setInt(Tile::TAG_Z, $tile->getPosition()->getFloorZ()); + $stream->put($nbtSerializer->write(new TreeRoot($nbt))); } }