61class Item implements \JsonSerializable{
 
   64    public const TAG_ENCH = 
"ench";
 
   65    private const TAG_ENCH_ID = 
"id"; 
 
   66    private const TAG_ENCH_LVL = 
"lvl"; 
 
   68    public const TAG_DISPLAY = 
"display";
 
   69    public const TAG_BLOCK_ENTITY_TAG = 
"BlockEntityTag";
 
   71    public const TAG_DISPLAY_NAME = 
"Name";
 
   72    public const TAG_DISPLAY_LORE = 
"Lore";
 
   74    public const TAG_KEEP_ON_DEATH = 
"minecraft:keep_on_death";
 
   76    private const TAG_CAN_PLACE_ON = 
"CanPlaceOn"; 
 
   77    private const TAG_CAN_DESTROY = 
"CanDestroy"; 
 
   81    protected int $count = 1;
 
   85    protected string $customName = 
"";
 
   87    protected array $lore = [];
 
   95    protected array $canPlaceOn = [];
 
  100    protected array $canDestroy = [];
 
  102    protected bool $keepOnDeath = 
false;
 
  116        protected string $name = 
"Unknown",
 
  117        private array $enchantmentTags = []
 
 
  122    public function hasCustomBlockData() : bool{
 
  123        return $this->blockEntityTag !== null;
 
  130        $this->blockEntityTag = 
null;
 
 
  138        $this->blockEntityTag = clone $compound;
 
 
  143    public function getCustomBlockData() : ?
CompoundTag{
 
  144        return $this->blockEntityTag;
 
  147    public function hasCustomName() : bool{
 
  148        return $this->customName !== 
"";
 
  151    public function getCustomName() : string{
 
  152        return $this->customName;
 
  159        Utils::checkUTF8($name);
 
  160        $this->customName = $name;
 
 
  168        $this->setCustomName(
"");
 
 
  185        foreach($lines as $line){
 
  186            if(!is_string($line)){
 
  187                throw new \TypeError(
"Expected string[], but found " . gettype($line) . 
" in given array");
 
  189            Utils::checkUTF8($line);
 
  191        $this->lore = $lines;
 
 
  200        return $this->canPlaceOn;
 
 
  207        $this->canPlaceOn = [];
 
  208        foreach($canPlaceOn as $value){
 
  209            $this->canPlaceOn[$value] = $value;
 
 
  218        return $this->canDestroy;
 
 
  225        $this->canDestroy = [];
 
  226        foreach($canDestroy as $value){
 
  227            $this->canDestroy[$value] = $value;
 
 
  235        return $this->keepOnDeath;
 
 
  238    public function setKeepOnDeath(
bool $keepOnDeath) : void{
 
  239        $this->keepOnDeath = $keepOnDeath;
 
  246        return $this->getNamedTag()->count() > 0;
 
 
  254        $this->serializeCompoundTag($this->nbt);
 
 
  265        if($tag->getCount() === 0){
 
  266            return $this->clearNamedTag();
 
  269        $this->nbt = clone $tag;
 
  270        $this->deserializeCompoundTag($this->nbt);
 
 
  282        $this->deserializeCompoundTag($this->nbt);
 
 
  290        $this->customName = 
"";
 
  294        if($display !== 
null){
 
  295            $this->customName = $display->getString(self::TAG_DISPLAY_NAME, $this->customName);
 
  296            $lore = $display->getListTag(self::TAG_DISPLAY_LORE, StringTag::class);
 
  298                foreach($lore as $t){
 
  299                    $this->lore[] = $t->getValue();
 
  305        $enchantments = $tag->
getListTag(self::TAG_ENCH, CompoundTag::class);
 
  306        if($enchantments !== 
null){
 
  307            foreach($enchantments as $enchantment){
 
  308                $magicNumber = $enchantment->getShort(self::TAG_ENCH_ID, -1);
 
  309                $level = $enchantment->getShort(self::TAG_ENCH_LVL, 0);
 
  313                $type = EnchantmentIdMap::getInstance()->fromId($magicNumber);
 
  315                    $this->addEnchantment(
new EnchantmentInstance($type, $level));
 
  320        $this->blockEntityTag = $tag->
getCompoundTag(self::TAG_BLOCK_ENTITY_TAG);
 
  322        $this->canPlaceOn = [];
 
  323        $canPlaceOn = $tag->
getListTag(self::TAG_CAN_PLACE_ON, StringTag::class);
 
  324        if($canPlaceOn !== 
null){
 
  325            foreach($canPlaceOn as $entry){
 
  326                $this->canPlaceOn[$entry->getValue()] = $entry->getValue();
 
  329        $this->canDestroy = [];
 
  330        $canDestroy = $tag->
getListTag(self::TAG_CAN_DESTROY, StringTag::class);
 
  331        if($canDestroy !== 
null){
 
  332            foreach($canDestroy as $entry){
 
  333                $this->canDestroy[$entry->getValue()] = $entry->getValue();
 
  337        $this->keepOnDeath = $tag->getByte(self::TAG_KEEP_ON_DEATH, 0) !== 0;
 
 
  340    protected function serializeCompoundTag(CompoundTag $tag) : void{
 
  341        $display = $tag->getCompoundTag(self::TAG_DISPLAY);
 
  343        if($this->customName !== 
""){
 
  344            $display ??= 
new CompoundTag();
 
  345            $display->setString(self::TAG_DISPLAY_NAME, $this->customName);
 
  347            $display?->removeTag(self::TAG_DISPLAY_NAME);
 
  350        if(count($this->lore) > 0){
 
  351            $loreTag = 
new ListTag();
 
  352            foreach($this->lore as $line){
 
  353                $loreTag->push(
new StringTag($line));
 
  355            $display ??= 
new CompoundTag();
 
  356            $display->setTag(self::TAG_DISPLAY_LORE, $loreTag);
 
  358            $display?->removeTag(self::TAG_DISPLAY_LORE);
 
  360        $display !== 
null && $display->count() > 0 ?
 
  361            $tag->setTag(self::TAG_DISPLAY, $display) :
 
  362            $tag->removeTag(self::TAG_DISPLAY);
 
  364        if(count($this->enchantments) > 0){
 
  365            $ench = 
new ListTag();
 
  366            $enchantmentIdMap = EnchantmentIdMap::getInstance();
 
  367            foreach($this->enchantments as $enchantmentInstance){
 
  368                $ench->push(CompoundTag::create()
 
  369                    ->setShort(self::TAG_ENCH_ID, $enchantmentIdMap->toId($enchantmentInstance->getType()))
 
  370                    ->setShort(self::TAG_ENCH_LVL, $enchantmentInstance->getLevel())
 
  373            $tag->setTag(self::TAG_ENCH, $ench);
 
  375            $tag->removeTag(self::TAG_ENCH);
 
  378        $this->blockEntityTag !== 
null ?
 
  379            $tag->setTag(self::TAG_BLOCK_ENTITY_TAG, clone $this->blockEntityTag) :
 
  380            $tag->removeTag(self::TAG_BLOCK_ENTITY_TAG);
 
  382        if(count($this->canPlaceOn) > 0){
 
  383            $canPlaceOn = 
new ListTag();
 
  384            foreach($this->canPlaceOn as $item){
 
  385                $canPlaceOn->push(
new StringTag($item));
 
  387            $tag->setTag(self::TAG_CAN_PLACE_ON, $canPlaceOn);
 
  389            $tag->removeTag(self::TAG_CAN_PLACE_ON);
 
  391        if(count($this->canDestroy) > 0){
 
  392            $canDestroy = 
new ListTag();
 
  393            foreach($this->canDestroy as $item){
 
  394                $canDestroy->push(
new StringTag($item));
 
  396            $tag->setTag(self::TAG_CAN_DESTROY, $canDestroy);
 
  398            $tag->removeTag(self::TAG_CAN_DESTROY);
 
  401        if($this->keepOnDeath){
 
  402            $tag->setByte(self::TAG_KEEP_ON_DEATH, 1);
 
  404            $tag->removeTag(self::TAG_KEEP_ON_DEATH);
 
  408    public function getCount() : int{
 
  416        $this->count = $count;
 
 
  428        if($count > $this->count){
 
  429            throw new \InvalidArgumentException(
"Cannot pop $count items from a stack of $this->count");
 
  433        $item->count = $count;
 
  435        $this->count -= $count;
 
 
  440    public function isNull() : bool{
 
  441        return $this->count <= 0;
 
  448        return $this->hasCustomName() ? $this->getCustomName() : $this->getVanillaName();
 
 
  468        return $this->enchantmentTags;
 
 
  481    final public function canBePlaced() : bool{
 
  482        return $this->getBlock()->canBePlaced();
 
  485    protected final function tryPlacementTransaction(Block $blockPlace, Block $blockReplace, Block $blockClicked, 
int $face, Vector3 $clickVector, ?
Player $player) : ?BlockTransaction{
 
  486        $position = $blockReplace->getPosition();
 
  487        $blockPlace->position($position->getWorld(), $position->getFloorX(), $position->getFloorY(), $position->getFloorZ());
 
  488        if(!$blockPlace->canBePlacedAt($blockReplace, $clickVector, $face, $blockReplace->getPosition()->equals($blockClicked->getPosition()))){
 
  491        $transaction = 
new BlockTransaction($position->getWorld());
 
  492        return $blockPlace->place($transaction, $this, $blockReplace, $blockClicked, $face, $clickVector, $player) ? $transaction : 
null;
 
  495    public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, 
int $face, Vector3 $clickVector, ?Player $player = 
null) : ?BlockTransaction{
 
  496        return $this->tryPlacementTransaction($this->getBlock($face), $blockReplace, $blockClicked, $face, $clickVector, $player);
 
  506    final public function getTypeId() : int{
 
  507        return $this->identifier->getTypeId();
 
  510    final public function getStateId() : int{
 
  511        return morton2d_encode($this->identifier->getTypeId(), $this->computeStateData());
 
  514    private function computeStateData() : int{
 
  515        $writer = new RuntimeDataWriter(16); 
 
  516        $this->describeState($writer);
 
  517        return $writer->getValue();
 
  531    public function getMaxStackSize() : int{
 
 
  592    public function getMiningEfficiency(
bool $isCorrectTool) : float{
 
  688    final public function equals(
Item $item, 
bool $checkDamage = 
true, 
bool $checkCompound = 
true) : bool{
 
  689        return $this->getStateId() === $item->getStateId() &&
 
  690            (!$checkCompound || $this->getNamedTag()->equals($item->getNamedTag()));
 
 
  697        return $this->equals($other, true, true);
 
 
  704        return $this->canStackWith($other) && $this->count === $other->count;
 
 
  707    final public function __toString() : string{
 
  708        return 
"Item " . $this->name . 
" (" . $this->getTypeId() . 
":" . $this->computeStateData() . 
")x" . $this->count . ($this->hasNamedTag() ? 
" tags:0x" . base64_encode((new 
LittleEndianNbtSerializer())->write(new 
TreeRoot($this->getNamedTag()))) : 
"");
 
  715        throw new \LogicException(
"json_encode()ing Item instances is no longer supported. Make your own method to convert the item to an array or stdClass.");
 
 
  730        if(isset($data[
"nbt"])){
 
  732        }elseif(isset($data[
"nbt_hex"])){
 
  733            $nbt = hex2bin($data[
"nbt_hex"]);
 
  734        }elseif(isset($data[
"nbt_b64"])){
 
  735            $nbt = base64_decode($data[
"nbt_b64"], 
true);
 
  738        $itemStackData = GlobalItemDataHandlers::getUpgrader()->upgradeItemTypeDataInt(
 
  740            (
int) ($data[
"damage"] ?? 0),
 
  741            (
int) ($data[
"count"] ?? 1),
 
  742            $nbt !== 
"" ? (
new LittleEndianNbtSerializer())->read($nbt)->mustGetCompoundTag() : 
null 
  746            return GlobalItemDataHandlers::getDeserializer()->deserializeStack($itemStackData);
 
  747        }
catch(ItemTypeDeserializeException $e){
 
  748            throw new SavedDataLoadingException($e->getMessage(), 0, $e);
 
 
  758        return 
GlobalItemDataHandlers::getSerializer()->serializeStack($this, $slot !== -1 ? $slot : null)->toNbt();
 
 
  767        if($itemData === 
null){
 
  768            return VanillaItems::AIR();
 
  772            return GlobalItemDataHandlers::getDeserializer()->deserializeStack($itemData);
 
  773        }
catch(ItemTypeDeserializeException $e){
 
  774            throw new SavedDataLoadingException($e->getMessage(), 0, $e);
 
 
  786            return self::nbtDeserialize($tag);
 
  789            $logger ??= \GlobalLogger::get();
 
  790            $logger->error(
"$errorLogContext: Error deserializing item (item will be replaced by AIR): " . $e->getMessage());
 
  792            return VanillaItems::AIR();
 
 
  796    public function __clone(){
 
  797        $this->nbt = clone $this->nbt;
 
  798        if($this->blockEntityTag !== 
null){
 
  799            $this->blockEntityTag = clone $this->blockEntityTag;