71 private function __construct(){
76 public static function getString(ByteBufferReader $in) : string{
77 return $in->readByteArray(VarInt::readUnsignedInt($in));
80 public static function putString(ByteBufferWriter $out,
string $v) : void{
81 VarInt::writeUnsignedInt($out, strlen($v));
82 $out->writeByteArray($v);
86 public static function getBool(ByteBufferReader $in) : bool{
87 return Byte::readUnsigned($in) !== 0;
90 public static function putBool(ByteBufferWriter $out,
bool $v) : void{
91 Byte::writeUnsigned($out, $v ? 1 : 0);
95 public static function getUUID(ByteBufferReader $in) : UuidInterface{
97 $p1 = strrev($in->readByteArray(8));
98 $p2 = strrev($in->readByteArray(8));
99 return Uuid::fromBytes($p1 . $p2);
102 public static function putUUID(ByteBufferWriter $out, UuidInterface $uuid) : void{
103 $bytes = $uuid->getBytes();
104 $out->writeByteArray(strrev(substr($bytes, 0, 8)));
105 $out->writeByteArray(strrev(substr($bytes, 8, 8)));
110 $skinId = self::getString($in);
111 $skinPlayFabId = self::getString($in);
112 $skinResourcePatch = self::getString($in);
113 $skinData = self::getSkinImage($in);
114 $animationCount = LE::readUnsignedInt($in);
116 for($i = 0; $i < $animationCount; ++$i){
117 $skinImage = self::getSkinImage($in);
118 $animationType = LE::readUnsignedInt($in);
119 $animationFrames = LE::readFloat($in);
120 $expressionType = LE::readUnsignedInt($in);
121 $animations[] =
new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
123 $capeData = self::getSkinImage($in);
124 $geometryData = self::getString($in);
125 $geometryDataVersion = self::getString($in);
126 $animationData = self::getString($in);
127 $capeId = self::getString($in);
128 $fullSkinId = self::getString($in);
129 $armSize = self::getString($in);
130 $skinColor = self::getString($in);
131 $personaPieceCount = LE::readUnsignedInt($in);
133 for($i = 0; $i < $personaPieceCount; ++$i){
134 $pieceId = self::getString($in);
135 $pieceType = self::getString($in);
136 $packId = self::getString($in);
137 $isDefaultPiece = self::getBool($in);
138 $productId = self::getString($in);
139 $personaPieces[] =
new PersonaSkinPiece($pieceId, $pieceType, $packId, $isDefaultPiece, $productId);
141 $pieceTintColorCount = LE::readUnsignedInt($in);
142 $pieceTintColors = [];
143 for($i = 0; $i < $pieceTintColorCount; ++$i){
144 $pieceType = self::getString($in);
145 $colorCount = LE::readUnsignedInt($in);
147 for($j = 0; $j < $colorCount; ++$j){
148 $colors[] = self::getString($in);
150 $pieceTintColors[] =
new PersonaPieceTintColor(
156 $premium = self::getBool($in);
157 $persona = self::getBool($in);
158 $capeOnClassic = self::getBool($in);
159 $isPrimaryUser = self::getBool($in);
160 $override = self::getBool($in);
170 $geometryDataVersion,
187 public static function putSkin(ByteBufferWriter $out, SkinData $skin) : void{
188 self::putString($out, $skin->getSkinId());
189 self::putString($out, $skin->getPlayFabId());
190 self::putString($out, $skin->getResourcePatch());
191 self::putSkinImage($out, $skin->getSkinImage());
192 LE::writeUnsignedInt($out, count($skin->getAnimations()));
193 foreach($skin->getAnimations() as $animation){
194 self::putSkinImage($out, $animation->getImage());
195 LE::writeUnsignedInt($out, $animation->getType());
196 LE::writeFloat($out, $animation->getFrames());
197 LE::writeUnsignedInt($out, $animation->getExpressionType());
199 self::putSkinImage($out, $skin->getCapeImage());
200 self::putString($out, $skin->getGeometryData());
201 self::putString($out, $skin->getGeometryDataEngineVersion());
202 self::putString($out, $skin->getAnimationData());
203 self::putString($out, $skin->getCapeId());
204 self::putString($out, $skin->getFullSkinId());
205 self::putString($out, $skin->getArmSize());
206 self::putString($out, $skin->getSkinColor());
207 LE::writeUnsignedInt($out, count($skin->getPersonaPieces()));
208 foreach($skin->getPersonaPieces() as $piece){
209 self::putString($out, $piece->getPieceId());
210 self::putString($out, $piece->getPieceType());
211 self::putString($out, $piece->getPackId());
212 self::putBool($out, $piece->isDefaultPiece());
213 self::putString($out, $piece->getProductId());
215 LE::writeUnsignedInt($out, count($skin->getPieceTintColors()));
216 foreach($skin->getPieceTintColors() as $tint){
217 self::putString($out, $tint->getPieceType());
218 LE::writeUnsignedInt($out, count($tint->getColors()));
219 foreach($tint->getColors() as $color){
220 self::putString($out, $color);
223 self::putBool($out, $skin->isPremium());
224 self::putBool($out, $skin->isPersona());
225 self::putBool($out, $skin->isPersonaCapeOnClassic());
226 self::putBool($out, $skin->isPrimaryUser());
227 self::putBool($out, $skin->isOverride());
231 private static function getSkinImage(ByteBufferReader $in) : SkinImage{
232 $width = LE::readUnsignedInt($in);
233 $height = LE::readUnsignedInt($in);
234 $data = self::getString($in);
236 return new SkinImage($height, $width, $data);
237 }
catch(\InvalidArgumentException $e){
238 throw new PacketDecodeException($e->getMessage(), 0, $e);
242 private static function putSkinImage(ByteBufferWriter $out, SkinImage $image) : void{
243 LE::writeUnsignedInt($out, $image->getWidth());
244 LE::writeUnsignedInt($out, $image->getHeight());
245 self::putString($out, $image->getData());
253 private static function getItemStackHeader(ByteBufferReader $in) : array{
254 $id = VarInt::readSignedInt($in);
259 $count = LE::readUnsignedShort($in);
260 $meta = VarInt::readUnsignedInt($in);
262 return [$id, $count, $meta];
265 private static function putItemStackHeader(ByteBufferWriter $out, ItemStack $itemStack) : bool{
266 if($itemStack->getId() === 0){
267 VarInt::writeSignedInt($out, 0);
271 VarInt::writeSignedInt($out, $itemStack->getId());
272 LE::writeUnsignedShort($out, $itemStack->getCount());
273 VarInt::writeUnsignedInt($out, $itemStack->getMeta());
279 private static function getItemStackFooter(ByteBufferReader $in,
int $id,
int $meta,
int $count) : ItemStack{
280 $blockRuntimeId = VarInt::readSignedInt($in);
281 $rawExtraData = self::getString($in);
283 return new ItemStack($id, $meta, $count, $blockRuntimeId, $rawExtraData);
286 private static function putItemStackFooter(ByteBufferWriter $out, ItemStack $itemStack) : void{
287 VarInt::writeSignedInt($out, $itemStack->getBlockRuntimeId());
288 self::putString($out, $itemStack->getRawExtraData());
296 [$id, $count, $meta] = self::getItemStackHeader($in);
298 return $id !== 0 ? self::getItemStackFooter($in, $id, $meta, $count) :
ItemStack::null();
302 public static function putItemStackWithoutStackId(ByteBufferWriter $out,
ItemStack $itemStack) : void{
303 if(self::putItemStackHeader($out, $itemStack)){
304 self::putItemStackFooter($out, $itemStack);
310 [$id, $count, $meta] = self::getItemStackHeader($in);
315 $hasNetId = self::getBool($in);
316 $stackId = $hasNetId ? self::readServerItemStackId($in) : 0;
318 $itemStack = self::getItemStackFooter($in, $id, $meta, $count);
323 public static function putItemStackWrapper(ByteBufferWriter $out,
ItemStackWrapper $itemStackWrapper) : void{
324 $itemStack = $itemStackWrapper->getItemStack();
325 if(self::putItemStackHeader($out, $itemStack)){
326 $hasNetId = $itemStackWrapper->getStackId() !== 0;
327 self::putBool($out, $hasNetId);
329 self::writeServerItemStackId($out, $itemStackWrapper->getStackId());
332 self::putItemStackFooter($out, $itemStack);
336 public static function getNetworkItemStackDescriptor(ByteBufferReader $in) : ItemStackWrapper{
337 $id = LE::readSignedShort($in);
338 $count = LE::readUnsignedShort($in);
339 $meta = VarInt::readUnsignedInt($in);
341 $hasNetId = self::getBool($in);
343 $variant = VarInt::readUnsignedInt($in);
344 $stackId = VarInt::readSignedInt($in);
350 $blockRuntimeId = VarInt::readUnsignedInt($in);
351 $rawExtraData = self::getString($in);
353 return new ItemStackWrapper($stackId,
new ItemStack($id, $meta, $count, $blockRuntimeId, $rawExtraData), $variant);
356 public static function putNetworkItemStackDescriptor(ByteBufferWriter $out, ItemStackWrapper $itemStackWrapper) : void{
357 LE::writeSignedShort($out, $itemStackWrapper->getItemStack()->getId());
358 LE::writeUnsignedShort($out, $itemStackWrapper->getItemStack()->getCount());
359 VarInt::writeUnsignedInt($out, $itemStackWrapper->getItemStack()->getMeta());
361 self::putBool($out, $hasNetId = $itemStackWrapper->getStackId() !== 0);
363 VarInt::writeUnsignedInt($out, $itemStackWrapper->getStackIdVariant());
364 VarInt::writeSignedInt($out, $itemStackWrapper->getStackId());
367 VarInt::writeUnsignedInt($out, $itemStackWrapper->getItemStack()->getBlockRuntimeId());
368 self::putString($out, $itemStackWrapper->getItemStack()->getRawExtraData());
373 $descriptorType = Byte::readUnsigned($in);
374 $descriptor = match($descriptorType){
375 ItemDescriptorType::INT_ID_META => IntIdMetaItemDescriptor::read($in),
376 ItemDescriptorType::STRING_ID_META => StringIdMetaItemDescriptor::read($in),
377 ItemDescriptorType::TAG => TagItemDescriptor::read($in),
378 ItemDescriptorType::MOLANG => MolangItemDescriptor::read($in),
379 ItemDescriptorType::COMPLEX_ALIAS => ComplexAliasItemDescriptor::read($in),
382 $count = VarInt::readSignedInt($in);
387 public static function putRecipeIngredient(ByteBufferWriter $out, RecipeIngredient $ingredient) : void{
388 $type = $ingredient->getDescriptor();
390 Byte::writeUnsigned($out, $type?->getTypeId() ?? 0);
393 VarInt::writeSignedInt($out, $ingredient->getCount());
406 $count = VarInt::readUnsignedInt($in);
408 for($i = 0; $i < $count; ++$i){
409 $key = VarInt::readUnsignedInt($in);
410 $type = VarInt::readUnsignedInt($in);
412 $data[$key] = self::readMetadataProperty($in, $type);
419 private static function readMetadataProperty(ByteBufferReader $in,
int $type) :
MetadataProperty{
421 ByteMetadataProperty::ID => ByteMetadataProperty::read($in),
422 ShortMetadataProperty::ID => ShortMetadataProperty::read($in),
423 IntMetadataProperty::ID => IntMetadataProperty::read($in),
424 FloatMetadataProperty::ID => FloatMetadataProperty::read($in),
425 StringMetadataProperty::ID => StringMetadataProperty::read($in),
426 CompoundTagMetadataProperty::ID => CompoundTagMetadataProperty::read($in),
427 BlockPosMetadataProperty::ID => BlockPosMetadataProperty::read($in),
428 LongMetadataProperty::ID => LongMetadataProperty::read($in),
429 Vec3MetadataProperty::ID => Vec3MetadataProperty::read($in),
442 VarInt::writeUnsignedInt($out, count($metadata));
443 foreach($metadata as $key => $d){
444 VarInt::writeUnsignedInt($out, $key);
445 VarInt::writeUnsignedInt($out, $d->getTypeId());
452 return VarInt::readSignedLong($in);
455 public static function putActorUniqueId(ByteBufferWriter $out,
int $eid) : void{
456 VarInt::writeSignedLong($out, $eid);
461 return VarInt::readUnsignedLong($in);
464 public static function putActorRuntimeId(ByteBufferWriter $out,
int $eid) : void{
465 VarInt::writeUnsignedLong($out, $eid);
474 $x = VarInt::readSignedInt($in);
475 $y = VarInt::readSignedInt($in);
476 $z = VarInt::readSignedInt($in);
484 VarInt::writeSignedInt($out, $blockPosition->getX());
485 VarInt::writeSignedInt($out, $blockPosition->getY());
486 VarInt::writeSignedInt($out, $blockPosition->getZ());
495 $x = LE::readFloat($in);
496 $y = LE::readFloat($in);
497 $z = LE::readFloat($in);
498 return new Vector3($x, $y, $z);
507 $x = LE::readFloat($in);
508 $y = LE::readFloat($in);
521 if($vector !== null){
522 self::putVector3($out, $vector);
524 LE::writeFloat($out, 0.0);
525 LE::writeFloat($out, 0.0);
526 LE::writeFloat($out, 0.0);
534 LE::writeFloat($out, $vector->x);
535 LE::writeFloat($out, $vector->y);
536 LE::writeFloat($out, $vector->z);
543 LE::writeFloat($out, $vector2->x);
544 LE::writeFloat($out, $vector2->y);
549 return Byte::readUnsigned($in) * (360 / 256);
552 public static function putRotationByte(ByteBufferWriter $out,
float $rotation) : void{
553 Byte::writeUnsigned($out, (int) ($rotation / (360 / 256)));
557 private static function readGameRule(ByteBufferReader $in,
int $type,
bool $isPlayerModifiable,
bool $isStartGame) :
GameRule{
559 BoolGameRule::ID => BoolGameRule::decode($in, $isPlayerModifiable),
560 IntGameRule::ID => IntGameRule::decode($in, $isPlayerModifiable, $isStartGame),
561 FloatGameRule::ID => FloatGameRule::decode($in, $isPlayerModifiable),
575 public static function getGameRules(ByteBufferReader $in,
bool $isStartGame) : array{
576 $count = VarInt::readUnsignedInt($in);
578 for($i = 0; $i < $count; ++$i){
579 $name = self::getString($in);
580 $isPlayerModifiable = self::getBool($in);
581 $type = VarInt::readUnsignedInt($in);
582 $rules[$name] = self::readGameRule($in, $type, $isPlayerModifiable, $isStartGame);
594 public static function putGameRules(ByteBufferWriter $out, array $rules,
bool $isStartGame) : void{
595 VarInt::writeUnsignedInt($out, count($rules));
596 foreach($rules as $name => $rule){
597 self::putString($out, $name);
598 self::putBool($out, $rule->isPlayerModifiable());
599 VarInt::writeUnsignedInt($out, $rule->getTypeId());
600 $rule->encode($out, $isStartGame);
606 $fromActorUniqueId = self::getActorUniqueId($in);
607 $toActorUniqueId = self::getActorUniqueId($in);
608 $type = Byte::readUnsigned($in);
609 $immediate = self::getBool($in);
610 $causedByRider = self::getBool($in);
611 $vehicleAngularVelocity = LE::readFloat($in);
612 return new EntityLink($fromActorUniqueId, $toActorUniqueId, $type, $immediate, $causedByRider, $vehicleAngularVelocity);
615 public static function putEntityLink(ByteBufferWriter $out,
EntityLink $link) : void{
616 self::putActorUniqueId($out, $link->fromActorUniqueId);
617 self::putActorUniqueId($out, $link->toActorUniqueId);
618 Byte::writeUnsigned($out, $link->type);
619 self::putBool($out, $link->immediate);
620 self::putBool($out, $link->causedByRider);
621 LE::writeFloat($out, $link->vehicleAngularVelocity);
628 $result->type = CommonTypes::getString($in);
629 $result->uuid = self::getUUID($in);
630 $result->requestId = self::getString($in);
631 $result->playerActorUniqueId = LE::readSignedLong($in);
636 public static function putCommandOriginData(ByteBufferWriter $out,
CommandOriginData $data) : void{
637 self::putString($out, $data->type);
638 self::putUUID($out, $data->uuid);
639 self::putString($out, $data->requestId);
640 LE::writeSignedLong($out, $data->playerActorUniqueId);
647 $result->paletteName = self::getString($in);
649 $result->ignoreEntities = self::getBool($in);
650 $result->ignoreBlocks = self::getBool($in);
651 $result->allowNonTickingChunks = self::getBool($in);
653 $result->dimensions = self::getBlockPosition($in);
654 $result->offset = self::getBlockPosition($in);
656 $result->lastTouchedByPlayerID = self::getActorUniqueId($in);
657 $result->rotation = Byte::readUnsigned($in);
658 $result->mirror = Byte::readUnsigned($in);
659 $result->animationMode = Byte::readUnsigned($in);
660 $result->animationSeconds = LE::readFloat($in);
661 $result->integrityValue = LE::readFloat($in);
662 $result->integritySeed = LE::readUnsignedInt($in);
663 $result->pivot = self::getVector3($in);
668 public static function putStructureSettings(ByteBufferWriter $out,
StructureSettings $structureSettings) : void{
669 self::putString($out, $structureSettings->paletteName);
671 self::putBool($out, $structureSettings->ignoreEntities);
672 self::putBool($out, $structureSettings->ignoreBlocks);
673 self::putBool($out, $structureSettings->allowNonTickingChunks);
675 self::putBlockPosition($out, $structureSettings->dimensions);
676 self::putBlockPosition($out, $structureSettings->offset);
678 self::putActorUniqueId($out, $structureSettings->lastTouchedByPlayerID);
679 Byte::writeUnsigned($out, $structureSettings->rotation);
680 Byte::writeUnsigned($out, $structureSettings->mirror);
681 Byte::writeUnsigned($out, $structureSettings->animationMode);
682 LE::writeFloat($out, $structureSettings->animationSeconds);
683 LE::writeFloat($out, $structureSettings->integrityValue);
684 LE::writeUnsignedInt($out, $structureSettings->integritySeed);
685 self::putVector3($out, $structureSettings->pivot);
692 $result->structureName = self::getString($in);
693 $result->filteredStructureName = self::getString($in);
694 $result->structureDataField = self::getString($in);
696 $result->includePlayers = self::getBool($in);
697 $result->showBoundingBox = self::getBool($in);
699 $result->structureBlockType = VarInt::readSignedInt($in);
700 $result->structureSettings = self::getStructureSettings($in);
701 $result->structureRedstoneSaveMode = VarInt::readSignedInt($in);
706 public static function putStructureEditorData(ByteBufferWriter $out,
StructureEditorData $structureEditorData) : void{
707 self::putString($out, $structureEditorData->structureName);
708 self::putString($out, $structureEditorData->filteredStructureName);
709 self::putString($out, $structureEditorData->structureDataField);
711 self::putBool($out, $structureEditorData->includePlayers);
712 self::putBool($out, $structureEditorData->showBoundingBox);
714 VarInt::writeSignedInt($out, $structureEditorData->structureBlockType);
715 self::putStructureSettings($out, $structureEditorData->structureSettings);
716 VarInt::writeSignedInt($out, $structureEditorData->structureRedstoneSaveMode);
721 $offset = $in->getOffset();
725 throw PacketDecodeException::wrap($e,
"Failed decoding NBT root");
727 $in->setOffset($offset);
731 public static function getNbtCompoundRoot(ByteBufferReader $in) :
CompoundTag{
733 return self::getNbtRoot($in)->mustGetCompoundTag();
734 }
catch(NbtDataException $e){
735 throw PacketDecodeException::wrap($e,
"Expected TAG_Compound NBT root");
741 return VarInt::readUnsignedInt($in);
744 public static function writeRecipeNetId(ByteBufferWriter $out,
int $id) : void{
745 VarInt::writeUnsignedInt($out, $id);
750 return VarInt::readUnsignedInt($in);
753 public static function writeCreativeItemNetId(ByteBufferWriter $out,
int $id) : void{
754 VarInt::writeUnsignedInt($out, $id);
770 return VarInt::readSignedInt($in);
779 VarInt::writeSignedInt($out, $id);
784 return VarInt::readSignedInt($in);
787 public static function writeItemStackRequestId(ByteBufferWriter $out,
int $id) : void{
788 VarInt::writeSignedInt($out, $id);
793 return VarInt::readSignedInt($in);
796 public static function writeLegacyItemStackRequestId(ByteBufferWriter $out,
int $id) : void{
797 VarInt::writeSignedInt($out, $id);
802 return VarInt::readSignedInt($in);
805 public static function writeServerItemStackId(ByteBufferWriter $out,
int $id) : void{
806 VarInt::writeSignedInt($out, $id);
815 public static function readOptional(ByteBufferReader $in, \Closure $reader) : mixed{
816 if(self::getBool($in)){
827 public static function writeOptional(ByteBufferWriter $out, mixed $value, \Closure $writer) : void{
829 self::putBool($out,
true);
830 $writer($out, $value);
832 self::putBool($out,
false);