13declare(strict_types=1);
15namespace pocketmine\network\mcpe\protocol\serializer;
60use Ramsey\Uuid\UuidInterface;
67 protected function __construct(
string $buffer =
"",
int $offset = 0){
69 parent::__construct($buffer, $offset);
72 public static function encoder() :
self{
76 public static function decoder(
string $buffer,
int $offset) :
self{
77 return new self($buffer, $offset);
87 public function putString(
string $v) : void{
88 $this->putUnsignedVarInt(strlen($v));
95 public function getUUID() : UuidInterface{
97 $p1 = strrev($this->get(8));
98 $p2 = strrev($this->
get(8));
99 return Uuid::fromBytes($p1 . $p2);
102 public function putUUID(UuidInterface $uuid) : void{
103 $bytes = $uuid->getBytes();
104 $this->put(strrev(substr($bytes, 0, 8)));
105 $this->put(strrev(substr($bytes, 8, 8)));
108 public function getSkin() :
SkinData{
109 $skinId = $this->getString();
110 $skinPlayFabId = $this->getString();
111 $skinResourcePatch = $this->getString();
112 $skinData = $this->getSkinImage();
113 $animationCount = $this->getLInt();
115 for($i = 0; $i < $animationCount; ++$i){
116 $skinImage = $this->getSkinImage();
117 $animationType = $this->getLInt();
118 $animationFrames = $this->getLFloat();
119 $expressionType = $this->getLInt();
120 $animations[] =
new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
122 $capeData = $this->getSkinImage();
123 $geometryData = $this->getString();
124 $geometryDataVersion = $this->getString();
125 $animationData = $this->getString();
126 $capeId = $this->getString();
127 $fullSkinId = $this->getString();
128 $armSize = $this->getString();
129 $skinColor = $this->getString();
130 $personaPieceCount = $this->getLInt();
132 for($i = 0; $i < $personaPieceCount; ++$i){
133 $pieceId = $this->getString();
134 $pieceType = $this->getString();
135 $packId = $this->getString();
136 $isDefaultPiece = $this->getBool();
137 $productId = $this->getString();
138 $personaPieces[] =
new PersonaSkinPiece($pieceId, $pieceType, $packId, $isDefaultPiece, $productId);
140 $pieceTintColorCount = $this->getLInt();
141 $pieceTintColors = [];
142 for($i = 0; $i < $pieceTintColorCount; ++$i){
143 $pieceType = $this->getString();
144 $colorCount = $this->getLInt();
146 for($j = 0; $j < $colorCount; ++$j){
147 $colors[] = $this->getString();
149 $pieceTintColors[] =
new PersonaPieceTintColor(
155 $premium = $this->getBool();
156 $persona = $this->getBool();
157 $capeOnClassic = $this->getBool();
158 $isPrimaryUser = $this->getBool();
159 $override = $this->getBool();
169 $geometryDataVersion,
186 public function putSkin(SkinData $skin) : void{
187 $this->putString($skin->getSkinId());
188 $this->putString($skin->getPlayFabId());
189 $this->putString($skin->getResourcePatch());
190 $this->putSkinImage($skin->getSkinImage());
191 $this->putLInt(count($skin->getAnimations()));
192 foreach($skin->getAnimations() as $animation){
193 $this->putSkinImage($animation->getImage());
194 $this->putLInt($animation->getType());
195 $this->putLFloat($animation->getFrames());
196 $this->putLInt($animation->getExpressionType());
198 $this->putSkinImage($skin->getCapeImage());
199 $this->putString($skin->getGeometryData());
200 $this->putString($skin->getGeometryDataEngineVersion());
201 $this->putString($skin->getAnimationData());
202 $this->putString($skin->getCapeId());
203 $this->putString($skin->getFullSkinId());
204 $this->putString($skin->getArmSize());
205 $this->putString($skin->getSkinColor());
206 $this->putLInt(count($skin->getPersonaPieces()));
207 foreach($skin->getPersonaPieces() as $piece){
208 $this->putString($piece->getPieceId());
209 $this->putString($piece->getPieceType());
210 $this->putString($piece->getPackId());
211 $this->putBool($piece->isDefaultPiece());
212 $this->putString($piece->getProductId());
214 $this->putLInt(count($skin->getPieceTintColors()));
215 foreach($skin->getPieceTintColors() as $tint){
216 $this->putString($tint->getPieceType());
217 $this->putLInt(count($tint->getColors()));
218 foreach($tint->getColors() as $color){
219 $this->putString($color);
222 $this->putBool($skin->isPremium());
223 $this->putBool($skin->isPersona());
224 $this->putBool($skin->isPersonaCapeOnClassic());
225 $this->putBool($skin->isPrimaryUser());
226 $this->putBool($skin->isOverride());
229 private function getSkinImage() : SkinImage{
230 $width = $this->getLInt();
231 $height = $this->getLInt();
232 $data = $this->getString();
234 return new SkinImage($height, $width, $data);
235 }
catch(\InvalidArgumentException $e){
236 throw new PacketDecodeException($e->getMessage(), 0, $e);
240 private function putSkinImage(SkinImage $image) : void{
241 $this->putLInt($image->getWidth());
242 $this->putLInt($image->getHeight());
243 $this->putString($image->getData());
251 private function getItemStackHeader() : array{
252 $id = $this->getVarInt();
257 $count = $this->getLShort();
258 $meta = $this->getUnsignedVarInt();
260 return [$id, $count, $meta];
263 private function putItemStackHeader(ItemStack $itemStack) : bool{
264 if($itemStack->getId() === 0){
269 $this->putVarInt($itemStack->getId());
270 $this->putLShort($itemStack->getCount());
271 $this->putUnsignedVarInt($itemStack->getMeta());
276 private function getItemStackFooter(
int $id,
int $meta,
int $count) : ItemStack{
277 $blockRuntimeId = $this->getVarInt();
278 $rawExtraData = $this->getString();
280 return new ItemStack($id, $meta, $count, $blockRuntimeId, $rawExtraData);
283 private function putItemStackFooter(ItemStack $itemStack) : void{
284 $this->putVarInt($itemStack->getBlockRuntimeId());
285 $this->putString($itemStack->getRawExtraData());
293 [$id, $count, $meta] = $this->getItemStackHeader();
295 return $id !== 0 ? $this->getItemStackFooter($id, $meta, $count) :
ItemStack::null();
299 public function putItemStackWithoutStackId(
ItemStack $itemStack) : void{
300 if($this->putItemStackHeader($itemStack)){
301 $this->putItemStackFooter($itemStack);
306 [$id, $count, $meta] = $this->getItemStackHeader();
311 $hasNetId = $this->getBool();
312 $stackId = $hasNetId ? $this->readServerItemStackId() : 0;
314 $itemStack = $this->getItemStackFooter($id, $meta, $count);
316 return new ItemStackWrapper($stackId, $itemStack);
319 public function putItemStackWrapper(ItemStackWrapper $itemStackWrapper) : void{
320 $itemStack = $itemStackWrapper->getItemStack();
321 if($this->putItemStackHeader($itemStack)){
322 $hasNetId = $itemStackWrapper->getStackId() !== 0;
323 $this->putBool($hasNetId);
325 $this->writeServerItemStackId($itemStackWrapper->getStackId());
328 $this->putItemStackFooter($itemStack);
332 public function getRecipeIngredient() : RecipeIngredient{
333 $descriptorType = $this->getByte();
334 $descriptor = match($descriptorType){
335 ItemDescriptorType::INT_ID_META => IntIdMetaItemDescriptor::read($this),
336 ItemDescriptorType::STRING_ID_META => StringIdMetaItemDescriptor::read($this),
337 ItemDescriptorType::TAG => TagItemDescriptor::read($this),
338 ItemDescriptorType::MOLANG => MolangItemDescriptor::read($this),
339 ItemDescriptorType::COMPLEX_ALIAS => ComplexAliasItemDescriptor::read($this),
342 $count = $this->getVarInt();
344 return new RecipeIngredient($descriptor, $count);
347 public function putRecipeIngredient(RecipeIngredient $ingredient) : void{
348 $type = $ingredient->getDescriptor();
350 $this->putByte($type?->getTypeId() ?? 0);
351 $type?->write($this);
353 $this->putVarInt($ingredient->getCount());
366 $count = $this->getUnsignedVarInt();
368 for($i = 0; $i < $count; ++$i){
369 $key = $this->getUnsignedVarInt();
370 $type = $this->getUnsignedVarInt();
372 $data[$key] = $this->readMetadataProperty($type);
380 ByteMetadataProperty::ID => ByteMetadataProperty::read($this),
381 ShortMetadataProperty::ID => ShortMetadataProperty::read($this),
382 IntMetadataProperty::ID => IntMetadataProperty::read($this),
383 FloatMetadataProperty::ID => FloatMetadataProperty::read($this),
384 StringMetadataProperty::ID => StringMetadataProperty::read($this),
385 CompoundTagMetadataProperty::ID => CompoundTagMetadataProperty::read($this),
386 BlockPosMetadataProperty::ID => BlockPosMetadataProperty::read($this),
387 LongMetadataProperty::ID => LongMetadataProperty::read($this),
388 Vec3MetadataProperty::ID => Vec3MetadataProperty::read($this),
401 $this->putUnsignedVarInt(count($metadata));
402 foreach($metadata as $key => $d){
403 $this->putUnsignedVarInt($key);
404 $this->putUnsignedVarInt($d->getTypeId());
413 return $this->getVarLong();
416 public function putActorUniqueId(
int $eid) : void{
417 $this->putVarLong($eid);
424 return $this->getUnsignedVarLong();
427 public function putActorRuntimeId(
int $eid) : void{
428 $this->putUnsignedVarLong($eid);
437 $x = $this->getVarInt();
438 $y = Binary::signInt($this->getUnsignedVarInt());
439 $z = $this->getVarInt();
447 $this->putVarInt($blockPosition->getX());
448 $this->putUnsignedVarInt(Binary::unsignInt($blockPosition->getY()));
449 $this->putVarInt($blockPosition->getZ());
458 $x = $this->getVarInt();
459 $y = $this->getVarInt();
460 $z = $this->getVarInt();
468 $this->putVarInt($blockPosition->getX());
469 $this->putVarInt($blockPosition->getY());
470 $this->putVarInt($blockPosition->getZ());
479 $x = $this->getLFloat();
480 $y = $this->getLFloat();
481 $z = $this->getLFloat();
482 return new Vector3($x, $y, $z);
491 $x = $this->getLFloat();
492 $y = $this->getLFloat();
505 if($vector !== null){
506 $this->putVector3($vector);
508 $this->putLFloat(0.0);
509 $this->putLFloat(0.0);
510 $this->putLFloat(0.0);
518 $this->putLFloat($vector->x);
519 $this->putLFloat($vector->y);
520 $this->putLFloat($vector->z);
527 $this->putLFloat($vector2->x);
528 $this->putLFloat($vector2->y);
535 return ($this->getByte() * (360 / 256));
538 public function putRotationByte(
float $rotation) : void{
539 $this->putByte((int) ($rotation / (360 / 256)));
542 private function readGameRule(
int $type,
bool $isPlayerModifiable) :
GameRule{
544 BoolGameRule::ID => BoolGameRule::decode($this, $isPlayerModifiable),
545 IntGameRule::ID => IntGameRule::decode($this, $isPlayerModifiable),
546 FloatGameRule::ID => FloatGameRule::decode($this, $isPlayerModifiable),
561 $count = $this->getUnsignedVarInt();
563 for($i = 0; $i < $count; ++$i){
564 $name = $this->getString();
565 $isPlayerModifiable = $this->getBool();
566 $type = $this->getUnsignedVarInt();
567 $rules[$name] = $this->readGameRule($type, $isPlayerModifiable);
580 $this->putUnsignedVarInt(count($rules));
581 foreach($rules as $name => $rule){
582 $this->putString($name);
583 $this->putBool($rule->isPlayerModifiable());
584 $this->putUnsignedVarInt($rule->getTypeId());
585 $rule->encode($this);
593 $fromActorUniqueId = $this->getActorUniqueId();
594 $toActorUniqueId = $this->getActorUniqueId();
595 $type = $this->getByte();
596 $immediate = $this->getBool();
597 $causedByRider = $this->getBool();
598 $vehicleAngularVelocity = $this->getLFloat();
599 return new EntityLink($fromActorUniqueId, $toActorUniqueId, $type, $immediate, $causedByRider, $vehicleAngularVelocity);
602 public function putEntityLink(
EntityLink $link) : void{
603 $this->putActorUniqueId($link->fromActorUniqueId);
604 $this->putActorUniqueId($link->toActorUniqueId);
605 $this->putByte($link->type);
606 $this->putBool($link->immediate);
607 $this->putBool($link->causedByRider);
608 $this->putLFloat($link->vehicleAngularVelocity);
617 $result->type = $this->getUnsignedVarInt();
618 $result->uuid = $this->getUUID();
619 $result->requestId = $this->getString();
621 if($result->type === CommandOriginData::ORIGIN_DEV_CONSOLE or $result->type === CommandOriginData::ORIGIN_TEST){
622 $result->playerActorUniqueId = $this->getVarLong();
629 $this->putUnsignedVarInt($data->type);
630 $this->putUUID($data->uuid);
631 $this->putString($data->requestId);
633 if($data->type === CommandOriginData::ORIGIN_DEV_CONSOLE or $data->type === CommandOriginData::ORIGIN_TEST){
634 $this->putVarLong($data->playerActorUniqueId);
638 public function getStructureSettings() : StructureSettings{
639 $result = new StructureSettings();
641 $result->paletteName = $this->getString();
643 $result->ignoreEntities = $this->getBool();
644 $result->ignoreBlocks = $this->getBool();
645 $result->allowNonTickingChunks = $this->getBool();
647 $result->dimensions = $this->getBlockPosition();
648 $result->offset = $this->getBlockPosition();
650 $result->lastTouchedByPlayerID = $this->getActorUniqueId();
651 $result->rotation = $this->getByte();
652 $result->mirror = $this->getByte();
653 $result->animationMode = $this->getByte();
654 $result->animationSeconds = $this->getLFloat();
655 $result->integrityValue = $this->getLFloat();
656 $result->integritySeed = $this->getLInt();
657 $result->pivot = $this->getVector3();
662 public function putStructureSettings(StructureSettings $structureSettings) : void{
663 $this->putString($structureSettings->paletteName);
665 $this->putBool($structureSettings->ignoreEntities);
666 $this->putBool($structureSettings->ignoreBlocks);
667 $this->putBool($structureSettings->allowNonTickingChunks);
669 $this->putBlockPosition($structureSettings->dimensions);
670 $this->putBlockPosition($structureSettings->offset);
672 $this->putActorUniqueId($structureSettings->lastTouchedByPlayerID);
673 $this->putByte($structureSettings->rotation);
674 $this->putByte($structureSettings->mirror);
675 $this->putByte($structureSettings->animationMode);
676 $this->putLFloat($structureSettings->animationSeconds);
677 $this->putLFloat($structureSettings->integrityValue);
678 $this->putLInt($structureSettings->integritySeed);
679 $this->putVector3($structureSettings->pivot);
682 public function getStructureEditorData() : StructureEditorData{
683 $result = new StructureEditorData();
685 $result->structureName = $this->getString();
686 $result->structureDataField = $this->getString();
688 $result->includePlayers = $this->getBool();
689 $result->showBoundingBox = $this->getBool();
691 $result->structureBlockType = $this->getVarInt();
692 $result->structureSettings = $this->getStructureSettings();
693 $result->structureRedstoneSaveMode = $this->getVarInt();
698 public function putStructureEditorData(StructureEditorData $structureEditorData) : void{
699 $this->putString($structureEditorData->structureName);
700 $this->putString($structureEditorData->structureDataField);
702 $this->putBool($structureEditorData->includePlayers);
703 $this->putBool($structureEditorData->showBoundingBox);
705 $this->putVarInt($structureEditorData->structureBlockType);
706 $this->putStructureSettings($structureEditorData->structureSettings);
707 $this->putVarInt($structureEditorData->structureRedstoneSaveMode);
710 public function getNbtRoot() : TreeRoot{
711 $offset = $this->getOffset();
713 return (
new NetworkNbtSerializer())->read($this->getBuffer(), $offset, 512);
714 }
catch(NbtDataException $e){
715 throw PacketDecodeException::wrap($e,
"Failed decoding NBT root");
717 $this->setOffset($offset);
721 public function getNbtCompoundRoot() : CompoundTag{
723 return $this->getNbtRoot()->mustGetCompoundTag();
724 }
catch(NbtDataException $e){
725 throw PacketDecodeException::wrap($e,
"Expected TAG_Compound NBT root");
729 public function readRecipeNetId() : int{
730 return $this->getUnsignedVarInt();
733 public function writeRecipeNetId(
int $id) : void{
734 $this->putUnsignedVarInt($id);
737 public function readCreativeItemNetId() : int{
738 return $this->getUnsignedVarInt();
741 public function writeCreativeItemNetId(
int $id) : void{
742 $this->putUnsignedVarInt($id);
756 return $this->getVarInt();
765 $this->putVarInt($id);
768 public function readItemStackRequestId() : int{
769 return $this->getVarInt();
772 public function writeItemStackRequestId(
int $id) : void{
773 $this->putVarInt($id);
776 public function readLegacyItemStackRequestId() : int{
777 return $this->getVarInt();
780 public function writeLegacyItemStackRequestId(
int $id) : void{
781 $this->putVarInt($id);
784 public function readServerItemStackId() : int{
785 return $this->getVarInt();
788 public function writeServerItemStackId(
int $id) : void{
789 $this->putVarInt($id);
798 if($this->getBool()){
811 $this->putBool(
true);
814 $this->putBool(
false);
getItemStackWithoutStackId()
putVector3Nullable(?Vector3 $vector)
putSignedBlockPosition(BlockPosition $blockPosition)
putEntityMetadata(array $metadata)
putGameRules(array $rules)
putVector2(Vector2 $vector2)
readItemStackNetIdVariant()
writeOptional(mixed $value, \Closure $writer)
putVector3(Vector3 $vector)
putBlockPosition(BlockPosition $blockPosition)
writeItemStackNetIdVariant(int $id)
readOptional(\Closure $reader)