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

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

View File

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

View File

@ -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();

View File

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

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = [];

View File

@ -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;

View File

@ -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

View File

@ -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;

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\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;

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

View File

@ -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();

View File

@ -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;