PocketMine-MP 5.35.1 git-e32e836dad793a3a3c8ddd8927c00e112b1e576a
Loading...
Searching...
No Matches
ItemDataUpgrader.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\data\bedrock\item\upgrade;
25
39use function array_map;
40
41final class ItemDataUpgrader{
42 private const TAG_LEGACY_ID = "id"; //TAG_Short (or TAG_String for Java itemstacks)
43
44 public function __construct(
45 private ItemIdMetaUpgrader $idMetaUpgrader,
46 private LegacyItemIdToStringIdMap $legacyIntToStringIdMap,
47 private R12ItemIdToBlockIdMap $r12ItemIdToBlockIdMap,
48 private BlockDataUpgrader $blockDataUpgrader,
49 private BlockItemIdMap $blockItemIdMap,
50 private BlockStateDictionary $blockStateDictionary
51 ){}
52
61 public function upgradeItemTypeDataString(string $rawNameId, int $meta, int $count, ?CompoundTag $nbt) : SavedItemStackData{
62 if(($r12BlockId = $this->r12ItemIdToBlockIdMap->itemIdToBlockId($rawNameId)) !== null){
63 try{
64 $blockStateData = $this->blockDataUpgrader->upgradeStringIdMeta($r12BlockId, $meta);
66 throw new SavedDataLoadingException("Failed to deserialize blockstate for legacy blockitem: " . $e->getMessage(), 0, $e);
67 }
68 }else{
69 //probably a standard item
70 $blockStateData = null;
71 }
72
73 [$newNameId, $newMeta] = $this->idMetaUpgrader->upgrade($rawNameId, $meta);
74
75 //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?
76
77 return new SavedItemStackData(
78 new SavedItemData($newNameId, $newMeta, $blockStateData, $nbt),
79 $count,
80 null,
81 null,
82 [],
83 []
84 );
85 }
86
92 public function upgradeItemTypeDataInt(int $legacyNumericId, int $meta, int $count, ?CompoundTag $nbt) : SavedItemStackData{
93 //do not upgrade the ID beyond this initial step - we need the 1.12 ID for the item ID -> block ID map in the
94 //next step
95 $rawNameId = $this->legacyIntToStringIdMap->legacyToString($legacyNumericId);
96 if($rawNameId === null){
97 throw new SavedDataLoadingException("Unmapped legacy item ID $legacyNumericId");
98 }
99 return $this->upgradeItemTypeDataString($rawNameId, $meta, $count, $nbt);
100 }
101
105 private function upgradeItemTypeNbt(CompoundTag $tag) : ?SavedItemData{
106 if(($nameIdTag = $tag->getTag(SavedItemData::TAG_NAME)) instanceof StringTag){
107 //Bedrock 1.6+
108
109 $rawNameId = $nameIdTag->getValue();
110 }elseif(($idTag = $tag->getTag(self::TAG_LEGACY_ID)) instanceof ShortTag){
111 //Bedrock <= 1.5, PM <= 1.12
112
113 if($idTag->getValue() === 0){
114 //0 is a special case for air, which is not a valid item ID
115 //this isn't supposed to be saved, but this appears in some places due to bugs in older versions
116 return null;
117 }
118 $rawNameId = $this->legacyIntToStringIdMap->legacyToString($idTag->getValue());
119 if($rawNameId === null){
120 throw new SavedDataLoadingException("Legacy item ID " . $idTag->getValue() . " doesn't map to any modern string ID");
121 }
122 }elseif($idTag instanceof StringTag){
123 //PC item save format - best we can do here is hope the string IDs match
124
125 $rawNameId = $idTag->getValue();
126 }else{
127 throw new SavedDataLoadingException("Item stack data should have either a name ID or a legacy ID");
128 }
129
130 $meta = $tag->getShort(SavedItemData::TAG_DAMAGE, 0);
131
132 $blockStateNbt = $tag->getCompoundTag(SavedItemData::TAG_BLOCK);
133 if($blockStateNbt !== null){
134 try{
135 $blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt);
136 }catch(BlockStateDeserializeException $e){
137 throw new SavedDataLoadingException("Failed to deserialize blockstate for blockitem: " . $e->getMessage(), 0, $e);
138 }
139 }elseif(($r12BlockId = $this->r12ItemIdToBlockIdMap->itemIdToBlockId($rawNameId)) !== null){
140 //this is a legacy blockitem represented by ID + meta
141 try{
142 $blockStateData = $this->blockDataUpgrader->upgradeStringIdMeta($r12BlockId, $meta);
143 }catch(BlockStateDeserializeException $e){
144 throw new SavedDataLoadingException("Failed to deserialize blockstate for legacy blockitem: " . $e->getMessage(), 0, $e);
145 }
146 }else{
147 //probably a standard item
148 $blockStateData = null;
149 }
150
151 [$newNameId, $newMeta] = $this->idMetaUpgrader->upgrade($rawNameId, $meta);
152
153 //TODO: Dirty hack to load old skulls from disk: Put this into item upgrade schema's before Mojang makes something with a non 0 default state
154 if($blockStateData === null && ($blockId = $this->blockItemIdMap->lookupBlockId($newNameId)) !== null){
155 $networkRuntimeId = $this->blockStateDictionary->lookupStateIdFromIdMeta($blockId, 0);
156
157 if($networkRuntimeId === null){
158 throw new SavedDataLoadingException("Failed to find blockstate for blockitem $newNameId");
159 }
160
161 $blockStateData = $this->blockStateDictionary->generateDataFromStateId($networkRuntimeId);
162 }
163
164 //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?
165 //TODO: read version from VersionInfo::TAG_WORLD_DATA_VERSION - we may need it to fix up old items
166
167 return new SavedItemData($newNameId, $newMeta, $blockStateData, $tag->getCompoundTag(SavedItemData::TAG_TAG));
168 }
169
174 $savedItemData = $this->upgradeItemTypeNbt($tag);
175 if($savedItemData === null){
176 //air - this isn't supposed to be saved, but older versions of PM saved it in some places
177 return null;
178 }
179 try{
180 //required
181 $count = Binary::unsignByte($tag->getByte(SavedItemStackData::TAG_COUNT));
182
183 //optional
184 $slot = ($slotTag = $tag->getTag(SavedItemStackData::TAG_SLOT)) instanceof ByteTag ? Binary::unsignByte($slotTag->getValue()) : null;
185 $wasPickedUp = ($wasPickedUpTag = $tag->getTag(SavedItemStackData::TAG_WAS_PICKED_UP)) instanceof ByteTag ? $wasPickedUpTag->getValue() : null;
186 $canPlaceOnList = $tag->getListTag(SavedItemStackData::TAG_CAN_PLACE_ON, StringTag::class);
187 $canDestroyList = $tag->getListTag(SavedItemStackData::TAG_CAN_DESTROY, StringTag::class);
188 }catch(NbtException $e){
189 throw new SavedDataLoadingException($e->getMessage(), 0, $e);
190 }
191
192 return new SavedItemStackData(
193 $savedItemData,
194 $count,
195 $slot,
196 $wasPickedUp !== 0,
197 $canPlaceOnList === null ? [] : array_map(fn(StringTag $t) => $t->getValue(), $canPlaceOnList->getValue()),
198 $canDestroyList === null ? [] : array_map(fn(StringTag $t) => $t->getValue(), $canDestroyList->getValue())
199 );
200 }
201
202 public function getIdMetaUpgrader() : ItemIdMetaUpgrader{ return $this->idMetaUpgrader; }
203}
upgradeItemTypeDataString(string $rawNameId, int $meta, int $count, ?CompoundTag $nbt)
upgradeItemTypeDataInt(int $legacyNumericId, int $meta, int $count, ?CompoundTag $nbt)
getListTag(string $name, string $tagClass=Tag::class)