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
This commit is contained in:
Dylan K. Taylor 2019-07-28 19:40:47 +01:00
parent 6c0ae6bf0b
commit e06ab0869a
32 changed files with 958 additions and 450 deletions

View File

@ -27,8 +27,15 @@ namespace pocketmine\entity;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
abstract class Animal extends Living implements Ageable{ abstract class Animal extends Living implements Ageable{
/** @var bool */
protected $baby = false;
public function isBaby() : bool{ 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);
} }
} }

View File

@ -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\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; 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\player\Player;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\timings\Timings; use pocketmine\timings\Timings;
@ -195,6 +195,35 @@ abstract class Entity extends Location{
/** @var TimingsHandler */ /** @var TimingsHandler */
protected $timings; 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){ public function __construct(World $world, CompoundTag $nbt){
$this->timings = Timings::getEntityTimings($this); $this->timings = Timings::getEntityTimings($this);
@ -234,22 +263,10 @@ abstract class Entity extends Location{
$this->propertyManager = new EntityMetadataCollection(); $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->attributeMap = new AttributeMap();
$this->addAttributes(); $this->addAttributes();
$this->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true);
$this->setGenericFlag(EntityMetadataFlags::HAS_COLLISION, true);
$this->initEntity($nbt); $this->initEntity($nbt);
$this->propertyManager->clearDirtyProperties(); //Prevents resending properties that were set during construction
$this->chunk->addEntity($this); $this->chunk->addEntity($this);
$this->world->addEntity($this); $this->world->addEntity($this);
@ -265,64 +282,63 @@ abstract class Entity extends Location{
* @return string * @return string
*/ */
public function getNameTag() : string{ public function getNameTag() : string{
return $this->propertyManager->getString(EntityMetadataProperties::NAMETAG); return $this->nameTag;
} }
/** /**
* @return bool * @return bool
*/ */
public function isNameTagVisible() : bool{ public function isNameTagVisible() : bool{
return $this->getGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG); return $this->nameTagVisible;
} }
/** /**
* @return bool * @return bool
*/ */
public function isNameTagAlwaysVisible() : bool{ public function isNameTagAlwaysVisible() : bool{
return $this->propertyManager->getByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG) === 1; return $this->alwaysShowNameTag;
} }
/** /**
* @param string $name * @param string $name
*/ */
public function setNameTag(string $name) : void{ public function setNameTag(string $name) : void{
$this->propertyManager->setString(EntityMetadataProperties::NAMETAG, $name); $this->nameTag = $name;
} }
/** /**
* @param bool $value * @param bool $value
*/ */
public function setNameTagVisible(bool $value = true) : void{ public function setNameTagVisible(bool $value = true) : void{
$this->setGenericFlag(EntityMetadataFlags::CAN_SHOW_NAMETAG, $value); $this->nameTagVisible = $value;
} }
/** /**
* @param bool $value * @param bool $value
*/ */
public function setNameTagAlwaysVisible(bool $value = true) : void{ public function setNameTagAlwaysVisible(bool $value = true) : void{
$this->propertyManager->setByte(EntityMetadataProperties::ALWAYS_SHOW_NAMETAG, $value ? 1 : 0); $this->alwaysShowNameTag = $value;
} }
/** /**
* @return string|null * @return string|null
*/ */
public function getScoreTag() : ?string{ public function getScoreTag() : ?string{
return $this->propertyManager->getString(EntityMetadataProperties::SCORE_TAG); return $this->scoreTag; //TODO: maybe this shouldn't be nullable?
} }
/** /**
* @param string $score * @param string $score
*/ */
public function setScoreTag(string $score) : void{ public function setScoreTag(string $score) : void{
$this->propertyManager->setString(EntityMetadataProperties::SCORE_TAG, $score); $this->scoreTag = $score;
} }
/** /**
* @return float * @return float
*/ */
public function getScale() : 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->height *= $multiplier;
$this->eyeHeight *= $multiplier; $this->eyeHeight *= $multiplier;
$this->recalculateBoundingBox(); $this->scale = $value;
$this->propertyManager->setFloat(EntityMetadataProperties::SCALE, $value); $this->recalculateBoundingBox();
} }
public function getBoundingBox() : AxisAlignedBB{ public function getBoundingBox() : AxisAlignedBB{
@ -361,39 +377,39 @@ abstract class Entity extends Location{
} }
public function isSneaking() : bool{ public function isSneaking() : bool{
return $this->getGenericFlag(EntityMetadataFlags::SNEAKING); return $this->sneaking;
} }
public function setSneaking(bool $value = true) : void{ public function setSneaking(bool $value = true) : void{
$this->setGenericFlag(EntityMetadataFlags::SNEAKING, $value); $this->sneaking = $value;
} }
public function isSprinting() : bool{ public function isSprinting() : bool{
return $this->getGenericFlag(EntityMetadataFlags::SPRINTING); return $this->sprinting;
} }
public function setSprinting(bool $value = true) : void{ public function setSprinting(bool $value = true) : void{
if($value !== $this->isSprinting()){ if($value !== $this->isSprinting()){
$this->setGenericFlag(EntityMetadataFlags::SPRINTING, $value); $this->sprinting = $value;
$attr = $this->attributeMap->getAttribute(Attribute::MOVEMENT_SPEED); $attr = $this->attributeMap->getAttribute(Attribute::MOVEMENT_SPEED);
$attr->setValue($value ? ($attr->getValue() * 1.3) : ($attr->getValue() / 1.3), false, true); $attr->setValue($value ? ($attr->getValue() * 1.3) : ($attr->getValue() / 1.3), false, true);
} }
} }
public function isImmobile() : bool{ public function isImmobile() : bool{
return $this->getGenericFlag(EntityMetadataFlags::IMMOBILE); return $this->immobile;
} }
public function setImmobile(bool $value = true) : void{ public function setImmobile(bool $value = true) : void{
$this->setGenericFlag(EntityMetadataFlags::IMMOBILE, $value); $this->immobile = $value;
} }
public function isInvisible() : bool{ public function isInvisible() : bool{
return $this->getGenericFlag(EntityMetadataFlags::INVISIBLE); return $this->invisible;
} }
public function setInvisible(bool $value = true) : void{ 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 * @return bool
*/ */
public function canClimb() : 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 * @param bool $value
*/ */
public function setCanClimb(bool $value = true) : void{ 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 * @return bool
*/ */
public function canClimbWalls() : 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 * @param bool $value
*/ */
public function setCanClimbWalls(bool $value = true) : void{ 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 * @return int|null
*/ */
public function getOwningEntityId() : ?int{ 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 * @return Entity|null
*/ */
public function getOwningEntity() : ?Entity{ public function getOwningEntity() : ?Entity{
$eid = $this->getOwningEntityId(); return $this->ownerId !== null ? $this->server->getWorldManager()->findEntity($this->ownerId) : null;
if($eid !== null){
return $this->server->getWorldManager()->findEntity($eid);
}
return null;
} }
/** /**
@ -461,11 +472,11 @@ abstract class Entity extends Location{
*/ */
public function setOwningEntity(?Entity $owner) : void{ public function setOwningEntity(?Entity $owner) : void{
if($owner === null){ if($owner === null){
$this->propertyManager->removeProperty(EntityMetadataProperties::OWNER_EID); $this->ownerId = null;
}elseif($owner->closed){ }elseif($owner->closed){
throw new \InvalidArgumentException("Supplied owning entity is garbage and cannot be used"); throw new \InvalidArgumentException("Supplied owning entity is garbage and cannot be used");
}else{ }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 * @return int|null
*/ */
public function getTargetEntityId() : ?int{ 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 * @return Entity|null
*/ */
public function getTargetEntity() : ?Entity{ public function getTargetEntity() : ?Entity{
$eid = $this->getTargetEntityId(); return $this->targetId !== null ? $this->server->getWorldManager()->findEntity($eid) : null;
if($eid !== null){
return $this->server->getWorldManager()->findEntity($eid);
}
return null;
} }
/** /**
@ -501,11 +507,11 @@ abstract class Entity extends Location{
*/ */
public function setTargetEntity(?Entity $target) : void{ public function setTargetEntity(?Entity $target) : void{
if($target === null){ if($target === null){
$this->propertyManager->removeProperty(EntityMetadataProperties::TARGET_EID); $this->targetId = null;
}elseif($target->closed){ }elseif($target->closed){
throw new \InvalidArgumentException("Supplied target entity is garbage and cannot be used"); throw new \InvalidArgumentException("Supplied target entity is garbage and cannot be used");
}else{ }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->setFloat("FallDistance", $this->fallDistance);
$nbt->setShort("Fire", $this->fireTicks); $nbt->setShort("Fire", $this->fireTicks);
$nbt->setShort("Air", $this->propertyManager->getShort(EntityMetadataProperties::AIR));
$nbt->setByte("OnGround", $this->onGround ? 1 : 0); $nbt->setByte("OnGround", $this->onGround ? 1 : 0);
$nbt->setByte("Invulnerable", $this->invulnerable ? 1 : 0); $nbt->setByte("Invulnerable", $this->invulnerable ? 1 : 0);
@ -566,11 +571,7 @@ abstract class Entity extends Location{
protected function initEntity(CompoundTag $nbt) : void{ protected function initEntity(CompoundTag $nbt) : void{
$this->fireTicks = $nbt->getShort("Fire", 0); $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->onGround = $nbt->getByte("OnGround", 0) !== 0;
$this->invulnerable = $nbt->getByte("Invulnerable", 0) !== 0; $this->invulnerable = $nbt->getByte("Invulnerable", 0) !== 0;
@ -717,7 +718,7 @@ abstract class Entity extends Location{
$this->justCreated = false; $this->justCreated = false;
$changedProperties = $this->propertyManager->getDirty(); $changedProperties = $this->getSyncedNetworkData(true);
if(!empty($changedProperties)){ if(!empty($changedProperties)){
$this->sendData($this->hasSpawned, $changedProperties); $this->sendData($this->hasSpawned, $changedProperties);
$this->propertyManager->clearDirtyProperties(); $this->propertyManager->clearDirtyProperties();
@ -758,8 +759,6 @@ abstract class Entity extends Location{
if($ticks > $this->getFireTicks()){ if($ticks > $this->getFireTicks()){
$this->setFireTicks($ticks); $this->setFireTicks($ticks);
} }
$this->setGenericFlag(EntityMetadataFlags::ONFIRE, $this->isOnFire());
} }
/** /**
@ -782,7 +781,6 @@ abstract class Entity extends Location{
public function extinguish() : void{ public function extinguish() : void{
$this->fireTicks = 0; $this->fireTicks = 0;
$this->setGenericFlag(EntityMetadataFlags::ONFIRE, false);
} }
public function isFireProof() : bool{ public function isFireProof() : bool{
@ -1601,7 +1599,7 @@ abstract class Entity extends Location{
$pk->headYaw = $this->yaw; //TODO $pk->headYaw = $this->yaw; //TODO
$pk->pitch = $this->pitch; $pk->pitch = $this->pitch;
$pk->attributes = $this->attributeMap->getAll(); $pk->attributes = $this->attributeMap->getAll();
$pk->metadata = $this->propertyManager->getAll(); $pk->metadata = $this->getSyncedNetworkData(false);
$player->sendDataPacket($pk); $player->sendDataPacket($pk);
} }
@ -1716,60 +1714,15 @@ abstract class Entity extends Location{
} }
/** /**
* @param int $propertyId * @param Player[]|Player $player
* @param int $flagId * @param MetadataProperty[] $data Properly formatted entity data, defaults to everything
* @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
*/ */
public function sendData($player, ?array $data = null) : void{ public function sendData($player, ?array $data = null) : void{
if(!is_array($player)){ if(!is_array($player)){
$player = [$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){ foreach($player as $p){
if($p === $this){ 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{ 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)); $this->server->broadcastPacket($players ?? $this->getViewers(), ActorEventPacket::create($this->id, $eventId, $eventData ?? 0));
} }

View File

@ -46,8 +46,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty;
use pocketmine\network\mcpe\protocol\types\entity\PlayerMetadataFlags;
use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\UUID; use pocketmine\utils\UUID;
@ -228,9 +227,6 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->hungerManager = new HungerManager($this); $this->hungerManager = new HungerManager($this);
$this->xpManager = new ExperienceManager($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->inventory = new PlayerInventory($this);
$this->enderChestInventory = new EnderChestInventory(); $this->enderChestInventory = new EnderChestInventory();
$this->initHumanData($nbt); $this->initHumanData($nbt);
@ -422,11 +418,11 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$pk->yaw = $this->yaw; $pk->yaw = $this->yaw;
$pk->pitch = $this->pitch; $pk->pitch = $this->pitch;
$pk->item = $this->getInventory()->getItemInHand(); $pk->item = $this->getInventory()->getItemInHand();
$pk->metadata = $this->propertyManager->getAll(); $pk->metadata = $this->getSyncedNetworkData(false);
$player->sendDataPacket($pk); $player->sendDataPacket($pk);
//TODO: Hack for MCPE 1.2.13: DATA_NAMETAG is useless in AddPlayerPacket, so it has to be sent separately //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); $player->getNetworkSession()->onMobArmorChange($this);
@ -448,25 +444,4 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->xpManager = null; $this->xpManager = null;
parent::destroyCycles(); 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);
}
} }

View File

@ -66,6 +66,7 @@ use function sqrt;
use const M_PI; use const M_PI;
abstract class Living extends Entity{ abstract class Living extends Entity{
protected const DEFAULT_BREATH_TICKS = 300;
protected $gravity = 0.08; protected $gravity = 0.08;
protected $drag = 0.02; protected $drag = 0.02;
@ -85,6 +86,13 @@ abstract class Living extends Entity{
/** @var ArmorInventory */ /** @var ArmorInventory */
protected $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; abstract public function getName() : string;
protected function initEntity(CompoundTag $nbt) : void{ protected function initEntity(CompoundTag $nbt) : void{
@ -114,6 +122,8 @@ abstract class Living extends Entity{
$this->setHealth($health); $this->setHealth($health);
$this->setAirSupplyTicks($nbt->getShort("Air", self::DEFAULT_BREATH_TICKS));
/** @var CompoundTag[]|ListTag $activeEffectsTag */ /** @var CompoundTag[]|ListTag $activeEffectsTag */
$activeEffectsTag = $nbt->getListTag("ActiveEffects"); $activeEffectsTag = $nbt->getListTag("ActiveEffects");
if($activeEffectsTag !== null){ if($activeEffectsTag !== null){
@ -172,6 +182,8 @@ abstract class Living extends Entity{
$nbt = parent::saveNBT(); $nbt = parent::saveNBT();
$nbt->setFloat("Health", $this->getHealth()); $nbt->setFloat("Health", $this->getHealth());
$nbt->setShort("Air", $this->getAirSupplyTicks());
if(!empty($this->effectManager->all())){ if(!empty($this->effectManager->all())){
$effects = []; $effects = [];
foreach($this->effectManager->all() as $effect){ foreach($this->effectManager->all() as $effect){
@ -598,7 +610,7 @@ abstract class Living extends Entity{
* @return bool * @return bool
*/ */
public function isBreathing() : 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 * @param bool $value
*/ */
public function setBreathing(bool $value = true) : void{ 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 * @return int
*/ */
public function getAirSupplyTicks() : 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 * @param int $ticks
*/ */
public function setAirSupplyTicks(int $ticks) : void{ 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 * @return int
*/ */
public function getMaxAirSupplyTicks() : 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 * @param int $ticks
*/ */
public function setMaxAirSupplyTicks(int $ticks) : void{ 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); $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{ protected function onDispose() : void{
$this->armorInventory->removeAllViewers(); $this->armorInventory->removeAllViewers();
parent::onDispose(); parent::onDispose();

View File

@ -40,6 +40,11 @@ class Villager extends Living implements Ageable{
public $width = 0.6; public $width = 0.6;
public $height = 1.8; public $height = 1.8;
/** @var bool */
private $baby = false;
/** @var int */
private $profession = self::PROFESSION_FARMER;
public function getName() : string{ public function getName() : string{
return "Villager"; return "Villager";
} }
@ -70,14 +75,21 @@ class Villager extends Living implements Ageable{
* @param int $profession * @param int $profession
*/ */
public function setProfession(int $profession) : void{ public function setProfession(int $profession) : void{
$this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $profession); $this->profession = $profession; //TODO: validation
} }
public function getProfession() : int{ public function getProfession() : int{
return $this->propertyManager->getInt(EntityMetadataProperties::VARIANT); return $this->profession;
} }
public function isBaby() : bool{ 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);
} }
} }

View File

@ -27,9 +27,11 @@ use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
abstract class WaterAnimal extends Living implements Ageable{ abstract class WaterAnimal extends Living implements Ageable{
/** @var bool */
protected $baby = false;
public function isBaby() : bool{ public function isBaby() : bool{
return $this->getGenericFlag(EntityMetadataFlags::BABY); return $this->baby;
} }
public function canBreathe() : bool{ public function canBreathe() : bool{
@ -40,4 +42,9 @@ abstract class WaterAnimal extends Living implements Ageable{
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2); $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2);
$this->attack($ev); $this->attack($ev);
} }
protected function syncNetworkData() : void{
parent::syncNetworkData();
$this->propertyManager->setGenericFlag(EntityMetadataFlags::BABY, $this->baby);
}
} }

View File

@ -26,7 +26,6 @@ namespace pocketmine\entity\effect;
use pocketmine\entity\Living; use pocketmine\entity\Living;
use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectAddEvent;
use pocketmine\event\entity\EntityEffectRemoveEvent; use pocketmine\event\entity\EntityEffectRemoveEvent;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
use pocketmine\utils\Color; use pocketmine\utils\Color;
use function abs; use function abs;
@ -38,8 +37,14 @@ class EffectManager{
/** @var EffectInstance[] */ /** @var EffectInstance[] */
protected $effects = []; protected $effects = [];
/** @var Color */
protected $bubbleColor;
/** @var bool */
protected $onlyAmbientEffects = false;
public function __construct(Living $entity){ public function __construct(Living $entity){
$this->entity = $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)){ if(!empty($colors)){
$propManager->setInt(EntityMetadataProperties::POTION_COLOR, Color::mix(...$colors)->toARGB()); $this->bubbleColor = Color::mix(...$colors);
$propManager->setByte(EntityMetadataProperties::POTION_AMBIENT, $ambient ? 1 : 0); $this->onlyAmbientEffects = $ambient;
}else{ }else{
$propManager->setInt(EntityMetadataProperties::POTION_COLOR, 0); $this->bubbleColor = new Color(0, 0, 0, 0);
$propManager->setByte(EntityMetadataProperties::POTION_AMBIENT, 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{ public function tick(int $tickDiff = 1) : bool{
foreach($this->effects as $instance){ foreach($this->effects as $instance){
$type = $instance->getType(); $type = $instance->getType();

View File

@ -107,12 +107,15 @@ class ExperienceOrb extends Entity{
*/ */
protected $targetPlayerRuntimeId = null; protected $targetPlayerRuntimeId = null;
/** @var int */
protected $xpValue = 1;
protected function initEntity(CompoundTag $nbt) : void{ protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt); parent::initEntity($nbt);
$this->age = $nbt->getShort("Age", 0); $this->age = $nbt->getShort("Age", 0);
$value = 0; $value = 1;
if($nbt->hasTag(self::TAG_VALUE_PC, ShortTag::class)){ //PC if($nbt->hasTag(self::TAG_VALUE_PC, ShortTag::class)){ //PC
$value = $nbt->getShort(self::TAG_VALUE_PC); $value = $nbt->getShort(self::TAG_VALUE_PC);
}elseif($nbt->hasTag(self::TAG_VALUE_PE, IntTag::class)){ //PE save format }elseif($nbt->hasTag(self::TAG_VALUE_PE, IntTag::class)){ //PE save format
@ -134,14 +137,14 @@ class ExperienceOrb extends Entity{
} }
public function getXpValue() : int{ public function getXpValue() : int{
return $this->propertyManager->getInt(EntityMetadataProperties::EXPERIENCE_VALUE) ?? 0; return $this->xpValue;
} }
public function setXpValue(int $amount) : void{ public function setXpValue(int $amount) : void{
if($amount <= 0){ if($amount <= 0){
throw new \InvalidArgumentException("XP amount must be greater than 0, got $amount"); 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{ public function hasTargetPlayer() : bool{
@ -225,4 +228,10 @@ class ExperienceOrb extends Entity{
public function canBeCollidedWith() : bool{ public function canBeCollidedWith() : bool{
return false; return false;
} }
protected function syncNetworkData() : void{
parent::syncNetworkData();
$this->propertyManager->setInt(EntityMetadataProperties::EXPERIENCE_VALUE, $this->xpValue);
}
} }

View File

@ -71,8 +71,6 @@ class FallingBlock extends Entity{
$damage = $nbt->getByte("Data", 0); $damage = $nbt->getByte("Data", 0);
$this->block = BlockFactory::get($blockId, $damage); $this->block = BlockFactory::get($blockId, $damage);
$this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $this->block->getRuntimeId());
} }
public function canCollideWith(Entity $entity) : bool{ public function canCollideWith(Entity $entity) : bool{
@ -138,4 +136,10 @@ class FallingBlock extends Entity{
return $nbt; return $nbt;
} }
protected function syncNetworkData() : void{
parent::syncNetworkData();
$this->propertyManager->setInt(EntityMetadataProperties::VARIANT, $this->block->getRuntimeId());
}
} }

View File

@ -238,7 +238,7 @@ class ItemEntity extends Entity{
$pk->position = $this->asVector3(); $pk->position = $this->asVector3();
$pk->motion = $this->getMotion(); $pk->motion = $this->getMotion();
$pk->item = $this->getItem(); $pk->item = $this->getItem();
$pk->metadata = $this->propertyManager->getAll(); $pk->metadata = $this->getSyncedNetworkData(false);
$player->sendDataPacket($pk); $player->sendDataPacket($pk);
} }

View File

@ -28,7 +28,6 @@ use pocketmine\entity\Explosive;
use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\ExplosionPrimeEvent; use pocketmine\event\entity\ExplosionPrimeEvent;
use pocketmine\nbt\tag\CompoundTag; 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\EntityLegacyIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
@ -61,14 +60,7 @@ class PrimedTNT extends Entity implements Explosive{
protected function initEntity(CompoundTag $nbt) : void{ protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt); parent::initEntity($nbt);
if($nbt->hasTag("Fuse", ShortTag::class)){ $this->fuse = $nbt->getShort("Fuse", 80, true);
$this->fuse = $nbt->getShort("Fuse");
}else{
$this->fuse = 80;
}
$this->setGenericFlag(EntityMetadataFlags::IGNITED, true);
$this->propertyManager->setInt(EntityMetadataProperties::FUSE_LENGTH, $this->fuse);
$this->world->addSound($this, new IgniteSound()); $this->world->addSound($this, new IgniteSound());
} }
@ -92,10 +84,6 @@ class PrimedTNT extends Entity implements Explosive{
$hasUpdate = parent::entityBaseTick($tickDiff); $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()){ if(!$this->isFlaggedForDespawn()){
$this->fuse -= $tickDiff; $this->fuse -= $tickDiff;
@ -119,4 +107,11 @@ class PrimedTNT extends Entity implements Explosive{
$explosion->explodeB(); $explosion->explodeB();
} }
} }
protected function syncNetworkData() : void{
parent::syncNetworkData();
$this->propertyManager->setGenericFlag(EntityMetadataFlags::IGNITED, true);
$this->propertyManager->setInt(EntityMetadataProperties::FUSE_LENGTH, $this->fuse);
}
} }

View File

@ -67,6 +67,9 @@ class Arrow extends Projectile{
/** @var int */ /** @var int */
protected $collideTicks = 0; protected $collideTicks = 0;
/** @var bool */
protected $critical = false;
public function __construct(World $world, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){ public function __construct(World $world, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){
parent::__construct($world, $nbt, $shootingEntity); parent::__construct($world, $nbt, $shootingEntity);
$this->setCritical($critical); $this->setCritical($critical);
@ -87,11 +90,11 @@ class Arrow extends Projectile{
} }
public function isCritical() : bool{ public function isCritical() : bool{
return $this->getGenericFlag(EntityMetadataFlags::CRITICAL); return $this->critical;
} }
public function setCritical(bool $value = true) : void{ public function setCritical(bool $value = true) : void{
$this->setGenericFlag(EntityMetadataFlags::CRITICAL, $value); $this->critical = $value;
} }
public function getResultDamage() : int{ public function getResultDamage() : int{
@ -199,4 +202,10 @@ class Arrow extends Projectile{
$playerInventory->addItem(clone $item); $playerInventory->addItem(clone $item);
$this->flagForDespawn(); $this->flagForDespawn();
} }
protected function syncNetworkData() : void{
parent::syncNetworkData();
$this->propertyManager->setGenericFlag(EntityMetadataFlags::CRITICAL, $this->critical);
}
} }

View File

@ -49,10 +49,15 @@ class SplashPotion extends Throwable{
protected $gravity = 0.05; protected $gravity = 0.05;
protected $drag = 0.01; protected $drag = 0.01;
/** @var bool */
protected $linger = false;
/** @var int */
protected $potionId = Potion::WATER;
protected function initEntity(CompoundTag $nbt) : void{ protected function initEntity(CompoundTag $nbt) : void{
parent::initEntity($nbt); parent::initEntity($nbt);
$this->setPotionId($nbt->getShort("PotionId", 0)); $this->setPotionId($nbt->getShort("PotionId", Potion::WATER));
} }
public function saveNBT() : CompoundTag{ public function saveNBT() : CompoundTag{
@ -139,14 +144,14 @@ class SplashPotion extends Throwable{
* @return int * @return int
*/ */
public function getPotionId() : int{ public function getPotionId() : int{
return $this->propertyManager->getShort(EntityMetadataProperties::POTION_AUX_VALUE) ?? 0; return $this->potionId;
} }
/** /**
* @param int $id * @param int $id
*/ */
public function setPotionId(int $id) : void{ 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 * @return bool
*/ */
public function willLinger() : 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 * @param bool $value
*/ */
public function setLinger(bool $value = true) : void{ 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{ public function getPotionEffects() : array{
return Potion::getPotionEffectsById($this->getPotionId()); 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);
}
} }

View File

@ -31,6 +31,7 @@ use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\handler\PacketHandler; use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityLink; use pocketmine\network\mcpe\protocol\types\entity\EntityLink;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
use function array_search; use function array_search;
use function count; use function count;
@ -164,7 +165,7 @@ class AddActorPacket extends DataPacket implements ClientboundPacket{
/** @var Attribute[] */ /** @var Attribute[] */
public $attributes = []; public $attributes = [];
/** @var array */ /** @var MetadataProperty[] */
public $metadata = []; public $metadata = [];
/** @var EntityLink[] */ /** @var EntityLink[] */
public $links = []; public $links = [];

View File

@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\handler\PacketHandler; use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
class AddItemActorPacket extends DataPacket implements ClientboundPacket{ class AddItemActorPacket extends DataPacket implements ClientboundPacket{
public const NETWORK_ID = ProtocolInfo::ADD_ITEM_ACTOR_PACKET; public const NETWORK_ID = ProtocolInfo::ADD_ITEM_ACTOR_PACKET;
@ -42,7 +43,7 @@ class AddItemActorPacket extends DataPacket implements ClientboundPacket{
public $position; public $position;
/** @var Vector3|null */ /** @var Vector3|null */
public $motion; public $motion;
/** @var array */ /** @var MetadataProperty[] */
public $metadata = []; public $metadata = [];
/** @var bool */ /** @var bool */
public $isFromFishing = false; public $isFromFishing = false;

View File

@ -29,6 +29,7 @@ use pocketmine\item\Item;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\handler\PacketHandler; use pocketmine\network\mcpe\handler\PacketHandler;
use pocketmine\network\mcpe\protocol\types\entity\EntityLink; use pocketmine\network\mcpe\protocol\types\entity\EntityLink;
use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty;
use pocketmine\utils\UUID; use pocketmine\utils\UUID;
use function count; use function count;
@ -57,7 +58,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{
public $headYaw = null; //TODO public $headYaw = null; //TODO
/** @var Item */ /** @var Item */
public $item; public $item;
/** @var array */ /** @var MetadataProperty[] */
public $metadata = []; public $metadata = [];
//TODO: adventure settings stuff //TODO: adventure settings stuff

View File

@ -27,16 +27,18 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\handler\PacketHandler; 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 class SetActorDataPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{ //TODO: check why this is serverbound
public const NETWORK_ID = ProtocolInfo::SET_ACTOR_DATA_PACKET; public const NETWORK_ID = ProtocolInfo::SET_ACTOR_DATA_PACKET;
/** @var int */ /** @var int */
public $entityRuntimeId; public $entityRuntimeId;
/** @var array */ /** @var MetadataProperty[] */
public $metadata; public $metadata;
public static function create(int $entityRuntimeId, array $metadata) : self{ public static function create(int $entityRuntimeId, array $metadata) : self{
$result = new self; $result = new self;
$result->entityRuntimeId = $entityRuntimeId; $result->entityRuntimeId = $entityRuntimeId;
$result->metadata = $metadata; $result->metadata = $metadata;

View File

@ -0,0 +1,65 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class BlockPosMetadataProperty implements MetadataProperty{
/** @var Vector3 */
private $value;
/**
* @param Vector3 $value
*/
public function __construct(Vector3 $value){
$this->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);
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class ByteMetadataProperty implements MetadataProperty{
use IntegerishMetadataProperty;
protected function min() : int{
return -0x80;
}
protected function max() : int{
return 0x7f;
}
public static function id() : int{
return EntityMetadataTypes::BYTE;
}
public static function read(NetworkBinaryStream $in) : self{
return new self($in->getByte());
}
public function write(NetworkBinaryStream $out) : void{
$out->putByte($this->value);
}
}

View File

@ -25,51 +25,27 @@ namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataTypes; use function get_class;
use function assert;
use function is_float;
use function is_int;
use function is_string;
class EntityMetadataCollection{ class EntityMetadataCollection{
/** @var MetadataProperty[] */
private $properties = []; private $properties = [];
/** @var MetadataProperty[] */
private $dirtyProperties = []; private $dirtyProperties = [];
public function __construct(){ 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 $key
* @param int $value * @param int $value
* @param bool $force * @param bool $force
*/ */
public function setByte(int $key, int $value, bool $force = false) : void{ public function setByte(int $key, int $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::BYTE, $value, $force);
}
/** $this->set($key, new ByteMetadataProperty($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;
} }
/** /**
@ -78,18 +54,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setShort(int $key, int $value, bool $force = false) : void{ public function setShort(int $key, int $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::SHORT, $value, $force); $this->set($key, new ShortMetadataProperty($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;
} }
/** /**
@ -98,18 +63,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setInt(int $key, int $value, bool $force = false) : void{ public function setInt(int $key, int $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::INT, $value, $force); $this->set($key, new IntMetadataProperty($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;
} }
/** /**
@ -118,18 +72,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setFloat(int $key, float $value, bool $force = false) : void{ public function setFloat(int $key, float $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::FLOAT, $value, $force); $this->set($key, new FloatMetadataProperty($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;
} }
/** /**
@ -138,19 +81,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setString(int $key, string $value, bool $force = false) : void{ public function setString(int $key, string $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::STRING, $value, $force); $this->set($key, new StringMetadataProperty($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;
} }
/** /**
@ -159,18 +90,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setItem(int $key, Item $value, bool $force = false) : void{ public function setItem(int $key, Item $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::SLOT, $value, $force); $this->set($key, new ItemStackMetadataProperty($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;
} }
/** /**
@ -179,18 +99,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setBlockPos(int $key, ?Vector3 $value, bool $force = false) : void{ public function setBlockPos(int $key, ?Vector3 $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::POS, $value ? $value->floor() : null, $force); $this->set($key, new BlockPosMetadataProperty($value ?? new Vector3(0, 0, 0)), $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;
} }
/** /**
@ -199,18 +108,7 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setLong(int $key, int $value, bool $force = false) : void{ public function setLong(int $key, int $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::LONG, $value, $force); $this->set($key, new LongMetadataProperty($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;
} }
/** /**
@ -219,74 +117,60 @@ class EntityMetadataCollection{
* @param bool $force * @param bool $force
*/ */
public function setVector3(int $key, ?Vector3 $value, bool $force = false) : void{ public function setVector3(int $key, ?Vector3 $value, bool $force = false) : void{
$this->setPropertyValue($key, EntityMetadataTypes::VECTOR3F, $value ? $value->asVector3() : null, $force); $this->set($key, new Vec3MetadataProperty($value ?? new Vector3(0, 0, 0)), $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;
} }
/** /**
* @param int $key * @param int $key
* @param int $type
* @param mixed $value * @param mixed $value
* @param bool $force * @param bool $force
*/ */
public function setPropertyValue(int $key, int $type, $value, bool $force = false) : void{ public function set(int $key, MetadataProperty $value, bool $force = false) : void{
if(!$force){ if(!$force and isset($this->properties[$key]) and !($this->properties[$key] instanceof $value)){
$this->checkType($key, $type); 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. * Returns all properties.
* *
* @return array * @return MetadataProperty[]
*/ */
public function getAll() : array{ public function getAll() : array{
return $this->properties; return $this->properties;
@ -295,7 +179,7 @@ class EntityMetadataCollection{
/** /**
* Returns properties that have changed and need to be broadcasted. * Returns properties that have changed and need to be broadcasted.
* *
* @return array * @return MetadataProperty[]
*/ */
public function getDirty() : array{ public function getDirty() : array{
return $this->dirtyProperties; return $this->dirtyProperties;

View File

@ -0,0 +1,62 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class FloatMetadataProperty implements MetadataProperty{
/** @var float */
private $value;
/**
* @param float $value
*/
public function __construct(float $value){
$this->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);
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class IntMetadataProperty implements MetadataProperty{
use IntegerishMetadataProperty;
protected function min() : int{
return -0x80000000;
}
protected function max() : int{
return 0x7fffffff;
}
public static function id() : int{
return EntityMetadataTypes::INT;
}
public static function read(NetworkBinaryStream $in) : self{
return new self($in->getVarInt());
}
public function write(NetworkBinaryStream $out) : void{
$out->putVarInt($this->value);
}
}

View File

@ -0,0 +1,64 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
trait IntegerishMetadataProperty{
/** @var int */
private $value;
/**
* @param int $value
*/
public function __construct(int $value){
if($value < $this->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);
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\item\Item;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class ItemStackMetadataProperty implements MetadataProperty{
/** @var Item */
private $value;
/**
* @param Item $value
*/
public function __construct(Item $value){
$this->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);
}
}

View File

@ -0,0 +1,52 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
use const PHP_INT_MAX;
use const PHP_INT_MIN;
final class LongMetadataProperty implements MetadataProperty{
use IntegerishMetadataProperty;
protected function min() : int{
return PHP_INT_MIN;
}
protected function max() : int{
return PHP_INT_MAX;
}
public static function id() : int{
return EntityMetadataTypes::LONG;
}
public static function read(NetworkBinaryStream $in) : self{
return new self($in->getVarLong());
}
public function write(NetworkBinaryStream $out) : void{
$out->putVarLong($this->value);
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
interface MetadataProperty{
public static function id() : int;
public function write(NetworkBinaryStream $out) : void;
public function equals(MetadataProperty $other) : bool;
}

View File

@ -0,0 +1,50 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class ShortMetadataProperty implements MetadataProperty{
use IntegerishMetadataProperty;
protected function min() : int{
return -0x8000;
}
protected function max() : int{
return 0x7fff;
}
public static function id() : int{
return EntityMetadataTypes::SHORT;
}
public static function read(NetworkBinaryStream $in) : self{
return new self($in->getSignedLShort());
}
public function write(NetworkBinaryStream $out) : void{
$out->putLShort($this->value);
}
}

View File

@ -0,0 +1,54 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
final class StringMetadataProperty implements MetadataProperty{
/** @var string */
private $value;
/**
* @param string $value
*/
public function __construct(string $value){
$this->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;
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\entity;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\serializer\NetworkBinaryStream;
class Vec3MetadataProperty implements MetadataProperty{
/** @var Vector3 */
private $value;
/**
* @param Vector3 $value
*/
public function __construct(Vector3 $value){
$this->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);
}
}

View File

@ -37,8 +37,17 @@ use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\TreeRoot; use pocketmine\nbt\TreeRoot;
use pocketmine\network\BadPacketException; use pocketmine\network\BadPacketException;
use pocketmine\network\mcpe\protocol\types\command\CommandOriginData; 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\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\BinaryDataException;
use pocketmine\utils\BinaryStream; use pocketmine\utils\BinaryStream;
use pocketmine\utils\UUID; use pocketmine\utils\UUID;
@ -224,108 +233,51 @@ class NetworkBinaryStream extends BinaryStream{
/** /**
* Decodes entity metadata from the stream. * Decodes entity metadata from the stream.
* *
* @param bool $types Whether to include metadata types along with values in the returned array * @return MetadataProperty[]
*
* @return array
* *
* @throws BadPacketException * @throws BadPacketException
* @throws BinaryDataException * @throws BinaryDataException
*/ */
public function getEntityMetadata(bool $types = true) : array{ public function getEntityMetadata() : array{
$count = $this->getUnsignedVarInt(); $count = $this->getUnsignedVarInt();
$data = []; $data = [];
for($i = 0; $i < $count; ++$i){ for($i = 0; $i < $count; ++$i){
$key = $this->getUnsignedVarInt(); $key = $this->getUnsignedVarInt();
$type = $this->getUnsignedVarInt(); $type = $this->getUnsignedVarInt();
$value = null;
switch($type){ $data[$key] = $this->readMetadataProperty($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;
}
} }
return $data; 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. * Writes entity metadata to the packet buffer.
* *
* @param array $metadata * @param MetadataProperty[] $metadata
*/ */
public function putEntityMetadata(array $metadata) : void{ public function putEntityMetadata(array $metadata) : void{
$this->putUnsignedVarInt(count($metadata)); $this->putUnsignedVarInt(count($metadata));
foreach($metadata as $key => $d){ foreach($metadata as $key => $d){
$this->putUnsignedVarInt($key); //data key $this->putUnsignedVarInt($key);
$this->putUnsignedVarInt($d[0]); //data type $this->putUnsignedVarInt($d::id());
switch($d[0]){ $d->write($this);
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]);
}
} }
} }

View File

@ -752,12 +752,11 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
* @return bool * @return bool
*/ */
public function isUsingItem() : bool{ public function isUsingItem() : bool{
return $this->getGenericFlag(EntityMetadataFlags::ACTION) and $this->startAction > -1; return $this->startAction > -1;
} }
public function setUsingItem(bool $value){ public function setUsingItem(bool $value){
$this->startAction = $value ? $this->server->getTick() : -1; $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(); $b->setOccupied();
} }
$this->sleeping = clone $pos; $this->sleeping = $pos;
$this->propertyManager->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, $pos);
$this->setPlayerFlag(PlayerMetadataFlags::SLEEP, true);
$this->setSpawn($pos); $this->setSpawn($pos);
@ -1099,8 +1095,6 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
(new PlayerBedLeaveEvent($this, $b))->call(); (new PlayerBedLeaveEvent($this, $b))->call();
$this->sleeping = null; $this->sleeping = null;
$this->propertyManager->setBlockPos(EntityMetadataProperties::PLAYER_BED_POSITION, null);
$this->setPlayerFlag(PlayerMetadataFlags::SLEEP, false);
$this->world->setSleepTicks(0); $this->world->setSleepTicks(0);
@ -2367,6 +2361,15 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
parent::attack($source); 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{ public function broadcastEntityEvent(int $eventId, ?int $eventData = null, ?array $players = null) : void{
if($this->spawned and $players === null){ if($this->spawned and $players === null){
$players = $this->getViewers(); $players = $this->getViewers();

View File

@ -32,7 +32,8 @@ use pocketmine\network\mcpe\protocol\PlayerListPacket;
use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; 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\network\mcpe\protocol\types\PlayerListEntry;
use pocketmine\utils\UUID; use pocketmine\utils\UUID;
use function str_repeat; use function str_repeat;
@ -104,8 +105,8 @@ class FloatingTextParticle implements Particle{
1 << EntityMetadataFlags::IMMOBILE 1 << EntityMetadataFlags::IMMOBILE
); );
$pk->metadata = [ $pk->metadata = [
EntityMetadataProperties::FLAGS => [EntityMetadataTypes::LONG, $flags], EntityMetadataProperties::FLAGS => new LongMetadataProperty($flags),
EntityMetadataProperties::SCALE => [EntityMetadataTypes::FLOAT, 0.01] //zero causes problems on debug builds EntityMetadataProperties::SCALE => new FloatMetadataProperty(0.01) //zero causes problems on debug builds
]; ];
$p[] = $pk; $p[] = $pk;