diff --git a/src/block/tile/FlowerPot.php b/src/block/tile/FlowerPot.php index e778d0774..9ef15053a 100644 --- a/src/block/tile/FlowerPot.php +++ b/src/block/tile/FlowerPot.php @@ -27,7 +27,9 @@ use pocketmine\block\Air; use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\data\bedrock\block\BlockStateDeserializeException; +use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\SavedDataLoadingException; +use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ShortTag; @@ -92,4 +94,8 @@ class FlowerPot extends Spawnable{ $nbt->setTag(self::TAG_PLANT_BLOCK, RuntimeBlockMapping::getInstance()->toStateData($this->plant->getStateId())->toNbt()); } } + + public function getRenderUpdateBugWorkaroundStateProperties(Block $block) : array{ + return [BlockStateNames::UPDATE_BIT => new ByteTag(1)]; + } } diff --git a/src/block/tile/Spawnable.php b/src/block/tile/Spawnable.php index 73ad2142a..67bc72fd9 100644 --- a/src/block/tile/Spawnable.php +++ b/src/block/tile/Spawnable.php @@ -23,7 +23,11 @@ declare(strict_types=1); namespace pocketmine\block\tile; +use pocketmine\block\Block; +use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\IntTag; +use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\protocol\types\CacheableNbt; use function get_class; @@ -49,6 +53,22 @@ abstract class Spawnable extends Tile{ $this->spawnCompoundCache = null; } + /** + * The Bedrock client won't re-render a block if the block's state properties didn't change. This is a problem when + * the tile may affect the block's appearance. For example, a cauldron's liquid changes colour based on the dye + * inside. + * + * This is worked around in vanilla by modifying one of the block's state properties to a different value, and then + * changing it back again. Since we don't want to litter core implementation with hacks like this, we brush it under + * the rug into Tile. + * + * @return ByteTag[]|IntTag[]|StringTag[] + * @phpstan-return array + */ + public function getRenderUpdateBugWorkaroundStateProperties(Block $block) : array{ + return []; + } + /** * Returns encoded NBT (varint, little-endian) used to spawn this tile to clients. Uses cache where possible, * populates cache if it is null. diff --git a/src/data/bedrock/block/convert/BlockObjectToBlockStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToBlockStateSerializer.php index 553f84ff0..26958f598 100644 --- a/src/data/bedrock/block/convert/BlockObjectToBlockStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToBlockStateSerializer.php @@ -907,7 +907,7 @@ final class BlockObjectToBlockStateSerializer implements BlockStateSerializer{ }); $this->map(Blocks::FLOWER_POT(), function() : Writer{ return Writer::create(Ids::FLOWER_POT) - ->writeBool(StateNames::UPDATE_BIT, true); //to keep MCPE happy + ->writeBool(StateNames::UPDATE_BIT, false); //to keep MCPE happy }); $this->map(Blocks::FROSTED_ICE(), function(FrostedIce $block) : Writer{ return Writer::create(Ids::FROSTED_ICE) diff --git a/src/world/World.php b/src/world/World.php index 7d5366b0b..870478992 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -36,6 +36,7 @@ use pocketmine\block\tile\TileFactory; use pocketmine\block\UnknownBlock; use pocketmine\block\VanillaBlocks; use pocketmine\data\bedrock\BiomeIds; +use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\Entity; use pocketmine\entity\EntityFactory; @@ -948,6 +949,22 @@ class World implements ChunkManager{ $fullBlock = $this->getBlockAt($b->x, $b->y, $b->z); $blockPosition = BlockPosition::fromVector3($b); + + $tile = $this->getTileAt($b->x, $b->y, $b->z); + if($tile instanceof Spawnable && ($fakeStateProperties = $tile->getRenderUpdateBugWorkaroundStateProperties($fullBlock)) !== null){ + $originalStateData = $blockMapping->toStateData($fullBlock->getStateId()); + $fakeStateData = new BlockStateData( + $originalStateData->getName(), + array_merge($originalStateData->getStates(), $fakeStateProperties), + $originalStateData->getVersion() + ); + $packets[] = UpdateBlockPacket::create( + $blockPosition, + $blockMapping->getBlockStateDictionary()->lookupStateIdFromData($fakeStateData) ?? throw new AssumptionFailedError("Unmapped fake blockstate data: " . $fakeStateData->toNbt()), + UpdateBlockPacket::FLAG_NETWORK, + UpdateBlockPacket::DATA_LAYER_NORMAL + ); + } $packets[] = UpdateBlockPacket::create( $blockPosition, $blockMapping->toRuntimeId($fullBlock->getStateId()), @@ -955,7 +972,6 @@ class World implements ChunkManager{ UpdateBlockPacket::DATA_LAYER_NORMAL ); - $tile = $this->getTileAt($b->x, $b->y, $b->z); if($tile instanceof Spawnable){ $packets[] = BlockActorDataPacket::create($blockPosition, $tile->getSerializedSpawnCompound()); }