First (untested) look at hooking all the itemstack serializer/deserializer stuff together

this should address #5063 and related issues, if it works correctly.
This commit is contained in:
Dylan K. Taylor 2022-06-23 19:02:16 +01:00
parent d8bba6ed3d
commit 5ed75731f2
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
10 changed files with 613 additions and 86 deletions

View File

@ -24,20 +24,14 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock\item;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\StringTag;
use function str_starts_with;
final class SavedItemData{
public const TAG_NAME = "Name";
private const TAG_DAMAGE = "Damage";
public const TAG_DAMAGE = "Damage";
public const TAG_BLOCK = "Block";
private const TAG_TAG = "tag";
private const TAG_ITEM_IDENTIFIER = "ItemIdentifier";
public const TAG_TAG = "tag";
public function __construct(
private string $name,
@ -54,43 +48,6 @@ final class SavedItemData{
public function getTag() : ?CompoundTag{ return $this->tag; }
public static function fromNbt(CompoundTag $tag) : self{
try{
//required
$name = $tag->getString(self::TAG_NAME);
$damage = $tag->getShort(self::TAG_DAMAGE);
//optional
$blockStateNbt = $tag->getCompoundTag(self::TAG_BLOCK);
$extraData = $tag->getCompoundTag(self::TAG_TAG);
}catch(NbtException $e){
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
}
//TODO: this hack probably doesn't belong here; it's necessary to deal with spawn eggs from before 1.16.100
if(
$name === ItemTypeIds::SPAWN_EGG &&
($itemIdentifierTag = $tag->getTag(self::TAG_ITEM_IDENTIFIER)) instanceof StringTag &&
str_starts_with($itemIdentifierTag->getValue(), "minecraft:")
){
\GlobalLogger::get()->debug("Handling legacy spawn egg for " . $itemIdentifierTag->getValue());
$name = $itemIdentifierTag->getValue() . "_spawn_egg";
}
try{
$blockStateData = $blockStateNbt !== null ? BlockStateData::fromNbt($blockStateNbt) : null;
}catch(BlockStateDeserializeException $e){
throw new SavedDataLoadingException("Failed to load item saved data: " . $e->getMessage(), 0, $e);
}
return new self(
$name,
$damage,
$blockStateData,
$extraData
);
}
public function toNbt() : CompoundTag{
$result = CompoundTag::create();
$result->setString(self::TAG_NAME, $this->name);

View File

@ -0,0 +1,87 @@
<?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\data\bedrock\item;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\utils\Binary;
use function array_map;
use function count;
final class SavedItemStackData{
public const TAG_COUNT = "Count";
public const TAG_SLOT = "Slot";
public const TAG_WAS_PICKED_UP = "WasPickedUp";
public const TAG_CAN_PLACE_ON = "CanPlaceOn";
public const TAG_CAN_DESTROY = "CanDestroy";
/**
* @param string[] $canPlaceOn
* @param string[] $canDestroy
*/
public function __construct(
private SavedItemData $typeData,
private int $count,
private ?int $slot,
private ?bool $wasPickedUp,
private array $canPlaceOn,
private array $canDestroy
){}
public function getTypeData() : SavedItemData{ return $this->typeData; }
public function getCount() : int{ return $this->count; }
public function getSlot() : ?int{ return $this->slot; }
public function getWasPickedUp() : ?bool{ return $this->wasPickedUp; }
/** @return string[] */
public function getCanPlaceOn() : array{ return $this->canPlaceOn; }
/** @return string[] */
public function getCanDestroy() : array{ return $this->canDestroy; }
public function toNbt() : CompoundTag{
$result = CompoundTag::create();
$result->setByte(self::TAG_COUNT, Binary::signByte($this->count));
if($this->slot !== null){
$result->setByte(self::TAG_SLOT, Binary::signByte($this->slot));
}
if($this->wasPickedUp !== null){
$result->setByte(self::TAG_WAS_PICKED_UP, $this->wasPickedUp ? 1 : 0);
}
if(count($this->canPlaceOn) !== 0){
$result->setTag(self::TAG_CAN_PLACE_ON, new ListTag(array_map(fn(string $s) => new StringTag($s), $this->canPlaceOn)));
}
if(count($this->canDestroy) !== 0){
$result->setTag(self::TAG_CAN_DESTROY, new ListTag(array_map(fn(string $s) => new StringTag($s), $this->canDestroy)));
}
return $result->merge($this->typeData->toNbt());
}
}

View File

@ -0,0 +1,174 @@
<?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\data\bedrock\item\upgrade;
use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader;
use pocketmine\data\bedrock\item\SavedItemData;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\nbt\NBT;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\utils\Binary;
use function assert;
use function ksort;
final class ItemDataUpgrader{
private const TAG_LEGACY_ID = "id"; //TAG_Short (or TAG_String for Java itemstacks)
/**
* @var ItemIdMetaUpgradeSchema[]
* @phpstan-var array<int, ItemIdMetaUpgradeSchema>
*/
private array $idMetaUpgradeSchemas = [];
/**
* @param ItemIdMetaUpgradeSchema[] $idMetaUpgradeSchemas
* @phpstan-param array<int, ItemIdMetaUpgradeSchema> $idMetaUpgradeSchemas
*/
public function __construct(
private LegacyItemIdToStringIdMap $legacyIntToStringIdMap,
private R12ItemIdToBlockIdMap $r12ItemIdToBlockIdMap,
private BlockDataUpgrader $blockDataUpgrader,
array $idMetaUpgradeSchemas
){
foreach($idMetaUpgradeSchemas as $schema){
$this->addIdMetaUpgradeSchema($schema);
}
}
public function addIdMetaUpgradeSchema(ItemIdMetaUpgradeSchema $schema) : void{
if(isset($this->idMetaUpgradeSchemas[$schema->getPriority()])){
throw new \InvalidArgumentException("Already have a schema with priority " . $schema->getPriority());
}
$this->idMetaUpgradeSchemas[$schema->getPriority()] = $schema;
ksort($this->idMetaUpgradeSchemas, SORT_NUMERIC);
}
private function upgradeItemTypeNbt(CompoundTag $tag) : SavedItemData{
if(($nameIdTag = $tag->getTag(SavedItemData::TAG_NAME)) instanceof StringTag){
//Bedrock 1.6+
$rawNameId = $nameIdTag->getValue();
}elseif(($idTag = $tag->getTag(self::TAG_LEGACY_ID)) instanceof ShortTag){
//Bedrock <= 1.5, PM <= 1.12
$rawNameId = $this->legacyIntToStringIdMap->legacyToString($idTag->getValue());
if($rawNameId === null){
throw new SavedDataLoadingException("Legacy item ID " . $idTag->getValue() . " doesn't map to any modern string ID");
}
}elseif($idTag instanceof StringTag){
//PC item save format - best we can do here is hope the string IDs match
$rawNameId = $idTag->getValue();
}else{
throw new SavedDataLoadingException("Item stack data should have either a name ID or a legacy ID");
}
$meta = $tag->getShort(SavedItemData::TAG_DAMAGE, 0);
$blockStateNbt = $tag->getCompoundTag(SavedItemData::TAG_BLOCK);
if($blockStateNbt !== null){
$blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
}elseif(($r12BlockId = $this->r12ItemIdToBlockIdMap->itemIdToBlockId($rawNameId)) !== null){
//this is a legacy blockitem represented by ID + meta
$blockStateData = $this->blockDataUpgrader->upgradeStringIdMeta($r12BlockId, $meta);
}else{
//probably a standard item
$blockStateData = null;
}
[$newNameId, $newMeta] = $this->upgradeItemStringIdMeta($rawNameId, $meta);
//TODO: this won't account for spawn eggs from before 1.16.100 - perhaps we're lucky and they just left the meta in there anyway?
return new SavedItemData($newNameId, $newMeta, $blockStateData, $tag->getCompoundTag(SavedItemData::TAG_TAG));
}
/**
* @return string[]
*/
private static function deserializeListOfStrings(?ListTag $list, string $tagName) : array{
if($list === null){
return [];
}
if($list->getTagType() !== NBT::TAG_String){
throw new SavedDataLoadingException("Unexpected type of list for tag '$tagName', expected TAG_String");
}
$result = [];
foreach($list as $item){
assert($item instanceof StringTag);
$result[] = $item->getValue();
}
return $result;
}
public function upgradeItemStackNbt(CompoundTag $tag) : SavedItemStackData{
$savedItemData = $this->upgradeItemTypeNbt($tag);
try{
//required
$count = Binary::unsignByte($tag->getByte(SavedItemStackData::TAG_COUNT));
//optional
$slot = ($slotTag = $tag->getTag(SavedItemStackData::TAG_SLOT)) instanceof ByteTag ? Binary::unsignByte($slotTag->getValue()) : null;
$wasPickedUp = ($wasPickedUpTag = $tag->getTag(SavedItemStackData::TAG_WAS_PICKED_UP)) instanceof ByteTag ? $wasPickedUpTag->getValue() : null;
$canPlaceOnList = $tag->getListTag(SavedItemStackData::TAG_CAN_PLACE_ON);
$canDestroyList = $tag->getListTag(SavedItemStackData::TAG_CAN_DESTROY);
}catch(NbtException $e){
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
}
return new SavedItemStackData(
$savedItemData,
$count,
$slot,
$wasPickedUp !== 0,
self::deserializeListOfStrings($canPlaceOnList, SavedItemStackData::TAG_CAN_PLACE_ON),
self::deserializeListOfStrings($canDestroyList, SavedItemStackData::TAG_CAN_DESTROY)
);
}
/**
* @phpstan-return array{string, int}
*/
public function upgradeItemStringIdMeta(string $id, int $meta) : array{
$newId = $id;
$newMeta = $meta;
foreach($this->idMetaUpgradeSchemas as $schema){
if(($remappedMetaId = $schema->remapMeta($newId, $newMeta)) !== null){
$newId = $remappedMetaId;
$newMeta = 0;
}elseif(($renamedId = $schema->renameId($newId)) !== null){
$newId = $renamedId;
}
}
return [$id, $meta];
}
}

View File

@ -0,0 +1,49 @@
<?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\data\bedrock\item\upgrade;
final class ItemIdMetaUpgradeSchema{
/**
* @param string[] $renamedIds
* @param string[][] $remappedMetas
* @phpstan-param array<string, string> $renamedIds
* @phpstan-param array<string, array<int, string>> $remappedMetas
*/
public function __construct(
private array $renamedIds,
private array $remappedMetas,
private int $priority
){}
public function getPriority() : int{ return $this->priority; }
public function renameId(string $id) : ?string{
return $this->renamedIds[$id] ?? null;
}
public function remapMeta(string $id, int $meta) : ?string{
return $this->remappedMetas[$id][$meta] ?? null;
}
}

View File

@ -0,0 +1,98 @@
<?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\data\bedrock\item\upgrade;
use pocketmine\data\bedrock\item\upgrade\model\ItemIdMetaUpgradeSchemaModel;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use Webmozart\PathUtil\Path;
use function file_get_contents;
use function gettype;
use function is_object;
use function json_decode;
use function ksort;
final class ItemIdMetaUpgradeSchemaUtils{
/**
* @return ItemIdMetaUpgradeSchema[]
* @phpstan-return array<int, ItemIdMetaUpgradeSchema>
*/
public static function loadSchemas(string $path) : array{
$iterator = new \RegexIterator(
new \FilesystemIterator(
$path,
\FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS
),
'/\/(\d{4}).*\.json$/',
\RegexIterator::GET_MATCH
);
$result = [];
/** @var string[] $matches */
foreach($iterator as $matches){
$filename = $matches[0];
$priority = (int) $matches[1];
$fullPath = Path::join($path, $filename);
try{
$raw = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($fullPath));
}catch(\ErrorException $e){
throw new \RuntimeException("Loading schema file $fullPath: " . $e->getMessage(), 0, $e);
}
try{
$schema = self::loadSchemaFromString($raw, $priority);
}catch(\RuntimeException $e){
throw new \RuntimeException("Loading schema file $fullPath: " . $e->getMessage(), 0, $e);
}
$result[$priority] = $schema;
}
ksort($result, SORT_NUMERIC);
return $result;
}
public static function loadSchemaFromString(string $raw, int $priority) : ItemIdMetaUpgradeSchema{
try{
$json = json_decode($raw, false, flags: JSON_THROW_ON_ERROR);
}catch(\JsonException $e){
throw new \RuntimeException($e->getMessage(), 0, $e);
}
if(!is_object($json)){
throw new \RuntimeException("Unexpected root type of schema file " . gettype($json) . ", expected object");
}
$jsonMapper = new \JsonMapper();
try{
$model = $jsonMapper->map($json, new ItemIdMetaUpgradeSchemaModel());
}catch(\JsonMapper_Exception $e){
throw new \RuntimeException($e->getMessage(), 0, $e);
}
return new ItemIdMetaUpgradeSchema($model->renamedIds, $model->remappedMetas, $priority);
}
}

View File

@ -21,8 +21,9 @@
declare(strict_types=1);
namespace pocketmine\data\bedrock;
namespace pocketmine\data\bedrock\item\upgrade;
use pocketmine\data\bedrock\LegacyToStringBidirectionalIdMap;
use pocketmine\utils\SingletonTrait;
use Webmozart\PathUtil\Path;

View File

@ -0,0 +1,87 @@
<?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\data\bedrock\item\upgrade;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
use Webmozart\PathUtil\Path;
use function array_search;
use function file_get_contents;
use function is_array;
use function is_string;
use function json_decode;
use const pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH;
/**
* Maps all known 1.12 and lower item IDs to their respective block IDs, where appropriate.
* If an item ID does not have a corresponding 1.12 block ID, assume the item is not a blockitem.
*
* This is only needed for deserializing blockitems from 1.8 and lower (or 1.12 and lower in the case of PM). In 1.9 and
* above, the blockstate NBT is stored in the itemstack NBT, and the item ID is not used.
*/
final class R12ItemIdToBlockIdMap{
use SingletonTrait;
private static function make() : self{
$map = json_decode(
Utils::assumeNotFalse(file_get_contents(Path::join(BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, '1.12.0_item_id_to_block_id_map.json')), "Missing required resource file"),
associative: true,
flags: JSON_THROW_ON_ERROR
);
if(!is_array($map)){
throw new AssumptionFailedError("Invalid blockitem ID mapping table, expected array as root type");
}
$builtMap = [];
foreach($map as $itemId => $blockId){
if(!is_string($itemId)){
throw new AssumptionFailedError("Invalid blockitem ID mapping table, expected string as key");
}
if(!is_string($blockId)){
throw new AssumptionFailedError("Invalid blockitem ID mapping table, expected string as value");
}
$builtMap[$itemId] = $blockId;
}
return new self($builtMap);
}
/**
* @param string[] $itemToBlock
* @phpstan-param array<string, string> $itemToBlock
*/
public function __construct(private array $itemToBlock){}
public function itemIdToBlockId(string $itemId) : ?string{
return $this->itemToBlock[$itemId] ?? null;
}
public function blockIdToItemId(string $blockId) : ?string{
//we don't need this for any functionality, so we're not concerned about performance here
//however, it might be nice to have for debugging
$itemId = array_search($blockId, $this->itemToBlock, true);
return $itemId !== false ? $itemId : null;
}
}

View File

@ -0,0 +1,39 @@
<?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\data\bedrock\item\upgrade\model;
final class ItemIdMetaUpgradeSchemaModel{
/**
* @var string[]
* @phpstan-var array<string, string>
*/
public array $renamedIds = [];
/**
* @var string[][]
* @phpstan-var array<string, array<int, string>>
*/
public array $remappedMetas = [];
}

View File

@ -31,6 +31,7 @@ use pocketmine\block\BlockBreakInfo;
use pocketmine\block\BlockToolType;
use pocketmine\block\VanillaBlocks;
use pocketmine\data\bedrock\EnchantmentIdMap;
use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\entity\Entity;
use pocketmine\item\enchantment\EnchantmentInstance;
@ -41,16 +42,14 @@ use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\NbtException;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\nbt\TreeRoot;
use pocketmine\player\Player;
use pocketmine\utils\Binary;
use pocketmine\utils\Utils;
use pocketmine\world\format\io\GlobalItemDataHandlers;
use function base64_decode;
use function base64_encode;
use function count;
use function get_class;
use function gettype;
use function hex2bin;
use function is_string;
@ -648,21 +647,16 @@ class Item implements \JsonSerializable{
* @param int $slot optional, the inventory slot of the item
*/
public function nbtSerialize(int $slot = -1) : CompoundTag{
$result = CompoundTag::create()
->setShort("id", $this->getId())
->setByte("Count", Binary::signByte($this->count))
->setShort("Damage", $this->getMeta());
$typeData = GlobalItemDataHandlers::getSerializer()->serialize($this);
$tag = $this->getNamedTag();
if($tag->count() > 0){
$result->setTag("tag", $tag);
}
if($slot !== -1){
$result->setByte("Slot", $slot);
}
return $result;
return (new SavedItemStackData(
$typeData,
$this->count,
$slot !== -1 ? $slot : null,
null,
[], //we currently represent canDestroy and canPlaceOn via NBT, like PC
[]
))->toNbt();
}
/**
@ -671,31 +665,13 @@ class Item implements \JsonSerializable{
* @throws SavedDataLoadingException
*/
public static function nbtDeserialize(CompoundTag $tag) : Item{
if($tag->getTag("id") === null || $tag->getTag("Count") === null){
return VanillaItems::AIR();
}
$itemData = GlobalItemDataHandlers::getUpgrader()->upgradeItemStackNbt($tag);
$count = Binary::unsignByte($tag->getByte("Count"));
$meta = $tag->getShort("Damage", 0);
$item = GlobalItemDataHandlers::getDeserializer()->deserialize($itemData->getTypeData());
$idTag = $tag->getTag("id");
if($idTag instanceof ShortTag){
$item = ItemFactory::getInstance()->get($idTag->getValue(), $meta, $count);
}elseif($idTag instanceof StringTag){ //PC item save format
try{
$item = LegacyStringToItemParser::getInstance()->parse($idTag->getValue() . ":$meta");
}catch(LegacyStringToItemParserException $e){
//TODO: improve error handling
return VanillaItems::AIR();
}
$item->setCount($count);
}else{
throw new SavedDataLoadingException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($idTag) . " given");
}
$itemNBT = $tag->getCompoundTag("tag");
if($itemNBT !== null){
$item->setNamedTag(clone $itemNBT);
$item->setCount($itemData->getCount());
if(($tagTag = $itemData->getTypeData()->getTag()) !== null){
$item->setNamedTag(clone $tagTag);
}
return $item;

View File

@ -0,0 +1,59 @@
<?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\world\format\io;
use pocketmine\data\bedrock\item\ItemDeserializer;
use pocketmine\data\bedrock\item\ItemSerializer;
use pocketmine\data\bedrock\item\upgrade\ItemDataUpgrader;
use pocketmine\data\bedrock\item\upgrade\ItemIdMetaUpgradeSchemaUtils;
use pocketmine\data\bedrock\item\upgrade\LegacyItemIdToStringIdMap;
use pocketmine\data\bedrock\item\upgrade\R12ItemIdToBlockIdMap;
use Webmozart\PathUtil\Path;
use const pocketmine\BEDROCK_ITEM_UPGRADE_SCHEMA_PATH;
final class GlobalItemDataHandlers{
private static ?ItemSerializer $itemSerializer = null;
private static ?ItemDeserializer $itemDeserializer = null;
private static ?ItemDataUpgrader $itemDataUpgrader = null;
public static function getSerializer() : ItemSerializer{
return self::$itemSerializer ??= new ItemSerializer(GlobalBlockStateHandlers::getSerializer());
}
public static function getDeserializer() : ItemDeserializer{
return self::$itemDeserializer ??= new ItemDeserializer(GlobalBlockStateHandlers::getDeserializer());
}
public static function getUpgrader() : ItemDataUpgrader{
return self::$itemDataUpgrader ??= new ItemDataUpgrader(
LegacyItemIdToStringIdMap::getInstance(),
R12ItemIdToBlockIdMap::getInstance(),
GlobalBlockStateHandlers::getUpgrader(),
ItemIdMetaUpgradeSchemaUtils::loadSchemas(Path::join(BEDROCK_ITEM_UPGRADE_SCHEMA_PATH, 'id_meta_upgrade_schema'))
);
}
}