From 083d1e9ef88afab9449fde8b5eed7760dc9ad2d1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 12 Mar 2017 14:46:34 +0000 Subject: [PATCH] Deprecated Item->deepEquals(), added automatic deep checking in equals(), added some documentation for Item API methods --- src/pocketmine/Player.php | 16 +- .../inventory/SimpleTransactionGroup.php | 4 +- src/pocketmine/item/Item.php | 180 ++++++++++++++++-- 3 files changed, 179 insertions(+), 21 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 89f88186d..a15728385 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2013,14 +2013,14 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true){ break; } - }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ + }elseif(!$this->inventory->getItemInHand()->equals($packet->item)){ $this->inventory->sendHeldItem($this); }else{ $item = $this->inventory->getItemInHand(); $oldItem = clone $item; //TODO: Implement adventure mode checks if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this)){ - if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ + if(!$item->equals($oldItem) or $item->getCount() !== $oldItem->getCount()){ $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } @@ -2047,7 +2047,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->isCreative()){ $item = $this->inventory->getItemInHand(); - }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ + }elseif(!$this->inventory->getItemInHand()->equals($packet->item)){ $this->inventory->sendHeldItem($this); break; }else{ @@ -2329,7 +2329,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this, true)){ if($this->isSurvival()){ - if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ + if(!$item->equals($oldItem) or $item->getCount() !== $oldItem->getCount()){ $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } @@ -2650,7 +2650,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $item = $packet->input[$y * 3 + $x]; $ingredient = $recipe->getIngredient($x, $y); if($item->getCount() > 0){ - if($ingredient === null or !$ingredient->deepEquals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag())){ + if($ingredient === null or !$ingredient->equals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag())){ $canCraft = false; break; } @@ -2666,7 +2666,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $item = clone $packet->input[$y * 3 + $x]; foreach($needed as $k => $n){ - if($n->deepEquals($item, !$n->hasAnyDamageValue(), $n->hasCompoundTag())){ + if($n->equals($item, !$n->hasAnyDamageValue(), $n->hasCompoundTag())){ $remove = min($n->getCount(), $item->getCount()); $n->setCount($n->getCount() - $remove); $item->setCount($item->getCount() - $remove); @@ -2695,7 +2695,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $ingredients = $packet->input; $result = $packet->output[0]; - if(!$canCraft or !$recipe->getResult()->deepEquals($result)){ + if(!$canCraft or !$recipe->getResult()->equals($result)){ $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": expected " . $recipe->getResult() . ", got " . $result . ", using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; @@ -2832,7 +2832,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } - if($transaction->getSourceItem()->deepEquals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! + if($transaction->getSourceItem()->equals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! //No changes, just a local inventory update sent by the client break; } diff --git a/src/pocketmine/inventory/SimpleTransactionGroup.php b/src/pocketmine/inventory/SimpleTransactionGroup.php index cd5c23d28..4e8811cf9 100644 --- a/src/pocketmine/inventory/SimpleTransactionGroup.php +++ b/src/pocketmine/inventory/SimpleTransactionGroup.php @@ -98,7 +98,7 @@ class SimpleTransactionGroup implements TransactionGroup{ } $checkSourceItem = $ts->getInventory()->getItem($ts->getSlot()); $sourceItem = $ts->getSourceItem(); - if(!$checkSourceItem->deepEquals($sourceItem) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ + if(!$checkSourceItem->equals($sourceItem) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ return false; } if($sourceItem->getId() !== Item::AIR){ @@ -108,7 +108,7 @@ class SimpleTransactionGroup implements TransactionGroup{ foreach($needItems as $i => $needItem){ foreach($haveItems as $j => $haveItem){ - if($needItem->deepEquals($haveItem)){ + if($needItem->equals($haveItem)){ $amount = min($needItem->getCount(), $haveItem->getCount()); $needItem->setCount($needItem->getCount() - $amount); $haveItem->setCount($haveItem->getCount() - $amount); diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index cdef7b45b..c5ef917a8 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -351,6 +351,12 @@ class Item implements ItemIds, \JsonSerializable{ } } + /** + * @param int $id + * @param int $meta + * @param int $count + * @param string $name + */ public function __construct(int $id, int $meta = 0, int $count = 1, string $name = "Unknown"){ $this->id = $id & 0xffff; $this->meta = $meta !== -1 ? $meta & 0xffff : -1; @@ -362,6 +368,13 @@ class Item implements ItemIds, \JsonSerializable{ } } + /** + * Sets the Item's NBT + * + * @param CompoundTag|string $tags + * + * @return $this + */ public function setCompoundTag($tags){ if($tags instanceof CompoundTag){ $this->setNamedTag($tags); @@ -374,16 +387,24 @@ class Item implements ItemIds, \JsonSerializable{ } /** + * Returns the serialized NBT of the Item * @return string */ public function getCompoundTag() : string{ return $this->tags; } + /** + * Returns whether this Item has a non-empty NBT. + * @return bool + */ public function hasCompoundTag() : bool{ return $this->tags !== ""; } + /** + * @return bool + */ public function hasCustomBlockData() : bool{ if(!$this->hasCompoundTag()){ return false; @@ -411,6 +432,11 @@ class Item implements ItemIds, \JsonSerializable{ return $this; } + /** + * @param CompoundTag $compound + * + * @return $this + */ public function setCustomBlockData(CompoundTag $compound){ $tags = clone $compound; $tags->setName("BlockEntityTag"); @@ -427,6 +453,9 @@ class Item implements ItemIds, \JsonSerializable{ return $this; } + /** + * @return CompoundTag|null + */ public function getCustomBlockData(){ if(!$this->hasCompoundTag()){ return null; @@ -440,6 +469,9 @@ class Item implements ItemIds, \JsonSerializable{ return null; } + /** + * @return bool + */ public function hasEnchantments() : bool{ if(!$this->hasCompoundTag()){ return false; @@ -457,7 +489,7 @@ class Item implements ItemIds, \JsonSerializable{ } /** - * @param $id + * @param int $id * * @return Enchantment|null */ @@ -534,6 +566,9 @@ class Item implements ItemIds, \JsonSerializable{ return $enchantments; } + /** + * @return bool + */ public function hasCustomName() : bool{ if(!$this->hasCompoundTag()){ return false; @@ -550,6 +585,9 @@ class Item implements ItemIds, \JsonSerializable{ return false; } + /** + * @return string + */ public function getCustomName() : string{ if(!$this->hasCompoundTag()){ return ""; @@ -566,6 +604,11 @@ class Item implements ItemIds, \JsonSerializable{ return ""; } + /** + * @param string $name + * + * @return $this + */ public function setCustomName(string $name){ if($name === ""){ $this->clearCustomName(); @@ -590,6 +633,9 @@ class Item implements ItemIds, \JsonSerializable{ return $this; } + /** + * @return $this + */ public function clearCustomName(){ if(!$this->hasCompoundTag()){ return $this; @@ -621,6 +667,10 @@ class Item implements ItemIds, \JsonSerializable{ return null; } + /** + * Returns a tree of Tag objects representing the Item's NBT + * @return null|CompoundTag + */ public function getNamedTag(){ if(!$this->hasCompoundTag()){ return null; @@ -630,6 +680,12 @@ class Item implements ItemIds, \JsonSerializable{ return $this->cachedNBT = self::parseCompoundTag($this->tags); } + /** + * Sets the Item's NBT from the supplied CompoundTag object. + * @param CompoundTag $tag + * + * @return $this + */ public function setNamedTag(CompoundTag $tag){ if($tag->getCount() === 0){ return $this->clearNamedTag(); @@ -641,37 +697,73 @@ class Item implements ItemIds, \JsonSerializable{ return $this; } + /** + * Removes the Item's NBT. + * @return Item + */ public function clearNamedTag(){ return $this->setCompoundTag(""); } + /** + * @return int + */ public function getCount() : int{ return $this->count; } + /** + * @param int $count + */ public function setCount(int $count){ $this->count = $count; } + /** + * Returns the name of the item, or the custom name if it is set. + * @return string + */ final public function getName() : string{ return $this->hasCustomName() ? $this->getCustomName() : $this->name; } + /** + * @return bool + */ final public function canBePlaced() : bool{ return $this->block !== null and $this->block->canBePlaced(); } + /** + * Returns whether an entity can eat or drink this item. + * @return bool + */ public function canBeConsumed() : bool{ return false; } + /** + * Returns whether this item can be consumed by the supplied Entity. + * @param Entity $entity + * + * @return bool + */ public function canBeConsumedBy(Entity $entity) : bool{ return $this->canBeConsumed(); } + /** + * Called when the item is consumed by an Entity. + * @param Entity $entity + */ public function onConsume(Entity $entity){ + } + /** + * Returns the block corresponding to this Item. + * @return Block + */ public function getBlock() : Block{ if($this->block instanceof Block){ return clone $this->block; @@ -680,22 +772,41 @@ class Item implements ItemIds, \JsonSerializable{ } } + /** + * @return int + */ final public function getId() : int{ return $this->id; } + /** + * @return int + */ final public function getDamage() : int{ return $this->meta; } + /** + * @param int $meta + */ public function setDamage(int $meta){ $this->meta = $meta !== -1 ? $meta & 0xFFFF : -1; } + /** + * Returns whether this item can match any item with an equivalent ID with any meta value. + * Used in crafting recipes which accept multiple variants of the same item, for example crafting tables recipes. + * + * @return bool + */ public function hasAnyDamageValue() : bool{ return $this->meta === -1; } + /** + * Returns the highest amount of this item which will fit into one inventory slot. + * @return int + */ public function getMaxStackSize(){ return 64; } @@ -762,28 +873,75 @@ class Item implements ItemIds, \JsonSerializable{ return 1; } + /** + * Called when a player uses this item on a block. + * + * @param Level $level + * @param Player $player + * @param Block $block + * @param Block $target + * @param int $face + * @param float $fx + * @param float $fy + * @param float $fz + * + * @return bool + */ public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ return false; } + /** + * Compares an Item to this Item and check if they match. + * + * @param Item $item + * @param bool $checkDamage Whether to verify that the damage values match. + * @param bool $checkCompound Whether to verify that the items' NBT match. + * + * @return bool + */ public final function equals(Item $item, bool $checkDamage = true, bool $checkCompound = true) : bool{ - return $this->id === $item->getId() and ($checkDamage === false or $this->getDamage() === $item->getDamage()) and ($checkCompound === false or $this->getCompoundTag() === $item->getCompoundTag()); - } - - public final function deepEquals(Item $item, bool $checkDamage = true, bool $checkCompound = true) : bool{ - if($this->equals($item, $checkDamage, $checkCompound)){ - return true; - }elseif($item->hasCompoundTag() and $this->hasCompoundTag()){ - return NBT::matchTree($this->getNamedTag(), $item->getNamedTag()); + if($this->id === $item->getId() and ($checkDamage === false or $this->getDamage() === $item->getDamage())){ + if($checkCompound){ + if($item->getCompoundTag() === $this->getCompoundTag()){ + return true; + }elseif($this->hasCompoundTag() and $item->hasCompoundTag()){ + //Serialized NBT didn't match, check the cached object tree. + return NBT::matchTree($this->getNamedTag(), $item->getNamedTag()); + } + }else{ + return true; + } } return false; } - final public function __toString() : string{ - return "Item " . $this->name . " (" . $this->id . ":" . ($this->meta === null ? "?" : $this->meta) . ")x" . $this->count . ($this->hasCompoundTag() ? " tags:0x" . bin2hex($this->getCompoundTag()) : ""); + /** + * @deprecated Use {@link Item#equals} instead, this method will be removed in the future. + * + * @param Item $item + * @param bool $checkDamage + * @param bool $checkCompound + * + * @return bool + */ + public final function deepEquals(Item $item, bool $checkDamage = true, bool $checkCompound = true) : bool{ + return $this->equals($item, $checkDamage, $checkCompound); } + /** + * @return string + */ + final public function __toString() : string{ + return "Item " . $this->name . " (" . $this->id . ":" . ($this->hasAnyDamageValue() ? "?" : $this->meta) . ")x" . $this->count . ($this->hasCompoundTag() ? " tags:0x" . bin2hex($this->getCompoundTag()) : ""); + } + + /** + * Returns an array of item stack properties that can be serialized to json. + * + * @return array + */ final public function jsonSerialize(){ return [ "id" => $this->id,