mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-06 03:47:16 +00:00
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:
parent
d8bba6ed3d
commit
5ed75731f2
@ -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);
|
||||
|
87
src/data/bedrock/item/SavedItemStackData.php
Normal file
87
src/data/bedrock/item/SavedItemStackData.php
Normal 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());
|
||||
}
|
||||
}
|
174
src/data/bedrock/item/upgrade/ItemDataUpgrader.php
Normal file
174
src/data/bedrock/item/upgrade/ItemDataUpgrader.php
Normal 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];
|
||||
}
|
||||
}
|
49
src/data/bedrock/item/upgrade/ItemIdMetaUpgradeSchema.php
Normal file
49
src/data/bedrock/item/upgrade/ItemIdMetaUpgradeSchema.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
87
src/data/bedrock/item/upgrade/R12ItemIdToBlockIdMap.php
Normal file
87
src/data/bedrock/item/upgrade/R12ItemIdToBlockIdMap.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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 = [];
|
||||
}
|
@ -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;
|
||||
|
59
src/world/format/io/GlobalItemDataHandlers.php
Normal file
59
src/world/format/io/GlobalItemDataHandlers.php
Normal 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'))
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user