read($tag); $data = self::$cachedParser->getData(); if(!($data instanceof CompoundTag)){ throw new \InvalidArgumentException("Invalid item NBT string given, it could not be deserialized"); } return $data; } private static function writeCompoundTag(CompoundTag $tag) : string{ if(self::$cachedParser === null){ self::$cachedParser = new NBT(NBT::LITTLE_ENDIAN); } self::$cachedParser->setData($tag); return self::$cachedParser->write(); } /** @var \SplFixedArray */ public static $list = null; /** @var Block|null */ protected $block; /** @var int */ protected $id; /** @var int */ protected $meta; /** @var string */ private $tags = ""; /** @var CompoundTag|null */ private $cachedNBT = null; /** @var int */ public $count; /** @var string */ protected $name; public function canBeActivated(){ return false; } public static function init(){ if(self::$list === null){ self::$list = new \SplFixedArray(65536); self::$list[self::IRON_SHOVEL] = IronShovel::class; self::$list[self::IRON_PICKAXE] = IronPickaxe::class; self::$list[self::IRON_AXE] = IronAxe::class; self::$list[self::FLINT_STEEL] = FlintSteel::class; self::$list[self::APPLE] = Apple::class; self::$list[self::BOW] = Bow::class; self::$list[self::ARROW] = Arrow::class; self::$list[self::COAL] = Coal::class; self::$list[self::DIAMOND] = Diamond::class; self::$list[self::IRON_INGOT] = IronIngot::class; self::$list[self::GOLD_INGOT] = GoldIngot::class; self::$list[self::IRON_SWORD] = IronSword::class; self::$list[self::WOODEN_SWORD] = WoodenSword::class; self::$list[self::WOODEN_SHOVEL] = WoodenShovel::class; self::$list[self::WOODEN_PICKAXE] = WoodenPickaxe::class; self::$list[self::WOODEN_AXE] = WoodenAxe::class; self::$list[self::STONE_SWORD] = StoneSword::class; self::$list[self::STONE_SHOVEL] = StoneShovel::class; self::$list[self::STONE_PICKAXE] = StonePickaxe::class; self::$list[self::STONE_AXE] = StoneAxe::class; self::$list[self::DIAMOND_SWORD] = DiamondSword::class; self::$list[self::DIAMOND_SHOVEL] = DiamondShovel::class; self::$list[self::DIAMOND_PICKAXE] = DiamondPickaxe::class; self::$list[self::DIAMOND_AXE] = DiamondAxe::class; self::$list[self::STICK] = Stick::class; self::$list[self::BOWL] = Bowl::class; self::$list[self::MUSHROOM_STEW] = MushroomStew::class; self::$list[self::GOLD_SWORD] = GoldSword::class; self::$list[self::GOLD_SHOVEL] = GoldShovel::class; self::$list[self::GOLD_PICKAXE] = GoldPickaxe::class; self::$list[self::GOLD_AXE] = GoldAxe::class; self::$list[self::STRING] = StringItem::class; self::$list[self::FEATHER] = Feather::class; self::$list[self::GUNPOWDER] = Gunpowder::class; self::$list[self::WOODEN_HOE] = WoodenHoe::class; self::$list[self::STONE_HOE] = StoneHoe::class; self::$list[self::IRON_HOE] = IronHoe::class; self::$list[self::DIAMOND_HOE] = DiamondHoe::class; self::$list[self::GOLD_HOE] = GoldHoe::class; self::$list[self::WHEAT_SEEDS] = WheatSeeds::class; self::$list[self::WHEAT] = Wheat::class; self::$list[self::BREAD] = Bread::class; self::$list[self::LEATHER_CAP] = LeatherCap::class; self::$list[self::LEATHER_TUNIC] = LeatherTunic::class; self::$list[self::LEATHER_PANTS] = LeatherPants::class; self::$list[self::LEATHER_BOOTS] = LeatherBoots::class; self::$list[self::CHAIN_HELMET] = ChainHelmet::class; self::$list[self::CHAIN_CHESTPLATE] = ChainChestplate::class; self::$list[self::CHAIN_LEGGINGS] = ChainLeggings::class; self::$list[self::CHAIN_BOOTS] = ChainBoots::class; self::$list[self::IRON_HELMET] = IronHelmet::class; self::$list[self::IRON_CHESTPLATE] = IronChestplate::class; self::$list[self::IRON_LEGGINGS] = IronLeggings::class; self::$list[self::IRON_BOOTS] = IronBoots::class; self::$list[self::DIAMOND_HELMET] = DiamondHelmet::class; self::$list[self::DIAMOND_CHESTPLATE] = DiamondChestplate::class; self::$list[self::DIAMOND_LEGGINGS] = DiamondLeggings::class; self::$list[self::DIAMOND_BOOTS] = DiamondBoots::class; self::$list[self::GOLD_HELMET] = GoldHelmet::class; self::$list[self::GOLD_CHESTPLATE] = GoldChestplate::class; self::$list[self::GOLD_LEGGINGS] = GoldLeggings::class; self::$list[self::GOLD_BOOTS] = GoldBoots::class; self::$list[self::FLINT] = Flint::class; self::$list[self::RAW_PORKCHOP] = RawPorkchop::class; self::$list[self::COOKED_PORKCHOP] = CookedPorkchop::class; self::$list[self::PAINTING] = Painting::class; self::$list[self::GOLDEN_APPLE] = GoldenApple::class; self::$list[self::SIGN] = Sign::class; self::$list[self::WOODEN_DOOR] = WoodenDoor::class; self::$list[self::BUCKET] = Bucket::class; self::$list[self::MINECART] = Minecart::class; self::$list[self::IRON_DOOR] = IronDoor::class; self::$list[self::REDSTONE] = Redstone::class; self::$list[self::SNOWBALL] = Snowball::class; self::$list[self::BOAT] = Boat::class; self::$list[self::LEATHER] = Leather::class; self::$list[self::BRICK] = Brick::class; self::$list[self::CLAY] = Clay::class; self::$list[self::SUGARCANE] = Sugarcane::class; self::$list[self::PAPER] = Paper::class; self::$list[self::BOOK] = Book::class; self::$list[self::SLIMEBALL] = Slimeball::class; self::$list[self::EGG] = Egg::class; self::$list[self::COMPASS] = Compass::class; self::$list[self::FISHING_ROD] = FishingRod::class; self::$list[self::CLOCK] = Clock::class; self::$list[self::GLOWSTONE_DUST] = GlowstoneDust::class; self::$list[self::RAW_FISH] = Fish::class; self::$list[self::COOKED_FISH] = CookedFish::class; self::$list[self::DYE] = Dye::class; self::$list[self::BONE] = Bone::class; self::$list[self::SUGAR] = Sugar::class; self::$list[self::CAKE] = Cake::class; self::$list[self::BED] = Bed::class; self::$list[self::COOKIE] = Cookie::class; self::$list[self::SHEARS] = Shears::class; self::$list[self::MELON] = Melon::class; self::$list[self::PUMPKIN_SEEDS] = PumpkinSeeds::class; self::$list[self::MELON_SEEDS] = MelonSeeds::class; self::$list[self::RAW_BEEF] = RawBeef::class; self::$list[self::STEAK] = Steak::class; self::$list[self::RAW_CHICKEN] = RawChicken::class; self::$list[self::COOKED_CHICKEN] = CookedChicken::class; self::$list[self::GOLD_NUGGET] = GoldNugget::class; self::$list[self::NETHER_WART] = NetherWart::class; self::$list[self::POTION] = Potion::class; self::$list[self::GLASS_BOTTLE] = GlassBottle::class; self::$list[self::SPIDER_EYE] = SpiderEye::class; self::$list[self::FERMENTED_SPIDER_EYE] = FermentedSpiderEye::class; self::$list[self::BLAZE_POWDER] = BlazePowder::class; self::$list[self::MAGMA_CREAM] = MagmaCream::class; self::$list[self::BREWING_STAND] = BrewingStand::class; self::$list[self::GLISTERING_MELON] = GlisteringMelon::class; self::$list[self::SPAWN_EGG] = SpawnEgg::class; self::$list[self::EMERALD] = Emerald::class; self::$list[self::ITEM_FRAME] = ItemFrame::class; self::$list[self::FLOWER_POT] = FlowerPot::class; self::$list[self::CARROT] = Carrot::class; self::$list[self::POTATO] = Potato::class; self::$list[self::BAKED_POTATO] = BakedPotato::class; self::$list[self::GOLDEN_CARROT] = GoldenCarrot::class; self::$list[self::MOB_HEAD] = MobHead::class; self::$list[self::PUMPKIN_PIE] = PumpkinPie::class; self::$list[self::NETHER_BRICK] = NetherBrick::class; self::$list[self::QUARTZ] = Quartz::class; self::$list[self::QUARTZ] = NetherQuartz::class; self::$list[self::COOKED_RABBIT] = CookedRabbit::class; // self::$list[self::CAMERA] = Camera::class; self::$list[self::BEETROOT] = Beetroot::class; self::$list[self::BEETROOT_SEEDS] = BeetrootSeeds::class; self::$list[self::BEETROOT_SOUP] = BeetrootSoup::class; self::$list[self::PRISMARINE_CRYSTALS] = PrismarineCrystals::class; self::$list[self::PRISMARINE_SHARD] = PrismarineShard::class; self::$list[self::NETHER_STAR] = NetherStar::class; self::$list[self::ENCHANTED_GOLDEN_APPLE] = GoldenAppleEnchanted::class; for($i = 0; $i < 256; ++$i){ if(Block::$list[$i] !== null){ self::$list[$i] = Block::$list[$i]; } } } self::initCreativeItems(); } private static $creative = []; private static function initCreativeItems(){ self::clearCreativeItems(); $creativeItems = new Config(Server::getInstance()->getFilePath() . "src/pocketmine/resources/creativeitems.json", Config::JSON, []); foreach($creativeItems->getAll() as $data){ $item = Item::get($data["id"], $data["damage"], $data["count"], $data["nbt"]); if($item->getName() === "Unknown"){ continue; } self::addCreativeItem($item); } } public static function clearCreativeItems(){ Item::$creative = []; } public static function getCreativeItems() : array{ return Item::$creative; } public static function addCreativeItem(Item $item){ Item::$creative[] = clone $item; } public static function removeCreativeItem(Item $item){ $index = self::getCreativeItemIndex($item); if($index !== -1){ unset(Item::$creative[$index]); } } public static function isCreativeItem(Item $item) : bool{ foreach(Item::$creative as $i => $d){ if($item->equals($d, !$item->isTool())){ return true; } } return false; } /** * @param $index * * @return Item|null */ public static function getCreativeItem(int $index){ return Item::$creative[$index] ?? null; } public static function getCreativeItemIndex(Item $item) : int{ foreach(Item::$creative as $i => $d){ if($item->equals($d, !$item->isTool())){ return $i; } } return -1; } /** * Returns an instance of the Item with the specified id, meta, count and NBT. * * @param int $id * @param int $meta * @param int $count * @param CompoundTag|string $tags * * @return Item */ public static function get(int $id, int $meta = 0, int $count = 1, $tags = "") : Item{ try{ $class = self::$list[$id]; if($class === null){ return (new Item($id, $meta, $count))->setCompoundTag($tags); }elseif($id < 256){ return (new ItemBlock(new $class($meta), $meta, $count))->setCompoundTag($tags); }else{ return (new $class($meta, $count))->setCompoundTag($tags); } }catch(\RuntimeException $e){ return (new Item($id, $meta, $count))->setCompoundTag($tags); } } /** * @param string $str * @param bool $multiple * * @return Item[]|Item */ public static function fromString(string $str, bool $multiple = false){ if($multiple === true){ $blocks = []; foreach(explode(",", $str) as $b){ $blocks[] = self::fromString($b, false); } return $blocks; }else{ $b = explode(":", str_replace([" ", "minecraft:"], ["_", ""], trim($str))); if(!isset($b[1])){ $meta = 0; }else{ $meta = $b[1] & 0xFFFF; } if(defined(Item::class . "::" . strtoupper($b[0]))){ $item = self::get(constant(Item::class . "::" . strtoupper($b[0])), $meta); if($item->getId() === self::AIR and strtoupper($b[0]) !== "AIR"){ $item = self::get($b[0] & 0xFFFF, $meta); } }else{ $item = self::get($b[0] & 0xFFFF, $meta); } return $item; } } /** * @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; $this->count = $count; $this->name = $name; if(!isset($this->block) and $this->id <= 0xff and isset(Block::$list[$this->id])){ $this->block = Block::get($this->id, $this->meta); $this->name = $this->block->getName(); } } /** * Sets the Item's NBT * * @param CompoundTag|string $tags * * @return $this */ public function setCompoundTag($tags){ if($tags instanceof CompoundTag){ $this->setNamedTag($tags); }else{ $this->tags = (string) $tags; $this->cachedNBT = null; } return $this; } /** * 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; } $tag = $this->getNamedTag(); if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof CompoundTag){ return true; } return false; } public function clearCustomBlockData(){ if(!$this->hasCompoundTag()){ return $this; } $tag = $this->getNamedTag(); if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof CompoundTag){ unset($tag->BlockEntityTag); $this->setNamedTag($tag); } return $this; } /** * @param CompoundTag $compound * * @return $this */ public function setCustomBlockData(CompoundTag $compound){ $tags = clone $compound; $tags->setName("BlockEntityTag"); if(!$this->hasCompoundTag()){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } $tag->BlockEntityTag = $tags; $this->setNamedTag($tag); return $this; } /** * @return CompoundTag|null */ public function getCustomBlockData(){ if(!$this->hasCompoundTag()){ return null; } $tag = $this->getNamedTag(); if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof CompoundTag){ return $tag->BlockEntityTag; } return null; } /** * @return bool */ public function hasEnchantments() : bool{ if(!$this->hasCompoundTag()){ return false; } $tag = $this->getNamedTag(); if(isset($tag->ench)){ $tag = $tag->ench; if($tag instanceof ListTag){ return true; } } return false; } /** * @param int $id * * @return Enchantment|null */ public function getEnchantment(int $id){ if(!$this->hasEnchantments()){ return null; } foreach($this->getNamedTag()->ench as $entry){ if($entry["id"] === $id){ $e = Enchantment::getEnchantment($entry["id"]); $e->setLevel($entry["lvl"]); return $e; } } return null; } /** * @param Enchantment $ench */ public function addEnchantment(Enchantment $ench){ if(!$this->hasCompoundTag()){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } if(!isset($tag->ench)){ $tag->ench = new ListTag("ench", []); $tag->ench->setTagType(NBT::TAG_Compound); } $found = false; foreach($tag->ench as $k => $entry){ if($entry["id"] === $ench->getId()){ $tag->ench->{$k} = new CompoundTag("", [ new ShortTag("id", $ench->getId()), new ShortTag("lvl", $ench->getLevel()) ]); $found = true; break; } } if(!$found){ $tag->ench->{count($tag->ench) + 1} = new CompoundTag("", [ new ShortTag("id", $ench->getId()), new ShortTag("lvl", $ench->getLevel()) ]); } $this->setNamedTag($tag); } /** * @return Enchantment[] */ public function getEnchantments() : array{ if(!$this->hasEnchantments()){ return []; } $enchantments = []; foreach($this->getNamedTag()->ench as $entry){ $e = Enchantment::getEnchantment($entry["id"]); $e->setLevel($entry["lvl"]); $enchantments[] = $e; } return $enchantments; } /** * @return bool */ public function hasCustomName() : bool{ if(!$this->hasCompoundTag()){ return false; } $tag = $this->getNamedTag(); if(isset($tag->display)){ $tag = $tag->display; if($tag instanceof CompoundTag and isset($tag->Name) and $tag->Name instanceof StringTag){ return true; } } return false; } /** * @return string */ public function getCustomName() : string{ if(!$this->hasCompoundTag()){ return ""; } $tag = $this->getNamedTag(); if(isset($tag->display)){ $tag = $tag->display; if($tag instanceof CompoundTag and isset($tag->Name) and $tag->Name instanceof StringTag){ return $tag->Name->getValue(); } } return ""; } /** * @param string $name * * @return $this */ public function setCustomName(string $name){ if($name === ""){ $this->clearCustomName(); } if(!$this->hasCompoundTag()){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } if(isset($tag->display) and $tag->display instanceof CompoundTag){ $tag->display->Name = new StringTag("Name", $name); }else{ $tag->display = new CompoundTag("display", [ new StringTag("Name", $name) ]); } $this->setCompoundTag($tag); return $this; } /** * @return $this */ public function clearCustomName(){ if(!$this->hasCompoundTag()){ return $this; } $tag = $this->getNamedTag(); if(isset($tag->display) and $tag->display instanceof CompoundTag){ unset($tag->display->Name); if($tag->display->getCount() === 0){ unset($tag->display); } $this->setNamedTag($tag); } return $this; } public function getLore() : array{ $tag = $this->getNamedTagEntry("display"); if($tag instanceof CompoundTag and isset($tag->Lore) and $tag->Lore instanceof ListTag){ $lines = []; foreach($tag->Lore->getValue() as $line){ $lines[] = $line->getValue(); } return $lines; } return []; } /** * @param string[] $lines * * @return $this */ public function setLore(array $lines){ $tag = $this->getNamedTag() ?? new CompoundTag("", []); if(!isset($tag->display)){ $tag->display = new CompoundTag("display", []); } $tag->display->Lore = new ListTag("Lore"); $tag->display->Lore->setTagType(NBT::TAG_String); $count = 0; foreach($lines as $line){ $tag->display->Lore[$count++] = new StringTag("", $line); } $this->setNamedTag($tag); return $this; } /** * @param $name * @return Tag|null */ public function getNamedTagEntry($name){ $tag = $this->getNamedTag(); if($tag !== null){ return $tag->{$name} ?? null; } return null; } /** * Returns a tree of Tag objects representing the Item's NBT * @return null|CompoundTag */ public function getNamedTag(){ if(!$this->hasCompoundTag()){ return null; }elseif($this->cachedNBT !== null){ return $this->cachedNBT; } 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(); } $this->cachedNBT = $tag; $this->tags = self::writeCompoundTag($tag); 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; }else{ return Block::get(self::AIR); } } /** * @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; } final public function getFuelTime(){ if(!isset(Fuel::$duration[$this->id])){ return null; } if($this->id !== self::BUCKET or $this->meta === 10){ return Fuel::$duration[$this->id]; } return null; } /** * @param Entity|Block $object * * @return bool */ public function useOn($object){ return false; } /** * @return bool */ public function isTool(){ return false; } /** * @return int|bool */ public function getMaxDurability(){ return false; } public function isPickaxe(){ return false; } public function isAxe(){ return false; } public function isSword(){ return false; } public function isShovel(){ return false; } public function isHoe(){ return false; } public function isShears(){ return false; } public function getDestroySpeed(Block $block, Player $player){ 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 */ final public function equals(Item $item, bool $checkDamage = true, bool $checkCompound = true) : bool{ 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; } /** * @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 */ final public 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, "damage" => $this->meta, "count" => $this->count, //TODO: separate items and stacks "nbt" => $this->tags ]; } /** * Serializes the item to an NBT CompoundTag * * @param int $slot optional, the inventory slot of the item * @param string $tagName the name to assign to the CompoundTag object * * @return CompoundTag */ public function nbtSerialize(int $slot = -1, string $tagName = "") : CompoundTag{ $tag = new CompoundTag($tagName, [ new ShortTag("id", $this->id), new ByteTag("Count", Binary::signByte($this->count)), new ShortTag("Damage", $this->meta), ]); if($this->hasCompoundTag()){ $tag->tag = clone $this->getNamedTag(); $tag->tag->setName("tag"); } if($slot !== -1){ $tag->Slot = new ByteTag("Slot", $slot); } return $tag; } /** * Deserializes an Item from an NBT CompoundTag * * @param CompoundTag $tag * * @return Item */ public static function nbtDeserialize(CompoundTag $tag) : Item{ if(!isset($tag->id) or !isset($tag->Count)){ return Item::get(0); } $count = Binary::unsignByte($tag->Count->getValue()); $meta = isset($tag->Damage) ? $tag->Damage->getValue() : 0; if($tag->id instanceof ShortTag){ $item = Item::get($tag->id->getValue(), $meta, $count); }elseif($tag->id instanceof StringTag){ //PC item save format $item = Item::fromString($tag->id->getValue()); $item->setDamage($meta); $item->setCount($count); }else{ throw new \InvalidArgumentException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($tag->id) . " given"); } if(isset($tag->tag) and $tag->tag instanceof CompoundTag){ $t = clone $tag->tag; $t->setName(""); $item->setNamedTag($t); } return $item; } public function __clone(){ if($this->block !== null){ $this->block = clone $this->block; } $this->cachedNBT = null; } }