72 private function __construct(){
77 public static function getString(ByteBufferReader $in) : string{
78 return $in->readByteArray(VarInt::readUnsignedInt($in));
81 public static function putString(ByteBufferWriter $out,
string $v) : void{
82 VarInt::writeUnsignedInt($out, strlen($v));
83 $out->writeByteArray($v);
87 public static function getBool(ByteBufferReader $in) : bool{
88 return Byte::readUnsigned($in) !== 0;
91 public static function putBool(ByteBufferWriter $out,
bool $v) : void{
92 Byte::writeUnsigned($out, $v ? 1 : 0);
96 public static function getUUID(ByteBufferReader $in) : UuidInterface{
98 $p1 = strrev($in->readByteArray(8));
99 $p2 = strrev($in->readByteArray(8));
100 return Uuid::fromBytes($p1 . $p2);
103 public static function putUUID(ByteBufferWriter $out, UuidInterface $uuid) : void{
104 $bytes = $uuid->getBytes();
105 $out->writeByteArray(strrev(substr($bytes, 0, 8)));
106 $out->writeByteArray(strrev(substr($bytes, 8, 8)));
111 $skinId = self::getString($in);
112 $skinPlayFabId = self::getString($in);
113 $skinResourcePatch = self::getString($in);
114 $skinData = self::getSkinImage($in);
115 $animationCount = LE::readUnsignedInt($in);
117 for($i = 0; $i < $animationCount; ++$i){
118 $skinImage = self::getSkinImage($in);
119 $animationType = LE::readUnsignedInt($in);
120 $animationFrames = LE::readFloat($in);
121 $expressionType = LE::readUnsignedInt($in);
122 $animations[] =
new SkinAnimation($skinImage, $animationType, $animationFrames, $expressionType);
124 $capeData = self::getSkinImage($in);
125 $geometryData = self::getString($in);
126 $geometryDataVersion = self::getString($in);
127 $animationData = self::getString($in);
128 $capeId = self::getString($in);
129 $fullSkinId = self::getString($in);
130 $armSize = self::getString($in);
131 $skinColor = self::getString($in);
132 $personaPieceCount = LE::readUnsignedInt($in);
134 for($i = 0; $i < $personaPieceCount; ++$i){
135 $pieceId = self::getString($in);
136 $pieceType = self::getString($in);
137 $packId = self::getString($in);
138 $isDefaultPiece = self::getBool($in);
139 $productId = self::getString($in);
140 $personaPieces[] =
new PersonaSkinPiece($pieceId, $pieceType, $packId, $isDefaultPiece, $productId);
142 $pieceTintColorCount = LE::readUnsignedInt($in);
143 $pieceTintColors = [];
144 for($i = 0; $i < $pieceTintColorCount; ++$i){
145 $pieceType = self::getString($in);
146 $colorCount = LE::readUnsignedInt($in);
148 for($j = 0; $j < $colorCount; ++$j){
149 $colors[] = self::getString($in);
151 $pieceTintColors[] =
new PersonaPieceTintColor(
157 $premium = self::getBool($in);
158 $persona = self::getBool($in);
159 $capeOnClassic = self::getBool($in);
160 $isPrimaryUser = self::getBool($in);
161 $override = self::getBool($in);
171 $geometryDataVersion,
188 public static function putSkin(ByteBufferWriter $out, SkinData $skin) : void{
189 self::putString($out, $skin->getSkinId());
190 self::putString($out, $skin->getPlayFabId());
191 self::putString($out, $skin->getResourcePatch());
192 self::putSkinImage($out, $skin->getSkinImage());
193 LE::writeUnsignedInt($out, count($skin->getAnimations()));
194 foreach($skin->getAnimations() as $animation){
195 self::putSkinImage($out, $animation->getImage());
196 LE::writeUnsignedInt($out, $animation->getType());
197 LE::writeFloat($out, $animation->getFrames());
198 LE::writeUnsignedInt($out, $animation->getExpressionType());
200 self::putSkinImage($out, $skin->getCapeImage());
201 self::putString($out, $skin->getGeometryData());
202 self::putString($out, $skin->getGeometryDataEngineVersion());
203 self::putString($out, $skin->getAnimationData());
204 self::putString($out, $skin->getCapeId());
205 self::putString($out, $skin->getFullSkinId());
206 self::putString($out, $skin->getArmSize());
207 self::putString($out, $skin->getSkinColor());
208 LE::writeUnsignedInt($out, count($skin->getPersonaPieces()));
209 foreach($skin->getPersonaPieces() as $piece){
210 self::putString($out, $piece->getPieceId());
211 self::putString($out, $piece->getPieceType());
212 self::putString($out, $piece->getPackId());
213 self::putBool($out, $piece->isDefaultPiece());
214 self::putString($out, $piece->getProductId());
216 LE::writeUnsignedInt($out, count($skin->getPieceTintColors()));
217 foreach($skin->getPieceTintColors() as $tint){
218 self::putString($out, $tint->getPieceType());
219 LE::writeUnsignedInt($out, count($tint->getColors()));
220 foreach($tint->getColors() as $color){
221 self::putString($out, $color);
224 self::putBool($out, $skin->isPremium());
225 self::putBool($out, $skin->isPersona());
226 self::putBool($out, $skin->isPersonaCapeOnClassic());
227 self::putBool($out, $skin->isPrimaryUser());
228 self::putBool($out, $skin->isOverride());
232 private static function getSkinImage(ByteBufferReader $in) : SkinImage{
233 $width = LE::readUnsignedInt($in);
234 $height = LE::readUnsignedInt($in);
235 $data = self::getString($in);
237 return new SkinImage($height, $width, $data);
238 }
catch(\InvalidArgumentException $e){
239 throw new PacketDecodeException($e->getMessage(), 0, $e);
243 private static function putSkinImage(ByteBufferWriter $out, SkinImage $image) : void{
244 LE::writeUnsignedInt($out, $image->getWidth());
245 LE::writeUnsignedInt($out, $image->getHeight());
246 self::putString($out, $image->getData());
254 private static function getItemStackHeader(ByteBufferReader $in) : array{
255 $id = VarInt::readSignedInt($in);
260 $count = LE::readUnsignedShort($in);
261 $meta = VarInt::readUnsignedInt($in);
263 return [$id, $count, $meta];
266 private static function putItemStackHeader(ByteBufferWriter $out, ItemStack $itemStack) : bool{
267 if($itemStack->getId() === 0){
268 VarInt::writeSignedInt($out, 0);
272 VarInt::writeSignedInt($out, $itemStack->getId());
273 LE::writeUnsignedShort($out, $itemStack->getCount());
274 VarInt::writeUnsignedInt($out, $itemStack->getMeta());
280 private static function getItemStackFooter(ByteBufferReader $in,
int $id,
int $meta,
int $count) : ItemStack{
281 $blockRuntimeId = VarInt::readSignedInt($in);
282 $rawExtraData = self::getString($in);
284 return new ItemStack($id, $meta, $count, $blockRuntimeId, $rawExtraData);
287 private static function putItemStackFooter(ByteBufferWriter $out, ItemStack $itemStack) : void{
288 VarInt::writeSignedInt($out, $itemStack->getBlockRuntimeId());
289 self::putString($out, $itemStack->getRawExtraData());
297 [$id, $count, $meta] = self::getItemStackHeader($in);
299 return $id !== 0 ? self::getItemStackFooter($in, $id, $meta, $count) :
ItemStack::null();
303 public static function putItemStackWithoutStackId(ByteBufferWriter $out,
ItemStack $itemStack) : void{
304 if(self::putItemStackHeader($out, $itemStack)){
305 self::putItemStackFooter($out, $itemStack);
311 [$id, $count, $meta] = self::getItemStackHeader($in);
316 $hasNetId = self::getBool($in);
317 $stackId = $hasNetId ? self::readServerItemStackId($in) : 0;
319 $itemStack = self::getItemStackFooter($in, $id, $meta, $count);
324 public static function putItemStackWrapper(ByteBufferWriter $out,
ItemStackWrapper $itemStackWrapper) : void{
325 $itemStack = $itemStackWrapper->getItemStack();
326 if(self::putItemStackHeader($out, $itemStack)){
327 $hasNetId = $itemStackWrapper->getStackId() !== 0;
328 self::putBool($out, $hasNetId);
330 self::writeServerItemStackId($out, $itemStackWrapper->getStackId());
333 self::putItemStackFooter($out, $itemStack);
339 $descriptorType = Byte::readUnsigned($in);
340 $descriptor = match($descriptorType){
341 ItemDescriptorType::INT_ID_META => IntIdMetaItemDescriptor::read($in),
342 ItemDescriptorType::STRING_ID_META => StringIdMetaItemDescriptor::read($in),
343 ItemDescriptorType::TAG => TagItemDescriptor::read($in),
344 ItemDescriptorType::MOLANG => MolangItemDescriptor::read($in),
345 ItemDescriptorType::COMPLEX_ALIAS => ComplexAliasItemDescriptor::read($in),
348 $count = VarInt::readSignedInt($in);
353 public static function putRecipeIngredient(ByteBufferWriter $out, RecipeIngredient $ingredient) : void{
354 $type = $ingredient->getDescriptor();
356 Byte::writeUnsigned($out, $type?->getTypeId() ?? 0);
359 VarInt::writeSignedInt($out, $ingredient->getCount());
372 $count = VarInt::readUnsignedInt($in);
374 for($i = 0; $i < $count; ++$i){
375 $key = VarInt::readUnsignedInt($in);
376 $type = VarInt::readUnsignedInt($in);
378 $data[$key] = self::readMetadataProperty($in, $type);
385 private static function readMetadataProperty(ByteBufferReader $in,
int $type) :
MetadataProperty{
387 ByteMetadataProperty::ID => ByteMetadataProperty::read($in),
388 ShortMetadataProperty::ID => ShortMetadataProperty::read($in),
389 IntMetadataProperty::ID => IntMetadataProperty::read($in),
390 FloatMetadataProperty::ID => FloatMetadataProperty::read($in),
391 StringMetadataProperty::ID => StringMetadataProperty::read($in),
392 CompoundTagMetadataProperty::ID => CompoundTagMetadataProperty::read($in),
393 BlockPosMetadataProperty::ID => BlockPosMetadataProperty::read($in),
394 LongMetadataProperty::ID => LongMetadataProperty::read($in),
395 Vec3MetadataProperty::ID => Vec3MetadataProperty::read($in),
408 VarInt::writeUnsignedInt($out, count($metadata));
409 foreach($metadata as $key => $d){
410 VarInt::writeUnsignedInt($out, $key);
411 VarInt::writeUnsignedInt($out, $d->getTypeId());
418 return VarInt::readSignedLong($in);
421 public static function putActorUniqueId(ByteBufferWriter $out,
int $eid) : void{
422 VarInt::writeSignedLong($out, $eid);
427 return VarInt::readUnsignedLong($in);
430 public static function putActorRuntimeId(ByteBufferWriter $out,
int $eid) : void{
431 VarInt::writeUnsignedLong($out, $eid);
440 $x = VarInt::readSignedInt($in);
441 $y = Binary::signInt(VarInt::readUnsignedInt($in));
442 $z = VarInt::readSignedInt($in);
450 VarInt::writeSignedInt($out, $blockPosition->getX());
451 VarInt::writeUnsignedInt($out, Binary::unsignInt($blockPosition->getY()));
452 VarInt::writeSignedInt($out, $blockPosition->getZ());
461 $x = VarInt::readSignedInt($in);
462 $y = VarInt::readSignedInt($in);
463 $z = VarInt::readSignedInt($in);
471 VarInt::writeSignedInt($out, $blockPosition->getX());
472 VarInt::writeSignedInt($out, $blockPosition->getY());
473 VarInt::writeSignedInt($out, $blockPosition->getZ());
482 $x = LE::readFloat($in);
483 $y = LE::readFloat($in);
484 $z = LE::readFloat($in);
485 return new Vector3($x, $y, $z);
494 $x = LE::readFloat($in);
495 $y = LE::readFloat($in);
508 if($vector !== null){
509 self::putVector3($out, $vector);
511 LE::writeFloat($out, 0.0);
512 LE::writeFloat($out, 0.0);
513 LE::writeFloat($out, 0.0);
521 LE::writeFloat($out, $vector->x);
522 LE::writeFloat($out, $vector->y);
523 LE::writeFloat($out, $vector->z);
530 LE::writeFloat($out, $vector2->x);
531 LE::writeFloat($out, $vector2->y);
536 return Byte::readUnsigned($in) * (360 / 256);
539 public static function putRotationByte(ByteBufferWriter $out,
float $rotation) : void{
540 Byte::writeUnsigned($out, (int) ($rotation / (360 / 256)));
544 private static function readGameRule(ByteBufferReader $in,
int $type,
bool $isPlayerModifiable,
bool $isStartGame) :
GameRule{
546 BoolGameRule::ID => BoolGameRule::decode($in, $isPlayerModifiable),
547 IntGameRule::ID => IntGameRule::decode($in, $isPlayerModifiable, $isStartGame),
548 FloatGameRule::ID => FloatGameRule::decode($in, $isPlayerModifiable),
562 public static function getGameRules(ByteBufferReader $in,
bool $isStartGame) : array{
563 $count = VarInt::readUnsignedInt($in);
565 for($i = 0; $i < $count; ++$i){
566 $name = self::getString($in);
567 $isPlayerModifiable = self::getBool($in);
568 $type = VarInt::readUnsignedInt($in);
569 $rules[$name] = self::readGameRule($in, $type, $isPlayerModifiable, $isStartGame);
581 public static function putGameRules(ByteBufferWriter $out, array $rules,
bool $isStartGame) : void{
582 VarInt::writeUnsignedInt($out, count($rules));
583 foreach($rules as $name => $rule){
584 self::putString($out, $name);
585 self::putBool($out, $rule->isPlayerModifiable());
586 VarInt::writeUnsignedInt($out, $rule->getTypeId());
587 $rule->encode($out, $isStartGame);
593 $fromActorUniqueId = self::getActorUniqueId($in);
594 $toActorUniqueId = self::getActorUniqueId($in);
595 $type = Byte::readUnsigned($in);
596 $immediate = self::getBool($in);
597 $causedByRider = self::getBool($in);
598 $vehicleAngularVelocity = LE::readFloat($in);
599 return new EntityLink($fromActorUniqueId, $toActorUniqueId, $type, $immediate, $causedByRider, $vehicleAngularVelocity);
602 public static function putEntityLink(ByteBufferWriter $out,
EntityLink $link) : void{
603 self::putActorUniqueId($out, $link->fromActorUniqueId);
604 self::putActorUniqueId($out, $link->toActorUniqueId);
605 Byte::writeUnsigned($out, $link->type);
606 self::putBool($out, $link->immediate);
607 self::putBool($out, $link->causedByRider);
608 LE::writeFloat($out, $link->vehicleAngularVelocity);
615 $result->type = VarInt::readUnsignedInt($in);
616 $result->uuid = self::getUUID($in);
617 $result->requestId = self::getString($in);
619 if($result->type === CommandOriginData::ORIGIN_DEV_CONSOLE or $result->type === CommandOriginData::ORIGIN_TEST){
620 $result->playerActorUniqueId = VarInt::readSignedLong($in);
626 public static function putCommandOriginData(ByteBufferWriter $out,
CommandOriginData $data) : void{
627 VarInt::writeUnsignedInt($out, $data->type);
628 self::putUUID($out, $data->uuid);
629 self::putString($out, $data->requestId);
631 if($data->type === CommandOriginData::ORIGIN_DEV_CONSOLE or $data->type === CommandOriginData::ORIGIN_TEST){
632 VarInt::writeSignedLong($out, $data->playerActorUniqueId);
640 $result->paletteName = self::getString($in);
642 $result->ignoreEntities = self::getBool($in);
643 $result->ignoreBlocks = self::getBool($in);
644 $result->allowNonTickingChunks = self::getBool($in);
646 $result->dimensions = self::getBlockPosition($in);
647 $result->offset = self::getBlockPosition($in);
649 $result->lastTouchedByPlayerID = self::getActorUniqueId($in);
650 $result->rotation = Byte::readUnsigned($in);
651 $result->mirror = Byte::readUnsigned($in);
652 $result->animationMode = Byte::readUnsigned($in);
653 $result->animationSeconds = LE::readFloat($in);
654 $result->integrityValue = LE::readFloat($in);
655 $result->integritySeed = LE::readUnsignedInt($in);
656 $result->pivot = self::getVector3($in);
661 public static function putStructureSettings(ByteBufferWriter $out,
StructureSettings $structureSettings) : void{
662 self::putString($out, $structureSettings->paletteName);
664 self::putBool($out, $structureSettings->ignoreEntities);
665 self::putBool($out, $structureSettings->ignoreBlocks);
666 self::putBool($out, $structureSettings->allowNonTickingChunks);
668 self::putBlockPosition($out, $structureSettings->dimensions);
669 self::putBlockPosition($out, $structureSettings->offset);
671 self::putActorUniqueId($out, $structureSettings->lastTouchedByPlayerID);
672 Byte::writeUnsigned($out, $structureSettings->rotation);
673 Byte::writeUnsigned($out, $structureSettings->mirror);
674 Byte::writeUnsigned($out, $structureSettings->animationMode);
675 LE::writeFloat($out, $structureSettings->animationSeconds);
676 LE::writeFloat($out, $structureSettings->integrityValue);
677 LE::writeUnsignedInt($out, $structureSettings->integritySeed);
678 self::putVector3($out, $structureSettings->pivot);
685 $result->structureName = self::getString($in);
686 $result->filteredStructureName = self::getString($in);
687 $result->structureDataField = self::getString($in);
689 $result->includePlayers = self::getBool($in);
690 $result->showBoundingBox = self::getBool($in);
692 $result->structureBlockType = VarInt::readSignedInt($in);
693 $result->structureSettings = self::getStructureSettings($in);
694 $result->structureRedstoneSaveMode = VarInt::readSignedInt($in);
699 public static function putStructureEditorData(ByteBufferWriter $out,
StructureEditorData $structureEditorData) : void{
700 self::putString($out, $structureEditorData->structureName);
701 self::putString($out, $structureEditorData->filteredStructureName);
702 self::putString($out, $structureEditorData->structureDataField);
704 self::putBool($out, $structureEditorData->includePlayers);
705 self::putBool($out, $structureEditorData->showBoundingBox);
707 VarInt::writeSignedInt($out, $structureEditorData->structureBlockType);
708 self::putStructureSettings($out, $structureEditorData->structureSettings);
709 VarInt::writeSignedInt($out, $structureEditorData->structureRedstoneSaveMode);
714 $offset = $in->getOffset();
718 throw PacketDecodeException::wrap($e,
"Failed decoding NBT root");
720 $in->setOffset($offset);
724 public static function getNbtCompoundRoot(ByteBufferReader $in) :
CompoundTag{
726 return self::getNbtRoot($in)->mustGetCompoundTag();
727 }
catch(NbtDataException $e){
728 throw PacketDecodeException::wrap($e,
"Expected TAG_Compound NBT root");
734 return VarInt::readUnsignedInt($in);
737 public static function writeRecipeNetId(ByteBufferWriter $out,
int $id) : void{
738 VarInt::writeUnsignedInt($out, $id);
743 return VarInt::readUnsignedInt($in);
746 public static function writeCreativeItemNetId(ByteBufferWriter $out,
int $id) : void{
747 VarInt::writeUnsignedInt($out, $id);
763 return VarInt::readSignedInt($in);
772 VarInt::writeSignedInt($out, $id);
777 return VarInt::readSignedInt($in);
780 public static function writeItemStackRequestId(ByteBufferWriter $out,
int $id) : void{
781 VarInt::writeSignedInt($out, $id);
786 return VarInt::readSignedInt($in);
789 public static function writeLegacyItemStackRequestId(ByteBufferWriter $out,
int $id) : void{
790 VarInt::writeSignedInt($out, $id);
795 return VarInt::readSignedInt($in);
798 public static function writeServerItemStackId(ByteBufferWriter $out,
int $id) : void{
799 VarInt::writeSignedInt($out, $id);
808 public static function readOptional(ByteBufferReader $in, \Closure $reader) : mixed{
809 if(self::getBool($in)){
820 public static function writeOptional(ByteBufferWriter $out, mixed $value, \Closure $writer) : void{
822 self::putBool($out,
true);
823 $writer($out, $value);
825 self::putBool($out,
false);