13declare(strict_types=1);
15namespace pocketmine\network\mcpe\protocol\serializer;
61use Ramsey\Uuid\UuidInterface;
68 protected function __construct(
string $buffer =
"",
int $offset = 0){
70 parent::__construct($buffer, $offset);
73 public static function encoder() :
self{
77 public static function decoder(
string $buffer,
int $offset) :
self{
78 return new self($buffer, $offset);
88 public function putString(
string $v) : void{
89 $this->putUnsignedVarInt(strlen($v));
96 public function getUUID() : UuidInterface{
98 $p1 = strrev($this->get(8));
99 $p2 = strrev($this->
get(8));
100 return Uuid::fromBytes($p1 . $p2);
103 public function putUUID(UuidInterface $uuid) : void{
104 $bytes = $uuid->getBytes();
105 $this->put(strrev(substr($bytes, 0, 8)));
106 $this->put(strrev(substr($bytes, 8, 8)));
109 public function getSkin() :
SkinData{
110 $skinId = $this->getString();
111 $skinPlayFabId = $this->getString();
112 $skinResourcePatch = $this->getString();
113 $skinData = $this->getSkinImage();
114 $animationCount = $this->getLInt();
116 for($i = 0; $i < $animationCount; ++$i){
117 $skinImage = $this->getSkinImage();
118 $animationType = $this->getLInt();
119 $animationFrames = $this->getLFloat();
120 $expressionType = $this->getLInt();
121 $animations[] =
new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
123 $capeData = $this->getSkinImage();
124 $geometryData = $this->getString();
125 $geometryDataVersion = $this->getString();
126 $animationData = $this->getString();
127 $capeId = $this->getString();
128 $fullSkinId = $this->getString();
129 $armSize = $this->getString();
130 $skinColor = $this->getString();
131 $personaPieceCount = $this->getLInt();
133 for($i = 0; $i < $personaPieceCount; ++$i){
134 $pieceId = $this->getString();
135 $pieceType = $this->getString();
136 $packId = $this->getString();
137 $isDefaultPiece = $this->getBool();
138 $productId = $this->getString();
139 $personaPieces[] =
new PersonaSkinPiece($pieceId, $pieceType, $packId, $isDefaultPiece, $productId);
141 $pieceTintColorCount = $this->getLInt();
142 $pieceTintColors = [];
143 for($i = 0; $i < $pieceTintColorCount; ++$i){
144 $pieceType = $this->getString();
145 $colorCount = $this->getLInt();
147 for($j = 0; $j < $colorCount; ++$j){
148 $colors[] = $this->getString();
150 $pieceTintColors[] =
new PersonaPieceTintColor(
156 $premium = $this->getBool();
157 $persona = $this->getBool();
158 $capeOnClassic = $this->getBool();
159 $isPrimaryUser = $this->getBool();
160 $override = $this->getBool();
170 $geometryDataVersion,
187 public function putSkin(SkinData $skin) : void{
188 $this->putString($skin->getSkinId());
189 $this->putString($skin->getPlayFabId());
190 $this->putString($skin->getResourcePatch());
191 $this->putSkinImage($skin->getSkinImage());
192 $this->putLInt(count($skin->getAnimations()));
193 foreach($skin->getAnimations() as $animation){
194 $this->putSkinImage($animation->getImage());
195 $this->putLInt($animation->getType());
196 $this->putLFloat($animation->getFrames());
197 $this->putLInt($animation->getExpressionType());
199 $this->putSkinImage($skin->getCapeImage());
200 $this->putString($skin->getGeometryData());
201 $this->putString($skin->getGeometryDataEngineVersion());
202 $this->putString($skin->getAnimationData());
203 $this->putString($skin->getCapeId());
204 $this->putString($skin->getFullSkinId());
205 $this->putString($skin->getArmSize());
206 $this->putString($skin->getSkinColor());
207 $this->putLInt(count($skin->getPersonaPieces()));
208 foreach($skin->getPersonaPieces() as $piece){
209 $this->putString($piece->getPieceId());
210 $this->putString($piece->getPieceType());
211 $this->putString($piece->getPackId());
212 $this->putBool($piece->isDefaultPiece());
213 $this->putString($piece->getProductId());
215 $this->putLInt(count($skin->getPieceTintColors()));
216 foreach($skin->getPieceTintColors() as $tint){
217 $this->putString($tint->getPieceType());
218 $this->putLInt(count($tint->getColors()));
219 foreach($tint->getColors() as $color){
220 $this->putString($color);
223 $this->putBool($skin->isPremium());
224 $this->putBool($skin->isPersona());
225 $this->putBool($skin->isPersonaCapeOnClassic());
226 $this->putBool($skin->isPrimaryUser());
227 $this->putBool($skin->isOverride());
230 private function getSkinImage() : SkinImage{
231 $width = $this->getLInt();
232 $height = $this->getLInt();
233 $data = $this->getString();
235 return new SkinImage($height, $width, $data);
236 }
catch(\InvalidArgumentException $e){
237 throw new PacketDecodeException($e->getMessage(), 0, $e);
241 private function putSkinImage(SkinImage $image) : void{
242 $this->putLInt($image->getWidth());
243 $this->putLInt($image->getHeight());
244 $this->putString($image->getData());
252 private function getItemStackHeader() : array{
253 $id = $this->getVarInt();
258 $count = $this->getLShort();
259 $meta = $this->getUnsignedVarInt();
261 return [$id, $count, $meta];
264 private function putItemStackHeader(ItemStack $itemStack) : bool{
265 if($itemStack->getId() === 0){
270 $this->putVarInt($itemStack->getId());
271 $this->putLShort($itemStack->getCount());
272 $this->putUnsignedVarInt($itemStack->getMeta());
277 private function getItemStackFooter(
int $id,
int $meta,
int $count) : ItemStack{
278 $blockRuntimeId = $this->getVarInt();
279 $rawExtraData = $this->getString();
281 return new ItemStack($id, $meta, $count, $blockRuntimeId, $rawExtraData);
284 private function putItemStackFooter(ItemStack $itemStack) : void{
285 $this->putVarInt($itemStack->getBlockRuntimeId());
286 $this->putString($itemStack->getRawExtraData());
294 [$id, $count, $meta] = $this->getItemStackHeader();
296 return $id !== 0 ? $this->getItemStackFooter($id, $meta, $count) :
ItemStack::null();
300 public function putItemStackWithoutStackId(
ItemStack $itemStack) : void{
301 if($this->putItemStackHeader($itemStack)){
302 $this->putItemStackFooter($itemStack);
307 [$id, $count, $meta] = $this->getItemStackHeader();
312 $hasNetId = $this->getBool();
313 $stackId = $hasNetId ? $this->readServerItemStackId() : 0;
315 $itemStack = $this->getItemStackFooter($id, $meta, $count);
317 return new ItemStackWrapper($stackId, $itemStack);
320 public function putItemStackWrapper(ItemStackWrapper $itemStackWrapper) : void{
321 $itemStack = $itemStackWrapper->getItemStack();
322 if($this->putItemStackHeader($itemStack)){
323 $hasNetId = $itemStackWrapper->getStackId() !== 0;
324 $this->putBool($hasNetId);
326 $this->writeServerItemStackId($itemStackWrapper->getStackId());
329 $this->putItemStackFooter($itemStack);
333 public function getRecipeIngredient() : RecipeIngredient{
334 $descriptorType = $this->getByte();
335 $descriptor = match($descriptorType){
336 ItemDescriptorType::INT_ID_META => IntIdMetaItemDescriptor::read($this),
337 ItemDescriptorType::STRING_ID_META => StringIdMetaItemDescriptor::read($this),
338 ItemDescriptorType::TAG => TagItemDescriptor::read($this),
339 ItemDescriptorType::MOLANG => MolangItemDescriptor::read($this),
340 ItemDescriptorType::COMPLEX_ALIAS => ComplexAliasItemDescriptor::read($this),
343 $count = $this->getVarInt();
345 return new RecipeIngredient($descriptor, $count);
348 public function putRecipeIngredient(RecipeIngredient $ingredient) : void{
349 $type = $ingredient->getDescriptor();
351 $this->putByte($type?->getTypeId() ?? 0);
352 $type?->write($this);
354 $this->putVarInt($ingredient->getCount());
367 $count = $this->getUnsignedVarInt();
369 for($i = 0; $i < $count; ++$i){
370 $key = $this->getUnsignedVarInt();
371 $type = $this->getUnsignedVarInt();
373 $data[$key] = $this->readMetadataProperty($type);
381 ByteMetadataProperty::ID => ByteMetadataProperty::read($this),
382 ShortMetadataProperty::ID => ShortMetadataProperty::read($this),
383 IntMetadataProperty::ID => IntMetadataProperty::read($this),
384 FloatMetadataProperty::ID => FloatMetadataProperty::read($this),
385 StringMetadataProperty::ID => StringMetadataProperty::read($this),
386 CompoundTagMetadataProperty::ID => CompoundTagMetadataProperty::read($this),
387 BlockPosMetadataProperty::ID => BlockPosMetadataProperty::read($this),
388 LongMetadataProperty::ID => LongMetadataProperty::read($this),
389 Vec3MetadataProperty::ID => Vec3MetadataProperty::read($this),
402 $this->putUnsignedVarInt(count($metadata));
403 foreach($metadata as $key => $d){
404 $this->putUnsignedVarInt($key);
405 $this->putUnsignedVarInt($d->getTypeId());
418 $count = $this->getUnsignedVarInt();
420 for($i = 0; $i < $count; ++$i){
421 $min = $this->getLFloat();
422 $max = $this->getLFloat();
423 $current = $this->getLFloat();
424 $default = $this->getLFloat();
425 $id = $this->getString();
428 for($j = 0, $modifierCount = $this->getUnsignedVarInt(); $j < $modifierCount; $j++){
429 $modifiers[] = AttributeModifier::read($this);
432 $list[] =
new Attribute($id, $min, $max, $current, $default, $modifiers);
442 $this->putUnsignedVarInt(count($attributes));
443 foreach($attributes as $attribute){
444 $this->putLFloat($attribute->getMin());
445 $this->putLFloat($attribute->getMax());
446 $this->putLFloat($attribute->getCurrent());
447 $this->putLFloat($attribute->getDefault());
448 $this->putString($attribute->getId());
450 $this->putUnsignedVarInt(count($attribute->getModifiers()));
451 foreach($attribute->getModifiers() as $modifier){
452 $modifier->write($this);
461 return $this->getVarLong();
464 public function putActorUniqueId(
int $eid) : void{
465 $this->putVarLong($eid);
472 return $this->getUnsignedVarLong();
475 public function putActorRuntimeId(
int $eid) : void{
476 $this->putUnsignedVarLong($eid);
485 $x = $this->getVarInt();
486 $y = Binary::signInt($this->getUnsignedVarInt());
487 $z = $this->getVarInt();
495 $this->putVarInt($blockPosition->getX());
496 $this->putUnsignedVarInt(Binary::unsignInt($blockPosition->getY()));
497 $this->putVarInt($blockPosition->getZ());
506 $x = $this->getVarInt();
507 $y = $this->getVarInt();
508 $z = $this->getVarInt();
516 $this->putVarInt($blockPosition->getX());
517 $this->putVarInt($blockPosition->getY());
518 $this->putVarInt($blockPosition->getZ());
527 $x = $this->getLFloat();
528 $y = $this->getLFloat();
529 $z = $this->getLFloat();
530 return new Vector3($x, $y, $z);
542 if($vector !== null){
543 $this->putVector3($vector);
545 $this->putLFloat(0.0);
546 $this->putLFloat(0.0);
547 $this->putLFloat(0.0);
555 $this->putLFloat($vector->x);
556 $this->putLFloat($vector->y);
557 $this->putLFloat($vector->z);
564 return ($this->getByte() * (360 / 256));
567 public function putRotationByte(
float $rotation) : void{
568 $this->putByte((int) ($rotation / (360 / 256)));
571 private function readGameRule(
int $type,
bool $isPlayerModifiable) :
GameRule{
573 BoolGameRule::ID => BoolGameRule::decode($this, $isPlayerModifiable),
574 IntGameRule::ID => IntGameRule::decode($this, $isPlayerModifiable),
575 FloatGameRule::ID => FloatGameRule::decode($this, $isPlayerModifiable),
590 $count = $this->getUnsignedVarInt();
592 for($i = 0; $i < $count; ++$i){
593 $name = $this->getString();
594 $isPlayerModifiable = $this->getBool();
595 $type = $this->getUnsignedVarInt();
596 $rules[$name] = $this->readGameRule($type, $isPlayerModifiable);
609 $this->putUnsignedVarInt(count($rules));
610 foreach($rules as $name => $rule){
611 $this->putString($name);
612 $this->putBool($rule->isPlayerModifiable());
613 $this->putUnsignedVarInt($rule->getTypeId());
614 $rule->encode($this);
622 $fromActorUniqueId = $this->getActorUniqueId();
623 $toActorUniqueId = $this->getActorUniqueId();
624 $type = $this->getByte();
625 $immediate = $this->getBool();
626 $causedByRider = $this->getBool();
627 return new EntityLink($fromActorUniqueId, $toActorUniqueId, $type, $immediate, $causedByRider);
630 public function putEntityLink(
EntityLink $link) : void{
631 $this->putActorUniqueId($link->fromActorUniqueId);
632 $this->putActorUniqueId($link->toActorUniqueId);
633 $this->putByte($link->type);
634 $this->putBool($link->immediate);
635 $this->putBool($link->causedByRider);
644 $result->type = $this->getUnsignedVarInt();
645 $result->uuid = $this->getUUID();
646 $result->requestId = $this->getString();
648 if($result->type === CommandOriginData::ORIGIN_DEV_CONSOLE or $result->type === CommandOriginData::ORIGIN_TEST){
649 $result->playerActorUniqueId = $this->getVarLong();
656 $this->putUnsignedVarInt($data->type);
657 $this->putUUID($data->uuid);
658 $this->putString($data->requestId);
660 if($data->type === CommandOriginData::ORIGIN_DEV_CONSOLE or $data->type === CommandOriginData::ORIGIN_TEST){
661 $this->putVarLong($data->playerActorUniqueId);
665 public function getStructureSettings() : StructureSettings{
666 $result = new StructureSettings();
668 $result->paletteName = $this->getString();
670 $result->ignoreEntities = $this->getBool();
671 $result->ignoreBlocks = $this->getBool();
672 $result->allowNonTickingChunks = $this->getBool();
674 $result->dimensions = $this->getBlockPosition();
675 $result->offset = $this->getBlockPosition();
677 $result->lastTouchedByPlayerID = $this->getActorUniqueId();
678 $result->rotation = $this->getByte();
679 $result->mirror = $this->getByte();
680 $result->animationMode = $this->getByte();
681 $result->animationSeconds = $this->getLFloat();
682 $result->integrityValue = $this->getLFloat();
683 $result->integritySeed = $this->getLInt();
684 $result->pivot = $this->getVector3();
689 public function putStructureSettings(StructureSettings $structureSettings) : void{
690 $this->putString($structureSettings->paletteName);
692 $this->putBool($structureSettings->ignoreEntities);
693 $this->putBool($structureSettings->ignoreBlocks);
694 $this->putBool($structureSettings->allowNonTickingChunks);
696 $this->putBlockPosition($structureSettings->dimensions);
697 $this->putBlockPosition($structureSettings->offset);
699 $this->putActorUniqueId($structureSettings->lastTouchedByPlayerID);
700 $this->putByte($structureSettings->rotation);
701 $this->putByte($structureSettings->mirror);
702 $this->putByte($structureSettings->animationMode);
703 $this->putLFloat($structureSettings->animationSeconds);
704 $this->putLFloat($structureSettings->integrityValue);
705 $this->putLInt($structureSettings->integritySeed);
706 $this->putVector3($structureSettings->pivot);
709 public function getStructureEditorData() : StructureEditorData{
710 $result = new StructureEditorData();
712 $result->structureName = $this->getString();
713 $result->structureDataField = $this->getString();
715 $result->includePlayers = $this->getBool();
716 $result->showBoundingBox = $this->getBool();
718 $result->structureBlockType = $this->getVarInt();
719 $result->structureSettings = $this->getStructureSettings();
720 $result->structureRedstoneSaveMode = $this->getVarInt();
725 public function putStructureEditorData(StructureEditorData $structureEditorData) : void{
726 $this->putString($structureEditorData->structureName);
727 $this->putString($structureEditorData->structureDataField);
729 $this->putBool($structureEditorData->includePlayers);
730 $this->putBool($structureEditorData->showBoundingBox);
732 $this->putVarInt($structureEditorData->structureBlockType);
733 $this->putStructureSettings($structureEditorData->structureSettings);
734 $this->putVarInt($structureEditorData->structureRedstoneSaveMode);
737 public function getNbtRoot() : TreeRoot{
738 $offset = $this->getOffset();
740 return (
new NetworkNbtSerializer())->read($this->getBuffer(), $offset, 512);
741 }
catch(NbtDataException $e){
742 throw PacketDecodeException::wrap($e,
"Failed decoding NBT root");
744 $this->setOffset($offset);
748 public function getNbtCompoundRoot() : CompoundTag{
750 return $this->getNbtRoot()->mustGetCompoundTag();
751 }
catch(NbtDataException $e){
752 throw PacketDecodeException::wrap($e,
"Expected TAG_Compound NBT root");
756 public function readRecipeNetId() : int{
757 return $this->getUnsignedVarInt();
760 public function writeRecipeNetId(
int $id) : void{
761 $this->putUnsignedVarInt($id);
764 public function readCreativeItemNetId() : int{
765 return $this->getUnsignedVarInt();
768 public function writeCreativeItemNetId(
int $id) : void{
769 $this->putUnsignedVarInt($id);
783 return $this->getVarInt();
792 $this->putVarInt($id);
795 public function readItemStackRequestId() : int{
796 return $this->getVarInt();
799 public function writeItemStackRequestId(
int $id) : void{
800 $this->putVarInt($id);
803 public function readLegacyItemStackRequestId() : int{
804 return $this->getVarInt();
807 public function writeLegacyItemStackRequestId(
int $id) : void{
808 $this->putVarInt($id);
811 public function readServerItemStackId() : int{
812 return $this->getVarInt();
815 public function writeServerItemStackId(
int $id) : void{
816 $this->putVarInt($id);
825 if($this->getBool()){
838 $this->putBool(
true);
841 $this->putBool(
false);
getItemStackWithoutStackId()
putAttributeList(Attribute ... $attributes)
putVector3Nullable(?Vector3 $vector)
putSignedBlockPosition(BlockPosition $blockPosition)
putEntityMetadata(array $metadata)
putGameRules(array $rules)
readItemStackNetIdVariant()
writeOptional(mixed $value, \Closure $writer)
putVector3(Vector3 $vector)
putBlockPosition(BlockPosition $blockPosition)
writeItemStackNetIdVariant(int $id)
readOptional(\Closure $reader)