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))); } }