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);
338 $descriptorType = Byte::readUnsigned($in);
339 $descriptor = match($descriptorType){
340 ItemDescriptorType::INT_ID_META => IntIdMetaItemDescriptor::read($in),
341 ItemDescriptorType::STRING_ID_META => StringIdMetaItemDescriptor::read($in),
342 ItemDescriptorType::TAG => TagItemDescriptor::read($in),
343 ItemDescriptorType::MOLANG => MolangItemDescriptor::read($in),
344 ItemDescriptorType::COMPLEX_ALIAS => ComplexAliasItemDescriptor::read($in),
347 $count = VarInt::readSignedInt($in);
352 public static function putRecipeIngredient(ByteBufferWriter $out, RecipeIngredient $ingredient) : void{
353 $type = $ingredient->getDescriptor();
355 Byte::writeUnsigned($out, $type?->getTypeId() ?? 0);
358 VarInt::writeSignedInt($out, $ingredient->getCount());
371 $count = VarInt::readUnsignedInt($in);
373 for($i = 0; $i < $count; ++$i){
374 $key = VarInt::readUnsignedInt($in);
375 $type = VarInt::readUnsignedInt($in);
377 $data[$key] = self::readMetadataProperty($in, $type);
384 private static function readMetadataProperty(ByteBufferReader $in,
int $type) :
MetadataProperty{
386 ByteMetadataProperty::ID => ByteMetadataProperty::read($in),
387 ShortMetadataProperty::ID => ShortMetadataProperty::read($in),
388 IntMetadataProperty::ID => IntMetadataProperty::read($in),
389 FloatMetadataProperty::ID => FloatMetadataProperty::read($in),
390 StringMetadataProperty::ID => StringMetadataProperty::read($in),
391 CompoundTagMetadataProperty::ID => CompoundTagMetadataProperty::read($in),
392 BlockPosMetadataProperty::ID => BlockPosMetadataProperty::read($in),
393 LongMetadataProperty::ID => LongMetadataProperty::read($in),
394 Vec3MetadataProperty::ID => Vec3MetadataProperty::read($in),
407 VarInt::writeUnsignedInt($out, count($metadata));
408 foreach($metadata as $key => $d){
409 VarInt::writeUnsignedInt($out, $key);
410 VarInt::writeUnsignedInt($out, $d->getTypeId());
417 return VarInt::readSignedLong($in);
420 public static function putActorUniqueId(ByteBufferWriter $out,
int $eid) : void{
421 VarInt::writeSignedLong($out, $eid);
426 return VarInt::readUnsignedLong($in);
429 public static function putActorRuntimeId(ByteBufferWriter $out,
int $eid) : void{
430 VarInt::writeUnsignedLong($out, $eid);
439 $x = VarInt::readSignedInt($in);
440 $y = VarInt::readSignedInt($in);
441 $z = VarInt::readSignedInt($in);
449 VarInt::writeSignedInt($out, $blockPosition->getX());
450 VarInt::writeSignedInt($out, $blockPosition->getY());
451 VarInt::writeSignedInt($out, $blockPosition->getZ());
460 $x = LE::readFloat($in);
461 $y = LE::readFloat($in);
462 $z = LE::readFloat($in);
463 return new Vector3($x, $y, $z);
472 $x = LE::readFloat($in);
473 $y = LE::readFloat($in);
486 if($vector !== null){
487 self::putVector3($out, $vector);
489 LE::writeFloat($out, 0.0);
490 LE::writeFloat($out, 0.0);
491 LE::writeFloat($out, 0.0);
499 LE::writeFloat($out, $vector->x);
500 LE::writeFloat($out, $vector->y);
501 LE::writeFloat($out, $vector->z);
508 LE::writeFloat($out, $vector2->x);
509 LE::writeFloat($out, $vector2->y);
514 return Byte::readUnsigned($in) * (360 / 256);
517 public static function putRotationByte(ByteBufferWriter $out,
float $rotation) : void{
518 Byte::writeUnsigned($out, (int) ($rotation / (360 / 256)));
522 private static function readGameRule(ByteBufferReader $in,
int $type,
bool $isPlayerModifiable,
bool $isStartGame) :
GameRule{
524 BoolGameRule::ID => BoolGameRule::decode($in, $isPlayerModifiable),
525 IntGameRule::ID => IntGameRule::decode($in, $isPlayerModifiable, $isStartGame),
526 FloatGameRule::ID => FloatGameRule::decode($in, $isPlayerModifiable),
540 public static function getGameRules(ByteBufferReader $in,
bool $isStartGame) : array{
541 $count = VarInt::readUnsignedInt($in);
543 for($i = 0; $i < $count; ++$i){
544 $name = self::getString($in);
545 $isPlayerModifiable = self::getBool($in);
546 $type = VarInt::readUnsignedInt($in);
547 $rules[$name] = self::readGameRule($in, $type, $isPlayerModifiable, $isStartGame);
559 public static function putGameRules(ByteBufferWriter $out, array $rules,
bool $isStartGame) : void{
560 VarInt::writeUnsignedInt($out, count($rules));
561 foreach($rules as $name => $rule){
562 self::putString($out, $name);
563 self::putBool($out, $rule->isPlayerModifiable());
564 VarInt::writeUnsignedInt($out, $rule->getTypeId());
565 $rule->encode($out, $isStartGame);
571 $fromActorUniqueId = self::getActorUniqueId($in);
572 $toActorUniqueId = self::getActorUniqueId($in);
573 $type = Byte::readUnsigned($in);
574 $immediate = self::getBool($in);
575 $causedByRider = self::getBool($in);
576 $vehicleAngularVelocity = LE::readFloat($in);
577 return new EntityLink($fromActorUniqueId, $toActorUniqueId, $type, $immediate, $causedByRider, $vehicleAngularVelocity);
580 public static function putEntityLink(ByteBufferWriter $out,
EntityLink $link) : void{
581 self::putActorUniqueId($out, $link->fromActorUniqueId);
582 self::putActorUniqueId($out, $link->toActorUniqueId);
583 Byte::writeUnsigned($out, $link->type);
584 self::putBool($out, $link->immediate);
585 self::putBool($out, $link->causedByRider);
586 LE::writeFloat($out, $link->vehicleAngularVelocity);
593 $result->type = CommonTypes::getString($in);
594 $result->uuid = self::getUUID($in);
595 $result->requestId = self::getString($in);
596 $result->playerActorUniqueId = LE::readSignedLong($in);
601 public static function putCommandOriginData(ByteBufferWriter $out,
CommandOriginData $data) : void{
602 self::putString($out, $data->type);
603 self::putUUID($out, $data->uuid);
604 self::putString($out, $data->requestId);
605 LE::writeSignedLong($out, $data->playerActorUniqueId);
612 $result->paletteName = self::getString($in);
614 $result->ignoreEntities = self::getBool($in);
615 $result->ignoreBlocks = self::getBool($in);
616 $result->allowNonTickingChunks = self::getBool($in);
618 $result->dimensions = self::getBlockPosition($in);
619 $result->offset = self::getBlockPosition($in);
621 $result->lastTouchedByPlayerID = self::getActorUniqueId($in);
622 $result->rotation = Byte::readUnsigned($in);
623 $result->mirror = Byte::readUnsigned($in);
624 $result->animationMode = Byte::readUnsigned($in);
625 $result->animationSeconds = LE::readFloat($in);
626 $result->integrityValue = LE::readFloat($in);
627 $result->integritySeed = LE::readUnsignedInt($in);
628 $result->pivot = self::getVector3($in);
633 public static function putStructureSettings(ByteBufferWriter $out,
StructureSettings $structureSettings) : void{
634 self::putString($out, $structureSettings->paletteName);
636 self::putBool($out, $structureSettings->ignoreEntities);
637 self::putBool($out, $structureSettings->ignoreBlocks);
638 self::putBool($out, $structureSettings->allowNonTickingChunks);
640 self::putBlockPosition($out, $structureSettings->dimensions);
641 self::putBlockPosition($out, $structureSettings->offset);
643 self::putActorUniqueId($out, $structureSettings->lastTouchedByPlayerID);
644 Byte::writeUnsigned($out, $structureSettings->rotation);
645 Byte::writeUnsigned($out, $structureSettings->mirror);
646 Byte::writeUnsigned($out, $structureSettings->animationMode);
647 LE::writeFloat($out, $structureSettings->animationSeconds);
648 LE::writeFloat($out, $structureSettings->integrityValue);
649 LE::writeUnsignedInt($out, $structureSettings->integritySeed);
650 self::putVector3($out, $structureSettings->pivot);
657 $result->structureName = self::getString($in);
658 $result->filteredStructureName = self::getString($in);
659 $result->structureDataField = self::getString($in);
661 $result->includePlayers = self::getBool($in);
662 $result->showBoundingBox = self::getBool($in);
664 $result->structureBlockType = VarInt::readSignedInt($in);
665 $result->structureSettings = self::getStructureSettings($in);
666 $result->structureRedstoneSaveMode = VarInt::readSignedInt($in);
671 public static function putStructureEditorData(ByteBufferWriter $out,
StructureEditorData $structureEditorData) : void{
672 self::putString($out, $structureEditorData->structureName);
673 self::putString($out, $structureEditorData->filteredStructureName);
674 self::putString($out, $structureEditorData->structureDataField);
676 self::putBool($out, $structureEditorData->includePlayers);
677 self::putBool($out, $structureEditorData->showBoundingBox);
679 VarInt::writeSignedInt($out, $structureEditorData->structureBlockType);
680 self::putStructureSettings($out, $structureEditorData->structureSettings);
681 VarInt::writeSignedInt($out, $structureEditorData->structureRedstoneSaveMode);
686 $offset = $in->getOffset();
690 throw PacketDecodeException::wrap($e,
"Failed decoding NBT root");
692 $in->setOffset($offset);
696 public static function getNbtCompoundRoot(ByteBufferReader $in) :
CompoundTag{
698 return self::getNbtRoot($in)->mustGetCompoundTag();
699 }
catch(NbtDataException $e){
700 throw PacketDecodeException::wrap($e,
"Expected TAG_Compound NBT root");
706 return VarInt::readUnsignedInt($in);
709 public static function writeRecipeNetId(ByteBufferWriter $out,
int $id) : void{
710 VarInt::writeUnsignedInt($out, $id);
715 return VarInt::readUnsignedInt($in);
718 public static function writeCreativeItemNetId(ByteBufferWriter $out,
int $id) : void{
719 VarInt::writeUnsignedInt($out, $id);
735 return VarInt::readSignedInt($in);
744 VarInt::writeSignedInt($out, $id);
749 return VarInt::readSignedInt($in);
752 public static function writeItemStackRequestId(ByteBufferWriter $out,
int $id) : void{
753 VarInt::writeSignedInt($out, $id);
758 return VarInt::readSignedInt($in);
761 public static function writeLegacyItemStackRequestId(ByteBufferWriter $out,
int $id) : void{
762 VarInt::writeSignedInt($out, $id);
767 return VarInt::readSignedInt($in);
770 public static function writeServerItemStackId(ByteBufferWriter $out,
int $id) : void{
771 VarInt::writeSignedInt($out, $id);
780 public static function readOptional(ByteBufferReader $in, \Closure $reader) : mixed{
781 if(self::getBool($in)){
792 public static function writeOptional(ByteBufferWriter $out, mixed $value, \Closure $writer) : void{
794 self::putBool($out,
true);
795 $writer($out, $value);
797 self::putBool($out,
false);