diff --git a/src/network/mcpe/ProcessSkinTask.php b/src/network/mcpe/ProcessSkinTask.php new file mode 100644 index 0000000000..76ba87ae19 --- /dev/null +++ b/src/network/mcpe/ProcessSkinTask.php @@ -0,0 +1,68 @@ + */ + private NonThreadSafeValue $skinData; + + /** + * @param \Closure(?Skin $skin,?string $error) : void $callback + */ + public function __construct( + SkinData $skinData, + \Closure $callback, + ){ + $this->skinData = new NonThreadSafeValue($skinData); + $this->storeLocal(self::TLS_KEY_ON_COMPLETION, $callback); + } + + public function onRun() : void{ + try{ + $skin = TypeConverter::getInstance()->getSkinAdapter()->fromSkinData($this->skinData->deserialize()); + $this->setResult($skin); + $this->error = null; + }catch(\InvalidArgumentException|InvalidSkinException $e){ + $this->error = $e->getMessage(); + } + } + + public function onCompletion() : void{ + /** @var Skin|null $result */ + $result = $this->getResult(); + /** @var \Closure(?Skin $skin,?string $error) : void $callback */ + $callback = $this->fetchLocal(self::TLS_KEY_ON_COMPLETION); + $callback($result, $this->error); + } +} diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 88b4ba1a05..0b44c690ac 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -29,7 +29,7 @@ use pocketmine\block\utils\SignText; use pocketmine\entity\animation\ConsumingItemAnimation; use pocketmine\entity\Attribute; -use pocketmine\entity\InvalidSkinException; +use pocketmine\entity\Skin; use pocketmine\event\player\PlayerEditBookEvent; use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\InventoryTransaction; @@ -40,12 +40,14 @@ use pocketmine\item\WritableBook; use pocketmine\item\WritableBookPage; use pocketmine\item\WrittenBook; +use pocketmine\lang\KnownTranslationFactory; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\InventoryManager; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\ProcessSkinTask; use pocketmine\network\mcpe\protocol\ActorEventPacket; use pocketmine\network\mcpe\protocol\ActorPickRequestPacket; use pocketmine\network\mcpe\protocol\AnimatePacket; @@ -840,15 +842,33 @@ public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{ $this->session->getLogger()->debug("Refused duplicate skin change request"); return true; } - $this->lastRequestedFullSkinId = $packet->skin->getFullSkinId(); + $currentFullSkinId = $this->lastRequestedFullSkinId = $packet->skin->getFullSkinId(); $this->session->getLogger()->debug("Processing skin change request"); - try{ - $skin = $this->session->getTypeConverter()->getSkinAdapter()->fromSkinData($packet->skin); - }catch(InvalidSkinException $e){ - throw PacketHandlingException::wrap($e, "Invalid skin in PlayerSkinPacket"); - } - return $this->player->changeSkin($skin, $packet->newSkinName, $packet->oldSkinName); + $this->player->getServer()->getAsyncPool()->submitTask(new ProcessSkinTask( + $packet->skin, + function(?Skin $skin, ?string $error) use ($currentFullSkinId, $packet) : void{ + if(!$this->session->isConnected()){ + return; + } + if($error !== null){ + $this->session->disconnectWithError( + reason: "Invalid skin: " . $error, + disconnectScreenMessage: KnownTranslationFactory::disconnectionScreen_invalidSkin() + ); + return; + } + if($skin !== null){ + if($currentFullSkinId !== $this->lastRequestedFullSkinId){ + $this->session->getLogger()->debug("Skin change request ignored due to newer skin change"); + return; + } + $this->player->changeSkin($skin, $packet->newSkinName, $packet->oldSkinName); + $this->session->getLogger()->debug("Skin change request processed"); + } + } + )); + return true; } public function handleSubClientLogin(SubClientLoginPacket $packet) : bool{ diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index c15753dad3..1f1ae8f05c 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -23,7 +23,7 @@ namespace pocketmine\network\mcpe\handler; -use pocketmine\entity\InvalidSkinException; +use pocketmine\entity\Skin; use pocketmine\event\player\PlayerPreLoginEvent; use pocketmine\lang\KnownTranslationFactory; use pocketmine\lang\Translatable; @@ -31,6 +31,7 @@ use pocketmine\network\mcpe\JwtException; use pocketmine\network\mcpe\JwtUtils; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\ProcessSkinTask; use pocketmine\network\mcpe\protocol\LoginPacket; use pocketmine\network\mcpe\protocol\types\login\AuthenticationData; use pocketmine\network\mcpe\protocol\types\login\ClientData; @@ -41,6 +42,7 @@ use pocketmine\player\PlayerInfo; use pocketmine\player\XboxLivePlayerInfo; use pocketmine\Server; +use pocketmine\utils\AssumptionFailedError; use Ramsey\Uuid\Uuid; use function is_array; @@ -68,22 +70,39 @@ public function handleLogin(LoginPacket $packet) : bool{ return true; } - $clientData = $this->parseClientData($packet->clientDataJwt); + if(!Uuid::isValid($extraData->identity)){ + throw new PacketHandlingException("Invalid login UUID"); + } + $clientData = $this->parseClientData($packet->clientDataJwt); try{ - $skin = $this->session->getTypeConverter()->getSkinAdapter()->fromSkinData(ClientDataToSkinDataHelper::fromClientData($clientData)); - }catch(\InvalidArgumentException | InvalidSkinException $e){ - $this->session->disconnectWithError( - reason: "Invalid skin: " . $e->getMessage(), - disconnectScreenMessage: KnownTranslationFactory::disconnectionScreen_invalidSkin() - ); - - return true; + $skinData = ClientDataToSkinDataHelper::fromClientData($clientData); + }catch(\InvalidArgumentException $e){ + throw PacketHandlingException::wrap($e); } + $this->server->getAsyncPool()->submitTask(new ProcessSkinTask( + $skinData, + function(?Skin $skin, ?string $error) use ($packet, $clientData, $extraData){ + if(!$this->session->isConnected()){ + return; + } + if($error !== null){ + $this->session->disconnectWithError( + reason: "Invalid skin: " . $error, + disconnectScreenMessage: KnownTranslationFactory::disconnectionScreen_invalidSkin() + ); + return; + } + if($skin === null){ + throw new AssumptionFailedError("This should never happen..."); + } + $this->onSkinDataProcessed($extraData, $clientData, $skin, $packet); + }, + )); + return true; + } - if(!Uuid::isValid($extraData->identity)){ - throw new PacketHandlingException("Invalid login UUID"); - } + protected function onSkinDataProcessed(AuthenticationData $extraData, ClientData $clientData, Skin $skin, LoginPacket $packet) : void{ $uuid = Uuid::fromString($extraData->identity); $arrClientData = (array) $clientData; $arrClientData["TitleID"] = $extraData->titleId; @@ -136,12 +155,10 @@ public function handleLogin(LoginPacket $packet) : bool{ $ev->call(); if(!$ev->isAllowed()){ $this->session->disconnect($ev->getFinalDisconnectReason(), $ev->getFinalDisconnectScreenMessage()); - return true; + return; } $this->processLogin($packet, $ev->isAuthRequired()); - - return true; } /**