mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 12:18:46 +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;
|
namespace pocketmine\data\bedrock\item;
|
||||||
|
|
||||||
use pocketmine\data\bedrock\block\BlockStateData;
|
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\CompoundTag;
|
||||||
use pocketmine\nbt\tag\StringTag;
|
|
||||||
use function str_starts_with;
|
|
||||||
|
|
||||||
final class SavedItemData{
|
final class SavedItemData{
|
||||||
|
|
||||||
public const TAG_NAME = "Name";
|
public const TAG_NAME = "Name";
|
||||||
private const TAG_DAMAGE = "Damage";
|
public const TAG_DAMAGE = "Damage";
|
||||||
public const TAG_BLOCK = "Block";
|
public const TAG_BLOCK = "Block";
|
||||||
private const TAG_TAG = "tag";
|
public const TAG_TAG = "tag";
|
||||||
private const TAG_ITEM_IDENTIFIER = "ItemIdentifier";
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $name,
|
private string $name,
|
||||||
@ -54,43 +48,6 @@ final class SavedItemData{
|
|||||||
|
|
||||||
public function getTag() : ?CompoundTag{ return $this->tag; }
|
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{
|
public function toNbt() : CompoundTag{
|
||||||
$result = CompoundTag::create();
|
$result = CompoundTag::create();
|
||||||
$result->setString(self::TAG_NAME, $this->name);
|
$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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\data\bedrock;
|
namespace pocketmine\data\bedrock\item\upgrade;
|
||||||
|
|
||||||
|
use pocketmine\data\bedrock\LegacyToStringBidirectionalIdMap;
|
||||||
use pocketmine\utils\SingletonTrait;
|
use pocketmine\utils\SingletonTrait;
|
||||||
use Webmozart\PathUtil\Path;
|
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\BlockToolType;
|
||||||
use pocketmine\block\VanillaBlocks;
|
use pocketmine\block\VanillaBlocks;
|
||||||
use pocketmine\data\bedrock\EnchantmentIdMap;
|
use pocketmine\data\bedrock\EnchantmentIdMap;
|
||||||
|
use pocketmine\data\bedrock\item\SavedItemStackData;
|
||||||
use pocketmine\data\SavedDataLoadingException;
|
use pocketmine\data\SavedDataLoadingException;
|
||||||
use pocketmine\entity\Entity;
|
use pocketmine\entity\Entity;
|
||||||
use pocketmine\item\enchantment\EnchantmentInstance;
|
use pocketmine\item\enchantment\EnchantmentInstance;
|
||||||
@ -41,16 +42,14 @@ use pocketmine\nbt\NbtDataException;
|
|||||||
use pocketmine\nbt\NbtException;
|
use pocketmine\nbt\NbtException;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
use pocketmine\nbt\tag\ListTag;
|
use pocketmine\nbt\tag\ListTag;
|
||||||
use pocketmine\nbt\tag\ShortTag;
|
|
||||||
use pocketmine\nbt\tag\StringTag;
|
use pocketmine\nbt\tag\StringTag;
|
||||||
use pocketmine\nbt\TreeRoot;
|
use pocketmine\nbt\TreeRoot;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\utils\Binary;
|
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
|
use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||||
use function base64_decode;
|
use function base64_decode;
|
||||||
use function base64_encode;
|
use function base64_encode;
|
||||||
use function count;
|
use function count;
|
||||||
use function get_class;
|
|
||||||
use function gettype;
|
use function gettype;
|
||||||
use function hex2bin;
|
use function hex2bin;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
@ -648,21 +647,16 @@ class Item implements \JsonSerializable{
|
|||||||
* @param int $slot optional, the inventory slot of the item
|
* @param int $slot optional, the inventory slot of the item
|
||||||
*/
|
*/
|
||||||
public function nbtSerialize(int $slot = -1) : CompoundTag{
|
public function nbtSerialize(int $slot = -1) : CompoundTag{
|
||||||
$result = CompoundTag::create()
|
$typeData = GlobalItemDataHandlers::getSerializer()->serialize($this);
|
||||||
->setShort("id", $this->getId())
|
|
||||||
->setByte("Count", Binary::signByte($this->count))
|
|
||||||
->setShort("Damage", $this->getMeta());
|
|
||||||
|
|
||||||
$tag = $this->getNamedTag();
|
return (new SavedItemStackData(
|
||||||
if($tag->count() > 0){
|
$typeData,
|
||||||
$result->setTag("tag", $tag);
|
$this->count,
|
||||||
}
|
$slot !== -1 ? $slot : null,
|
||||||
|
null,
|
||||||
if($slot !== -1){
|
[], //we currently represent canDestroy and canPlaceOn via NBT, like PC
|
||||||
$result->setByte("Slot", $slot);
|
[]
|
||||||
}
|
))->toNbt();
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -671,31 +665,13 @@ class Item implements \JsonSerializable{
|
|||||||
* @throws SavedDataLoadingException
|
* @throws SavedDataLoadingException
|
||||||
*/
|
*/
|
||||||
public static function nbtDeserialize(CompoundTag $tag) : Item{
|
public static function nbtDeserialize(CompoundTag $tag) : Item{
|
||||||
if($tag->getTag("id") === null || $tag->getTag("Count") === null){
|
$itemData = GlobalItemDataHandlers::getUpgrader()->upgradeItemStackNbt($tag);
|
||||||
return VanillaItems::AIR();
|
|
||||||
}
|
|
||||||
|
|
||||||
$count = Binary::unsignByte($tag->getByte("Count"));
|
$item = GlobalItemDataHandlers::getDeserializer()->deserialize($itemData->getTypeData());
|
||||||
$meta = $tag->getShort("Damage", 0);
|
|
||||||
|
|
||||||
$idTag = $tag->getTag("id");
|
$item->setCount($itemData->getCount());
|
||||||
if($idTag instanceof ShortTag){
|
if(($tagTag = $itemData->getTypeData()->getTag()) !== null){
|
||||||
$item = ItemFactory::getInstance()->get($idTag->getValue(), $meta, $count);
|
$item->setNamedTag(clone $tagTag);
|
||||||
}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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $item;
|
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