diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 2803fa432..5e855a3ad 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1945,7 +1945,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } } - }elseif($item === null or $slot === -1 or !$item->equals($packet->item)){ // packet error or not implemented + }elseif($item === null or $slot === -1 or !$item->deepEquals($packet->item)){ // packet error or not implemented $this->inventory->sendContents($this); break; }elseif($this->isCreative()){ @@ -1985,14 +1985,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()->equals($packet->item)){ + }elseif(!$this->inventory->getItemInHand()->deepEquals($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->equals($oldItem, true) or $item->getCount() !== $oldItem->getCount()){ + if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ $this->inventory->setItemInHand($item, $this); $this->inventory->sendHeldItem($this->hasSpawned); } @@ -2015,7 +2015,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade if($this->isCreative()){ $item = $this->inventory->getItemInHand(); - }elseif(!$this->inventory->getItemInHand()->equals($packet->item)){ + }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ $this->inventory->sendHeldItem($this); break; }else{ @@ -2251,7 +2251,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)){ if($this->isSurvival()){ - if(!$item->equals($oldItem, true) or $item->getCount() !== $oldItem->getCount()){ + if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ $this->inventory->setItemInHand($item, $this); $this->inventory->sendHeldItem($this->hasSpawned); } @@ -2610,7 +2610,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $slot = -1; $checkDamage = $ingredient->getDamage() === null ? false : true; foreach($this->inventory->getContents() as $index => $i){ - if($ingredient->equals($i, $checkDamage) and ($i->getCount() - $used[$index]) >= 1){ + if($ingredient->deepEquals($i, $checkDamage) and ($i->getCount() - $used[$index]) >= 1){ $slot = $index; $used[$index]++; break; @@ -2723,7 +2723,7 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade break; } - if($transaction->getSourceItem()->equals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! + if($transaction->getSourceItem()->deepEquals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()){ //No changes! //No changes, just a local inventory update sent by the server break; } diff --git a/src/pocketmine/block/BurningFurnace.php b/src/pocketmine/block/BurningFurnace.php index c0d794252..ce7bc4d1d 100644 --- a/src/pocketmine/block/BurningFurnace.php +++ b/src/pocketmine/block/BurningFurnace.php @@ -82,6 +82,12 @@ class BurningFurnace extends Solid{ $nbt->CustomName = new String("CustomName", $item->getCustomName()); } + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; diff --git a/src/pocketmine/block/Chest.php b/src/pocketmine/block/Chest.php index 11e8c4db3..d3fc5cd1c 100644 --- a/src/pocketmine/block/Chest.php +++ b/src/pocketmine/block/Chest.php @@ -109,6 +109,12 @@ class Chest extends Transparent{ $nbt->CustomName = new String("CustomName", $item->getCustomName()); } + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + $tile = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); if($chest instanceof TileChest and $tile instanceof TileChest){ diff --git a/src/pocketmine/block/EnchantingTable.php b/src/pocketmine/block/EnchantingTable.php index 8b1d7f712..fb2768955 100644 --- a/src/pocketmine/block/EnchantingTable.php +++ b/src/pocketmine/block/EnchantingTable.php @@ -52,6 +52,12 @@ class EnchantingTable extends Transparent{ $nbt->CustomName = new String("CustomName", $item->getCustomName()); } + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; diff --git a/src/pocketmine/entity/Item.php b/src/pocketmine/entity/Item.php index a76d2216b..da3aed53d 100644 --- a/src/pocketmine/entity/Item.php +++ b/src/pocketmine/entity/Item.php @@ -70,6 +70,10 @@ class Item extends Entity{ if(isset($this->namedtag->Thrower)){ $this->thrower = $this->namedtag["Thrower"]; } + if(!isset($this->namedtag->Item)){ + $this->close(); + return; + } $this->item = NBT::getItemHelper($this->namedtag->Item); diff --git a/src/pocketmine/inventory/SimpleTransactionGroup.php b/src/pocketmine/inventory/SimpleTransactionGroup.php index c48fd48cc..595cff384 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->equals($sourceItem) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ + if(!$checkSourceItem->deepEquals($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->equals($haveItem)){ + if($needItem->deepEquals($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 b51b2abfb..5db473e76 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -968,6 +968,62 @@ class Item{ return $this->tags !== "" and $this->tags !== null; } + public function hasCustomBlockData(){ + if(!$this->hasCompoundTag()){ + return false; + } + + $tag = $this->getNamedTag(); + if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof Compound){ + return true; + } + + return false; + } + + public function clearCustomBlockData(){ + if(!$this->hasCompoundTag()){ + return $this; + } + $tag = $this->getNamedTag(); + + if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof Compound){ + unset($tag->display->BlockEntityTag); + $this->setNamedTag($tag); + } + + return $this; + } + + public function setCustomBlockData(Compound $compound){ + $tags = clone $compound; + $tags->setName("BlockEntityTag"); + + if(!$this->hasCompoundTag()){ + $tag = new Compound("", []); + }else{ + $tag = $this->getNamedTag(); + } + + $tag->BlockEntityTag = $tags; + $this->setNamedTag($tag); + + return $this; + } + + public function getCustomBlockData(){ + if(!$this->hasCompoundTag()){ + return null; + } + + $tag = $this->getNamedTag(); + if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof Compound){ + return $tag->BlockEntityTag; + } + + return null; + } + public function hasCustomName(){ if(!$this->hasCompoundTag()){ return false; @@ -1191,4 +1247,14 @@ class Item{ 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){ + if($item->equals($item)){ + return true; + }elseif($item->hasCompoundTag() or $this->hasCompoundTag()){ + return NBT::matchTree($this->getNamedTag(), $item->getNamedTag()); + } + + return false; + } + } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 6492d0e63..275e6365b 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -1727,7 +1727,8 @@ class Level implements ChunkManager, Metadatable{ } if($hand->getId() === Item::SIGN_POST or $hand->getId() === Item::WALL_SIGN){ - $tile = Tile::createTile("Sign", $this->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [ + + $nbt = new Compound("", [ "id" => new String("id", Tile::SIGN), "x" => new Int("x", $block->x), "y" => new Int("y", $block->y), @@ -1736,10 +1737,19 @@ class Level implements ChunkManager, Metadatable{ "Text2" => new String("Text2", ""), "Text3" => new String("Text3", ""), "Text4" => new String("Text4", "") - ])); + ]); + if($player !== null){ - $tile->namedtag->Creator = new String("Creator", $player->getRawUniqueId()); + $nbt->Creator = new String("Creator", $player->getRawUniqueId()); } + + if($item->hasCustomBlockData()){ + foreach($item->getCustomBlockData() as $key => $v){ + $nbt->{$key} = $v; + } + } + + Tile::createTile("Sign", $this->getChunk($block->x >> 4, $block->z >> 4), $nbt); } $item->setCount($item->getCount() - 1); if($item->getCount() <= 0){ diff --git a/src/pocketmine/nbt/NBT.php b/src/pocketmine/nbt/NBT.php index c2c92a685..16773c936 100644 --- a/src/pocketmine/nbt/NBT.php +++ b/src/pocketmine/nbt/NBT.php @@ -104,11 +104,11 @@ class NBT{ * @return Item */ public static function getItemHelper(Compound $tag){ - if(!isset($tag->id) or !isset($tag->Damage) or !isset($tag->Count)){ + if(!isset($tag->id) or !isset($tag->Count)){ return Item::get(0); } - $item = Item::get($tag->id->getValue(), $tag->Damage->getValue(), $tag->Count->getValue()); + $item = Item::get($tag->id->getValue(), !isset($tag->Damage) ? 0 : $tag->Damage->getValue(), $tag->Count->getValue()); if(isset($tag->tag) and $tag->tag instanceof Compound){ $item->setNamedTag($tag->tag); @@ -117,6 +117,70 @@ class NBT{ return $item; } + public static function matchList(Enum $tag1, Enum $tag2){ + if($tag1->getName() !== $tag2->getName() or $tag1->getCount() !== $tag2->getCount()){ + return false; + } + + foreach($tag1 as $k => $v){ + if(!($v instanceof Tag)){ + continue; + } + + if(!isset($tag2->{$k}) or !($tag2->{$k} instanceof $v)){ + return false; + } + + if($v instanceof Compound){ + if(!self::matchTree($v, $tag2->{$k})){ + return false; + } + }elseif($v instanceof Enum){ + if(!self::matchList($v, $tag2->{$k})){ + return false; + } + }else{ + if($v->getValue() !== $tag2->{$k}->getValue()){ + return false; + } + } + } + + return true; + } + + public static function matchTree(Compound $tag1, Compound $tag2){ + if($tag1->getName() !== $tag2->getName() or $tag1->getCount() !== $tag2->getCount()){ + return false; + } + + foreach($tag1 as $k => $v){ + if(!($v instanceof Tag)){ + continue; + } + + if(!isset($tag2->{$k}) or !($tag2->{$k} instanceof $v)){ + return false; + } + + if($v instanceof Compound){ + if(!self::matchTree($v, $tag2->{$k})){ + return false; + } + }elseif($v instanceof Enum){ + if(!self::matchList($v, $tag2->{$k})){ + return false; + } + }else{ + if($v->getValue() !== $tag2->{$k}->getValue()){ + return false; + } + } + } + + return true; + } + public function get($len){ if($len < 0){ $this->offset = strlen($this->buffer) - 1; diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index 9edab04a8..e8fe1729f 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -47,7 +47,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ $this->inventory = new ChestInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof Enum)){ - $this->namedtag->Items = new Enum("Inventory", []); + $this->namedtag->Items = new Enum("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } @@ -91,8 +91,8 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ - if($slot["Slot"] === $index){ - return $i; + if((int) $slot["Slot"] === (int) $index){ + return (int) $i; } }