From e06ab0869a9f8c26f71c529b21f7659cc3f7c2fb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 28 Jul 2019 19:40:47 +0100 Subject: [PATCH] mostly rewrite internal entity metadata handling - Only sync the metadata set when needed for sending - Don't use metadata set for storing data, add properties instead - Use objects inside metadata sets instead of arrays --- src/pocketmine/entity/Animal.php | 9 +- src/pocketmine/entity/Entity.php | 212 ++++++++--------- src/pocketmine/entity/Human.php | 31 +-- src/pocketmine/entity/Living.php | 35 ++- src/pocketmine/entity/Villager.php | 18 +- src/pocketmine/entity/WaterAnimal.php | 9 +- .../entity/effect/EffectManager.php | 30 ++- .../entity/object/ExperienceOrb.php | 15 +- src/pocketmine/entity/object/FallingBlock.php | 8 +- src/pocketmine/entity/object/ItemEntity.php | 2 +- src/pocketmine/entity/object/PrimedTNT.php | 21 +- src/pocketmine/entity/projectile/Arrow.php | 13 +- .../entity/projectile/SplashPotion.php | 22 +- .../network/mcpe/protocol/AddActorPacket.php | 3 +- .../mcpe/protocol/AddItemActorPacket.php | 3 +- .../network/mcpe/protocol/AddPlayerPacket.php | 3 +- .../mcpe/protocol/SetActorDataPacket.php | 4 +- .../types/entity/BlockPosMetadataProperty.php | 65 +++++ .../types/entity/ByteMetadataProperty.php | 50 ++++ .../types/entity/EntityMetadataCollection.php | 222 +++++------------- .../types/entity/FloatMetadataProperty.php | 62 +++++ .../types/entity/IntMetadataProperty.php | 50 ++++ .../entity/IntegerishMetadataProperty.php | 64 +++++ .../entity/ItemStackMetadataProperty.php | 62 +++++ .../types/entity/LongMetadataProperty.php | 52 ++++ .../types/entity/MetadataProperty.php | 35 +++ .../types/entity/ShortMetadataProperty.php | 50 ++++ .../types/entity/StringMetadataProperty.php | 54 +++++ .../types/entity/Vec3MetadataProperty.php | 62 +++++ .../mcpe/serializer/NetworkBinaryStream.php | 116 +++------ src/pocketmine/player/Player.php | 19 +- .../world/particle/FloatingTextParticle.php | 7 +- 32 files changed, 958 insertions(+), 450 deletions(-) create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/BlockPosMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/ByteMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/FloatMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/IntMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/IntegerishMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/ItemStackMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/LongMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/MetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/ShortMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/StringMetadataProperty.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/entity/Vec3MetadataProperty.php diff --git a/src/pocketmine/entity/Animal.php b/src/pocketmine/entity/Animal.php index 28ee08417..09fd1c685 100644 --- a/src/pocketmine/entity/Animal.php +++ b/src/pocketmine/entity/Animal.php @@ -27,8 +27,15 @@ namespace pocketmine\entity; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; abstract class Animal extends Living implements Ageable{ + /** @var bool */ + protected $baby = false; public function isBaby() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::BABY); + return $this->baby; + } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); } } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 8bd71020a..0cbeb014a 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -54,7 +54,7 @@ use pocketmine\network\mcpe\protocol\SetActorMotionPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; -use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; +use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; use pocketmine\player\Player; use pocketmine\Server; use pocketmine\timings\Timings; @@ -195,6 +195,35 @@ abstract class Entity extends Location{ /** @var TimingsHandler */ protected $timings; + /** @var string */ + protected $nameTag = ""; + /** @var bool */ + protected $nameTagVisible = true; + /** @var bool */ + protected $alwaysShowNameTag = false; + /** @var string */ + protected $scoreTag = ""; + /** @var float */ + protected $scale = 1.0; + + /** @var bool */ + protected $canClimb = false; + /** @var bool */ + protected $canClimbWalls = false; + /** @var bool */ + protected $immobile = false; + /** @var bool */ + protected $invisible = false; + /** @var bool */ + protected $sneaking = false; + /** @var bool */ + protected $sprinting = false; + + /** @var int|null */ + protected $ownerId = null; + /** @var int|null */ + protected $targetId = null; + public function __construct(World $world, CompoundTag $nbt){ $this->timings = Timings::getEntityTimings($this); @@ -234,22 +263,10 @@ abstract class Entity extends Location{ $this->propertyManager = new EntityMetadataCollection(); - $this->propertyManager->setLong(EntityMetadataProperties::FLAGS, 0); - $this->propertyManager->setShort(EntityMetadataProperties::MAX_AIR, 400); - $this->propertyManager->setString(EntityMetadataProperties::NAMETAG, ""); - $this->propertyManager->setLong(EntityMetadataProperties::LEAD_HOLDER_EID, -1); - $this->propertyManager->setFloat(EntityMetadataProperties::SCALE, 1); - $this->propertyManager->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->width); - $this->propertyManager->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->height); - $this->attributeMap = new AttributeMap(); $this->addAttributes(); - $this->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true); - $this->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, true); - $this->initEntity($nbt); - $this->propertyManager->clearDirtyProperties(); //Prevents resending properties that were set during construction $this->chunk->addEntity($this); $this->world->addEntity($this); @@ -265,64 +282,63 @@ abstract class Entity extends Location{ * @return string */ public function getNameTag() : string{ - return $this->propertyManager->getString(EntityMetadataProperties::NAMETAG); + return $this->nameTag; } /** * @return bool */ public function isNameTagVisible() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG); + return $this->nameTagVisible; } /** * @return bool */ public function isNameTagAlwaysVisible() : bool{ - return $this->propertyManager->getByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG) === 1; + return $this->alwaysShowNameTag; } - /** * @param string $name */ public function setNameTag(string $name) : void{ - $this->propertyManager->setString(EntityMetadataProperties::NAMETAG, $name); + $this->nameTag = $name; } /** * @param bool $value */ public function setNameTagVisible(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $value); + $this->nameTagVisible = $value; } /** * @param bool $value */ public function setNameTagAlwaysVisible(bool $value = true) : void{ - $this->propertyManager->setByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG, $value ? 1 : 0); + $this->alwaysShowNameTag = $value; } /** * @return string|null */ public function getScoreTag() : ?string{ - return $this->propertyManager->getString(EntityMetadataProperties::SCORE_TAG); + return $this->scoreTag; //TODO: maybe this shouldn't be nullable? } /** * @param string $score */ public function setScoreTag(string $score) : void{ - $this->propertyManager->setString(EntityMetadataProperties::SCORE_TAG, $score); + $this->scoreTag = $score; } /** * @return float */ public function getScale() : float{ - return $this->propertyManager->getFloat(EntityMetadataProperties::SCALE); + return $this->scale; } /** @@ -338,9 +354,9 @@ abstract class Entity extends Location{ $this->height *= $multiplier; $this->eyeHeight *= $multiplier; - $this->recalculateBoundingBox(); + $this->scale = $value; - $this->propertyManager->setFloat(EntityMetadataProperties::SCALE, $value); + $this->recalculateBoundingBox(); } public function getBoundingBox() : AxisAlignedBB{ @@ -361,39 +377,39 @@ abstract class Entity extends Location{ } public function isSneaking() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::SNEAKING); + return $this->sneaking; } public function setSneaking(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::SNEAKING, $value); + $this->sneaking = $value; } public function isSprinting() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::SPRINTING); + return $this->sprinting; } public function setSprinting(bool $value = true) : void{ if($value !== $this->isSprinting()){ - $this->setGenericFlag(EntityMetadataFlags::SPRINTING, $value); + $this->sprinting = $value; $attr = $this->attributeMap->getAttribute(Attribute::MOVEMENT_SPEED); $attr->setValue($value ? ($attr->getValue() * 1.3) : ($attr->getValue() / 1.3), false, true); } } public function isImmobile() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::IMMOBILE); + return $this->immobile; } public function setImmobile(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::IMMOBILE, $value); + $this->immobile = $value; } public function isInvisible() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::INVISIBLE); + return $this->invisible; } public function setInvisible(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::INVISIBLE, $value); + $this->invisible = $value; } /** @@ -401,7 +417,7 @@ abstract class Entity extends Location{ * @return bool */ public function canClimb() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::CAN_CLIMB); + return $this->canClimb; } /** @@ -410,7 +426,7 @@ abstract class Entity extends Location{ * @param bool $value */ public function setCanClimb(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $value); + $this->canClimb = $value; } /** @@ -419,7 +435,7 @@ abstract class Entity extends Location{ * @return bool */ public function canClimbWalls() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::WALLCLIMBING); + return $this->canClimbWalls; } /** @@ -428,7 +444,7 @@ abstract class Entity extends Location{ * @param bool $value */ public function setCanClimbWalls(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::WALLCLIMBING, $value); + $this->canClimbWalls = $value; } /** @@ -436,7 +452,7 @@ abstract class Entity extends Location{ * @return int|null */ public function getOwningEntityId() : ?int{ - return $this->propertyManager->getLong(EntityMetadataProperties::OWNER_EID); + return $this->ownerId; } /** @@ -444,12 +460,7 @@ abstract class Entity extends Location{ * @return Entity|null */ public function getOwningEntity() : ?Entity{ - $eid = $this->getOwningEntityId(); - if($eid !== null){ - return $this->server->getWorldManager()->findEntity($eid); - } - - return null; + return $this->ownerId !== null ? $this->server->getWorldManager()->findEntity($this->ownerId) : null; } /** @@ -461,11 +472,11 @@ abstract class Entity extends Location{ */ public function setOwningEntity(?Entity $owner) : void{ if($owner === null){ - $this->propertyManager->removeProperty(EntityMetadataProperties::OWNER_EID); + $this->ownerId = null; }elseif($owner->closed){ throw new \InvalidArgumentException("Supplied owning entity is garbage and cannot be used"); }else{ - $this->propertyManager->setLong(EntityMetadataProperties::OWNER_EID, $owner->getId()); + $this->ownerId = $owner->getId(); } } @@ -474,7 +485,7 @@ abstract class Entity extends Location{ * @return int|null */ public function getTargetEntityId() : ?int{ - return $this->propertyManager->getLong(EntityMetadataProperties::TARGET_EID); + return $this->targetId; } /** @@ -484,12 +495,7 @@ abstract class Entity extends Location{ * @return Entity|null */ public function getTargetEntity() : ?Entity{ - $eid = $this->getTargetEntityId(); - if($eid !== null){ - return $this->server->getWorldManager()->findEntity($eid); - } - - return null; + return $this->targetId !== null ? $this->server->getWorldManager()->findEntity($eid) : null; } /** @@ -501,11 +507,11 @@ abstract class Entity extends Location{ */ public function setTargetEntity(?Entity $target) : void{ if($target === null){ - $this->propertyManager->removeProperty(EntityMetadataProperties::TARGET_EID); + $this->targetId = null; }elseif($target->closed){ throw new \InvalidArgumentException("Supplied target entity is garbage and cannot be used"); }else{ - $this->propertyManager->setLong(EntityMetadataProperties::TARGET_EID, $target->getId()); + $this->targetId = $target->getId(); } } @@ -557,7 +563,6 @@ abstract class Entity extends Location{ $nbt->setFloat("FallDistance", $this->fallDistance); $nbt->setShort("Fire", $this->fireTicks); - $nbt->setShort("Air", $this->propertyManager->getShort(EntityMetadataProperties::AIR)); $nbt->setByte("OnGround", $this->onGround ? 1 : 0); $nbt->setByte("Invulnerable", $this->invulnerable ? 1 : 0); @@ -566,11 +571,7 @@ abstract class Entity extends Location{ protected function initEntity(CompoundTag $nbt) : void{ $this->fireTicks = $nbt->getShort("Fire", 0); - if($this->isOnFire()){ - $this->setGenericFlag(EntityMetadataFlags::ONFIRE); - } - $this->propertyManager->setShort(EntityMetadataProperties::AIR, $nbt->getShort("Air", 300)); $this->onGround = $nbt->getByte("OnGround", 0) !== 0; $this->invulnerable = $nbt->getByte("Invulnerable", 0) !== 0; @@ -717,7 +718,7 @@ abstract class Entity extends Location{ $this->justCreated = false; - $changedProperties = $this->propertyManager->getDirty(); + $changedProperties = $this->getSyncedNetworkData(true); if(!empty($changedProperties)){ $this->sendData($this->hasSpawned, $changedProperties); $this->propertyManager->clearDirtyProperties(); @@ -758,8 +759,6 @@ abstract class Entity extends Location{ if($ticks > $this->getFireTicks()){ $this->setFireTicks($ticks); } - - $this->setGenericFlag(EntityMetadataFlags::ONFIRE, $this->isOnFire()); } /** @@ -782,7 +781,6 @@ abstract class Entity extends Location{ public function extinguish() : void{ $this->fireTicks = 0; - $this->setGenericFlag(EntityMetadataFlags::ONFIRE, false); } public function isFireProof() : bool{ @@ -1601,7 +1599,7 @@ abstract class Entity extends Location{ $pk->headYaw = $this->yaw; //TODO $pk->pitch = $this->pitch; $pk->attributes = $this->attributeMap->getAll(); - $pk->metadata = $this->propertyManager->getAll(); + $pk->metadata = $this->getSyncedNetworkData(false); $player->sendDataPacket($pk); } @@ -1716,60 +1714,15 @@ abstract class Entity extends Location{ } /** - * @param int $propertyId - * @param int $flagId - * @param bool $value - * @param int $propertyType - */ - public function setDataFlag(int $propertyId, int $flagId, bool $value = true, int $propertyType = EntityMetadataTypes::LONG) : void{ - if($this->getDataFlag($propertyId, $flagId) !== $value){ - $flags = (int) $this->propertyManager->getPropertyValue($propertyId, $propertyType); - $flags ^= 1 << $flagId; - $this->propertyManager->setPropertyValue($propertyId, $propertyType, $flags); - } - } - - /** - * @param int $propertyId - * @param int $flagId - * - * @return bool - */ - public function getDataFlag(int $propertyId, int $flagId) : bool{ - return (((int) $this->propertyManager->getPropertyValue($propertyId, -1)) & (1 << $flagId)) > 0; - } - - /** - * Wrapper around {@link Entity#getDataFlag} for generic data flag reading. - * - * @param int $flagId - * - * @return bool - */ - public function getGenericFlag(int $flagId) : bool{ - return $this->getDataFlag($flagId >= 64 ? EntityMetadataProperties::FLAGS2 : EntityMetadataProperties::FLAGS, $flagId % 64); - } - - /** - * Wrapper around {@link Entity#setDataFlag} for generic data flag setting. - * - * @param int $flagId - * @param bool $value - */ - public function setGenericFlag(int $flagId, bool $value = true) : void{ - $this->setDataFlag($flagId >= 64 ? EntityMetadataProperties::FLAGS2 : EntityMetadataProperties::FLAGS, $flagId % 64, $value, EntityMetadataTypes::LONG); - } - - /** - * @param Player[]|Player $player - * @param array $data Properly formatted entity data, defaults to everything + * @param Player[]|Player $player + * @param MetadataProperty[] $data Properly formatted entity data, defaults to everything */ public function sendData($player, ?array $data = null) : void{ if(!is_array($player)){ $player = [$player]; } - $pk = SetActorDataPacket::create($this->getId(), $data ?? $this->propertyManager->getAll()); + $pk = SetActorDataPacket::create($this->getId(), $data ?? $this->getSyncedNetworkData(false)); foreach($player as $p){ if($p === $this){ @@ -1783,6 +1736,39 @@ abstract class Entity extends Location{ } } + /** + * @param bool $dirtyOnly + * + * @return MetadataProperty[] + */ + final protected function getSyncedNetworkData(bool $dirtyOnly) : array{ + $this->syncNetworkData(); + + return $dirtyOnly ? $this->propertyManager->getDirty() : $this->propertyManager->getAll(); + } + + protected function syncNetworkData() : void{ + $this->propertyManager->setByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG, $this->alwaysShowNameTag ? 1 : 0); + $this->propertyManager->setFloat(EntityMetadataProperties::BOUNDING_BOX_HEIGHT, $this->height); + $this->propertyManager->setFloat(EntityMetadataProperties::BOUNDING_BOX_WIDTH, $this->width); + $this->propertyManager->setFloat(EntityMetadataProperties::SCALE, $this->scale); + $this->propertyManager->setLong(EntityMetadataProperties::LEAD_HOLDER_EID, -1); + $this->propertyManager->setLong(EntityMetadataProperties::OWNER_EID, $this->ownerId ?? -1); + $this->propertyManager->setLong(EntityMetadataProperties::TARGET_EID, $this->targetId ?? -1); + $this->propertyManager->setString(EntityMetadataProperties::NAMETAG, $this->nameTag); + $this->propertyManager->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag); + + $this->propertyManager->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $this->nameTagVisible); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, true); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::IMMOBILE, $this->immobile); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::INVISIBLE, $this->invisible); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::ONFIRE, $this->isOnFire()); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::SNEAKING, $this->sneaking); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::WALLCLIMBING, $this->canClimbWalls); + } + public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{ $this->server->broadcastPacket($players ?? $this->getViewers(), ActorEventPacket::create($this->id, $eventId, $eventData ?? 0)); } diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index daacc7264..c17d1d2bc 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -46,8 +46,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; -use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; -use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags; +use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\player\Player; use pocketmine\utils\UUID; @@ -228,9 +227,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->hungerManager = new HungerManager($this); $this->xpManager = new ExperienceManager($this); - $this->setPlayerFlag(PlayerMetadataFlags::SLEEP, false); - $this->propertyManager->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, null); - $this->inventory = new PlayerInventory($this); $this->enderChestInventory = new EnderChestInventory(); $this->initHumanData($nbt); @@ -422,11 +418,11 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->item = $this->getInventory()->getItemInHand(); - $pk->metadata = $this->propertyManager->getAll(); + $pk->metadata = $this->getSyncedNetworkData(false); $player->sendDataPacket($pk); //TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately - $this->sendData($player, [EntityMetadataProperties::NAMETAG => [EntityMetadataTypes::STRING, $this->getNameTag()]]); + $this->sendData($player, [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]); $player->getNetworkSession()->onMobArmorChange($this); @@ -448,25 +444,4 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->xpManager = null; parent::destroyCycles(); } - - /** - * Wrapper around {@link Entity#getDataFlag} for player-specific data flag reading. - * - * @param int $flagId - * - * @return bool - */ - public function getPlayerFlag(int $flagId) : bool{ - return $this->getDataFlag(EntityMetadataProperties::PLAYER_FLAGS, $flagId); - } - - /** - * Wrapper around {@link Entity#setDataFlag} for player-specific data flag setting. - * - * @param int $flagId - * @param bool $value - */ - public function setPlayerFlag(int $flagId, bool $value = true) : void{ - $this->setDataFlag(EntityMetadataProperties::PLAYER_FLAGS, $flagId, $value, EntityMetadataTypes::BYTE); - } } diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index b10231c12..7a922b515 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -66,6 +66,7 @@ use function sqrt; use const M_PI; abstract class Living extends Entity{ + protected const DEFAULT_BREATH_TICKS = 300; protected $gravity = 0.08; protected $drag = 0.02; @@ -85,6 +86,13 @@ abstract class Living extends Entity{ /** @var ArmorInventory */ protected $armorInventory; + /** @var bool */ + protected $breathing = true; + /** @var int */ + protected $breathTicks = self::DEFAULT_BREATH_TICKS; + /** @var int */ + protected $maxBreathTicks = self::DEFAULT_BREATH_TICKS; + abstract public function getName() : string; protected function initEntity(CompoundTag $nbt) : void{ @@ -114,6 +122,8 @@ abstract class Living extends Entity{ $this->setHealth($health); + $this->setAirSupplyTicks($nbt->getShort("Air", self::DEFAULT_BREATH_TICKS)); + /** @var CompoundTag[]|ListTag $activeEffectsTag */ $activeEffectsTag = $nbt->getListTag("ActiveEffects"); if($activeEffectsTag !== null){ @@ -172,6 +182,8 @@ abstract class Living extends Entity{ $nbt = parent::saveNBT(); $nbt->setFloat("Health", $this->getHealth()); + $nbt->setShort("Air", $this->getAirSupplyTicks()); + if(!empty($this->effectManager->all())){ $effects = []; foreach($this->effectManager->all() as $effect){ @@ -598,7 +610,7 @@ abstract class Living extends Entity{ * @return bool */ public function isBreathing() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::BREATHING); + return $this->breathing; } /** @@ -608,7 +620,7 @@ abstract class Living extends Entity{ * @param bool $value */ public function setBreathing(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::BREATHING, $value); + $this->breathing = $value; } /** @@ -618,7 +630,7 @@ abstract class Living extends Entity{ * @return int */ public function getAirSupplyTicks() : int{ - return $this->propertyManager->getShort(EntityMetadataProperties::AIR); + return $this->breathTicks; } /** @@ -627,7 +639,7 @@ abstract class Living extends Entity{ * @param int $ticks */ public function setAirSupplyTicks(int $ticks) : void{ - $this->propertyManager->setShort(EntityMetadataProperties::AIR, $ticks); + $this->breathTicks = $ticks; } /** @@ -635,7 +647,7 @@ abstract class Living extends Entity{ * @return int */ public function getMaxAirSupplyTicks() : int{ - return $this->propertyManager->getShort(EntityMetadataProperties::MAX_AIR); + return $this->maxBreathTicks; } /** @@ -644,7 +656,7 @@ abstract class Living extends Entity{ * @param int $ticks */ public function setMaxAirSupplyTicks(int $ticks) : void{ - $this->propertyManager->setShort(EntityMetadataProperties::MAX_AIR, $ticks); + $this->maxBreathTicks = $ticks; } /** @@ -755,6 +767,17 @@ abstract class Living extends Entity{ $player->getNetworkSession()->onMobArmorChange($this); } + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectManager->hasOnlyAmbientEffects() ? 1 : 0); + $this->propertyManager->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->effectManager->getBubbleColor()->toARGB())); + $this->propertyManager->setShort(EntityMetadataProperties::AIR, $this->breathTicks); + $this->propertyManager->setShort(EntityMetadataProperties::MAX_AIR, $this->maxBreathTicks); + + $this->propertyManager->setGenericFlag(EntityMetadataFlags::BREATHING, $this->breathing); + } + protected function onDispose() : void{ $this->armorInventory->removeAllViewers(); parent::onDispose(); diff --git a/src/pocketmine/entity/Villager.php b/src/pocketmine/entity/Villager.php index 417964eba..9172a9477 100644 --- a/src/pocketmine/entity/Villager.php +++ b/src/pocketmine/entity/Villager.php @@ -40,6 +40,11 @@ class Villager extends Living implements Ageable{ public $width = 0.6; public $height = 1.8; + /** @var bool */ + private $baby = false; + /** @var int */ + private $profession = self::PROFESSION_FARMER; + public function getName() : string{ return "Villager"; } @@ -70,14 +75,21 @@ class Villager extends Living implements Ageable{ * @param int $profession */ public function setProfession(int $profession) : void{ - $this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $profession); + $this->profession = $profession; //TODO: validation } public function getProfession() : int{ - return $this->propertyManager->getInt(EntityMetadataProperties::VARIANT); + return $this->profession; } public function isBaby() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::BABY); + return $this->baby; + } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); + + $this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $this->profession); } } diff --git a/src/pocketmine/entity/WaterAnimal.php b/src/pocketmine/entity/WaterAnimal.php index a98f8b192..a6aeb6a8f 100644 --- a/src/pocketmine/entity/WaterAnimal.php +++ b/src/pocketmine/entity/WaterAnimal.php @@ -27,9 +27,11 @@ use pocketmine\event\entity\EntityDamageEvent; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; abstract class WaterAnimal extends Living implements Ageable{ + /** @var bool */ + protected $baby = false; public function isBaby() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::BABY); + return $this->baby; } public function canBreathe() : bool{ @@ -40,4 +42,9 @@ abstract class WaterAnimal extends Living implements Ageable{ $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2); $this->attack($ev); } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); + } } diff --git a/src/pocketmine/entity/effect/EffectManager.php b/src/pocketmine/entity/effect/EffectManager.php index b8f88ac29..797b90500 100644 --- a/src/pocketmine/entity/effect/EffectManager.php +++ b/src/pocketmine/entity/effect/EffectManager.php @@ -26,7 +26,6 @@ namespace pocketmine\entity\effect; use pocketmine\entity\Living; use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; -use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\utils\Color; use function abs; @@ -38,8 +37,14 @@ class EffectManager{ /** @var EffectInstance[] */ protected $effects = []; + /** @var Color */ + protected $bubbleColor; + /** @var bool */ + protected $onlyAmbientEffects = false; + public function __construct(Living $entity){ $this->entity = $entity; + $this->bubbleColor = new Color(0, 0, 0, 0); } /** @@ -176,16 +181,29 @@ class EffectManager{ } } - $propManager = $this->entity->getDataPropertyManager(); if(!empty($colors)){ - $propManager->setInt(EntityMetadataProperties::POTION_COLOR, Color::mix(...$colors)->toARGB()); - $propManager->setByte(EntityMetadataProperties::POTION_AMBIENT, $ambient ? 1 : 0); + $this->bubbleColor = Color::mix(...$colors); + $this->onlyAmbientEffects = $ambient; }else{ - $propManager->setInt(EntityMetadataProperties::POTION_COLOR, 0); - $propManager->setByte(EntityMetadataProperties::POTION_AMBIENT, 0); + $this->bubbleColor = new Color(0, 0, 0, 0); + $this->onlyAmbientEffects = false; } } + /** + * @return Color + */ + public function getBubbleColor() : Color{ + return $this->bubbleColor; + } + + /** + * @return bool + */ + public function hasOnlyAmbientEffects() : bool{ + return $this->onlyAmbientEffects; + } + public function tick(int $tickDiff = 1) : bool{ foreach($this->effects as $instance){ $type = $instance->getType(); diff --git a/src/pocketmine/entity/object/ExperienceOrb.php b/src/pocketmine/entity/object/ExperienceOrb.php index 6d89b372e..ce4baaded 100644 --- a/src/pocketmine/entity/object/ExperienceOrb.php +++ b/src/pocketmine/entity/object/ExperienceOrb.php @@ -107,12 +107,15 @@ class ExperienceOrb extends Entity{ */ protected $targetPlayerRuntimeId = null; + /** @var int */ + protected $xpValue = 1; + protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); $this->age = $nbt->getShort("Age", 0); - $value = 0; + $value = 1; if($nbt->hasTag(self::TAG_VALUE_PC, ShortTag::class)){ //PC $value = $nbt->getShort(self::TAG_VALUE_PC); }elseif($nbt->hasTag(self::TAG_VALUE_PE, IntTag::class)){ //PE save format @@ -134,14 +137,14 @@ class ExperienceOrb extends Entity{ } public function getXpValue() : int{ - return $this->propertyManager->getInt(EntityMetadataProperties::EXPERIENCE_VALUE) ?? 0; + return $this->xpValue; } public function setXpValue(int $amount) : void{ if($amount <= 0){ throw new \InvalidArgumentException("XP amount must be greater than 0, got $amount"); } - $this->propertyManager->setInt(EntityMetadataProperties::EXPERIENCE_VALUE, $amount); + $this->xpValue = $amount; } public function hasTargetPlayer() : bool{ @@ -225,4 +228,10 @@ class ExperienceOrb extends Entity{ public function canBeCollidedWith() : bool{ return false; } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setInt(EntityMetadataProperties::EXPERIENCE_VALUE, $this->xpValue); + } } diff --git a/src/pocketmine/entity/object/FallingBlock.php b/src/pocketmine/entity/object/FallingBlock.php index db7ef94b6..99e2b8ea8 100644 --- a/src/pocketmine/entity/object/FallingBlock.php +++ b/src/pocketmine/entity/object/FallingBlock.php @@ -71,8 +71,6 @@ class FallingBlock extends Entity{ $damage = $nbt->getByte("Data", 0); $this->block = BlockFactory::get($blockId, $damage); - - $this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $this->block->getRuntimeId()); } public function canCollideWith(Entity $entity) : bool{ @@ -138,4 +136,10 @@ class FallingBlock extends Entity{ return $nbt; } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $this->block->getRuntimeId()); + } } diff --git a/src/pocketmine/entity/object/ItemEntity.php b/src/pocketmine/entity/object/ItemEntity.php index 085c5a754..421db601a 100644 --- a/src/pocketmine/entity/object/ItemEntity.php +++ b/src/pocketmine/entity/object/ItemEntity.php @@ -238,7 +238,7 @@ class ItemEntity extends Entity{ $pk->position = $this->asVector3(); $pk->motion = $this->getMotion(); $pk->item = $this->getItem(); - $pk->metadata = $this->propertyManager->getAll(); + $pk->metadata = $this->getSyncedNetworkData(false); $player->sendDataPacket($pk); } diff --git a/src/pocketmine/entity/object/PrimedTNT.php b/src/pocketmine/entity/object/PrimedTNT.php index 755325c58..23f6deaea 100644 --- a/src/pocketmine/entity/object/PrimedTNT.php +++ b/src/pocketmine/entity/object/PrimedTNT.php @@ -28,7 +28,6 @@ use pocketmine\entity\Explosive; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\ExplosionPrimeEvent; use pocketmine\nbt\tag\CompoundTag; -use pocketmine\nbt\tag\ShortTag; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; @@ -61,14 +60,7 @@ class PrimedTNT extends Entity implements Explosive{ protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); - if($nbt->hasTag("Fuse", ShortTag::class)){ - $this->fuse = $nbt->getShort("Fuse"); - }else{ - $this->fuse = 80; - } - - $this->setGenericFlag(EntityMetadataFlags::IGNITED, true); - $this->propertyManager->setInt(EntityMetadataProperties::FUSE_LENGTH, $this->fuse); + $this->fuse = $nbt->getShort("Fuse", 80, true); $this->world->addSound($this, new IgniteSound()); } @@ -92,10 +84,6 @@ class PrimedTNT extends Entity implements Explosive{ $hasUpdate = parent::entityBaseTick($tickDiff); - if($this->fuse % 5 === 0){ //don't spam it every tick, it's not necessary - $this->propertyManager->setInt(EntityMetadataProperties::FUSE_LENGTH, $this->fuse); - } - if(!$this->isFlaggedForDespawn()){ $this->fuse -= $tickDiff; @@ -119,4 +107,11 @@ class PrimedTNT extends Entity implements Explosive{ $explosion->explodeB(); } } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setGenericFlag(EntityMetadataFlags::IGNITED, true); + $this->propertyManager->setInt(EntityMetadataProperties::FUSE_LENGTH, $this->fuse); + } } diff --git a/src/pocketmine/entity/projectile/Arrow.php b/src/pocketmine/entity/projectile/Arrow.php index 25a32e35a..91f4de3d1 100644 --- a/src/pocketmine/entity/projectile/Arrow.php +++ b/src/pocketmine/entity/projectile/Arrow.php @@ -67,6 +67,9 @@ class Arrow extends Projectile{ /** @var int */ protected $collideTicks = 0; + /** @var bool */ + protected $critical = false; + public function __construct(World $world, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){ parent::__construct($world, $nbt, $shootingEntity); $this->setCritical($critical); @@ -87,11 +90,11 @@ class Arrow extends Projectile{ } public function isCritical() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::CRITICAL); + return $this->critical; } public function setCritical(bool $value = true) : void{ - $this->setGenericFlag(EntityMetadataFlags::CRITICAL, $value); + $this->critical = $value; } public function getResultDamage() : int{ @@ -199,4 +202,10 @@ class Arrow extends Projectile{ $playerInventory->addItem(clone $item); $this->flagForDespawn(); } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setGenericFlag(EntityMetadataFlags::CRITICAL, $this->critical); + } } diff --git a/src/pocketmine/entity/projectile/SplashPotion.php b/src/pocketmine/entity/projectile/SplashPotion.php index 7d589dd20..bed33146a 100644 --- a/src/pocketmine/entity/projectile/SplashPotion.php +++ b/src/pocketmine/entity/projectile/SplashPotion.php @@ -49,10 +49,15 @@ class SplashPotion extends Throwable{ protected $gravity = 0.05; protected $drag = 0.01; + /** @var bool */ + protected $linger = false; + /** @var int */ + protected $potionId = Potion::WATER; + protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); - $this->setPotionId($nbt->getShort("PotionId", 0)); + $this->setPotionId($nbt->getShort("PotionId", Potion::WATER)); } public function saveNBT() : CompoundTag{ @@ -139,14 +144,14 @@ class SplashPotion extends Throwable{ * @return int */ public function getPotionId() : int{ - return $this->propertyManager->getShort(EntityMetadataProperties::POTION_AUX_VALUE) ?? 0; + return $this->potionId; } /** * @param int $id */ public function setPotionId(int $id) : void{ - $this->propertyManager->setShort(EntityMetadataProperties::POTION_AUX_VALUE, $id); + $this->potionId = $id; //TODO: validation } /** @@ -154,7 +159,7 @@ class SplashPotion extends Throwable{ * @return bool */ public function willLinger() : bool{ - return $this->getDataFlag(EntityMetadataProperties::FLAGS, EntityMetadataFlags::LINGER); + return $this->linger; } /** @@ -163,7 +168,7 @@ class SplashPotion extends Throwable{ * @param bool $value */ public function setLinger(bool $value = true) : void{ - $this->setDataFlag(EntityMetadataProperties::FLAGS, EntityMetadataFlags::LINGER, $value); + $this->linger = $value; } /** @@ -172,4 +177,11 @@ class SplashPotion extends Throwable{ public function getPotionEffects() : array{ return Potion::getPotionEffectsById($this->getPotionId()); } + + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setShort(EntityMetadataProperties::POTION_AUX_VALUE, $this->potionId); + $this->propertyManager->setGenericFlag(EntityMetadataFlags::LINGER, $this->linger); + } } diff --git a/src/pocketmine/network/mcpe/protocol/AddActorPacket.php b/src/pocketmine/network/mcpe/protocol/AddActorPacket.php index e854484f2..cea94a301 100644 --- a/src/pocketmine/network/mcpe/protocol/AddActorPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddActorPacket.php @@ -31,6 +31,7 @@ use pocketmine\network\BadPacketException; use pocketmine\network\mcpe\handler\PacketHandler; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; use pocketmine\network\mcpe\protocol\types\entity\EntityLink; +use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; use function array_search; use function count; @@ -164,7 +165,7 @@ class AddActorPacket extends DataPacket implements ClientboundPacket{ /** @var Attribute[] */ public $attributes = []; - /** @var array */ + /** @var MetadataProperty[] */ public $metadata = []; /** @var EntityLink[] */ public $links = []; diff --git a/src/pocketmine/network/mcpe/protocol/AddItemActorPacket.php b/src/pocketmine/network/mcpe/protocol/AddItemActorPacket.php index 5fb28f0f4..ded9268cb 100644 --- a/src/pocketmine/network/mcpe/protocol/AddItemActorPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddItemActorPacket.php @@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\network\mcpe\handler\PacketHandler; +use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; class AddItemActorPacket extends DataPacket implements ClientboundPacket{ public const NETWORK_ID = ProtocolInfo::ADD_ITEM_ACTOR_PACKET; @@ -42,7 +43,7 @@ class AddItemActorPacket extends DataPacket implements ClientboundPacket{ public $position; /** @var Vector3|null */ public $motion; - /** @var array */ + /** @var MetadataProperty[] */ public $metadata = []; /** @var bool */ public $isFromFishing = false; diff --git a/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php b/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php index 10a5964c3..a33476f50 100644 --- a/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php @@ -29,6 +29,7 @@ use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\network\mcpe\handler\PacketHandler; use pocketmine\network\mcpe\protocol\types\entity\EntityLink; +use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; use pocketmine\utils\UUID; use function count; @@ -57,7 +58,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{ public $headYaw = null; //TODO /** @var Item */ public $item; - /** @var array */ + /** @var MetadataProperty[] */ public $metadata = []; //TODO: adventure settings stuff diff --git a/src/pocketmine/network/mcpe/protocol/SetActorDataPacket.php b/src/pocketmine/network/mcpe/protocol/SetActorDataPacket.php index 308454f57..cb9a60339 100644 --- a/src/pocketmine/network/mcpe/protocol/SetActorDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetActorDataPacket.php @@ -27,16 +27,18 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\handler\PacketHandler; +use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; class SetActorDataPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{ //TODO: check why this is serverbound public const NETWORK_ID = ProtocolInfo::SET_ACTOR_DATA_PACKET; /** @var int */ public $entityRuntimeId; - /** @var array */ + /** @var MetadataProperty[] */ public $metadata; public static function create(int $entityRuntimeId, array $metadata) : self{ + $result = new self; $result->entityRuntimeId = $entityRuntimeId; $result->metadata = $metadata; diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/BlockPosMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/BlockPosMetadataProperty.php new file mode 100644 index 000000000..5bf56214f --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/BlockPosMetadataProperty.php @@ -0,0 +1,65 @@ +value = $value->floor(); + } + + /** + * @return Vector3 + */ + public function getValue() : Vector3{ + return $this->value; + } + + public static function id() : int{ + return EntityMetadataTypes::POS; + } + + public static function read(NetworkBinaryStream $in) : self{ + $vec = new Vector3(0, 0, 0); + $in->getSignedBlockPosition($vec->x, $vec->y, $vec->z); + return new self($vec); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putSignedBlockPosition($this->value->x, $this->value->y, $this->value->z); + } + + public function equals(MetadataProperty $other) : bool{ + return $other instanceof $this and $other->value->equals($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/ByteMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/ByteMetadataProperty.php new file mode 100644 index 000000000..1945b316d --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/ByteMetadataProperty.php @@ -0,0 +1,50 @@ +getByte()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/EntityMetadataCollection.php b/src/pocketmine/network/mcpe/protocol/types/entity/EntityMetadataCollection.php index c56f5be5c..61bc933bf 100644 --- a/src/pocketmine/network/mcpe/protocol/types/entity/EntityMetadataCollection.php +++ b/src/pocketmine/network/mcpe/protocol/types/entity/EntityMetadataCollection.php @@ -25,51 +25,27 @@ namespace pocketmine\network\mcpe\protocol\types\entity; use pocketmine\item\Item; use pocketmine\math\Vector3; -use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; -use function assert; -use function is_float; -use function is_int; -use function is_string; +use function get_class; class EntityMetadataCollection{ + /** @var MetadataProperty[] */ private $properties = []; - + /** @var MetadataProperty[] */ private $dirtyProperties = []; public function __construct(){ } - /** - * @param int $key - * - * @return int|null - */ - public function getByte(int $key) : ?int{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::BYTE); - assert(is_int($value) or $value === null); - return $value; - } - /** * @param int $key * @param int $value * @param bool $force */ public function setByte(int $key, int $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::BYTE, $value, $force); - } - /** - * @param int $key - * - * @return int|null - */ - public function getShort(int $key) : ?int{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::SHORT); - assert(is_int($value) or $value === null); - return $value; + $this->set($key, new ByteMetadataProperty($value), $force); } /** @@ -78,18 +54,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setShort(int $key, int $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::SHORT, $value, $force); - } - - /** - * @param int $key - * - * @return int|null - */ - public function getInt(int $key) : ?int{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::INT); - assert(is_int($value) or $value === null); - return $value; + $this->set($key, new ShortMetadataProperty($value), $force); } /** @@ -98,18 +63,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setInt(int $key, int $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::INT, $value, $force); - } - - /** - * @param int $key - * - * @return float|null - */ - public function getFloat(int $key) : ?float{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::FLOAT); - assert(is_float($value) or $value === null); - return $value; + $this->set($key, new IntMetadataProperty($value), $force); } /** @@ -118,18 +72,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setFloat(int $key, float $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::FLOAT, $value, $force); - } - - /** - * @param int $key - * - * @return null|string - */ - public function getString(int $key) : ?string{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::STRING); - assert(is_string($value) or $value === null); - return $value; + $this->set($key, new FloatMetadataProperty($value), $force); } /** @@ -138,19 +81,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setString(int $key, string $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::STRING, $value, $force); - } - - /** - * @param int $key - * - * @return null|Item - */ - public function getItem(int $key) : ?Item{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::SLOT); - assert($value instanceof Item or $value === null); - - return $value; + $this->set($key, new StringMetadataProperty($value), $force); } /** @@ -159,18 +90,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setItem(int $key, Item $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::SLOT, $value, $force); - } - - /** - * @param int $key - * - * @return null|Vector3 - */ - public function getBlockPos(int $key) : ?Vector3{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::POS); - assert($value instanceof Vector3 or $value === null); - return $value; + $this->set($key, new ItemStackMetadataProperty($value), $force); } /** @@ -179,18 +99,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setBlockPos(int $key, ?Vector3 $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::POS, $value ? $value->floor() : null, $force); - } - - /** - * @param int $key - * - * @return int|null - */ - public function getLong(int $key) : ?int{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::LONG); - assert(is_int($value) or $value === null); - return $value; + $this->set($key, new BlockPosMetadataProperty($value ?? new Vector3(0, 0, 0)), $force); } /** @@ -199,18 +108,7 @@ class EntityMetadataCollection{ * @param bool $force */ public function setLong(int $key, int $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::LONG, $value, $force); - } - - /** - * @param int $key - * - * @return null|Vector3 - */ - public function getVector3(int $key) : ?Vector3{ - $value = $this->getPropertyValue($key, EntityMetadataTypes::VECTOR3F); - assert($value instanceof Vector3 or $value === null); - return $value; + $this->set($key, new LongMetadataProperty($value), $force); } /** @@ -219,74 +117,60 @@ class EntityMetadataCollection{ * @param bool $force */ public function setVector3(int $key, ?Vector3 $value, bool $force = false) : void{ - $this->setPropertyValue($key, EntityMetadataTypes::VECTOR3F, $value ? $value->asVector3() : null, $force); - } - - /** - * @param int $key - */ - public function removeProperty(int $key) : void{ - unset($this->properties[$key]); - } - - /** - * @param int $key - * - * @return bool - */ - public function hasProperty(int $key) : bool{ - return isset($this->properties[$key]); - } - - /** - * @param int $key - * - * @return int - */ - public function getPropertyType(int $key) : int{ - if(isset($this->properties[$key])){ - return $this->properties[$key][0]; - } - - return -1; - } - - private function checkType(int $key, int $type) : void{ - if(isset($this->properties[$key]) and $this->properties[$key][0] !== $type){ - throw new \RuntimeException("Expected type $type, but have " . $this->properties[$key][0]); - } - } - - /** - * @param int $key - * @param int $type - * - * @return mixed - */ - public function getPropertyValue(int $key, int $type){ - if($type !== -1){ - $this->checkType($key, $type); - } - return isset($this->properties[$key]) ? $this->properties[$key][1] : null; + $this->set($key, new Vec3MetadataProperty($value ?? new Vector3(0, 0, 0)), $force); } /** * @param int $key - * @param int $type * @param mixed $value * @param bool $force */ - public function setPropertyValue(int $key, int $type, $value, bool $force = false) : void{ - if(!$force){ - $this->checkType($key, $type); + public function set(int $key, MetadataProperty $value, bool $force = false) : void{ + if(!$force and isset($this->properties[$key]) and !($this->properties[$key] instanceof $value)){ + throw new \InvalidArgumentException("Can't overwrite property with mismatching types (have " . get_class($this->properties[$key]) . ")"); + } + if(!isset($this->properties[$key]) or !$this->properties[$key]->equals($value)){ + $this->properties[$key] = $this->dirtyProperties[$key] = $value; + } + } + + public function setGenericFlag(int $flagId, bool $value) : void{ + $propertyId = $flagId >= 64 ? EntityMetadataProperties::FLAGS2 : EntityMetadataProperties::FLAGS; + $realFlagId = $flagId % 64; + $flagSetProp = $this->properties[$propertyId] ?? null; + if($flagSetProp === null){ + $flagSet = 0; + }elseif($flagSetProp instanceof LongMetadataProperty){ + $flagSet = $flagSetProp->getValue(); + }else{ + throw new \InvalidArgumentException("Wrong type found for flags, want long, but have " . get_class($flagSetProp)); + } + + if((($flagSet >> $realFlagId) & 1) !== ($value ? 1 : 0)){ + $flagSet ^= (1 << $realFlagId); + $this->setLong($propertyId, $flagSet); + } + } + + public function setPlayerFlag(int $flagId, bool $value) : void{ + $flagSetProp = $this->properties[EntityMetadataProperties::PLAYER_FLAGS] ?? null; + if($flagSetProp === null){ + $flagSet = 0; + }elseif($flagSetProp instanceof ByteMetadataProperty){ + $flagSet = $flagSetProp->getValue(); + }else{ + throw new \InvalidArgumentException("Wrong type found for flags, want byte, but have " . get_class($flagSetProp)); + } + if((($flagSet >> $flagId) & 1) !== ($value ? 1 : 0)){ + $flagSet ^= (1 << $flagId); + $this->setByte(EntityMetadataProperties::PLAYER_FLAGS, $flagSet); } - $this->properties[$key] = $this->dirtyProperties[$key] = [$type, $value]; } /** * Returns all properties. * - * @return array + * @return MetadataProperty[] */ public function getAll() : array{ return $this->properties; @@ -295,7 +179,7 @@ class EntityMetadataCollection{ /** * Returns properties that have changed and need to be broadcasted. * - * @return array + * @return MetadataProperty[] */ public function getDirty() : array{ return $this->dirtyProperties; diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/FloatMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/FloatMetadataProperty.php new file mode 100644 index 000000000..6554af0a0 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/FloatMetadataProperty.php @@ -0,0 +1,62 @@ +value = $value; + } + + /** + * @return float + */ + public function getValue() : float{ + return $this->value; + } + + public static function id() : int{ + return EntityMetadataTypes::FLOAT; + } + + public function equals(MetadataProperty $other) : bool{ + return $other instanceof $this and $other->value === $this->value; + } + + public static function read(NetworkBinaryStream $in) : self{ + return new self($in->getLFloat()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putLFloat($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/IntMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/IntMetadataProperty.php new file mode 100644 index 000000000..1bdcbb888 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/IntMetadataProperty.php @@ -0,0 +1,50 @@ +getVarInt()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putVarInt($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/IntegerishMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/IntegerishMetadataProperty.php new file mode 100644 index 000000000..a36f3c7dd --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/IntegerishMetadataProperty.php @@ -0,0 +1,64 @@ +min() or $value > $this->max()){ + throw new \InvalidArgumentException("Value is out of range " . $this->min() . " - " . $this->max()); + } + $this->value = $value; + } + + abstract protected function min() : int; + + abstract protected function max() : int; + + /** + * @return int + */ + public function getValue() : int{ + return $this->value; + } + + public function equals(MetadataProperty $other) : bool{ + return $other instanceof $this and $other->value === $this->value; + } + + public static function buildFromFlags(array $flags) : self{ + $value = 0; + foreach($flags as $flag => $v){ + if($v){ + $value |= 1 << $flag; + } + } + return new self($value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/ItemStackMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/ItemStackMetadataProperty.php new file mode 100644 index 000000000..0d2cea475 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/ItemStackMetadataProperty.php @@ -0,0 +1,62 @@ +value = clone $value; + } + + /** + * @return Item + */ + public function getValue() : Item{ + return clone $this->value; + } + + public static function id() : int{ + return EntityMetadataTypes::SLOT; + } + + public function equals(MetadataProperty $other) : bool{ + return $other instanceof $this and $other->value->equalsExact($this->value); + } + + public static function read(NetworkBinaryStream $in) : self{ + return new self($in->getSlot()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putSlot($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/LongMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/LongMetadataProperty.php new file mode 100644 index 000000000..463014d5a --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/LongMetadataProperty.php @@ -0,0 +1,52 @@ +getVarLong()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putVarLong($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/MetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/MetadataProperty.php new file mode 100644 index 000000000..9a920d70b --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/MetadataProperty.php @@ -0,0 +1,35 @@ +getSignedLShort()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putLShort($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/StringMetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/StringMetadataProperty.php new file mode 100644 index 000000000..daf781c3c --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/StringMetadataProperty.php @@ -0,0 +1,54 @@ +value = $value; + } + + public static function id() : int{ + return EntityMetadataTypes::STRING; + } + + public static function read(NetworkBinaryStream $in) : self{ + return new self($in->getString()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putString($this->value); + } + + public function equals(MetadataProperty $other) : bool{ + return $other instanceof $this and $other->value === $this->value; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/entity/Vec3MetadataProperty.php b/src/pocketmine/network/mcpe/protocol/types/entity/Vec3MetadataProperty.php new file mode 100644 index 000000000..33fee7162 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/entity/Vec3MetadataProperty.php @@ -0,0 +1,62 @@ +value = $value->asVector3(); + } + + /** + * @return Vector3 + */ + public function getValue() : Vector3{ + return clone $this->value; + } + + public static function id() : int{ + return EntityMetadataTypes::VECTOR3F; + } + + public static function read(NetworkBinaryStream $in) : self{ + return new self($in->getVector3()); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putVector3($this->value); + } + + public function equals(MetadataProperty $other) : bool{ + return $other instanceof $this and $other->value->equals($this->value); + } +} diff --git a/src/pocketmine/network/mcpe/serializer/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/serializer/NetworkBinaryStream.php index 173b99f2d..42919c621 100644 --- a/src/pocketmine/network/mcpe/serializer/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/serializer/NetworkBinaryStream.php @@ -37,8 +37,17 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\TreeRoot; use pocketmine\network\BadPacketException; use pocketmine\network\mcpe\protocol\types\command\CommandOriginData; +use pocketmine\network\mcpe\protocol\types\entity\BlockPosMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\ByteMetadataProperty; use pocketmine\network\mcpe\protocol\types\entity\EntityLink; -use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; +use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\IntMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\ItemStackMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\ShortMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\Vec3MetadataProperty; use pocketmine\utils\BinaryDataException; use pocketmine\utils\BinaryStream; use pocketmine\utils\UUID; @@ -224,108 +233,51 @@ class NetworkBinaryStream extends BinaryStream{ /** * Decodes entity metadata from the stream. * - * @param bool $types Whether to include metadata types along with values in the returned array - * - * @return array + * @return MetadataProperty[] * * @throws BadPacketException * @throws BinaryDataException */ - public function getEntityMetadata(bool $types = true) : array{ + public function getEntityMetadata() : array{ $count = $this->getUnsignedVarInt(); $data = []; for($i = 0; $i < $count; ++$i){ $key = $this->getUnsignedVarInt(); $type = $this->getUnsignedVarInt(); - $value = null; - switch($type){ - case EntityMetadataTypes::BYTE: - $value = $this->getByte(); - break; - case EntityMetadataTypes::SHORT: - $value = $this->getSignedLShort(); - break; - case EntityMetadataTypes::INT: - $value = $this->getVarInt(); - break; - case EntityMetadataTypes::FLOAT: - $value = $this->getLFloat(); - break; - case EntityMetadataTypes::STRING: - $value = $this->getString(); - break; - case EntityMetadataTypes::SLOT: - $value = $this->getSlot(); - break; - case EntityMetadataTypes::POS: - $value = new Vector3(); - $this->getSignedBlockPosition($value->x, $value->y, $value->z); - break; - case EntityMetadataTypes::LONG: - $value = $this->getVarLong(); - break; - case EntityMetadataTypes::VECTOR3F: - $value = $this->getVector3(); - break; - default: - throw new BadPacketException("Unknown entity metadata type " . $type); - } - if($types){ - $data[$key] = [$type, $value]; - }else{ - $data[$key] = $value; - } + + $data[$key] = $this->readMetadataProperty($type); } return $data; } + private function readMetadataProperty(int $type) : MetadataProperty{ + switch($type){ + case ByteMetadataProperty::id(): return ByteMetadataProperty::read($this); + case ShortMetadataProperty::id(): return ShortMetadataProperty::read($this); + case IntMetadataProperty::id(): return IntMetadataProperty::read($this); + case FloatMetadataProperty::id(): return FloatMetadataProperty::read($this); + case StringMetadataProperty::id(): return StringMetadataProperty::read($this); + case ItemStackMetadataProperty::id(): return ItemStackMetadataProperty::read($this); + case BlockPosMetadataProperty::id(): return BlockPosMetadataProperty::read($this); + case LongMetadataProperty::id(): return LongMetadataProperty::read($this); + case Vec3MetadataProperty::id(): return Vec3MetadataProperty::read($this); + default: + throw new BadPacketException("Unknown entity metadata type " . $type); + } + } + /** * Writes entity metadata to the packet buffer. * - * @param array $metadata + * @param MetadataProperty[] $metadata */ public function putEntityMetadata(array $metadata) : void{ $this->putUnsignedVarInt(count($metadata)); foreach($metadata as $key => $d){ - $this->putUnsignedVarInt($key); //data key - $this->putUnsignedVarInt($d[0]); //data type - switch($d[0]){ - case EntityMetadataTypes::BYTE: - $this->putByte($d[1]); - break; - case EntityMetadataTypes::SHORT: - $this->putLShort($d[1]); //SIGNED short! - break; - case EntityMetadataTypes::INT: - $this->putVarInt($d[1]); - break; - case EntityMetadataTypes::FLOAT: - $this->putLFloat($d[1]); - break; - case EntityMetadataTypes::STRING: - $this->putString($d[1]); - break; - case EntityMetadataTypes::SLOT: - $this->putSlot($d[1]); - break; - case EntityMetadataTypes::POS: - $v = $d[1]; - if($v !== null){ - $this->putSignedBlockPosition($v->x, $v->y, $v->z); - }else{ - $this->putSignedBlockPosition(0, 0, 0); - } - break; - case EntityMetadataTypes::LONG: - $this->putVarLong($d[1]); - break; - case EntityMetadataTypes::VECTOR3F: - $this->putVector3Nullable($d[1]); - break; - default: - throw new \InvalidArgumentException("Invalid data type " . $d[0]); - } + $this->putUnsignedVarInt($key); + $this->putUnsignedVarInt($d::id()); + $d->write($this); } } diff --git a/src/pocketmine/player/Player.php b/src/pocketmine/player/Player.php index 1e883c137..e364d11be 100644 --- a/src/pocketmine/player/Player.php +++ b/src/pocketmine/player/Player.php @@ -752,12 +752,11 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, * @return bool */ public function isUsingItem() : bool{ - return $this->getGenericFlag(EntityMetadataFlags::ACTION) and $this->startAction > -1; + return $this->startAction > -1; } public function setUsingItem(bool $value){ $this->startAction = $value ? $this->server->getTick() : -1; - $this->setGenericFlag(EntityMetadataFlags::ACTION, $value); } /** @@ -1078,10 +1077,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, $b->setOccupied(); } - $this->sleeping = clone $pos; - - $this->propertyManager->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $pos); - $this->setPlayerFlag(PlayerMetadataFlags::SLEEP, true); + $this->sleeping = $pos; $this->setSpawn($pos); @@ -1099,8 +1095,6 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, (new PlayerBedLeaveEvent($this, $b))->call(); $this->sleeping = null; - $this->propertyManager->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, null); - $this->setPlayerFlag(PlayerMetadataFlags::SLEEP, false); $this->world->setSleepTicks(0); @@ -2367,6 +2361,15 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, parent::attack($source); } + protected function syncNetworkData() : void{ + parent::syncNetworkData(); + + $this->propertyManager->setGenericFlag(EntityMetadataFlags::ACTION, $this->startAction > -1); + + $this->propertyManager->setPlayerFlag(PlayerMetadataFlags::SLEEP, $this->sleeping !== null); + $this->propertyManager->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $this->sleeping ?? new Vector3(0, 0, 0)); + } + public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{ if($this->spawned and $players === null){ $players = $this->getViewers(); diff --git a/src/pocketmine/world/particle/FloatingTextParticle.php b/src/pocketmine/world/particle/FloatingTextParticle.php index 6deb6b6ac..96ca19efa 100644 --- a/src/pocketmine/world/particle/FloatingTextParticle.php +++ b/src/pocketmine/world/particle/FloatingTextParticle.php @@ -32,7 +32,8 @@ use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; -use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; +use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty; +use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\utils\UUID; use function str_repeat; @@ -104,8 +105,8 @@ class FloatingTextParticle implements Particle{ 1 << EntityMetadataFlags::IMMOBILE ); $pk->metadata = [ - EntityMetadataProperties::FLAGS => [EntityMetadataTypes::LONG, $flags], - EntityMetadataProperties::SCALE => [EntityMetadataTypes::FLOAT, 0.01] //zero causes problems on debug builds + EntityMetadataProperties::FLAGS => new LongMetadataProperty($flags), + EntityMetadataProperties::SCALE => new FloatMetadataProperty(0.01) //zero causes problems on debug builds ]; $p[] = $pk;